aupCTF Writeup

この大会は2023/6/24 14:00(JST)~2023/6/26 2:00(JST)に開催されました。
この大会は個人戦。結果は690点でした。順位や参加者数は不明です。
自分で解けた問題をWriteupとして書いておきます。

Sanity check (Misc)

ルールにフラグフォーマットが書いてあった。

aupCTF{5an1ty-ch3ck}

Fun (Misc)

scriptタグで挟み、ブラウザで開くと、フラグがポップアップされて表示された。

aupCTF{j4v45c1pt_but_f*ck3d}

Zoo (Misc)

YouTubeに最初にアップロードされた日時をEpoch Timestampで答える問題。
Googleで調べると、以下のページが見つかる。

https://en.wikipedia.org/wiki/Me_at_the_zoo

YouTubeに初めてアップロードされた日時は以下の通り。

April 24, 2005, at 03:31:52 UTC

Epoch Timestampに変換する。

1114313512
aupCTF{1114313512}

The Circle Of Life (Misc)

GCodeで書かれているようだ。以下のURLのオンラインのNC viewerで開くと、フラグが見える。

https://ncviewer.com/

aupCTF{Ti3_i3_fu9_rig4ht}

Starter (Web)

HTMLソースをを見ると、以下のようになっている。

            <div class="word">
                <span id="letter1">a</span>
                <span id="letter2">u</span>
                <span id="letter3">p</span>
                <span id="letter4">C</span>
                <span id="letter5">T</span>
                <span id="letter6">F</span>
                <span id="letter7">{</span>
            </div>
            <div class="word">
                <span id="letter8">w</span>
                <span id="letter9">4</span>
                <span id="letter10">5</span>
                <span id="letter11">n</span>
                <span id="letter12">'</span>
                <span id="letter13">t</span>
                <span id="letter14">-</span>
                <span id="letter15">t</span>
                <span id="letter16">h</span>
                <span id="letter17">4</span>
                <span id="letter18">7</span>
                <span id="letter19">-</span>
                <span id="letter20">h</span>
                <span id="letter21">4</span>
                <span id="letter22">r</span>
                <span id="letter23">d</span>
                <span id="letter24">-</span>
                <span id="letter25">r</span>
                <span id="letter26">1</span>
                <span id="letter27">g</span>
                <span id="letter28">h</span>
                <span id="letter29">7</span>
            </div>
            <div class="word">
                <span id="letter30">}</span>
            </div>

順に並べると、フラグになる。

aupCTF{w45n't-th47-h4rd-r1gh7}

SQLi - 1 (Web)

SQLインジェクション。以下のように入力し、Submitすると、フラグが表示された。

Username: ' or 1=1 --
Password: (空)
aupCTF{3a5y-sql-1nj3cti0n}

Header (Web)

Webページにはこう書いてある。

リクエストヘッダ'GETFLAG'が'yes'の場合、フラグを含むページを返すということらしい。

$ curl https://challs.aupctf.live/header/ -H "GETFLAG: yes"   
aupCTF{cust0m-he4d3r-r3qu3st}
aupCTF{cust0m-he4d3r-r3qu3st}

Directory (Web)

https://challs.aupctf.live/dir/page/1/https://challs.aupctf.live/dir/page/1000/までのリンクがある。どれかにフラグが含まれているらしい。
スクリプトで順にページを見ていき、フラグを探す。

#!/usr/bin/env python3
import requests

for i in range(1, 1001):
    url = 'https://challs.aupctf.live/dir/page/%d/' % i
    print('[+] url:', url)
    r = requests.get(url)
    if 'No flag for you' not in r.text:
        print(r.text)
        break

実行結果は以下の通り。

        :
        :
[+] url: https://challs.aupctf.live/dir/page/710/
[+] url: https://challs.aupctf.live/dir/page/711/
[+] url: https://challs.aupctf.live/dir/page/712/
<!DOCTYPE html>
<html>
<head>
    <title>You Found Me</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
        }
        h1, h2{
            text-align: center;
            margin-top: 50px;
        }

    </style>
</head>
<body>
    <h1>Here is your flag, You deserve it</h1>
    <br>
    <h2>The flag is: aupCTF{d1r3ct0r13s-tr1v14l-fl4g}</h2>
</body>
</html>
aupCTF{d1r3ct0r13s-tr1v14l-fl4g}

Thread (Forensics)

