DISKO 1 picoCTF Writeup


I spent over an hour doing everything wrong on picoCTF DISKO 1 before the flag basically yelled at me. The challenge gives you a compressed FAT32 disk image and asks you to find a hidden flag. My instinct was to mount it, analyze partitions, and dig through the filesystem like a proper forensics investigator. The flag had other ideas. This writeup is mostly about that wasted hour — because the lesson here isn’t the two-line solution, it’s understanding why every other approach fails first.


Challenge Overview

CTF: picoCTF
Challenge name: DISKO 1
Category: Forensics
Difficulty: Easy
Given file: disko-1.dd.gz

The challenge description is sparse — something about a disk image with a hidden flag. No hints about what filesystem, what tool, or where in the image the flag lives. Just a .dd.gz file and go.

I ran file on it first:

$ file disko-1.dd.gz
disko-1.dd.gz: gzip compressed data, was "disko-1.dd", last modified: ..., from Unix

Straightforward — gzip-compressed disk image. I gunzipped it and checked what we had:

$ gunzip disko-1.dd.gz
$ file disko-1.dd
disko-1.dd: DOS/MBR boot record, code offset 0x58+2, OEM-ID "mkfs.fat", sectors/cluster 4,
root entries 512, sectors 20480 (volumes <=32 MB), Media descriptor 0xf8,
sectors/FAT 20, sectors/track 32, heads 64, serial number 0x672d4c8c,
unlabeled, FAT (16 bit)

FAT16 disk image. From here I made what felt like a completely reasonable decision — and then kept making bad decisions for the next hour.

The Rabbit Hole: An Hour of Doing It the Hard Way

Attempt 1: Mounting the image (~15 minutes lost)

My first instinct on any disk image challenge is to mount it and browse the filesystem. It's what forensics investigators do with real disks, and I assumed the flag would be sitting in some file I could just open.

$ mkdir /tmp/disko
$ sudo mount -o loop disko-1.dd /tmp/disko
$ ls -la /tmp/disko/

The filesystem mounted fine, but the directory was completely empty. I tried ls -la, checked for hidden files with ls -A, even ran find /tmp/disko -name "*" — nothing. Just an empty FAT16 volume.

The reason this fails matters: mounting a FAT image only shows you the active filesystem entries. Any file that was deleted, or data written directly to disk sectors without going through the filesystem, is invisible via mount. That's actually the whole point of low-level disk forensics — the filesystem is just one view of the raw data.

Also worth noting: sudo mount modifies the image's access timestamps. On a real forensics case that's contamination. Always work on a copy, or use read-only mount options (-o loop,ro).

Attempt 2: binwalk (~25 minutes lost)

Empty mount, so maybe there's something embedded in the image itself. binwalk is my go-to for that.

$ binwalk disko-1.dd

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             DOS MBR boot record, code offset: 0x58, disk signature: 0x672D4C8C
512           0x200           FAT, 16-bit, sectors/FAT: 20, sector size: 512

Just the FAT boot sector and partition metadata. No JPEG signatures, no zip files, no nested archives. I ran binwalk -e disko-1.dd anyway hoping for something, got only the raw FAT data extracted, nothing useful.

binwalk finds embedded files by their magic byte signatures. If the flag is stored as plain text in a slack space or deleted sector — no magic bytes, nothing to find.

Attempt 3: fdisk and Sleuth Kit (~20 minutes lost)

Maybe there's a hidden partition I'm missing. I checked the partition table:

$ fdisk -l disko-1.dd
Disk disko-1.dd: 10 MiB, 10485760 bytes, 20480 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x672d4c8c

Device       Boot Start   End Sectors  Size Id Type
disko-1.dd1        32  20479   20448  9.9M  4 FAT16 <32M

One partition, FAT16, nothing hidden. I tried The Sleuth Kit anyway — fls to list files including deleted ones, icat to pull raw inode content:

$ fls -r disko-1.dd
(no output)

$ fls -r -d disko-1.dd
(no output)

$ fsstat disko-1.dd
FILE SYSTEM INFORMATION
--------------------------------------------
File System Type: FAT16
...
METADATA INFORMATION
--------------------------------------------
Range: 2 - 2
Root Directory Range: 2 - 2

CONTENT INFORMATION
--------------------------------------------
Sector Size: 512
Cluster Size: 2048
Total Cluster Range: 2 - 2499
...
FAT CONTENTS (in sectors)
--------------------------------------------
(no entries)

