misc
Challenges

Null Sanity

Are you ready for another great Nullcon event in Berlin?!
Awesome! Here's a first flag for your sanity: ENO{Let's_H4ve_S0m3_FuN_In_Berlin!}
ENO{Let's_H4ve_S0m3_FuN_In_Berlin!}
usbstorage

I attached my friend's USB drive to my laptop and accidently copied a private file, which I immediately deleted. But my friend still somehow got the file from looking at the USB message their drive recorded...
tl;dr: Well I'm pretty sure the intended solution was not simply carve the file out using dd
or binwalk
, but it is what it is. My teammate tnishamon was grinding on the second solution for this one (which is quite funny since bro was literally fighting for his life with this challenge and the atruecryptographer challenge frame by frame).
❯ binwalk -e usbstorage.pcapng
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------
1343984 0x1481F0 gzip compressed...
❯ cd _usbstorage.pcapng-0.extracted
❯ file 1481F0
1481F0: POSIX tar archive (GNU)
❯ tar -xvf 1481F0
flag.gz
❯ gunzip flag.gz
❯ cat flag
ENO{USB_STORAGE_SHOW_ME_THE_FLAG_PLS}
Now onto the second (intended?) solution, the pcapng file us usb mass stoarge messages hidden (hence the name).
The other approach requires parsing the pcapng
file to extract USB bulk OUT transfers, which contain the actual data written to the storage device. Here's how it works:
Step 1: Extract Frame Data Parses each frame in the packet capture, identifying USB bulk transfer packets and extracting their payloads. It correlates frame numbers with their corresponding data lengths to ensure accurate reconstruction.
Step 2: Hexdump Parsing: Parses hexdump-formatted output, extracting only the relevant hex bytes while ignoring ASCII representations.
Step 3: Data Reconstruction By taking the last
usb.data_len
bytes from each frame (representing the bulk OUT payload), reconstructs the complete data stream that was written to the USB storage device.Step 4: File Carving The reconstructed binary data contains a gzip-compressed tar archive starting at byte offset 24576.
If you are a visual learner~ here da go 🐧
┌─────────────────────────────────────────────────────────────────┐
│ usbstorage.pcapng │
│ (USB Packet Capture) │
└─────────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Parse USB Frames │
│ Frame 1: [USB Data] Frame 2: [USB Data] ... Frame N: [USB] │
└─────────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Extract USB Bulk OUT Transfers │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │Frame 123│ │Frame 456│ │Frame 789│ │Frame XYZ│ ... │
│ │12 34 56 │ │AB CD EF │ │78 9A BC │ │DE F0 12 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Reconstruct Binary Stream │
│ [12 34 56 AB CD EF 78 9A BC DE F0 12 ... ] → bulk_out.bin │
└─────────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ File Carving │
│ bulk_out.bin: [junk data...] [GZIP at offset 24576] [more...] │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ flag.tar.gz │ │
│ └─────────────┘ │
└─────────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Extract & Get Flag 🤷♂ │
│ tar xzf flag.tar.gz │
│ │ │
│ ▼ │
│ ENO{USB_STORAGE_SHOW_ME_THE_FLAG_PLS} │
└─────────────────────────────────────────────────────────────────┘
import re, binascii
# Load (frame -> usb.data_len)
lens = {}
for ln in open('out_lens.tsv'):
ln = ln.strip()
if not ln: continue
f, l = ln.split()
lens[int(f)] = int(l)
out = bytearray()
cur_frame = None
cur_hex = []
def flush():
global out, cur_frame, cur_hex
if cur_frame is None or cur_frame not in lens or not cur_hex:
return
# Gather all hex bytes in this frame
hexbytes = []
for line in cur_hex:
# hexdump lines look like: " 0000 12 34 56 ... |....|"
m = re.match(r'^\s*[0-9A-Fa-f]{4}\s+(.*)$', line)
if not m:
continue
# take only the left hex columns (stop before the ASCII)
left = m.group(1).split(' ')[0] # split before big gap to ASCII
for tok in left.split():
if re.fullmatch(r'[0-9A-Fa-f]{2}', tok):
hexbytes.append(tok)
frame_bytes = binascii.unhexlify(''.join(hexbytes)) if hexbytes else b''
need = lens[cur_frame]
# take the *last* usb.data_len bytes (bulk OUT payload)
if need > 0 and len(frame_bytes) >= need:
out += frame_bytes[-need:]
elif need > 0 and frame_bytes: # truncated capture, take whatever we have
out += frame_bytes
# reset
cur_hex.clear()
with open('out_vx.txt','r', errors='replace') as f:
for line in f:
m = re.match(r'^(?:Frame|Packet)\s+(\d+):', line)
if m:
flush()
cur_frame = int(m.group(1))
continue
if re.match(r'^\s*[0-9A-Fa-f]{4}\s', line):
cur_hex.append(line)
# last one
flush()
open('bulk_out.bin','wb').write(out)
print(f"Wrote bulk_out.bin ({len(out)} bytes)")
>
# carve the gzip member starting at byte 24576
dd if=bulk_out.bin of=flag.tar.gz bs=1 skip=24576 status=none
# sanity checks
file flag.tar.gz
gzip -t flag.tar.gz
tar -xvf flag.tar
cat flag
ENO{USB_STORAGE_SHOW_ME_THE_FLAG_PLS}
atruecryptographer (upsolve)

You know what I like most? Nullcon aftermovies and Kerckhoffs's principle! But since I'm a true cryptographer and a 1337 h4xx0r, I can even provide you my password without you ever finding my secrets: U"gkXYg;^#qXxJ(jm*jKik|N/gezj7)z
My question is: Are you a true cryptographer, too? Prove it by finding my secret!
https://static.enoflag.de/nullconctfberlin2025/nullcon-aftermovie.mp4
The challenge name is hinting towards this...osint as its finest. The MP4 is actually a TrueCrypt volume. That rat is disguised as a video file but is actually a TrueCrypt encrypted container. So to solve this. Download TrueCrypt
, and then shkaboom.
Mount the volume in TrueCrypt:
In TrueCrypt, click "Select File..."
Browse to and select the downloaded "aftermovie.mp4" file
Click "Mount"
ENO{Tru3_Cryp7_St3G0_F04_Ze_W1n!}
Tbh, this sh baffled me.
Last updated