PicoCTF Writeup – c0rrupt

# Information:

CTF Name: PicoCTF

CTF Challenge: c0rrupt

Challenge Category: Forensics

Challenge Points: 250

PicoCTF 2019

# Challenge Description:  

We found this file. Recover the flag.  

Hint: Try fixing the file header

Writeup  

In this challenge we are given file with the name “mystery“. The hint tells us to fix the file header. Knowing this I first tried to figure out what is the file type of the mystery file. To do so I did the following command:

Step 1

mregra on Cyber $ file mystery
mystery: data
mregra on Cyber $ 

The file command usually displays the type of the file that is given as argument. In this case the type is data, this is basically the default when the file command does not recognize the file type.  

Okay so this probably means that the header of the mystery file is broken in some way, lets check it out! To do so I did the following:

Step 2

 mregra on Cyber $ xxd -g 1 mystery | head
 00000000: 89 65 4e 34 0d 0a b0 aa 00 00 00 0d 43 22 44 52  .eN4........C"DR
 00000010: 00 00 06 6a 00 00 04 47 08 02 00 00 00 7c 8b ab  ...j...G.....|..
 00000020: 78 00 00 00 01 73 52 47 42 00 ae ce 1c e9 00 00  x....sRGB.......
 00000030: 00 04 67 41 4d 41 00 00 b1 8f 0b fc 61 05 00 00  ..gAMA......a...
 00000040: 00 09 70 48 59 73 aa 00 16 25 00 00 16 25 01 49  ..pHYs...%...%.I
 00000050: 52 24 f0 aa aa ff a5 ab 44 45 54 78 5e ec bd 3f  R$......DETx^..?
 00000060: 8e 64 cd 71 bd 2d 8b 20 20 80 90 41 83 02 08 d0  .d.q.-.  ..A....
 00000070: f9 ed 40 a0 f3 6e 40 7b 90 23 8f 1e d7 20 8b 3e  [email protected]@{.#... .>
 00000080: b7 c1 0d 70 03 74 b5 03 ae 41 6b f8 be a8 fb dc  ...p.t...Ak.....
 00000090: 3e 7d 2a 22 33 6f de 5b 55 dd 3d 3d f9 20 91 88  >}*"3o.[U.==. ..
 mregra on Cyber $

The xxd command displays the hexdump of the file given as argument, in this case the mystery file. However my command is more complex, for instance the | head says that we just want the header of the hexdump, not the entire file. The flag -g separate the output of every bytes (two hexadecimal characters in this case) by a white space. The 1 is for the -g flag and it selects the bits mode. To learn more read this.  

By looking at the output I was able to see that this is suppose to be a PNG file. I know that because we have several strings that are common in PNG, such as gAMA, pHYs. However, the magic numbers, the first 8 bytes of the file are wrong. I decided to fix them for start. To do so you can use hexedit, bless or some other hex editor. I decided to use the terminal and learn some extra Linux commands in the process. After several hours I manage to find that this command does what I want:

Step 3

 mregra on Cyber $ cp mystery fixed.png
 mregra on Cyber $ printf '\x89\x50\x4E\x47\x0D\x0A\x1A\x0A' | dd of=fixed.png bs=1 seek=0 count=8 conv=notrunc
 8+0 records in
 8+0 records out
 8 bytes copied, 0,000168898 s, 47,4 kB/s
 mregra on Cyber $ 

Now I will try to explain each part of the commands above. First I copied the contents of mystery into a new file: fixed.png. I did so to keep the original file and work on a copy in case I need to go back for some reason.  

The next command is a little more complex. It is basically a combination of two commands. Lets start with the printf. The printf prints data according to given formats. In this case the \x which represents hexadecimal. If we used instead the echo command it would not work because it would not be formatted. The hexadecimal values correspond to the PNG magic numbers. The pipe (|) as previously in different posts, gives the output of the command on the left as input to the command in the right.  

The dd command copies a file while converting and formatting depending on the operands given (See: link). In this case we have “dd of=fixed.png bs=1 seek=0 count=8 conv=notrunc“. The of writes to the fixed.png file. The bs=1 writes up to 1 bytes at a time. The seek=0 represent the number of bytes to skip at start of output, in this case we are in the begin of the file so it is 0. The count=8 copies 8 input blocks, and finally the conv=notrunc it presents the output file to be truncated.  

