SunshineCTF 2023 Writeup

この大会は2023/10/7 23:00(JST)~2023/10/9 23:00(JST)に開催されました。
今回もチームで参戦。結果は501点で821チーム中176位でした。
自分で解けた問題をWriteupとして書いておきます。

Initialization (Misc)

問題にフラグが書いてあった。

sun{i_am_here}

DDR (Scripting)

$ nc chal.2023.sunshinectf.games 23200
Welcome to DIGITAL DANCE ROBOTS!

       -- INSTRUCTIONS --       
 Use the WASD keys to input the 
 arrow that shows up on screen. 
 If you beat the high score of  
     255, you win a FLAG!     

   -- Press ENTER To Start --   


⇧⇧⇩⇧⇩⇧⇨⇧⇦⇩⇦⇩⇩⇧⇦⇧⇧⇨⇦⇦⇦⇩⇨⇨⇩⇧⇨⇦⇨⇦⇦⇩⇨⇦⇦⇩⇧⇦⇩⇧⇨⇩⇧⇧⇧⇧⇦⇩⇩⇩
You lose... better luck next time!
Score: 1

矢印の向きをWASDで答える。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket

def recvuntil(s, tail):
    data = b''
    while True:
        if tail in data:
            return data.decode()
        data += s.recv(1)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chal.2023.sunshinectf.games', 23200))

