Introduction
I was recently sent an email by someone who was hit with a new species of ransomware. This one encrypted all of the documents on the system, attached the extension .vscrypt
to the end, and changed the desktop wallpaper to a ransom note written in Russian. Here are my findings…
MD5 Sum | Filename | Magic Number | Packer | Build Time |
---|---|---|---|---|
56e78abca7acad5165b7390eaa32ca67 56e78abca7acad5165b7390eaa32ca67 | Possible trojan.scr Possible trojan.scr | MS-DOS executable (EXE), OS/2 or MS Windows MS-DOS executable (EXE), OS/2 or MS Windows | not detected (because I haven’t kept my database up to date) not detected (because I haven’t kept my database up to date) |
Fri Jun 19 15:22:17 1992 Fri Jun 19 15:22:17 1992 |
…when executed, drops the following files onto the system: [It pretends to be an installer for Windows Media Player 9
(English).]
MD5 Sum | Filename | Magic Number | Packer | Build Time |
---|---|---|---|---|
7e5f5bea9600121a41dd4619abf70029 7e5f5bea9600121a41dd4619abf70029 | /Program Files/Flash Media Arts,inc/SWF Video/23854356.avi /Program Files/Flash Media Arts,inc/SWF Video/23854356.avi | MS-DOS executable (EXE), OS/2 or MS Windows MS-DOS executable (EXE), OS/2 or MS Windows | Microsoft Visual C++ v7.0 Microsoft Visual C++ v7.0 | Tue Nov 20 16:59:32 2007 Tue Nov 20 16:59:32 2007 |
010d7b79d002d747f420a7880f89ee38 010d7b79d002d747f420a7880f89ee38 | /Program Files/Flash Media Arts,inc/SWF Video/Free_update.exe /Program Files/Flash Media Arts,inc/SWF Video/Free_update.exe | MS-DOS executable (EXE), OS/2 or MS Windows MS-DOS executable (EXE), OS/2 or MS Windows | Microsoft Visual Basic v5.0/v6. Microsoft Visual Basic v5.0/v6. | Tue May 12 04:03:40 2009 Tue May 12 04:03:40 2009 |
aa74d413fcc98fef29ba9bd75f894093 aa74d413fcc98fef29ba9bd75f894093 | /Program Files/Flash Media Arts,inc/SWF Video/Uninstall.exe /Program Files/Flash Media Arts,inc/SWF Video/Uninstall.exe | MS-DOS executable (EXE), OS/2 or MS Windows MS-DOS executable (EXE), OS/2 or MS Windows | not detected, but packed anyway not detected, but packed anyway | Fri Jun 19 15:22:17 1992 Fri Jun 19 15:22:17 1992 |
031fc32cd1b5bde5b1efa1d148815000 031fc32cd1b5bde5b1efa1d148815000 | /Program Files/Flash Media Arts,inc/SWF Video/Uninstall.ini /Program Files/Flash Media Arts,inc/SWF Video/Uninstall.ini | ISO-8859 text, with CRLF line terminators ISO-8859 text, with CRLF line terminators | N/A N/A | N/A N/A |
d3583ac12d068e231c0b1e62c2a7eb49 d3583ac12d068e231c0b1e62c2a7eb49 | /Program Files/Flash Media Arts,inc/SWF Video/Video_codec.exe /Program Files/Flash Media Arts,inc/SWF Video/Video_codec.exe | MS-DOS executable (EXE), OS/2 or MS Windows MS-DOS executable (EXE), OS/2 or MS Windows | Microsoft Visual Basic v5.0/v6.0 Microsoft Visual Basic v5.0/v6.0 | Tue May 12 04:03:40 2009 Tue May 12 04:03:40 2009 |
5f9927ee59b4881a2ce8634332f63fa8 5f9927ee59b4881a2ce8634332f63fa8 | /Program Files/Flash Media Arts,inc/SWF Video/svchost.exe /Program Files/Flash Media Arts,inc/SWF Video/svchost.exe | MS-DOS executable (EXE), OS/2 or MS Windows MS-DOS executable (EXE), OS/2 or MS Windows | Microsoft Visual Basic v5.0/v6.0 Microsoft Visual Basic v5.0/v6.0 | Tue May 12 04:03:40 2009 Tue May 12 04:03:40 2009 |
It immediately executes Video_codec.exe
and svchost.exe
. svchost.exe
and CSCA1.DLL
were written in Delphi. These files all use the same packer written in Visual Basic 6 of all things. The packer uses Blowfish, and has a giant blob of a Base64 encoded EXE file embedded in it which it drops to disk, and then that
does something else, and blah blah blah, I got around it on the first try. Whomever compiled this packer, appears to have a German Locale, based upon paths like: C:\Programme\Microsoft Visual Studio\VB98\VB6.OLB
(Free_update.exe
is a keylogger by the way.) The VSCrypt
ransomware is svchost.exe
in this case. And these are the files it drops when executed:
MD5 Sum | Filename | Magic Number | Packer | Build Time |
---|---|---|---|---|
b817a4c8ca2479be0ea7e5dab1cb4432 b817a4c8ca2479be0ea7e5dab1cb4432 | /vsworkdir/CSCA1.DLL /vsworkdir/CSCA1.DLL | MS-DOS executable (EXE), OS/2 or MS Windows MS-DOS executable (EXE), OS/2 or MS Windows | not detected (because I haven’t kept my database up to date) not detected (because I haven’t kept my database up to date) |
Fri Jun 19 15:22:17 1992 Fri Jun 19 15:22:17 1992 |
80e1d714045a4402e3992a195f7e7a08 80e1d714045a4402e3992a195f7e7a08 | /vsworkdir/shantazh.jpg /vsworkdir/shantazh.jpg | JPEG image data, JFIF standard 1.00, comment: “LEAD Technologies Inc. V1.01″ JPEG image data, JFIF standard 1.00, comment: “LEAD Technologies Inc. V1.01″ | N/A N/A | N/A N/A |
So what does it do?
VSCrypt (svchost.exe
) searches each directory on the system, for the following files:
*.jpg
*.rar
*.zip
*.txt
*.xls
*.rtf
*.jpeg
*.html
*.htm
*.php
*.eml
*.3gp
*.7z
For each file like this it finds, it encrypts it using the Control Sum Cript Algorithm v1.0
(CSCA1.DLL) with the password Fantazma1518061DgFgvFdvHyfvFdWwlm876Ql
, more on this below. Adds a .vscrypt
to the end of the filename, and deletes the original. At the end of this process, it drops shantazh.jpg
and sets [HKEY_CURRENT_USER\Control Panel\Desktop] ConvertedWallpaper = "C:\vsworkdir\shantazh.jpg" in the registry — setting the desktop background to the «шантаж» or blackmail
note.
All your files are belong to us.
I went through the effort of typing this in while translating, so here is the text of the message in the shantazh.jpg
file. I don't speak Russian very well, so please forgive me if I made any typos.
Привет я Trojan encoder точнее одна из его разновидностей ::) мой автор человек с ником КОРЕКТОР и он с удовольствием продаст вам дешифратор для тех файлов что я успел зашифровать на вашем компьютере за скромную сумму в 10 долларов это где то 350 рублей вот данные для свизи с моим хозяиномMail: [email protected] icq 481095
ах да чуть не забыл не стирайте файлы с расширением
vscrypt если сотрете вернуть зашифрованую информацию станет не возможно
Удачи искренне ваши Trojan encoder и КОРРЕКТОР
It basically says that either a person or a program named Trojan Encoder
(I can't tell) has encrypted your files, and if you want to get them back, they're selling CORRECTOR for the affordable price of US$10 or about 350 Rubles. (Update: better translation here)
So here, I'll save you the US$10…
Control Sum Cript Algorithm v1.0
This is an additive stream cipher, which generates it's keystream by repeatedly taking the CRC32 sum of a shuffled string (the password). The shuffling algorithm takes the first byte of the password, and moves it to the end. Then for the next call, it takes the second byte of the password, and moves that to the end. And then the third byte, moved to the end. And so on, until it reaches the end of the string, then it goes back to the first byte and moves that to the end, and so on. Intuitively, if you shuffle a deck of cards this way, it'll eventually unshuffle itself (since you always swap with the same position). For a deck of 38 cards, or a password of 38 characters, the pattern will cycle after only 1110 shuffles. Making the password longer won't help, it's not linear; 50 characters will cycle after 735 shuffles, and 65 characters will cycle after 448 shuffles. (But if you want to try this experiment with a deck of 52 cards, It's going to take 32,844 card swaps to get the cards back into their original order.) So, basically the number of permutations before you cycle is this sequence: A051732 Number of interlacings (or shuffles) required to restore deck of n cards to original order
times (n-1)
(i.e. a(n)*(n-1)
). (Thanks to Andrea for finding that for me.)
So, with a four byte CRC32 of this shuffled password, the keystream cycles every (1110*4)=4440 bytes.
Offset | Generator | CRC (keystream) |
---|---|---|
0 0 | crc32("antazma1518061DgFgvFdvHyfvFdWwlm876QlF") crc32("antazma1518061DgFgvFdvHyfvFdWwlm876QlF") | bc 2f 25 80 bc 2f 25 80 |
4 4 | crc32("atazma1518061DgFgvFdvHyfvFdWwlm876QlFn") crc32("atazma1518061DgFgvFdvHyfvFdWwlm876QlFn") | ae f8 53 e4 ae f8 53 e4 |
8 8 | crc32("atzma1518061DgFgvFdvHyfvFdWwlm876QlFna") crc32("atzma1518061DgFgvFdvHyfvFdWwlm876QlFna") | c3 0b 1d c9 c3 0b 1d c9 |
12 12 | crc32("atza1518061DgFgvFdvHyfvFdWwlm876QlFnam") crc32("atza1518061DgFgvFdvHyfvFdWwlm876QlFnam") | 68 9b 8f aa 68 9b 8f aa |
20 20 | crc32("atza518061DgFgvFdvHyfvFdWwlm876QlFnam1") crc32("atza518061DgFgvFdvHyfvFdWwlm876QlFnam1") | d8 d4 a7 6a d8 d4 a7 6a |
[...] [...] | [...] [...] | [...] [...] |
4432 4432 | crc32("Fantazma1518061DgFgvFdvHyfvFdWwlm876lQ") crc32("Fantazma1518061DgFgvFdvHyfvFdWwlm876lQ") | 34 20 02 e1 34 20 02 e1 |
4436 4436 | crc32("Fantazma1518061DgFgvFdvHyfvFdWwlm876Ql") crc32("Fantazma1518061DgFgvFdvHyfvFdWwlm876Ql") | 9b 24 82 d3 9b 24 82 d3 |
4440 4440 | crc32("antazma1518061DgFgvFdvHyfvFdWwlm876QlF") crc32("antazma1518061DgFgvFdvHyfvFdWwlm876QlF") | bc 2f 25 80 bc 2f 25 80 |
4444 4444 | crc32("atazma1518061DgFgvFdvHyfvFdWwlm876QlFn") crc32("atazma1518061DgFgvFdvHyfvFdWwlm876QlFn") | ae f8 53 e4 ae f8 53 e4 |
You just XOR
bc 2f 25 80 ae f8 53 e4 c3 0b 1d c9 68 9b 8f
aa
… with your data to encrypt or decrypt it. Here's a link to the full keystream if you're curious: Download vscrypt_keystream.bin Statistics about this keystream appear below.
<speculation>I believe that it's possible to recover the password, given only the keystream from this cipher. (Just hand the program you want to crack a file full of nulls to get the keystream.) You can easilly figure out the length of the password from the cycle length of the keystream. And CRC32 is very linear, you can reverse the function and get some of the original bits out. With a zillion CRC's for each permutation, and with knowledge of which eight bits of the key are being shuffled each time. It should be possible to just write a big linear equation to solve for the key, given only the keystream.</speculation>
Also, any repeated CRC32's in the stream, also indicate that there is a letter (octet) which appears at least twice. (When the two identical octets get moved to the end (positions (length) and (length-1), and it's permutation number (length - 1 - (offset MOD length)) then the two swap with themselves, giving the same CRC result. The password
Fantazma1518061DgFgvFdvHyfvFdWwlm876Ql for example, the letter v
does this.
Iteration | Generator | CRC (keystream) |
---|---|---|
328 328 | crc32("W1wF0gF6Dfd8nmtQgl5v71aaFz861aHdmylvFv") crc32("W1wF0gF6Dfd8nmtQgl5v71aaFz861aHdmylvFv") | 95 f4 fb ca 95 f4 fb ca |
329 329 | crc32("W1wF0gF6Dfd8nmtQgl5v71aaFz861aHdmlvFvy") crc32("W1wF0gF6Dfd8nmtQgl5v71aaFz861aHdmlvFvy") | 6e 95 cd 36 6e 95 cd 36 |
330 330 | crc32("W1wF0gF6Dfd8nmtQgl5v71aaFz861aHdmlFvyv") crc32("W1wF0gF6Dfd8nmtQgl5v71aaFz861aHdmlFvyv") | 01 89 aa f5 01 89 aa f5 |
331 331 | crc32("W1wF0gF6Dfd8nmtQgl5v71aaFz861aHdmlFyvv") crc32("W1wF0gF6Dfd8nmtQgl5v71aaFz861aHdmlFyvv") | f3 d2 6e 79 f3 d2 6e 79 |
332 332 | crc32("W1wF0gF6Dfd8nmtQgl5v71aaFz861aHdmlFyvv") crc32("W1wF0gF6Dfd8nmtQgl5v71aaFz861aHdmlFyvv") | f3 d2 6e 79 f3 d2 6e 79 |
333 333 | crc32("1wF0gF6Dfd8nmtQgl5v71aaFz861aHdmlFyvvW") crc32("1wF0gF6Dfd8nmtQgl5v71aaFz861aHdmlFyvvW") | 6d ae 69 d4 6d ae 69 d4 |
[...] [...] | [...] [...] | [...] [...] |
884 884 | crc32("W1wF0gF6Df18nmtQgl5v7daaFz861aHdmlvFvy") crc32("W1wF0gF6Df18nmtQgl5v7daaFz861aHdmlvFvy") | 92 1a 36 f9 92 1a 36 f9 |
885 885 | crc32("W1wF0gF6Df18nmtQgl5v7daaFz861aHdmlFvyv") crc32("W1wF0gF6Df18nmtQgl5v7daaFz861aHdmlFvyv") | fd 06 51 3a fd 06 51 3a |
886 886 | crc32("W1wF0gF6Df18nmtQgl5v7daaFz861aHdmlFyvv") crc32("W1wF0gF6Df18nmtQgl5v7daaFz861aHdmlFyvv") | 0f 5d 95 b6 0f 5d 95 b6 |
887 887 | crc32("W1wF0gF6Df18nmtQgl5v7daaFz861aHdmlFyvv") crc32("W1wF0gF6Df18nmtQgl5v7daaFz861aHdmlFyvv") | 0f 5d 95 b6 0f 5d 95 b6 |
888 888 | crc32("1wF0gF6Df18nmtQgl5v7daaFz861aHdmlFyvvW") crc32("1wF0gF6Df18nmtQgl5v7daaFz861aHdmlFyvvW") | d5 eb ad 60 d5 eb ad 60 |
The letter v
appears in the string Fantazma1518061DgFgvFdvHyfvFdWwlm876Ql
at positions 20, 23, and 27 (counting up from 1, this is math not computer science). So, there is some kind of mathamatical relationship between the numbers: 20, 23, 27, 38, 331, 332, 886, 887, and 1110. But I haven't figured out what it is yet.
So, yeah, even if you can't just rip the password out of the EXE, treating this cipher as a black-box, I believe that key recovery is possible with only knowledge of the keystream.
Disassembly/Source Code
In the first draft of this, I had a disassembly of most of the code out of C:\vsworkdir\CSCA1.DLL
, with some commentary by me of what things were doing. But then I discovered the source code for CSCA1.DLL on the «Блог программистов» (Programmer's Blog), which is much more understandable than my marked up disassembly. Source Code: csca1.zip and this is the article that rpy3uH (the author) wrote about it: Шифровка с помощью пароля. Улучшаем алгоритм шифрования (Encoding with password. We improve the coding algorithm. [or something like that] I don't really speak Russian, so I wasn't sure at first if this was just an analysis of CSCA1.DLL by someone else, or the origin. It seems to refer to some earlier article about a cypher that's not as good at this one, but I can't quite figure out where that is.)
Proof of Concept
Anyway, I hacked together a vscrypt decoder in perl. It needs some work, but I spent less than an hour on this. I don't recommend that you use this at all, but if you're writing your own decoder, this is something to work from.
Usage:
perl vsdecrypt.pl foo.txt.vscrypt foo.txt
or
perl vsdecrypt.pl inputfile outputfile
#!/usr/bin/perl -w
use lib qw( blib/lib lib );
use Archive::Zip;
use FileHandle;
use Fcntl;
exit(0); #Don't really use this script, it's a demo for other programmers.
my $infile = shift; #TODO: check for missing arguments.
my $outfile = shift;
my $inbuf;
my $outbuf;
sysopen(IN , $infile , O_RDONLY); binmode(IN );
sysopen(OUT, $outfile, O_WRONLY|O_CREAT); binmode(OUT);
my $key = "Fantazma1518061DgFgvFdvHyfvFdWwlm876Ql";
# I'm sure there must be a more clever way to do this in Perl
sub permutate {
my $offset = shift;
my $key = shift;
$offset = $offset % (length($key)-1);
my $beginning = substr($key, 0, $offset);
my $middle = substr($key, $offset, 1);
my $end = substr($key, $offset+1);
$key = $beginning . $end . $middle;
return $key;
}
#DEBUG
#for (my $i=0; $i < 60 ; $i++) {
# $key = permutate($i,$key);
# printf("%i\t%s\t%08x\n",$i,$key,Archive::Zip::computeCRC32($key,0));
#}
my $offset = 0;
while (0!=sysread(IN, $inbuf, 4)) {
$key = permutate($offset++,$key);
#DEBUG printf("%08x\t%08x\n",unpack("V",$inbuf),Archive::Zip::computeCRC32($key,0));
$outbuf = unpack("V",$inbuf) ^ Archive::Zip::computeCRC32($key,0);
syswrite(OUT, pack("V",$outbuf), 4);
}
# TODO: Deal with last one, two, or three bytes.
exit(0);
There is also a free tool written and distributed by Dr. Web: ftp://ftp.drweb.com/pub/drweb/tools/te37decrypt.exe
I can't endorse it, but I'm sure that it works better than my Perl script
above.
What about .encrypt files?
That's something completely different. That's GPCode
and I'm going to write something up on it soonish. If you suddenly found yourself in the situation where all of your documents are encrypted with .encrypt
at the end of the filename. Stop using the machine immediately, yank the plug if you have to. Your files still exist in the newly created empty space of the filesystem. You can undelete your files if you don't overwrite them first. If you catch this early enough, I think it is also possible to dump the memory from the system, and pull the encryption key out of it. (I haven't tested this yet.) If you don't know how to do a memory dump of your system, you're better off just pulling the plug and going the undelete route. Oh and if you are hit by this, and you do undelete the malware that did this, please send me a sample. <julia→fireeye.com>
You can stop reading now
This is all of the entropy that is really in vscrypt…
echo -n Fantazma1518061DgFgvFdvHyfvFdWwlm876Ql | stan
General statistics for the stream, bytes 38
Arithmetic mean: 86.473684 ~ 0x56(V)
Median: 97.000000 ~ 0x61(a)
Deviation: 25.538142
Chi-Square test: 464.269556
Entropy per byte: 4.346226
Correlation co.: 0.442653
Pattern length 1, different 23, total 38, bytes 38, depth 6
- Pattern range
0x30(0): 0x00000001 - 0x7a(z): 0x00000001
- 10 most used patterns
0x46(F): 0x00000004 0x31(1): 0x00000003 0x61(a): 0x00000003
0x76(v): 0x00000003 0x38(8): 0x00000002 0x36(6): 0x00000002
0x67(g): 0x00000002 0x64(d): 0x00000002 0x6d(m): 0x00000002
0x6c(l): 0x00000002
Pattern length 2, different 35, total 37, bytes 38, depth 9
- Pattern range
0x3036(06): 0x00000001 - 0x7a6d(zm): 0x00000001
- 10 most used patterns
0x7646(vF): 0x00000002 0x4664(Fd): 0x00000002 0x4661(Fa): 0x00000001
0x3135(15): 0x00000001 0x3036(06): 0x00000001 0x3531(51): 0x00000001
0x3138(18): 0x00000001 0x3144(1D): 0x00000001 0x3830(80): 0x00000001
0x3631(61): 0x00000001
And these are the stats for the whole generated keystream. Notice that 0xf3d26e79 and 0x0f5d95b6 appear twice in the stream. See the stuff above about the letter v
.
stan -p 4 vscrypt_keystream.bin
General statistics for the stream, bytes 4440
Arithmetic mean: 128.209685
Median: 129.000000
Deviation: 73.703444 ~ 0x49(I)
Chi-Square test: 276.303657
Entropy per byte: 7.953987
Correlation co.: -0.029830
Pattern length 1, different 256, total 4440, bytes 4440, depth 16
- Pattern range
0x00( ): 0x00000019 - 0xff( ): 0x00000013
- 10 most used patterns
0x2f(/): 0x0000001e 0x10( ): 0x0000001d 0x48(H): 0x0000001d
0x1b( ): 0x0000001b 0x12( ): 0x0000001b 0xe0( ): 0x0000001b
0xfb( ): 0x0000001a 0x00( ): 0x00000019 0x94( ): 0x00000019
0x60(`): 0x00000019
Pattern length 2, different 4285, total 4439, bytes 4440, depth 28
- Pattern range
0x0005( ): 0x00000001 - 0xfff6( ): 0x00000001
- 10 most used patterns
0x00cd( ): 0x00000003 0x948b( ): 0x00000003 0x20bb( ): 0x00000002
0x0be4( ): 0x00000002 0x0921( !): 0x00000002 0x04f8( ): 0x00000002
0x01c2( ): 0x00000002 0x08a7( ): 0x00000002 0x07f0( ): 0x00000002
0x1dc9( ): 0x00000002
Pattern length 3, different 4432, total 4438, bytes 4440, depth 31
- Pattern range
0x000592( ): 0x00000001 - 0xfff609( ): 0x00000001
- 10 most used patterns
0xf3d26e( n): 0x00000002 0xd26e79( ny): 0x00000002
0x4d7fc2(M ): 0x00000002 0x00cdc5( ): 0x00000002
0x0f5d95( ] ): 0x00000002 0x5d95b6(] ): 0x00000002
0x00c1b1( ): 0x00000001 0x006fe1( o ): 0x00000001
0x001b3e( >): 0x00000001 0x000592( ): 0x00000001
Pattern length 4, different 4435, total 4437, bytes 4440, depth 30
- Pattern range
0x0005921a( ): 0x00000001 - 0xfff60921( !): 0x00000001
- 10 most used patterns
0xf3d26e79( ny): 0x00000002 0x0f5d95b6( ] ): 0x00000002
0x0b1dc968( h): 0x00000001 0x094b425f( KB_): 0x00000001
0x01603ee9( `> ): 0x00000001 0x00cdece4( ): 0x00000001
0x00c1b1f0( ): 0x00000001 0x006fe10f( o ): 0x00000001
0x001b3e2b( >+): 0x00000001 0x0005921a( ): 0x00000001
Julia Wolf @ FireEye Malware Intelligence Lab
Questions/Comments to research [@] fireeye [.] com