ハッシュ値を元に、Popular threat labelを答える問題。VirusTotalで以下のハッシュを調べる。

d0ee6ffc8ce0e7f21cdcbd5e98c2dd4174a5d1b0266ec7f69075a0d9bea14757

popular threat labelは以下の通り。

trojan.nanocore/msil
aupCTF{trojan.nanocore/msil}

I Love Math (Forensics)

PDFにパスワードがかかっているので、クラックする。

$ pdf2john math.pdf > hash.txt
$ john --wordlist=dict/rockyou.txt hash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (PDF [MD5 SHA2 RC4/AES 32/64])
Cost 1 (revision) is 6 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
naruto           (math.pdf)     
1g 0:00:00:00 DONE (2023-06-25 13:54) 12.50g/s 1600p/s 1600c/s 1600C/s samantha..diamond
Use the "--show --format=PDF" options to display all of the cracked passwords reliably
Session completed.

パスワードはnaruto。このパスワードでPDFを開くが、フラグが見つからない。「I can't see anything here」とだけ書いてある。全選択し、コピペする。

I can't see anything here
The flag for this challenge is of the form:
aupCTF{I_Love_Math_x_y},
where x and y are the solution to these equations:
2x + 4y = 16
3x + 7y = 25

x, yを求める必要がある。普通に数学として解く。

6x + 12y = 48
6x + 14y = 50

上記から以下のことがわかる。

y = 1

さらに以下のことがわかる。

x = 6
aupCTF{I_Love_Math_6_1}

Syrio Forel (Steganography)

base64デコードすると、jpgになる。画像にこう書いてある。

what do we say to the god of death

これをGoogle検索すると、以下の言葉が出てくる。

Not today
aupCTF{Not_today}

Masterpeice (Steganography)

Audacityで開き、スペクトログラムを見ると、フラグが現れた。

aupCTF{Sp3ct0gr4m_ri4ght}

LSB (Steganography)

$ zsteg naruto.png       
imagedata           .. text: "$:;`VV(-1"
b1,b,lsb,xy         .. file: OpenPGP Secret Key
b1,rgb,lsb,xy       .. text: "aupCTF{zst1g-1s-c00l_rig3ht}"
b2,rgb,lsb,xy       .. file: big endian ispell 3.0 hash file, and 13698 string characters
aupCTF{zst1g-1s-c00l_rig3ht}

Deep (Steganography)

問題タイトルからDeepSoundの問題と推測する。パスワードがかかっているので、John the Ripperでクラックする。

$ deepsound2john truefan.wav > hash.txt
$ john --wordlist=dict/rockyou.txt hash.txt
Created directory: /home/kali/.john
Using default input encoding: UTF-8
Loaded 1 password hash (dynamic_1529 [sha1($p null_padded_to_len_32) (DeepSound) 256/256 AVX2 8x1])
Warning: no OpenMP support for this hash type, consider --fork=2
Press 'q' or Ctrl-C to abort, almost any other key for status
iamironman       (truefan.wav)     
1g 0:00:00:00 DONE (2023-06-25 12:51) 20.00g/s 7056Kp/s 7056Kc/s 7056KC/s julian24..hotchick13
Use the "--show --format=dynamic_1529" options to display all of the cracked passwords reliably
Session completed.

パスワードはiamironman。DeepSoundでこのパスワードを使って秘密ファイルを抽出する。marvel.exeが抽出できた。

$ strings marvel.exe| grep python    
bpython310.dll
6python310.dll

Python3.10製なのでデコンパイルする。

$ python3 pyinstxtractor.py marvel.exe                                       
[+] Processing marvel.exe
[+] Pyinstaller version: 2.1+
[+] Python version: 3.10
[+] Length of package: 6342224 bytes
[+] Found 61 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: pyi_rth_inspect.pyc
[+] Possible entry point: marvel.pyc
[!] Warning: This script is running in a different Python version than the one used to build the executable.
[!] Please run this script in Python 3.10 to prevent extraction errors during unmarshalling
[!] Skipping pyz extraction
[+] Successfully extracted pyinstaller archive: marvel.exe

You can now use a python decompiler on the pyc files within the extracted directory

$ pycdc marvel.exe_extracted/marvel.pyc 
# Source Generated with Decompyle++
# File: marvel.pyc (Python 3.10)

