Web application penetration testing checklist
Map the whole application before you attack any of it — every route, parameter and role. Then test each input against the injection classes, and check access control on every authenticated action separately. The bug people miss is rarely exotic; it is an input they never mapped or an object reference they never tampered with.
The trap on a web exam is jumping to exploitation on the first interesting parameter before you have mapped the application. The finding that gets people through is usually not a clever payload — it is an input, a parameter or an endpoint they never discovered, or an object reference they never thought to tamper with. Coverage beats cleverness.
The rule
Map the entire application before you exploit anything, and test every input, not just the obvious one. A single unsanitised parameter you never found is worth more than a deep dive into the one you did — and it is the parameter you skipped that the exam is built around.
Map the application first
Browse every function as every role you have, with an intercepting proxy recording all of it. Then go wider than the UI:
- Content discovery. Directory and file brute-forcing finds admin panels, backups, old endpoints and API routes the navigation never links.
- Fingerprint the stack. Response headers, cookies, error pages and framework tells. The technology dictates which vulnerability classes are even possible and what the payloads look like.
- Read the client-side JavaScript. It names endpoints, parameters and hidden features that the interface never exposes. This is the single most skipped step and one of the most productive.
- Enumerate every input. Not just form fields — query and body parameters, JSON keys, headers, cookies and path segments are all inputs.
- Map the roles. Note what each account level can reach. You will need this the moment you test access control.
Read the source when you have it
BSCP and CBBH are black-box — you infer behaviour from responses. OSWE and CWEE are white-box: you get the code, and the method changes. Trace user input from where it enters to every dangerous sink, grep for the functions that execute, query or deserialise, and diff versions where you can. On a white-box exam, the vulnerability is in the source before it is on the wire.
Test every input against the classes
SQL injection. Break the query, then confirm — error-based, boolean, or time-based blind. A database read is often a path to the filesystem or to code execution, not just to data.
Cross-site scripting. Reflected, stored and DOM. Find where your input is echoed and in what context, then break out of that context.
Command injection. Anywhere input reaches a shell. Test blind with time delays and out-of-band callbacks when there is no visible output.
Server-side template injection. Test math in the input; a rendered result is often a straight line to code execution.
Path traversal and file inclusion. Read files outside the web root; local file inclusion can escalate to execution.
XXE. Anywhere XML is parsed — file read and SSRF.
Server-side request forgery. Any feature that fetches a URL. Reach internal services and cloud metadata endpoints the app can see and you cannot.
Insecure deserialisation. Serialised objects in cookies or parameters, the highest-impact class when the language and gadget line up.
Access control — the highest-value class
Broken access control is where the marks are on web exams, and it needs no payload. Test every authenticated action as a lower-privileged user and as another user: change an ID and see whose data you get (IDOR), request an admin function directly, replay one role’s request in another’s session. Vertical and horizontal escalation are separate checks — do both.
When to stop testing an input
When you have tested it against each class that applies and confirmed how it is handled. An input you noted but never fuzzed is the web equivalent of an open port you scanned but never looked at — the most common reason an app that was vulnerable looked clean.