> For the complete documentation index, see [llms.txt](https://thienguen.gitbook.io/ctf-writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://thienguen.gitbook.io/ctf-writeups/ctf/2025/nullconctf/misc.md).

# misc

## Challenges

<figure><img src="/files/u4AeVTLz3QRCxtG57sLB" alt=""><figcaption></figcaption></figure>

### Null Sanity

<figure><img src="/files/NZEnoayr3U7qFErUvF4W" alt=""><figcaption></figcaption></figure>

> 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

<figure><img src="/files/2X5Eofj03bLY3FGLJnqg" alt=""><figcaption></figcaption></figure>

> 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...
>
> [usbstorage.pcapng](https://ctf.nullcon.net/files/4602b791c636c378e5f49406e4e9e8e5/usbstorage.pcapng.gz?token=eyJ1c2VyX2lkIjozNzY2LCJ0ZWFtX2lkIjoxNjQyLCJmaWxlX2lkIjo4OX0.aLtxTQ.6j7v6lHg1lkWOTIKOk8JeM4Z5so)

{% hint style="success" %}
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).
{% endhint %}

```bash
❯ 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}                │
└─────────────────────────────────────────────────────────────────┘
```

{% hint style="info" %}
Script
{% endhint %}

```python
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

<figure><img src="/files/YLwQ2vZvQuDEU9GDucE8" alt=""><figcaption></figcaption></figure>

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

{% embed url="<https://www.truecrypt.org/downloads>" %}

{% hint style="success" %}
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 <mark style="color:blue;">**`TrueCrypt`**</mark>, and then shkaboom.
{% endhint %}

* **Mount the volume in TrueCrypt**:

  * In TrueCrypt, click "Select File..."
  * Browse to and select the downloaded "aftermovie.mp4" file
  * Click "Mount"

  <figure><img src="/files/KIiiDhdC1Zykg8Oj0erw" alt=""><figcaption></figcaption></figure>

  <figure><img src="/files/gi3oG4Bvzm4PvX24jaTH" alt=""><figcaption></figcaption></figure>
* **`ENO{Tru3_Cryp7_St3G0_F04_Ze_W1n!}`**
* Tbh, this sh baffled me.
