# Hology 8 2025 CTF — Writeup (Quals)
---
## Daftar Isi
- Web
- pyjail?
- Reverse
- Hidden Factory
- ObligatoryFlagCheckerThatIsPacked
- Crypto
- The Architect’s Hasty Encryption
- p-power-rsa
- Forensic
- The Track Less Travelled
---
# Web Exploitation
## pyjail?
Oke, jadi ada endpoint `pyjail.php` yang melakukan `eval` python. Biasanya sih ini di bruteforce pake trik MRO, tapi disini ada WAF yang ngeblok karakter krusial (`' " ( ) _`) jadinya kita engga bisa sembarangan.
Dan ternyata saat dicek lebih lanjut, ada beberapa kesalahan dari challnya, flag nya disimpen di root dokumen dan bisa diakses langsung.
**Flag:** `HOLOGY8{h0l1_m0l1_r3g3x_g07_pwn3d??_51ff8a6c}`
**Jadi tinggal kita buka:** `http://ctf.hology.id:8282/flag.txt`
Maaf cara solve nya unintended :(
---
# Reverse Engineering
## 1. Hidden Factory
**Ringkasnya:** pertama kita connect ke `nc ctf.hology.id 1000`, lalu kita akan dapat ciphertext bernama *ENCRYPTED BLUEPRINT*. Sistem enkripsinya ini sendiri terbagi menjadi tiga layer, dan sistem nya harus kita balikan menjadi :
Stage‑3 → Stage‑2 → Stage‑1.
- **Stage‑1:** Caesar di alfabet kustom (shift +23). Alfabet: `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789{}_` (panjang 65).
- **Stage‑2:** XOR pake nilai yang bergantung pada posisi: untuk index `i`: `val = (i*7 + 23) % 256`, `byte ^= val`.
- **Stage‑3:** Base64 + substitusi alfabet (map custom ke standard sebelum decode).
**Solve:**
Kita harus reverse Stage‑3 (balikan map + base64 decode) selanjutnya kita reverse Stage‑2 (xor lagi sama nilai yang sama), dan terakhir kita reverse Stage‑1 (shift -23).
**Final script solver nya:**
```py
import base64
STD_ALPH = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
CUST_ALPH = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"
ALPH1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789{}_"
OFFSET = 23
MULT = 7
def stage3_decode(s):
trans = str.maketrans(CUST_ALPH, STD_ALPH)
b64 = s.translate(trans)
return base64.b64decode(b64)
def stage2_unscramble(bs):
out = bytearray()
for i, b in enumerate(bs):
val = (i * MULT + OFFSET) % 256
out.append(b ^ val)
return bytes(out)
def stage1_inverse(s):
out = []
L = len(ALPH1)
for ch in s.decode('latin-1'):
if ch in ALPH1:
idx = ALPH1.index(ch)
out.append(ALPH1[(idx - OFFSET) % L])
else:
out.append(ch)
return ''.join(out)
# pake fungsi di atas untuk decrypt ciphertext dari nc
```
**Flag:** `HOLOGY8{f4ct0ry_r3v3rs3_3ng1n33r1ng_m4st3r_0f_th3_h1dd3n_bluepr1nt_cr4ck3r}`
---
## 2. ObligatoryFlagCheckerThatIsPacked
Binary ini dipack menggunakan UPX. Setelah unpack, program akan membaca input 30 byte lalu dicek pake kombinasi konstanta `partA` dan `partB`.
**Inti chall ini:** ada rumus `local_58[i] = partA[i] ^ ROL8(partB[i],3)` yang membandingkan input dengan `local_58 ^ key[i & 7]` dan `key = "easy_key"`. Jadi kita bisa recover flag dengan ekstrak `partA` & `partB` dari binary dan menghitungnya.
**Hal yang harus kita lakukan untuk solve chall ini:**
1. Unpack UPX (atau run di loader terus dump memory).
2. Cari `partA` & `partB` di .rodata.
3. Menghitung `local_58[i] = partA[i] ^ rol8(partB[i],3)` untuk i 0..29.
4. key = b"easy_key" (8 byte), flag[i] = local_58[i] ^ key[i & 7].
**Contoh pseudo:**
```py
def rol8(x, r=3):
return ((x << r) | (x >> (8 - r))) & 0xFF
# ambil A,B dari binary -> hitung -> dapat flag
```
**Solver**:
```py
import struct
def rol8(x, r=3):
return ((x << r) | (x >> (8 - r))) & 0xFF
def read_sections(elf):
e_shoff = struct.unpack_from("<Q", elf, 0x28)[0]
e_shentsz = struct.unpack_from("<H", elf, 0x3A)[0]
e_shnum = struct.unpack_from("<H", elf, 0x3C)[0]
e_shstrndx = struct.unpack_from("<H", elf, 0x3E)[0]
sections = []
for i in range(e_shnum):
off = e_shoff + i * e_shentsz
sh = struct.unpack_from("<IIQQQQIIQQ", elf, off)
sections.append({
"name_off": sh[0],
"type": sh[1],
"flags": sh[2],
"addr": sh[3],
"offset": sh[4],
"size": sh[5],
"link": sh[6],
"info": sh[7],
"addralign": sh[8],
"entsize": sh[9],
})
# Read section header string table
shstr = sections[e_shstrndx]
shstr_data = elf[
shstr["offset"] : shstr["offset"] + shstr["size"]
]
def get_name(off):
end = shstr_data.find(b"\x00", off)
return shstr_data[off:end].decode()
for s in sections:
s["name"] = get_name(s["name_off"])
return sections
def read_symbols(elf, sections):
symtab = next(s for s in sections if s["name"] == ".symtab")
strtab = next(s for s in sections if s["name"] == ".strtab")
symdat = elf[
symtab["offset"] : symtab["offset"] + symtab["size"]
]
strdat = elf[
strtab["offset"] : strtab["offset"] + strtab["size"]
]
symbols = []
for i in range(symtab["size"] // symtab["entsize"]):
off = i * symtab["entsize"]
st_name, st_info, st_other, st_shndx, st_value, st_size = \
struct.unpack_from("<IbbHQQ", symdat, off)
end = strdat.find(b"\x00", st_name)
name = strdat[st_name:end].decode()
symbols.append({
"name": name,
"info": st_info,
"shndx": st_shndx,
"value": st_value,
"size": st_size,
})
return symbols
def get_bytes(elf, sections, symbols, name):
sym = next(s for s in symbols if s["name"] == name)
sec = sections[sym["shndx"]]
file_off = sec["offset"] + (sym["value"] - sec["addr"])
return elf[file_off : file_off + sym["size"]]
# ============================================================
# Main
# ============================================================
PATH = "unpacked_rev" # hasil: upx -d
with open(PATH, "rb") as f:
elf = f.read()
sections = read_sections(elf)
symbols = read_symbols(elf, sections)
A = get_bytes(elf, sections, symbols, "partA")
B = get_bytes(elf, sections, symbols, "partB")
local58 = bytes(
A[i] ^ rol8(B[i], 3)
for i in range(30)
)
key = b"easy_key"
flag = bytes(
local58[i] ^ key[i & 7]
for i in range(30)
)
print(flag.decode())
```
**Flag:** `HOLOGY8{n33d3d_4_4_r3v_p4ck3r}`
---
# Cryptography
## 1. The Architect’s Hasty Encryption
Ada 3 RSA artifacts. Dua diantaranya mempunyai shared prime yang mana bisa kita gunakan **gcd** untuk dapetin **p**. Dan artefak ketiga menggunakan **n = p^2** yang mana akan memudahkan kita untuk dekripsi.
**Flow enkripsi nya:**
- p = gcd(nA, nB)
- Kita faktorkan nX (kalo nX == p^2), kita hitung d = e^{-1} mod phi(nX)
- Setelah itu kita dekripsi cX dan kita akan mendapatkan **KEY** nya
- Terakhir, kita ambil **KEY (ASCII)** jadi **HEX**, lalu kita gabung ke p (jadi string), dan sha256. Setelah itu kita akan mendapatkan **flag** nya.
**Hasil SHA256 yang keluar:**
`7ec2c6c4821ffe2bd9fa2045ef69791a22e595425e2948844e9b1fd3b37d54ca`
**Flag:** `HOLOGY8{7ec2c6c4821ffe2bd9fa2045ef69791a22e595425e2948844e9b1fd3b37d54ca}`
---
## 2. p‑power‑rsa
Chall ini menggunakan RSA dengan modulus yang tidak square‑free, misal `N = p^r * q` (r≥2). Ada hubungan kecil antara dua private exponents, yang bisa dipakai LLL/Coppersmith untuk menemukan delta kecil.
**Garis besar nya:**
- Formulasi masalah jadi root finding linear kecil.
- Kita akan menggunakan LLL atau small-root technique untuk mendapatkan Δ.
- Dari Δ yang kita dapatkan, gcd akan ngebocorin faktor **p^(r−1)** lalu bisa kita recover **p,q** dan kita decrypt.
```py
# ============================================================
# solve_ppower_rsa_fixed.sage
# ============================================================
from sage.all import *
# ------------------------------------------------------------
# Setup
# ------------------------------------------------------------
Z = ZZ
PR = PolynomialRing
# ------------------------------------------------------------
# Constants from challenge
# ------------------------------------------------------------
c = Integer(
111485787416215732083419888356058001033979399362109360508
26187089114840724405691532084205700450578271560281812344730088409
50208480418896958145264338686293650231691792222492663896258529529
87468768744093949285183059141092635835298369742277053305386129494
49893288884757430080838808926832528531554476875824396277630533852
26221852536414060400397668638390068574218789239859899349649806086
78162026614939501709616234022560513068727741227451418843199235135
74909526934129781982520409344915204011945042876058332259941182649
38664198308269684384412766681923585648346035966663578374146570211
99051732261588602224173516664537564148394089550429651483809997867
45067799292206419334487477074750340090409380178087362694809249384
83778916870232621005585404027295880314360304324547942498123434383
90584037127772193904170421087845937142186548759745304853458426108
89547272654404100971318581885200905937761466010011130409221400225
29357970105883386976827646232264113729591494752134918980265858392
27437083313605739136374826611550285890516268274465906516027362250
60976763088927714433438532990708303684257823041138278098690349309
61352233448908795589570445785916699320291444553085845395422872534
81580691466811698093274707201630462570730775293023489199764838103
52375566246137475119895846742635800668869428611373349947620894937
86972396117163064777826915719498372710037996238034841685083674404
66097349584557388186877058472216663997659060248313992041943213504
83011292871077770270415445375268583670974485113451091685851930681
09172032501486157989738547685799120192688845063502134676407713464
85176419582269169891572750725266499997539100897292414501918122708
31903820127718178087134916043563030557962263828373741474240294496
36142143723965161304999822527648186438619503913331971241426026581
89297469501831730837739445119194832277196410874479162266849124548
98849413857255170640387605610788711387174199311750727941171876289
82386126756769131559394794494188680420063298157018187160344334249
36492656520443344944013881853487228563694901400446844531668
)
e = Integer(65537)
N = Integer(
453749993609862423767406315499943576715619270719355402974
23859805295539492811573655205214935999778624622035669768208587595
29218143036086983706210015934982026641371056147977060929637666485
97009961505999304804868628819913053836743203352789790043629722002
65756125120047946168238784120998943211497549162417738445189139137
91868779434356749413927932306679870757960053047235867464992349071
81428238498088293365909082016312817879158926491633438828864279569
81388044992890319744231405679808294831634533302012433342227101026
65582407044626577954373312845684702730716237844639808043564070484
93506788785174496491522489832266993857493571141791095824560928202
78785391845490692529410810830763245464318129228423813901413965566
14688558576411125274758109643984898177268867155526515486406552089
66527802039076290335379020936965043298763839401599171514405533064
38288924650907511067102750891160442877581241774036912780601511247
78162240900133870550291471107609587274609296125964886837151575243
48089512845563356023108705591279503195318426654042354995168730827
22427577433858935496084389231152779521025662507616153014801430837
35102054758031266202731220263554484311016232518079148726570946364
73462128842533375598676295429191094715741380629940237085984351828
45540267141135904098507938822378845568711349570185162389495695764
55509255199128563782586379754441399685527831592764818262945580264
30514802530631451990707908860581231085522384501312007775067802744
55072012130971156806367205765189457157795566712527784194089612138
95974778009995100998978024585195694883991499678909222606659665158
78625384142699235491857754702310410293104365494144520258022000143
26549526973003116317052964039607035814432505055426282735050697422
53165941764318391828345884572254968633128186074911835384215030850
95259381779598083377226530394482769313660477032407154580895878594
63272220865967511425425870472798001225357792182807761489625539080
56978900160113279840292213115387898067931606626553838499139436472
82192959359920313936222580284259387292748269260001904819421
)
# ------------------------------------------------------------
# Derived values
# ------------------------------------------------------------
A = e1 * e2
B = e1 - e2
# ------------------------------------------------------------
# Utilities
# ------------------------------------------------------------
def recover_p_r_q_from_G(G, N):
"""
Recover p, r, q assuming:
G = p^(r-1)
N = p^r * q
"""
p = gcd(G, N // G)
if p <= 1:
fac = factor(G)
p = max(f[0] for f in fac)
r = valuation(N, p)
pr = p**r
q = N // pr
assert N == pr * q
return int(p), int(r), int(q)
def decrypt_ppower(c, e, p, r, q):
"""
RSA decryption for p^r * q modulus
"""
dp = inverse_mod(e, (p - 1) * p**(r - 1))
dq = inverse_mod(e, (q - 1))
mp = power_mod(c, dp, p**r)
mq = power_mod(c, dq, q)
return int(crt([mp, mq], [p**r, q]))
# ------------------------------------------------------------
# Coppersmith: Linear small root with unknown divisor
# ------------------------------------------------------------
def small_root_linear_unknown_divisor(
A, B, N, X,
m_list=(3, 4, 5, 6),
t_list=(1, 2, 3, 4),
scales=(1, 2, 3, 4, 6, 8, 12, 16, 24, 32)
):
"""
Find small Δ such that:
A*Δ - B ≡ 0 (mod φ(N))
using Howgrave-Graham / LLL
"""
RZ.<x> = PolynomialRing(ZZ)
RQ.<y> = PolynomialRing(QQ)
f = A*x - B
tried = 0
for m in m_list:
for t in t_list:
for s in scales:
tried += 1
print(f"[*] Trying m={m}, t={t}, scale={s} (#{tried})")
Mmod = N * s
polys = []
for j in range(t):
polys.append((x**j) * (Mmod**m))
for i in range(1, m + 1):
for j in range(m - i + 1):
polys.append((x**j) * (f**i) * (Mmod**(m - i)))
polys_scaled = [p(x * X) for p in polys]
deg = max(p.degree() for p in polys_scaled)
rows = [
[int(pp[i]) if i <= pp.degree() else 0 for i in range(deg + 1)]
for pp in polys_scaled
]
Bmat = Matrix(ZZ, rows)
Bred = Bmat.LLL()
for row in Bred.rows():
if all(c == 0 for c in row):
continue
gZ = sum(row[i] * x**i for i in range(len(row)))
gQ = RQ(gZ(x=y / X))
if gQ.is_zero():
continue
den = lcm([c.denominator() for c in gQ.coefficients()])
GZ = (den * gQ).change_ring(ZZ)
for root, _ in GZ.roots(ZZ):
root = int(root)
if root.bit_length() > X.nbits() + 2:
continue
Gcand = gcd(N, A*root - B)
if 1 < Gcand < N:
return root, int(Gcand)
return None, None
# ------------------------------------------------------------
# Main
# ------------------------------------------------------------
X = Integer(2)**1012
Delta, G = small_root_linear_unknown_divisor(A, B, N, X)
if not Delta:
print("[!] Retry with more aggressive parameters...")
Delta, G = small_root_linear_unknown_divisor(
A, B, N, X,
m_list=(5, 6, 7),
t_list=(2, 3, 4, 5),
scales=(1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 64, 128)
)
if not Delta:
raise RuntimeError("Failed to recover Δ")
print(f"[+] Delta = {Delta}")
print(f"[+] G = {G}")
p, r, q = recover_p_r_q_from_G(G, N)
print(f"[+] p = {p}")
print(f"[+] r = {r}")
print(f"[+] q = {q}")
m = decrypt_ppower(c, e, p, r, q)
pt = Integer(m).to_bytes((m.nbits() + 7) // 8, "big")
print("[+] Plaintext (hex):", pt.hex())
print("[+] Plaintext (ascii):", pt.decode(errors="ignore"))
```
**Flag:** `HOLOGY8{Mu17i_P0w3R_RSA_w17h_W34k_Struc7ur3}`
---
# Forensic
## The Track Less Travelled
Chall ini berupa file MP4 yang punya track audio tersembunyi, handlernya bukan standard jadi player biasa tidak akan bisa membukanya. Isinya merupakan AAC‑LC tanpa ADTS header.
**Cara solve chall ini:**
1. Parse MP4 (`ffprobe` / parse box) cari trak yang **sus**.
2. Dapatkan `AudioSpecificConfig` dari `esds`.
3. Dari `stsz` / `stco` / `stsc` dibuat panjang untuk setiap frame, lalu buat header ADTS 7 byte per frame, dan gabungkan menjadi => `track_hidden.aac`.
4. Convert ke WAV, bersihin noise, lalu kita dengarkan audio nya (**yap, ini mau main CTF apa test TOEFL plis)** flag disebutkan satu persatu hurufnya di audio.
**Solver:**
```py
#!/usr/bin/env python3
"""
Extract hidden AAC tracks from a tricky MP4 by reconstructing ADTS headers
"""
import struct
import sys
from pathlib import Path
# ============================================================
# MP4 box helpers
# ============================================================
def find_child(data, parent_off, parent_size, type_name):
i = parent_off + 8
end = parent_off + parent_size
while i + 8 <= end:
size = struct.unpack(">I", data[i:i + 4])[0]
typ = data[i + 4:i + 8].decode("latin1")
if typ == type_name:
return i, size
if size == 0:
break
i += size
return None
def parse_boxes(data, start=0, end=None):
if end is None:
end = len(data)
i = start
boxes = []
while i + 8 <= end:
size = struct.unpack(">I", data[i:i + 4])[0]
typ = data[i + 4:i + 8].decode("latin1")
if size == 0:
size = end - i
header = 8
elif size == 1:
size = struct.unpack(">Q", data[i + 8:i + 16])[0]
header = 16
else:
header = 8
boxes.append((i, size, typ))
if typ in ("moov", "trak", "mdia", "minf", "stbl", "udta", "meta"):
child_start = i + header + (4 if typ == "meta" else 0)
boxes += parse_boxes(data, child_start, i + size)
i += size
return boxes
# ============================================================
# Sample table parsing
# ============================================================
def get_stbl_boxes(data, trak_off, trak_size):
mdia = find_child(data, trak_off, trak_size, "mdia")
minf = find_child(data, mdia[0], mdia[1], "minf")
stbl = find_child(data, minf[0], minf[1], "stbl")
def child(name):
return find_child(data, stbl[0], stbl[1], name)
return {
name: child(name)
for name in ("stsd", "stts", "stsc", "stsz", "stco", "co64")
}
def parse_stco(data, off, size):
entry_count = struct.unpack(">I", data[off + 12:off + 16])[0]
return [
struct.unpack(">I", data[off + 16 + 4 * i:off + 20 + 4 * i])[0]
for i in range(entry_count)
]
def parse_stsz(data, off, size):
sample_size = struct.unpack(">I", data[off + 12:off + 16])[0]
entry_count = struct.unpack(">I", data[off + 16:off + 20])[0]
if sample_size != 0:
return [sample_size] * entry_count
return [
struct.unpack(">I", data[off + 20 + 4 * i:off + 24 + 4 * i])[0]
for i in range(entry_count)
]
def parse_stsc(data, off, size):
entry_count = struct.unpack(">I", data[off + 12:off + 16])[0]
entries = []
for i in range(entry_count):
a = off + 16 + 12 * i
first_chunk = struct.unpack(">I", data[a:a + 4])[0]
samples_per_chunk = struct.unpack(">I", data[a + 4:a + 8])[0]
sample_desc_index = struct.unpack(">I", data[a + 8:a + 12])[0]
entries.append(
(first_chunk, samples_per_chunk, sample_desc_index)
)
return entries
def build_chunk_samples(stco_list, stsc_entries, stsz_list):
chunks = []
sample_index = 0
for idx, chunk_offset in enumerate(stco_list, start=1):
entry = None
for e in stsc_entries:
if e[0] <= idx:
entry = e
samples_per_chunk = entry[1] if entry else 0
size = stsz_list[sample_index] if samples_per_chunk else 0
chunks.append((chunk_offset, size))
sample_index += samples_per_chunk
return chunks
# ============================================================
# AAC / ADTS helpers
# ============================================================
def find_decoder_specific_info(esds_blob):
payload = esds_blob[12:]
i = 0
if i >= len(payload) or payload[i] != 0x03:
return None
i += 1
length = 0
while True:
b = payload[i]
i += 1
length = (length << 7) | (b & 0x7F)
if not (b & 0x80):
break
i += 2 # ES_ID
flags = payload[i]
i += 1
if flags & 0x80:
i += 2
if flags & 0x40:
url_len = payload[i]
i += 1 + url_len
if flags & 0x20:
i += 1
if payload[i] != 0x04:
return None
i += 1
while payload[i] & 0x80:
i += 1
i += 14 # decoder config
if payload[i] != 0x05:
return None
i += 1
slen = 0
while True:
b = payload[i]
i += 1
slen = (slen << 7) | (b & 0x7F)
if not (b & 0x80):
break
return payload[i:i + slen]
def parse_asc(asc):
b0, b1 = asc[0], asc[1]
aot = (b0 >> 3) & 0x1F
sf_index = ((b0 & 0x07) << 1) | ((b1 >> 7) & 0x01)
ch_config = (b1 >> 3) & 0x0F
return aot, sf_index, ch_config
def build_adts_header(frame_length, aot, sf_index, ch_config):
profile = aot - 1
hdr = bytearray(7)
hdr[0] = 0xFF
hdr[1] = 0xF1
hdr[2] = (
((profile & 0x3) << 6)
| ((sf_index & 0xF) << 2)
| ((ch_config >> 2) & 0x1)
)
hdr[3] = ((ch_config & 0x3) << 6) | ((frame_length >> 11) & 0x3)
hdr[4] = (frame_length >> 3) & 0xFF
hdr[5] = ((frame_length & 0x7) << 5) | 0x1F
hdr[6] = 0xFC
return bytes(hdr)
# ============================================================
# Extraction
# ============================================================
def extract_track_aac(mp4_bytes, trak_off, trak_size, out_path):
stbl = get_stbl_boxes(mp4_bytes, trak_off, trak_size)
stsd_off, _ = stbl["stsd"]
entry_off = stsd_off + 16
entry_size = struct.unpack(">I", mp4_bytes[entry_off:entry_off + 4])[0]
region = mp4_bytes[entry_off:entry_off + entry_size]
esds_pos = region.find(b"esds")
esds_off = entry_off + esds_pos - 4
esds_size = struct.unpack(">I", mp4_bytes[esds_off:esds_off + 4])[0]
asc = find_decoder_specific_info(
mp4_bytes[esds_off:esds_off + esds_size]
)
aot, sf_idx, ch = parse_asc(asc)
stco = parse_stco(mp4_bytes, *stbl["stco"])
stsz = parse_stsz(mp4_bytes, *stbl["stsz"])
stsc = parse_stsc(mp4_bytes, *stbl["stsc"])
chunks = build_chunk_samples(stco, stsc, stsz)
out = bytearray()
for off, size in chunks:
if size == 0:
continue
frame = mp4_bytes[off:off + size]
hdr = build_adts_header(7 + len(frame), aot, sf_idx, ch)
out += hdr + frame
Path(out_path).write_bytes(out)
return out_path
# ============================================================
# Main
# ============================================================
def main():
if len(sys.argv) < 2:
print("Usage: python extract_hidden_track.py chall.mp4")
sys.exit(1)
mp4 = Path(sys.argv[1]).read_bytes()
boxes = parse_boxes(mp4)
traks = [b for b in boxes if b[2] == "trak"]
if len(traks) < 2:
print("Only one track found.")
sys.exit(1)
out = extract_track_aac(
mp4,
traks[1][0],
traks[1][1],
"track2_hidden.aac",
)
print("Wrote", out)
if __name__ == "__main__":
main()
```
**Flag:** `HOLOGY8{4ud4c1ty_s0_g00d_w8r5d6ls}`
---
Sekian terimakasih :>
JANGAN LUPA MAIN CTF YAAA!!!