DEV Community

Cover image for CTF Writeup: picoCTF 2022 Forensics
Lena
Lena

Posted on • Updated on

CTF Writeup: picoCTF 2022 Forensics

My picoCTF 2022 writeups are broken up into the following sections,
1. Forensics (Solved 13/13)
2. Cryptography (Solved 11/15)
3. Binary Exploitation (Solved 5/14)
4. Reverse Engineering (Solved 2/12)
5. Web Exploitation (Solved 2/12)

All my writeups can also be found on my GitHub's CTFwriteups repository

Total points earned:
Image description

The Forensics challenges I solved in picoCTF 2022 are the following,

Table of Contents

100 points

Enhance!

The challenge is the following,

Figure 1

We are also given the file drawing.flag.svg.

Figure 1

I decided to view the contents of the file using,

$ strings drawing.flag.svg

This showed the following,

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="210mm"
   height="297mm"
   viewBox="0 0 210 297"
   version="1.1"
   id="svg8"
   inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
   sodipodi:docname="drawing.svg">
  <defs
     id="defs2" />
...
...

         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3756">c </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.09824"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3758">o </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.10265"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3760">C </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.10706"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3762">T </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.11147"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3764">F { 3 n h 4 n </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.11588"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3752">c 3 d _ 6 7 8 3 c c 4 6 }</tspan></text>
  </g>
</svg>
Enter fullscreen mode Exit fullscreen mode

In the last few rows, I saw { 3 n h 4 n and c 3 d _ 6 7 8 3 c c 4 6 }, which looked like the flag, so I concatenated this to form {3nh4nc3d_6783cc46}. I assumed that this was the flag, and I just needed to add the picoCTF wrapper.

Therefore, the flag is,

picoCTF{3nh4nc3d_6783cc46}

File types

The challenge is the following,

Figure 1

We are also given the file Flag.pdf. I tried to open this up in my PDF reader, but it said that it cannot be opened.

Figure 1

So I checked the file type using,

$ file Flag.pdf

And this revealed that it was a shell archive text

Figure 1

So I copied this file into a file with a .sh extension,

$ cp Flag.pdf Flag.sh

And added the execution permission,

$ chmod +x Flag.sh

And executed this script,

$ ./Flag.sh

Figure 1

After executing, a file called flag was generated, and checking the file type revealed that it was a current ar archive.

Figure 1

Then I used the binwalk to extract the ar archive,

$ binwalk -e flag

Which created a new folder called _flag.extracted, and inside was a file called 64.

Figure 1

I checked the file type of 64, and revealed that it was a gzip compressed data

Figure 1

I used binwalk to extract the gzip,

$ binwalk -e 64

The extracted folder contained a file called flag,

Figure 1

I checked the file type of flag, and revealed that it was a lzip compressed data. Using binwalk did not extract it, so I extracted this using,

$ lzip -d -k flag

Figure 1

This created a file called flag.out, and revealed that it was a LZ4 compressed data. So I extracted it using,

$ lz4 -d flag.out flag2.out

Figure 1

This created a file called flag2.out, and revealed that it was a LZMA compressed data. So I extracted it using,

$ lzma -d -k flag2.out

However, this returned Filename has an unknown suffix, skipping, so I renamed it to flag2.lzma and I extracted it using,

$ lzma -d -k flag2.lzma

Figure 1

This created a file called flag2, and revealed that it was a LZOP compressed data. Like last time, it gave unknown suffix, so I renamed it to flag2.lzop, and I extracted it using,

$ lzop -d -k flag2.lzop -o flag3

Figure 1

This created a file called flag3, and revealed that it was a LZIP compressed data. So I extracted it using,

$ lzip -d -k flag3

Figure 1

This created a file called flag3.out, and revealed that it was a XZ compressed data. I renamed it to flag4.xz and I extracted it using,

$ xz -d -k flag4.xz

Figure 1

This created a file called flag4, and revealed that it was a ASCII text and contained the following,

7069636f4354467b66316c656e406d335f6d406e3170756c407431306e5f
6630725f3062326375723137795f33343765616536357d0a
Enter fullscreen mode Exit fullscreen mode

I went ahead to CyberChef and converted this from hex,

Figure 1

Therefore, the flag is,

picoCTF{f1len@m3_m@n1pul@t10n_f0r_0b2cur17y_347eae65}

Lookey here

The challenge is the following,

Figure 1

We are also given the file anthem.flag.txt.

I viewed the contents of the file, which contained a very long text.

Figure 1

I counted the words using,

$ wc -w anthem.flag.txt

