Ret2esp / Ret2reg
Ret2esp
Porque o ESP (Ponteiro de Pilha) sempre aponta para o topo da pilha, essa técnica envolve substituir o EIP (Ponteiro de Instrução) pelo endereço de uma instrução jmp esp
ou call esp
. Ao fazer isso, o shellcode é colocado imediatamente após o EIP sobrescrito. Quando a instrução ret
é executada, o ESP aponta para o próximo endereço, precisamente onde o shellcode está armazenado.
Se o Address Space Layout Randomization (ASLR) não estiver ativado no Windows ou Linux, é possível usar as instruções jmp esp
ou call esp
encontradas em bibliotecas compartilhadas. No entanto, com o ASLR ativo, pode ser necessário procurar essas instruções dentro do programa vulnerável em si (e pode ser necessário derrotar o PIE).
Além disso, ser capaz de colocar o shellcode após a corrupção do EIP, em vez de no meio da pilha, garante que quaisquer instruções push
ou pop
executadas durante a operação da função não interfiram no shellcode. Essa interferência poderia ocorrer se o shellcode fosse colocado no meio da pilha da função.
Espaço insuficiente
Se você estiver com espaço insuficiente para escrever após sobrescrever o RIP (talvez apenas alguns bytes), escreva um shellcode jmp
inicial como:
E escreva o shellcode no início da pilha.
Exemplo
Você pode encontrar um exemplo dessa técnica em https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp com um exploit final como:
Você pode ver outro exemplo dessa técnica em https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html. Há um estouro de buffer sem NX habilitado, é usado um gadget para reduzir o endereço de $esp
e então um jmp esp;
para pular para o shellcode:
Ret2reg
Da mesma forma, se soubermos que uma função retorna o endereço onde o shellcode está armazenado, podemos aproveitar as instruções call eax
ou jmp eax
(conhecidas como técnica ret2eax), oferecendo outro método para executar nosso shellcode. Assim como eax, qualquer outro registro contendo um endereço interessante poderia ser usado (ret2reg).
Exemplo
Você pode encontrar alguns exemplos aqui:
strcpy
irá armazenar emeax
o endereço do buffer onde o shellcode foi armazenado eeax
não está sendo sobrescrito, então é possível usar umret2eax
.
ARM64
Ret2sp
No ARM64, não existem instruções que permitem saltar para o registro SP. Pode ser possível encontrar um gadget que move sp para um registro e então salta para esse registro, mas na libc do meu kali não consegui encontrar nenhum gadget assim:
Os únicos que descobri mudariam o valor do registro onde sp foi copiado antes de pular para ele (então se tornaria inútil):
Ret2reg
Se um registro tiver um endereço interessante, é possível pular para ele apenas encontrando a instrução adequada. Você poderia usar algo como:
No ARM64, é o x0
que armazena o valor de retorno de uma função, então poderia ser que x0 armazene o endereço de um buffer controlado pelo usuário com um shellcode para executar.
Código de exemplo:
Verificando a desmontagem da função, é possível ver que o endereço do buffer (vulnerável a bof e controlado pelo usuário) é armazenado em x0
antes de retornar do estouro de buffer:
Também é possível encontrar o gadget br x0
na função do_stuff
:
Vamos usar esse gadget para pular para ele porque o binário é compilado SEM PIE. Usando um padrão, é possível ver que o deslocamento do estouro de buffer é 80, então o exploit seria:
Se em vez de fgets
fosse usado algo como read
, teria sido possível contornar o PIE também apenas sobrescrevendo os últimos 2 bytes do endereço de retorno para retornar à instrução br x0;
sem precisar saber o endereço completo.
Com fgets
não funciona porque adiciona um byte nulo (0x00) no final.
Protections
NX: Se a pilha não for executável, isso não ajudará, pois precisamos colocar o shellcode na pilha e pular para executá-lo.
References
Last updated