nodejs-express.md (8736B)
1 --- 2 title: "NodeJS Express" 3 section: "Network Services" 4 sectionSlug: "network-services-pentesting" 5 sourcePath: "src/network-services-pentesting/pentesting-web/nodejs-express.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/nodejs-express.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # NodeJS Express 14 15 ## Quick Fingerprinting 16 17 Useful Express indicators during recon: 18 19 - `X-Powered-By: Express` or stack traces mentioning `express`, `body-parser`, `qs`, `cookie-parser`, `express-session`, or `finalhandler` 20 - Cookies prefixed with `s:` (signed cookie) or `j:` (JSON cookie) 21 - Session cookies such as `connect.sid` 22 - Hidden form fields or query parameters such as `_method=PUT` / `_method=DELETE` 23 - Error pages leaking `Cannot GET /path`, `Cannot POST /path`, `Unexpected token` in `body-parser`, or `URIError` during query parsing 24 25 When you confirm Express, focus on the middleware chain, because most interesting bugs come from parsers, proxy trust, session handling, and method-tunneling rather than from the framework core itself. 26 27 ## Cookie Signature 28 29 The `cookie-monster` tool automates testing candidate Express.js cookie secrets and re-signing cookie values once a secret is known.<sup>[[3]](#references)</sup> 30 31 Express commonly exposes two useful cookie formats: 32 33 - `s:<value>.<sig>` signed cookies handled by `cookie-parser` or `express-session` 34 - `j:<json>` JSON cookies that are automatically parsed by `cookie-parser` 35 36 If `cookie-parser` receives a signed cookie and its signature is invalid, the unsigned value becomes `false` rather than the attacker-supplied string. When the application supplies an array of secrets, verification tries each secret in order, so retained rotation keys continue to validate old signatures.<sup>[[4]](#references)</sup> 37 38 ### Single cookie with a specific name 39 40 ```bash 41 cookie-monster -c eyJmb28iOiJiYXIifQ== -s LVMVxSNPdU_G8S3mkjlShUD78s4 -n session 42 ``` 43 44 ### Custom wordlist 45 46 ```bash 47 cookie-monster -c eyJmb28iOiJiYXIifQ== -s LVMVxSNPdU_G8S3mkjlShUD78s4 -w custom.lst 48 ``` 49 50 ### Test multiple cookies using batch mode 51 52 ```bash 53 cookie-monster -b -f cookies.json 54 ``` 55 56 ### Test multiple cookies using batch mode with a custom wordlist 57 58 ```bash 59 cookie-monster -b -f cookies.json -w custom.lst 60 ``` 61 62 ### Encode and sign a new cookie 63 64 If you know the secret you can sign the cookie. 65 66 ```bash 67 cookie-monster -e -f new_cookie.json -k secret 68 ``` 69 70 ## Query String and URL-Encoded Parser Abuse 71 72 Express targets often become interesting when they parse attacker-controlled keys into nested objects. 73 74 - `req.query` can be configured with different parsers, including `qs` 75 - `express.urlencoded({ extended: true })` uses `qs`-style parsing for `application/x-www-form-urlencoded` 76 - Nested parsing unlocks object injection, mass assignment, NoSQL injection, and prototype pollution chains if the parsed object is merged into application state<sup>[[2]](#references)</sup> 77 78 Practical payloads to try: 79 80 ```bash 81 # Mass assignment style probe 82 curl 'https://target.example/profile?role=admin&isAdmin=true' 83 84 # Nested object / qs syntax 85 curl 'https://target.example/search?user[role]=admin&filters[name][$ne]=x' 86 87 # URL-encoded body against express.urlencoded({ extended: true }) 88 curl -X POST 'https://target.example/api/update' -H 'Content-Type: application/x-www-form-urlencoded' --data 'profile[role]=admin&filters[$ne]=x' 89 ``` 90 91 If the app reflects or persists the resulting object, pivot into the dedicated pages for exploitation details: 92 93 [Mass Assignment Cwe 915](/hacktricks/pentesting-web/mass-assignment-cwe-915) 94 95 [Express Prototype Pollution Gadgets](/hacktricks/pentesting-web/deserialization/nodejs-proto-prototype-pollution/express-prototype-pollution-gadgets) 96 97 Extra tests that are worth sending against Express specifically: 98 99 - Deep nesting to look for parser limits, timeouts, or 400/413 differences 100 - Duplicate keys to see whether the app keeps the first value, the last one, or an array 101 - Bracket syntax such as `a[b][c]=1`, dotted syntax such as `a.b=1`, and `__proto__` / `constructor[prototype]` payloads 102 103 ## `trust proxy` Abuse 104 105 If the app uses `app.set("trust proxy", true)` or trusts too many hops, Express will derive security-relevant values from forwarding headers. If the reverse proxy does not overwrite them, a client can spoof them directly.<sup>[[1]](#references)</sup> 106 107 That affects: 108 109 - `req.hostname` via `X-Forwarded-Host` 110 - `req.protocol` via `X-Forwarded-Proto` 111 - `req.ip` / `req.ips` via `X-Forwarded-For` 112 113 This is useful for: 114 115 - Password reset poisoning and absolute URL poisoning 116 - Bypassing IP-based allowlists, rate limits, or audit trails 117 - Influencing `secure` cookie handling and HTTPS-only logic in apps that key off `req.protocol` 118 - Poisoning redirects or cacheable responses when the app templates absolute links with forwarded host/proto headers 119 120 ```http 121 POST /reset-password HTTP/1.1 122 Host: target.example 123 X-Forwarded-Host: attacker.example 124 X-Forwarded-Proto: https 125 X-Forwarded-For: 127.0.0.1 126 Content-Type: application/json 127 128 {"email":"victim@target.example"} 129 ``` 130 131 Check whether generated links, redirect locations, logs, or access-control decisions now use attacker-supplied values. 132 133 Related pages: 134 135 [Reset Password](/hacktricks/pentesting-web/reset-password) 136 137 [Readme](/hacktricks/pentesting-web/cache-deception/overview) 138 139 ## `express-session` Testing Notes 140 141 Common Express deployments use `express-session`, which signs the session identifier cookie but stores the real state server-side. 142 143 Useful checks: 144 145 - **Session fixation**: authenticate with a pre-login cookie and verify whether the SID stays the same after login 146 - **Weak secret rotation**: some deployments verify cookies with an array of old secrets, so previously valid signatures may continue to work 147 - **`saveUninitialized: true`**: the application stores new but unmodified sessions, increasing anonymous session volume. It does not create fixation by itself, but it supplies pre-authentication SIDs that make a failure to rotate the SID easier to test. 148 - **`MemoryStore`** is intentionally unsuitable for production: it leaks memory under many workloads, does not scale beyond one process, and loses sessions on restart.<sup>[[5]](#references)</sup> 149 150 A practical fixation workflow: 151 152 1. Obtain an anonymous session cookie from the target. 153 2. Send that cookie to a victim or authenticate with it yourself. 154 3. Check whether login binds the authenticated state to the existing SID. 155 4. If it does, replay the same cookie in a separate browser session. 156 157 If the application does not rotate or regenerate the session after authentication, test whether authenticated state remains bound to a SID chosen or learned before login. `req.session.regenerate()` is the middleware's built-in rotation primitive.<sup>[[5]](#references)</sup> 158 159 ## Method Override Tunneling 160 161 Some Express apps use `method-override` to tunnel verbs that HTML forms cannot send natively. When enabled, always test whether you can smuggle dangerous methods through a route that the front-end, WAF, or CSRF logic assumed was only `POST`. 162 163 Typical probes: 164 165 ```http 166 POST /users/42 HTTP/1.1 167 Host: target.example 168 X-HTTP-Method-Override: DELETE 169 Content-Type: application/x-www-form-urlencoded 170 171 confirm=yes 172 ``` 173 174 ```http 175 POST /users/42?_method=PUT HTTP/1.1 176 Host: target.example 177 Content-Type: application/x-www-form-urlencoded 178 179 role=admin 180 ``` 181 182 Interesting impacts: 183 184 - Reaching hidden `PUT` / `PATCH` / `DELETE` routes through a `POST`-only edge control 185 - Bypassing route-specific middleware that only checks `req.method` 186 - Triggering state-changing handlers via CSRF when the application validates only the outer request method 187 188 The official `method-override` middleware checks only original `POST` requests by default (`options.methods: ['POST']`), so prioritize `POST` requests with header, body, and query-string override values.<sup>[[6]](#references)</sup> 189 190 ## References 191 192 - [1] [Express behind proxies - Express.js](https://expressjs.com/en/guide/behind-proxies.html) 193 - [2] [Server-side prototype pollution: Black-box detection without the DoS - PortSwigger Research](https://portswigger.net/research/server-side-prototype-pollution) 194 - [3] [DigitalInterruption/cookie-monster](https://github.com/DigitalInterruption/cookie-monster) 195 - [4] [Express.js - cookie-parser middleware](https://expressjs.com/en/resources/middleware/cookie-parser.html) 196 - [5] [Express.js - express-session middleware](https://expressjs.com/en/resources/middleware/session.html) 197 - [6] [Express.js - method-override middleware](https://expressjs.com/en/resources/middleware/method-override.html)