PicoCTF 2022 - Operation Orchid

Cameron published on
5 min, 910 words

Categories: CTF Writeup

The challenge description goes as follows:

Download this disk image and find the flag.

We're then given an option to download the mentioned compressed disk image (disk.flag.img.gz). Decompress the archive with gunzip disk.flag.img.gz.

Let's check what kind of disk image this is:

$ file disk.flag.img
disk.flag.img: DOS/MBR boot sector; partition 1 : ID=0x83, active, start-CHS (0x0,32,33), end-CHS (0xc,223,19), startsector 2048, 204800 sectors; partition 2 : ID=0x82, start-CHS (0xc,223,20), end-CHS (0x19,159,6), startsector 206848, 204800 sectors; partition 3 : ID=0x83, start-CHS (0x19,159,7), end-CHS (0x32,253,11), startsector 411648, 407552 sectors

Okay at the very least the system recognizes disk.flag.img as a valid image. Let's try to mount the image to see if we can access the files.

$ mkdir ./mnt
$ sudo mount disk.flag.img ./mnt/
mount: /home/cameron/notes/ctf/picoCTF_2022/Forensics/OperationOrchid/mnt: wrong fs type, bad option, bad superblock on /dev/loop0, missing codepage or helper program, or other error.

Well that didn't work. It's important to know that, while the mount command can often automatically detect the filesystem of a device or disk image, it doesn't automatically detect the offset.

We can get some more information about the image, by viewing the partition table with the mmls command.

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

Here we have all the information we need to calculate the offset in bytes. The mmls output lists each section by a count of sectors. So in this case, we can see the first Linux partition starts at sector 2048. To calculate the offset we just need to multiply the number of sectors by the bytes per sector listed above as

Units are in 512-byte sectors

So, our offset = 512 x 2048 = 1048576 bytes. Let's try to mount the image again with the appropriate offset.

$ sudo mount -o offset=1048576 disk.flag.img ./mnt/

Nice, we didn't get any errors this time. Let's check the ./mnt/ directory

$ tree ./mnt/
├── boot -> .
├── config-virt
├── extlinux.conf
├── initramfs-virt
├── ldlinux.c32
├── ldlinux.sys
├── libcom32.c32
├── libutil.c32
├── lost+found [error opening dir]
├── mboot.c32
├── menu.c32
├── System.map-virt
├── vesamenu.c32
└── vmlinuz-virt

2 directories, 12 files

Okay, doesn't look like there is much here. This just seems to be the boot partition. We can check out the extlinux.conf

$ cat extlinux.conf
# Generated by update-extlinux 6.04_pre1-r9
DEFAULT menu.c32
MENU TITLE Alpine/Linux Boot Menu
MENU AUTOBOOT Alpine will be booted automatically in # seconds.
LABEL virt
  MENU LABEL Linux virt
  LINUX vmlinuz-virt
  INITRD initramfs-virt
  APPEND root=UUID=7b688671-b1b5-4e64-830d-36ebc2d4259e modules=sd-mod,usb-storage,ext4 quiet rootfstype=ext4


It looks like this is an Alpine Linux image which is neat but isn't particularly helpful here. Let's look at the next partition we saw in the partition table.

The 2nd partition started at sector 206848, but it says it's a swap partition which is essentially a file that acts as extra memory to keep your system from crashing if it is using too much RAM. We'll skip this partition for now and look at the last partition which should contain the files for the system this image was pulled from.

The last partition starts at sector 411648, so 512 x 411648 = 210763776 bytes is our new offset. Let's unmount the first partition and remount with the new offset.

$ sudo umount ./mnt
$ sudo mount -o offset=210763776 disk.flag.img ./mnt/
$ tree -L 1 ./mnt/
├── bin
├── boot
├── dev
├── etc
├── home
├── lib
├── lost+found
├── media
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin
├── srv
├── swap
├── sys
├── tmp
├── usr
└── var

Nice, that looks like a standard Linux system to me. Let's see if we can find that flag. A good first step is to search for any files with "flag" in the name since this is a CTF after all. We can do that with the find command.

$ sudo find ./mnt/ -iname "*flag*"

Well that looks promising. Let's check what kind of file that is.

$ sudo file ./mnt/root/flag.txt.enc
./mnt/root/flag.txt.enc: openssl enc'd data with salted password

Let's also check if flag.txt.enc shows up in any logs, scripts or other files on the image.

$ sudo grep -r flag.txt.enc ./mnt
./mnt/root/.ash_history:openssl aes256 -salt -in flag.txt -out flag.txt.enc -k unbreakablepassword1234567

It looks like the openssl command was used to encrypt the flag.txt with a password of unbreakablepassword1234567. The command we saw in /mnt/root/.ash_history uses the openssl-enc function of openssl to encrypt the data. If we look through the man page, we'll notice that openssl-enc has an option for decryption.

-e  Encrypt the input data: this is the default.
-d  Decrypt the input data.

Let's try decrypting flag.txt.enc with the password we found.

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

We got some warnings, but it looks like the command still output a decrypted file flag.txt. Let's see what it says.

$ cat flag.txt

And there's our flag.

Don't forget to unmount the image once you're done!

$ sudo umount ./mnt/