The FAT has zero directory entries — no active files, no deleted files, nothing. The filesystem structure is completely empty. If the flag existed as a proper file, fls -d (show deleted) would have caught it. It didn't, because there was never a FAT entry for the flag at all.

At this point I'd been staring at this for about an hour. I stepped back and re-read the challenge name: DISKO 1. And the filename: disko-1.dd. And then I thought about the simplest possible thing you can do with a binary file when you don't know what's in it.

The Solution: What I Should Have Tried First

$ strings disko-1.dd | grep "pico"
picoCTF{1t5_ju5t_4_5tr1n9_be6031da}

That's it. Two seconds. The flag was sitting in raw disk sectors the entire time, untouched by anything I'd done. I read that flag — 1t5_ju5t_4_5tr1n9 — and decoded the leet speak: 1=i, t=t, 5=s, 4=a. "it's just a string." The challenge name, the filename, and the flag text were all pointing at the same two-word answer. I just refused to listen for an hour.

Why strings works here

strings doesn't care about filesystem structure, headers, or file formats. It scans the entire binary file and pulls out any sequence of printable ASCII characters above a minimum length (default: 4 characters). Since the flag was written directly to a disk sector as plain text — not inside a file, just raw bytes on the disk — the filesystem tools couldn't find it, but strings could.

FAT16 divides a disk into fixed-size sectors (512 bytes each) grouped into clusters. The filesystem maintains two structures: a File Allocation Table (the FAT itself) that chains clusters into files, and a root directory that maps filenames to their starting cluster. If you write bytes directly to a data sector using dd without creating a FAT entry, those bytes exist physically on disk but are completely invisible to any filesystem-aware tool. No filename, no cluster chain, no directory entry — just raw bytes at a known offset.

You can confirm exactly where the flag lives using strings -t x to get the hex offset, then verify with xxd:

$ strings -t x disko-1.dd | grep "pico"
 29800 picoCTF{1t5_ju5t_4_5tr1n9_be6031da}

$ xxd -s 0x29800 -l 64 disko-1.dd
00029800: 7069 636f 4354 467b 3174 355f 6a75 3574  picoCTF{1t5_ju5t
00029810: 5f34 5f35 7431 7233 6e39 5f62 6536 3033  _4_5tr1n9_be603
00029820: 3164 617d 0000 0000 0000 0000 0000 0000  1da}............

Offset 0x29800 is sector 211 (0x29800 / 512 = 211), sitting in the data region of the FAT16 partition. No cluster chain, no directory entry — just 36 bytes of ASCII text written directly to disk.

This is also how deleted file recovery works in the real world: after you delete a file, the FAT entry is removed but the actual data sectors aren't zeroed out until they're overwritten. Forensics tools like photorec and commercial software like EnCase recover data exactly this way — by scanning raw sectors, not trusting the filesystem's version of what's there.

Capture the Flag

picoCTF{1t5_ju5t_4_5tr1n9_be6031da}

The flag itself is the lesson: it's just a string. Start with strings.

Full Trial Process

StepActionCommandResultWhy it failed / succeeded
1Identify file typefile disko-1.dd.gzgzip compressed disk image✅ Correct first step
2Decompressgunzip disko-1.dd.gzdisko-1.dd (FAT16 image)✅ Required before any analysis
3Mount imagesudo mount -o loop disko-1.dd /tmp/diskoEmpty directory❌ Data written directly to sectors, not as a filesystem file
4binwalk scanbinwalk disko-1.ddOnly FAT boot sector detected❌ No magic byte signatures — plain text has none
5Partition inspectionfdisk -l disko-1.ddSingle FAT16 partition, nothing hidden❌ No hidden partitions
6Sleuth Kit file listfls -r disko-1.ddNo output❌ No FAT directory entries exist, not even deleted
7Raw string scanstrings disko-1.dd | grep "pico"Flag found immediately✅ Scans raw bytes regardless of filesystem structure

Command Reference

strings

strings is part of GNU binutils. It extracts sequences of printable characters from binary files. The default minimum length is 4 characters, which you can adjust with -n:

# Default (4+ char sequences)
strings disko-1.dd | grep "pico"

# Longer minimum (reduces noise)
strings -n 8 disko-1.dd | grep "pico"

# Show file offsets of each string
strings -t x disko-1.dd | grep "pico"