import sys
questions = [
    {
        'question': 'How much does Morgan Starks love her dad? [number]',
        'answer': '3000' },
    {
        'question': 'What is the full form of J.A.R.V.I.S.?',
        'answer': 'Just A Rather Very Intelligent System' },
    {
        'question': 'What is the full form of AI that replaced J.A.R.V.I.S.?',
        'answer': 'Female Replacement Intelligent Digital Assistant Youth' }]

def ask_question(question):
    user_answer = input(question['question'] + ' \n')
    if user_answer.lower() == question['answer'].lower():
        return True


def main():
    for question in questions:
        if not ask_question(question):
            print('Incorrect answer!')
            sys.exit(0)
    print('\n\n\nCongratulations! You are a true Marvel Nerd like Me ;/')
    print('Flag format: iron man first appearance year underscore last appearance year in movies')
    input('Press any key to exit...')

if __name__ == '__main__':
    main()
    return None

映画でアイアンマンが最初に登場した年と、最後に登場した年がフラグ。

aupCTF{2008_2019}

Obfuscated (Steganography)

JPGファイルだがバイト単位で前半4ビットと後半4ビットが入れ替わっているので、修復する。

#!/usr/bin/env python3
with open('flag.jpg', 'rb') as f:
    data = f.read()

out = b''
for d in data:
    code = (d >> 4) + ((d & 0xf) << 4)
    out += bytes([code])

with open('flag_fix.jpg', 'wb') as f:
    f.write(out)


修復した画像にフラグが書いてあった。

aupCTF{sw4p3d_w0w453?5422asd!1}

XOR (Steganography)

2つの画像のXORを取ってみる。

#!/usr/bin/env python3
from PIL import Image

img1 = Image.open('img1.png').convert('RGB')
img2 = Image.open('img2.png').convert('RGB')

w, h = img1.size

output_img = Image.new('RGB', (w, h), (255, 255, 255))

for y in range(h):
    for x in range(w):
        r1, g1, b1 = img1.getpixel((x, y))
        r2, g2, b2 = img2.getpixel((x, y))
        output_img.putpixel((x, y), (r1 ^ r2, g1 ^ g2, b1 ^ b2))

output_img.save('flag.png')


XORした画像には以下のように書いてある。

Shorts ID: LYkCU8Kn3a8

このIDの長さからYouTubeのパラメータと推測し、以下にアクセスする。

https://www.youtube.com/watch?v=LYkCU8Kn3a8

hexahueの画像が流れているので、デコードしていく。

w0wgraps
aupCTF{w0wgraps}

Rotation (Cryptography)

シーザー暗号。https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherでAscii charactersの範囲で復号する。

Rotation 27:
smhULX{q0m-x0mfv-e3}

さらにLetters onlyの範囲で復号する。

Rotation 18:
aupCTF{y0u-f0und-m3}
aupCTF{y0u-f0und-m3}

Ancient Cipher (Cryptography)

シーザー暗号と推測して、https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。

