DISKO 3 picoGym Exclusive Solution

Published: March 5, 2024

Description

A disk image holds a flag that strings alone cannot find - it is compressed inside a .gz file buried in a subdirectory. Mount the image and decompress the hidden file to read it.

Download disko-3.dd.gz from the picoGym challenge page.

Decompress the outer archive, then prepare a mount point.

bash
gunzip disko-3.dd.gz
bash
file disko-3.dd
bash
sudo mkdir -p /mnt/disko3

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1
    Decompress and attempt strings
    Observation
    I noticed the outer archive is a .gz file, so decompressing it first is mandatory before any analysis; running strings | grep pico is the fastest cheap recon to check whether the flag is stored as plain text before committing to heavier filesystem techniques.
    Run gunzip then try strings disko-3.dd | grep -i pico. This time nothing useful appears - the flag is not stored as plain text. The image is FAT32 like DISKO 1, but the flag is hidden inside a compressed file.
    bash
    gunzip disko-3.dd.gz
    bash
    file disko-3.dd
    bash
    strings disko-3.dd | grep -i pico
    What didn't work first

    Tried: Try strings with a wider minimum-length to catch any encoded flag fragments.

    Running strings -n 4 disko-3.dd | grep -i pico still returns nothing because the flag content is stored compressed, so the bytes are non-printable ciphertext-like data with no recognizable ASCII run. Lowering the minimum string length floods output with noise but never surfaces the flag. The correct approach is to mount the image and inspect the actual filesystem rather than scanning raw bytes.

    Tried: Use binwalk to carve compressed streams directly from the raw image.

    Running binwalk -e disko-3.dd will detect and extract the gzip stream from the image, but it often places the extracted file in a _disko-3.dd.extracted/ directory with a numeric offset name (e.g., 0.gz) that is easy to overlook. If the carved stream is corrupted or binwalk mis-identifies the boundary, gunzip will fail with a CRC error. Mounting the image cleanly with -o loop gives direct filesystem access to the correctly named flag.gz and avoids these extraction artifacts.

    Learn more

    When strings | grep returns nothing, the flag is not stored as raw ASCII in the disk image. It may be compressed (gzip, bzip2, xz), encoded (base64, hex), encrypted, or stored in a format that produces non-printable bytes. This is the moment to mount the image and inspect the actual file structure instead of searching raw bytes.

    A compressed file stored inside a filesystem appears to strings as a burst of random-looking bytes (the compressed data) bookended by a few readable bytes from the gzip header (the magic bytes 1f 8b and the original filename, if stored). The flag content itself is invisible until decompressed.

  2. Step 2
    Mount the image and navigate the filesystem
    Observation
    I noticed strings | grep returned nothing useful, which suggested the flag is stored inside a compressed file on a real filesystem rather than as raw bytes, so mounting the image with -o loop was the next logical step to browse its directory tree directly.
    Mount the disk image with mount -o loop to browse it as a live filesystem. Navigate to the /log/ directory where flag.gz is located.
    bash
    sudo mount -o loop disko-3.dd /mnt/disko3
    bash
    ls /mnt/disko3/
    bash
    ls /mnt/disko3/log/
    What didn't work first

    Tried: Mount the image without the loop option, passing the raw filename directly to mount.

    Running sudo mount disko-3.dd /mnt/disko3 without -o loop fails with "mount: /mnt/disko3: can only mount block devices" because the kernel requires a real block device, not a regular file. The -o loop flag tells the kernel to attach a loop device (e.g., /dev/loop0) to the file first, presenting it as a virtual block device that FAT32 mounting can operate on.

    Tried: Use fdisk -l disko-3.dd to find a partition offset, then mount with -o offset= instead of loop.

    fdisk may report a partition start sector, and calculating offset = sector * 512 can work when the image contains a partitioned disk with an MBR. However, disko-3.dd is an unpartitioned FAT32 image (no MBR, filesystem starts at sector 0), so an explicit offset mount produces "wrong fs type" or a garbled directory listing. The simple -o loop mount without an offset is correct here.

    Learn more

    The -o loop option tells the Linux kernel to use a loop device - a virtual block device that maps a regular file as if it were a physical disk. This lets you mount a disk image file exactly like a real drive. The kernel reads the FAT32 superblock from the image and makes the filesystem accessible at the mount point.

    In professional forensics, images are mounted read-only (add -o ro,loop) to preserve evidence integrity. A write-enabled mount could update access timestamps, modify journal entries, or trigger filesystem repair operations that alter the evidence. For CTF purposes, a read-only mount is still good practice even though there is no legal chain-of-custody requirement.

    Once mounted, standard Unix commands (ls, find, cat) work normally against the filesystem. This is often the most convenient way to browse a disk image when the flag is buried in a directory tree rather than scattered as raw bytes.

  3. Step 3
    Extract and decompress the flag file
    Observation
    I noticed flag.gz in the /log/ directory of the mounted image, and the .gz extension directly indicated the flag was gzip-compressed, so copying it to /tmp/ and running gunzip was the clear path to recover the plaintext flag.
    Copy flag.gz from the mounted image to a writable location, then decompress it with gunzip and read it with cat. The flag appears as plain text.
    bash
    cp /mnt/disko3/log/flag.gz /tmp/flag.gz
    bash
    gunzip /tmp/flag.gz
    bash
    cat /tmp/flag
    bash
    sudo umount /mnt/disko3
    What didn't work first

    Tried: Decompress flag.gz in place directly on the mounted filesystem instead of copying it out first.

    Running sudo gunzip /mnt/disko3/log/flag.gz attempts to write the decompressed file back onto the mounted image. If the mount is read-only (as recommended with -o ro,loop), gunzip fails with "Read-only file system". Even on a read-write mount, writing back to the image is unnecessary and risks corrupting it. Copying to /tmp/ first keeps the source image pristine and works regardless of mount permissions.

    Tried: Use zcat /mnt/disko3/log/flag.gz to print the decompressed contents without copying or creating a temp file.

    zcat is a valid shortcut and will work to print the flag to stdout. However, if the decompressed output is not a text file or contains a trailing newline, the terminal may garble the display. More importantly, zcat does not remove the .gz file, so beginners relying solely on this may not realize they still need to unmount cleanly. The explicit cp-then-gunzip-then-cat sequence makes each transformation visible and is easier to debug if any step fails.

    Learn more

    Copying the compressed file out of the mounted image before decompressing it is cleaner than decompressing in place on the mounted filesystem - it avoids any risk of writing to the image and keeps the analysis tidy. The gunzip command decompresses flag.gz in place, producing a file named flag, which cat then displays.

    This challenge pattern - a flag compressed and hidden in a non-obvious subdirectory - is common in CTF forensics. The key skill is knowing when raw-byte techniques like strings will not work and switching to filesystem-level exploration. The lesson from the DISKO series as a whole: try the quick approach first (strings | grep), but be ready to mount, navigate, and decompress when the flag is not plaintext.

    Always unmount (sudo umount) after analysis. Leaving a loop-mounted image open can cause issues if you later try to delete or move the disk image file, and on some systems the loop device will remain reserved until explicitly released.

Interactive tools
  • Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.
  • File Magic IdentifierIdentify file types from magic numbers. Paste hex bytes or drop a file to detect PNG, JPEG, ZIP, PDF, ELF, PCAP, SQLite, and dozens of other formats.
  • Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.

Flag

Reveal flag

picoCTF{n3v3r_z1p_2_h1d3...}

Mount the image with `mount -o loop`, find `flag.gz` in the `/log/` directory, gunzip it, and cat the result.

Key takeaway

strings cannot find flags stored inside compressed files because the payload bytes are non-printable after compression; when strings/grep returns nothing on a disk image, mount it with loop to browse the real directory tree and check whether the flag is wrapped in a layer like gzip. The lesson from the DISKO series is to try the fast approach first but recognize when silence means the flag is encoded rather than stored plainly, and to switch to filesystem-level exploration rather than raw-byte scanning.

Related reading

Want more picoGym Exclusive writeups?

Useful tools for Forensics

What to try next