ROP - Return Oriented Programing
Aprenda e pratique Hacking na AWS:Treinamento HackTricks AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking no GCP:
Treinamento HackTricks GCP Red Team Expert (GRTE)
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 geralmente 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 uma carga útil para realizar operações arbitrárias, contornando efetivamente as proteções NX/DEP.
Como o ROP Funciona
Sequestro de Fluxo de Controle: Primeiro, um atacante precisa sequestrar o fluxo de controle de um programa, geralmente explorando um estouro de buffer para sobrescrever um endereço de retorno salvo na pilha.
Encadeamento de Gadgets: O atacante então seleciona e encadeia cuidadosamente gadgets para realizar as ações desejadas. Isso poderia envolver configurar argumentos para uma chamada de função, chamar a função (por exemplo,
system("/bin/sh")
), e lidar com qualquer limpeza necessária ou operações adicionais.Execução da Carga Útil: Quando a função vulnerável retorna, em vez de retornar para uma localização legítima, ela começa a executar a cadeia de gadgets.
Ferramentas
Normalmente, gadgets podem ser encontrados usando ROPgadget, ropper ou diretamente do pwntools (ROP).
Cadeia ROP no Exemplo x86
Convenções de Chamada x86 (32 bits)
cdecl: O chamador limpa a pilha. Os argumentos da função são empurrados para a pilha em ordem reversa (da direita para a esquerda). Os argumentos são empurrados para a pilha da direita para a esquerda.
stdcall: Semelhante ao cdecl, mas o callee é responsável por limpar a pilha.
Encontrando Gadgets
Primeiramente, vamos assumir que identificamos os gadgets necessários dentro do binário ou de suas bibliotecas carregadas. Os gadgets de interesse são:
pop eax; ret
: Este gadget desempilha o valor do topo da pilha para o registradorEAX
e então retorna, permitindo controlarEAX
.pop ebx; ret
: Semelhante ao anterior, mas para o registradorEBX
, possibilitando controle sobreEBX
.mov [ebx], eax; ret
: Move o valor emEAX
para a localização de memória apontada porEBX
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 o pwntools, preparamos a pilha para a execução da cadeia ROP da seguinte forma visando executar system('/bin/sh')
, observe como a cadeia começa com:
Uma instrução
ret
para fins de alinhamento (opcional)Endereço da função
system
(supondo ASLR desativado e libc conhecida, mais informações em Ret2lib)Marcador de posição para o endereço de retorno de
system()
Endereço da string
"/bin/sh"
(parâmetro para a função system)
Exemplo de Cadeia ROP em x64
Convenções de chamada x64 (64 bits)
Utiliza a convenção de chamada System V AMD64 ABI em sistemas semelhantes ao Unix, onde os primeiros seis argumentos inteiros ou ponteiros são passados nos registradores
RDI
,RSI
,RDX
,RCX
,R8
eR9
. Argumentos adicionais são passados na pilha. O valor de retorno é colocado emRAX
.A convenção de chamada do Windows x64 utiliza
RCX
,RDX
,R8
eR9
para os quatro primeiros argumentos inteiros ou ponteiros, com argumentos adicionais passados na pilha. O valor de retorno é colocado emRAX
.Registradores: Os registradores de 64 bits incluem
RAX
,RBX
,RCX
,RDX
,RSI
,RDI
,RBP
,RSP
eR8
aR15
.
Encontrando Gadgets
Para nosso propósito, vamos focar em gadgets que nos permitirão definir o registrador RDI (para passar a string "/bin/sh" como argumento para system()) e então chamar a função system(). Vamos assumir que identificamos os seguintes gadgets:
pop rdi; ret: Desempilha o valor do topo da pilha em RDI e então retorna. Essencial para definir nosso argumento para system().
ret: Um retorno simples, útil para alinhamento de pilha em alguns cenários.
E sabemos o endereço da função system().
Cadeia ROP
Abaixo está um exemplo usando pwntools para configurar e executar uma cadeia ROP com o objetivo de executar system('/bin/sh') em x64:
Neste exemplo:
Utilizamos o gadget
pop rdi; ret
para definirRDI
como o endereço de"/bin/sh"
.Saltamos diretamente para
system()
após definirRDI
, com o endereço desystem()
na cadeia.O
ret_gadget
é usado para alinhamento se o ambiente de destino 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 exigem esse alinhamento. Se a pilha não estiver alinhada corretamente (ou seja, 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 gadget ret antes de chamar system em sua cadeia ROP.
Diferença principal entre x86 e x64
Como x64 usa registradores para os primeiros argumentos, muitas vezes requer menos gadgets do que x86 para chamadas de função simples, mas encontrar e encadear os gadgets corretos 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 do maior espaço de endereçamento na arquitetura x64 oferece tanto oportunidades quanto desafios para o desenvolvimento de exploits, especialmente no contexto da Programação Orientada a Retorno (ROP).
Exemplo de Cadeia ROP em ARM64
Noções Básicas do ARM64 & Convenções de Chamada
Verifique a seguinte página para obter essas informações:
Introduction to ARM64v8Proteções Contra ROP
Canários de Pilha: Em caso de BOF, é necessário ignorar o canário de pilha para sobrescrever os 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 arbitrariamente de uma biblioteca carregada com parâmetros arbitrários (geralmente algo como
system('/bin/sh')
.
Ret2Syscall: Usa ROP para preparar uma chamada a uma syscall, por exemplo,
execve
, e fazê-la executar comandos arbitrários.
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 de EBP (embora também seja necessário controlar o EIP).
Outros Exemplos e Referências
64 bits, Pie e nx habilitados, sem canário, sobrescreva RIP com um endereço
vsyscall
com o único propósito de retornar para o próximo endereço na pilha que será uma sobrescrita parcial do endereço para obter a parte da função que vaza a flagarm64, sem ASLR, gadget ROP para tornar a pilha executável e saltar para shellcode na pilha
Aprenda e pratique Hacking AWS:Treinamento HackTricks AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:
Treinamento HackTricks GCP Red Team Expert (GRTE)
Last updated