memory_leakage
A struct read back without clearing it first leaks adjacent heap memory field by field.
#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 임.