Which showed that there was 19139 words.

Figure 1

I know the flag format is picoCTF{xxx}, so I decided to grep it using,

$ cat anthem.flag.txt | grep "picoCTF"

Which revealed the flag.

Figure 1

Therefore, the flag is,

picoCTF{gr3p_15_@w3s0m3_4554f5f5}

Packets Primer

The challenge is the following,

Figure 1

We are also given the file network-dump.flag.pcap. Opening this up on Wireshark showed the following,

Figure 1

I decided to Follow TCP stream, which revealed the flag

Figure 1

Therefore, the flag is,

picoCTF{p4ck37_5h4rk_d0565941}

Redaction gone wrong

The challenge is the following,

Figure 1

We are also given the file Financial_Report_for_ABC_Labs.pdf.

I opened it up in a PDF viewer,

Figure 1

I saw that some texts were covered in black highlight, so I opened it up on Word and changed the text color of the highlighted words to red, which revealed the flag.

Figure 1

Therefore, the flag is,

picoCTF{C4n_Y0u_S33_m3_fully}

Sleuthkit Intro

The challenge is the following,

Figure 1

We are also given the file disk.img.gz

I downloaded the file, extracted it, and used the following command,

$ mmls disk.img

Which showed the partitions and their size.

Figure 1

The challenge asks for the Linux partition size, which is 0000202752.

I inputted this Linux partition size to the remote access checker program, which gave me the flag.

Figure 1

Therefore, the flag is,

picoCTF{mm15_f7w!}

200 points

Sleuthkit Apprentice

The challenge is the following,

Figure 1

We are also given the file disk.flag.img.gz. I downloaded the file, extracted it, and checked the partitions using

$ mmls disk.flag.img.

Figure 1

I also checked the file system information for the Linux partition starting at 0000360448 using,

$ fsstat -o 0000360448 disk.flag.img

Figure 1

This showed that the Linux partition was using a Ext4 partition with a block size of 1024 bytes.

I wanted to check if there were any strings that could hint to a flag file, so I checked for the string flag using,

$ strings -t d disk.flag.img | grep -iE "flag".

Figure 1

However, there were too many entries with the string flag, so I decided to narrow the string search down. Since the flag format is picoCTF{xxx}, I decided to search for the string pico using,

$ strings -t d disk.flag.img | grep -iE "pico".

Figure 1

However, nothing useful came up. I assumed that the flag might be contained in a .txt file as that is the most common means of storing the flag in a disk forensics challenge. Therefore, I assumed that the flag might be contained in a file named flag.txt,

$ strings -t d disk.flag.img | grep -iE "flag.txt".

Figure 1

This outputted some interesting entries, and the following caught my eye.

nano flag.txt
iconv -f ascii -t utf16 flag.txt > flag.uni.txt
shred -zu flag.txt
Enter fullscreen mode Exit fullscreen mode

I decided to look further into this, so I took the offset for nano flag.txt, which is 204193835, and subtracted 184549376 (which is 360448 * 512) using,

$ expr 204193835 - 184549376

and divided 19644459 by the block size 1024 bytes using,

$ expr 19644459 / 1024

Then I used that result, 19184 to find the inode number of the file containing the string file.txt using,

$ ifind -f ext4 -o 360448 -d 19184 disk.flag.img

This returned 2363, so I printed the contents of that file using,

$ icat -f ext4 -o 360448 disk.flag.img 2363

Figure 1

This showed the full command. I saw that a directory called my_folder was created, moved into the my_folder directory, flag was written into flag.txt, flag.txt was copied into flag.uni.txt, and the original flag.txt was deleted securely using shred, which would make it extremely difficult to recover. From this, I assumed that the flag is contained in flag.uni.txt in the my_folder directory, so I decided to search for that using,

$ strings -t d disk.flag.img | grep -iE "flag.uni.txt".

Figure 1

Now I know what file I am supposed to look for and what directory and partition it was in. I opened up Autopsy and searched for the directory that contained flag.txt and flag.uni.txt in the fourth partition of the disk, which is Linux (0x83) 360448-614399.

Figure 1

So I went to /root/my_folder directory, and I saw that flag.txt did not contain any relevant information because it was shredded. So I looked into flag.uni.txt, which contained the flag.

Figure 1

Therefore, the flag is,

picoCTF{by73_5urf3r_152f373f}

300 points

Eavesdrop

The challenge is the following,

Figure 1

We are also given the file capture.flag.pcap. Opening this up on Wireshark showed the following,

Figure 1

I did Follow TCP stream, which revealed a conversation between two people.

