Zh3r0 CTFに参加したおはなし
はじめに
ctftime.orgにて見つけた、Zh3r0 CTFとやらに、2日目の昼ぐらいから1人でひっそりと参加しました。 結果は、1384ポイントで、810チーム中175位でした。 もうちょっと頑張りたかったなあというお気持ちです。
いろんなジャンルの問題が出題されていたんですが、ほとんどのジャンルの触りの問題しか解けなかったです。つらい。 以下にCTFdのリッチなスコア画面の一部を載せておきます。 16時前から20時ぐらいまでが一番取り組んでたはずなのに、全然スコアが変わってないですね。悲しい。
では、備忘録としてwriteupを書いていきます(welcome系の問題は省きます)。
目次
[toc]
Writeups
RSA-Warmup - Crypt (20)
RSA is one of the first public-key cryptosystems and is widely used for secure data transmission. In such a cryptosystem, the encryption key is public and distinct from the decryption key which is kept secret. You all know this :p here is a warmup question.
nc crypto.zh3r0.ml 8451
Author : Finch
Cryptの超簡単な問題らしいです。
手始めに、与えられたサーバーにアクセスすると、次のように、N
とCT
と、よく見たらe
が与えられます。
$ nc crypto.zh3r0.ml 8451 N: 308051270710135702996040605286818433793711353550617317701193169151276189067532164511643510276134063421796768864465665703584702749269261428789682425289413761516824552560721023024186455596868904214620356109141770431007778797144062315815972973290199066311100079569526397683776312985291516152625811225886351004501626870823 e 65537 CT: 30709473425159171299520825095261005615552655385449912891827878235048571229401267923174369034256520103525411275610842692530767429124293047575582224576587413577905912072168026583801146478806821956101260895155089710384650128939221689021763048961179401991902924891096688884606683625578665778409730072721585153101434625997
RSA - GrinningChicken CTFによれば、N
を素因数分解して、p
とq
が得られれば、d
を求めることができ、それと、N
を使ってCT
を求めることができるようです。
Integer factorization calculatorというサイトで、N
を与えると、p
とq
が得られます。
import binascii from Crypto.PublicKey import RSA from Crypto.Util.number import inverse import gmpy2 N = 360603269950717127958760246163951087043653062932088423467851263183841195982469186986247797462136128745502344222370032221671761738315302847959684763944339067125598238100462217330746123763793945305815944226742458211575982476444545607727592701928013154987794678644639056679851880595959438464365438444242105681976170048233 CT = 42437077913617929464480903272150567089748882677114169372376817944866452865821528116587879624328040402591495026434331016168378827908346491973919641330181046283054065817159232012784632830695669632367600387846791108451794862353062701183232865747649340186603902638878987762345311390914593947979228065750978945968847039156 e = 65537 p = 3945777523 q = 91389660934696628601267503891133876035223454470493830593968656246471602190853979119858197200779210973649804589443912299784207001859213248896517869881012819365081632885682140201787074695086387237706393165731067045661201227448054883080034388935316856937702891021471069563860932510045989854548988580733548286771 d = inverse(e, (p-1)*(q-1)) key = RSA.construct((N, e, d)) print(binascii.unhexlify(hex(key.decrypt(CT))[2:]).decode("utf-8"))
というわけで、以上のようなコードで、フラグが得られます。 コードはSECCON Beginners CTF 2018 - RSA is Power - こんとろーるしーこんとろーるぶいを参考にさせていただきました。
$ python dec.py zh3r0{RSA_1s_Fun}
Web-Warmup - Web (50)
Chall Link : http://web.zh3r0.ml:8080/ Easy peasy.
Author : careless_finch
とりあえず、curl
すると、以下のようなHTMLが得られます。
なんだか構造がすごく気持ち悪いですね。(暴言)
$ curl http://web.zh3r0.ml:8080/ <html> <title>Welcome Here</title> <link rel="stylesheet" type="text/css" href="bg.css"> <h1> Hey. This is just WARM-UP</h1> <input type="hidden" id="one" name="one" value="none"> <img src="warm.jpg" alt="nothing is here " id='selector'>
input
があるし、そこにPOSTするのかなあ、warm.jpg
のステガノかなあ、と思ったりしたのですが、bg.css
を覗くとフラグがありました。
$ curl http://web.zh3r0.ml:8080/bg.css /*css is easy, I think. Don't you?zh3r0{y3s_th1s_1s_w4rmup}*/ #selector { width:100%; height:100%;
Tokens - Web (50)
The flag was sent by Mr.4N0NYM4U5 to my victim. But i dont have the username and password of the victim to login into the discord account. The only thing i have is a god damn token. Can you help me to get the flag. Ill give you the token and it is all you need. Token : NzIyMzM1MTQ5NDA0MTkyODIw.XunLaw.xASADEeu9iXsYf1wqTFOil_jgfo
Author : Mr.4N0NYM4U5
discordのAPIを与えられたTokenを使って叩けみたいな問題でした。 discordにはユーザー用のTokenとbot用のTokenがあるようで、最初はdiscordのbot用のSDK的なsomethingを使って、頑張って調査して、時間を溶かしてしまいました。 まあ普通に問題文を読めば、ユーザー用だよねってことがわかるので、ユーザー用のTokenとしてどう使うかを調査しました。
BurpSuiteを立ち上げながら、ブラウザ版のdiscordにログインしたり、適当な操作をやったあと、一通り通信を眺めてみました。
結果的に、Authorization
ヘッダにTokenを含めて/api/v6/
以下のエンドポイントを叩いていることがわかりました。
とりあえず、そのへんにあった/api/v6/users/@me/guilds
というRequestのTokenを書き換えて投げてみると、フラグが得られました。
(あとがき)writeupを書きながら検証していると、上記と同じ手順で解けなかったので、おそらく想定解ではなかった?のか、discord内の何かが変わった可能性があります。writeup執筆時点での、うまくいった手順は以下のとおりです。 APIのドキュメントはこちらを参考にしました。
/api/v6/users/@me/channels
にTokenを含めてGETリクエスト- レスポンスに見に行くべきchannelのidが含まれている
[ { "id":"722336834264498177", "last_message_id":"722774529239285781", "last_pin_timestamp":"2020-06-16T20:48:55.080000+00:00", "type":1, "recipients":[ { "id":"402012560553148426", "username":"Mr.4N0NYM4U5", "avatar":"c5c89aac100ab60a58dadda34a3797c5", "discriminator":"2435", "public_flags":0 } ] } ]
/api/v6/channels/722336834264498177/messages
にTokenを含めてGETリクエスト- レスポンスにフラグらしき文字列が含まれている
[ ...(中略) { "id":"722338901221703732", "type":0, "content":"`flag : zh3r0{1et_7he_F0rce_8e_With_YoU}`", "channel_id":"722336834264498177", "author":{ "id":"402012560553148426", "username":"Mr.4N0NYM4U5", "avatar":"c5c89aac100ab60a58dadda34a3797c5", "discriminator":"2435", "public_flags":0 }, ...(中略) } ]
Katycat - Forensics (175)
katycat trying to find the flag but she is lazy. will you help her to find the flag?
Author : cryptonic007
猫の写った、png画像が与えられます。 とりあえず、stringsや、binwalk、exiftoolsをかけてみましたが、めぼしいヒントは得られず。 次に、青空白猫に入れて、ステガノグラフィー解析機能で、次候補ボタンをポチポチ押していました。 すると、ビット0抽出で、左上に、黒い点の列が見えたので、LSBにデータが隠されていることがわかりました。
というわけで、同ソフトウェアのビット抽出機能を使って、RGBそれぞれのLSB(0ビット目)のみ抽出して、データを表示してみると、https://pastebin.com/hvgCXNcP
というURLが得られました。
サイトに行くと、Base64エンコードされたような、文字列が保存されていたので、CyberChefに入れて、Base64 Decodeすると、出力されたバイナリの先頭にPK
、中間にflag.txt
という文字列が存在することがわかりました。
PK
はzipファイルのマジックナンバーなので、zipファイルのバイナリデータであることがわかりました。
以下のコマンドで、zipファイルとしてダウンロードします。
$ curl -s https://pastebin.com/raw/hvgCXNcP | base64 -D > out.zip
しかし、解凍しようとすると、パスワードがかかっていることがわかります。
$ unzip out.zip Archive: out.zip [out.zip] flag.txt password:
zip2john
を使ってパスワードハッシュを求め、john
でそのハッシュを使って、パスワードをcrackします。
$ zip2john out.zip > test.hash ver 1.0 efh 5455 efh 7875 out.zip/flag.txt PKZIP Encr: 2b chk, TS_chk, cmplen=37, decmplen=25, crc=BCC1DEEE $ john test.hash ...(前略) kitkat (out.zip/flag.txt) ...(後略)
上記パスワードを使って、unzip
してflag.txt
の中を見てみると、暗号化されたような文字列がまた現れます。
$ unzip out.zip Archive: out.zip [out.zip] flag.txt password: extracting: flag.txt ushiken:Katycat user$ cat flag.txt K9bC_L`D?f0DEb8c?_06cDJN
一応ASCIIに収まっていることから、ROTかな?という軽い推測をして、CyberChefに再度入力してみると、ROT13ではなく、ROT47でフラグを複合できました。
zh3r0{1sn7_st3g4n0_e4sy}
snakes everywhere - Reversing (357)
Can a snake go backwards
Author : Whit3_D3vi1
py_dis1
とsnake.txt
という2つのファイルが与えられます。
py_dis1
には、以下のような文字列が入っており、snake.txt
には数十バイトのバイナリデータが入っていました。
$ head -20 py_dis1 2 0 LOAD_CONST 0 (0) 2 LOAD_CONST 1 (('flag1',)) 4 IMPORT_NAME 0 (flags) 6 IMPORT_FROM 1 (flag1) 8 STORE_NAME 1 (flag1) 10 POP_TOP 3 12 LOAD_NAME 1 (flag1) 14 STORE_NAME 2 (flag) 4 16 LOAD_CONST 2 ('zh3r0{fake flag}') 18 STORE_NAME 3 (fake_flag) 6 20 LOAD_CONST 3 ('I_l0v3_r3v3r51ng') 22 STORE_NAME 4 (key) 8 24 LOAD_NAME 5 (len) 26 LOAD_NAME 2 (flag) 28 CALL_FUNCTION 1 30 LOAD_CONST 4 (38) ...(後略)
py dis
でggると、dis --- Python バイトコードの逆アセンブラ — Python 3.8.3 ドキュメントというサイトが出てきました。
pythonのbytecodeを逆アセンブルすることができるらしいです。
早速インストールして使ってみました。
$ pip install dis $ echo "a=1;print(a)" | python -m dis 1 0 LOAD_CONST 0 (1) 2 STORE_NAME 0 (a) 4 LOAD_NAME 1 (print) 6 LOAD_NAME 0 (a) 8 CALL_FUNCTION 1 10 POP_TOP 12 LOAD_CONST 1 (None) 14 RETURN_VALUE
1列目が、入力されたコードの行番号、2列目がバイトコードの位置、3列目が人間が読める形式のオペコード(命令)、4列目は命令が参照する内部変数のインデックス、5列目がオペランド(被演算子)のようです。
内部変数には、co_consts
, co_names
, co_varnames
などのタプルがあり、こちらに変数名などを保持しているそうです(参考: inspect --- 活動中のオブジェクトの情報を取得する — Python 3.8.3 ドキュメント")。
このコードを簡単に解説します。
コードでは、最初にLOAD_CONST (1)
で1
がスタックに積まれて、STORE_NAME (a)
でa
にスタックのトップにある1
をストアしています(a=1;
)。
次に、LOAD_NAME (print)
とLOAD_NAME (a)
をそれぞれスタックに積み、CALL_FUNCTION
でスタックのトップを引数、その次を関数として呼び出します(print(a)
)。
最後に、プログラムの終了処理を行っています。
dis
をつかった解析は、writeup執筆中に見つけた、dis/inspect モジュールを使った Python のハッキングというサイトに詳しく日本語で書いてありました。
以上のような形で、逆アセンブル結果から、元のコードを(無理やり)復元できることがわかったので、復元してみた結果が、以下のコードです。
このコードを$ python -m dis rev1.py
とすると、だいたい元の逆アセンブル結果と同じ結果が得られます。
from flags import flag1 flag = flag1 fake_flag = "zh3r0{fake flag}" key = "I_l0v3_r3v3r51ng" if not len(flag) == 38: raise AssertionError def xor(str1, str2): return chr(ord(str1) ^ ord(str2)) ciphertext = '' for i in range(len(flag)//3): ciphertext += chr(ord(key[i])*ord(flag[i])-i) for i in range(len(flag)//3, len(flag)//3*2): ciphertext += chr(ord(flag[i])*ord(key[i % len(key)])+i) for i in range(len(key)//2, len(flag)): ciphertext += xor(key[i % 16], flag[i]) file = open('ciphertext.txt', 'w') print(len(ciphertext)) file.write(ciphertext) file.close()
このコードから、以下のことがわかります。
flags.py
のflag1
という変数にフラグが格納されている- フラグの文字列長は38文字
I_l0v3_r3v3r51ng
という文字列を鍵として、フラグを暗号化しているciphertext.txt
というファイルに暗号化されたフラグが格納されている- おそらく、
ciphertext.txt
とsnake.txt
は同じもの
以上のことから、このコードからさらに、暗号化されたフラグを復号するコードを書く必要があることがわかります。 ということで、復号するコードが以下のコードです。
from rev1 import xor FLAG_LEN = 38 key = "I_l0v3_r3v3r51ng" with open('snake.txt', 'r') as f: ciphertext = f.read() flag = ["A" for _ in range(FLAG_LEN)] for i in range(FLAG_LEN//3): flag[i] = chr(int((ord(ciphertext[i])+i)/ord(key[i]))) for i in range(FLAG_LEN//3, FLAG_LEN//3*2): flag[i] = chr(int((ord(ciphertext[i])-i)/ord(key[i % len(key)]))) lev3_offset = FLAG_LEN//3*2 c = 0 for i in range(len(key)//2, FLAG_LEN): flag[i] = xor(key[i % 16], ciphertext[c+lev3_offset]) c += 1 print("".join(flag))
ここでは、次の4つの法則性を使って、復号コードを作成しました。
chr(ord('A')) => 'A'
ord(chr(0x41)) => 0x41
c = a ^ b = b ^ a
a = b ^ c
というわけで、このコードを使って復号すると、フラグが得られます。
$ python dec.py 54 zh3r0{Python_disass3mbly_is v3ry_E4sy}
Subset of subset of hacking machines challenges
Description: Here's the url ;)
hackit.zh3r0.ml
Author : Mr.Holmes
hackthebox的な問題で、1つのマシンをターゲットにフラグを探す問題になっていて、全部で6つのフラグが隠されていました。 そのうちの3つを見つけたので、それぞれの解法をスコアの低い順に書いていきます。
Flag 5 (50)
手始めに、nmap -sV hackit.zh3r0.ml
で、ポートスキャンをしたところ、tcpの22番と99番がopenになっていることが確認されました。
22番はhttpらしきサービスが、99番はOpenSSH 7.6p1
が立っているようでした。
まずは、httpから見ていこうと思い、Firefoxでhttp://hackit.zh3r0.ml:22/
にアクセスすると、このアドレスへの接続は制限されています
と言われてアクセスできなかったので、こちらを参考に以下の手順で設定を追加しました。
about:config
をURLバーに打ち込み危険性を承知の上で使用する
ボタンをクリック- 検索バーに
network.security.ports.banned.override
と打ち込み、文字列
にチェックを入れて、+
ボタンをクリック - 値を打ち込めるので、
22
と入力し、チェックボタンをクリック
以上の手順のあと、もう一度http://hackit.zh3r0.ml:22/
にアクセスしてみると、z3hr0{shouldve_added_some_filter_here}
というフラグが得られました。
Flag 1 (227)
http://hackit.zh3r0.ml:22/
に対して、dirb
というブルートフォースでwebサービス上のディレクトリやファイルを探すコマンドを使ってみた結果、robots.txt
が存在することがわかったため、curl
でアクセスしてみました。
$ curl http://hackit.zh3r0.ml:22/robots.txt Hmmm you shouldnt be here.... 2yryYz3sx16kWt72agdFZJqxZ5kqqvocsnU416bULtVs63Cvwmvr6f34Ck8sSHQU1PPnQqD7bqW9q By the way did you see any ads on this thing?
暗号化されたような文字列を得られたので、CyberChefにて、Baseなんちゃら系で復号していると、Base58で復号できることがわかりました。
I tried to hide this, anyway, check out /clue3349203.txt
復号された文字列によると、/clue3349203.txt
に次のヒントが隠されているようだったので、curl
にて再びアクセスしてみました。
$ curl http://hackit.zh3r0.ml:22/clue3349203.txt ([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+...(後略)
JSFuckと呼ばれる、javascriptのbrainfuck言語のような文字列が与えられたので、JsUnFuckというサイトで、デコードしてみた結果、console.log('Employee ID: 865151c643cbbb7e3bf4fd5dbb71354e')
という文字列が得られました。
しかし、これを使える場所がまだ見つかっていなかったので、他を当たることにしました。
その後、99番ポートのSSHに対して、UsernameEnumerationしていましたが、途中のアナウンスで、
:TIME_SAVING_TIP: SSH is out of scope. Bruteforcing SSH on hacking-machine is not required. Allowed? Yes. But while setting passwords I made sure they are not in any publicly available wordlist.
と言われていることに気づきました。
そして、Well-knownポートしかスキャンしていないことに、途中で気づいて、全ポートスキャンをしました(その間にラーメン花月の油そばを食べに行ってきました)。
全ポートスキャンをした結果、新たにtcpの324番ポートにvsftpd 3.0.3
と4994番ポートにunknownなサービスが立ち上がっていることがわかりました。
とりあえず、ftpの調査をしたところ、以下の通り煽られてしまいました。
$ ftp hackit.zh3r0.ml 324 Connected to hackit.zh3r0.ml. 220 (vsFTPd 3.0.3) Name (hackit.zh3r0.ml:user): anonymous 331 Please specify the password. Password: 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. ftp> ls -l 229 Entering Extended Passive Mode (|||51696|) 150 Here comes the directory listing. -rw-r--r-- 1 ftp ftp 22 Jun 16 23:45 test.txt 226 Directory send OK. ftp> get test.txt local: test.txt remote: test.txt 229 Entering Extended Passive Mode (|||23036|) 150 Opening BINARY mode data connection for test.txt (22 bytes). 100% |****************************************************************************************************************************************| 22 41.71 KiB/s 00:00 ETA 226 Transfer complete. 22 bytes received in 00:00 (0.12 KiB/s) ftp> ^D 221 Goodbye. $ cat test.txt LOL Nothing here. ;-;
次に、4994番ポートにcurl
でアクセスしてみたところ、以下のような出力でフラグが得られました。
しかし、curl
コマンドが終了しませんでした。
$ curl http://hackit.zh3r0.ml:4994/ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ||Employee Entry|| ---------------------------------------------------------- Sherlock Holmes Inc. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here's a free flag for you, just for finding this door! Flag 1: zh3r0{pr05_d0_full_sc4n5} Heyo, Watcha looking at? Employee ID yoo! : Go away kiddo, huh, Kids these days!
Flag 3 (445)
curl
コマンドが終了しなかったことから、4994番ポートはhttpではなく、なんらかのプログラムが動いているのではないかと気づき、nc
コマンドにて再度アクセスしてみたところ、標準入力で何かを入力することができました。
このメッセージから、Employee ID
を入力しろと言われているので、先に得られたidを入力したところ、フラグを得ることができました。
$ nc hackit.zh3r0.ml 4994 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ||Employee Entry|| ---------------------------------------------------------- Sherlock Holmes Inc. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here's a free flag for you, just for finding this door! Flag 1: zh3r0{pr05_d0_full_sc4n5} Heyo, Watcha looking at? Employee ID yoo! : 865151c643cbbb7e3bf4fd5dbb71354e Hey I know you! You work here! If you don't like to read, you haven't found the right book. - JK Rowling Flag 3: zh3r0{y0ur_b0nu5_i5_p4id}
おわりに
ほとんど、初級の問題しか解けなかったので、もっと精進したい(いつも言ってる気がする)。 いろいろ知らなかった技術とか、忘れてたことを思い出せたので、よかったです。 あと7日間(2020/06/25まで?)はサーバーが動いているらしいので、writeupを読みながら解けなかった問題を解いていきたいです。