この記事は SecHack365 Advent Calendar 2022 の4日目の記事です。
参加記は 鮮度が命 最上川
— task4233 (@task4233) August 12, 2022
ということで、今終わったところではありますが、2022-12-03T00:00+09:00 から24時間開催された、taskctf に参加しました。
(SecHack365 修了生の開催した CTF に SecHack365 現役生が参加したということで、アドベントカレンダーにねじこんでおきます。)
nyaarch64として参加して全完、成績は136人中3位でした。
taskctf22は終了しました!
— task4233 (@task4233) December 3, 2022
ご参加いただき、ありがとうございました。
今年は全完者が9名出ました。おめでとうございます🎉
今後の改善のために、アンケート未回答の方はご回答お願いいたします🙇♂️
→ https://t.co/9eXYMH9AZk#taskctf pic.twitter.com/IAP0PYgIb6
知ったきっかけ
SecHack365 で使用しているチャットツール TypeTalk の雑談トピックで、アシスタントの方が taskctf のツイートを共有されているのを見て知りました。
正直生活リズムを頑張って直していた時期だったこともあり、一瞬迷いましたが、誕生日イベントということもあり、仕方がないので参加することにしました。
それでは、解けた問題の writeup、ついでに incorrect だった提出を書いていきます。
tutorial
submit_flag
問題文に flag が書かれているので、そのまま提出します。
flag: taskctf{th1s_1s_f14g}
just_google_it
ググれという圧を華麗に受け流しつつ、base64 デコードしてみます。
すると、大量の Hello!
という文字列が見えます。
邪魔なので、sed で消します。
$ cat base64_encoded.txt | base64 -d | sed 's/Hello! //g'
taskctf{Y0u_n0w_know_base64!}
表示された flag を提出します。
try_python
Python を使えという圧を華麗に受け流しつつ、bc に食わせてあげます。
テキストは数値がスペース区切りになっているため、スペースを +
に置換してあげるとよさそうですが、ファイル末尾にもスペースがあるため、最後の1文字だけ取り除いてあげます。
echo 使ってるのは、そのままパイプで渡すとエラーになってしまったためです。最後に改行がないからだと思います。
$ echo $(cat numbers.txt | sed 's/ /+/g' | head -c-1) | bc
250000
表示された数値を taskctf{}
で囲んで提出します。
flag: taskctf{250000}
build_docker_environment
心の中で task4233 を信頼できない気持ちがあるので、すぐに指示通りに docker コンテナを起動するのではなく、一旦ダウンロードしたファイルを確認します。
すると、.env
ファイルに flag のようなものが見えます。
FLAG=taskctf{D1d_y0u_run_d0cker_c0nta1ner?}
早とちりでこれを提出して incorrect だと悲しいので、他のファイルにも目を通します。
すると、app/app.py
で app/templates/index.html
に flag を埋め込んで表示しているだけに見えます。
どこかに JavaScript がひそんでいるかを調べるよりも実行しちゃった方が早いなと思い、docker compose up -d
を実行して、localhost:31777
にアクセスします。
表示された (さっき見た) flag を提出します。
flag: taskctf{D1d_y0u_run_d0cker_c0nta1ner?}
osint
welcome
ググったら st98 さんの writeup が出てくるので、その中に書いてある flag をそのまま提出します。
flag: taskctf{let's_enj0y!}
ramen
Google Lens を使うと、すぐにそれっぽいラーメン屋がみつかります。
見つけたら、flag にどこまで含めるか guess します。
例からいい感じに短い感じにして提出してみます。合ってた。
flag: taskctf{蝋燭屋}
kofun
一旦 Google Lens に投げてみますが、うまく出てきません。
task4233 古墳
で Google 検索すると、問題文に書いてある ツイート が出てきます。
友達と古墳を20基程度回るなどした pic.twitter.com/ZqE77AKNlY
— task4233 (@task4233) August 14, 2022
ツイートには、添付されている写真以外にもう1枚写真が添付されているため、こちらを Google Lens に投げてみますが、これもうまく見つけることができません。
そこで、古墳 はにわ
で Google 画像検索してみると、似たような古墳が成田空港の近くに存在することが分かります。
(ちなみに、ここで早とちりして芝山古墳で出して incorrect になっています。)
ツイートを見ると、いくつもの古墳を回ったと書いてありますが、このすぐ近くにはそこまで多くの古墳はなさそうです。
そこで、もう少し広いエリアの地図で古墳を探してみますが、ピンと来るものはありません。
諦めつつ 成田 古墳 扉
で Google 画像検索すると、それっぽい柵のある古墳の写真が見つかります。
これを flag の形式に当てはめて提出してみると、合っていました。
flag: taskctf{上福田岩屋古墳}
douro
標識が英語っぽいので、英語圏だなとなります。
設置されている標識のテキスト RIGHT TURN PERMITTED WITHOUT STOPPING
で Google 画像検索して、デザインが似ているものを探してみると、Portland のもの (pdf) が見つかります。
画像からは Culver P
らしき文字も読み取れるので、Portland 周辺で Culver で検索してみますが、それらしいものは見つかりません。
標識の下部には人名のようなものが記載されています。読み取れる WITHERS
と MATHEIS
で Google 画像検索してみると、同じものの画像 が出てきます。
それによると Irvine Ranch Water District
と書いてあります。検索してみると Los Angeles 周辺にあるようです。
この周辺で Culver で検索してみると、Culver Plaza
というものがあり、画像に写っているサインも見つかります。
あとは Google Map で座標 を入手して、問題文の flag 形式を よく見て 提出するだけです。flag format で incorrect を2つ増やしました (カンマ)。悔しい。
flag: taskctf{33.693_-117.798}
web
robots
robot といえば robots.txt
なので、アクセスしてみると Disallow: /admin/flag
が見えます。
あまりにも怪しいので /admin/flag
にアクセスしてみると、401 Unauthorized
と表示され、IP アドレスでアクセス制限されていることが分かります。
まずはレスポンスヘッダーを見て、web サーバーソフトのバージョンを見てみると、Server: nginx/1.23.2
となっていて、現時点の最新版であることが分かります。
使えそうな脆弱性がなさそうなことが分かったので、設定ミスを攻撃することを試みます。
こういう場合に真っ先に思い受かぶのは X-Forwarded-For
ヘッダーで、サーバーがおそらく docker で実行されていることを推測して、X-Forwarded-For: 172.17.0.1
を試してみます。
すると、表示される IP アドレスがヘッダーで与えた値に変化することが分かります。この方法でいけそうですが、この IP アドレスではダメなようです。
あとは一通り 192.168.0.0/16
, 10.0.0.0/8
, 172.16.0.0/12
アドレスブロックあたりを適当に試しますが、どれもうまくいきません。
ちょっと「本当かな?」と思いながら 127.0.0.1
を試すとうまくいきました。
表示された flag を提出して終わりです。
$ curl -i http://34.82.208.2:31481/admin/flag -H 'X-Forwarded-For: 127.0.0.1'
HTTP/1.1 200 OK
Server: nginx/1.23.2
Date: Fri, 02 Dec 2022 16:42:37 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 1072
Connection: keep-alive
<html>
<head>
<title>robots</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.7/dist/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<h1>flag</h1>
<p>taskctf{th15_c0ntr0l_y0u_th1nk_y0u_h4ve_1s_4n_1llu5i0n}</p>
</div>
</body>
</html>
flag: taskctf{th15_c0ntr0l_y0u_th1nk_y0u_h4ve_1s_4n_1llu5i0n}
first
こういうのはだいたい SQLi です。コードを見ます。SQLi です。
multiple statement と OUTER JOIN には対応していないようです。ちょっと面倒ですね。
Blind SQLi まではする必要ないよな……面倒だし……と思いながら UNION SELECT を試しますが、500エラーになります。
より詳しい情報を得るために、頑張ってローカルで適当にデータを投入してテスト環境を構築してみると、どうやら UNION SELECT は使えていて、エラーメッセージが表示されていないだけな気がしました。
うまくデータの型を合わせて UNION SELECT してみると、データが表示されました。
あとは、posts の表示が邪魔なので、始めの SELECT 文の条件が常に false になるようにして (1=0
)、UNION SELECT
で取りたいデータを取得します。
users.id は UUIDv7 になっているようです。これは時間順にソートが可能な UUID なので、そのまま ORDER BY id ASC
します。
そして、知りたいのは100番目に登録したユーザーなので、先頭99人を飛ばして、そこから1人だけ取得するために LIMIT 99, 1
します。
つまり、最終的に入力したのは以下のクエリです。
' AND 1=0 UNION SELECT 1, name, id FROM users ORDER BY id ASC LIMIT 99, 1; --
あとは出てきたユーザー名を flag format に従って提出します。
flag: taskctf{Satomi_Kato}
……まあ、posts の100番目のユーザーを提出して一度incorrectになっているのですが……。
misc
ransomeware
base64 デコードすると、1文字ずつ ord して、key と xor して chr で戻しているようです。
key を取得している C2 サーバーにはアクセスできませんが、全ての文字に同じ key で xor しているので、一部を適当に推測してみます。
まず、暗号化されたテキストはきれいな UTF-8 に見えます。そして、全ての文字が 3Bytes となっています。
ASCII や他のエンコードだと、きれいに 3Bytes ごとに特徴的な繰り返しになることは珍しいため、元々 UTF-8 の 3Bytes 文字であることが推測されます。
また、flag format から、全角の英数字であると推測できます。
試しに taskctf
と暗号化されたテキストの各文字の ord を xor してみると、どの文字も 47692
となっているようでした。
あとは、これを key として復号してみると、一部が半角カナになってしまった flag のようなものが得られます。
ここからは guess で、アンダーバーな気がするのでアンダーバーに置換してみて、uconv
で半角に変換して flag を得ます。
$ python
Python 3.10.8 (main, Nov 1 2022, 14:18:21) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> with open('./taskctf_flag.txt.encrypted', 'r') as f:
... data = f.read()
...
>>> data
'䔘䔍䔟䔇䔏䔘䔊䔗䔔䕜䔞䔳䕝䔟䔳䔉䕘䔟䔕䔳䕛䕜䔳䕝䔁䔜䔀䔉䔁䔉䔂䕛䔑䕦'
>>> for ch in data:
... print(chr(ord(ch) ^ 47692))
...
t
a
s
k
c
t
f
{
x
0
r
ソ
1
s
ソ
e
4
s
y
ソ
7
0
ソ
1
m
p
l
e
m
e
n
7
}
J
>>> ^D
$ echo 't
a
s
k
c
t
f
{
x
0
r
ソ
1
s
ソ
e
4
s
y
ソ
7
0
ソ
1
m
p
l
e
m
e
n
7
}' | tr -d '\n' | sed 's/ソ/_/g' | uconv -x Fullwidth-Halfwidth
taskctf{x0r_1s_e4sy_70_1mplemen7}
提出してみて……、合っていたようです。よかった。
flag: taskctf{x0r_1s_e4sy_70_1mplemen7}
shellgei
あー、JavaScript とかでよく見る記号プログラミングですね、分かります。
軽く検索してみると、情報は少ないものの、頑張ればできそう なことが分かります。
まあ、必要なコマンド (cat flag.txt
) を ASCII テーブルから8進数表記に変換して、それをうまく記号だけで表現して、bash に与えると実行されるように頑張ります。
しかし、どうやら提出したテキストは直接 bash に与えられるのではなく、一旦ファイルに保存された後に実行されるようでした。
仕方がないので頑張って数字から bash を錬成して実行します。
あとは表示された flag を提出するだけです。
flag: taskctf{I_g0tt3_6e_re4l_w1th_y0u}
全完した後、時間があったので、適当にカレントディレクトリに Happy birthday
とテキストファイルに吐くものも実行しておきました。あと、気力があったら bash 記号プログラミングは別で記事書きます。
anti_detection
これはね、分かりますよ。なんか yara ルールみたいな感じでいい感じにこのバイナリを検知するようなものが裏で動いていて、それをうまく回避しつつ同じ動作をするバイナリを作ればいいんですね。はー面倒。。。
まずはどのへんを見て検知しているのか知るために、適当に hexedit で開いてファイルを壊していきます。まあ strings で引っ掛かるところとか main 関数とかが怪しい気がするので、そのへんを適当に潰しながら提出してみますが、うまく検知パターンが分かりそうにありません。
あと、当然ですが、壊しまくって検知されないファイルはそのまま実行されて500エラーになります。
じゃあまあとりあえず shell script 投げとくか……。ということで、objdump -d
の結果を見ながら適当に shell script を書きます。
#!/bin/bash
echo Happy birthday > message.txt
echo -n flag: $(cat flag.txt)
通った。あとは表示された flag を提出するだけです (サーバーにメッセージも添えつつ)。
flag: taskctf{p0werfu1_fuzzy_h4sh}
追記:
メッセージ、派手にやってる人がいますね。
アンケート
ごめん、あんまりヒント見てないです。あとで公開されたら見ます。
おわりに
bash4233 か?となっていました。
楽しかったです。また次回もあれば参加すると思います。ありがとうございました。ねむい。
— task4233 (@task4233) December 3, 2022
今日はたっぷり寝ます。おやすみなさい。