The -t x flag (print offset in hex) is useful when you want to confirm where in the disk the string lives and use dd or xxd to inspect surrounding bytes. See the GNU binutils documentation for full options.

gunzip / gzip

Disk images are often compressed to reduce transfer size. gunzip decompresses .gz files in place. If you want to preserve the original: gunzip -k disko-1.dd.gz (keep original). For .tar.gz archives containing disk images: tar xzf archive.tar.gz.

file

file reads a file's magic bytes to identify its type regardless of extension. Always run this first on unknown files — extensions in CTFs are sometimes wrong or misleading.

Beginner Tips

Why mounting returns an empty directory

A FAT filesystem maintains a directory table that maps filenames to disk sectors. If data is written directly to the disk without creating a directory entry, mount finds no files to show. This isn't a bug — the filesystem is technically empty. The data exists at the raw byte level but is invisible to the OS's file layer.

The forensics tool ladder

For disk image challenges, I now use this rough order before reaching for heavy tools:

  1. file — confirm file type
  2. strings | grep "pico" — check for plaintext flags immediately
  3. binwalk — scan for embedded files
  4. mount — browse active filesystem
  5. fls / icat (Sleuth Kit) — recover deleted files
  6. xxd / hex editor — raw byte inspection

Running strings | grep "pico" costs five seconds. On DISKO 1, it would have saved an hour. I now run it before anything else on every disk image challenge.

strings gives false positives — here's how to filter

On larger disk images, strings can produce thousands of lines. Use a tight grep pattern:

# picoCTF flags always start with "picoCTF{"
strings image.dd | grep "picoCTF{"

# Case-insensitive if you're unsure
strings image.dd | grep -i "pico"

# Show 2 lines of context around matches
strings image.dd | grep -A2 -B2 "picoCTF{"

Why "sudo mount" can be a problem

Mounting a filesystem — even read-only — can update access timestamps on some filesystems. In CTFs this doesn't matter, but in real forensics it's evidence contamination. The safe habit is to always create a copy first (cp disko-1.dd disko-1.dd.bak) and work on the copy, or use -o loop,ro for a read-only mount.

What You Learn

strings before everything else on disk images. The reflex to mount, analyze partitions, and run forensics tools is correct for harder challenges — but strings | grep is a two-second check that costs nothing. Running it first has saved me a lot of time on CTF problems where the flag is just sitting in raw sectors.

Filesystem tools only see filesystem data. mount, fls, and icat all rely on FAT directory entries. Data written below the filesystem layer is invisible to them. This is foundational for understanding disk forensics: the filesystem is an abstraction over raw bytes, and raw bytes can contain information the filesystem never knew about.

The challenge title is a hint. "DISKO 1" and the flag text "1t5_ju5t_4_5tr1n9" are explicit signals pointing at strings. CTF problem names are almost always hints about the intended solution path. Reading them carefully before diving into tools is worth the 30 seconds.

Real-world parallel: This exact scenario — data hidden in raw disk sectors outside the filesystem — appears in digital forensics investigations. Malware sometimes hides payloads in unallocated sectors or disk slack space specifically because most OS-level scanning won't catch it. Tools like strings, photorec, and Autopsy exist precisely for this: they scan raw bytes instead of trusting the filesystem's version of events.

Next time I'd solve this in under 30 seconds: gunzip, then immediately strings | grep "pico". If that fails, then mount and escalate. The lesson from an hour of wrong turns is that the simplest tool should always come first.

Further Reading

This problem is part of the picoCTF Forensics series. If you want to understand the broader toolkit for these challenges, CTF Forensics Tools: The Ultimate Guide for Beginners covers the full landscape — from strings and binwalk through Sleuth Kit and memory forensics tools.

Here are related articles from alsavaudomila.com that complement this challenge:

The strings command appeared here in its simplest form, but it's a surprisingly deep tool. strings in CTF: How to Extract Hidden Data from Binaries covers the full range of flags, common patterns in CTF problems, and how to filter output on noisy binaries.

Disk image analysis is a recurring theme in picoCTF Forensics. binwalk in CTF: Spot False Positives Fast goes deeper into the tool that came up empty here — including how to distinguish real embedded files from binwalk's false positive DER certificate hits.

For challenges where mounting actually is the right call, fdisk in CTF: Partition Analysis and Common Challenge Patterns walks through how to read partition tables, identify filesystem types, and work with disk images that have multiple partitions.

投稿をさらに読み込む