Now lets check if the bytes were properly fixed by repeating the command in the second step.  

 mregra on Cyber $ xxd -g 1 mystery | head
 00000000: 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 43 22 44 52  .PNG........C"DR
 00000010: 00 00 06 6a 00 00 04 47 08 02 00 00 00 7c 8b ab  ...j...G.....|..
 00000020: 78 00 00 00 01 73 52 47 42 00 ae ce 1c e9 00 00  x....sRGB.......
 00000030: 00 04 67 41 4d 41 00 00 b1 8f 0b fc 61 05 00 00  ..gAMA......a...
 00000040: 00 09 70 48 59 73 aa 00 16 25 00 00 16 25 01 49  ..pHYs...%...%.I
 00000050: 52 24 f0 aa aa ff a5 ab 44 45 54 78 5e ec bd 3f  R$......DETx^..?
 00000060: 8e 64 cd 71 bd 2d 8b 20 20 80 90 41 83 02 08 d0  .d.q.-.  ..A....
 00000070: f9 ed 40 a0 f3 6e 40 7b 90 23 8f 1e d7 20 8b 3e  [email protected]@{.#... .>
 00000080: b7 c1 0d 70 03 74 b5 03 ae 41 6b f8 be a8 fb dc  ...p.t...Ak.....
 00000090: 3e 7d 2a 22 33 6f de 5b 55 dd 3d 3d f9 20 91 88  >}*"3o.[U.==. ..
 mregra on Cyber $

Step 4

Now that we fixed the magic number in the header lets move on. After the magic number, in a PNG file we usually have the IHDR chunk. But by looking at the output above we have C”DR. Lets fix that!    

 mregra on Cyber $ printf '\x00\x00\x00\x0D\x49\x48\x44\x52' | dd of=fixed.png bs=1 seek=8 count=8 conv=notrunc
 8+0 records in
 8+0 records out
 8 bytes copied, 0,000221603 s, 36,1 kB/s
 mregra on Cyber $ 

This command is similar to the one in step 3. The differences are that, first, the hexadecimal correspond to the values to fix the IHDR, considering that we have a few zeros between PNG and IHDR, which remain the same, we just fix the next part, it was: 0d 43 22 44 52, and now it is 0d 49 48 44 52. We know that the first 4 bytes of the chunk represent the length of the chunk, in this case we have 0D, so the length is 14 bytes.  

Lets now see if the file is recognized as a PNG file or not:    

 mregra on Cyber $ file fixed.png
 fixed.png: PNG image data, 1642 x 1095, 8-bit/color RGB, non-interlaced
 mregra on Cyber $ 

However, it still is not possible to open the image. Lets use the pngcheck tool to see what errors remain:

Step 5

 mregra on Cyber $ pngcheck -v -f fixed.png
 File: fixed.png (202940 bytes)
   chunk IHDR at offset 0x0000c, length 13
     1642 x 1095 image, 24-bit RGB, non-interlaced
   chunk sRGB at offset 0x00025, length 1
     rendering intent = perceptual
   chunk gAMA at offset 0x00032, length 4: 0.45455
   chunk pHYs at offset 0x00042, length 9: 2852132389x5669 pixels/meter
   CRC error in chunk pHYs (computed 38d82c82, expected 495224f0)
 :  invalid chunk length (too large)
 ERRORS DETECTED in fixed.png
 mregra on Cyber $ 

As you can see we still have errors in the file. In this case, the CRC error in chunk pHYs is not correct. Lets fix that:  

 mregra on Cyber $ printf '\x38\xD8\x2C\x82' | dd of=fixed.png bs=1 seek=79 count=4 conv=notrunc
 4+0 records in
 4+0 records out
 4 bytes copied, 0,000150642 s, 26,6 kB/s
 mregra on Cyber $ 

Lets see if we still have errors in the PNG  

 mregra on Cyber $ pngcheck -v -f fixed.png
 File: fixed.png (202940 bytes)
  chunk IHDR at offset 0x0000c, length 13
    1642 x 1095 image, 24-bit RGB, non-interlaced
  chunk sRGB at offset 0x00025, length 1
    rendering intent = perceptual
  chunk gAMA at offset 0x00032, length 4: 0.45455
  chunk pHYs at offset 0x00042, length 9: 2852132389x5669 pixels/meter
 :  invalid chunk length (too large)
 ERRORS DETECTED in fixed.png
 mregra on Cyber $ 

Step 6

