{
	"id": "e2df2505-bb08-46e2-8796-08c3550ec2b8",
	"created_at": "2026-04-06T00:18:54.58167Z",
	"updated_at": "2026-04-10T03:20:47.220673Z",
	"deleted_at": null,
	"sha1_hash": "ddb784b7941881df2ff43c64091bab320eb577fd",
	"title": "Open the DARKGATE – Brute Forcing DARKGATE Encodings",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1036227,
	"plain_text": "Open the DARKGATE – Brute Forcing DARKGATE Encodings\r\nBy Sean Straw\r\nPublished: 2024-01-18 · Archived: 2026-04-05 17:29:29 UTC\r\nKey Takeaways\r\nKroll has observed a recent shift in the base64 encoding used for DARKGATE.\r\nThe base64 alphabet is now randomized based on characteristics of the victim system.\r\nA weakness in the seed value randomness makes the new alphabet trivial to brute force.\r\nThe discovered alphabet can be used to decode the on-disk configuration and keylogging outputs.\r\nThe keylogger output files contain the keystrokes stolen by DARKGATE. Examiners can analyze these\r\nfiles to identify potentially stolen passwords or other at-risk information.\r\nSummary\r\nDARKGATE is Windows-based malware that is sold on the dark web. DARKGATE is a fully functional backdoor\r\nthat can steal browser information, drop additional payloads, and steal keystrokes. Kroll previously noted\r\nDARKGATE’s distribution via Teams.\r\nWhen the DARKGATE payload runs on a victim system, it creates a randomly named folder within\r\nC:\\ProgramData that contains encoded files. Within the randomly named folder is a short configuration file and\r\nthe output of keystrokes logged on the system. Both the keylogging output and the configuration files are encoded\r\nusing a custom base64 alphabet. In previous versions, the encoding used a hardcoded, nonstandard alphabet.\r\nKroll recently analyzed newer versions of DARKGATE, specifically 5.2.3, which randomly shuffles the\r\nnonstandard alphabet in use. Kroll identified a weakness in this shuffling that trivializes guessing the correct\r\nalphabet for a system without needing the hardware ID. \r\nThis analysis enables forensic analysts to decode the configuration and keylogger files without needing to first\r\ndetermine the hardware ID. The keylogger output files contain keystrokes stolen by DARKGATE, which can\r\ninclude typed passwords, composed emails and other sensitive information. \r\nDARKGATE’s Created Files\r\nBelow is an example folder structure created by DARKGATE:\r\n.\\ProgramData\\hgehakb\\Autoit3.exe (Legitimate AutoIT executable)\r\n.\\ProgramData\\hgehakb\\abbhebe.au3 (Loader script)\r\n.\\ProgramData\\hgehakb\\bebdbhk\\08-12-2023.log (Encoded keylogger captures)\r\n.\\ProgramData\\hgehakb\\bebdbhk\\cffhbdd (Encoded stored configuration)\r\nDARKGATE’s Hardware ID Generation\r\nhttps://www.kroll.com/en/insights/publications/cyber/brute-forcing-darkgate-encodings\r\nPage 1 of 8\n\nWhen DARKGATE runs, it generates a unique hardware ID. This value is comprised of several concatenated\r\nsystem attributes:\r\nThe Windows version\r\nThe product ID\r\nThe processor name and number of cores\r\nThe username\r\nThe system hostname\r\nThese values are combined and hashed using MD5. Rather than using the standard hexadecimal representation for\r\nan MD5 hash, DARKGATE uses a substituted alphabet consisting of abcdefKhABCDEFGH, as illustrated in\r\nFigure 1. \r\n \r\nFigure 1.  Generation of the bot ID\r\nThe DARKGATE Shuffle\r\nThe version of DARKGATE that was analyzed shuffles the base64 alphabet in use at the initialization of the\r\nprogram. DARKGATE swaps the last character with a random character before it, moving from back to front in\r\nthe alphabet. The shuffling occurs as follows:\r\nThe hardware ID is used to create a seed value for a pseudorandom number generator. The seed value is\r\ngenerated by adding each byte of the 32-byte ID together. This becomes the seed value.\r\nA counter starting at the last digit of the hardcoded base64 alphabet is set.\r\nThe pseudorandom number generator multiplies the seed by 0x8088405 and adds 1, storing this as the new\r\nSRAND value.\r\nhttps://www.kroll.com/en/insights/publications/cyber/brute-forcing-darkgate-encodings\r\nPage 2 of 8\n\nThe new seed value is multiplied by the length of the counter and shifted 32 bits right. This effectively\r\nchooses a pseudorandom number between 0 and the counter (non-inclusive).\r\nThe alphabet character at the randomly chosen number and the counter are swapped.\r\nThe counter is decremented. Execution then returns to step 3 until the counter reaches 0.\r\nAs base64 has an alphabet length of 64, this leaves 64!, or roughly 1.2 × 10^89, possible alphabets.\r\nThe alphabet shuffling is straightforward, as shown in Figure 2.\r\n \r\nFigure 2. The DARKGATE alphabet shuffle implementation\r\nThe “hash” of the bytes occurs right before the alphabet shuffling (see Figure 3).\r\nhttps://www.kroll.com/en/insights/publications/cyber/brute-forcing-darkgate-encodings\r\nPage 3 of 8\n\nFigure 3. Summing the hardware ID bytes just before shuffling the alphabet\r\nWeak Seed Generation\r\nThe flaw in the DARKGATE alphabet shuffle exists within the use of the sum of the hardware ID as a seed. As\r\ndiscussed previously, the DARKGATE hardware ID is a 32-byte ascii representation of an MD5 sum. MD5 is a\r\ncryptographic hash, and the distribution of each byte is random. These bytes are then summed.\r\nDespite the randomness of the hardware ID, there are specific lowest and highest possible byte values. In the\r\nsample Kroll analyzed, the lowest byte value in the hardware ID is A, or 0x41. The highest possible value is h, or\r\n0x68. These represent the lowest and highest value ascii characters in the MD5 alphabet.\r\nTherefore, the lowest possible seed value is 0x20 * 0x41 (32 A’s), or 0x820 (2080 in decimal), while the highest\r\npossible seed value is 0x20 * 0x68 (32 h’s), or 0xD00 (3328 in decimal). This leaves us with a total of 0xD00 -\r\n0x820 = 0x4E0 (1248 in decimal) possible custom base64 alphabets—very easy to brute force!\r\nPutting It into Practice\r\nThe DARKGATE shuffle can be reimplemented in Python. Then, by iterating through all possible combinations,\r\nbase64 decoding of each alphabet can be attempted to look for values that decode properly.\r\ndef shuffle(alphabet: str, seed: int) -\u003e str:\r\n  \"\"\"Python version of the DARKGATE shuffle\"\"\"\r\n  alphabet = list(alphabet)\r\n  for i in range(len(alphabet), 0, -1):\r\n    seed = (seed * 0x8088405 + 1) \u0026 0xFFFFFFFF\r\n    rand_val = (i * seed) \u003e\u003e 32\r\n    alphabet[rand_val], alphabet[i-1] = alphabet[i-1], alphabet[rand_val]\r\n  return ''.join(alphabet)\r\nWhile Python’s base64 library does not include an option to use a nonstandard alphabet, use of the translate\r\nfunction simplifies the decoding. This precludes the need to reimplement the base64 encoding algorithm.\r\nb64decode(ciphertext.translate(str.maketrans(custom_alphabet, BASE64_ALPHABET)))\r\nhttps://www.kroll.com/en/insights/publications/cyber/brute-forcing-darkgate-encodings\r\nPage 4 of 8\n\nThe output shows the decoded alphabet.\r\n$ py .\\darkgate_decode.py .\\encoded_config\r\nMIN_SEED: 2080\r\nMAX_SEED: 3328\r\n--\u003eAlphabet:\r\n82k+YQg3xdZJbhK40oOH5nqsUp6XC1fmM7EvRFISPtiATlzLjruGaWwDyB9=NeVc\r\n--\u003eDecoded:\r\ndomains=GkPdpxZB35LtSI9HV0WXS8PtSIcjGmcX34WBSedf\r\nepoch=1701885746\r\npuerto=2351\r\nversion=5.2.3\r\nhwid=GBGChDDffdHDedHHAAhBdbahEHAcHBaC\r\nThe alphabet can then be used to decode .log files using a tool such as CyberChef, see Figure 4. As some of the\r\nlogged keystrokes can end up as nonstandard characters, using the provided Python script is not recommended.\r\nhttps://www.kroll.com/en/insights/publications/cyber/brute-forcing-darkgate-encodings\r\nPage 5 of 8\n\nFigure 4. Decoding an example keylogger output in CyberChef\r\nWithin the keylogger output shown above, “encwindow” bookends the hex-encoded Window name of the program\r\nfrom which the keystroke was logged. In the above example, the windows are the OpenAI authentication link,\r\n“ChatGPT – Google Chrome” and “Untitled – Message (HTML).”\r\nScript\r\nBelow is a script to brute force DARKGATE configuration files. Note that different samples may have different\r\nhardcoded base64 alphabets or MD5 alphabets. If so, the corresponding values should be modified.\r\nhttps://www.kroll.com/en/insights/publications/cyber/brute-forcing-darkgate-encodings\r\nPage 6 of 8\n\n\"\"\"darkgate_decode.py\r\nAuthor: sean.straw@kroll.com\r\nDecode a DARKGATE configuration file, identifying the alphabet in use.\r\nRecommended this be run against a configuration file.\r\nUse CyberChef or a similar tool to decode .log files with the identified alphabet.\r\nDepending on the DARKGATE sample, you may need to modify the HWID alphabet.\r\nYou may also need to modify the hardcoded base64 alphabet.\r\nUsage:\r\n  py darkgate_decode.py \u003cfile\u003e\r\nExample:\r\n  $ py darkgate_decode.py .\\bekaeae\r\n  py .\\decode.py .\\encoded_config\r\n  MIN_SEED: 2080\r\n  MAX_SEED: 3328\r\n  --\u003eAlphabet:\r\n  82k+YQg3xdZJbhK40oOH5nqsUp6XC1fmM7EvRFISPtiATlzLjruGaWwDyB9=NeVc\r\n  --\u003eDecoded:\r\n  domains=GkPdpxZB35LtSI9HV0WXS8PtSIcjGmcX34WBSedf\r\n  epoch=1701885746\r\n  puerto=2351\r\n  version=5.2.3\r\n  hwid=GBGChDDffdHDedHHAAhBdbahEHAcHBaC\r\nThe \"domains\" value is encoded with the hardcoded base64 alphabet, NOT the shuffled one.\r\n\"\"\"\r\nfrom typing import List, Tuple\r\nimport base64\r\nimport sys\r\nfrom pathlib import Path\r\nORIGINAL_ALPHABET = r'zLAxuU0kQKf3sWE7ePRO2imyg9GSpVoYC6rhlX48ZHnvjJDBNFtMd1I5acwbqT+='\r\nBASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\r\ndef shuffle(alphabet: str, seed: int) -\u003e str:\r\n  \"\"\"Python version of the DARKGATE shuffle\"\"\"\r\n  alphabet = list(alphabet)\r\n  for i in range(len(alphabet), 0, -1):\r\n    seed = (seed * 0x8088405 + 1) \u0026 0xFFFFFFFF\r\n    rand_val = (i * seed) \u003e\u003e 32\r\n    alphabet[rand_val], alphabet[i-1] = alphabet[i-1], alphabet[rand_val]\r\n  return ''.join(alphabet)\r\nHWID_ALPHABET = [ord(x) for x in \"abcdefKhABCDEFGH\"]\r\nMIN_SEED = min(HWID_ALPHABET) * 32\r\nMAX_SEED = max(HWID_ALPHABET) * 32\r\nprint(f\"MIN_SEED: {MIN_SEED}\")\r\nprint(f\"MAX_SEED: {MAX_SEED}\")\r\nALL_ALPHABETS = [shuffle(ORIGINAL_ALPHABET, i) for i in range(MIN_SEED, MAX_SEED+1)]\r\nALL_ALPHABETS.append(ORIGINAL_ALPHABET)\r\nhttps://www.kroll.com/en/insights/publications/cyber/brute-forcing-darkgate-encodings\r\nPage 7 of 8\n\ndef force(payload: str) -\u003e List[Tuple[str, str]]:\r\n\"\"\"Iterate through and find all possible alphabet solutions\"\"\"\r\n  res = []\r\n  for alphabet in ALL_ALPHABETS:\r\n    t = payload.translate(str.maketrans(alphabet, BASE64_ALPHABET))\r\n    # Add extra padding. The base64 module will trim it if not needed\r\n    t += \"===\"\r\n    try:\r\n      t = base64.b64decode(t)\r\n    except base64.binascii.Error:\r\n      continue\r\n    try:\r\n      t = t.decode('utf-8')\r\n    except ValueError:\r\n      continue\r\n    else:\r\n      res.append((alphabet, t))\r\n  return res\r\nencoded_text = Path(sys.argv[1]).read_text(encoding='ascii')\r\npossible_alphabets = force(encoded_text)\r\nif len(possible_alphabets) \u003c 1:\r\n  print(\"Err: no alphabets found\")\r\nelse:\r\n  for (alphabet_, decoded) in possible_alphabets:\r\n    print(\"--\u003eAlphabet:\")\r\n    print(alphabet_)\r\n    print(\"--\u003eDecoded:\")\r\n    print(decoded)\r\nSource: https://www.kroll.com/en/insights/publications/cyber/brute-forcing-darkgate-encodings\r\nhttps://www.kroll.com/en/insights/publications/cyber/brute-forcing-darkgate-encodings\r\nPage 8 of 8",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://www.kroll.com/en/insights/publications/cyber/brute-forcing-darkgate-encodings"
	],
	"report_names": [
		"brute-forcing-darkgate-encodings"
	],
	"threat_actors": [],
	"ts_created_at": 1775434734,
	"ts_updated_at": 1775791247,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/ddb784b7941881df2ff43c64091bab320eb577fd.pdf",
		"text": "https://archive.orkl.eu/ddb784b7941881df2ff43c64091bab320eb577fd.txt",
		"img": "https://archive.orkl.eu/ddb784b7941881df2ff43c64091bab320eb577fd.jpg"
	}
}