GDB / GEF
¿Por qué GDB en offensive security?
- Análisis de shellcode: ver byte a byte cómo se ejecuta
- Buffer overflow: encontrar offset exacto al return address
- Anti-debug bypass: entender qué checa el binario
- Reverse engineering: entender binarios sin código fuente
- Exploit development: verificar que tu payload funciona
Iniciar GDB
bash
gdb -q ./binario # -q = quiet (sin banner)
gdb -q ./binario core # analizar core dump
gdb --args ./binario arg1 # con argumentos
gef ./binario # GEF directamente (si está configurado)
# Desde pwntools (scripting de exploits):
from pwn import *
p = gdb.debug('./binario', gdbscript='b main\nc')Breakpoints — controlar dónde pausar
bash
# Por nombre de función o label
b _start # breakpoint al inicio del programa
b main # en función main
b loopFib # en label loopFib
# Por offset desde un label
b *_start+17 # 17 bytes después de _start
b *loopFib+9 # en la instrucción js de loopFib
# Por dirección exacta
b *0x401011 # dirección absoluta
# Condicional — solo pausa si se cumple la condición
b loopFib if $rbx > 10 # pausa cuando rbx supere 10
b *0x401012 if $rcx == 0 # pausa cuando el contador llegue a 0
# Administrar breakpoints
info break # listar todos los breakpoints
del 1 # eliminar breakpoint #1
del # eliminar TODOS
disable 2 # desactivar sin eliminar
enable 2 # reactivarEn contexto ofensivo
bash
# Breakpoint antes de strcpy para ver el buffer antes del overflow
b strcpy
# Breakpoint en la return address del frame vulnerable
b *vuln_func+45 # justo antes del ret
# Breakpoint condicional para cuando el canary cambie
b *función+offset if $rax != canary_value
# Watchpoint — pausa cuando se ESCRIBE en una dirección
watch *0x602060 # pausa cuando se modifica 0x602060 (ej: GOT entry)
rwatch *0x602060 # pausa cuando se LEEEjecución — controlar el flujo
bash
r # run (ejecutar desde el inicio)
r arg1 arg2 # run con argumentos
r < input.txt # run con stdin desde archivo
c # continue (hasta siguiente breakpoint)
si # step instruction (UNA instrucción ASM) ← usar siempre en ASM
ni # next instruction (como si pero no entra en calls)
s # step (por línea de fuente — evitar en ASM puro)
n # next (por línea, sin entrar en funciones)
finish # ejecutar hasta que la función actual retorne
until *0x401050 # ejecutar hasta llegar a esa dirección
jump *0x401050 # saltar directamente a esa dirección (sin ejecutar lo intermedio)Diferencia crítica: si vs ni
bash
# si (step into) — entra DENTRO de los calls
# Antes: → 0x401020 call printf
si
# Después: → 0x7f... <printf+0> ← estás dentro de printf
# ni (next over) — ejecuta el call pero NO entra
# Antes: → 0x401020 call printf
ni
# Después: → 0x401025 mov rax, 0 ← continuó después del callEn contexto ofensivo
bash
# Analizar shellcode byte a byte
b *0x401000 # start
r
si # ejecutar 1 instrucción
si # siguiente
si # ...
# Llegar rápido al punto de crash
r # dejar que crashee
# gdb muestra dónde fue el SIGSEGV
# ver qué había en rsp en ese momento → eso es lo que kontrolasVer registros
bash
info registers # todos los registros
info reg rax rbx rip # solo algunos
p $rax # imprimir rax (en decimal por defecto)
p/x $rax # en hexadecimal
p/d $rbx # en decimal
p/o $rcx # en octal
p/t $rax # en binario
p/s $rsi # como string (si apunta a string)
p/f $xmm0 # como float
# Modificar registros (útil para testing)
set $rax = 0x1337 # cambiar rax mientras debuggeas
set $rip = 0x401050 # redirigir ejecución manualmenteEn contexto ofensivo
bash
# Verificar valor de rsp al momento del ret (buffer overflow)
b *vuln+0x45 # breakpoint en el ret
r
p/x $rsp # ver qué dirección va a saltar
x/1xg $rsp # ver el contenido de rsp (la return address)
# Si la return address es 0x4141414141414141 ("AAAAAAAA")
# significa que controlamos el return address ✓
# ahora calcular el offset exacto con cyclic pattern:
# cyclic 200 | ./binario
# x/1xg $rsp → ver el patrón que llegó ahí
# cyclic -l 0x6161616e → offset exactoVer memoria
bash
# x/[cantidad][formato][tamaño] dirección
x/16xg $rsp # 16 valores de 8 bytes (qword) en hex desde rsp
x/32xb $rdi # 32 bytes desde rdi
x/4xw 0x402000 # 4 dwords desde dirección 0x402000
x/s $rdi # ver como string (hasta null byte)
x/i $rip # ver instrucción en rip
x/10i $rip # ver 10 instrucciones desde rip
# Formatos:
# x = hex, d = decimal, s = string, i = instrucción, c = char
# Tamaños:
# b = 1 byte, h = 2 bytes, w = 4 bytes, g = 8 bytesEn contexto ofensivo
bash
# Ver el stack completo durante un overflow
x/40xg $rsp # ver 40 qwords del stack
# Buscar patrón en memoria (encontrar offset)
# después de enviar cyclic pattern:
x/1xg $rsp # ver qué valor cayó en rsp
# luego: cyclic -l VALOR → te da el offset
# Ver si el shellcode está bien en memoria
x/50xb $rsp+0x100 # ver bytes del shellcode inyectado
x/20i $rsp+0x100 # ver como instrucciones (verificar que se desensambla bien)
# Buscar string en memoria
find 0x400000, 0x500000, "/bin/sh" # buscar "/bin/sh" en el binario
# encuentra la dirección → usar en ret2libc
# Ver GOT table (para info leaks)
x/10xg 0x602000 # ver entradas GOTDesensamblar
bash
disas _start # desensamblar función completa
disas loopFib # con offsets exactos para breakpoints
disas 0x401000, 0x401050 # rango de direcciones
disas /m main # con código fuente intercalado (si hay símbolos)
# Ver instrucción en rip actual
x/i $rip
x/5i $rip # próximas 5 instruccionesEn contexto ofensivo
bash
# Encontrar gadgets ROP manualmente
disas funcion
# buscar visualmente: pop rdi; ret / pop rsi; ret / syscall; ret
# Verificar PLT/GOT
disas puts@plt # ver stub de plt
x/1xg 0x602020 # ver GOT entry de puts (dirección real en libc)
# Analizar función desconocida (reverse engineering)
disas 0x401234
# ir instrucción por instrucción con si para entender qué haceGEF — indicators visuales
● en dirección → breakpoint activo en esa instrucción
→ en instrucción → próxima instrucción a ejecutar (rip)
En la sección code:x86:64:
TAKEN [Reason: S] → el salto condicional SÍ se tomará
NOT taken [Reason: !(S)] → el salto NO se tomará
En $eflags:
MAYÚSCULAS = flag ACTIVO
minúsculas = flag inactivo
Ejemplo: [ZERO carry PARITY adjust sign INTERRUPT direction overflow]
→ ZF=1 (ZERO activo), CF=0, PF=1, SF=0...
En registros:
$rsp → 0x7fff1000 → 0x0000000000000001
↑ dirección ↑ valor al que apunta (GEF lo desreferencia)
En stack:
0x7fff1000│+0x0000: 0x0000000000000001 ← $rsp
El │ separa dirección del valor
El offset +0x0000 es relativo a rspComandos de análisis de binarios
bash
# Ver info del binario
checksec # protecciones: NX, PIE, RELRO, canary, etc.
info files # secciones del binario cargado
info functions # listar funciones con símbolos
info variables # variables globales
# Buscar strings útiles
find /bin/sh # buscar string "/bin/sh" en memoria
grep -r "sh" ./ # antes de cargar
# Ver memoria mapeada
info proc mappings # qué está cargado y con qué permisos
# Muestra: dirección inicio, fin, permisos (rwx), nombre
# Útil para verificar si hay segmento rwx (donde inyectar shellcode)
# Modificar memoria (para testing de exploits)
set {char}0x401000 = 0x90 # escribir NOP en esa dirección
set {long}$rsp = 0x401337 # cambiar return address en el stackEn contexto ofensivo
bash
# checksec output y qué significa:
# NX enabled → stack no ejecutable (necesitas ROP o heap spray)
# PIE enabled → ASLR de código (necesitas info leak para dirección base)
# RELRO Full → GOT protegido (no puedes sobreescribir GOT entries)
# Stack canary → protección contra overflow básico
# RUNPATH → ruta de búsqueda de librerías
# Buscar "/bin/sh" en libc (para ret2libc)
info proc mappings # encontrar base de libc
# supón que libc está en 0x7ffff7a0d000
find 0x7ffff7a0d000, 0x7ffff7c00000, "/bin/sh"
# te da la dirección exacta → usarla en tu exploit
# Ver si lograste control del RIP
b *vuln+offset_ret
r
p/x $rip # ver si rip = tu valor
# si es 0x4141414141414141 → controlado ✓Comandos de objdump complementarios
bash
# Desensamblar .text
objdump -M intel -d binario
# Desensamblar TODO
objdump -M intel -D binario
# Solo instrucciones (sin bytes ni addresses — legible)
objdump -M intel --no-show-raw-insn --no-addresses -d binario
# Ver .data como hex+ASCII (encontrar strings, flags, etc.)
objdump -sj .data binario
# Buscar gadgets ROP (alternativa a ROPgadget)
objdump -M intel -d binario | grep -A1 'pop rdi'
objdump -M intel -d binario | grep 'syscall'Flujo típico para explotar un buffer overflow
bash
# 1. Ver protecciones
checksec
# 2. Encontrar el crash
python3 -c "print('A'*200)" | ./binario
# o con cyclic:
python3 -c "from pwn import *; print(cyclic(200))" | ./binario
# 3. En gdb, ver qué valor cayó en rsp al crashear
gdb -q ./binario
r < <(python3 -c "from pwn import *; sys.stdout.buffer.write(cyclic(200))")
# al crashear:
x/1xg $rsp # ver patrón en rsp
# luego:
python3 -c "from pwn import *; print(cyclic_find(0x6161616e))" # offset
# 4. Verificar control de rip
# enviar offset bytes de padding + dirección objetivo
r < <(python3 -c "print('A'*offset + 'B'*8)")
p/x $rip # debería ser 0x4242424242424242
# 5. Construir exploit
# [padding][dirección_shellcode o gadget ROP]