{
	"id": "8748e917-795d-462a-a3b2-d372e61b1348",
	"created_at": "2026-04-06T00:08:07.759998Z",
	"updated_at": "2026-04-10T13:12:47.107638Z",
	"deleted_at": null,
	"sha1_hash": "5c31c59315e4b7c2b62ab683b0d63d833bf89fe7",
	"title": "DarkGate - Threat Breakdown Journey",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1698410,
	"plain_text": "DarkGate - Threat Breakdown Journey\r\nBy 0xToxin\r\nPublished: 2023-08-06 · Archived: 2026-04-05 13:54:16 UTC\r\nIntroPermalink\r\nOver the past month, a widespread phishing campaign has targeted individuals globally.\r\nThe campaigns execution chain ends with the deployment of a malware known as: DarkGate. A loader type\r\nmalware.\r\nDarkGate is exclusively sold on underground online forums and the developer keeps a very tight amount of seats\r\nfor customers.\r\nThe LurePermalink\r\nThe adversary behind the campaign distributed a high volume campaign of phishing emails, those mails were\r\nstolen conversation threads that the adversary had access to.\r\nThe challenge here lies in the fact that users often trust what they remember, and because of that, I think users who\r\naren’t aware of such tactics could easily become infected and fall prey to the “social engineering” trap.\r\nBelow, you’ll find an example of the content the adversary added to the hijacked conversation thread:\r\nI’ve created a diagram that demonstrates the execution flow of the campaign:\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 1 of 16\n\nGeofence CheckPermalink\r\nHonestly, I’m still trying to figure out what checks need to be passed to get through the geofence set by the\r\nadversary. After examining some of the URLs on URLscan.io, I discovered that those which were successful in\r\nobtaining a payload featured the refresh header in their response (makes sense). This header included the URL\r\nneeded to download the payload, for instance:\r\nIf the user successfully passes the check, an MSI file is downloaded from the URL, following the structure:\r\nProject_[0-9]{7}\\.msi\r\nMSI LoaderPermalink\r\nThe downloaded MSI carries two embedded files:\r\nCustomAction.dll\r\nWrappedSetupProgram.cab\r\nThe DLL is called upon by the MSI to unpack the content housed in WrappedSetupProgram.cab and execute it.\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 2 of 16\n\nThe cab archive includes two files:\r\nAutoit3.exe\r\nUGtZgHHT.au3 (AutoIT 3 script)\r\nAutoIT ScriptPermalink\r\nUpon initial examination, the script appears to be altered. Typically, most AutoIT scripts I’ve come across begin\r\nwith the magic bytes A3 48 4B BE and 41 55 33 21 45 41 (AU3!EA) like explained in this blog:\r\nYou can find the au3 script magic  bytes AU!EA06 (06 here is the subtype of the script), inside of its hex\r\ndump as shown in the picture below.\r\nHowever, the script I analyzed contained a substantial amount of what seemed to be junk data at the start of the\r\nfile. (We’ll get back to this later in the blog)\r\nI managed to locate the magic bytes indicating the AU3 script’s starting point at the offset 0xA0A5C :\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 3 of 16\n\nTo extract the actual script, I changed the file’s extension from au3 to a3x (representing an AutoIT3 compiled\r\nscript) and used the tool myAut2Exe for extraction.\r\nShellcode CallWindowProc InjectionPermalink\r\nThe AU3 script consists of two main components:\r\n1. A segmented hex-encoded shellcode that is concatenated into a single variable.\r\n2. Injection and execution of the shellcode.\r\nThe first part is quite self-explanatory. In my analysis, the variable was named $SSUGZNUOOE, and it appeared\r\nover 2,000 times in the script:\r\nThe second segment of the script initiates by verifying the existence of the ProgramFiles folder and confirming\r\nthat the username executing the script is not SYSTEM. I suspect these checks are evasion tactics to ensure the\r\nscript runs within a standard Windows environment rather than a sandbox or custom setup.\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 4 of 16\n\nThe script proceeds to convert the hex-encoded shellcode to a binary string using the BinaryToString function\r\nand assigns it to the $MZRSVIMCSW variable. The variable $MFCKUCOYGW is initialized as a DLL\r\nstructure sized to the shellcode using the DllStructCreate function.\r\nThe script checks if the path C:\\Program Files (x86)\\Sophos exists. If it doesn’t, a hex-encoded command is\r\nexecuted which, upon decoding, reveals the use of the API VirtualProtect to modify the memory region\r\nprotection of $MZRSVIMCSW to ERX. (My theory is that the DarkGate developer noticed Sophos could detect\r\nchanges in protection type)\r\nThe script then copies the content of the shellcode into the DLL structure and injects it by calling the API\r\nCallWindowProc . (I found a youtube video that presents a POC for the injection)\r\nShellCode AnalysisPermalink\r\nUpon loading the ShellCode in IDA, it becomes immediately apparent that the shellcode consists of a single large\r\nfunction that loads stack-strings.\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 5 of 16\n\nIn addition, I used FLOSS to check on the strings and FLOSS successfully extracted 71 strings:\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 6 of 16\n\nNext, I will use BlobRunner to invoke the shellcode, set a breakpoint after all the stack-strings have been pushed\r\nonto the stack, and dump the memory containing the executable that was pushed:\r\nLoader AnalysisPermalink\r\nThe loader we’ve dumped will be in charge of decoding and executing part of the junk data stored inside of the\r\nAutoIT script (After decoding we will face with the final binary which is the DarkGate loader)\r\nThe loader requires a a command line argument which will be the path to the AutoIT script. The loader will check\r\nfor the argument and if it’s not ends with .au3 or the executable can’t get a handle for the file a message box with\r\nthe text “bin 404” will appear and the loader will terminate itself.\r\nWhen the loader successfully accesses the AutoIT script, it reads its content and segments it based on the\r\ncharacter: | (0x7C).\r\nNext, the loader retrieves 8 bytes from the second offset of the data located in the second element of the array.\r\n(Represented as: stringsArray[2][1:9] == xorKeyData ).\r\nThe character a is then prefixed to these extracted bytes. (Resulting in: a + xorKeyData == modifiedXorKey ).\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 7 of 16\n\nTo generate the decryption key, the loader first determines the length of the concatenated byte array, then employs\r\nan XOR loop over each byte in the array ( len(modifiedXorKey) ^ modifiedXorKey[0] ^ modifiedXorKey[1]\r\n... ).\r\nThe loader fetches the data from the third element of the array and decodes it from base64. Each byte of this data\r\nis XOR-ed with the decryption key and also applied with a NOT operation.\r\nThe outcome of this process is an executable, which is the final payload (DarkGate malware)\r\nTo streamline this process, I’ve created a Python script capable of extracting and decrypting the DarkGate payload\r\nfrom the AutoIT script:\r\nfrom base64 import b64decode\r\nAUTO_IT_PATH = '' #Change to the AutoIT script path.\r\nFINAL_PAYLOAD_PATH = '' #Change to output path.\r\nfileData = open(AUTO_IT_PATH, 'rb').read().decode(errors='ignore')\r\nstringsArray = fileData.split('|')\r\nmodifiedXorKey = 'a' + stringsArray[1][1:9]\r\ndecodedData = b64decode(stringsArray[2])\r\nkey = len(modifiedXorKey)\r\nfor byte in modifiedXorKey:\r\n key ^= ord(byte)\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 8 of 16\n\nfinalPayload = b''\r\nfor byte in decodedData:\r\n finalPayload += bytes([~(byte ^ key)\u0026 0xFF])\r\nopen(FINAL_PAYLOAD_PATH, 'wb').write(finalPayload)\r\nprint('[+] Final Payload Was Created!')\r\nDarkGate AnalysisPermalink\r\nEssentially, you can read through the developer’s sale thread on xss.is and understand the various capabilities of\r\nthe loader, which include:\r\nHVNC\r\nCrypto miner setup\r\nBrowser history and cookie theft\r\nRDP\r\nHAnyDesk\r\nDuring my analysis, my primary objective was to decrypt the contained strings, locate the C2 strings (since\r\nthey’re not available in plain text), and decrypt the network traffic.\r\nStrings DecryptionPermalink\r\nDuring my investigation, I found two embedded strings (each 64 characters long) which are invoked by two\r\ndifferent but similar functions:\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 9 of 16\n\nWhen checking the cross-references for the first string (used in the function on the left), we can see a total of 864\r\ncalls to the function.\r\nThe first argument passed to the function is the container for the return value, and the second argument is the\r\n“encrypted” string.\r\nThese hard-coded strings are part of a custom Base64 decoding routine. I’d like to extend my personal thanks to\r\n@rivitna2 for correcting me when initially published the strings decoding script.\r\nThe first batch of decoded strings represents all the strings utilized by DarkGate during its execution. Some of\r\nthese strings looks like notification messages sent to the C2, such as:\r\n- New Bot: DarkGate is inside hAnyDesk user with admin rights\r\n- DarkGate not found to get executed on the new hAnyDesk Desktop, Did you enabled Startup option on builder?\r\n- Credentials detected, removing them!\r\nYou can find a list of all decoded strings here\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 10 of 16\n\nThe second hard-coded string is employed in the same routine, but it’s called much less frequently. The developer\r\ntried to mess up a bit with researchers from discovering DarkGate’s configurations by adding this second hard-coded string. It is used for decoding DarkGate’s configurations and it also plays a role in decoding the network\r\ntraffic data.\r\nBy decoding the data associated with the second hard-coded string, I managed to uncover DarkGate’s\r\nconfiguration:\r\nhttp://80.66.88.145|\r\n0=7891\r\n1=Yes\r\n2=Yes\r\n3=No\r\n5=Yes\r\n4=50\r\n6=No\r\n8=Yes\r\n7=4096\r\n9=No\r\n10=bbbGcB\r\n11=No\r\n12=No\r\n13=Yes\r\n14=4\r\n15=bIWRRCGvGiXOga\r\n16=4\r\n17=No\r\n18=Yes\r\n19=Yes\r\nBelow is an IDAPython script that requires both the wrapper function calls and the hard-coded strings:\r\nimport idc\r\nimport idautils\r\nimport idaapi\r\nimport re\r\nDECRYPTION_FUNCTION_1 = # Replace with \"Wrapper\" function call\r\nLIST_1 = # Add 64 length list\r\nSTRINGS_FILE_1 = # Output file path\r\nDECRYPTION_FUNCTION_2 = # Replace with \"Wrapper\" function call\r\nLIST_2 = # Add 64 length list\r\nSTRINGS_FILE_2 = # Output file path\r\ndef decShiftFunc(arg1, arg2, arg3, arg4):\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 11 of 16\n\nfinal = ''\r\n tmp = (arg1 \u0026 0x3F) * 4\r\n final += chr(((arg2 \u0026 0x30) \u003e\u003e 4) + tmp)\r\n tmp = (arg2 \u0026 0xF) * 16\r\n final += chr(((arg3 \u0026 0x3C) \u003e\u003e 2) + tmp)\r\n final += chr((arg4 \u0026 0x3F) + ((arg3 \u0026 0x03) \u003c\u003c 6))\r\n return final.replace('\\0','')\r\ndef decWrapperFunc(encData, listNum):\r\n hexList = []\r\n for x in encData:\r\n hexList.append(listNum.index(x))\r\n subLists = [hexList[i:i+4] for i in range(0, len(hexList), 4)]\r\n if len(subLists[-1]) \u003c 4:\r\n subLists[-1].extend([0x00] * (4 - len(subLists[-1])))\r\n finalString = ''\r\n for subList in subLists:\r\n finalString += decShiftFunc(subList[0],subList[1],subList[2],subList[3])\r\n return finalString\r\ndef getArg(ref_addr):\r\n ref_addr = idc.prev_head(ref_addr)\r\n if idc.print_insn_mnem(ref_addr) == 'mov':\r\n if idc.get_operand_type(ref_addr, 1) == idc.o_imm:\r\n return(idc.get_operand_value(ref_addr, 1))\r\n else:\r\n return None\r\ndef listDecrypt(functionEA, listID, fileID):\r\n stringsList = []\r\n for xref in idautils.XrefsTo(functionEA):\r\n argPtr = getArg(xref.frm)\r\n if not argPtr:\r\n continue\r\n data = idc.get_bytes(argPtr, 300)\r\n encData = re.sub(b'[^\\x20-\\x7F]+', '', data.split(b'\\x00')[0]).decode() # Cleaning...\r\n decData = decWrapperFunc(encData,listID)\r\n stringsList.append(decData)\r\n idc.set_cmt(idc.prev_head(xref.frm), decData, 1)\r\n \r\n print(f'[+] {len(stringsList)} Strings were extracted')\r\n out = open(fileID, 'w')\r\n for string in stringsList:\r\n out.write(f'{string}\\n')\r\n out.close()\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 12 of 16\n\nprint('[*] Staring decryption of list 1')\r\nlistDecrypt(DECRYPTION_FUNCTION_1,LIST_1,STRINGS_FILE_1)\r\nprint('[+] Staring decryption of list 2')\r\nlistDecrypt(DECRYPTION_FUNCTION_2,LIST_2,STRINGS_FILE_2)\r\nNetwork Traffic DecryptionPermalink\r\nAs I hinted in the previous section, DarkGate’s network activity indeed incorporates both data obfuscation\r\ntechniques we’ve encountered during the analysis:\r\nLoop XOR\r\nCustom Base64 Decoding\r\nNow, let’s examine one of the network streams that is transmitted to the C2:\r\nIn the POST request, we can observe several fields:\r\nid\r\ndata\r\nact\r\nThe id is our XOR key initializer, which generates the actual XOR key using the same technique we used to\r\ninitialize the XOR key for decrypting the final DarkGate payload. ( len(id) ^ id[0] ^ id[1] .. )\r\nThe data field is encoded using the second hard-coded string. After decoding, this string will undergo an XOR\r\noperation with the key generated from id, as well as a NOT operation.\r\nTo simplify this process, I’ve created a Python script that decrypts the data:\r\nLIST = '' # Replace list used for config decoding\r\nDATA = '' # Replace with the encrypted data from the network traffic\r\nID = '' # Replace with the ID from the network traffic\r\ndef decShiftFunc(arg1, arg2, arg3, arg4):\r\n final = ''\r\n tmp = (arg1 \u0026 0x3F) * 4\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 13 of 16\n\nfinal += chr(((arg2 \u0026 0x30) \u003e\u003e 4) + tmp)\r\n tmp = (arg2 \u0026 0xF) * 16\r\n final += chr(((arg3 \u0026 0x3C) \u003e\u003e 2) + tmp)\r\n final += chr((arg4 \u0026 0x3F) + ((arg3 \u0026 0x03) \u003c\u003c 6))\r\n return final.replace('\\0','')\r\nhexList = []\r\nfor x in DATA:\r\n hexList.append(LIST.index(x))\r\nsubLists = [hexList[i:i+4] for i in range(0, len(hexList), 4)]\r\nif len(subLists[-1]) \u003c 4:\r\n subLists[-1].extend([0x00] * (4 - len(subLists[-1])))\r\nfinalString = ''\r\nfor subList in subLists:\r\n finalString += decShiftFunc(subList[0],subList[1],subList[2],subList[3])\r\nkey = len(ID)\r\nfor x in ID:\r\n key ^= ord(x)\r\nplainData = ''\r\nfor x in finalString:\r\n plainData += chr(~(ord(x) ^ key)\u0026 0xFF)\r\nprint(f'[+] Output: {plainData}')\r\nBelow is the output of the script for these parameters:\r\n- LIST = zLAxuU0kQKf3sWE7ePRO2imyg9GSpVoYC6rhlX48ZHnvjJDBNFtMd1I5acwbqT+=\r\n- DATA = FpOkFahzFpOuNjxuFsfNFsOAMpOuNvkuFQrcHwtMDfmlHahzFpOuNqOuFs7uFsOAJqOuNj5uFs3kFsOAFpOuNqxuFs3WFsOAjjOuNvk\r\n- ID = GEabbfEcbKBadGaccCDCaGKccGGfKHKG\r\n1033|410064006D0069006E00|MSXGLQPS|4100700070006C00690063006100740069006F006E00200056006500720069006600690065007\r\nSummaryPermalink\r\nOn this campaign we’ve uncovered a global campaign using hijacked email threads for phishing, which leads to\r\nthe download of a sophisticated malware known as DarkGate. Users downloading the malware received an MSI\r\nfile with two embedded files which carried encoded shellcode for execution. DarkGate also used unique decoding\r\nfor two embedded strings, revealing commands sent to the C2 and the malware’s configuration. Obfuscation\r\ntechniques like Loop XOR and custom Base64 decoding were observed in DarkGate’s network activity. Python\r\nscripts were created to decrypt the payload and data in this comprehensive analysis.\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 14 of 16\n\nYara RulePermalink\r\nI created a YARA rule based on the procedure used to decode the strings:\r\nrule Win_DarkGate\r\n{\r\nmeta:\r\nauthor = \"0xToxin\"\r\ndescription = \"DarkGate Strings Decoding Routine\"\r\ndate = \"2023-08-01\"\r\nstrings:\r\n$chunk_1 = {\r\n8B 55 ??\r\n8A 4D ??\r\n80 E1 3F\r\nC1 E1 02\r\n8A 5D ??\r\n80 E3 30\r\n81 E3 FF 00 00 00\r\nC1 EB 04\r\n02 CB\r\n88 4C 10 ??\r\nFF 45 ??\r\n80 7D ?? 40\r\n74 ??\r\n8B 45 ??\r\nE8 ?? ?? ?? ??\r\n8B 55 ??\r\n8A 4D ??\r\n80 E1 0F\r\nC1 E1 04\r\n8A 5D ??\r\n80 E3 3C\r\n81 E3 FF 00 00 00\r\nC1 EB 02\r\n02 CB\r\n88 4C 10 ??\r\nFF 45 ??\r\n80 7D ?? 40\r\n74 ??\r\n8B 45 ??\r\nE8 ?? ?? ?? ??\r\n8B 55 ??\r\n8A 4D ??\r\n80 E1 03\r\nC1 E1 06\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 15 of 16\n\n8A 5D ??\r\n80 E3 3F\r\n02 CB\r\n88 4C 10 ??\r\nFF 45 ??\r\n}\r\ncondition:\r\nany of them\r\n}\r\nReferencesPermalink\r\nDarkGate Final Payload Extractor\r\nDarkGate Strings Decoder\r\nDarkGate Decoded Strings\r\nDarkGate Network Traffic Decryptor\r\nFortinet Blog About DarkGate\r\nDarkGate Selling Thread On xss.is\r\nTriage Scan\r\nSource: https://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nhttps://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/\r\nPage 16 of 16",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://0xtoxin.github.io/threat%20breakdown/DarkGate-Camapign-Analysis/"
	],
	"report_names": [
		"DarkGate-Camapign-Analysis"
	],
	"threat_actors": [],
	"ts_created_at": 1775434087,
	"ts_updated_at": 1775826767,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/5c31c59315e4b7c2b62ab683b0d63d833bf89fe7.pdf",
		"text": "https://archive.orkl.eu/5c31c59315e4b7c2b62ab683b0d63d833bf89fe7.txt",
		"img": "https://archive.orkl.eu/5c31c59315e4b7c2b62ab683b0d63d833bf89fe7.jpg"
	}
}