Figure 1

It seemed like these two people had been exchanging files, and one person forgot how to decrypt it, so the other person tells them to decrypt it using,

openssl des3 -d -salt -in file.des3 -out file.txt -k supersecretpassword123

I looked through the packets, and found the file that started with Salted in packet 57. I knew this was the file I was looking for, because OpenSSL with des3 salt will generate an encrypted file that starts with Salted.

Figure 1

So I exported the packet as saltedfile.bin using File > Export Packet Bytes

Figure 1

I decrypted it using what was mentioned in the conversation,

openssl des3 -d -salt -in saltedfile.bin -out file.txt -k supersecretpassword123

After decryption succeeded, I was left with file.txt that contained the flag.

Figure 1

Therefore, the flag is,

picoCTF{nc_73115_411_445e5629}

Operation Oni

The challenge is the following,

Figure 1

We are also given the file disk.img.gz. I downloaded the file, extracted it. The challenge says to use a key_file to ssh to the remote machine, so I assumed that I need to look for a file that contained the key. I tried to find the partition information using,

$ mmls disk.img

As most private keys contain the string OPENSSH PRIVATE KEY, I string searched that using,

$ strings -t d disk.img | grep -iE "OPENSSH PRIVATE KEY"

Figure 1

There were files that contained OPENSSH PRIVATE KEY, so now I have to find the actual contents of the private key file. I used the offset 114562048 and did the operations similar to Sleuthkit Apprentice to find the file contents using the commands,

$ expr 114562048 - 105906176

$ expr 8655872 / 1024

$ ifind -f ext4 -o 206848 -d 8453 disk.img

$ icat -f ext4 -o 206848 disk.img 15

Figure 1

This showed the private key,

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCCLR0HD7N0U/aOCIfE8hu37rqZXPVlAw2nZTCsXzW5iwAAAJhPAhe3TwIX
twAAAAtzc2gtZWQyNTUxOQAAACCCLR0HD7N0U/aOCIfE8hu37rqZXPVlAw2nZTCsXzW5iw
AAAEC64hkpckRMk5Jy2dRmcBTSoQvavri0nLhU6aqeGBT1OIItHQcPs3RT9o4Ih8TyG7fu
uplc9WUDDadlMKxfNbmLAAAADnJvb3RAbG9jYWxob3N0AQIDBAUGBw==
-----END OPENSSH PRIVATE KEY-----
Enter fullscreen mode Exit fullscreen mode

I also confirmed using Autopsy, and saw that this private key file was in /root/.ssh/id_ed25519 in the Linux partition that starts at 0000206848.

Figure 1

So I went into the webshell, and put the private key into key_file, and tried to ssh to the remote server using,

ssh -i key_file -p 64039

Figure 1

However, it had the permissions 0664 which was too open so the private key was unusable. Therefore, I changed the permissions to 400 using,

$ chmod 400 key_file

And I did ssh again to the remote server, which contained a file called flag.txt which contained the flag,

Figure 1

Therefore, the flag is,

picoCTF{k3y_5l3u7h_dc01cec5}

St3g0

The challenge is the following,

Figure 1

We are also given the file pico.flag.png

Figure 1

I went to Steganography Online to decode the image, but decoding the image did not reveal anything.

Figure 1

I decided to use zsteg instead, with the -a option to try all known methods, and the -v option to run verbosely,

zsteg -a -v pico.flag.png

This revealed the flag at b1,rgb,lsb,xy, where rgb means it uses RGB channel, lsb means least significant bit comes first, and xy means the pixel iteration order is from left to right.

Figure 1

Therefore, the flag is,

picoCTF{7h3r3_15_n0_5p00n_1b8d71db}

400 points

Operation Orchid

The challenge is the following,

Figure 1

We are also given the file disk.flag.img.gz. I downloaded the file, extracted it. I did the operations in Sleuthkit Apprentice to find the partition informations, and I decided to string search flag.txt using,

$ strings -t d disk.flag.img | grep -iE "flag.txt"

Figure 1

From this, I assumed that the flag was first written into flag.txt, encrypted and put into flag.txt.enc using OpenSSL aes256 with the salt option and a password with unbreakablepassword1234567, and flag.txt was shredded.

I double checked with Autopsy, and saw that the commands used were contained in .ash_history.

Figure 1

As the OpenSSL with the salt option generates encrypted text that starts with Salted, I decided to string search that using,

strings -t d disk.flag.img | grep -iE "Salted"

I also decided to find the full contents of the file that contained Salted using,

$ expr 221247488 - 210763776

