Daily AlpacaHack Writeup (2026-03-09 ~ 2026-03-15)
Daily AlpacaHack の Writeup.
(2026/3/9 - 2026/3/15)
後から解いたものなど含むまとめ.
FLAG OVER (Misc, 2026/3/9)
- 環境変数
FLAGが書き換えられた状態のシェルからフラグを読み込む問題 - 環境変数は
/proc/$PID/environに記録されるため, 親プロセスの環境変数を確認できればフラグが獲得できそう - Docker が配布されるので試すと PID の 1 番を参照するとフラグが確認できた
cat /proc/1/environ
- リモートサーバでも同じ方法でフラグ獲得
💯 (Misc, 2026/3/10)
- Python で int に渡したときに 100 として認識される 10 文字以下の文字列を 100 種類以上送ることができればフラグが獲得できる
- 入力は
stripがかけられるため空白などは削除される - Python は
0100など 0 埋めはそのまま解釈される - また最近の Python では数字を 100 などの単位で分けて見やすくするために
1_000のようなアンダーバーを含む記法も使用可能 (ドキュメント) - 先頭に符号 "+" をつけることも可能
- 以上を組み合わせることで 100 以上のパターンを作り出せた
import itertools from pwn import * chars = ['0', '_'] factors = [] factor_set = set() for i in range(8): for s in itertools.product(chars, repeat=i): s = ''.join(s) if not s.startswith('0'): continue if '__' in s: continue t = s + "100" t = t.strip() assert int(t) == 100 and len(t) <= 10.0 and t not in factor_set factors.append(t) factor_set.add(t) if len(t) < 10: t = "+" + t assert int(t) == 100 and len(t) <= 10.0 and t not in factor_set factors.append(t) factor_set.add(t) t = s + "10_0" t = t.strip() assert int(t) == 100 and len(t) <= 10.0 and t not in factor_set factors.append(t) factor_set.add(t) _, host, port = "nc 34.170.146.252 23793".split() sh = remote(host, port) for i in range(100): prompt = sh.recvuntil("100?".encode()) print(prompt.decode()) sh.sendline(factors[i].encode()) sh.interactive()
Find XOR key (Crypto, 2026/3/11)
- フラグに 7 文字の
keyが順番に XOR されている - XOR は二回同じ数字を掛けると元の数字が分かる (
(a ^ b) ^ a == b) Alpaca{で 7 文字のため, 暗号文の先頭 7 文字に既知平文で XOR をかけることでkeyを算出することができる
import string with open("output.txt", "r") as f: c = f.read().strip() key = [] plain = "Alpaca{" for i in range(7): tmp_int = int(c[2*i:2*(i+1)], 16) for char in string.ascii_letters: if chr(tmp_int ^ ord(char)) == plain[i]: print("key found!", ord(char)) key.append(ord(char)) assert len(key) > i, "key not found......" flag = "" for i in range(len(c) // 2): flag += chr(int(c[2*i:2*(i+1)], 16) ^ key[i % 7]) print(flag)
kappa overflow (Pwn, 2026/3/12)
- Windows 用の exe ファイルとソースコードが渡される
- エラーハンドラーらしきものが設定されており, エラーが生じると
win関数が起動する仕組みとなっていた - 入力が
getsで受け取られていたため, 十分長い長さの文字列を送れば BOF が生じると考え以下を送ったところうまくいった
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Another Login Challenge (Web, 2026/3/13)
- Javascript でユーザー名とパスワードを検証するログインフォーム
- 典型的な password にリストを送るなどの方式は
!==で比較されているため難しそう - 以前 Javascript の難読化の問題などで用いられているのを見かけたが, Javascript では
a.ToStringをa["toString"]のように書くことができるらしい - 実際に以下のようなコードが成立する
a = 2; console.log(a.toString()); console.log(a["toString"]());
- これを用いて
user.passwordにundefinedを代入できれば,user.passwordとpasswordがどちらもundefinedで検証をバイパスできると考えられる user.passwordはuserをtoString関数にすることでundefinedになった
let users = { admin: { password: "testpass", }, }; user = users["toString"]; console.log(user); console.log(user.password);
- 以上より
usernameにtoStringを入れてpasswordの項目を削除した状態で POST リクエストを送ることでフラグ獲得できた
Heuristic IPPON (Misc, 2026/3/14)
- 大喜利を回答し IPPON がもらえればフラグが獲得できる
- ソースを読まずに試したところ単語と接続詞などの有無が点数にかかわるらしいと分かったが, フラグは獲得できず
- ソースを読むと評価基準とワードリスト, 長さ指定があるため, それに合う回答ならばクリアできる
フラグを用意し忘れたらしい
flag.txt (Misc, 2026/3/15)
flag.txtがそのまま配布されているため読み込もうとしたが, ファイルサイズが極端に大きいためそのまま読み込むのは難しい- ソースが配られているので読むと, フラグの中身を置くオフセットを決めて, それより前を
., それより後を!で埋めていることが分かる - HTTP では
Rangeヘッダーを指定することでファイルの特定の範囲のみをオフセット指定で読み込むことができる (ダウンロードの中断・再開などで使われているらしい) .か!かで現在のオフセットから見たフラグのオフセットの位置の方向が分かるため, 二分探索でフラグを獲得できる
import requests target_url = "https://flag-txt.chal.alp4ca.com/flag.txt" resp = requests.head(target_url) c_length = int(resp.headers.get("Content-Length").strip()) print("Content-Length:", c_length) binary_itr = c_length // 2 search_range = binary_itr // 2 while True: print("search:", binary_itr, "range:", search_range) headers = {"Range" : f"bytes={binary_itr}-{binary_itr+512}"} resp = requests.get(target_url, headers=headers) if "{" in resp.text or "]" in resp.text: print("hit!:", binary_itr) print(resp.text) break elif "." in resp.text: binary_itr += search_range elif "!" in resp.text: binary_itr -= search_range else: print("some error occured in", binary_itr, search_range) exit() search_range = search_range // 2