memory_leakage

A struct read back without clearing it first leaks adjacent heap memory field by field.

2026.08.18 Pwn original post
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

FILE *fp;

struct my_page {
    char name[16];
    int age;
};

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

int main() {
    struct my_page my_page;
    char flag_buf[56];
    int idx;

    memset(flag_buf, 0, sizeof(flag_buf));
    initialize();

    while (1) {
        printf("1. Join\n");
        printf("2. Print information\n");
        printf("3. GIVE ME FLAG!\n");
        printf("> ");
        scanf("%d", &idx);

        switch (idx) {
            case 1:
                printf("Name: ");
                read(0, my_page.name, sizeof(my_page.name));
                printf("Age: ");
                scanf("%d", &my_page.age);
                break;
            case 2:
                printf("Name: %s\n", my_page.name);
                printf("Age: %d\n", my_page.age);
                break;
            case 3:
                fp = fopen("/flag", "r");
                fread(flag_buf, 1, 56, fp);
                break;
            default:
                break;
        }
    }
}

핵심 취약점은 여기임.

read(0, my_page.name, sizeof(my_page.name));

my_page.name 크기는 16바이트인데, read() 는 널 바이트 \0 를 자동으로 붙여주지 않음. 그런데 출력할 때는 이렇게 함.

printf("Name: %s\n", my_page.name);

%s 는 문자열 끝을 만날 때까지 계속 출력해. 즉, my_page.name 뒤에 \0 이 없으면 스택 뒤쪽 메모리까지 계속 읽어서 출력할 수 있음.

공격 흐름

중요한 건 먼저 flag 를 메모리에 올려야 해.

case 3:
    fp = fopen("/flag", "r");
    fread(flag_buf, 1, 56, fp);
    break;

3번을 누르면 /flag 내용이 flag_buf 에 들어감. 하지만 직접 출력해주지는 않음. 그다음 1번 Join 에서 이름을 16바이트 꽉 채워서 입력하면 \0 이 안 붙음. 그 후 2번 Print information 을 선택하면,

printf("Name: %s\n", my_page.name);

때문에 my_page.name 부터 시작해서 뒤쪽 스택 메모리를 \0 이 나올 때까지 계속 출력함. 그 과정에서 flag_buf 안의 flag 가 같이 새어 나올 수 있음.

그래서 입력은:

> 3
> 1
Name: AAAAAAAAAAAAAAAA
Age: 1094795585
> 2

여기서 age 값이 궁금할 텐데, age 값을 널 바이트가 없는 값으로 넣어야만 한다. (입력은 따로 받아도, 메모리에서는 name 바로 뒤에 age 가 붙어 있음) 구조체가 이렇게 생겼지.

struct my_page {
    char name[16];
    int age;
};

그러면 메모리 배치는 보통 이렇게 돼.

offset 0x00 ~ 0x0f : name[16]
offset 0x10 ~ 0x13 : age

예를 들어 1234는 메모리에 이런 식으로 저장될 수 있어.

d2 04 00 00

여기 안에 00 널 바이트가 들어가면 %s 출력이 거기서 끊길 수 있어. 예를 들어 1094795585 이 값은 hex 로 0x41414141, 메모리에 “AAAA”처럼 들어가서 중간에 \0 이 없음. 그래서 더 나은 값은 1094795585 임.