• Dumpear un payload oculto de un binario con gdb

    Lanzamos gdb de forma interactiva sobre el binario:

    gdb /Ruta/Al/Binario

    Desactivamos la paginación:

    set pagination off

    Desactivamos el confirm:

    set confirm off

    Ponemos un punto de interrupción justo antes de que el binario ejecute la primera llamada al sistema que le indiquemos. En este caso pararemos antes de la syscall mprotect, que es la llamada al sistema que reserva memoria para luego cargar algo allí:

    catch syscall mprotect

    Ejecutamos el binario:

    run

    Veremos que la ejecución se detiene justo antes de ejecutar primera mprotect:

    Catchpoint 1 (call to syscall mprotect), 0x0000000003fe6ee5 in ?? ()

    En ese momento podemos ver que tiene el binario mapeado en memoria, ejecutando en gdb:

    info proc mappings

    Si nos da, por ejemplo:

    process 13302
    Mapped address spaces:
    
              Start Addr           End Addr       Size     Offset  Perms  objfile
                0x400000           0x401000     0x1000        0x0  rw-p   /mnt/host/MAGIC
                0x401000          0x2c1f000  0x281e000        0x0  rw-p   
               0x2c1f000          0x3fe8000  0x13c9000        0x0  r-xp   /mnt/host/MAGIC
          0x7ffff7ff8000     0x7ffff7ff9000     0x1000        0x0  rw-p   
          0x7ffff7ff9000     0x7ffff7ffd000     0x4000        0x0  r--p   [vvar]
          0x7ffff7ffd000     0x7ffff7fff000     0x2000        0x0  r-xp   [vdso]
          0x7ffffffde000     0x7ffffffff000    0x21000        0x0  rw-p   [stack]
    

    Podemos observar que, en ese preciso momento de la ejecución, el binario tiene mapeados diferentes espacios en memoria. Los mapeos que nos interesan son los que NO tienen objfile. Esto es porque en Linux, cada línea de /proc/<pid>/maps representa una región de memoria mapeada con su origen. Las que terminan con rutas o etiquetas específicas como vvar, vdso o stack no son payloads nuevos, sino partes conocidas del proceso.

    Por ello, para esa salida, nos interesa dumpear las líneas 0x401000 a 0x2c1f000 y 0x7ffff7ff8000 a 0x7ffff7ff9000. Lo hacemos con:

    dump memory ~/ProbablePayloadStage2_1.bin 0x401000 0x2c1f000
    dump memory ~/ProbablePayloadStage2_2.bin 0x7ffff7ff8000 0x7ffff7ff9000

    Si además, revisando el strace del binario, observamos que ese mapeo de memoria (o muy cercano) fue marcado como ejecutable, pasando de prot_read a prot_exec:

    12590 mprotect(0x404000, 20289869, PROT_READ|PROT_EXEC) = 0

    …el dumpeo se justifica aún más.

    Arriba digo que la dirección de memoria del mapeo no tiene que ser exacta y puede ser muy cercana es porque el kernel mapea memoria en múltiplos de 4 KB (0x1000).  Cuando vemos en strace que el binario hace:

    mprotect(0x404000, 20289869, PROT_READ|PROT_EXEC)

    …el kernel alinea la dirección base hacia abajo al límite de página más cercano (0x401000). Por eso en /proc/<pid>/maps vemos el inicio real de la región como 0x401000, no 0x404000.

    Entonces, teniendo ambos dumps, ya podemos empezar a trabajar con ellos. Primero salimos de gdb:

    quit

    Y luego, para obtener información sobre los dumps, usamos las herramientas habituales:

    file ~/ProbablePayloadStage2_1.bin
    file ~/ProbablePayloadStage2_2.bin

    o

    strings -a ~/ProbablePayloadStage2_1.bin
    strings -a ~/ProbablePayloadStage2_2.bin

    …etc.

    NOTA:

    Es posible hacer un script que ejecute esto de forma automática:

    cat > /mnt/host/tmp/dump_stage3.gdb
    echo 'set pagination off'                                                                   | tee -a ExtractMemoryMaps.gdb
    echo 'set confirm off'                                                                      | tee -a ExtractMemoryMaps.gdb
    echo 'catch syscall mprotect'                                                               | tee -a ExtractMemoryMaps.gdb
    echo 'commands'                                                                             | tee -a ExtractMemoryMaps.gdb
    echo '  silent'                                                                             | tee -a ExtractMemoryMaps.gdb
    echo '  printf "\n[*] mprotect called at: %p, len=%d, prot=%d\n", $rdi, $rsi, $rdx' '       | tee -a ExtractMemoryMaps.gdb
    echo '  if ($rsi > 1000000 && ($rdx == 5 || $rdx == 4))'                                    | tee -a ExtractMemoryMaps.gdb
    echo '    dump memory /mnt/host/tmp/Stage3_dump.bin $rdi ($rdi + $rsi)'                     | tee -a ExtractMemoryMaps.gdb
    echo '    printf "[+] Dumped probable Stage3 region from %p to %p\n", $rdi, ($rdi + $rsi)'  | tee -a ExtractMemoryMaps.gdb
    echo '    detach'                                                                           | tee -a ExtractMemoryMaps.gdb
    echo '    quit'                                                                             | tee -a ExtractMemoryMaps.gdb
    echo '  end'                                                                                | tee -a ExtractMemoryMaps.gdb
    echo '  continue'                                                                           | tee -a ExtractMemoryMaps.gdb
    echo 'end'                                                                                  | tee -a ExtractMemoryMaps.gdb
    echo 'run'                                                                                  | tee -a ExtractMemoryMaps.gdb
    

    Luego lo podemos ejecutar, con:

    gdb -q -x /mnt/host/tmp/dump_stage3.gdb /mnt/host/MAGIC

    Ignora los mprotect pequeños (como el de 3117 bytes).

    Solo dumpea si la longitud (len) es mayor de 1 MB y los permisos incluyen PROT_EXEC.

    Eso debería capturar la gran región de 0x404000 (unos 20 MB).

    Los comentarios están cerrados.