data = recvuntil(s, b'Start --   \r\n').rstrip()
print(data)
s.sendall(b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

for i in range(255):
    print('Round %d' % (i + 1))
    data = recvuntil(s, b'\n').rstrip()
    print(data)

    ans = ''
    for d in data:
        if d == '⇧':
            ans += 'W'
        elif d == '⇦':
            ans += 'A'
        elif d == '⇩':
            ans += 'S'
        elif d == '⇨':
            ans += 'D'
    print(ans)
    s.sendall(ans.encode() + b'\n')

data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

Welcome to DIGITAL DANCE ROBOTS!

       -- INSTRUCTIONS --       
 Use the WASD keys to input the 
 arrow that shows up on screen. 
 If you beat the high score of  
     255, you win a FLAG!     

   -- Press ENTER To Start --

Round 1
⇨⇩⇩⇧⇩⇩⇨⇨⇧⇨⇧⇦⇧⇨⇨⇩⇨⇨⇩⇨⇩⇨⇩⇧⇩⇧⇦⇨⇦⇨⇧⇦⇧⇧⇧⇦⇩⇧⇦⇩⇧⇦⇨⇩⇧⇨⇨⇨⇨⇩
DSSWSSDDWDWAWDDSDDSDSDSWSWADADWAWWWASWASWADSWDDDDS
Round 2
⇨⇧⇩⇧⇨⇨⇩⇧⇩⇦⇩⇦⇨⇨⇩⇦⇩⇦⇦⇩⇧⇩⇨⇩⇦⇦⇨⇦⇧⇨⇩⇦⇧⇩⇩⇧⇧⇧⇩⇧⇧⇧⇧⇦⇧⇧⇨⇩⇧⇩
DWSWDDSWSASADDSASAASWSDSAADAWDSAWSSWWWSWWWWAWWDSWS
Round 3
⇧⇧⇧⇦⇧⇦⇧⇦⇧⇧⇨⇧⇦⇨⇦⇩⇧⇨⇩⇨⇧⇦⇨⇦⇩⇩⇧⇩⇧⇩⇧⇨⇦⇩⇩⇩⇧⇨⇨⇦⇨⇩⇧⇩⇧⇨⇧⇦⇨⇨
WWWAWAWAWWDWADASWDSDWADASSWSWSWDASSSWDDADSWSWDWADD
Round 4
⇩⇦⇧⇦⇩⇦⇩⇧⇨⇧⇨⇦⇩⇨⇧⇦⇩⇨⇨⇨⇩⇨⇧⇦⇦⇧⇨⇧⇨⇦⇧⇨⇩⇦⇨⇨⇨⇦⇦⇧⇨⇨⇧⇧⇦⇨⇩⇦⇦⇩
SAWASASWDWDASDWASDDDSDWAAWDWDAWDSADDDAAWDDWWADSAAS
Round 5
⇨⇩⇦⇧⇦⇧⇦⇧⇨⇩⇨⇩⇨⇧⇩⇩⇨⇩⇩⇧⇧⇧⇩⇨⇩⇩⇨⇩⇦⇩⇦⇧⇩⇧⇦⇩⇩⇩⇧⇦⇦⇦⇦⇧⇨⇨⇦⇧⇨⇧
DSAWAWAWDSDSDWSSDSSWWWSDSSDSASAWSWASSSWAAAAWDDAWDW
        :
        :
Round 251
⇨⇩⇦⇨⇧⇩⇧⇨⇩⇨⇧⇩⇦⇩⇦⇨⇦⇦⇧⇨⇧⇩⇩⇧⇧⇨⇩⇨⇦⇦⇧⇦⇨⇨⇨⇩⇦⇦⇦⇦⇧⇦⇨⇨⇩⇦⇩⇩⇨⇨
DSADWSWDSDWSASADAAWDWSSWWDSDAAWADDDSAAAAWADDSASSDD
Round 252
⇦⇧⇦⇧⇦⇧⇧⇨⇨⇩⇦⇧⇦⇧⇩⇦⇨⇨⇦⇩⇨⇩⇧⇦⇩⇩⇦⇦⇦⇦⇩⇦⇨⇧⇩⇩⇨⇨⇦⇨⇩⇨⇨⇨⇨⇨⇦⇩⇦⇧
AWAWAWWDDSAWAWSADDASDSWASSAAAASADWSSDDADSDDDDDASAW
Round 253
⇨⇩⇦⇧⇦⇩⇦⇨⇩⇩⇨⇧⇨⇨⇨⇧⇩⇩⇧⇨⇨⇧⇩⇩⇩⇦⇨⇩⇩⇧⇨⇨⇨⇨⇧⇧⇨⇧⇦⇦⇩⇧⇨⇩⇩⇨⇩⇩⇩⇧
DSAWASADSSDWDDDWSSWDDWSSSADSSWDDDDWWDWAASWDSSDSSSW
Round 254
⇩⇧⇧⇧⇧⇨⇦⇦⇨⇨⇦⇧⇧⇦⇩⇧⇨⇨⇧⇧⇦⇦⇩⇦⇨⇨⇦⇨⇦⇦⇨⇧⇧⇩⇦⇨⇦⇨⇩⇦⇨⇩⇦⇦⇨⇦⇦⇧⇩⇩
SWWWWDAADDAWWASWDDWWAASADDADAADWWSADADSADSAADAAWSS
Round 255
⇩⇦⇨⇩⇧⇨⇧⇨⇨⇩⇨⇧⇩⇩⇨⇦⇦⇨⇨⇩⇧⇧⇦⇨⇨⇩⇧⇧⇩⇦⇦⇩⇦⇩⇦⇨⇦⇩⇦⇧⇦⇦⇨⇦⇨⇦⇧⇧⇧⇨
SADSWDWDDSDWSSDAADDSWWADDSWWSAASASADASAWAADADAWWWD
YOU WIN!!!
Your prize is sun{d0_r0b0t5_kn0w_h0w_t0_d4nc3}
sun{d0_r0b0t5_kn0w_h0w_t0_d4nc3}

Dill (Reversing)

pycをデコンパイルする。

$ decompyle3 dill.cpython-38.pyc 
# decompyle3 version 3.9.0
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.10.6 (main, Mar 10 2023, 10:55:28) [GCC 11.3.0]
# Embedded file name: dill.py
# Compiled at: 2023-10-07 04:53:54
# Size of source mod 2**32: 914 bytes


class Dill:
    prefix = 'sun{'
    suffix = '}'
    o = [5,1,3,4,7,2,6,0]

    def __init__(self) -> None:
        self.encrypted = 'bGVnbGxpaGVwaWNrdD8Ka2V0ZXRpZGls'

    def validate(self, value: str) -> bool:
        if not (value.startswith(Dill.prefix) and value.endswith(Dill.suffix)):
            return False
        value = value[len(Dill.prefix):-len(Dill.suffix)]
        if len(value) != 32:
            return False
        c = [value[i:i + 4] for i in range(0, len(value), 4)]
        value = ''.join([c[i] for i in Dill.o])
        if value != self.encrypted:
            return False
        return True
# okay decompiling dill.cpython-38.pyc

上記のコードからフラグは以下の条件であることがわかる。

・"sun{"から始まり、"}"で終わる。
・"{"と"}"の間の文字列の長さは32
・4バイトごとの順序をoの順序に入れ替えて、encryptedになる。

元の順序に戻す。

#!/usr/bin/env python3
o = [5,1,3,4,7,2,6,0]
encrypted = 'bGVnbGxpaGVwaWNrdD8Ka2V0ZXRpZGls'

value = ''
for i in range(8):
    index = o.index(i)
    value += encrypted[index*4:index*4+4]

flag = 'sun{%s}' % value
print(flag)
sun{ZGlsbGxpa2V0aGVwaWNrbGVnZXRpdD8K}

Hotdog Stand (Web)

https://hotdog.web.2023.sunshinectf.games/robots.txtにアクセスする。

User-agent: * Disallow: /configs/ Disallow: /backups/ Disallow: /hotdog-database/

以下2つにアクセスしたが、Not Foundになった。

https://hotdog.web.2023.sunshinectf.games/configs/
https://hotdog.web.2023.sunshinectf.games/backups/

https://hotdog.web.2023.sunshinectf.games/hotdog-database/にアクセスしたら、robot_data.dbがダウンロードされた。
DB Browserで開く。
credentialsテーブルを見ると、以下のデータがある。

username: hotdogstand
password: slicedpicklesandonions
role: admin

hotdogstand / slicedpicklesandonionsでログインすると、フラグが表示された。

sun{5l1c3d_p1cKl35_4nd_0N10N2}

BeepBoop Cryptography (Crypto)

スペース区切りで"beep", "boop"がたくさんある。"beep"を0, "boop"を1にしてデコードする。さらにrot13でデコードすると、フラグになった。

#!/usr/bin/env python3
import codecs

with open('BeepBoop', 'r') as f:
    enc = f.read().rstrip()

enc = enc.replace('beep', '0').replace('boop', '1')
enc = enc.replace(' ', '')

msg = ''
for i in range(0, len(enc), 8):
    msg += chr(int(enc[i:i+8], 2))

msg = codecs.decode(msg, 'rot-13')
print(msg)
sun{exterminate-exterminate-exterminate}