|
Brought to you by:
Suppliers of:
|
|
|
| |
| The ZIP format is one of the most widely used compression/archival programs on computers systems. Its use is even more extended on Windows platform, with WinZIP program. A vulnerability in the way WinZIP encrypts files allows quick recovery of the files even if they are encrypted with very long passwords (over 18 characters). |
| |
Credit:
Bibliography:
[1] - "Known Plaintext Attack on the PKZIP Stream Cipher". Eli Biham and Paul C. Kocher
[2] - "ZIP Attack with reduced Known-Plaintext". Mikel Stay
[3] - PKZIP Specs: APPNOTE.txt.
The information has been provided by Mike Stevens - and Elisa Flanders.
|
| |
The PKZIP encryption scheme has been proved weak in many papers that are listed at the end of this advisory. Mike and Elisa have found another way to attack the encryption scheme. This new method was found using reversing engineering techniques on WinZIP's IBDL32.dll.
A known problem in finding the password that was used to encrypt a file is knowing its valid plain text [1]. Mikel Stay published in 2001 "ZIP Attack with Reduced Known-Plaintext" [2], that improved the first technique, but the problem still remained, getting the plaintext.
Getting plaintext is even more difficult because the plaintext is in its compressed form and a minimal change on the data would represent a great alteration, so if we did not know the full file content included on zip file we could not do anything.
The encryption scheme:
Status change functions:
void update_keys(char p) {
key0=crc32(key0, p);
key1=key1 + (key0 & 0xff);
key1=key1 * 134775813 + 1;
key2=crc32(key2, key1>>24);
}
char decrypt_byte(char b) {
unsigned short tmp;
tmp=key2 | 2;
return(tmp * (tmp^1)>>8);
}
To initialize the keys:
..
key0=305419896;
key1=591751049;
key2=878082192;
for(i=0 ; i<strlen(pass) ; i++) {
update_keys(pass[i]);
}
..
To code a byte of data:
..
tmp=byte^decrypt_byte(byte);
update_keys(tmp);
..
Besides, 12 random bytes are prepended to the compressed data in order to make plaintext attack more difficult. These bytes are very important to initialization state of the stream code. Because if we know the state of the stream code on any time we can reverse it until we get to the beginning.
The attack:
Most ZIP coders use rand function to generate these 12 random bytes, but other zippers use their own functions, such is the case of WinZIP.
Attacking this encryption scheme using this 12 prepended random bytes is not a new idea, [2] describes this kind of attack but a minimum of five files in a zip are needed in order to succeed.
If we reverse engineer WinZIP rand() generation code, we find the following code.
46c5a0 push ebp
46c5a1 mov ebp, esp
46c5a3 mov eax, [IBDL32.dll:Seed]
46c5a8 mov edx, eax
46c5aa add edx, edx
46c5ac add edx, eax
46c5ae shl edx, 2
46c5b1 add edx, eax
46c5b3 mov ecx, edx
46c5b5 shl ecx, 4
46c5b8 add ecx, eax
46c5ba mov edx, ecx
46c5bc shl edx, 8
46c5bf sub edx, eax
46c5c1 shl edx, 2
46c5c4 lea ecx, [eax+edx]
46c5c7 mov [IBDL32.dll:Seed], ecx
46c5cd mov eax, [IBDL32.dll:Seed]
46c5c2 sar eax, 10h
46c5c5 mov ecx, eax
46c5c7 and ch, 7fh
46c5ca movzx edx, cx
46c5cd mov eax, edx
46c5cf ret
It seems to be an obfuscated code, but if we analyze this, we can see that C code may be of the sorts of:
unsigned short rand()
{
seed=0x343fD * seed;
return ((seed >> 16)&0x7fff);
}
A normal rand implementation looks like:
unsigned short rand()
{
seed=0x343fD * seed + 0x269ec3;
return ((seed >> 16)&0x7fff);
}
Initially it would appear that the person who wrote this code forgot to add 0x269ec3 to seed, but this also leads to a security problem, because the possible sequences are reduced from 2^(12*8) to 2^(3*8). Now we will show how to crack a ZIP file using this problem.
Reducing the sequences makes it easier to do perform a brute force attack and then guess the state of the stream coder (key0, key1, key2). Using these keys and the known formula form pkcrack's source code (stage2 line 175):
/* The equation from section 3.3 is used twice here:
* (1) key1_{n-1} + LSB(key0_n) = rhs = (key1_n - 1) * INVCONST
* and
* (2) key1_{n-2} + LSB(key0_{n-1}) = (key1_{n-1} - 1) * INVCONST
*
* At this point we know key1_n, MSB(key1_{n-1}) and MSB(key1_{n-2}).
*
* From (2) follows
* MSB(key1_{n-2}) = MSB((key1_{n-1} - 1) * INVCONST - LSB(key0_{n-1}))
* Inserting (1) yields
* MSB(key1_{n-2}) = MSB((rhs - 1)*INVCONST -
* LSB(key0_n)*INVCONST - LSB(key0_{n-1}))
* which means that either
* (a) MSB(key1_{n-2}) = MSB((rhs - 1)*INVCONST) -
* MSB(LSB(key0_n)*INVCONST - LSB(key0_{n-1}))
* or
* (b) MSB(key1_{n-2}) = MSB((rhs - 1)*INVCONST) -
* MSB(LSB(key0_n)*INVCONST - LSB(key0_{n-1})) - 1
*
* It can easily be verified that for any two bytes b1, b2:
* MSB( b1*INVCONST + b2 ) = MSB( b1*INVCONST )
* (simple exhaustive test on 2^16 combinations)
*
* We have computed diff = MSB((rhs - 1)*INVCONST) - MSB(key1_{n-2}).
* Now all we have to do is find values for key0_n so that
* (following from (1))
* MSB(key1_{n-1}) = MSB(rhs-LSB(key0_n))
* and (following from (a) and (b)) either
* diff = MSB(LSB(key0_n)*INVCONST)
* or
* diff = MSB(LSB(key0_n)*INVCONST) + 1
*
* Candidate values are selected using the precomputed lookup table mTab2.
*/
Proof of concept:
Mike Stevens - and Elisa Flanders have been able to exploit this weak random generation on ZIP files with more that 3 files (12*3 => 36 bytes of known plaintext), but with less than two hours on a Pentium 500Mhz with 128Mb.
Attacks to ZIPs with less than 3 files might be also possible because in WinZIP the two CRC most important bytes are stored within the 12 "random" numbers.
tocrack.zip is a file created with WinZIP 8.0, the password is "&THPOL101%ISLAME@|1" which is a 19 length password , ( which impossible to crack if we brute force the password ) , but what we are brute forcing is the 3 keys that are generated with the password.
[rmn@mightsoft ~]$ zipinfo tocrack.zip
Archive: tocrack.zip 679 bytes 3 files
-rw-rw-rw- 2.0 fat 138 T- defX 8-Feb-03 01:31 file3.txt
-rw-rw-rw- 2.0 fat 163 T- defX 8-Feb-03 01:31 file2.txt
-rw-rw-rw- 2.0 fat 270 T- defX 8-Feb-03 01:31 file1.txt
3 files, 571 bytes uncompressed, 339 bytes compressed: 40.6%
[rmn@mightsoft ~]$ ./zipproof -p tocrack.zip
[*] generating posible secuences.. DONE.
[*] reducing number of posible Key2.. DONE.
[*] Bruteforcing:
[-] Key2 => 0x6a54f21e
[*] Generating initial keys:
[-] Key0 => 0xaca8571c
[-] Key1 => 0x439e8759
[-] Key2 => 0x508d8f22
[*] Tryng to get password.. Not found!
[E] Is not posible to find a password for these keys, you can use
findkeys tool (from pkcrack) to get it.
[rmn@mightsoft ~]$
It was not possible to recover the password because it was too large, but with those keys, it is very easy to extract data files from ZIP (using pkcrack zipdecrypt).
[rmn@mightsoft ~]$ ./zipdecrypt 0xaca8571c 0x439e8759 0x508d8f22 tocrack2.zip result.zip
Decrypting file1.txt (bb6c9531638e70d425ebe60b)... OK!
Decrypting file2.txt (0f57542dbf0e18517a5cee0b)... OK!
Decrypting file3.txt (2d2bc008f19607809298f90b)... OK!
Affected targets:
The problem is in IBDL32.dll that is used in some ZIP compressors like WinZIP.
Recommendations:
Even if this problem is not present in your ZIP compressor. As the ZIP encryption scheme is very weak and should not be used to encrypt sensitive data. Use zip and then encrypt with your favorite encryption program.
|
|
|
|
|