Skip to main content

Disk, disk, sleuth! II picoCTF 2021 Solution

Use filesystem forensic tools to navigate a disk image and recover a file from the directory structure.

Published: April 2, 2026Updated: August 13, 2026

Description

All we know is the file is called 'down-at-the-bottom.txt'. Use Sleuth Kit inode tools on dds2-alpine.flag.img.gz to find and read it.

Download and decompress the disk image.

bash
wget <url>/dds2-alpine.flag.img.gz
bash
gunzip dds2-alpine.flag.img.gz

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1Find the partition offset with mmls
    Observation
    The challenge provides a raw disk image, not a bare filesystem, so there is a partition table in front. Find where the target partition starts before any Sleuth Kit tool can read it.
    Run mmls to print the partition table. The Linux partition's starting sector is what you'll feed to -o on the next commands.
    bash
    mmls dds2-alpine.flag.img
    What didn't work first

    Tried: Run fls directly on the raw image without the -o offset, e.g. fls dds2-alpine.flag.img

    Without -o, fls reads the very start of the image as a filesystem superblock and finds the MBR partition table instead, so it returns no entries or an error about not determining the file system type. The ext2 filesystem starts at sector 2048, not sector 0, and mmls is what tells you that.

    Tried: Skip mmls and guess the offset by trying common values like -o 63 or -o 512

    Older MBR-partitioned disks put the first partition at a 63-sector offset, but modern images, this one included, align to 2048-sector boundaries. -o 63 gives an invalid superblock error. Read the offset from the partition table with mmls rather than assuming it.

    Learn more

    Sample mmls output on this image:

    DOS Partition Table
    Offset Sector: 0
    Units are in 512-byte sectors
    
          Slot      Start        End          Length       Description
    000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
    001:  -------   0000000000   0000002047   0000002048   Unallocated
    002:  000:000   0000002048   0000262143   0000260096   Linux (0x83)

    The Linux partition starts at sector 2048, so every subsequent TSK command needs -o 2048.

  2. Step 2Find the file's inode number with fls
    Observation
    The challenge names the target file, down-at-the-bottom.txt, but gives no path and no inode. fls can list every filesystem entry recursively from the partition offset, and grepping that output produces the inode.
    Use fls to list filesystem entries in the image. The -r flag recurses, -p prints full paths, -o 2048 is the partition offset from mmls. Grep for the filename and read the inode column.
    bash
    fls -r -p -o 2048 dds2-alpine.flag.img | grep down-at-the-bottom
    What didn't work first

    Tried: Mount the disk image with mount -o loop,offset=1048576 and then use find to locate the file

    Computing the byte offset, 2048 sectors times 512 bytes, and mounting does work on a live system with root. But forensic practice avoids mounting evidence images, because the OS may write access-time updates or journal entries and alter the image. fls reads the filesystem structures directly, read-only.

    Tried: Run fls without -r and manually navigate directories by inode, passing each directory inode back to fls

    fls without -r lists one directory at a time, leaving you to descend into each subdirectory by hand. The -r flag recurses on its own, and piping to grep is far faster. The manual route also needs the root inode, usually 2 on ext2, which is another step.

    Learn more

    Reading fls output. A typical line looks like this; the inode number is the second column:

    r/r 18582:	root/down-at-the-bottom.txt
    ^   ^         ^
    |   |         path (full because of -p)
    |   inode (this is what you pass to icat)
    type (regular file / regular file)

    Inodes are the data structures in Unix filesystems (ext2/3/4, UFS) that store file metadata: permissions, timestamps, owner, and pointers to the data blocks. Every file and directory has an inode number. The filename is stored in the directory entry, which maps names to inode numbers.

    fls (file listing) from The Sleuth Kit lists directory entries directly from the raw disk image without mounting it. The partition offset -o 2048 tells TSK where the filesystem starts within the disk image (in 512-byte sectors), exactly the value mmls reported.

  3. Step 3Extract the file contents with icat
    Observation
    fls returns the inode number, and the prompt asks for Sleuth Kit inode tools. icat is the one that reads a file's data blocks straight out of the raw image given nothing but that inode.
    Use icat with the inode number found in the previous step to extract and print the file contents. icat reads file data blocks directly from the raw image given just the inode number.
    bash
    icat -o 2048 dds2-alpine.flag.img <inode_number>
    What didn't work first

    Tried: Use icat without -o 2048, running icat dds2-alpine.flag.img <inode_number>

    Without the partition offset, icat reads the start of the raw image as a filesystem, but that region is the MBR, not ext2. It either errors out or returns garbage data blocks rather than the file. Every TSK command touching files inside the partition needs -o 2048.

    Tried: Copy the inode number from the fls column but accidentally use the file type prefix digits instead

    The fls line reads 'r/r 18582: root/down-at-the-bottom.txt'. The inode is 18582, but the r/r type prefix and the colon make it easy to copy the wrong thing. A wrong number sends icat to an unrelated inode's data blocks, giving garbled output or nothing at all.

    Learn more

    icat (inode cat) extracts the content of a file given its inode number, reading directly from the raw disk image. This works even for deleted files (whose directory entries have been removed but whose inode and data blocks have not yet been overwritten). It is a key tool in deleted file recovery.

    The workflow of fls (find inode) followed by icat (extract content) is the standard TSK pattern for targeted file recovery from disk images, equivalent to find plus cat on a live filesystem but operating on the raw image. For memory-side forensics with a similar inspect-without-mounting workflow, see Volatility 3 for memory forensics.

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{f0r3ns1c4t0r_n0v1c3_...}

fls lists filesystem entries (including deleted files) by inode; icat extracts a file's content given its inode number - critical for deleted file recovery.

Key takeaway

Unix filesystems separate filenames from file data through inodes: directory entries map names to inode numbers, and inodes point at the data blocks. The Sleuth Kit uses that separation to enumerate and extract files from raw images without mounting them, which matters when a filesystem is damaged, volume-encrypted, or evidence that must not change. The same model is why deleted files stay recoverable until their inode and blocks are reallocated, a property investigators rely on constantly.

Related reading

Useful tools for Forensics

Where to go next