$ expr 10483712 / 1024

$ ifind -f ext4 -o 411648 -d 10238 disk.flag.img

$ icat -f ext4 -o 411648 disk.flag.img 1782

Figure 1

It contained the encrypted file with the contents,

Salted__???3[u
              :dmޠ
D-Z{z?+g?p?=?N???\??B?Ȥ7? ???؎$?'%
Enter fullscreen mode Exit fullscreen mode

I double checked with Autopsy, and confirmed that the Salted file was there.

Figure 1

So I redirected the output to flag.txt.enc using,

$ icat -f ext4 -o 411648 disk.flag.img 1782 > flag.txt.enc

As it was encrypted using openssl aes256 -salt -in flag.txt -out flag.txt.enc -k unbreakablepassword1234567, I decrypted it using,

$ openssl aes256 -d -salt -in flag.txt.enc -out flag.txt -k unbreakablepassword1234567

Figure 1

Therefore, the flag is,

picoCTF{h4un71ng_p457_c512004e}

SideChannel

The challenge is the following,
Figure 1

We are also given the file pin_checker

The following shows the example execution, where Incorrect Length is outputted when a PIN that's not 8-digits is entered, Checking PIN... is outputted if a 8-digit PIN is entered, and Access denied. is outputted if the 8-digit PIN is incorrect.

Figure 1

There is a noticeable time delay during the Checking PIN... and Access denied., so we can use a time-based side channel attack here.

From the program behaviour, I saw that the length is first checked, and if the length is 8, the program proceeds to check the digits of the 8-digit PIN code (otherwise, it immediately returns Incorrect length).

I made the following Python script side.py to measure the time before Access denied. is outputted

from pwn import *
import time
import sys

io = process(['./pin_checker'])

context.arch = 'amd64'
gs = '''
continue
'''

#allow pin number to be inputted as argument, eg: python3 side.py 12345678
pin = str(sys.argv[1])

#send the pin as the input to the pin_checker
io.sendline(pin)
#skip first output, which is 'Please enter your 8-digit PIN code:'
io.recvline()
#skip second output, which is PIN length
io.recvline()
#skip third output, which is 'Checking PIN...'
io.recvline()
#start the timer
start = time.time()
#fourth output is 'Access denied.', we are measuring time until this is outputted
recevied4 = io.recvline()
#stop the timer
stop = time.time()
#calculate time difference
time_taken = stop - start

log.info(f"Time taken: {time_taken}")
log.info(f"Received4: {recevied4}")
log.info(f"Guess: {pin}")


sys.exit()

Enter fullscreen mode Exit fullscreen mode

I made the script so that the PIN could be inputted like the following,

$ python3 side.py 12345678

The following shows the example execution, where the Time taken is outputted in seconds.

Figure 1

I assumed that the PIN is checked from left to right, where Access denied. is outputted as soon as the leftmost digit does not match. Therefore, the PIN with the correct leftmost digit should take the longest time because it will move onto the next digit comparison. For the first test batch, I decided to use 00000000, 10000000, 20000000, 30000000, 40000000, 50000000, 60000000, 70000000, 80000000, 90000000 for the PINs. To automate this process, I made the following shell script auto.sh,

#!/bin/bash

python3 side.py 00000000
python3 side.py 10000000
python3 side.py 20000000
python3 side.py 30000000
python3 side.py 40000000
python3 side.py 50000000
python3 side.py 60000000
python3 side.py 70000000
python3 side.py 80000000
python3 side.py 90000000
Enter fullscreen mode Exit fullscreen mode

Before I executed this script, I closed all programs that I wasn't using to reduce variations in time due to background processes. I then executed this script,

Figure 1

Here, I saw that the pin 40000000 took the longest, with a significant time difference from the other PINs. I executed this script again to confirm,

Figure 1

Therefore, 40000000 is what I will be using for the second test batch, thus I used the following shell script.

#!/bin/bash

python3 side.py 40000000
python3 side.py 41000000
python3 side.py 42000000
python3 side.py 43000000
python3 side.py 44000000
python3 side.py 45000000
python3 side.py 46000000
python3 side.py 47000000
python3 side.py 48000000
python3 side.py 49000000
Enter fullscreen mode Exit fullscreen mode

I executed the script twice,

Figure 1

This shows that 48000000 takes the longest, therefore I will be using this for the third test batch,

 #!/bin/bash

 python3 side.py 48000000
 python3 side.py 48100000
 python3 side.py 48200000
 python3 side.py 48300000
 python3 side.py 48400000
 python3 side.py 48500000
 python3 side.py 48600000
 python3 side.py 48700000
 python3 side.py 48800000
 python3 side.py 48900000
Enter fullscreen mode Exit fullscreen mode

I executed the script twice,

Figure 1

This shows that 48300000 takes the longest, therefore I will be using this for the fourth test batch,

 #!/bin/bash

 python3 side.py 48300000
 python3 side.py 48310000
 python3 side.py 48320000
 python3 side.py 48330000
 python3 side.py 48340000
 python3 side.py 48350000
 python3 side.py 48360000
 python3 side.py 48370000
 python3 side.py 48380000
 python3 side.py 48390000
Enter fullscreen mode Exit fullscreen mode

I executed the script twice,

Figure 1

This shows that 48390000 takes the longest, therefore I will be using this for the fifth test batch,

#!/bin/bash

python3 side.py 48390000
python3 side.py 48391000
python3 side.py 48392000
python3 side.py 48393000
python3 side.py 48394000
python3 side.py 48395000
python3 side.py 48396000
python3 side.py 48397000
python3 side.py 48398000
python3 side.py 48399000
Enter fullscreen mode Exit fullscreen mode

I executed the script twice,

Figure 1

This shows that 48390000 takes the longest, therefore I will be using this for the sixth test batch,

  #!/bin/bash

  python3 side.py 48390000
  python3 side.py 48390100
  python3 side.py 48390200
  python3 side.py 48390300
  python3 side.py 48390400
  python3 side.py 48390500
  python3 side.py 48390600
  python3 side.py 48390700
  python3 side.py 48390800
  python3 side.py 48390900

Enter fullscreen mode Exit fullscreen mode

I executed the script twice,

Figure 1

This shows that 48390500 takes the longest, therefore I will be using this for the seventh test batch,

#!/bin/bash

python3 side.py 48390500
python3 side.py 48390510
python3 side.py 48390520
python3 side.py 48390530
python3 side.py 48390540
python3 side.py 48390550
python3 side.py 48390560
python3 side.py 48390570
python3 side.py 48390580
python3 side.py 48390590
Enter fullscreen mode Exit fullscreen mode

I executed the script twice,

Figure 1

This shows that 48390510 takes the longest, therefore I will be using this for the eighth test batch,

#!/bin/bash

python3 side.py 48390510
python3 side.py 48390511
python3 side.py 48390512
python3 side.py 48390513
python3 side.py 48390514
python3 side.py 48390515
python3 side.py 48390516
python3 side.py 48390517
python3 side.py 48390518
python3 side.py 48390519
Enter fullscreen mode Exit fullscreen mode

Executing this showed that 48390513 is the correct PIN.

Figure 1

I logged into the master server using this PIN, which gave me the flag,

Figure 1

Therefore, the flag is,

picoCTF{t1m1ng_4tt4ck_8d0e5357}

Torrent Analyze

The challenge is the following,

Figure 1

We are also given the file torrent.pcap. As this is a torrent challenge, I went to Wireshark and enabled the BitTorrent DHT Protocol (BT-DHT) by going to Analyze -> Enabled Protocol.

Figure 1

The overall packet capture looks like the following,

Figure 1

I applied the bt-dht filter, and looked through the packets, and saw that some contained info_hash.

Figure 1

The challenge only wants us to find the file name, and not reconstruct the file, so I knew that this info_hash information will be very important because it tells us the hash of the file. As hash is 68 61 73 68 in hex, I inputted this hex value into the Wireshark search to look for all packets that contained this hash information.

Figure 1

The first packet that contained info_hash was packet 79 with a hash value of 17d62de1495d4404f6fb385bdfd7ead5c897ea22

Figure 1

So I looked up 17d62de1495d4404f6fb385bdfd7ead5c897ea22 on Google, and saw that it corresponded to Awakened.2013.1080p.BluRay.X264-iNVANDRAREN.

Figure 1

The first packet that contained info_hash was packet 332 with a hash value of 17c1e42e811a83f12c697c21bed9c72b5cb3000d

Figure 1

This file corresponded to name: Zoo (2017) 720p WEB-DL x264 ESubs - MkvHub.Com.

Figure 1

I looked through a few more, and I was at packet 51080 which had a hash value of e2467cbf021192c241367b892230dc1e05c0580e.

Figure 1

I Googled this, and saw that it corresponded to ubuntu-19.10-desktop-amd64.iso from LinuxTracker.org.

Figure 1

Figure 1

Therefore, the flag is,

picoCTF{ubuntu-19.10-desktop-amd64.iso}

Discussion (0)