FluxArchiv (Part 1) (Category: Reversing) Author(s): sqall
These funny humans
try to exclude us from the delicious beer of the Oktoberfest! They made up a
passcode for everyone who wants to enter the Festzelt. Sadly, our human
informant friend could not learn the passcode for us. But he heard a
conversation between two drunken humans, that they were using the same passcode
for this intercepted archive file. They claimed that the format is is
absolutely secure and solves any kind of security issue. It’s written by this
funny hacker group named FluxFingers. Real jerks if you ask me. Anyway, it
seems that the capability of drunken humans to remember things is limited. So
they just used a 6 character passcode with only numbers and upper-case letters.
So crack this passcode and get our ticket to their delicious german
beer!
Here is the
challenge:
https://ctf.fluxfingers.net/static/downloads/fluxarchiv/hacklu2013_archiv_challenge1.tar.gz
FluxArchiv (Part 2) (Category: Reversing) Author(s): sqall
These sneaky humans!
They do not just use one passcode, but two to enter the Festzelt. We heard that
the passcode is hidden inside the archive file. It seems that the FluxFingers
overrated their programming skill and had a major logical flaw in the archive
file structure. Some of the drunken Oktoberfest humans found it and abused this
flaw in order to transfer hidden messages. Find this passcode so we can finally
drink their beer!
(only solvable when FluxArchiv (Part 1) was solved)
Here is the challenge:
https://ctf.fluxfingers.net/static/downloads/fluxarchiv/hacklu2013_archiv_challenge1.tar.gz
Solution
When trying to solve this challenge, we accidently forgot to read the challenge
description so we ended up solving part2 before part1. For this challenge, we
get a program and an archive created by this program. Starting the program
gives:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| $ ./archiv
################################################################################
FluxArchiv - solved security since 2007!
Written by sqall - leading expert in social-kernel-web-reverse-engineering.
################################################################################
Unknown or invalid command.
Usage: ./archiv <command> <archiv> <password> <file>
commands:
-l <archiv> <password> - lists all files in the archiv.
-a <archiv> <password> <file> - adds a file to the archiv (when archiv does not exist create a new archiv).
-x <archiv> <password> <filename> - extracts the given filename from the archiv.
-d <archiv> <password> <filename> - delete the given filename from the archiv.
|
So the programm requires a password for every operation on an archive file.
After some reversing, we found out, that the password is hashed with sha1 and
the result is used as seed for a rc4 prng. Everytime a chunk ob data should be
encrypted, the prng is reset with the sha1sum and the data is xored with the
prng data. So to find out more about the fileformat, we created two archive
files with different password:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| $ dd if=/dev/urandom of=1mb bs=1024 count=1024
1024+0 Datensätze ein
1024+0 Datensätze aus
1048576 Bytes (1,0 MB) kopiert, 0,0755623 s, 13,9 MB/s
$ ./archiv -a test1 pw1 1mb
################################################################################
FluxArchiv - solved security since 2007!
Written by sqall - leading expert in social-kernel-web-reverse-engineering.
################################################################################
Archiv test1 successfully created.
Progress:
0% ... 10% ... 20% ... 30% ... 40% ... 50% ... 60% ... 70% ... 80% ... 90% ... 100%
File 1mb successfully added to the archiv.
$ ./archiv -a test2 pw2 1mb
################################################################################
FluxArchiv - solved security since 2007!
Written by sqall - leading expert in social-kernel-web-reverse-engineering.
################################################################################
Archiv test2 successfully created.
Progress:
0% ... 10% ... 20% ... 30% ... 40% ... 50% ... 60% ... 70% ... 80% ... 90% ... 100%
File 1mb successfully added to the archiv.
|
The result should be two files with completely the same content only the
cipherstream is different, so with the following script, we can find the
cipherstream starts and reverse the format:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| #!/usr/bin/env python2
import Crypto.Cipher.ARC4
import Crypto.Hash.SHA
xor = lambda a,b: "".join(chr(ord(i)^ord(j)) for i,j in zip(a,b))
f1 = open("test1").read()
f2 = open("test2").read()
assert(len(f1) == len(f2))
stream1 = Crypto.Cipher.ARC4.new(Crypto.Hash.SHA.new("pw1").digest()).encrypt("\x00" * 0x10000)
stream2 = Crypto.Cipher.ARC4.new(Crypto.Hash.SHA.new("pw2").digest()).encrypt("\x00" * 0x10000)
xored_stream = xor(stream1, stream2)
xored_files = xor(f1, f2)
pos = 0
while pos < len(f1):
subpos = 0
while pos + subpos < len(f1) and xored_files[pos+subpos] == xored_stream[subpos]:
subpos += 1
if subpos != 0:
print "%06x: %r (%d bytes)" % (pos, xor(f1[pos:pos+subpos], stream1), subpos)
pos += subpos
else:
pos += 1
|
The script spits out something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| $ ./find_offsets.py
000020: 'FluXL1sT' (8 bytes)
000028: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000030: '\x01\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000038: '\xf9\x03\x00\x00\x00\x00\x00\x00' (8 bytes)
000040: '(\xd7\xf8\xd0\x98\xfc~\x8a\xe3\xc2<\x01\x0f9X\x84' (16 bytes)
000050: '1mb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
0000b0: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0000b8: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0000c0: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (16 bytes)
0000d0: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
000130: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000138: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000140: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (16 bytes)
000150: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
0001b0: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0001b8: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0001c0: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (16 bytes)
0001d0: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
000230: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000238: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000240: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (16 bytes)
000250: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
0002b0: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0002b8: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0002c0: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (16 bytes)
0002d0: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
000330: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000338: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000340: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (16 bytes)
000350: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
0003b0: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0003b8: '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0003c0: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (16 bytes)
0003d0: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
000430: '\x02\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000438: '[random junk]' (1032 bytes)
000840: '\x03\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000848: '[random junk]' (1032 bytes)
101fa0: '\xf9\x03\x00\x00\x00\x00\x00\x00' (8 bytes)
[snip]
101fa8: '[random junk]' (1032 bytes)
1023b0: '\x00\x00\x10\x00\x00\x00\x00\x00' (8 bytes)
1023b8: '[random junk]' (64 bytes)
|
Looks like the format is as follows:
- 12byte unencrypted “FluXArChiV13” header (can be seen when looking at the hexdump)
- 20byte unencrypted sha1 hash of scrambled input pw (found while reversing)
- 8byte “FluXL1sT” header
- 8byte of unknown data
- 8 file entries:
- 2x 8bytes of unknown data
- 16bytes md5sum (found while reversing and checked against 1mb file)
- 96 byte filename, padded with null bytes
- many data chunks:
- 8bytes of unknown data
- 1032byte file data
What is interesting, is the encrypted “FluXL1sT” header and the zero padded
filenames, because we can reconstruct the keystream from these, lets see what
we can find out about the original archive when using what we already know
about the format:
1
2
3
4
5
6
7
8
9
10
11
12
13
| #!/usr/bin/env python2
import sys
xor = lambda a,b: "".join(chr(ord(i)^ord(j)) for i,j in zip(a,b))
f = open(sys.argv[1]).read()
assert(f[:12] == "FluXArChiV13")
pos = 32
keystream = xor(f[pos:pos+8], "FluXL1sT")
pos += 16
for _ in xrange(8):
pos += 32
print "%06x %r" % (pos, xor(keystream, f[pos:pos + len(keystream)]))
pos += 96
|
This first version only prints the first 8 byte of the filenames:
1
2
3
4
5
6
7
8
9
| $ ./parser.py FluxArchiv.arc
000050 'attentio'
0000d0 'Did_You_'
000150 '\x00\x00\x00\x00\x00\x00\x00\x00'
0001d0 'fluxfing'
000250 '\x00\x00\x00\x00\x00\x00\x00\x00'
0002d0 '\x00\x00\x00\x00\x00\x00\x00\x00'
000350 '\x00\x00\x00\x00\x00\x00\x00\x00'
0003d0 '\x00\x00\x00\x00\x00\x00\x00\x00'
|
Looks good, especially the entries without filename - we can use them to
reconstruct the whole file list:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| #!/usr/bin/env python2
import sys
xor = lambda a,b: "".join(chr(ord(i)^ord(j)) for i,j in zip(a,b))
f = open(sys.argv[1]).read()
assert(f[:12] == "FluXArChiV13")
keystream = f[0x3d0:0x3d0+96]
pos = 0x20
lengths = [
8, 8,
8,8,16,96, 8,8,16,96, 8,8,16,96, 8,8,16,96,
8,8,16,96, 8,8,16,96, 8,8,16,96, 8,8,16,96,
]
for len_ in lengths:
print "%06x %r (%d bytes)" % (pos, xor(keystream, f[pos:pos+len_]), len_)
pos += len_
|
This is the result:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| $ ./parser.py FluxArchiv.arc
000020 'FluXL1sT' (8 bytes)
000028 '\xad\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000030 '\x01\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000038 '\x16\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000040 '\xff\xea:RT\xc0Xz\xd5\xb2\xec\xe8\x1b\xe8\xba\xb5' (16 bytes)
000050 'attentionzombie.mp3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
0000b0 '\x02\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0000b8 '\x86\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0000c0 '\xecV8\xdaH\x10\xf7^\x075\xa3\xdf\x9e0\xc1\xb0' (16 bytes)
0000d0 'Did_You_Know.jpg\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
000130 '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000138 '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000140 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (16 bytes)
000150 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
0001b0 '\x89\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0001b8 '\t\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0001c0 '+\xee\x8b\xecl\x1a\x8a\xd6X\xdf\xfb\xe1K\xc1G\x92' (16 bytes)
0001d0 'fluxfingers.png\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
000230 '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000238 '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000240 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (16 bytes)
000250 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
0002b0 '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0002b8 '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0002c0 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (16 bytes)
0002d0 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
000330 '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000338 '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
000340 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (16 bytes)
000350 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
0003b0 '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0003b8 '\x00\x00\x00\x00\x00\x00\x00\x00' (8 bytes)
0003c0 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (16 bytes)
0003d0 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' (96 bytes)
|
Looks like we got the whole file listing and it looks consistent, so we can
assume the keystream is correct. But we still need more bytes of the keystream
to decompress the whole data because we only have 96 bytes at the moment, but
while going through what we already have, we found a new file listing at
0x2bef0
with this script:
1
2
3
4
5
6
7
8
9
10
11
12
13
| #!/usr/bin/env python2
import sys
xor = lambda a,b: "".join(chr(ord(i)^ord(j)) for i,j in zip(a,b))
f = open(sys.argv[1]).read()
assert(f[:12] == "FluXArChiV13")
keystream = f[0x3d0:0x3d0+96]
pos = 0x3d0 + 96
while pos < len(f):
print "%06x %r" % (pos, xor(keystream, f[pos:pos+8]))
pos += 8
print "%06x %r" % (pos, xor(keystream, f[pos:pos+len(keystream)]))
pos += 1032
|
Small modifications to the directory listing parser spit out another
filelisting with just one file th_oh-noes-everybody-panic.gif
and an md5sum
of f1df033f0179fe101d11700fe796b4fd
, luckily we could find the original (we
couldn’t find the original of any other file until now) with a matching md5sum
and could reconstruct enough bytes to parse the whole file with this script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| #!/usr/bin/env python2
import sys
xor = lambda a,b: "".join(chr(ord(i)^ord(j)) for i,j in zip(a,b))
f = open(sys.argv[1]).read()
assert(f[:12] == "FluXArChiV13")
img = open("th_oh-noes-everybody-panic.gif").read()
keystream = f[0x3d0:0x3d0+96]
pos = f.find(xor(keystream, img))
keystream = xor(img, f[pos:])
pos = 0x3d0 + 96
while pos < len(f):
print "%06x %r" % (pos, xor(keystream, f[pos:pos+8]))
pos += 8
print "%06x %r" % (pos, xor(keystream, f[pos:pos+len(keystream)]))
pos += 1032
|
Some searching in the output revealed this: Flag: D3letinG-1nd3x_F4iL
.
We entered the flag into the Part1 field but it didn’t work - it was the flag
for Part2 :)
Solving part1 now was easy because we had the rc4 keystream and could
bruteforce against it with a very simple bruteforce script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
| #!/usr/bin/env python2
import sys
xor = lambda a,b: "".join(chr(ord(i)^ord(j)) for i,j in zip(a,b))
f = open(sys.argv[1]).read()
assert(f[:12] == "FluXArChiV13")
keystream = f[0x3d0:0x3d0+6]
import Crypto.Cipher.ARC4
import Crypto.Hash.SHA
import itertools
import string
import threading
import os
alphabet = string.uppercase + string.digits
threads = 4
class T(threading.Thread):
def __init__(self, t):
self.t = t
threading.Thread.__init__(self)
self.daemon = True
self.start()
def run(self):
t = self.t
for first in alphabet[t::threads]:
for k in itertools.product(alphabet, repeat = 5):
res = Crypto.Cipher.ARC4.new(Crypto.Hash.SHA.new(first + "".join(k)).digest()).encrypt(keystream)
if ord(res[0]) != 0:
continue
if ord(res[1]) != 0:
continue
if ord(res[2]) != 0:
continue
if ord(res[3]) != 0:
continue
if ord(res[4]) != 0:
continue
if ord(res[5]) != 0:
continue
sys.stdout.write("%s\n" % (first + "".join(k)))
os.kill(os.getpid(), 11)
threads = [T(t) for t in xrange(threads)]
for t in threads:
t.join()
|
And this was the result:
1
2
3
| $ ./password_bf.py FluxArchiv.arc
PWF41L
Segmentation fault
|
Flag for Part1 is: PWF41L