{
	"id": "89b9d918-8f5d-456a-aee1-644edb12b0c8",
	"created_at": "2026-04-06T00:21:21.90816Z",
	"updated_at": "2026-04-10T03:38:03.339611Z",
	"deleted_at": null,
	"sha1_hash": "2eecbed10b18122346fb89e93900fb8e7cbc680c",
	"title": "New TA402/MOLERATS Malware – Decrypting .NET Reactor Strings",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1532323,
	"plain_text": "New TA402/MOLERATS Malware – Decrypting .NET Reactor\r\nStrings\r\nBy 0verfl0w_\r\nPublished: 2021-07-06 · Archived: 2026-04-05 16:42:19 UTC\r\nIt’s sure been a while since the last post! We’ve gone through several iterations of website design over the past\r\nfew months (plus fixing all the malformed images due to the theme transfer), but should be back for good now!\r\nFor this commemorative post, we’ll be diving into a recently discovered malware sample known as LastConn, a\r\npayload used by the MOLERATS APT group, which was obfuscated using .NET Reactor. The problem is, de4dot\r\nis unable to deobfuscate it, so the job falls upon us to do so. We’ll be examining the string encryption routine,\r\nreplicating it in Python, testing it manually, and then automating it somewhat to extract all string related indicators\r\nfrom the binary, and decrypt the relevant strings! Let’s get into it!\r\nLastConn MD5 Hash: D07654434D64B73FE8CB49CFB9B7E3FB\r\nTable Of Contents\r\nMOLERATS Group\r\nLastConn Implant\r\n.NET Reactor Obfuscator\r\nInitial Analysis\r\nManual String Decryption\r\nReplicating the Algorithms\r\nSemi-Automation\r\nMOLERATS: Overview\r\nMOLERATS, also known as TA402, are a Middle Eastern based APT group known for performing intrusions\r\nagainst Middle Eastern Government Organisations, including Israel, the UAE, and Turkey. The most recent\r\ncampaign, discovered by ProofPoint, once again targeted government organisations and organisations with\r\ndiplomatic relationships in the Middle East. The prime focus of the attackers is to exfiltrate sensitive information\r\nin order to gather intelligence, using spear-phishing as an initial infection vector. In this campaign, ProofPoint\r\ndiscovered the threat actors utilising a somewhat new malware dubbed as LastConn.\r\nLastConn: Overview\r\nLastConn is believed to be an updated version of the SharpStage backdoor, previously discovered by\r\nCyberReason back in December 2020. The SharpStage backdoor, developed in .NET, utilised DropBox API for\r\nexfiltration, and had specific checks for Arabic on the infected machine. LastConn also implemented similar\r\nchecks, as well as the DropBox API for communication. One of the discovered samples utilised an obfuscator that\r\nDe4Dot could not successfully deobfuscate, known as .NET Reactor.\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nPage 1 of 13\n\n.NET Reactor: Overview\r\n.NET Reactor is a powerful code protection and software licensing system for software written for the .NET\r\nFramework, and supports all languages that generate .NET assemblies. It is commercially available, and provides\r\nfeatures such as string encryption, control flow obfuscation, and code virtualisation. A free trial is provided for the\r\nsoftware as well, which seems to have been used by the threat actors, based on a string found in the list of\r\ndecrypted strings. Talking about decrypted strings, let’s start analysing!\r\nInitial Analysis:\r\nOpening up the initial sample in PEStudio, we can confirm that we are dealing with a fairly large .NET binary. At\r\na first look, my thoughts were that the main payload was packed, resulting in the large file size, however upon\r\nopening the sample in dnSpy we can see that it is not packed – in fact we can see the unobfuscated symbols in the\r\nAssembly Explorer, with classes like Dropbox.Api and Newtonsoft.Json visible.\r\nAs mentioned, .NET Reactor provides functionality to encrypt strings, obfuscate control flow, and even virtualise\r\nthe .NET instructions in a similar fashion to x86 Assembly virtualisation, which, luckily in this case, the threat\r\nactors chose not to enable! Instead, the main methods of protection in this binary surround the string encryption\r\nand control flow obfuscation, as well as the addition of junk code. We can clearly see this in the entry point\r\nfunction, labelled as LsfApkF4M(). \r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nPage 2 of 13\n\nYou’ll probably notice the junk code (subtraction of 212870 from 277629) which will always return true.\r\nHowever, the junk code isn’t limited to one-liners; two comparisons are performed between null and the return\r\nvalues from the function dQWY6qG82SAbCK3Pxa() – which will also return null. Therefore, we can now take\r\nnote that a large number of functions in the binary are probably going to be made up of junk code that all return a\r\nconstant value.\r\nThe main function of importance inside the entry point is at the very end, where we see the sample will execute\r\nForm1() – this is where the interesting stuff occurs. However, just before that we do encounter the very first “anti-analysis” check, which is a date check. The sample will refuse to continue execution if it has been executed after\r\nthe 30th of June, 2021. While this is not the most interesting function, it does show us the first instance of a string\r\ndecryption function. If the sample has been executed after the 30th of June, an exception will be thrown. The\r\nargument passed to the Exception() call is a string, and is returned from the function\r\nMYcw9uffxdYPAXmUtn.pyM1eVFCveMv9BuGJ6().\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nPage 3 of 13\n\nUpon checking the identified “anti-analysis” function again, and seeing that the function would display “This\r\nassembly is protected by an unregistered version of Eziriz’s “.NET Reactor”! This assembly won’t further work.”,\r\nit became clear that this is more likely to be a function created by .NET Reactor, to prevent the obfuscated payload\r\nworking after a specific trial end date.\r\nThis particular function has the value 208444 passed as an argument, indicating that the strings are potentially\r\nstored in some kind of an array/list. Regardless, we have now found what looks to be a string encryption routine,\r\nso let’s dive in!\r\nString Decryption:\r\nUnfortunately for us, the control flow of this function has been highly obfuscated, with multiple goto’s, loops, and\r\nplenty of conditional statements. However, we can piece some information together just by scrolling through the\r\nlines of code. Firstly, there is an array variable which is constantly changing through the flow of execution, at\r\nleast in the first loop. It changes so often it would be very time consuming to manually calculate the bytes inside\r\nthe variable, and so dynamic analysis will have to be used.\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nPage 4 of 13\n\nNext, we can see a variable named binaryReader is referred to quite a lot throughout the function. A simple\r\nCTRL+F indicates this will contain data read from the resource 28VD5i1hSj4mcdhHmc.KIl6nvHWBWAvSEm7PO.\r\nInitially, this data does not seem to be used for anything interesting, however it is a strange blob of data and is still\r\nreferenced in the function, so let’s go ahead and extract that to be used later on.\r\nAt the very end of the string decryption function, we can clearly see the variable returned is named string, and it\r\nis retrieved through the variable array3. array3 is initialized above, with a Copy() call, which will copy the data\r\nfrom MYcw9uffxdYPAXmUtn.aAgdDBUcpQV to array3. Another point of interest is the usage of the\r\nargument M448gdJtBGnIC5sjsy as the index – this argument is equal to 208444 in the first call to this function. \r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nPage 5 of 13\n\nMYcw9uffxdYPAXmUtn.aAgdDBUcpQV is initialised using data inside 3 variables, and a function call: array,\r\narray2, u, and MYcw9uffxdYPAXmUtn().K5vdDAvqBdJ(). array and array2 are dynamically generated through\r\nthe multiple loops and conditional statements, but u on the other hand contains the data inside binaryReader: the\r\nresource data we dumped previously. \r\nThe function MYcw9uffxdYPAXmUtn().K5vdDAvqBdJ() is our first real algorithm inside the string encryption\r\nroutine. The algorithm itself is seemingly custom to .NET Reactor, and uses the data inside array to decrypt the\r\nresource data. The decrypted data is stored as an array, at which point the integer passed in as an argument to the\r\ninitial string encryption routine is used as an offset.\r\nEnough about theory, let’s go ahead and debug the sample using dnSpy to extract the data inside array and\r\narray2, and then we can move onto looking at replicating the algorithm! \r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nPage 6 of 13\n\nDoing so should be fairly simple, as we know where the decrypted data is returned, so we just need to set a\r\nbreakpoint on the function MYcw9uffxdYPAXmUtn().K5vdDAvqBdJ(), and then dump the data inside the target\r\nvariables. Buuut we cannot place a breakpoint on that address, as dnSpy cannot create one. \r\nInstead, we will place a breakpoint just after the try block has come to an end, so where the variable num5 is\r\ninitialised. Sure enough, we can now dump both target variables – array is 32 bytes long, and array2 is 16 bytes\r\nlong, indicating a possible key and IV setup. With all the pieces of the puzzle, we can now go ahead and attempt\r\nto replicate the decryption!\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nPage 7 of 13\n\nReplicating Algorithms:\r\nWe will be replicating the algorithm in Python, and luckily as dnSpy decompiles .NET binaries very well, it\r\nshould be a somewhat quick process considering how closely decompiled .NET resembles Python. Before we do\r\nthat, let’s go ahead and run the binary through de4dot, as it will provide us a nice base to work off by removing as\r\nmuch obfuscation as it can.\r\nViewing the string encryption function, it is clear that de4dot has done a great job. The biggest difference however\r\nis inside the custom algorithm, in the function mmmdDDP5Yd6(). In the images below, you can see the difference\r\nbetween the original binary (left), and the de4dot altered binary (right). This difference will make it a lot easier to\r\nreplicate the algorithm.\r\nSo, let’s start by implementing the mmmdDDP5Yd6() function. If we were to copy and paste it (and remove the\r\nU’s), it would execute correctly, but the returned value would be incorrect. The reason for this is Python is happy\r\nto work on a 64 bit integer, and so if we were to execute the code using 4 as the value for the uint_0 variable, the\r\nresult would be –0x627474A8294. We only want to deal with 32 bit values, so we will be using \u0026 0xFFFFFFFF\r\nin our function a lot; specifically on every line, like shown below.\r\nRunning the updated code, using the same value for uint_0, we get 0xB8B4C22C. So now we know we can avoid\r\ndealing with 64 bit integers, we can jump back to the main algorithm, and replicate that!\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nPage 8 of 13\n\nThe rest of the algorithm is fairly simple to implement, however there is 1 global variable that stands out:\r\nK2qdDH9707O. This is assigned in a call just before the string encryption algorithm, and is in fact the data stored\r\ninside the variable array. Interestingly, array2 does not seem to be used at all throughout the function, so we can\r\nignore it from here on out.\r\nAfter converting the script from .NET to Python, we can now go ahead and test it! We already have the array\r\ndata, and the resource data, so all we need to figure out is how the function uses the argument to locate the correct\r\nstring.\r\nWell, we don’t need to look very hard to find it – jumping right to the end we can see a fairly simple block of\r\ncode, which we covered at the beginning of the String Decryption chapter. \r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nPage 9 of 13\n\nFirst, num3 is calculated by calling ToInt32() and passing MYcw9uffxdYPAXmUtn.aAgdDBUcpQV as the\r\nsource data, and M448gdJtBGnIC5sjsy (the function argument) as the start index. Next, the variable array3 is\r\ninitialized to the size of num3, so we can safely assume that num3 is a string size. array3 is then filled with data\r\nfrom MYcw9uffxdYPAXmUtn.aAgdDBUcpQV, with the start index set to M448gdJtBGnIC5sjsy + 4. This\r\nmeans the strings will be stored as follows:\r\n[4 BYTE SIZE][STRING]\r\nAnd that is pretty much it! The string blob itself is decrypted all at once, and so the argument is only used to\r\nretrieve a specific string in the decrypted data. Putting all this together, we get the following script:\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nPage 10 of 13\n\nRunning it with a few values we can find in the script also yields some nice results! We can also just dump all the\r\ndecrypted strings to browse through, to get a good idea of what this tool is capable of doing!\r\nNow, it is all good being able to decrypt strings with user input, but let’s take this one step further and attempt to\r\nautomate it!\r\nSemi-Automation:\r\nAutomation is where things start to become quite complex. I don’t typically focus on .NET malware, and so\r\nthere’s still a number of things I have to figure out – including figuring out how to have an automated string\r\ndecrypter resolve strings or even comment similar to IDAPython. Currently, the automation of this string\r\ndecrypter goes as far as locating all calls to the string decryption function, extracting the offset, and returning the\r\ncorrect string for that function. Unfortunately, this is where that stops. I have yet to successfully overwrite the IL\r\ninstructions with a simple ldstr (like this blog post), and receive the following error:\r\nIf anyone has any idea what the issue is, I’d be very grateful if you could let me know! Regardless, let’s have a\r\nlook at how we can use Python and DNLIB to locate function calls and offset arguments in the binary!\r\nIn order to load the dnlib library, we need to make sure we have pythonnet installed, which can be installed using\r\npip install pythonnet. Additionally, make sure you have the DNLIB DLL downloaded! With that, we need to\r\nimport the Common Language Runtime (manages execution of .NET programs), as well as the System.Reflection\r\nnamespace.  This can be done as follows:\r\nimport clr\r\nfrom System.Reflection import Assembly, MethodInfo, BindingFlag\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nPage 11 of 13\n\nfrom System import Type\r\nThen, we need to load DNLIB using clr.AddReference(). This allows us to import functions from DNLIB,\r\nincluding the DotNet namespace. And now we’re ready to start parsing .NET binaries!\r\nclr.AddReference(r\"dnlib\")\r\nimport dnlib\r\nfrom dnlib.DotNet import *\r\nfrom dnlib.DotNet.Emit import OpCodes\r\nThe parsing code was adapted from polynomenx’s blog post as listed above, and it is a brilliant example of what is\r\npossible pairing DNLIB with Python. In this case, we can search through the binary in a similar way, searching for\r\nall mentions of the method pyM1eVFCveMv9BuGJ6. \r\nAfter executing the above script, we can view the glory that is automation! We can print all the strings, or simply\r\npipe the output to a file to view later on – providing us with the same output that ProofPoint uploaded to their\r\nGitHub. While .NET Reactor obfuscated malware does not use the same encryption key, it’s fairly simple to\r\nreverse the string encryption (at least in this version) and use the tools we covered in this post to develop a semi-automated string decrypter, speeding your analysis up by 10-fold!\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nPage 12 of 13\n\nYou can grab the full (and cleaned up) script from here! \r\nIt’s currently optimized for Python 2.7, but with some slight alterations it should be good to go for Python 3!\r\nAfter uploading the script I noticed some issues with it not picking up several calls to the string decryption\r\nfunction inside the main Pro.Form1(). It does pick up quite a few strings, though there are some obvious strings\r\nthat do not appear in the dump, but are visible in the string dump on the ProofPoint ThreatResearch Github.\r\nIt could just be that I’m running the entire thing in Python instead of C#, but if anyone knows the specifics I’d\r\nlove to find out!\r\nAnd that wraps up this post on decrypting the strings inside the MOLERATS LastConn payload!\r\nYou may have noticed the changes to the website design, as well as cleaned up the course page – now we have\r\nfinally finished working on the design and restoration, posts will be more frequent, so stay tuned! We’ve got quite\r\na lot planned over the next few months!\r\nAnyway, if you’ve got any feedback or questions, feel free to drop a comment down below, drop me a message\r\nover Twitter (@0verfl0w_), or via email (daniel@0ffset.net)! \r\nThanks for taking the time to read through the post!\r\nSource: https://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/\r\nPage 13 of 13",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://www.0ffset.net/reverse-engineering/malware-analysis/molerats-string-decryption/"
	],
	"report_names": [
		"molerats-string-decryption"
	],
	"threat_actors": [
		{
			"id": "0c502f6d-640d-4e69-bfb8-328ba6540d4f",
			"created_at": "2022-10-25T15:50:23.756782Z",
			"updated_at": "2026-04-10T02:00:05.324924Z",
			"deleted_at": null,
			"main_name": "Molerats",
			"aliases": [
				"Molerats",
				"Operation Molerats",
				"Gaza Cybergang"
			],
			"source_name": "MITRE:Molerats",
			"tools": [
				"MoleNet",
				"DustySky",
				"DropBook",
				"SharpStage",
				"PoisonIvy"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "e5cad6bf-fa91-4128-ba0d-2bf3ff3c6c6b",
			"created_at": "2025-08-07T02:03:24.53077Z",
			"updated_at": "2026-04-10T02:00:03.680525Z",
			"deleted_at": null,
			"main_name": "ALUMINUM SARATOGA",
			"aliases": [
				"APT-C-23",
				"Arid Viper",
				"Desert Falcon",
				"Extreme Jackal ",
				"Gaza Cybergang",
				"Molerats ",
				"Operation DustySky ",
				"TA402"
			],
			"source_name": "Secureworks:ALUMINUM SARATOGA",
			"tools": [
				"BlackShades",
				"BrittleBush",
				"DarkComet",
				"LastConn",
				"Micropsia",
				"NimbleMamba",
				"PoisonIvy",
				"QuasarRAT",
				"XtremeRat"
			],
			"source_id": "Secureworks",
			"reports": null
		},
		{
			"id": "1162e0d4-b69c-423d-a4da-f3080d1d2b0c",
			"created_at": "2023-01-06T13:46:38.508262Z",
			"updated_at": "2026-04-10T02:00:03.006018Z",
			"deleted_at": null,
			"main_name": "Molerats",
			"aliases": [
				"Gaza Cybergang",
				"Operation Molerats",
				"Extreme Jackal",
				"ALUMINUM SARATOGA",
				"G0021",
				"BLACKSTEM",
				"Gaza Hackers Team",
				"Gaza cybergang"
			],
			"source_name": "MISPGALAXY:Molerats",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "0ad97d64-7970-48ca-83f6-3635c66e315c",
			"created_at": "2023-11-21T02:00:07.400003Z",
			"updated_at": "2026-04-10T02:00:03.479189Z",
			"deleted_at": null,
			"main_name": "TA402",
			"aliases": [],
			"source_name": "MISPGALAXY:TA402",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "847f600c-cf90-44c0-8b39-fb0d5adfcef4",
			"created_at": "2022-10-25T16:07:23.875541Z",
			"updated_at": "2026-04-10T02:00:04.768142Z",
			"deleted_at": null,
			"main_name": "Molerats",
			"aliases": [
				"ATK 89",
				"Aluminum Saratoga",
				"Extreme Jackal",
				"G0021",
				"Gaza Cybergang",
				"Gaza Hackers Team",
				"Molerats",
				"Operation DustySky",
				"Operation DustySky Part 2",
				"Operation Molerats",
				"Operation Moonlight",
				"Operation SneakyPastes",
				"Operation TopHat",
				"TA402",
				"TAG-CT5"
			],
			"source_name": "ETDA:Molerats",
			"tools": [
				"BadPatch",
				"Bladabindi",
				"BrittleBush",
				"Chymine",
				"CinaRAT",
				"Darkmoon",
				"Downeks",
				"DropBook",
				"DustySky",
				"ExtRat",
				"Gen:Trojan.Heur.PT",
				"H-Worm",
				"H-Worm RAT",
				"Houdini",
				"Houdini RAT",
				"Hworm",
				"Iniduoh",
				"IronWind",
				"Jenxcus",
				"JhoneRAT",
				"Jorik",
				"KasperAgent",
				"Kognito",
				"LastConn",
				"Micropsia",
				"MoleNet",
				"Molerat Loader",
				"NeD Worm",
				"NimbleMamba",
				"Njw0rm",
				"Pierogi",
				"Poison Ivy",
				"Quasar RAT",
				"QuasarRAT",
				"SPIVY",
				"Scote",
				"SharpSploit",
				"SharpStage",
				"WSHRAT",
				"WelcomeChat",
				"Xtreme RAT",
				"XtremeRAT",
				"Yggdrasil",
				"dinihou",
				"dunihi",
				"njRAT",
				"pivy",
				"poisonivy"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775434881,
	"ts_updated_at": 1775792283,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/2eecbed10b18122346fb89e93900fb8e7cbc680c.pdf",
		"text": "https://archive.orkl.eu/2eecbed10b18122346fb89e93900fb8e7cbc680c.txt",
		"img": "https://archive.orkl.eu/2eecbed10b18122346fb89e93900fb8e7cbc680c.jpg"
	}
}