6 minutes
CWEE Path Notes — Module 1: Injection Attacks

This kicks off a series where I work through the HTB Certified Web Exploitation Expert (CWEE) path — the Senior Web Penetration Tester job-role path — one module at a time and write down what actually stuck. This is more of “here’s the reasoning and the things that bit me” — the point is the technique, not an answer key.
Module 1 is Injection Attacks, and it covers three of the less-glamorous injection classes that developers rarely defend against precisely because they’re less common: XPath injection, LDAP injection, and HTML injection in PDF generation libraries. That obscurity is the whole opportunity — if an app touches XML, an LDAP directory, or a server-side PDF renderer, it’s often untested ground.
XPath Injection

XPath injection rhymes with SQLi but against an XML document. The interesting part is how far you can push it when results aren’t handed to you.
- Auth bypass comes down to boolean logic and operator precedence.
andbinds tighter thanor, so a wrapper like' or true() or 'makes the username predicate dominate regardless of a password clause you can’t satisfy (passwords are usually hashed server-side, so you’re not matching them — you’re overriding the logic). - Data exfiltration leans on the union operator (
|) to append//text()and dump the document, or path traversal to climb to the root. The union approach wins when you don’t yet know the schema depth. - Blind and advanced exfil is where it gets real: result caps force you to select a single target node instead of dumping everything, and a fully blind app turns into character-by-character extraction with
string-length()+substring()against a boolean (or time-based) signal.
The recurring lesson: confirm your true/false signal before automating anything. A universally-true and a universally-false probe up front saves hundreds of wasted requests — and it catches subtle payload bugs like quote-balancing against an app’s appended closing quote, which will silently turn a valid injection into an XPath syntax error.
LDAP Injection

LDAP filters are prefix notation wrapped in parentheses — (&(uid=x)(userPassword=y)) — and the game is manipulating that filter’s logic rather than stacking queries.
- The wildcard
*is the workhorse for both auth bypass and blind extraction (prefix matching is yoursubstring()equivalent). - Auth bypass is usually a wildcard or paren-injection away — but the more instructive scenario is when the target account isn’t the first directory entry. The module teaches the basic bypass; the exercise makes you reach for a
NOTclause(!(uid=...))to exclude the account you keep landing on. - Blind exfil injects an OR clause targeting a second attribute (e.g.
description) of a specific user, closing the filter cleanly through the password field, then walks the value with wildcard prefix matching.
A small but important gotcha: LDAP substring matching is typically case-insensitive, so recovered secrets may need a second pass to nail casing.
HTML Injection in PDF Generation Libraries

This is the standout section and a genuine gear change. Web apps feed user data into HTML, hand it to a server-side PDF library (wkhtmltopdf, dompdf, mPDF…), and the library renders it like a browser. Inject HTML and you control what the server fetches.
- Fingerprint first. The PDF’s
Creator/Producermetadata (viaexiftoolorpdfinfo) tells you the engine and version, which dictates everything — most importantly whether it runs JavaScript. - Server-Side XSS → SSRF. With a JS-capable engine you can script requests; even without one, declarative tags (
<iframe>,<img>,<link>, CSSurl()) trigger server-side fetches. An<iframe>is the dangerous one because it renders the response back into the PDF, turning blind SSRF into full read access to internal services. - LFI.
file://reads via XHR (base64 + newline-chunk to survive PDF truncation), or declarative fallbacks and library-specific tricks like mPDF annotations.
The mental model that matters: the victim is the server generating the PDF, not the user viewing it. It runs with the server’s network position and filesystem access.
Two environment behaviors cost me time and are worth internalizing, because the material doesn’t warn you about them:
- Old engines are picky. wkhtmltopdf on patched Qt 4.8.7 WebKit chokes on modern JS syntax — one
constorfor...ofsilently kills the entire<script>block. Write ES5. - Same-Origin Policy still applies. HTTP
XMLHttpRequestfrom the rendered context is SOP-blocked, so it returns errors for everything. That’s why HTTP SSRF uses iframes and XHR is reserved for thefile://scheme.
Chaining It Together

The module’s capstone is a black-box target that forces you to combine these classes with zero hand-holding — and that’s where the learning consolidates. Without spoiling it, the meta-lessons:
readonlyis a client-side hint, never a server-side control. When the obvious input is sanitized, test the fields the UI won’t let you touch — in a proxy, not the browser.- Encode the whole value, not just the scary characters. Nesting a URL inside an HTML attribute inside a POST body means one stray delimiter breaks the parameter boundary. Encode the entire blob and let the app decode it once.
- A PDF is fixed pages — you can’t scroll. If your extraction overflows a page, stop dumping everything and return one small result per request. Constraints of the output medium shape the exploit as much as the vulnerability does.
- Recon dictates the attack. Fingerprint, map every sink, confirm the signal — then reach for the technique. Every payload in this module is a variation on a handful of primitives once you know what you’re looking at.
Prevention

CWEE isn’t just exploitation — you write the fixes too, so the prevention sections carry real weight:
- XPath: whitelist alphanumeric input; semantic/type validation over blacklisting. There’s no universal parameterized-XPath API, so manual sanitization is the defense.
- LDAP: escape the filter metacharacters, but the architectural fix is authenticating with a bind operation instead of a search filter — no filter, no password-side injection. Still escape the username going into the DN.
- PDF: if you don’t need user HTML, entity-encode it entirely. If you do, harden the library (no JS, no local files, no/whitelisted remote resources) and add network-layer egress controls as defense in depth. Assume defaults are insecure.
The framing that reads well in a report: controls at each tier — app-layer sanitization, library-config hardening, network-layer egress — not a single flag flip.
Takeaways

Module 1 sets the tone for the whole path: obscure vuln classes, chained together, against modern-looking apps, with the expectation that you can both break and fix. What made the difference wasn’t payload memorization — it was methodology. Confirm the signal. Fingerprint before firing. Adapt to the environment’s limits. Return small, targeted results. Those habits scale to everything ahead.
Module 1 done — Injecting payloads blindfolded, 16 September 2026.

Next up: NoSQL injection. More soon.