In this post, we’ll walk through the Operation Oni and Operation Orchid challenges from the PicoCTF competition held in March 2022. Both of these challenges involve the use of tools from The Sleuth Kit suite. In order to follow along, I’d recommend installing the suite of tools.

Operation Oni

The challenge has an associated instance which we’ll need to log into using SSH using the following command:

ssh -i key_file -p 61948 ctf-player@saturn.picoctf.net

We are provided with a compressed disk image disk.img.gz which we’ll decompress with:

gunzip disk.img.gz

To list the partition table for the given disk image, we will use the mmls command. If mmls is unable to properly determine the filesystem of a given volume, we can specify it using the -t flag.

Let’s run mmls on the disk image by itself.

mmls disk.img
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   0000206847   0000204800   Linux (0x83)
003:  000:001   0000206848   0000471039   0000264192   Linux (0x83)

We can see two linux partitions, one starting at offset 2048 and another at 206848

In order to list the files in a given volume, we can use the fls command. For this, we will need to specify the offset of the volume using the -o flag.

Let’s take a look at the first partition at offset 2048.

fls -o 2048 disk.img
d/d 11: lost+found
r/r 12: ldlinux.sys
r/r 13: ldlinux.c32
r/r 15: config-virt
r/r 16: vmlinuz-virt
r/r 17: initramfs-virt
l/l 18: boot
r/r 20: libutil.c32
r/r 19: extlinux.conf
r/r 21: libcom32.c32
r/r 22: mboot.c32
r/r 23: menu.c32
r/r 14: System.map-virt
r/r 24: vesamenu.c32
V/V 25585:      $OrphanFiles

This looks like the boot partition of a linux installation. Let’s move on to the next partition at offset 206848.

fls -o 206848 disk.img
d/d 458:        home
d/d 11: lost+found
d/d 12: boot
d/d 13: etc
d/d 79: proc
d/d 80: dev
d/d 81: tmp
d/d 82: lib
d/d 85: var
d/d 94: usr
d/d 104:        bin
d/d 118:        sbin
d/d 464:        media
d/d 468:        mnt
d/d 469:        opt
d/d 470:        root
d/d 471:        run
d/d 473:        srv
d/d 474:        sys
V/V 33049:      $OrphanFiles

This looks like the standard linux filesystem hierarchy where we can see the /home and /root directories. Let’s investigate the /root directory. To do so, we will append the inode number associated with the directory as an argument to fls.

The inode number of /root is 470 here.

d/d 470:        root

We’ll run the previous command with the inode number.

fls -o 206848 disk.img 470
r/r 2344:       .ash_history
d/d 3916:       .ssh

Here, we can see the .ssh directory and the root user’s shell history file. We’ll try listing the .ssh directory. Again, we’ll supply the associated inode number, here, 3916.

fls -o 206848 disk.img 3916
r/r 2345:       id_ed25519
r/r 2346:       id_ed25519.pub

Here, we can see a pair of SSH private and public keys. The one ending in .pub being the public key. Private keys are often used as an alternative to password authentication to SSH into a machine. We’ll dump the content of this file using the icat command. For using this command, we’ll need to specify the offset of the volume and the inode number of the file, 2345 here.

We’ll redirect the output of the command into a file called key_file.

icat -o 206848 disk.img 2345 > key_file

We can try using the private key to authenticate since this key is not password protected. Before running the SSH command, we must set the permissions on the file to read / write only by us.

chmod 600 key_file

Let’s use the command that was provided with the challenge.

ssh -i key_file -p 61948 ctf-player@saturn.picoctf.net

We get a successful login as the user ctf-player. Let’s list the files in our home directory.

ctf-player@challenge:~$ ls
flag.txt

We’ll view the contents of the flag.txt file.

ctf-player@challenge:~$ cat flag.txt
picoCTF{k3y_5l3u7h_af277f77}

Operation Orchid

We are provided with a disk image disk.flag.img.gz which we’ll decompress with:

gunzip disk.flag.img.gz

Let’s look at the partition table using mmls.

mmls disk.flag.img
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   0000206847   0000204800   Linux (0x83)
003:  000:001   0000206848   0000411647   0000204800   Linux Swap / Solaris x86 (0x82)
004:  000:002   0000411648   0000819199   0000407552   Linux (0x83)

Listing the volume at offset 2048 using fls, we see a boot partition.

fls -o 2048 ./disk.flag.img
d/d 11: lost+found
r/r 12: ldlinux.sys
r/r 13: ldlinux.c32
r/r 15: config-virt
r/r 16: vmlinuz-virt
r/r 17: initramfs-virt
l/l 18: boot
r/r 20: libutil.c32
r/r 19: extlinux.conf
r/r 21: libcom32.c32
r/r 22: mboot.c32
r/r 23: menu.c32
r/r 14: System.map-virt
r/r 24: vesamenu.c32
V/V 25585:      $OrphanFiles

We’ll move on to the next Linux partition at offset 411648.

fls -o 411648 ./disk.flag.img
d/d 460:        home
d/d 11: lost+found
d/d 12: boot
d/d 13: etc
d/d 81: proc
d/d 82: dev
d/d 83: tmp
d/d 84: lib
d/d 87: var
d/d 96: usr
d/d 106:        bin
d/d 120:        sbin
d/d 466:        media
d/d 470:        mnt
d/d 471:        opt
d/d 472:        root
d/d 473:        run
d/d 475:        srv
d/d 476:        sys
d/d 2041:       swap
V/V 51001:      $OrphanFiles

Let’s try listing the home folder of the root user at /root.

d/d 472:        root

We’ll use its inode number, 472.

fls -o 411648 ./disk.flag.img 472
r/r 1875:       .ash_history
r/r * 1876(realloc):    flag.txt
r/r 1782:       flag.txt.enc

We can dump the contents of the flag.txt file using icat like we did previously.

icat -o 411648 ./disk.flag.img 1876
           -0.881573            34.311733

A set of coordinates? Latitudes and longitudes? Not very helpful. Let’s dump flag.txt.enc to a file we can work on later.

icat -o 411648 ./disk.flag.img 1782 > flag.txt.enc

Let’s investigate the shell history to see what the root user was upto the last time.

icat -o 411648 ./disk.flag.img 1875
touch flag.txt
nano flag.txt 
apk get nano
apk --help
apk add nano
nano flag.txt 
openssl
openssl aes256 -salt -in flag.txt -out flag.txt.enc -k unbreakablepassword1234567
shred -u flag.txt
ls -al
halt

Ah! So they encrypted the original flag.txt with AES256 using the openssl command. We can see the key that was supplied to the command with the -k flag.

Now that we know the key, we can decrypt the flag.txt.enc file.

We’ll use the -d flag for decryption, set the input file, the argument to the -in flag, to flag.txt.enc and omit the -out flag so that it outputs to stdout.

openssl aes256 -d -salt -in flag.txt.enc -k unbreakablepassword1234567
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
bad decrypt
140377178797312:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:crypto/evp/evp_enc.c:610:
picoCTF{h4un71ng_p457_17237fce}

There we have it, we’ve captured the flag.