HTB CWPE study materials
HTB CWPE study resources

HTB CWPE common mistakes

HTB CWPE · Understand the traps and how to avoid them.

Mistake 1

Starting exploitation before finishing content discovery: the candidate picks the first login form or parameter they see and begins fuzzing payloads, never running a directory/file brute-force with a modern wordlist, never checking robots.txt, sitemap.xml, .git/, backup files, or alternate vhosts. They miss the secondary application or hidden endpoint that the intended chain depends on.

The exam feels time-limited, so candidates rush toward 'doing something' with Burp Repeater. HTB-style machines and the CWPE scope deliberately place pivot points in non-linked paths (admin panels, API docs, debug endpoints), and manual browsing only reveals what the developers linked. Without a directory brute-force and vhost enumeration, the attack surface is a fraction of what exists.

What to do instead

Make a fixed recon checklist and complete it before touching payloads: full TCP scan, HTTP title/headers on every port, ffuf/gobuster with a large wordlist plus common extensions (php, bak, old, zip, txt, json), vhost fuzzing with Host header, robots.txt/sitemap.xml/.well-known, source-map and JS bundle review for hidden routes, and a check for .git/ and .env exposure. Record every discovered endpoint in a notes file and only then move to exploitation.

Mistake 2

Treating authentication as a single-step password guess: the candidate tries a short default-credential list, gets a 200 on login, and stops, without testing username enumeration via response timing or message differences, without checking whether the 'remember me' cookie or JWT is signed with a weak or empty secret, and without testing password-reset token predictability or host-header poisoning in reset emails.

Login pages look like a dead end after a few failed attempts, and candidates do not know that the real flaw is usually in the session artifact (JWT, cookie, reset flow) rather than the password field. They also skip enumeration because the response body looks identical, missing length or timing differences.

What to do instead

For every auth flow, capture the full request/response in Burp and compare valid vs invalid usernames by status code, body length, and response time. Decode any JWT and test alg:none, HS256 with a cracked or default secret (hashcat with rockyou), and kid/jku injection. For reset flows, test whether the token is derived from the username or timestamp, whether the reset link honors an attacker-controlled Host header, and whether the token is invalidated after use. Try default creds only after these structural tests.

Mistake 3

Testing only classic ' OR 1=1-- payloads and giving up when they are blocked: the candidate does not differentiate error-based, boolean-blind, time-blind, and out-of-band channels, does not adapt the payload to the DBMS (MySQL vs MSSQL vs PostgreSQL vs SQLite), and does not test NoSQL operators such as {"$ne": null} or {"$regex": "^a"} on JSON login endpoints.

Training material often shows one payload shape, and WAFs or parameterized queries block it. Candidates conclude 'no SQLi' instead of switching channel. NoSQL injection is frequently missed entirely because the endpoint accepts JSON and the tester only sends form-encoded data.

What to do instead

For each injectable parameter, run a channel matrix: error-based (extractvalue/updatexml for MySQL, CAST for MSSQL), boolean-blind with a true/false pair and compare response length, time-blind with SLEEP/pg_sleep/WAITFOR DELAY, and out-of-band via DNS if egress allows. Identify the DBMS from error strings or version functions. For JSON endpoints, send both form-encoded and application/json bodies, and test Mongo operators ($ne, $gt, $regex, $where) on username/password fields. Use sqlmap only after manual confirmation so you understand the injection context.

Mistake 4

Reflected XSS is reported as 'alert(1) works' and the candidate moves on, without checking whether the cookie is HttpOnly, without building a session-stealing or CSRF-bypassing payload, and without testing stored XSS in admin-reviewed fields or DOM XSS in client-side sinks (location.hash, innerHTML, eval).

alert(1) is the easiest proof and feels like completion. But the exam scores impact: a reflected alert in a non-HttpOnly context is low value, while a stored XSS in an admin panel or a DOM sink reachable by the victim is a pivot. Candidates also forget that many frameworks encode < and > but not attribute or JavaScript contexts.

What to do instead

For every XSS finding, record the exact context (HTML body, attribute, JS string, URL) and test the matching breakout. Check Set-Cookie for HttpOnly; if absent, demonstrate cookie theft against your own listener. Test stored XSS in profile, comment, and filename fields, and DOM XSS by tracing source (location, document.referrer) to sink (innerHTML, document.write, eval) in the JS bundle. If CSP is present, read the header and test for bypasses (JSONP endpoints, unsafe-inline, base-uri).

Mistake 5

Sending full URLs to every parameter that looks like a link and declaring SSRF when the server fetches it, without testing filter bypasses (decimal/hex IP, DNS rebinding, redirect chains, 302 to internal host), without checking which internal services are reachable (cloud metadata 169.254.169.254, Redis, internal admin panels), and without distinguishing blind SSRF via DNS callback from full-read SSRF.

A simple http://127.0.0.1 hit often returns a block page, and candidates stop. The real value is in the bypass and in what the internal fetch reaches. Similarly, file inclusion is tested with ../../etc/passwd only, ignoring php://filter, data://, and log poisoning when the wrapper is restricted.

What to do instead