Now it seems the problem is that we have an invalid chunk length. Lets go back to the hexadecimal of the header, but only after the pHYs, because it is fixed up until that point:  

 mregra on Cyber $ xxd -g 1 -s 0x53 -l 77 fixed.png | head
 00000053: aa aa ff a5 ab 44 45 54 78 5e ec bd 3f 8e 64 cd  .....DETx^..?.d.
 00000063: 71 bd 2d 8b 20 20 80 90 41 83 02 08 d0 f9 ed 40  q.-.  ..A......@
 00000073: a0 f3 6e 40 7b 90 23 8f 1e d7 20 8b 3e b7 c1 0d  ..n@{.#... .>...
 00000083: 70 03 74 b5 03 ae 41 6b f8 be a8 fb dc 3e 7d 2a  p.t...Ak.....>}*
 00000093: 22 33 6f de 5b 55 dd 3d 3d f9 20 91 88           "3o.[U.==. ..
 mregra on Cyber $

In the above command the flag -s followed by 0x53 represents the starting point, in this case it is the end of the pHYs. The -l 77, represetns the length to display, in this case it is the end of the first head command.  

As we know, the first 4 bytes of the chunk represent the chunk size, in this case we have aa aa ff a5 which is a huge size. Another interesting thing is that the DET does not exist in the PNG as a chunk, this must be wrong and by looking at the PNG specification I was able to figure out that the most probable correct chunk is the IDAT chunk type. To fix that we simply have to:    

 mregra on Cyber $ printf '\x49\x44\x41\x54' | dd of=fixed.png bs=1 seek=87 count=4 conv=notrunc
 4+0 records in
 4+0 records out
 4 bytes copied, 0,000159724 s, 25,0 kB/s
 mregra on Cyber $ 

Step 7

Now that we fixed the IDAT chunk lets try to fix the problem with the size. From the PNG specialization we know that “There can be multiple IDAT chunks; if so, they must appear consecutively with no other intervening chunks.” So lets try to see if there are others and the size difference between this one and the next.    

 mregra on Cyber $ binwalk -R "IDAT" fixed.png

 DECIMAL       HEXADECIMAL     DESCRIPTION
 ---------------------------------------- 
 ----------------------------------------
 87            0x57            Raw signature (IDAT)
 65544         0x10008         Raw signature (IDAT)
 131080        0x20008         Raw signature (IDAT)
 196616        0x30008         Raw signature (IDAT)

 mregra on Cyber $ 

Before moving on lets analyze the command that I used. I used the binwalk command, see this link for more information on binwalk. Binwalk is a tool for searching binary images for executable code or embedded files. In general it is a binary image searching tool. In this case case we are searching the PNG file for the raw sequence of bytes “IDAT” (-R represents the raw sequence of bytes and it receives a string as input).  

In the output we see that the first one starts at 0x57 + 0X4 = 0x5B (We want the end of the IDAT, and this value represents the begining) and ends at 0x10008 – 0X4 (We need to subtract the length). If we subtract that and also 4 for the check sum of the first IDAT we get: 0x10004 – 0x5B – 0x4 = 0XFFA5. Now lets fix the size, to do so we simply have to override the AA AA with zeros, as such:  

 mregra on Cyber $ printf '\x00\x00' | dd of=fixed.png bs=1 seek=83 count=2 conv=notrunc
 2+0 records in
 2+0 records out
 2 bytes copied, 0,000137944 s, 14,5 kB/s
 mregra on Cyber $ 

Now lets verify if we still have errors in the PNG file:  

mregra on Cyber $ pngcheck -v -f fixed.png
 File: fixed.png (202940 bytes)
   chunk IHDR at offset 0x0000c, length 13
     1642 x 1095 image, 24-bit RGB, non-interlaced
   chunk sRGB at offset 0x00025, length 1
     rendering intent = perceptual
   chunk gAMA at offset 0x00032, length 4: 0.45455
   chunk pHYs at offset 0x00042, length 9: 2852132389x5669 pixels/meter
   chunk IDAT at offset 0x00057, length 65445
     zlib: deflated, 32K window, fast compression
   chunk IDAT at offset 0x10008, length 65524
   chunk IDAT at offset 0x20008, length 65524
   chunk IDAT at offset 0x30008, length 6304
   chunk IEND at offset 0x318b4, length 0
 No errors detected in fixed.png (9 chunks, 96.3% compression).
 mregra on Cyber $ 

We are error free! Lets now look at the image:  

And the flag is:

Show flag
picoCTF{c0rrupt10n_1847995}

To solved this challenge I used Wikipedia, PNG Specification. And other sites that I added in the text.

Thank you very much for reading!

Cheers,

MRegra


Share this post:

Popular posts

Leave a Reply

Your email address will not be published. Required fields are marked *