{
	"id": "3493b0e9-7936-43bb-a4e3-9a8cae630062",
	"created_at": "2026-04-06T00:07:12.182328Z",
	"updated_at": "2026-04-10T13:12:28.303743Z",
	"deleted_at": null,
	"sha1_hash": "7e4a72f28776b3f2ad843bfdf1a81bd920e6b320",
	"title": "Brute Ratel Config Decoding update",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 729675,
	"plain_text": "Brute Ratel Config Decoding update\r\nBy Jason Reaves\r\nPublished: 2022-10-25 · Archived: 2026-04-05 19:23:28 UTC\r\nBy: Jason Reaves\r\nThere have been a few reports on how to decrypt Brute Ratels[1] configuration data along with a few decryptors\r\ncreated[2,3]. However, the developer added in the release notes that they changed it to be a dynamic key instead\r\nof the hardcoded key everyone refers to. The hardcoded key is still used and exists for decrypting some of the\r\nstrings on board.\r\nPress enter or click to view image in full size\r\nhttps://medium.com/walmartglobaltech/brute-ratel-config-decoding-update-7820455022cb\r\nPage 1 of 8\n\nRef: https://bruteratel.com/release_notes/releases.txt\r\nWe start with a sample from a TrendMicro report on BlackBasta actors leveraging QBot to deliver Brute Ratel and\r\nCobaltStrike:\r\n62cb24967c6ce18d35d2a23ebed4217889d796cf7799d9075c1aa7752b8d3967\r\nThe shellcode-based loader is stored onboard and is loaded into memory. The shellcode stager uses a few Anti\r\nDebugging checks such as checking the NtGlobalFlag.\r\nThe encoded onboard DLL is still stored RC4 encrypted as mentioned in the MDSec blog[3] the key is the last 8\r\nbytes:\r\nhttps://medium.com/walmartglobaltech/brute-ratel-config-decoding-update-7820455022cb\r\nPage 2 of 8\n\nRC4\r\nManually decoding:\r\n\u003e\u003e\u003e data[-8:]\r\n'*%@{.de|'\r\n\u003e\u003e\u003e rc4 = ARC4.new(data[-8:])\r\n\u003e\u003e\u003e t = rc4.decrypt(data)\r\n\u003e\u003e\u003e t[:1000]\r\n'zn\u003cdq{f%\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\r\n...snip...\r\nhttps://medium.com/walmartglobaltech/brute-ratel-config-decoding-update-7820455022cb\r\nPage 3 of 8\n\nAs we previously mentioned, the RC4 key for the config is no longer the hardcoded value in the DLL. Instead, it\r\nis now the last 8 bytes from the decoded DLL blob:\r\n\u003e\u003e\u003e a = base64.b64decode('FE2frlPu/3cYTkUYWP9aoUwTUKZ778EWaz5b2nzDTz2OAR2qI5Jvqozn6a2BTADp7kUTrsTI6ss\r\n\u003e\u003e\u003e rc4 = ARC4.new('\\x24\\x7b\\x29\\x75\\x5e\\x2f\\x2e\\x70')\r\n\u003e\u003e\u003e rc4.decrypt(a)\r\n'0|5|5|||||eyJjaGFubmVsIjoi|In0=|0|1|symantecuptimehost.com|8080|Mozilla/5.0 (Windows NT 10.0; Win64\r\nSo, if we wanted to automate, we need to account for two methods I’ve seen being used for loading the config and\r\nDLL data by the shellcode layer.\r\nGet Jason Reaves’s stories in your inbox\r\nJoin Medium for free to get updates from this writer.\r\nRemember me for faster sign in\r\nThe call over method which calls over the relevant data causing it’s address to be pushed onto the stack:\r\nPress enter or click to view image in full size\r\nCall over method\r\nAlso the stack load method where chunks of the data are pushed onto the stack causing it to be rebuilt:\r\nhttps://medium.com/walmartglobaltech/brute-ratel-config-decoding-update-7820455022cb\r\nPage 4 of 8\n\nStack load method\r\nFor the call over method, we just look for the instructions leading to the call and then pull out the data. I’ll be\r\nusing a naive method, but I would recommend switching the code to using YARA as your decoder will last much\r\nlonger.\r\ncfg_off = blob.find('\\x5a\\xe8\\x00\\x00\\x00\\x00\\x59\\x48\\x01\\xd1\\x48\\x83\\xc1\\x0a\\xff\\xd1')\r\ncfg_len = struct.unpack_from('\u003cI', blob[cfg_off-4:])[0]\r\ncfg_off += 16\r\ncfg = blob[cfg_off:cfg_off+cfg_len]\r\nFor finding the data in this scenario, we use a similar approach by just finding the call instruction sequence and\r\npulling out the length while we are there:\r\nif cfg != '':#Few ways to find the end\r\n #way1\r\n off1 = blob.find('\\x41\\x59\\xe8\\x00\\x00\\x00\\x00\\x41\\x58')\r\n l = struct.unpack_from('\u003cI', blob[off1-4:])[0]\r\n bb = blob[off1+19:]\r\n bb = bb[:l]\r\nDecoding the config, then just involves first decrypting the DLL and recovering the key:\r\n rc4 = ARC4.new(bb[-8:])\r\n decoded = rc4.decrypt(bb[:-8]) rc4 = ARC4.new(decoded[-8:])\r\n decoded_cfg = rc4.decrypt(base64.b64decode(cfg)) print(decoded_cfg)\r\nhttps://medium.com/walmartglobaltech/brute-ratel-config-decoding-update-7820455022cb\r\nPage 5 of 8\n\nFor the stack-based loading, I will be using the Unicorn[5] emulator which I’ve used for decoding data out of\r\nprevious malware samples. First, we need the config data:\r\nelse:\r\n #need to pull from stack\r\n offset = data.find(needle) blob = data[offset:] STACK=0x90000\r\n code_base = 0x10000000\r\n mu = Uc(UC_ARCH_X86,UC_MODE_64) test = re.findall(r'''4883e4f04831c050.+4889e168''',binascii.h\r\n temp = [test[0][:-2]]\r\n mu.mem_map(code_base, 0x100000) mu.mem_map(STACK, 4096*10)\r\n for i in range(len(temp)):\r\n #print(temp[i])\r\n try:\r\n blob = binascii.unhexlify(temp[i])\r\n except:\r\n blob = binascii.unhexlify(temp[i][1:])\r\n mu.mem_write(code_base, '\\x00'*0x100000)\r\n mu.mem_write(STACK, '\\x00'*(4096*10)) mu.mem_write(code_base,blob)\r\n mu.reg_write(UC_X86_REG_ESP,STACK+4096)\r\n mu.reg_write(UC_X86_REG_EBP,STACK+4096)\r\n try:\r\n mu.emu_start(code_base, code_base+len(blob), timeout=10000)\r\n except:\r\n pass\r\n a = mu.mem_read(STACK,4096*10)\r\n b = a.rstrip('\\x00')\r\n b = b.lstrip('\\x00')\r\n cfg = str(b)\r\nFor the data, we just need to account for a larger stack size:\r\n mu = Uc(UC_ARCH_X86,UC_MODE_64)#045e95f1a5bcc1ce2eeb905ab1c5f440a42364a170008309faef1cfdba296644\r\n test = re.findall(r'''00005a4[89].+4989e068''',binascii.hexlify(blob))\r\n if len(test) \u003e 0:\r\n temp = [test[0][6:-2]]\r\n mu.mem_map(code_base, 0x100000) mu.mem_map(STACK, 4096*200)\r\n for i in range(len(temp)):\r\n try:\r\n blob = binascii.unhexlify(temp[i])\r\n except:\r\n blob = binascii.unhexlify(temp[i][1:])\r\n mu.mem_write(code_base, '\\x00'*0x100000)\r\n mu.mem_write(STACK, '\\x00'*(4096*200)) mu.mem_write(code_base,blob)\r\n mu.reg_write(UC_X86_REG_ESP,STACK+(4096*100))\r\n mu.reg_write(UC_X86_REG_EBP,STACK+(4096))\r\n mu.emu_start(code_base, code_base+len(blob), timeout=100000)\r\nhttps://medium.com/walmartglobaltech/brute-ratel-config-decoding-update-7820455022cb\r\nPage 6 of 8\n\na = mu.mem_read(STACK,4096*200) b = a.rstrip('\\x00')\r\n b = b.lstrip('\\x00')\r\n b = str(b)\r\nDecoding the config is then the same process of first decrypting the DLL:\r\n rc4 = ARC4.new(b[-8:])\r\n t = rc4.decrypt(b[:-8]) rc4 = ARC4.new(t[-8:]) decoded_cfg = rc4.decrypt(base64\r\n print(decoded_cfg)\r\nWhile enumerating samples off VirusTotal, we also discovered what looks more like a stager version:\r\nd79f991d424af636cd6ce69f33347ae6fa15c6b4079ae46e9f9f6cfa25b09bb0\r\nThis version just loads a bytecode blob onto the stack:\r\nStager like version\r\nThe decoding of the bytecode config is once again just the last 8 bytes as an RC4 key:\r\n|{\"channel\":\"|\"}|1|login.offices365.de|443|Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537\r\nIOCs\r\nhttps://medium.com/walmartglobaltech/brute-ratel-config-decoding-update-7820455022cb\r\nPage 7 of 8\n\nsymantecuptimehost.com\r\nlogin.offices365.de\r\nReferences\r\n1: https://bruteratel.com/\r\n2: https://github.com/Immersive-Labs-Sec/BruteRatel-DetectionTools/blob/main/ConfigDecoder.py\r\n3: https://www.mdsec.co.uk/2022/08/part-3-how-i-met-your-beacon-brute-ratel/\r\n4: https://www.trendmicro.com/en_us/research/22/j/black-basta-infiltrates-networks-via-qakbot-brute-ratel-and-coba.html\r\n5: https://www.unicorn-engine.org/\r\nSource: https://medium.com/walmartglobaltech/brute-ratel-config-decoding-update-7820455022cb\r\nhttps://medium.com/walmartglobaltech/brute-ratel-config-decoding-update-7820455022cb\r\nPage 8 of 8",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://medium.com/walmartglobaltech/brute-ratel-config-decoding-update-7820455022cb"
	],
	"report_names": [
		"brute-ratel-config-decoding-update-7820455022cb"
	],
	"threat_actors": [],
	"ts_created_at": 1775434032,
	"ts_updated_at": 1775826748,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/7e4a72f28776b3f2ad843bfdf1a81bd920e6b320.pdf",
		"text": "https://archive.orkl.eu/7e4a72f28776b3f2ad843bfdf1a81bd920e6b320.txt",
		"img": "https://archive.orkl.eu/7e4a72f28776b3f2ad843bfdf1a81bd920e6b320.jpg"
	}
}