For SSRF, first confirm the fetch with a Collaborator/listener domain, then escalate: try 127.0.0.1, 0x7f000001, 2130706433, [::1], and a DNS name you control that resolves to 127.0.0.1; test open redirects on the same host as a bypass. If the response is readable, request cloud metadata paths and internal service banners. For LFI, test ../../etc/passwd, php://filter/convert.base64-encode/resource=index.php to read source, /proc/self/environ, and log poisoning via User-Agent when execution is possible. Record whether the include is local-only or allows wrappers.

Mistake 6

Seeing a serialized object or a template expression and immediately firing a known gadget chain or {{7*7}} without first identifying the exact engine, language, and library versions, and without checking whether the sink is actually evaluated server-side versus client-side.

Public exploit chains are version-specific, and candidates paste payloads that fail silently. They also confuse client-side template rendering (Angular, Vue) with server-side SSTI (Jinja2, Twig, Freemarker), leading to wasted time. For deserialization, they do not check the magic bytes or the language-specific format (Java rO0AB, PHP O:..., Python pickle) before choosing gadgets.

What to do instead

Fingerprint first: for SSTI, submit a polyglot arithmetic probe (${77}, {{77}}, #{7*7}, <%= 7*7 %>) and inspect which evaluates; then use engine-specific syntax to reach RCE (Jinja2 config/class, Twig _self, Freemarker Execute). For deserialization, base64-decode the cookie or parameter and identify the format from the first bytes; enumerate library versions from headers, error pages, or composer.json/package.json if reachable; then select a gadget chain that matches those versions and test in a controlled way before using it against the target.

Mistake 7

Testing IDOR only by changing a numeric ID in the URL and getting a 403, then abandoning it, without trying UUID enumeration, horizontal vs vertical object references, mass assignment on profile update, or forced browsing to admin functions that are hidden but not access-controlled.

Modern apps use UUIDs and server-side ownership checks on the obvious endpoint, so the naive test fails. Candidates do not realize that the same object is often exposed through a second endpoint (export, PDF, API v1) that lacks the check, or that role is a client-supplied field in a JSON update.

What to do instead

Map every endpoint that returns an object and test each with two accounts of different privilege: swap IDs (numeric, UUID, slug), swap tokens, and remove role parameters. Look for export/download/print endpoints and older API versions that may skip authorization. Test mass assignment by adding role, isAdmin, or verified fields to profile update JSON and observing the response. For hidden admin routes, force-browse with the low-priv cookie and compare 200 vs 302/403.

Mistake 8

Ignoring the API layer: the candidate tests only the web UI and never enumerates /api/, /v1/, /graphql, or swagger.json, never checks HTTP method tampering (GET vs POST vs PUT vs PATCH), and never tests GraphQL introspection or field-level authorization.

The UI hides the API, and candidates assume the browser is the only client. Modern HTB targets expose JSON APIs that accept different content types and methods, and GraphQL endpoints often allow introspection that reveals mutations the UI never calls.

What to do instead

Run content discovery specifically for API paths and documentation files (swagger.json, openapi.json, /api/docs, /graphql). Send the same request with GET, POST, PUT, PATCH, DELETE and with Content-Type: application/json vs form-encoded, and compare responses. On GraphQL, send an introspection query; if enabled, enumerate mutations and test each with a low-priv token. Check whether API responses include fields the UI hides (password hashes, internal IDs, role flags) and whether versioned paths (/v1 vs /v2) enforce the same auth.

Mistake 9

Treating each finding as isolated: the candidate reports an open redirect, an SSRF, and an admin panel separately instead of chaining them, and stops after obtaining a low-priv shell without enumerating the host for credentials, config files, or lateral movement paths.

Exam tasks often require a chain to reach the flag, and a single low-impact bug is not enough. Candidates also treat shell access as the finish line, missing that post-exploitation enumeration (env vars, config files, database creds, SSH keys, cron jobs) is where the final objective usually lives.

What to do instead

After each finding, write down what primitive it gives (redirect, fetch, read, write, execute) and look for a second bug that converts it (open redirect + SSRF bypass, LFI + log poisoning, XSS + CSRF to admin action). After a shell, run a structured local enumeration: env, /etc/passwd, config files for DB and API creds, ~/.ssh, cron, sudo -l, and network connections to internal services. Document the chain end-to-end so the report shows impact.

Mistake 10

Not managing time and evidence: the candidate spends hours on one rabbit hole, does not keep a per-target checklist, and at the end cannot reproduce which payload produced which result because requests were sent ad hoc from the terminal without saving them.

Under exam pressure, the first interesting lead consumes the session, and without a written state candidates re-test the same endpoints. Ad hoc curl commands leave no record, so writing the report or re-verifying a finding becomes guesswork.

What to do instead

Use a timeboxed workflow: 20-30 minutes per hypothesis, then park it in a notes file with the exact request and result. Keep a per-domain checklist (recon, auth, injection, client-side, SSRF/LFI, deserialization/SSTI, access control, API, chain/post) and tick items as tested. Save every confirmed request as a Burp item or a numbered curl script so the final report can cite reproducible evidence.