はじめに
この記事では、私が 2025 年 12 月 23 日に Daily AlpacaHack で出題した Rotten Beef の解説をします。問題を先に解きたい方は こちら からどうぞ。
問題文
腐った牛肉を復活させてください!
🦙 < フラグは /flag.txt にあるパカ
配布ファイル (main.c)
// gcc -o chal main.c -O0 #include <stdio.h> #include <unistd.h> int main(void) { int key = 0xdead, dummy = 0xdead; char buffer[12]; printf("input > "); scanf("%11s", buffer); printf("Your input: "); printf(buffer, &key, &dummy); // !? printf("\n"); if(key == 0xbeef){ printf("key = 0x%x (dummy = 0x%x), ok!\n", key, dummy); printf("Congratulations! spawning shell...\n"); execve("/bin/sh", NULL, NULL); } else{ printf("key = 0x%x (dummy = 0x%x), try again!\n", key, dummy); } return 0; } __attribute__((constructor)) void setup() { setbuf(stdin, NULL); setbuf(stdout, NULL); }
解説
まず、ソースコードの以下の部分に自明な Format String Bug があります。 (Format String Bug について詳しく知らない方は ptr-yudai さんの記事 を先に読むことを推奨します。)
printf(buffer, &key, &dummy); // !?
このバグを利用して、変数 key の値を 0xbeef = 48879 にする方法を考えます。
都合の良いことに、第一引数に key のアドレスが入っているので、書式指定文字列のうち %n を使えばよいです。
「48879 文字出力させたあと、出力した文字数を第一引数が指すアドレスに書き込む」という処理をさせたいので、%48879c%1$n と入力すればよいです。これは、文字数制限の 11 bytes に収まるので OK です。
なお、%48879c%n と入力した場合、出力した文字数が第二引数が指すアドレスに書き込まれるので、dummy が書き換わってしまいます。
flag: Alpaca{format_str1ng_is_s0_fun}