Okay, soforth we have finished 7 challenges only leaving two challenges in the Misc- and one in the Encoding categories. After a good pause from the screen I'm again ready - lets take a look on the "ZipFil" challenge.
Firstly we download the provided zipfil.zip
and of-cause trying to deflate it with the command unzip zipfil.zip
. As expected, it did not work. Looking at the files information with file zipfil.zip
only gives us zipfil.zip: data
. This tells us that the file we have is either not a real zip-file, or that it is corrupted in some way.
Using the command zipdetails -v zipfil.zip
we can try to see if any information could lead us along the way, but this tells us "No Central Directory records found" which only ensures us, that the file is no-way near a valid zip-file.
zipinfo zipfil.zip
can sometimes give a more elaborate information about the zip-file. But as seen in the output below, this just confirms our information that a central directory is missing. Further looking at the output, it goes on by talking about multi-part zip-files, but as we are only given a single file, it does not seems to be the case, though lets have that in mind for later, should it be relevant.
Archive: zipfil.zip
[zipfil.zip]
End-of-central-directory signature not found. Either this file is not
a zipfile, or it constitutes one disk of a multi-part archive. In the
latter case the central directory and zipfile comment will be found on
the last disk(s) of this archive.
zipinfo: cannot find zipfile directory in one of zipfil.zip or
zipfil.zip.zip, and cannot find zipfil.zip.ZIP, period.
Maybe a reparation of the file withzip -FF zipfil.zip --out repaired.zip
could solve the issue? Unfortunately a repair did not help.
With a file of 242 bytes a quick strings zipfil.zip
might be a long shot but it is quick and doesn't hurt anyone. Only "PEUG" pops up - not of interest.
We could be standing with a file that unzip
just couldn't handle - what about the Java archive command? jar -xvf zipfil.zip
gives us "java.util.zip.ZipException: zip END header not found" - no luck here either.
What about investigating the magic-bytes with file -i zipfil.zip
, as it can tell us a bit more information than just the plain file
-command?
Running the command gives us the information application/octet-stream; charset=binary
, which should be application/zip; charset=binary
if the file was to be recognized as a valid zip-file. We can see the start of the file with xxd zipfil.zip | head
. As seen below, the file indeed seems to have some issues - the first four HEX-bytes should read 504b0304
in the hexdump (for a normal zip-file non-empty and not multi-part spanned) to be a valid zip-file.
00000000: 0000 0000 00e8 0000 00e4 0010 0010 0000 ................
00000010: 0000 6050 b405 0000 0000 4000 0000 0040 ..`P......@....@
00000020: 1000 b087 57d5 c89d 9130 0050 4555 4787 ....W....0.PEUG.
00000030: 47e2 7616 c666 0000 0000 184a 0000 0010 G.v..f.....J....
00000040: 0000 0000 0081 0080 0000 0003 0000 00c3 ................
00000050: 8686 e8c9 f4a3 b5d8 0000 0090 00a0 30e1 ..............0.
00000060: 2010 b405 0000 0003 0000 00c3 8686 e8c9 ...............
00000070: 8070 b405 d429 68c0 6fd0 5f87 9ed2 1e54 .p...)h.o._....T
00000080: 60e5 794f 80d2 8c4d e80a f217 cb9b 28a5 `.yO...M......(.
00000090: 1cf3 b655 1163 c31c ca92 8ecd 0e7e f6cd ...U.c.......~..
Let us try fixing those magic bytes and see if we can unzip the file. We can change the four first HEX-bytes via hexeditor
in the terminal, changing it to 504b0304
and saving. Running the xxd zipfil.zip | head
again to verify the change, and all seems good. An unzip zipfil.zip
still doesn't work, and re-running the file -i
command we can see that the magic bytes did change the file to be recognized as a valid zip-file.
Checking with chardet
and chardetect
to see if we maybe have some weird encoding, but no usable result is given.
After this, I tried different methods to deflate the zip-file -- rumors on the net has it, that unzip
might not be as "stable" or robust for errors in the archive-file. All of the different tried methods (listed below) did not work for me; It seems that we do not have a "Central directory", as there should be in a valid zip-file.
jar xvf zipfil2.zip
7z x zipfil2.zip
zcat zipfil2.zip > zipfil
dd if=zipfil2.zip| gunzip -f > zipfil
This missing "Central directory" is further exemplified with the zipdetails
-command telling us "No Central Directory records found" as we have seen before.
With this information, it seems that we will never be able to un-corrupt the file enough for a successful deflating event to occur. But the zip-file format are the basis of multiple file-formats, as seem on https://en.wikipedia.org/wiki/List_of_files_signatures. Could we try opening it as a docx-formatted file?
Okay, a quick cp zipfil2.zip her.docx
and opening it in LibreOffice Writer. As seen in the below image -- no luck here.
Let us then try a rar-file:
cp zipfil2.zip zipfile2.rar
$ unrar x zipfil2.rar
UNRAR 6.00 freeware Copyright (c) 1993-2020 Alexander Roshal
zipfil2.rar is not RAR archive
No files to extract
Not a rar-file then. Okay, that could be the magic-bytes again. A change of the magic-bytes to 526172211a0700
to force the file to a rar v1.50 (and up). One thing to be aware of here, is that the magic-bytes for rar, expands over some non-null bytes in the file leaving me to question if this is the right way to go -- but lets try.
No; that didn't work as "Unexpected end of archive" and "Main archive header is corrupt" are displayed. Even trying magic-bytes 526172211a070100
as the newer rar v5.00 (and up) didn't help.
Why did we even try rar-files? I don't know; rar is not, and have never been, build upon the zip-format. My eyes had played me - it's the jar
(Java Archive) format, not rar-format building upon the zip-format.
But no - that didn't help us either:
$ cp zipfil2.zip zipfil2.jar
$ jar xvf zipfil2.jar
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
java.lang.IllegalArgumentException: malformed input off : 4, length : 1
at java.base/java.lang.StringCoding.throwMalformed(StringCoding.java:685)
at java.base/java.lang.StringCoding.decodeUTF8_0(StringCoding.java:872)
at java.base/java.lang.StringCoding.newStringUTF8NoRepl(StringCoding.java:965)
at java.base/java.lang.System$2.newStringUTF8NoRepl(System.java:2205)
at java.base/java.util.zip.ZipCoder$UTF8.toString(ZipCoder.java:60)
at java.base/java.util.zip.ZipCoder.toString(ZipCoder.java:87)
at java.base/java.util.zip.ZipInputStream.readLOC(ZipInputStream.java:303)
at java.base/java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:125)
at jdk.jartool/sun.tools.jar.Main.extract(Main.java:1361)
at jdk.jartool/sun.tools.jar.Main.run(Main.java:409)
at jdk.jartool/sun.tools.jar.Main.main(Main.java:1681)
Caused by: java.nio.charset.MalformedInputException: Input length = 1
... 11 more
With no luck here either, I started to backtrack a bit. Looking at the hexdump for the file with xxd
(see below), we see the missing magic-bytes. Fixing those had no luck for us yet, so looking further in the hexdump I started to see a corrupted end of the file.
A zip-file is build of local file header(s)/central directory file header(s) and ends with a "End of central directory record (EOCDR)" -- see section 4.3.6 at the zip-file specification on https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT. Normally a zip-file can be embedded in other files, so when deflating a zip-file it actually stops the deflating with the EOCDR and the rest of the files content is not touched. This means that an EOCDR is not required to be at the very end of a file.
00000000: 0000 0000 00e8 0000 00e4 0010 0010 0000 ................
00000010: 0000 6050 b405 0000 0000 4000 0000 0040 ..`P......@....@
00000020: 1000 b087 57d5 c89d 9130 0050 4555 4787 ....W....0.PEUG.
00000030: 47e2 7616 c666 0000 0000 184a 0000 0010 G.v..f.....J....
00000040: 0000 0000 0081 0080 0000 0003 0000 00c3 ................
00000050: 8686 e8c9 f4a3 b5d8 0000 0090 00a0 30e1 ..............0.
00000060: 2010 b405 0000 0003 0000 00c3 8686 e8c9 ...............
00000070: 8070 b405 d429 68c0 6fd0 5f87 9ed2 1e54 .p...)h.o._....T
00000080: 60e5 794f 80d2 8c4d e80a f217 cb9b 28a5 `.yO...M......(.
00000090: 1cf3 b655 1163 c31c ca92 8ecd 0e7e f6cd ...U.c.......~..
000000a0: 9eef 55b8 9df4 382b 8525 59ea f1d7 87bd ..U...8+.%Y.....
000000b0: 0000 0000 4000 0000 0040 1000 b087 57d5 ....@....@....W.
000000c0: c89d 91d5 c89d 9130 0090 4555 4787 47e2 .......0..EUG.G.
000000d0: 7616 c666 00c1 0080 0000 0003 0000 00c3 v..f............
000000e0: 8686 e8c9 f4a3 b5d8 0000 0090 00a0 4030 ..............@0
000000f0: b405 ..
An EOCDR is started by the hex-value of 0x06054b50
-- a value not found in the file. This baffled me a bit; Are we required to calculate and produce the EOCDR ourself to solve the CTF? That seems a bit out of the league in the context of the 300-points granted for the flag, in relation to the other tasks. But yeah, maybe?
Looking at zip -FF zipfile2.zip --out outfile.zip
for a magic-bytes fixed version of the file, could not help us with the repair of the file.
With no other leads to follow, lets try building the EOCDR our self. Below is listed a table of the build-up of the EOCDR (from Wikipedia ).
Offset | Bytes | Description |
---|---|---|
0 | 4 | End of central directory signature = 0x06054b50 |
4 | 2 | Number of this disk (or 0xffff for ZIP64) |
6 | 2 | Disk where central directory starts (or 0xffff for ZIP64) |
8 | 2 | Number of central directory records on this disk (or 0xffff for ZIP64) |
10 | 2 | Total number of central directory records (or 0xffff for ZIP64) |
12 | 4 | Size of central directory (bytes) (or 0xffffffff for ZIP64) |
16 | 4 | Offset of start of central directory, relative to start of archive (or 0xffffffff for ZIP64) |
20 | 2 | Comment length (n) |
22 | n | Comment |
So, firstly we need the indicator of 0x06054b50
followed by multi-part information's. As we are suspecting this to be a single archive not spanning multiple files, we can provide 0x0000
for both the "number of disks" and "disk where central directory starts" (those starts after all the files). This gives us the following hexdump:
504b 0506 0000 0000
But now we need to provide the number of central directories in within the file. Hmm, we do not know this number.. Looking at the hexdump for the file to calculate the number, we have to look for the "central directory file header signature" which is 0x02014b50
. But hey -- no signatures is to be found.. So no files in the archive? That seems strange. Maybe we only need the hex-data, extract that and do some magic on that, disregarding the zip-file idea?
Okay - it might seems wild, but taking a sip of my "Faxe-kondi" (a danish "sports" soft-drink ala Sprite) overlooking the hexdump in the background it just hit me. WHY IS THE END OF THE FILE THE SAME AS THE BEGINNING SHOULD BE - JUST BACKWARDS??
Is this "just" a game of reversing?
In Linux we have tac
to reverse a file, it works like cat
(seeing the resemblance?) so a tac zipfil.zip > reversed.zip
and then looking at the hexdump with xxd
gives us:
00000000: f217 cb9b 28a5 1cf3 b655 1163 c31c ca92 ....(....U.c....
00000010: 8ecd 0e7e f6cd 9eef 55b8 9df4 382b 8525 ...~....U...8+.%
00000020: 59ea f1d7 87bd 0000 0000 4000 0000 0040 Y.........@....@
00000030: 1000 b087 57d5 c89d 91d5 c89d 9130 0090 ....W........0..
00000040: 4555 4787 47e2 7616 c666 00c1 0080 0000 EUG.G.v..f......
00000050: 0003 0000 00c3 8686 e8c9 f4a3 b5d8 0000 ................
00000060: 0090 00a0 4030 b405 0000 0000 00e8 0000 ....@0..........
00000070: 00e4 0010 0010 0000 0000 6050 b405 0000 ..........`P....
00000080: 0000 4000 0000 0040 1000 b087 57d5 c89d ..@....@....W...
00000090: 9130 0050 4555 4787 47e2 7616 c666 0000 .0.PEUG.G.v..f..
000000a0: 0000 184a 0000 0010 0000 0000 0081 0080 ...J............
000000b0: 0000 0003 0000 00c3 8686 e8c9 f4a3 b5d8 ................
000000c0: 0000 0090 00a0 30e1 2010 b405 0000 0003 ......0. .......
000000d0: 0000 00c3 8686 e8c9 8070 b405 d429 68c0 .........p...)h.
000000e0: 6fd0 5f87 9ed2 1e54 60e5 794f 80d2 8c4d o._....T`.yO...M
000000f0: e80a ..
That was not as expected - maybe the concatenating is messing with the bytes. Using the command < zipfil.zip xxd -p -c1 | tac | xxd -p -r > reversed.zip
we, in short, print the file with single octets per line, reverse it, and then assemble it again putting the result into a new reversed file.
00000000: 05b4 3040 a000 9000 0000 d8b5 a3f4 c9e8 ..0@............
00000010: 8686 c300 0000 0300 0000 8000 c100 66c6 ..............f.
00000020: 1676 e247 8747 5545 9000 3091 9dc8 d591 .v.G.GUE..0.....
00000030: 9dc8 d557 87b0 0010 4000 0000 0040 0000 ...W....@....@..
00000040: 0000 bd87 d7f1 ea59 2585 2b38 f49d b855 .......Y%.+8...U
00000050: ef9e cdf6 7e0e cd8e 92ca 1cc3 6311 55b6 ....~.......c.U.
00000060: f31c a528 9bcb 17f2 0ae8 4d8c d280 4f79 ...(......M...Oy
00000070: e560 541e d29e 875f d06f c068 29d4 05b4 .`T...._.o.h)...
00000080: 7080 c9e8 8686 c300 0000 0300 0000 05b4 p...............
00000090: 1020 e130 a000 9000 0000 d8b5 a3f4 c9e8 . .0............
000000a0: 8686 c300 0000 0300 0000 8000 8100 0000 ................
000000b0: 0000 1000 0000 4a18 0000 0000 66c6 1676 ......J.....f..v
000000c0: e247 8747 5545 5000 3091 9dc8 d557 87b0 .G.GUEP.0....W..
000000d0: 0010 4000 0000 0040 0000 0000 05b4 5060 ..@....@......P`
000000e0: 0000 0000 1000 1000 e400 0000 e800 0000 ................
000000f0: 0000 ..
Now we can see the the individual hex chars in their correct places. But still reversed -- 05
should be 50
. This can be be fixed with the command rev
to reverse the string before piping it to tac
. So the full command is now: < zipfil.zip xxd -p -c1 | rev | tac | xxd -p -r > reversed.zip
.
00000000: 504b 0304 0a00 0900 0000 8d5b 3a4f 9c8e PK.........[:O..
00000010: 6868 3c00 0000 3000 0000 0800 1c00 666c hh<...0.......fl
00000020: 6167 2e74 7874 5554 0900 0319 d98c 5d19 ag.txtUT......].
00000030: d98c 5d75 780b 0001 0400 0000 0004 0000 ..]ux...........
00000040: 0000 db78 7d1f ae95 5258 b283 4fd9 8b55 ...x}...RX..O..U
00000050: fee9 dc6f e7e0 dce8 29ac c13c 3611 556b ...o....)..<6.Uk
00000060: 3fc1 5a82 b9bc 712f a08e d4c8 2d08 f497 ?.Z...q/....-...
00000070: 5e06 45e1 2de9 78f5 0df6 0c86 924d 504b ^.E.-.x......MPK
00000080: 0708 9c8e 6868 3c00 0000 3000 0000 504b ....hh<...0...PK
00000090: 0102 1e03 0a00 0900 0000 8d5b 3a4f 9c8e ...........[:O..
000000a0: 6868 3c00 0000 3000 0000 0800 1800 0000 hh<...0.........
000000b0: 0000 0100 0000 a481 0000 0000 666c 6167 ............flag
000000c0: 2e74 7874 5554 0500 0319 d98c 5d75 780b .txtUT......]ux.
000000d0: 0001 0400 0000 0004 0000 0000 504b 0506 ............PK..
000000e0: 0000 0000 0100 0100 4e00 0000 8e00 0000 ........N.......
000000f0: 0000 ..
And would you look at that! A file with the correct magic-bytes in the start, a single central directory (and with the information of only one local file header, we know only one file is within the archive) and a valid EOCDR.
Just to make sure, we check file -i
and see that the file indeed is a zip-file. Even running zipdetails
works and tells us the filename within - that's what we are looking for!
[...]
001E Filename 'flag.txt'
[...]
Trying unzip
on the file works - though it now asks for the password of the file. That should be fairly trivial to fix. A good way to crack a password-protected zip-file is using fcrackzip
with a wordlist. Let's try that:
$ fcrackzip -b -D -p /usr/share/wordlists/rockyou.txt -u reversed4.zip
PASSWORD FOUND!!!!: pw == 123456
Now we're able to deflate the file with unzip
using the found password, and then cat
-ing away the flag.txt
file provided.
FLAG
StormCTF{Misc2:B73dba52ceDA4dDccb31Ec1b1cDa24Ff}