Background
http://blog.fireeye.com/research/2009/03/a-new-method-to-monetize-scareware.html
http://voices.washingtonpost.com/securityfix/2009/03/antivirus2009_holds_victims_do.html
Exposition
The Filefix Professional 2009
(wizard.exe) demo
version
will uncorrupt
(read: decrypt) one file. Which means that
I can learn everything I need to know to decrypt all files from analyzing
just this binary itself.
So, where to start looking? Well a file decryption routine is going to
need to read and write files, so search for calls to ReadFile.
Almost the first thing I find is a loop that calls ReadFile,
has an inner loop that XOR's over each byte in the buffer, and
then calls WriteFile. Hmmm… (See appendix.)
Now all I need are some encrypted files. Filefix Pro doesn't encrypt anything itself, and I didn't have a sample of the malware which did. Fortunately (for me), we were in contact with some of the victims, so as soon as I had some samples it confirmed my suspicion about the encryption just being ECB-XOR. The only thing which took me more than a minute to figure out was that the crypto key was stored at the end of the file. (Since I had already figured out how to decrypt it without knowing the key.)
Spending a little more time reading the binary, I also found the routine
which checks for valid keys at the ends of files. This allows Filefix to tell
corrupt
and non-corrupt
files apart when scanning the disk. There is a
strict mathematical relationship between the four bytes of the key.
Implemented as three simple boolean tests. If you do the math, this
also means that there are only 256 possible valid keys.
In English
Using the key 0x6622CDAB as an example:
- Divide 0xCD by two, and compare to 0x66
- XOR 0xAB by 0x66 and compare to 0xCD
- AND 0xAB by 0x66 and compare to 0x22
In Perl
@key = (0xAB, 0xCD, 0x22, 0x66);
print "Key Check: First Test Pass\n" if ( ($key[1]>>1) == $key[3] );
print "Key Check: Second Test Pass\n" if ( ($key[0]^$key[3]) == $key[1] );
print "Key Check: Third Test Pass\n" if ( ($key[0]&$key[3]) == $key[2] );
In the original assembly
I have to wonder if the original was written in assembly too. Compilers generally don't generate code like this.
; I hope you can understand this...
mov eax, [esp+10h+Buffer] ; pointer to four byte result from ReadFile()
; Pretend it read 0x6622CDAB from the file
test ah, ah ; EAX=0x6622CDAB so AH is not 0
jz short loc_405C16 ; loc_405C16 calls CloseHandle() and returns 0
mov cl, byte ptr [esp+10h+Buffer+3] ; CL=0x66 The last byte from the input buffer.
mov dl, ah ; DL=0xCD
shr dl, 1 ; DL=0x66
cmp cl, dl ; if (0x66==0x66)
jnz short loc_405C16 ; Yes equal, keep going...
mov dl, cl ; DL=0x66 and CL=0x66 so this
; MOV is completely pointless
xor dl, ah ; DL=0xAB AH=0x66
cmp al, dl ; if (0xCD==0xCD)
jnz short loc_405C16 ; Yep, it does, so keep going...
and al, cl ; AL=0x66&0xAB -> EAX = 0x6622CD22
cmp byte ptr [esp+10h+Buffer+2], al ; if (0x22==0x22) Next to last buffer byte
jnz short loc_405C16 ; So, if we get past here it was a valid key.
By shuffling the math around a bit, you can generate all of the possible valid keys like this (in Perl)
# Mathematically 0x00000000 is a valid key,
# but there's a test for that, so it's not.
for ($B=1; $B<=0xFF; $B++) {
$D = $B >> 1;
$A = $B ^ $D;
$C = $A & $D;
print sprintf("%02x%02x%02x%02x\n", $A, $B, $C, $D);
}
With a keyspace of only 255 valid keys out of 4,294,967,296, and assuming a
perfectly random distribution (YMMV), there is a 1 in 16,843,009 chance that
a non-encrypted file will be decrypted, and corrupted for-reals, since the
last four bytes will be truncated. The file will also be (re-)encrypted,
since this crypto function is its own inverse. (Well, almost, the missing
key will stop Filefix from re-re-decrypting the file, except for one chance
in 4,294,967,296, when the last four bytes of the encrypted file are the same
as the last four bytes of the original file. There are 253 chances out of
4,294,967,296 that it will re-re-re-encrypt the file again with a new key (with
just the XOR's of the two wrong keys.) (If the keys form an Algebraic Group
then this doesn't matter.) (Each time you go through this composition,
you'll loose eight bytes from the end of the file.) (Ok my quick analysis of
the keyspace leads me to believe that the XOR operator does not form a
group over this set.)
Rationale Tangent
The second/third byte of the key… ok
the $C or $key[2] byte from the above examples,
can only have one of 34 possible values, and 0x03 is not
one of them. But 0x01 and 0x02 are valid, so if you
XOR'd them together the result would be 0x03, which is
not valid. Therefore this keyspace is not closed under XOR and is not an Algebraic Group. (So you now have at most 2,147,483,648 possible keys, as
the first byte (big-endian) will never have the high-bit set.)
But anyway, yeah, there is a non-zero chance that Filefix will chop one of
255 possible byte patterns off the end of your perfectly good file, and
then be unable to re-decrypt it itself. (If this happens to you, you can
XOR the first four bytes of the now for-reals corrupt file with the
magic-number of that filetype's signature (i.e. "JFIF" for JPEGs, "%PDF"
for PDFs, 0xD0CF11E0 for Office Documents.) and use that as
the key, and put those four bytes back onto the end of the file.
(I've never actually tried any of this, it's all purely hypothetical.)
Anyway, you don't really need to know any of the above in order to decrypt a file…
The Decryption Algorithm
- Filefix searches the entire filesystem for files with one of the following (28) extensions:
"doc", "xls", "ppt", "pdf", "jpg", "jpeg", "png", "mp3", "wma", "mdb", "pst", "docx", "docm", "dotx", "dotm", "xlsx", "xlsm", "xltx", "xltm", "xlsb", "xlam", "pptx", "pptm", "potx", "potm", "ppam", "ppsx", "ppsm" - It opens each file, and reads the last four bytes. If these four bytes
satisfy a particular boolean formula (see above), then the file is
corrupt
, and the file is added to the to-decrypt list. - Create a new file, which is this four byte key repeatedly
XOR'd over the victim-file. Think of it asXORin ECB mode with a 32-bit key. - Don't write the key back out to the new file. The newly created file will be four bytes shorter than the encrypted file. Delete the encrypted file, and give the newly decrypted file the same filename as the original. (Filesystem metadata is not preserved.)
Yeah, that's it.
I don't have a sample of the trojan which performs this encryption, but based upon other people's reports, these are my deductions and speculations:
The installer for this malware [Vundo?] has the MD5 hash of d54340d03ebf417ac29ce5a087e944c2 (UPX packed) and injects
C:\WINDOWS\SYSTEM32\fpfstb.dll
into theexplorer.exe
process using
. [Information via Norman Sandbox (Mar-08-2009) and Bleeping Computer] There may be other variants of this malware.InjectLibraryRemoteSee Also: http://www.virustotal.com/analisis/63f02eea746bdf7b784dfaf9d3a0369e
- From the decryption algorithm above, we can deduce the encryption algorithm.
- The malware generates a random (maybe?) key like this (except not using
Perl obviously):
$B = 255 * rand(); # There's probably a test for (0==$B) in the malware
$D = $B >> 1;
$A = $B ^ $D;
$C = $A & $D;
print sprintf("%02x%02x%02x%02x\n", $A, $B, $C, $D); - Create a new file, which is this key repeatedly
XOR'd over the victim-file. Think of it asXORin ECB mode with a 32-bit key. - Append the four byte key to the end of the new
corrupted
file, and delete the original. The new file grows by four bytes longer than the original. Apparently the filenames match up, but no other metadata.
- The malware generates a random (maybe?) key like this (except not using
Perl obviously):
- According to victim reports, the trojan encrypts all of the files
(matching above extension list) within the
My Documents
directory, and then files in other locations, only when opened. This makes me wonder if the trojan has hooked a function or two in explorer.exe and is only encrypting on file open or close, and that everything ending up encrypted underMy Documents
is a side effect of another benign system process scanning through the files. (This is pure speculation on my part.) - Puts an icon into the Windows System Tray, which pops up balloons about
fixing corrupt files. Apparently directs you to the
Filefix Professional 2009
website.
That's nice, but how do I get my files back?
Please be aware, I can not vouch for any of these, as I have not tested them at all. Use at your own risk; if it breaks you get to keep the pieces, [include standard disclaimers here].
Boban Spasic [malzilla.org] wrote a batch decryptor in Delphi (I think, but
it's a Windows EXE so you don't have to care), which is probably the easiest
option currently if you don't know how to program.
http://www.malzilla.org/anti_filefix/anti_filefix.exe
Aaron Atwater [Dalhousie University] wrote a batch decrptor in Java. Available here: http://torch.cs.dal.ca/~atwater/fileunlocker/
There's my own hastily written proof-of-concept Perl script [See previous blog entry], which you probably shouldn't use if you don't know Perl.
Update Mar 25
Symantec has also released a tool to decrypt the corrupt files:
http://www.symantec.com/content/en/us/global/removal_tool/threat_writeups/FixXrupter.exe
(Written by Masaki Suenaga in VC7 I think. Sorry I keep doing this, PE file forensics is one of my side-projects.)
Information about that from here: https://forums2.symantec.com/t5/blogs/blogarticlepage/blog-id/malicious_code/article-id/255
I should have mentioned, the instructions for Bobby's tool (linked above) are at: http://www.bleepingcomputer.com/virus-removal/remove-filefix-professional
Thanks to everyone I mentioned, and anyone I forgot.
Thanks also go to Dan and Travis (via Devshed) who sent me some encrypted files.
Appendix
Ok, you can stop reading now.
The Filefix Pro 2009
executable I examined has the following properties:
Filename: wizard.exe
Build Time: Wed Mar 18 09:03:10 2009
MD5sum: e1827fbbf959d7c5f3219a1f0b0c35fc
Size: 626688
; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
; int __cdecl Decryption_Routine(LPCSTR lpFileName)
Decryption_Routine proc near ; CODE XREF: StartAddress+130↑p
var_D = byte ptr -0Dh
lDistanceToMove = dword ptr -0Ch
NumberOfBytesWritten= dword ptr -8
Buffer = dword ptr -4
lpFileName = dword ptr 4
sub esp, 10h
push ebx
push esi
mov esi, [esp+18h+lpFileName]
xor bl, bl
push esi ; lpFileName
mov [esp+1Ch+var_D], bl
call Key_Check_Routine
add esp, 4
test al, al
jz loc_405E95
push 0 ; hTemplateFile
push 0 ; dwFlagsAndAttributes
push 3 ; dwCreationDisposition
push 0 ; lpSecurityAttributes
push 1 ; dwShareMode
push 0C0000000h ; dwDesiredAccess
push esi ; lpFileName
call ds:CreateFileA
mov esi, eax
cmp esi, 0FFFFFFFFh
jz loc_405E95
push ebp
push edi
push 0 ; lpFileSizeHigh
push esi ; hFile
call ds:GetFileSize
mov ebx, eax
cmp ebx, 40h
jbe loc_405E88
mov ebp, ds:SetFilePointer
push 0 ; dwMoveMethod
push 0 ; lpDistanceToMoveHigh
lea eax, [ebx-4]
push eax ; lDistanceToMove
push esi ; hFile
mov [esp+30h+lDistanceToMove], 0
call ebp ; SetFilePointer
push 0 ; lpOverlapped
lea ecx, [esp+24h+lDistanceToMove]
push ecx ; lpNumberOfBytesRead
push 4 ; nNumberOfBytesToRead
lea edx, [esp+2Ch+Buffer]
push edx ; lpBuffer
push esi ; hFile
call ds:ReadFile
push 10000h ; size_t
call _malloc
mov edi, eax
add esp, 4
test edi, edi
jz loc_405E88
xor eax, eax
add ebx, 0FFFFFFFCh
mov [esp+20h+lDistanceToMove], eax
jz short loc_405E56
loc_405DE7: ; CODE XREF: Decryption_Routine+114↓j
push 0 ; dwMoveMethod
push 0 ; lpDistanceToMoveHigh
push eax ; lDistanceToMove
push esi ; hFile
mov [esp+30h+NumberOfBytesWritten], 0
call ebp ; SetFilePointer
push 0 ; lpOverlapped
lea eax, [esp+24h+NumberOfBytesWritten]
push eax ; lpNumberOfBytesRead
push 10000h ; nNumberOfBytesToRead
push edi ; lpBuffer
push esi ; hFile
call ds:ReadFile
mov ecx, [esp+20h+NumberOfBytesWritten]
xor eax, eax
test ecx, ecx
jbe short loc_405E26
main_crypto_loop: ; CODE XREF: Decryption_Routine+E4↓j
mov edx, eax
and edx, 3
mov dl, byte ptr [esp+edx+20h+Buffer]
xor [eax+edi], dl ; The "Decryption" operator.
inc eax
cmp eax, ecx
jb short main_crypto_loop
loc_405E26: ; CODE XREF: Decryption_Routine+D3↑j
mov eax, [esp+20h+lDistanceToMove]
push 0 ; dwMoveMethod
push 0 ; lpDistanceToMoveHigh
push eax ; lDistanceToMove
push esi ; hFile
call ebp ; SetFilePointer
mov edx, [esp+20h+NumberOfBytesWritten]
push 0 ; lpOverlapped
lea ecx, [esp+24h+NumberOfBytesWritten]
push ecx ; lpNumberOfBytesWritten
push edx ; nNumberOfBytesToWrite
push edi ; lpBuffer
push esi ; hFile
call ds:WriteFile
mov eax, [esp+20h+lDistanceToMove]
add eax, [esp+20h+NumberOfBytesWritten]
cmp eax, ebx
mov [esp+20h+lDistanceToMove], eax
jb short loc_405DE7
loc_405E56: ; CODE XREF: Decryption_Routine+A5↑j
push edi ; void *
call _free
mov eax, [esp+24h+lDistanceToMove]
add esp, 4
push 0 ; dwMoveMethod
push 0 ; lpDistanceToMoveHigh
add eax, 0FFFFFFFCh
push eax ; lDistanceToMove
push esi ; hFile
call ebp ; SetFilePointer
push esi ; hFile
call ds:SetEndOfFile
push esi ; hObject
mov bl, 1
call ds:CloseHandle
pop edi
pop ebp
pop esi
mov al, bl
pop ebx
add esp, 10h
retn
; –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
loc_405E88: ; CODE XREF: Decryption_Routine+51↑j
; Decryption_Routine+96↑j
mov bl, [esp+20h+var_D]
push esi ; hObject
call ds:CloseHandle
pop edi
pop ebp
loc_405E95: ; CODE XREF: Decryption_Routine+1A↑j
; Decryption_Routine+3B↑j
pop esi
mov al, bl
pop ebx
add esp, 10h
retn
Decryption_Routine endp
Julia Wolf @ FireEye Malware Intelligence Lab
Questions/Comments to research [@] fireeye [.] com

Twitter
I have the same problem as Jim Nixon with the .encrypt files and the same letter. Is there a fix?
Posted by: Dana Boatman | 2009.04.11 at 11:18 AM
Jim Nixon:
I'm receiving your emails, but when I reply to your Comcast address, the mail bounces. Do you have a different email address?
Posted by: Julia Wolf | 2009.04.10 at 10:00 AM
Her files all have added prefix .encrypt.
That's something different than this whole Vundo/FileFix thing. Can you email me at julia(at)fireeye.com? I think I can find a solution.
Posted by: FE Malware Researcher | 2009.03.30 at 01:31 PM
You are awesome - like what you do. My Daughter's PC was infected by this Ransom attack. Also has WinCodecPRO that keeps giving false alerts. Her files all have added prefix .encrypt. Task Manager won't work - says "File not found." Screensaver and Backgrounds gone. Tried Running Bobby's program but would not select files just stayed blank. The Notepad Ransom Note message (titled "crypt") they left says all files are encrypted, buy the decoder for $300 by Western Union or Bank Transfer. Says to e-mail: rc4help@yahoo.com, other contact: www.cryptoraes512.ueuo.com. These guys are really evil and this is very frustrating, all her school papers, pictures, etc. are on there and we cannot access, any help appreciated. Thanks so much.
Posted by: Jim Nixon | 2009.03.28 at 03:04 PM