I dropped my message into the bin of Legos. It’s all scrambled up
now. Please help.
Initial Analysis
The challenge provides a Python script encoder.py and
its corresponding output output.txt. The goal is to recover
a flag that has had its bits scrambled based on a seed derived from a
hardcoded string.
Seed Generation
The script uses RSA to “encrypt” the string
"I_LOVE_RNG":
1 2 3 4
text = "I_LOVE_RNG" n, seed = RSA_enc(text) # ... enc_seed = pow(seed, e, n)
The RSA_enc function calculates
seed = pow(plain_num, e, n). In the main
function, it then calculates enc_seed = pow(seed, e, n).
Since e = 7 is very small and
the plaintext "I_LOVE_RNG" is short, the value me (where m is the integer representation of
the text) is much smaller than the 4096-bit RSA modulus n. This means
pow(m, e, n) is simply me without any
modular reduction.
Bit Shuffling
1 2 3 4
flag_bits = get_flag_bits(flag) for i inrange(10): random.seed(seed*(i+1)) random.shuffle(flag_bits)
The flag is converted into a list of bits and shuffled 10 times using
Python’s random.shuffle. Each shuffle is seeded with a
value derived from the seed identified above.
Solution
To recover the flag, we follow these steps:
Recover the Seed: Calculate the integer value of
"I_LOVE_RNG" and raise it to the 7th power to get the
seed. We can verify this against enc_seed in
the output file by checking if seed**7 == enc_seed.
Reverse the Shuffle: Since
random.shuffle is deterministic when the seed is known, we
can track how the positions change. Instead of shuffling the bits
directly, we shuffle a list of indices
[0, 1, 2, ..., len(bits)-1].
Reconstruct the Flag: After 10 shuffles, the index
list tells us where each original bit ended up. We map the bits from the
encoded flag back to their original positions and convert the bit array
back into a string.
defget_flag_bits(flag_hex): flag_bytes = bytes.fromhex(flag_hex) bits = [] for b in flag_bytes: bits.extend(list(bin(b)[2:].zfill(8))) return bits
defbit_arr_to_str(bit_arr): byte_arr = [] for i inrange(0, len(bit_arr), 8): byte = bit_arr[i:i+8] char = int(''.join(byte), 2) byte_arr.append(char) returnbytes(byte_arr)
# Values from output.txt enc_flag_hex = "a9fa3c5e51d4cea498554399848ad14aa0764e15a6a2110b6613f5dc87fa70f17fafbba7eb5a2a5179" text = "I_LOVE_RNG" plain_num = int.from_bytes(text.encode(), "big") seed = plain_num ** 7
flag_bits_enc = get_flag_bits(enc_flag_hex) num_bits = len(flag_bits_enc) indices = list(range(num_bits))
# Replay the shuffle on the indices for i inrange(10): random.seed(seed * (i + 1)) random.shuffle(indices)
# Map scrambled bits back to original positions flag_bits_orig = [None] * num_bits for j inrange(num_bits): flag_bits_orig[indices[j]] = flag_bits_enc[j]
My friend tells me there’s an office of a store that sells special
bricks above a well-known shopping centre on this street. My friend
tells me I’ll it will bring me joy, but I’m not sure that the bricks are
from the famous company. Can you give me the contact email address of
the store so I can learn more?
Flag format: UMASS{email@example.com}
Key clues: - “Special bricks” (possibly a LEGO alternative store) -
“Above a well-known shopping centre” - “Bring me joy” (might be part of
the store’s name)
Solution
By researching shopping centers in Hong Kong known for hobbyist
shops, we identified Ho King Commercial Centre
(好景商业中心) and Ho King Shopping Centre
(好景商场) as likely locations.
Using tools like Google Gemini Deep Search to find shops selling
“bricks” in these locations, we found a store associated with “Joy”
(matching the “bring me joy” hint). The store in question is
Joying Bricks.
Searching for their contact information lead to the official email
address.
Hey! A man was caught with malware on his PC in Lego City. Luckily,
we were able to get a packet capture of his device during the download.
Help Lego City Police figure out the source of this malicious
download.
The flag for this challenge is in the format UMASS{[sha256sum]} of
the malicious download.
Initial Analysis
The challenge provides a packet capture
(thedamage.pcapng) containing traffic from a machine that
downloaded malware. The goal is to identify the malicious file and its
hash.
Solution
Using tshark, we analyzed the HTTP traffic to see what
files were downloaded:
The launcher file appeared suspicious, but its file type
was obscured and it didn’t match known malware signatures.
Examining installer.py revealed it was a dropper script
that decrypts launcher:
1 2 3 4 5 6 7 8 9 10 11 12 13
import hashlib import nacl.secret
deffix_error(): seed = "38093248092rsjrwedoaw3" key = hashlib.sha256(seed.encode()).digest() # XSalsa20 + Poly1305 MAC box = nacl.secret.SecretBox(key) withopen("./launcher", "rb") as f: data = f.read() decrypted = box.decrypt(data) withopen("./launcher", "wb") as f: f.write(decrypted)
The script uses PyNaCl (SecretBox) to decrypt
launcher using the SHA256 hash of the seed
38093248092rsjrwedoaw3.
Decryption and Hashing
After decrypting the file, we calculated its SHA256 hash. The
decrypted file was identified as a
FreeBSD/i386 compact demand paged dynamically linked executable.
We are given a file cake. Binary STL files, introduced
in 1987, do not have a mandatory magic number. The first 80 bytes are
reserved for a header (comments/description), which in this case are all
zeros.
Because of this lack of a fixed signature, tools like
file may simply identify it as data.
Solution
Using ImHex with an STL file pattern, we can confirm
the file structure. The triangleCount field
(2a99 at offset 0x50) reveals that there are 39,210
triangles in the model.
1
triangleCount -> 2a99 -> 39210
By importing the model into Blender and zooming into
its interior, we can find the flag model floating in the center.
The challenge provides a GIF file (CHALL.gif).
Inspecting the file with standard tools reveals a 12-frame
animation.
File Analysis: Running
identify -verbose CHALL.gif shows that the image uses an
8-color Global Color Table (3-bit).
Palette Anomaly: The palette contains redundant
entries for almost identical colors. Specifically, the dark blue color
of the water (11, 41, 71) is mapped to both index
1 and index 3.
Solution
In many steganography challenges involving GIFs or indexed PNGs,
redundant palette indices are used to hide data. I wrote a script to
isolate these two specific indices by extracting the frames and
visualizing the pixels.
1 2 3 4 5 6 7 8 9 10 11
import PIL.Image import numpy as np
# 'P' 代表 Palette(调色板)模式 img = PIL.Image.open('frame-0.gif').convert('P') data = np.array(img)
# Visualize index 1 as '#' and everything else as '.' for r inrange(100): row_str = "".join(["#"if x == 1else"."for x in data[r]]) print(row_str)
By printing the pixel grid of the water area and specifically looking
for the “hidden” index (Index 1 vs Index 3), a clear ASCII art
representation of the flag appeared:
Decrypt tampered audit log entries to reconstruct evidence of
unauthorized access.
Initial Analysis
The challenge provides a secure audit log export from “ClinCore
Health Systems”. Scanning through the logs, several
EncryptedToken entries are visible, formatted as Base32
strings.
This indicates that the tokens are encrypted using a repeating 4-byte
XOR key.
Solution
While most tokens in the log decode to fragmented text, the token on
line 108
(YX2THEVPQ4LNRIMFCHIKFUCBRL2IGF6567KEFW7Q2AK5XIUEJXI7FUCE33VQ====)
decodes to raw binary data, suggesting it contains the flag.
Since the flag format is SDG{...}, we can perform a
known-plaintext attack to recover the 4-byte XOR key by XORing the first
4 bytes of the ciphertext with SDG{.
1 2 3 4 5 6 7 8 9 10 11 12
import base64
# The tampered token (line 108) ct = base64.b32decode("YX2THEVPQ4LNRIMFCHIKFUCBRL2IGF6567KEFW7Q2AK5XIUEJXI7FUCE33VQ====")
# Use "SDG{" as known plaintext to derive the 4-byte XOR key known = b"SDG{" key = bytes([ct[i] ^ known[i] for i inrange(4)])
# Decrypt with repeating 4-byte key plaintext = bytes([ct[i] ^ key[i % 4] for i inrange(len(ct))]) print(plaintext.decode())
Score! You found a treasure chest! Now if only you could figure out
how to unlock it… maybe there’s a magic word?
Initial Analysis
The challenge provides a Linux binary that prompts for a “magic
word.” If the correct word is entered, it displays a treasure chest
(ASCII art) and presumably the flag. The goal is to reverse-engineer the
“magic word” verification logic.
Using IDA Pro to decompile the main function, the
following program flow was identified:
Input Collection: The program reads up to 256
characters from stdin using fgets.
Padding & Allocation:
It calculates the input length (v8).
It determines a padding value (v7 = v8 % 8).
It allocates memory for the input plus padding and copies the string
into it.
Crucially, it appends v7 null bytes.
Encryption: It calls sub_4012A9, which
iterates through the input in 8-byte blocks and encrypts them using
sub_4011C6.
Verification: The program checks if the final
processed length v8 is 34 (0x22) and if
the resulting ciphertext matches a hardcoded byte array at
unk_404080.
The function sub_4011C6 implements a variation of the
Tiny Encryption Algorithm (TEA).
Key: The key is the hardcoded string
"tiny_encrypt_key".
Constants: It uses the standard TEA delta
0x9E3779B9.
Structure: It performs 32 rounds of Feistel-like
transformations.
Implementation Detail: Unlike standard TEA, it
updates the “sum” (v6) at the start of the loop and uses key indices
[0, 1] for the first half and [2, 3] for the
second half of the block update.
Extraction & Decoding
To solve the challenge, we extract the target ciphertext from
0x404080 and the 16-byte key. Since TEA is a symmetric
block cipher, we can implement a decryption routine.
Target Ciphertext (34 bytes):38 75 5B CB 44 D2 BE 5D 96 9C 56 43 EA 98 06 75 4A 48 13 E6 D4 E8 8E 4F 72 70 8B FF DC 99 F8 76 C5 C9
# Data from unk_404080 ciphertext = bytes.fromhex("38755BCB44D2BE5D969C5643EA9806754A4813E6D4E88E4F72708BFFDC99F876") key = b"tiny_encrypt_key"
flag = b"" for i inrange(0, len(ciphertext), 8): flag += tea_decrypt(ciphertext[i:i+8], key)
print(flag.decode().strip('\x00'))
Conclusion
Running the decryption yields the magic word/flag. The check for
length 34 and the specific padding logic suggests the original flag was
29 characters long, which when padded with 5 null bytes (29 % 8 = 5),
results in the 34-byte block compared by the binary.
You have recieved a message in a bottle, saying something about the
strange behavior of sea creatures. I wonder what that could be
about?
Initial Analysis
The challenge provides a rosbag2 recording consisting of
a metadata.yaml file and a SQLite database
mystery_message_0.db3. The metadata indicates that the
recording contains messages for several topics, most notably
/draw_commands.
Database Inspection
Opening the database with sqlite3, we find several
tables, including topics and messages. The
topics table reveals that the /draw_commands
topic uses the std_msgs/msg/String type and contains
JSON-formatted instructions.
1 2
SELECT*FROM topics WHERE name ='/draw_commands'; -- topic_id 6, type std_msgs/msg/String
Data Extraction
The messages in the messages table are stored in CDR
(Common Data Representation) format. For
std_msgs/msg/String, the actual string data starts after an
8-byte header (4 bytes CDR header + 4 bytes string length).
By extracting and parsing these strings, we find two types of
commands:
{"cmd": "pen", "off": 0/1, ...}: Controls whether the
turtle is drawing.
{"cmd": "teleport", "x": ..., "y": ..., "theta": ...}:
Moves the turtle to specific coordinates.
Visualization
Using a Python script with matplotlib, we can
reconstruct the path drawn by the turtle. By treating
teleport as a movement and pen as the draw
state, we can plot the lines.
The coordinates reveal two distinct rows of characters:
# Pagination A:\> dir /p # WideListFormat A:\> dir /w # Pipingtomore A:\> dir | more
cat.exe (type)
1 2 3 4 5 6 7 8 9 10 11 12 13
DOS <DIR> 12-15-255:07p COMMAND COM 54,64505-31-946:22a WINA20 3869,34905-31-946:22a CONFIG SYS 7112-15-255:07p AUTOEXEC BAT 7812-15-255:07p FLAG TXT 6004-02-265:07a 6 file(s) 64,203 bytes 10,428,416 bytes free C:\>typeflag.txt pwn.college{QMkbfHdYCkFD_y-FmBdS66_cyD2.******************} C:\>
ls.exe (dir)
DOS filenames were case-insensitive
DOS assumed that every file had a name of 8 characters or less and an
“extension” (after the one allowed . in the filename) of 3 characters or
less.
there is no permission or ownership information
As a side note, extensions were critical in DOS for similar reasons
that they remain important in Windows: figuring out the type of file.
Specifically for DOS, programs needed to be named with extensions of EXE
(normal DOS executable), COM (simple, older executables that were
basically just raw binary code), and BAT (a batch script containing
commands to run, analogous to a weird variant of a typical shell
script).
DOS assigned floppy drives the A: and B: drives, and “mounting”
happened automatically on access (or, more specifically, there was no
filesystem “mounting”; access just happened). The two letters were for
computers that had both a 5.25” and a 3.5” floppy drive
Any boot process includes not just the loading of the OS itself, but
its initialization. In DOS, this was elegantly handled by autoexec.bat,
a script that specified what commands would run when the system
booted.
you can access the various menu items using your Alt key (e.g., Alt-F
to open the File menu).
FUN FACT: doskey was introduced in MS-DOS 5.0 (1991) and was a
game-changer for productivity. Before doskey, if you made a typo in a
long command, you’d have to retype the whole thing!
edit and add doskey at top of autoexec.bat and quit relaunch
PKTCHK COM 85601-28-929:58p PKTMODE COM 1,72801-28-929:58p PKTMULTI COM 1,82201-28-929:59p PKTRING COM 2,42001-28-929:59p PKTSEND COM 2,34601-28-929:59p PKTSTAT COM 1,69301-28-929:59p PKTTRAF COM 6,01901-28-929:59p TERMIN COM 1,41901-28-929:58p TRACE COM 2,12401-28-929:58p WINPKT COM 3,61701-28-929:59p 18 file(s) 116,641 bytes 192,512 bytes free A:\PKTDRVR>pcntpkint=0x60 PacketdriverforanPCNTPK, version 03.10 Packetdriverskeletoncopyright 1988-92, CrynwrSoftware. Thisprogramisfreesoftware; seethefileCOPYINGfordetails. NOWARRANTY; seethefileCOPYINGfordetails. Packetdriverisatsegment 0BC8 Interruptnumber 0xB (11) I/Oport 0xC000 (49152) MyEthernetaddressis 52:54:00:12:34:56 A:\PKTDRVR>