{
	"id": "2a089ee7-4fdf-465a-baff-d71088ebb38c",
	"created_at": "2026-04-06T00:08:24.548733Z",
	"updated_at": "2026-04-10T03:37:32.584578Z",
	"deleted_at": null,
	"sha1_hash": "7e89d3ec711dfc7587eeb37048134e3d224bd8c3",
	"title": "Genetic Analysis of CryptoWall Ransomware",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 4919540,
	"plain_text": "Genetic Analysis of CryptoWall Ransomware\r\nBy Ryan Cornateanu\r\nPublished: 2020-11-24 · Archived: 2026-04-05 19:43:50 UTC\r\nA strain of a Crowti ransomware emerged, the variant known as CryptoWall, was spotted by researchers in early\r\n2013. Ransomware by nature is extraordinarily destructive but this one in particular was a bit beyond that. Over\r\nthe next 2 years, with over 5.25 billion files encrypted and 1 million+ systems infected, this virus has definitely\r\nmade its mark in the pool of cyber weapons. Below you can find a list of the top ten infected countries:\r\nSource: Dell Secure Works\r\nCryptoWall is distinct in that its campaign ID initially gets sent back to their C2 servers for verification purposes.\r\nThe motivation behind these ID’s are to track samples by the loader vectors. The one we will be analyzing in our\r\nlaboratory experiment has the crypt1 ID that was first seen around February 26th, 2014. The infection vector is\r\nstill unknown today but we will be showing how to unpack the loader, and extract the main ransomware file.\r\nSome of the contagions have been caused by Drive-by downloads, Cutwail/Upatre, Infinity/Goon exploit kit,\r\nMagnitude exploit kit, Nuclear exploit kit/Pony Loader, and Gozi/Neverquest.\r\nInitial Analysis\r\nWe will start by providing the hash of the packed loader file:\r\n➜ CryptoWall git:(master) openssl md5 cryptowall.bin\r\nMD5(cryptowall.bin)= 47363b94cee907e2b8926c1be61150c7\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 1 of 27\n\nRunning the file command on the bin executable, we can confirm that this is a PE32 executable (GUI) Intel\r\n80386, for MS Windows . Similar to the analysis we did on the Cozy Bear’s Beacon Loader, we will be using IDA\r\nPro as our flavor of disassembler tools.\r\nLoading the packed executable into our control flow graph view, it becomes apparent fairly quickly that this is\r\npacked loader code, and the real CryptoWall code is hiding somewhere within.\r\nPress enter or click to view image in full size\r\nWinMain CFG View\r\nChecking the resource section of this binary only shows that it has two valid entries; the first one being a size of\r\n91,740 bytes. Maybe we will get lucky and the hidden PE will be here?\r\nPress enter or click to view image in full size\r\nDumped resource section\r\nUnfortunately not! This looks like some custom base64 encoded data that will hopefully get used later somewhere\r\ndown the line in our dissection of the virus. If we scroll down to the end of WinMain() you’ll notice a jump\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 2 of 27\n\ninstruction that points to EAX . It will look something like this in the decompiler view:\r\nJUMPOUT(eax=decrypted_code_segment);\r\nUnpacking Binary Loaders\r\nAt this point, we have to open up a debugger, and view this area of code as it is being resolved dynamically. What\r\nyou will want to do is a set a breakpoint at 0x00402dda , which is the location of the jmp instruction. Once you\r\nhit this breakpoint after continuing execution, you’ll notice EAX now points to a new segment of code. Dumping\r\nEAX in the disassembler will lead you to the 2nd stage loader. Use the debugger’s step into feature, and our\r\ninstruction pointer should be safely inside the decrypted loader area.\r\nPress enter or click to view image in full size\r\n2nd Stage\r\nLet’s go over what is happening at this stage of the malware. EBP+var_EA6E gets loaded effectively into EDX ,\r\nEAX then holds the index count incrementer to follow the next few bytes at data address 302C9AEh .\r\n.data:0302CA46 mov bl, byte ptr (loc_302C9AE - 302C9AEh)[eax]\r\n.data:0302CA48 add ebx, esi\r\n.data:0302CA4A mov [edx], bl\r\nAll this snippet of code is doing is loading bytes from the address mentioned above and storing it at bl (the\r\nlower 8 bits of EBX ). The byte from bl is then moved into the pointer value of EDX . At the end of this routine\r\nEBP+var_EA6E will hold a valid address that gets called as EAX (we can see the line highlighted in red in the\r\nimage above). Stepping into EAX will now bring us to the third stage of the loading process.\r\nA lot is going on at this point; this function has a couple thousand lines of assembly to go over, so at this point it’s\r\nbetter we open the decompiler view to see what is happening. After resolving some of the strings on the stack,\r\nthere is some key information that starts to pop up on the resource section we viewed earlier.\r\npLockRsrc = GetProcAddress(kernel32, \u0026LockResource);\r\npSizeofResource = GetProcAddress(kernel32, \u0026SizeofResource);\r\npLoadResource = GetProcAddress(kernel32, \u0026LoadResource);\r\npGetModuleHandle = GetProcAddress(kernel32, \u0026GetModuleHandleA);\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 3 of 27\n\npFindRsrc = GetProcAddress(kernel32, \u0026FindResourceA);\r\npVirtualAlloc = GetProcAddress(kernel32, \u0026VirtualAlloc);\r\nThe malware is loading all functions dynamically that have to do with our resource section. After the data gets\r\nloaded into memory, CryptoWall begins its custom base64 decoding technique and then continues to a decryption\r\nmethod as seen below.\r\nPress enter or click to view image in full size\r\nMost of what is happening here can be explained in a decryptor I wrote that resolves the shellcode from the\r\nresource section. If you head over to the python script, you’ll notice the custom base64 decoder is fairly simple. It\r\nwill use a hardcoded charset, and check to see if any of the bytes from the resource section match a byte from the\r\ncharset; if it is a match, it breaks from the loop. The next character gets subtracted by one and compared to a value\r\nof zero, if greater, it will take that value and modulate by 256 ; that byte will then get stored in a buffer array. It\r\nwill perform this in a loop 89,268 times, as that is the size of the encoded string inside the resource section.\r\nSecondary to this, another decryption process starts on our recently decoded data from the algorithm above.\r\nLooking at the python script again, we can see that hardcoded XOR keys were extracted in the debugger if you set\r\na breakpoint inside the decryption loop. All that is happening here is each byte is getting decrypted by a rotating\r\nthree byte key. Once the loop is finished, the code will return the address of the decrypted contents, which\r\nessentially just contains an address to another subroutine:\r\nloop:\r\n buffer = *(base_addr + idx) - (*n ^ (\u0026addr + 0xFFE6DF5F + idx));\r\n *(base_addr + idx++) = buffer;\r\n…\r\nFourth_Stage_Loader = base_addr;\r\nreturn (\u0026Fourth_Stage_Loader)(buffer, b64_decoded_str, a1);\r\nThe base_addr transfers data to another variable that we named Fourth_Stage_Loader which holds the address\r\nof the newest function, and can be used as a caller. If we dump the address at call dword ptr gs:(loc_1920A1–\r\n1920A1h)[eax] into memory, you’ll see bytes that start with a generic x86 function prologue like 55 8b ec 81 .\r\nDump this to a file, and we can actually emulate this shellcode. In doing so, we don’t have to step through all this\r\ncode in the debugger; instead it will hopefully tell us how to unpack and get to the main CryptoWall file.\r\nSide note: the python script I wrote will automatically decode \u0026 decrypt the resource section, and dump it to a bin\r\nfile by running =\u003e python decrypt_shellcode_loader.py -e .\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 4 of 27\n\n0x1000: push ebp\r\n0x1001: mov ebp, esp\r\n0x1003: add esp, 0xfffff004\r\n....\r\nAn easy way to see what this next stage in the malware’s loader is doing is by using one of my favorite shellcode\r\nemulator tools called ScDbg. By using this tool, we can figure out exactly where we need to set our breakpoints in\r\norder to get to the main ransomware file. We are going to look for calls such as VirtualAlloc ,\r\nWriteProcessMemory , CreateProcessA , etc.\r\nC:\\\u003e scdbg.exe /s 3200000 /bp WriteProcessMemory /f dump.binLoaded 10587 bytes from file extractions/\r\nBreakpoint 0 set at 7c802213\r\nInitialization Complete..\r\nMax Steps: 3200000\r\nUsing base offset: 0x4010004011cf GetProcAddress(LoadLibraryA)\r\n40165f GetProcAddress(VirtualAlloc)\r\n401c46 GetProcAddress(GetCurrentProcessId)\r\n401c52 GetCurrentProcessId() = 29\r\n...\r\n401d46 CloseHandle(18be)\r\n401f40 VirtualAlloc(base=0 , sz=20400) = 600000\r\n4021e1 VirtualAllocEx(pid=1269, base=400000 , sz=25000) = 621000\r\n /* Breakpoint 0 hit at: 7c802213 */\r\n4021fe WriteProcessMemory(pid=1269, base=400000 , buf=600000, sz=400, written=12fd70)\r\n /* Breakpoint 0 hit at: 7c802213 */\r\n40224e WriteProcessMemory(pid=1269, base=401000 , buf=600400, sz=16400, written=12fd70)\r\nInteresting… it looks like the malware is allocating memory to its own process by using GetCurrentProcessId()\r\nand allocating a large enough space to inject a PE file into itself. After memory allocation, CryptoWall injects the\r\npayload file twice, once for the header, and the second time for the rest of the file. If you set a breakpoint at\r\nWriteProcessMemory , and continue execution twice, you can dump the second argument ( ECX ) on the stack to\r\nsee the hidden PE file.\r\nPress enter or click to view image in full size\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 5 of 27\n\nThere is an Anti-VM trick along the way in the 3rd stage part of the loader process that needs to be patched in\r\norder to hit the injection process, so I wrote an x32Dbg python plugin to help automate the patching and dumping\r\noperation.\r\nReversing the Main Crypto Binary\r\nCryptoWall’s entry point starts off by dynamically resolving all imports to obtain all of NTDLL’s offsets by using\r\nthe process environment block.\r\nPress enter or click to view image in full size\r\nIt will then call a subroutine that is responsible for using the base address of the loaded DLL and uses many\r\nhardcoded DWORD addresses to locate hundreds of functions.\r\nSide Note: If you would like to make your life a whole lot easier with resolving the function names in each\r\nsubroutine, I made a local type definition for IDA Pro over here. The resolving import function table will look a\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 6 of 27\n\nlot cleaner than what you see above:\r\nPress enter or click to view image in full size\r\nAfter the function returns, the malware will proceed to generate a unique hash based on your system information,\r\nthe resulting string will be MD5 hashed =\u003e DESKTOP-QR18J6QB0CBF8E8Intel64 Family 6 Model 70 Stepping 1,\r\nGenuineIntel . After computing the hash, it will setup a handle to an existing named event object with the\r\nspecified desired access that will be called as \\\\BaseNamedObjects\\\\C6B359277232C8E248AFD89C98E96D65 .\r\nThe main engine of the code starts a few routines after the malware checks for system information, events, anti-vm, and running processes.\r\nPress enter or click to view image in full size\r\nMost of the time the ransomware will successfully inject its main thread into svchost and not explorer ; so\r\nlet’s follow that trail. Since this is a 32-bit binary its going to attempt to find svchost.exe inside of SysWOW64\r\ninstead of System32 . After successfully locating the full path, it will create a new thread using the\r\nRtlCreateUserThread() API call. Once the thread is created, NtResumeThread() will be used on the process to\r\nstart the ransomware_thread code. Debugging these types of threads can be a little convoluted, and setting\r\nbreakpoints doesn’t always work.\r\n.text:00416F40 ransomware_thread proc near\r\n.text:00416F40 start+86↓o\r\n.text:00416F40\r\n.text:00416F40 var_14 = dword ptr -14h\r\n.text:00416F40 var_10 = dword ptr -10h\r\n.text:00416F40 var_C = dword ptr -0Ch\r\n.text:00416F40 var_8 = dword ptr -8\r\n.text:00416F40 var_4 = dword ptr -4\r\n.text:00416F40\r\n.text:00416F40 000 push ebp\r\n.text:00416F41 004 mov ebp, esp\r\n.text:00416F43 004 sub esp, 14h\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 7 of 27\n\n.text:00416F46 018 call ResolveImportsFromDLL\r\n...\r\nUsing x32Dbg, you can set the EIP to address 0x00416F40 since this thread is not resource dependent on any of\r\nthe other code that has been executed up until this point; this thread even utilizes the ResolveImportsFromDLL\r\nfunction we saw in the beginning of the program’s entry point… meaning, the forced instruction pointer jump will\r\nnot damage the integrity of the ransomware.\r\nisHandleSet = SetSecurityHandle();\r\nif ( isHandleSet \u0026\u0026 SetupC2String() )\r\n{\r\n v8 = 0;\r\n v6 = 0;\r\n IsSuccess = WhichProcessToInject(\u0026v8, \u0026v6);\r\n if ( IsSuccess )\r\n {\r\n IsSuccess = StartThreadFromProcess(-1, InjectedThread,\r\n 0, 0, 0);\r\n FreeVirtualMemory(v8);\r\n }\r\n}\r\nThe thread will go through a series of configurations that involve setting up security attributes, MD5 hashing the\r\nhostname of the infected system, and then searching to either inject new code into svchost or explorer . In\r\norder to start a new thread, the function WhichProcessToInject will query the registry path, and check\r\npermissions on what key values the malware has access to. Once chosen, the InjectedThread process will\r\nresume. Stepping into that thread, we can see the module size is fairly small.\r\n.text:00412E80 InjectedThread proc near ; DATA\r\n.text:00412E80\r\n.text:00412E80 000 push ebp\r\n.text:00412E81 004 mov ebp, esp\r\n.text:00412E83 004 call MainInjectedThread\r\n.text:00412E88 004 push 0\r\n.text:00412E8A 008 call ReturnFunctionName\r\n.text:00412E8F 008 mov eax, [eax+0A4h]\r\n.text:00412E95 008 call eax\r\n.text:00412E97 004 xor eax, eax\r\n.text:00412E99 004 pop ebp\r\n.text:00412E9A 000 retn\r\n.text:00412E9A InjectedThread endp\r\nAt address 0x00412E83 , a subroutine gets called that will bring the malware to start the next series of functions\r\nthat involves the C2 server configuration callback, and the encryption of files. After the thread is finished\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 8 of 27\n\nexecuting, EAX resolves a function at offset +0x0A4 which will show RtlExitUserThread being invoked. Once\r\nwe enter MainInjectedThread , you’ll notice the first function at 0x004011B40 is giving us the first clue of how\r\nthe files will be encrypted.\r\n.text:00411D06 06C push 0F0000000h\r\n.text:00411D0B 070 push 1\r\n.text:00411D0D 074 lea edx, [ebp+reg_crypt_path]\r\n.text:00411D10 074 push edx\r\n.text:00411D11 078 push 0\r\n.text:00411D13 07C lea eax, [ebp+var_8]\r\n.text:00411D16 07C push eax\r\n.text:00411D17 080 call ReturnFunctionName\r\n.text:00411D1C 080 mov ecx, [eax+240h]\r\n.text:00411D22 080 call ecx ; CryptAcquireContext\r\nCryptAcquireContext is used to acquire a handle to a particular key container within a particular cryptographic\r\nservice provider (CSP). In our case, the CSP being used is Microsoft\\Enhanced\\Cryptographic\\Provider\\V1 ,\r\nwhich coincides with algorithms such as DES, HMAC, MD5, and RSA.\r\nPress enter or click to view image in full size\r\nOnce the CryptoContext is populated, the ransomware will use the MD5 hash created to label the victim’s\r\nsystem information and register it as a key path as such → software\\\\C6B359277232C8E248AFD89C98E96D65 . The\r\nransom note is processed by a few steps. The first step is to generate the TOR addresses which end up resolving\r\nfour addresses: http[:]//torforall[.]com , http[:]//torman2[.]com , http[:]//torwoman[.]com , and\r\nhttp[:]//torroadsters[.]com . These DNS records will be used later on to inject into the ransomware HTML\r\nfile. Next, the note gets produced by the use of the Win32 API function, RtlDecompressBuffer , to decompress\r\nthe data using COMPRESSION_FORMAT_LZNT1 . The compressed ransom note can be found in the .data section and\r\nconsists of 0x52B8 bytes.\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 9 of 27\n\nDecompressing the note is kind of a mess in python as there is no built in function that is able to do LZNT1\r\ndecompression. You can find the actual call at address 0x004087F3 .\r\n.text:004087CF 024 lea ecx, [ebp+var_8]\r\n.text:004087D2 024 push ecx\r\n.text:004087D3 028 mov edx, [ebp+arg_4]\r\n.text:004087D6 028 push edx\r\n.text:004087D7 02C mov eax, [ebp+arg_6]\r\n.text:004087DA 02C push eax\r\n.text:004087DB 030 mov ecx, [ebp+var_18]\r\n.text:004087DE 030 push ecx\r\n.text:004087DF 034 mov edx, [ebp+var_C]\r\n.text:004087E2 034 push edx\r\n.text:004087E3 038 movzx eax, [ebp+var_12]\r\n.text:004087E7 038 push eax\r\n.text:004087E8 03C call ReturnFunctionName\r\n.text:004087ED 03C mov ecx, [eax+178h]\r\n.text:004087F3 03C call ecx\r\n// Decompiled below\r\n(*(RtlDecompressBuffer))(COMPRESSION_FORMAT_LZNT1,\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 10 of 27\n\nuncompressed_buffer,\r\n UncompressedBufferSize,\r\n CompressedBuffer,\r\n CompressedBufferSize,\r\n FinalUncompressedSize) )\r\nAfter the function call, uncompressed_buffer will be a data filled pointer to a caller-allocated buffer (allocated\r\nfrom a paged or non-paged pool) that receives the decompressed data from CompressedBuffer. This parameter is\r\nrequired and cannot be NULL, which is why there is an NtAllocateVirtualMemory() call to this parameter\r\nbefore being passed to decompression. The script I wrote will grab the compressed data from the PE file, and run a\r\nLZNT1 decompression algorithm then place the buffer in an HTML file. The resulting note will appear on the\r\nvictims system as such:\r\nOnce the note is decompressed, the HTML fields will be populated with multiple TOR addresses at subroutine\r\nsub_00414160() . The note is stored in memory then follows a few more checks before the malware sends its first\r\nC2 POST request. Stepping into SendRequestToC2 which is located at 0x00416A50 , the first thing we notice is a\r\nbuffer being allocated 60 bytes of memory.\r\n.text:00416A77 018 push 3Ch\r\n.text:00416A79 01C call AllocateSetMemory\r\n.text:00416A7E 01C add esp, 4\r\n.text:00416A81 018 mov [ebp+campaign_str], eax\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 11 of 27\n\nAll this information will eventually help us write a proper fake C2 server that will allow us to communicate with\r\nthe ransomware since CryptoWall’s I2P servers are no longer active. Around address 0x004052E0 , which we\r\nlabeled EncryptData_SendToC2 will be responsible for taking our generated campaign string and sending it as an\r\ninitial ping.\r\nPress enter or click to view image in full size\r\nIf you set a breakpoint at this function, you can see what the parameter contains:\r\n{1|crypt1|C6B359277232C8E248AFD89C98E96D65} . Once inside this module, you'll notice three key functions; one\r\nresponsible for byte swapping, a key scheduling algorithm, and the other doing the actual encryption. The\r\ngenerated RC4 encryption will end up as a hash string:\r\n85b088216433863bdb490295d5bd997b35998c027ed600c24d05a55cea4cb3deafdf4161e6781d2cd9aa243f5c12a717cf649\r\nCommand \u0026 Control Communication\r\nThe malware sets itself up for a POST request to its I2P addresses that cycle between proxy1–1–1.i2p \u0026\r\nproxy2–2–2.i2p . The way this is done is by using the function at 0x0040B880 to generate a random seed based\r\non epoch time, and use that to create a string that ranges from 11 to 16 bytes. This PRNG (Pseudo-Random\r\nNumber Generator) string will be used as the POST request’s URI and as the key used in the byte swapping\r\nfunction before the RC4 encryption.\r\nPress enter or click to view image in full size\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 12 of 27\n\nTo give us an example, if our generated string results in tfuzxqh6wf7mng , then after the function call, that string\r\nwill turn into 67ffghmnqtuwxz . That string gets used for a 256-generated key scheduling algorithm, and the\r\nPOST request (I.E., http://proxy1–1–1.i2p/67ffghmnqtuwxz ). You can find the reverse engineered algorithm\r\nhere.\r\nPress enter or click to view image in full size\r\nThe next part will take this byte swapped key, then RC4 encrypt some campaign information that the malware has\r\ngathered, which unencrypted, will look like this:\r\n{1|crypt1|C6B359277232C8E248AFD89C98E96D65|0|2|1||55.59.84.254}\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 13 of 27\n\nThis blob consists of the campaign ID, an MD5 hashed unique computer identifier, a CUUID, and the victims\r\npublic IP address. After preparation of this campaign string, the ransomware will begin to resolve the two I2P\r\naddresses. Once CryptoWall sends its first ping to the C2 server, the malware expects back an RC4 encrypted\r\nstring, which will contain a public key used to encrypt all the files on disk. The malware has the ability to decrypt\r\nthis string using the same RC4 algorithm from earlier, and will parse the info from this block:\r\n{216|1pai7ycr7jxqkilp.onion|[pub_key]|US|[unique_id]} . The onion route is for the ransom note, and is a\r\npersonalized route that the victim can enter using a TOR browser. The site most likely contains further instructions\r\non how to pay the ransom.\r\nGet Ryan Cornateanu’s stories in your inbox\r\nJoin Medium for free to get updates from this writer.\r\nRemember me for faster sign in\r\nSince the C2 servers are no longer active; in order to actually know what our fake C2 server should send back to\r\nthe malware; the parser logic had to be carefully dissected which is located at 0x00405203 .\r\nPress enter or click to view image in full size\r\nIn this block, the malware decrypts the data it received from the C2 server. Once decrypted, it stores the first byte\r\nin ECX and compares hex value to 0x7B (char: ‘{‘ ). Tracing this function call to the return value, the string\r\nreturned back will remove brackets from start to end. At memory address 0x00404E69 , a DWORD pointer at\r\neax+2ch holds our newly decrypted and somewhat parsed string, that will be checked for a length greater than 0.\r\nIf the buffer holds weight, we move on over to the final processing of this string routine at 0x00404B00 , that I\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 14 of 27\n\ndubbed ParseC2Data() . This function takes four parameters, char* datain , int datain_size , char\r\n*dataout , int dataout_size . The first blob on datain data gets parsed from the first 0x7C (char: ‘|’ ) and\r\nextracts the victim id.\r\nvictim_id = GetXBytesFromC2Data(decrypted_block_data_from_c2, \u0026hex_7c, \u0026ptr_to_data_out);\r\nptr_to_data_out and EAX will now hold an ID number of 216 (we got that number since we placed it there in\r\nour fake C2). The next block of code will finish the rest of the data:\r\nwhile ( victim_id )\r\n{\r\n if ( CopyMemoryToAnotherLocation(\u0026some_buffer_to_copy_too,\r\n 8 * idx + 8) )\r\n {\r\n CopyBlocksofMemory(victim_id,\r\n \u0026some_buffer_to_copy_too[2 * idx + 1],\r\n \u0026some_buffer_to_copy_too[2 * idx]);\r\n ++idx;\r\n if ( ptr_to_data_out )\r\n {\r\n for ( i = 0; *(i + ptr_to_data_out) == 0x7C; ++i )\r\n {\r\n if (\r\n CopyMemoryToAnotherLocation(\u0026some_buffer_to_copy_too,\r\n 8 * idx + 8) )\r\n {\r\n ++v9;\r\n ++idx;\r\n }\r\n }\r\n }\r\n }\r\n victim_id = GetXBytesFromC2Data(0, \u0026hex_7c_0,\r\n \u0026ptr_to_data_out);\r\n ++v5;\r\n ++v9;\r\n}\r\nWhat’s happening here is that by every iteration of the character ‘|’ we grab the next chunk of data and place it\r\nin memory into some type structure. The data jumps X amount of times per loop until it reaches the last 0x7C\r\nbyte. It will loop a total of four times. After this function returns, dataout will contain a pointer in memory to\r\nthis local type, which we reversed to look like this:\r\nstruct _C2ResponseData\r\n{\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 15 of 27\n\nint victim_id;\r\n char *onion_route;\r\n const char* szPemPubKey;\r\n char country_code[2];\r\n char unique_id[4];\r\n};\r\nShortly after, there is a check to make sure the victim id generated is no greater than 0x3E8 or that it is not an\r\nunsigned value.\r\nvalue_of_index = CheckID(*(*parsed_data_out-\u003evictim_id));\r\nif ( value_of_index \u003e 0x3E8 || value_of_index == 0xFFFFFFFF )\r\n value_of_index = 0x78;\r\nI believe certain malware will often perform these checks throughout the parsing of the C2 response server to\r\nmake sure the data being fed back is authentic. Over at 0x00404F35 , there is another check to see how many\r\ntimes it tried to reach the command server. If the check reaches exactly 3 times then it will move to check if the\r\nonion route is valid; all CryptoWall variants hardcode the first string index with ascii ‘1’ . If it does not start\r\nwith this number, then it will try to reach back again for a different payload. The other anti-tamper check it makes\r\nfor the onion route is a CRC32 hash against the payload, if the compressed route does not equal 0x63680E35 , the\r\nmalware will try one last time to compare against the DWORD value of 0x30BBB749 . The variant has two\r\nhardcoded 256 byte arrays to which it compares the encrypted values against. Brute-forcing can take a long time\r\nbut is possible with a python script that I made here. The checksum is quite simple, it will take each letter of the\r\nsite string and logical-XOR against an unsigned value:\r\ntmp = ord(site[i])) ^ (ret_value \u0026 0xffffff)\r\nIt will take the tmp value and use it as an index in the hardcoded byte array to perform another logical-XOR\r\nagainst :\r\nret_value = bytes_array[tmp*4:(tmp*4)+4] ^ (0xFFFFFFFF \u003e\u003e 8)\r\nThe return value then gets inverted giving us a 4 byte hash to verify against. Now the malware moves on over to\r\nthe main thread responsible for encrypting the victims files at 0x00412988 . The first function call in this thread is\r\nfrom CryptAcquireContextW , and that will acquire a handle to a particular key container within a CSP. 16 bytes\r\nwill then be allocated to the stack using VirtualAlloc; which will be the buffer to the original key.\r\nisDecompressed = CreateTextForRansomwareNote(0, 0, 0);\r\nif ( !isRequestSuccess || !isDecompressed )\r\n{\r\n remaining_c2_data = 0;\r\n while ( 1 )\r\n {\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 16 of 27\n\nisRequestSuccess = SecondRequestToC2(\u0026rsa_key,\r\n \u0026rsa_key_size, \u0026remaining_c2_data);\r\n if ( isRequestSuccess )\r\n break;\r\n sleep(0x1388u);\r\n}\r\nOnce the text for the ransom note is decompressed, CryptoWall will place this note as an HTML, PNG, and TXT\r\nfile inside of every directory the virus went through to encrypt documents. After this point, it will go through\r\nanother round of requests to the I2P C2 servers to request another RSA 2048-bit public key. This key will be the\r\none used for encryption. This strain will do a number of particular hardcoded hash checks on the data it gets back\r\nfrom the C2.\r\nDecoding the Key\r\nCryptoWall will use basic Win32 Crypto functions like CryptStringToBinaryA , CryptDecodeObjectEx , \u0026\r\nCryptImportPublicKeyInfo to decode the RSA key returned. Then it will import the public key information into\r\nthe provider which then returns a handle of the public key. After importing is finished, all stored data will go into a\r\nlocal type structure like this:\r\nstruct _KeyData\r\n{\r\n char *key;\r\n int key_size;\r\n BYTE *hash_data_1;\r\n BYTE *hash_data_2;\r\n};// Gets used here at 0x00412B8C\r\nif ( ImportKey_And_EncryptKey(\r\n cryptContext,\r\n rsa_key,\r\n rsa_key_size,\r\n OriginalKey-\u003ekey,\r\n \u0026OriginalKey-\u003ekey_size,\r\n \u0026OriginalKey-\u003ehash_data_1,\r\n \u0026OriginalKey-\u003ehash_data_2) )\r\n{\r\nThe next actions the malware takes is pretty basic for ransomware.. it will loop through every available drive, and\r\nuse GetDriveTypeW to determine whether a disk drive is a removable, fixed, CD-ROM, RAM disk, or network\r\ndrive. In our case, the C drive is the only open drive which falls under the category of DRIVE_FIXED . CryptoWall\r\nwill only check if the drive is CD-ROM because it will not try to spread in that case.\r\n.text:00412C1B mov ecx, [ebp+driver_letter]\r\n.text:00412C1E push ecx\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 17 of 27\n\n.text:00412C1F call GetDriveTypeW\r\n.text:00412C2C cmp eax, 5\r\n.text:00412C2F jz skip_drive\r\nEAX holds the integer value returned from the function call which represents the type of drive associated with\r\nthat number (5 == DRIVE_CDROM). You can find the documentation here.\r\nThe exciting part is near as we are about to head over to where the malware duplicates the key it retrieved from\r\nour fake C2 server at address 0x00412C7A . What is happening here is pretty straight forward, and we can show in\r\npseudo-code:\r\nif (OriginalKey)\r\n DuplicatedKey = HeapAlloc(16)\r\n if (DuplicatedKey)\r\n CryptDuplicateKey(OriginalKey, 0, 0, DuplicatedKey)\r\n memcpy(DuplicatedKey, OriginalKey, OrignalKey_size)\r\n CryptDestroyKey(OriginalKey)\r\nEssentially CryptDuplicateKey is making an exact copy of a key and the state of the key. The DuplicatedKey\r\nvariable ends up becoming a struct as we can see after the function call at 0x00412C7A , it gets used to store\r\nvolume information about the drive its currently infecting.\r\nGetVolumeInformation(driver_letter, DuplicatedKey + 20);\r\nif ( MoveDriverLetterToDupKeyStruct(driver_letter,\r\n (DuplicatedKey + 16), 0) {\r\n ...\r\nThat is why 24 bytes was used to allocate to the heap when creating this variable instead of 16. Now we can\r\ndefine our struct from what we know so far:\r\nstruct _DupKey\r\n{\r\n const char *key;\r\n int key_size;\r\n DWORD unknown1;\r\n DWORD unknown2;\r\n char *drive_letter;\r\n LPDWORD lpVolumeSerialNumber;\r\n DWORD unknown3;\r\n};// Now our code looks cleaner from above\r\nGetVolumeInformation(driver_letter,\r\n \u0026DuplicatedKey-\u003elpVolumeSerialNumber);\r\nif ( MoveDriverLetterToDupKeyStruct(driver_letter,\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 18 of 27\n\n\u0026DuplicatedKey-\u003edrive_letter, 0) {\r\n ...\r\nEncrypting of Files\r\nAfter the malware is finished storing all pertinent information regarding how and where it will do its encryption,\r\nCryptoWall moves forward to the main encryption loop at 0x00416780 .\r\nEncryption Loop Control Flow Graph\r\nAs we can see, the control flow graph is fairly long in this subroutine, but nothing out of the ordinary when it\r\ncomes to ransomware. A lot has to be done before encrypting files. At the start of this function, we see an\r\nimmediate call to HeapAlloc to allocate 260 bytes of memory. We can automatically assume this will be used to\r\nstore the file’s absolute path, as Windows OS only allows a max of 260 bytes. Upon success, there is also an\r\nallocation of virtual memory with a size of 592 bytes that will later be used as the file buffer contents. Then the\r\nAPI call FindFirstFileW uses this newly allocated buffer to store the first filename found on system. The\r\npseudo-code below will explain the flow:\r\nlpFileName = Allocate260BlockOfMemory(); // HeapAlloc\r\nif ( lpFileName )\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 19 of 27\n\n{\r\n (*(wcscpy + 292))(lpFileName, driver_letter);\r\n ...\r\n lpFindFileData = AllocateSetMemory(592); // VirtualAlloc\r\n if ( lpFindFileData )\r\n {\r\n hFile = (*(FindFirstFileW + 504))(lpFileName, lpFindFileData);\r\n if ( hFile != 0xFFFFFFFF )\r\n {\r\n v29 = 0;\r\n do\r\n {\r\n // Continue down to further file actions\r\nBefore the malware opens up the first victim file, it needs to make sure the file and file extension themselves are\r\nnot part of their hardcoded blacklist of bytes. It does this check using a simple CRC-32 hash check. It will take the\r\nfilename, and extension; compress it down to a DWORD, then compare that DWORD to a list of bytes that live in\r\nthe .data section.\r\nPress enter or click to view image in full size\r\nTo see how the algorithm works, I reversed it to python code, and wrote my own file checker.\r\n➜ python tor_site_checksum_finder.py --check-file-ext \"dll\"\r\n[!] Searching PE sections for compressed .data\r\n[!] Searching PE sections for compressed extension .data\r\n[-] '.dll' is not a valid file extension for Cryptowall\r\n➜ python tor_site_checksum_finder.py --check-file-ext \"py\"\r\n[!] Searching PE sections for compressed .data\r\n[!] Searching PE sections for compressed extension .data\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 20 of 27\n\n[+] '.py' is a valid file extension for Cryptowall\r\nNow we can easily tell what type of files CryptoWall will attack. Obvious extensions like .dll , .exe , and\r\n.sys is a very common file type for ransomware to avoid.\r\nPress enter or click to view image in full size\r\nIf the file passes these two checks, then it moves on over to the last part of the equation; the actual encryption\r\nlocated at 0x00412260 . We can skip the first few function calls as they are not pertinent to what is about to\r\nhappen. If you take a look at address 0x00412358 , there is a subroutine that takes in three parameters; a file\r\nhandle, our DuplicateKeyStruct, and a file size. Stepping into the function, we can immediately tell what is\r\nhappening:\r\nif(ReadFileA(hFile, lpBuffer,\r\n DuplicateKeyStruct-\u003efile_hash_size,\r\n \u0026lpNumberOfBytesRead, 0) \u0026\u0026 lpNumberOfBytesRead) ==\r\n DuplicateKeyStruct-\u003efile_hash_size\r\n{\r\n if(memcmp(lpBuffer, DuplicateKeyStruct-\u003efile_hash,\r\n DuplicateKeyStruct-\u003efile_hash_size))\r\n {\r\n isCompare = 1;\r\n }\r\n}\r\nThe pseudo-code is telling us that if an MD5 hash of the file is present in the header, then its already been\r\nencrypted. If this function returns isCompared to be true, then CryptoWall moves on to another file and will\r\nleave this one alone. If it returns false from the Compare16ByteHeader() function call, the malware will append to\r\nthe file’s extension by using a simple algorithm to generate a three lettered string to place at the end. The\r\ngeneration takes a timestamp, uses it as a seed, and takes that seed to then mod the first three bytes by 26 then\r\nadded to 97.\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 21 of 27\n\n*(v8 + 2 * i) = DataSizeBasedOnSeed(0, 0x3E8u) % 26 + 97;\r\nThis is essentially a rotation cipher, where you have a numerical variable checked by a modulate to ensure it\r\ndoesn’t go past alphanumeric values, then the addition to 97 rotates the ordinal 45 times. As an example, if we\r\nhave the letter ‘A’ , then after this cipher, it ends up becoming an ’n’ . In conclusion, if the victim file is named\r\nhello.py , this subroutine will rename it to hello.py.3xy .\r\nNext, around address 0x004123F0 , the generation of an AES-256 key begins with another call to Win32’s\r\nCryptAcquireContextW . The phProv handler gets passed over to be used in CryptGenKey and\r\nCryptGetKeyParam .\r\nif ( CryptGenKey(hProv, 0x6610, 1, \u0026hKey) ):\r\n pbData_1 = 0;\r\n pdwDataLen_1 = 4;\r\n if ( CryptGetKeyParam(hKey, 8, \u0026pbData_1, \u0026pdwDataLen_1, 0, 4)\r\nThe hexadecimal value of 0x6610 shown above tells us that the generated key is going to be AES-256 as seen in\r\nMS-DOCS. Once the hKey address to which the function copies the handle of the newly generated key is\r\npopulated, CryptGetKeyParam will be used to make the key and transfer it into pbData ; a pointer to a buffer that\r\nreceives the data. One last call in this function we labeled as GenerateAESKey() gets called which is\r\nCryptExportKey . This will take the handle to the key to be exported and pass it the function, and the function\r\nreturns a key BLOB. The second parameter of the GenerateAESKey() will hold the aes_key .\r\nPress enter or click to view image in full size\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 22 of 27\n\nThe next call is one of the most important ones to understand how eventually we can decrypt the files that\r\nCryptoWall infected. EncryptAESKey() uses the pointer to DuplicateKeyStruct-\u003ersa_key to encrypt our AES\r\nkey into a 256 byte blob. Exploring inside this function call is fairly simple; it uses CryptDuplicateKey and\r\nCryptEncrypt to take our public RSA 2048-bit key from earlier, our newly generated AES key to duplicate both\r\nkeys to save for later, and encrypt the buffer. The fifth parameter is our data out in this case and once the function\r\nreturns, what we labeled as encrypted_AESkey_buffer will hold our RSA encrypted key.\r\nAt around address 004124A5 , you will see two calls to WriteFileA . The first call will move the 16 byte MD5\r\nhash at the top of the victim file, and the second call will write out the 256 bytes of encrypted key buffer right\r\nbelow the hash.\r\nPress enter or click to view image in full size\r\nScreenshot shows 128 byte encrypted key buffer, but it was a copy mistake; Supposed to be 256\r\nbytes of encrypted key text.\r\nThe picture above shows what an example file will look like up until this stage of the infection. The plaintext is\r\nstill intact, but the headers now hold the hash of the file and the encrypted AES key used to encrypt the plaintext\r\nin the next phase. ReadFileA will shortly get called at 0x0041261B , which will read out everything after the\r\nheader of the file to start the encryption process.\r\nPress enter or click to view image in full size\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 23 of 27\n\nNow that 272 bytes belong to the header, anything after that we can assume is free range for the next function to\r\ndeal with. We don’t really need to deep dive too much into what DuplicateAESKey_And_Encrypt() does as it is\r\npretty self explanatory. The file contents are encrypted using the already generated AES key from above that was\r\npassed into the HCRYPTKEY *hKey variable. The sixth parameter of this function is the pointer which will contain\r\nthe encrypted buffer. At this point the ransomware will replace the plaintext with an encrypted blob, and the AES\r\nkey is free’d from memory.\r\nPress enter or click to view image in full size\r\nExample of a fully encrypted file\r\nAfter the file is finished being processed, the loop will continue until every allow listed file type on disk is\r\nencrypted.\r\nDecrypting Victim Files\r\nUnfortunately in this case, it is only possible to write a decryption algorithm if you know the private key used\r\nwhich is generated on the C2 side. This is going to be a two step process as in order to decrypt the file contents,\r\nwe need to decrypt the AES key that has been RSA encrypted.\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 24 of 27\n\nThe fake C2 server I wrote also includes an area where a private key is generated at the same time that the public\r\nkey is generated. So in my case, all encrypted files on my VM are able to be decrypted.\r\nSide Note: In order to run this C2 server, you have to place the malware’s hardcoded I2P addresses in\r\n/etc/hosts on Windows. Then make sure the server has started before executing the malware as there will be a\r\nlot of initial verification going back and forth between the malware and ‘C2’ to ensure its legitimacy. Your file\r\nshould look like this:\r\n127.0.0.1 proxy1-1-1.i2p\r\n127.0.0.1 proxy2-2-2.i2p\r\nAnother reason why we un the fake C2 server before executing the malware is so we don’t end up in some dead\r\nlock state. The output from our server will look something like this:\r\nC:\\CryptoWall\\\u003e python.exe fake_c2_i2p_server.py\r\n* Serving Flask app \"fake_c2_server\" (lazy loading)\r\n127.0.0.1 - - [31/Mar/2020 15:10:06] \"�[33mGET / HTTP/1.1�[0m\" 404 -\r\nData Received from CryptoWall Binary:\r\n------------------------------\r\n[!] Found URI Header: 93n14chwb3qpm\r\n[+] Created key from URI: 13349bchmnpqw\r\n[!] Found ciphertext: ff977e974ca21f20a160ebb12bd99bd616d3690c3f4358e2b8168f54929728a189c8797bfa12cfa\r\n[+] Recovered plaintext: b'{1|crypt1|C6B359277232C8E248AFD89C98E96D65|0|2|1||55.59.84.254}'\r\n[+] Sending encrypted data blob back to cryptowall process\r\n127.0.0.1 - - [31/Mar/2020 15:11:52] \"�[37mPOST /93n14chwb3qpm HTTP/1.1�[0m\" 200\r\nStep by step, the first thing we have to do is write a program that imports the private key file. I used C++ for this\r\nportion because for the life of me I could not figure out how to mimic the CryptDecodeObjectEx API call that\r\ndecodes the key in a X509_ASN_ENCODING and PKCS_7_ASN_ENCODING format. Once you have the key blob from\r\nthis function, we can use this function as the malware does and call CryptImportKey , but this time it is a private\r\nkey and not a public key ;). Since the first 16 bytes of the victim file contains the MD5 hash of the unencrypted\r\nfile, we know we can skip that part and focus on the 256 bytes after that part of the header. The block size is\r\ngoing be 256 bytes and AES offset will be 272 , since that will be the last byte needed in the cryptographic\r\nequation. Once we get the blob, it is now okay to call CryptDecrypt and print out the 32 byte key blob:\r\nif (!CryptDecrypt(hKey, NULL, FALSE, 0, keyBuffer, \u0026bytesRead))\r\n{\r\n printf(\"[-] CryptDecrypt failed with error 0x%.8X\\n\",\r\n GetLastError());\r\n return FALSE;\r\n} printf(\"[+] Decrypted AES Key =\u003e \");\r\nfor(int i = 0; i \u003c bytesRead; i++)\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 25 of 27\n\n{\r\n printf(\"%02x\", keyBuffer[i]);\r\n}\r\nYou can find the whole script here. Now that we are half way there and we have an AES key, the last thing to do is\r\nwrite a simple python script that will take that key / encrypted file and decrypt all remaining contents of it after the\r\n272nd byte.\r\nenc_data_remainder = file_data[272:]\r\ncipher = AES.new(aes_key, AES.MODE_ECB)\r\nplaintext = cipher.decrypt(enc_data_remainder)\r\nThe script to perform this action is in the same folder on Github. If you want to see how the whole thing looks\r\nfrom start to finish, it will go like this:\r\n➜ decrypt_aes_key.exe priv_key_1.pem loveme.txt\r\n[+] Initialized crypto provider\r\n[+] Successfully imported private key from PEM file\r\n[!] Extracted encrypted AES keys from file\r\n[+] Decrypted AES Key =\u003e 08020000106600002000000040b4247954af27637ce4f7fabfe1ccfc6cd55fc724caa840f828\r\n[+] Successfully decrypted key from file\r\n➜ python decrypt_file.py loveme.txt 40b4247954af27637ce4f7fabfe1ccfc6cd55fc724caa840f82848ea4800b32\r\n[+] Decrypting file\r\n[+] Found hash header =\u003e e91049c35401f2b4a1a131bd992df7a6\r\n[+] Plaintext from file: b'\"hello world\" \\r\\n\\'\r\nConclusion\r\nOverall this was one of the biggest leading cyber threats back in 2013, and the threat actors behind this malicious\r\nvirus have shown their years of experience when it comes to engineering a ransomware such as this.\r\nAlthough this ransomware is over 6 years old, it still fascinated me so much to reverse engineer this virus that I\r\nwanted to share all the tooling I have wrote for it. Every step of the way their was another challenge to overcome,\r\nwhether it was knowing what the malware expected the encrypted payload to look like coming back from the C2,\r\nfiguring out how to decrypt their C2 I2P servers using RC4, decompressing the ransomware note using some hard\r\nto mimic LZNT1 algorithm, or even understanding their obscure way of generating domain URI paths… it was all\r\naround a gigantic puzzle for a completionist engineer like myself.\r\nHere is the repository that contains all the programs I wrote that helped me research CryptoWall.\r\nThank you for following along! I hope you enjoyed it as much as I did. If you have any questions on this article or\r\nwhere to find the challenge, please DM me at my Instagram: @hackersclub or Twitter: @ringoware\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 26 of 27\n\nHappy Hunting :)\r\nSource: https://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nhttps://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f\r\nPage 27 of 27",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://ryancor.medium.com/genetic-analysis-of-cryptowall-ransomware-843f86055c7f"
	],
	"report_names": [
		"genetic-analysis-of-cryptowall-ransomware-843f86055c7f"
	],
	"threat_actors": [
		{
			"id": "b740943a-da51-4133-855b-df29822531ea",
			"created_at": "2022-10-25T15:50:23.604126Z",
			"updated_at": "2026-04-10T02:00:05.259593Z",
			"deleted_at": null,
			"main_name": "Equation",
			"aliases": [
				"Equation"
			],
			"source_name": "MITRE:Equation",
			"tools": null,
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "d90307b6-14a9-4d0b-9156-89e453d6eb13",
			"created_at": "2022-10-25T16:07:23.773944Z",
			"updated_at": "2026-04-10T02:00:04.746188Z",
			"deleted_at": null,
			"main_name": "Lead",
			"aliases": [
				"Casper",
				"TG-3279"
			],
			"source_name": "ETDA:Lead",
			"tools": [
				"Agentemis",
				"BleDoor",
				"Cobalt Strike",
				"CobaltStrike",
				"RbDoor",
				"RibDoor",
				"Winnti",
				"cobeacon"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "5b748f86-ac32-4715-be9f-6cf25ae48a4e",
			"created_at": "2024-06-04T02:03:07.956135Z",
			"updated_at": "2026-04-10T02:00:03.689959Z",
			"deleted_at": null,
			"main_name": "IRON HEMLOCK",
			"aliases": [
				"APT29 ",
				"ATK7 ",
				"Blue Kitsune ",
				"Cozy Bear ",
				"The Dukes",
				"UNC2452 ",
				"YTTRIUM "
			],
			"source_name": "Secureworks:IRON HEMLOCK",
			"tools": [
				"CosmicDuke",
				"CozyCar",
				"CozyDuke",
				"DiefenDuke",
				"FatDuke",
				"HAMMERTOSS",
				"LiteDuke",
				"MiniDuke",
				"OnionDuke",
				"PolyglotDuke",
				"RegDuke",
				"RegDuke Loader",
				"SeaDuke",
				"Sliver"
			],
			"source_id": "Secureworks",
			"reports": null
		},
		{
			"id": "46b3c0fc-fa0c-4d63-a38a-b33a524561fb",
			"created_at": "2023-01-06T13:46:38.393409Z",
			"updated_at": "2026-04-10T02:00:02.955738Z",
			"deleted_at": null,
			"main_name": "APT29",
			"aliases": [
				"Cloaked Ursa",
				"TA421",
				"Blue Kitsune",
				"BlueBravo",
				"IRON HEMLOCK",
				"G0016",
				"Nobelium",
				"Group 100",
				"YTTRIUM",
				"Grizzly Steppe",
				"ATK7",
				"ITG11",
				"COZY BEAR",
				"The Dukes",
				"Minidionis",
				"UAC-0029",
				"SeaDuke"
			],
			"source_name": "MISPGALAXY:APT29",
			"tools": [
				"SNOWYAMBER",
				"HALFRIG",
				"QUARTERRIG"
			],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "20d3a08a-3b97-4b2f-90b8-92a89089a57a",
			"created_at": "2022-10-25T15:50:23.548494Z",
			"updated_at": "2026-04-10T02:00:05.292748Z",
			"deleted_at": null,
			"main_name": "APT29",
			"aliases": [
				"APT29",
				"IRON RITUAL",
				"IRON HEMLOCK",
				"NobleBaron",
				"Dark Halo",
				"NOBELIUM",
				"UNC2452",
				"YTTRIUM",
				"The Dukes",
				"Cozy Bear",
				"CozyDuke",
				"SolarStorm",
				"Blue Kitsune",
				"UNC3524",
				"Midnight Blizzard"
			],
			"source_name": "MITRE:APT29",
			"tools": [
				"PinchDuke",
				"ROADTools",
				"WellMail",
				"CozyCar",
				"Mimikatz",
				"Tasklist",
				"OnionDuke",
				"FatDuke",
				"POSHSPY",
				"EnvyScout",
				"SoreFang",
				"GeminiDuke",
				"reGeorg",
				"GoldMax",
				"FoggyWeb",
				"SDelete",
				"PolyglotDuke",
				"AADInternals",
				"MiniDuke",
				"SeaDuke",
				"Sibot",
				"RegDuke",
				"CloudDuke",
				"GoldFinder",
				"AdFind",
				"PsExec",
				"NativeZone",
				"Systeminfo",
				"ipconfig",
				"Impacket",
				"Cobalt Strike",
				"PowerDuke",
				"QUIETEXIT",
				"HAMMERTOSS",
				"BoomBox",
				"CosmicDuke",
				"WellMess",
				"VaporRage",
				"LiteDuke"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "f27790ff-4ee0-40a5-9c84-2b523a9d3270",
			"created_at": "2022-10-25T16:07:23.341684Z",
			"updated_at": "2026-04-10T02:00:04.549917Z",
			"deleted_at": null,
			"main_name": "APT 29",
			"aliases": [
				"APT 29",
				"ATK 7",
				"Blue Dev 5",
				"BlueBravo",
				"Cloaked Ursa",
				"CloudLook",
				"Cozy Bear",
				"Dark Halo",
				"Earth Koshchei",
				"G0016",
				"Grizzly Steppe",
				"Group 100",
				"ITG11",
				"Iron Hemlock",
				"Iron Ritual",
				"Midnight Blizzard",
				"Minidionis",
				"Nobelium",
				"NobleBaron",
				"Operation Ghost",
				"Operation Office monkeys",
				"Operation StellarParticle",
				"SilverFish",
				"Solar Phoenix",
				"SolarStorm",
				"StellarParticle",
				"TEMP.Monkeys",
				"The Dukes",
				"UNC2452",
				"UNC3524",
				"Yttrium"
			],
			"source_name": "ETDA:APT 29",
			"tools": [
				"7-Zip",
				"ATI-Agent",
				"AdFind",
				"Agentemis",
				"AtNow",
				"BEATDROP",
				"BotgenStudios",
				"CEELOADER",
				"Cloud Duke",
				"CloudDuke",
				"CloudLook",
				"Cobalt Strike",
				"CobaltStrike",
				"CosmicDuke",
				"Cozer",
				"CozyBear",
				"CozyCar",
				"CozyDuke",
				"Danfuan",
				"EnvyScout",
				"EuroAPT",
				"FatDuke",
				"FoggyWeb",
				"GeminiDuke",
				"Geppei",
				"GoldFinder",
				"GoldMax",
				"GraphDrop",
				"GraphicalNeutrino",
				"GraphicalProton",
				"HAMMERTOSS",
				"HammerDuke",
				"LOLBAS",
				"LOLBins",
				"LiteDuke",
				"Living off the Land",
				"MagicWeb",
				"Mimikatz",
				"MiniDionis",
				"MiniDuke",
				"NemesisGemina",
				"NetDuke",
				"OnionDuke",
				"POSHSPY",
				"PinchDuke",
				"PolyglotDuke",
				"PowerDuke",
				"QUIETEXIT",
				"ROOTSAW",
				"RegDuke",
				"Rubeus",
				"SNOWYAMBER",
				"SPICYBEAT",
				"SUNSHUTTLE",
				"SeaDaddy",
				"SeaDask",
				"SeaDesk",
				"SeaDuke",
				"Sharp-SMBExec",
				"SharpView",
				"Sibot",
				"Solorigate",
				"SoreFang",
				"TinyBaron",
				"WINELOADER",
				"WellMail",
				"WellMess",
				"cobeacon",
				"elf.wellmess",
				"reGeorg",
				"tDiscoverer"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775434104,
	"ts_updated_at": 1775792252,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/7e89d3ec711dfc7587eeb37048134e3d224bd8c3.pdf",
		"text": "https://archive.orkl.eu/7e89d3ec711dfc7587eeb37048134e3d224bd8c3.txt",
		"img": "https://archive.orkl.eu/7e89d3ec711dfc7587eeb37048134e3d224bd8c3.jpg"
	}
}