Rotation 17:
aupCTF{B0b's_Bad_Crypt0graphy_5k1lls}
aupCTF{B0b's_Bad_Crypt0graphy_5k1lls}

Disorder (Cryptography)

転置暗号のようなので、その法則を探す。

          11111111112222222222
012345678901234567890123456789
utsa}Ts0aXa{1_eC1ngXph__XF_tmX

10から左に10ごと、3箇所回ったら、5個左に行って、3箇所回る。
11から左に10ごと、3箇所回ったら、5個左に行って、3箇所回る。
これを繰り返す。

10: a
00: u
20: p
15: C
05: T
25: F

11: {
01: t
21: h
16: 1
06: s
26: _

12: 1
02: s
22: _
17: n
07: 0
27: t

13: _
03: a
23: _
18: g
08: a
28: m

14: e
04: }
24: X
19: X
09: X
29: X

パディングのXを除き並べると、フラグになっている。

aupCTF{th1s_1s_n0t_a_game}

RSA (Cryptography)

RSA暗号で、n, e, cの他にp + qとp - qの値がわかっている。
phiは以下の計算で割り出すことができる。

phi = (p - 1) * (q - 1) = n - (p + q) + 1

あとは通常通り復号する。

#!/usr/bin/env python3
from Crypto.Util.number import *

with open('output.txt', 'r') as f:
    params = f.read().splitlines()

n = int(params[0].split(' ')[-1])
e = int(params[1].split(' ')[-1], 16)
c = int(params[2].split(' ')[-1])
sum_pq = int(params[3].split(' ')[-1])

phi = n - sum_pq + 1
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)
aupCTF{3a5y_tw0_3quat10n5_and_hax3d_3}

Swiss Army Knife (Cryptography)

"X"を"0"に、"Y"を"1"に置換しデコードする。
base64文字列になるので、デコードする。
モールス信号になるので、デコードする。
base32文字列になるので、デコードする。
シーザー暗号で暗号化されているので、復号する。

#!/usr/bin/env python3
from base64 import *
from string import *

morse = {'.-': 'A', '-...': 'B', '-.-.': 'C', '-..': 'D', '.': 'E',
    '..-.': 'F', '--.': 'G', '....': 'H', '..': 'I', '.---': 'J', '-.-': 'K',
    '.-..': 'L', '--': 'M', '-.': 'N', '---': 'O', '.--.': 'P', '--.-': 'Q',
    '.-.': 'R', '...': 'S', '-': 'T', '..-': 'U', '...-': 'V', '.--': 'W',
    '-..-':'X' , '-.--': 'Y', '--..': 'Z', '-----': '0', '.----': '1',
    '..---': '2', '...--': '3', '....-': '4', '.....': '5', '-....': '6',
    '--...': '7', '---..': '8', '----.': '9', '-...-': '='
}

with open('decode.txt', 'r') as f:
    enc = f.read()

enc = enc.replace('X', '0').replace('Y', '1')
enc = enc.split(' ')

msg = ''
for c in enc:
    msg += chr(int(c, 2))
print('decoded 1:', msg)

dec = b64decode(msg).decode()
print('decoded 2:', dec)

codes = dec.split(' ')
msg = ''
for code in codes:
    msg += morse[code]
print('decoded 3:', msg)

dec = b32decode(msg).decode()
print('decoded 4:', dec)

flag = ''
for c in dec:
    if c in ascii_uppercase:
        index = (ascii_uppercase.index(c) + 7) % 26
        flag += ascii_uppercase[index]
    elif c in ascii_lowercase:
        index = (ascii_lowercase.index(c) + 7) % 26
        flag += ascii_lowercase[index]
    else:
        flag += c
print('flag:', flag)

実行結果は以下の通り。

decoded 1: LS0tIC4tLiAtLi4tIC0tLiAuLi4gLi4uLSAuLi4gLS4gLi0uLiAuLi0uIC4uLi4uIC4tLSAtLSAuLi4tLSAuLS4gLi0uIC0uIC4uLi0gLi0uIC0tLiAuLi4gLS0gLi0tLSAtIC4uLS4gLi4tIC0tLi4gLi0tIC0tLSAuLi4uLiAuLS4gLS0uLSAtLS0gLi4uLi0gLS4tLSAuLS0gLS0tIC0uLi4uIC4tLiAuLi4tIC4uLS4gLi4uLSAtLi4tIC0uLiAtLi0gLS0gLi4uLS0gLS4uLSAuLS0uIC4uLSAtLi4uLSAtLi4uLSAtLi4uLSAtLi4uLSAtLi4uLSAtLi4uLQ==
decoded 2: --- .-. -..- --. ... ...- ... -. .-.. ..-. ..... .-- -- ...-- .-. .-. -. ...- .-. --. ... -- .--- - ..-. ..- --.. .-- --- ..... .-. --.- --- ....- -.-- .-- --- -.... .-. ...- ..-. ...- -..- -.. -.- -- ...-- -..- .--. ..- -...- -...- -...- -...- -...- -...-
decoded 3: ORXGSVSNLF5WM3RRNVRGSMJTFUZWO5RQO4YWO6RVFVXDKM3XPU======
decoded 4: tniVMY{fn1mbi13-3gv0w1gz5-n53w}
flag: aupCTF{mu1tip13-3nc0d1ng5-u53d}
aupCTF{mu1tip13-3nc0d1ng5-u53d}

Battista's Bet (Cryptography)

Vigenere暗号と推測し、https://www.dcode.fr/vigenere-cipherで復号する。
鍵はSECRETでフラグの形式になった。

aupCTF{B3lla50W0uldB3Pr0ud}

Servay (Misc)

アンケートに答えたら、フラグが表示された。

aupCTF{th4nk5_f0r_y0ur_valu4bl3_f33db4ck}