OSWE study materials
OSWE study resources

OSWE common mistakes

OSWE · Understand the traps and how to avoid them.

Mistake 1

Starts the target by fuzzing URLs and parameters in the proxy and only opens the provided source to confirm a bug already suspected, so no route-to-sink map is ever built and white-box-only bugs (loose comparisons, custom dispatchers, helper functions reused by a forgotten endpoint) are never found.

Habit carried over from black-box practice exams and bug bounty work: source review feels slower than watching a response change, and reading a 400-line router file gives no visible reward.

What to do instead

Spend the first block of time reading only. List every entry point from the route/index files, follow each one into its controller and down to dangerous sinks (file include, deserialization, command execution, raw query builders), and write the resulting table down before sending a single request.

Mistake 2

Accepts the login and session code as correct because the flow looks ordinary, never auditing the actual comparison and token-generation statements.

Auth code reads plausibly, so candidates hunt for visibly 'broken' logic instead of auditing operators and entropy: == versus ===, strcmp/in_array without strict mode, magic-hash strings, and tokens built from time(), rand(), mt_rand() or md5(username).

What to do instead

Grep the auth codebase for ==, strcmp, in_array, md5, sha1, rand, uniqid and setcookie, and read every hit. For each token or identifier, determine the seed space and reproduce candidate values locally before assuming it is unguessable.

Mistake 3

Stops once a payload changes the response or throws a database error. Reports 'SQL injection confirmed' and moves to the next target without dumping a credential, escalating a privilege, or reaching a file-read/command sink.

Proof-of-reflection feels like success and the full extraction path is tedious; candidates underestimate that the objective is a working exploit demonstrating real impact, not a vulnerability report.

What to do instead

For every confirmed injection, decide the end objective first (a credential, an admin session, a file read, an OS command), then build the payload set that reaches it: UNION column mapping, stacked queries, boolean/time-based extraction, or writing a file. Script it as you go.

Mistake 4

Reports alert(1) or prompt(document.domain) as the finding and never delivers a payload that performs a privileged action or defeats the application's own filter in a different context (attribute, JS string, DOM sink).

XSS is treated as a single payload test rather than a context problem. The reflected/attribute/JS-string/DOM distinction is glossed over and filter bypass is attempted by trial and error instead of by reading the sanitiser.

What to do instead

Enumerate every output context from source, craft a payload per context (attribute breaking, JS string escape, DOM sink via location/hash/postMessage), read the exact sanitiser or encoder to design the bypass, and finish with a payload that completes the admin action end to end.

Mistake 5

Sends ../../../../etc/passwd once, sees it stripped, and concludes the traversal is not exploitable, never testing ....//, URL or double encoding, absolute paths, wrapper schemes, or the extension/subdirectory the code appends.

Only the obvious payload is tried. Candidates do not model the sanitiser as a function with a specific number of passes and a specific suffix, so they never craft input that survives those transforms.

What to do instead

Read the exact sanitising and concatenation code, count the passes, write out what the string becomes after each pass, and derive a payload for that transform. Include php://filter, data://, absolute paths, and account for any appended suffix or forced prefix directory.

Mistake 6

Finds unserialize() on user-controlled input but gives up when no obvious __destruct or __wakeup RCE appears, or sends a hand-edited serialized string that the application silently rejects because the length prefixes or encoding layer (base64, urlencode, signed cookie) do not match.

Gadget chains must be assembled from many small magic-method-to-sink hops, and a single wrong length field fails silently; candidates debug by guessing at the payload string instead of re-serializing programmatically.

What to do instead

Enumerate every class with magic methods, trace each one to its sinks, and build the POP chain in code using the target's own classes (or faithful stubs) so lengths are computed correctly. Then re-read the receive code to confirm the exact encoding layer and sign it the way the app expects, testing against a local copy of the app.

Mistake 7

Posts a classic external-entity payload, gets an empty response, and declares the endpoint not vulnerable, without checking whether an internal DTD is required, whether parameter entities are needed, or whether the result must be exfiltrated out of band.

Parser configuration decides everything: entity loader disabled, DTD not allowed, errors not echoed. The payload syntax differs between those cases, and the first empty response is misread as 'not vulnerable'.

What to do instead

Read the XML parsing call and its flags first, then send a payload that matches that configuration: internal DTD versus external DTD, parameter entities for blind-but-parsed cases, and an attacker-hosted DTD plus a listener when nothing is reflected. Use the php://filter or file:// wrapper depending on the parser's supported schemes.

Mistake 8

Only requests http://127.0.0.1:80/ and a few common ports, then stops if that fails, never using redirects, alternate schemes, decimal/hex IP forms, DNS rebinding, or parsing tricks against an allowlist, and never chaining the reachability into an actual internal objective.

SSRF is treated as a port scanner rather than an access primitive. The mismatch between the URL validator and the actual fetch is invisible unless both code paths are read side by side.

What to do instead

Locate the fetch call and the validator, note whether they use the same parser and whether redirects are followed. Test scheme changes, IP-format variants, userinfo tricks and allowlist bypasses, then aim the SSRF at a concrete target such as an internal admin panel, internal API or cloud metadata endpoint rather than just proving loopback reachability.

Mistake 9

Skips the business-logic portions because 'there is no payload to send', or tests them ad hoc through the UI instead of scripting multi-request sequences that skip workflow steps, reuse a one-time token, submit negative or zero quantities, or fire two requests at the same instant.

Logic bugs do not appear in a single request and require state tracking across steps; the browser UI hides the fact that the client can reorder, repeat or parallelise requests.

What to do instead

Read the workflow code for exactly where state, price, quantity or limits are validated. Then script the whole sequence with a step-skip, token-reuse, negative-value or concurrent-request variant, and confirm the invariant breaks server-side rather than just in the rendered page.

Mistake 10

Works the exploit by hand in the proxy, writes the required script last, and hardcodes a session cookie or CSRF token captured earlier, so the script works once in the session it was authored in and fails when run from a clean state at grading.

Manual exploitation hides every dynamic dependency. Tokens expire, anti-CSRF values rotate, and the exploit's preconditions (a created user, an uploaded file, a warmed cache) are not re-created by a script that only sends the final request.

What to do instead

Build the exploit script in parallel with discovery. Parameterise base URL and credentials, log in from scratch, scrape fresh tokens per request, create the script's own preconditions, print an unambiguous success marker plus the exfiltrated data, and re-run it from a fully reset target before considering the target finished.