{
	"id": "1dda75b8-863a-42d3-ab04-f9db7f69bc73",
	"created_at": "2026-04-06T00:12:19.750736Z",
	"updated_at": "2026-04-10T03:24:24.222876Z",
	"deleted_at": null,
	"sha1_hash": "8580b048d360cb93f0e029e00343e391a0e96291",
	"title": "Cobalt Strike: Using Process Memory To Decrypt Traffic – Part 3",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 168074,
	"plain_text": "Cobalt Strike: Using Process Memory To Decrypt Traffic – Part 3\r\nBy Didier Stevens\r\nPublished: 2021-11-03 · Archived: 2026-04-05 22:42:50 UTC\r\nWe decrypt Cobalt Strike traffic with cryptographic keys extracted from process memory.\r\nThis series of blog posts describes different methods to decrypt Cobalt Strike traffic. In part 1 of this series, we revealed\r\nprivate encryption keys found in rogue Cobalt Strike packages. And in part 2, we decrypted Cobalt Strike traffic starting\r\nwith a private RSA key. In this blog post, we will explain how to decrypt Cobalt Strike traffic if you don’t know the private\r\nRSA key but do have a process memory dump.\r\nCobalt Strike network traffic can be decrypted with the proper AES and HMAC keys. In part 2, we obtained these keys by\r\ndecrypting the metadata with the private RSA key. Another way to obtain the AES and HMAC key, is to extract them from\r\nthe process memory of an active beacon.\r\nOne method to produce a process memory dump of a running beacon, is to use Sysinternals’ tool procdump. A full process\r\nmemory dump is not required, a dump of all writable process memory is sufficient.\r\nExample of a command to produce a process dump of writable process memory: “procdump.exe -mp 1234”, where -mp is\r\nthe option to dump writable process memory and 1234 is the process ID of the running beacon. The process dump is stored\r\ninside a file with extension .dmp.\r\nFor Cobalt Strike version 3 beacons, the unencrypted metadata can often be found in memory by searching for byte\r\nsequence 0x0000BEEF. This sequence is the header of the unencrypted metadata. The earlier in the lifespan of a process the\r\nprocess dump is taken, the more likely it is to contain the unencrypted metadata.\r\nFigure 1: binary editor view of metadata in process memory\r\nTool cs-extract-key.py can be used to find and decode this metadata, like this:\r\nhttps://blog.nviso.eu/2021/11/03/cobalt-strike-using-process-memory-to-decrypt-traffic-part-3/\r\nPage 1 of 4\n\nFigure 2: extracted and decoded metadata\r\nThe metadata contains the raw key: 16 random bytes. The AES and HMAC keys are derived from this raw key by\r\ncalculating the SHA256 value of the raw key. The first half of the SHA256 value is the HMAC key, and the second half is\r\nthe AES key.\r\nThese keys can then be used to decrypt the captured network traffic with tool cs-parse-http-traffic.py, like explained in Part\r\n2.\r\nRemark that tool cs-extract-key.py is likely to produce false positives: namely byte sequences that start with 0x0000BEEF,\r\nbut are not actual metadata. This is the case for the example in figure 2: the first instance is indeed valid metadata, as it\r\ncontains a recognizable machine name and username (look at Field: entries). And the AES and HMAC key extracted from\r\nthat metadata, have also been found at other positions in process memory. But that is not the case for the second instance (no\r\nrecognizable names, no AES and HMAC keys found at other locations). And thus that is a false positive that must be\r\nignored.\r\nFor Cobalt Strike version 4 beacons, it is very rare that the unencrypted metadata can be recovered from process memory.\r\nFor these beacons, another method can be followed. The AES and HMAC keys can be found in writable process memory,\r\nbut there is no header that clearly identifies these keys. They are just 16-byte long sequences, without any distinguishable\r\nfeatures. To extract these keys, the method consists of performing a kind of dictionary attack. All possible 16-byte long, non-null sequences found in process memory, will be used to try to decrypt a piece of encrypted C2 communication. If the\r\ndecryption succeeds, a valid key has been found.\r\nThis method does require a process memory dump and encrypted data.\r\nThis encrypted data can be extracted using tool cs-parse-http-traffic.py like this: cs-parse-http-traffic.py -k unknown\r\ncapture.pcapng\r\nWith an unknown key (-k unknown), the tool will extract the encrypted data from the capture file, like this:\r\nhttps://blog.nviso.eu/2021/11/03/cobalt-strike-using-process-memory-to-decrypt-traffic-part-3/\r\nPage 2 of 4\n\nFigure 3: extracting encrypted data from a capture file\r\nPacket 103 is an HTTP response to a GET request (packet 97). The encrypted data of this response is 64 bytes long:\r\nd12c14aa698a6b85a8ed3c3c33774fe79acadd0e95fa88f45b66d8751682db734472b2c9c874ccc70afa426fb2f510654df7042aa7d2384229518f\r\nThis is encrypted data, sent by the team server to the beacon: it contains tasks to be executed by the beacon (remark that in\r\nthese examples, we look at encrypted traffic that has not been transformed, we will cover traffic transformed by malleable\r\ninstructions in an upcoming blog post).\r\nWe can attempt to decrypt this data by providing tool cs-extract-key.py with the encrypted task (option -t) and the process\r\nmemory dump: cs-extract-key.py -t\r\nd12c14aa698a6b85a8ed3c3c33774fe79acadd0e95fa88f45b66d8751682db734472b2c9c874ccc70afa426fb2f510654df7042aa7d2384229518f\r\nrundll32.exe_211028_205047.dmp.\r\nFigure 4: extracting AES and HMAC keys from process memory\r\nThe recovered AES and HMAC key can then be used to decrypt the traffic (-k HMACkey:AESkey):\r\nhttps://blog.nviso.eu/2021/11/03/cobalt-strike-using-process-memory-to-decrypt-traffic-part-3/\r\nPage 3 of 4\n\nFigure 5: decrypting traffic with HMAC and AES key provided via option -k\r\nThe decrypted tasks seen in figure 5, are “data jitter”. Data jitter is a Cobalt Strike option, that sends random data to the\r\nbeacon (random data that is ignored by the beacon). With the default Cobalt Strike beacon profile, no random data is sent,\r\nand data is not transformed using malleable instructions. This means that with such a beacon profile, no data is sent to the\r\nbeacon as long as there are no tasks to be performed by the beacon: the Content-length of the HTTP reply is 0.\r\nSince the absence of tasks results in no encrypted data being transmitted, it is quite easy to determine if a beacon received\r\ntasks or not, even when the traffic is encrypted. An absence of (encrypted) data means that no tasks were sent. To obfuscate\r\nthis absence of commands (tasks), Cobalt Strike can be configured to exchange random data, making each packet unique.\r\nBut in this particular case, that random data is useful to blue teamers: it permits us to recover the cryptographic keys from\r\nprocess memory. If no random data would be sent, nor actual tasks, we would never see encrypted data and thus we would\r\nnot be able to identify the cryptographic keys inside process memory.\r\nData sent by the beacon to the team server contains the results of the tasks executed by the beacon. This data is sent with a\r\nPOST request (default), and is known as a callback. This data too can be used to find decryption keys. In that case, the\r\nprocess is the same as shown above, but the option to use is -c (callback) in stead of -t (tasks). The reason the options are\r\ndifferent, is that the way the data is encrypted by the team server is slightly different from the way the data is encrypted by\r\nthe beacon, and the tool must be told which way to encrypt the data was used.\r\nSome considerations regarding process memory dumps\r\nFor a process memory dump of maximum 10MB, the “dictionary” attack will take a couple of minutes.\r\nFull process dumps can be used too, but the dictionary attack can take much longer because of the larger size of the dump.\r\nTool cs-extract-key.py reads the process memory dump as a flat file, and thus a larger file means more processing to be\r\ndone.\r\nHowever, we are working on a tool that can parse the data structure of a dump file and extract / decode memory sections that\r\nare most likely to contain keys, thus speeding up the key recovery process.\r\nRemark that beacons can be configured to encode their writable memory while they are not active (sleeping): in such cases,\r\nthe AES and HMAC keys are encoded too, and can not be recovered using the methods described here. The dump parsing\r\ntool we are working on will handle this situation too.\r\nFinally, if the method explained here for version 3 beacons does not work with your particular memory dump, try the\r\nmethod for version 4 beacons. This method works also for version 3 beacons.\r\nConclusion\r\nCryptographic keys are required to decrypt Cobalt Strike traffic. The best situation is to have the corresponding private RSA\r\nkey. If that is not the case, HMAC and AES keys can be recovered using a process memory dump and capture file with\r\nencrypted traffic.\r\nAbout the authors\r\nDidier Stevens is a malware expert working for NVISO. Didier is a SANS Internet Storm Center senior handler and\r\nMicrosoft MVP, and has developed numerous popular tools to assist with malware analysis. You can find Didier\r\non Twitter and LinkedIn.\r\nYou can follow NVISO Labs on Twitter to stay up to date on all our future research and publications.\r\nSource: https://blog.nviso.eu/2021/11/03/cobalt-strike-using-process-memory-to-decrypt-traffic-part-3/\r\nhttps://blog.nviso.eu/2021/11/03/cobalt-strike-using-process-memory-to-decrypt-traffic-part-3/\r\nPage 4 of 4",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://blog.nviso.eu/2021/11/03/cobalt-strike-using-process-memory-to-decrypt-traffic-part-3/"
	],
	"report_names": [
		"cobalt-strike-using-process-memory-to-decrypt-traffic-part-3"
	],
	"threat_actors": [
		{
			"id": "610a7295-3139-4f34-8cec-b3da40add480",
			"created_at": "2023-01-06T13:46:38.608142Z",
			"updated_at": "2026-04-10T02:00:03.03764Z",
			"deleted_at": null,
			"main_name": "Cobalt",
			"aliases": [
				"Cobalt Group",
				"Cobalt Gang",
				"GOLD KINGSWOOD",
				"COBALT SPIDER",
				"G0080",
				"Mule Libra"
			],
			"source_name": "MISPGALAXY:Cobalt",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		}
	],
	"ts_created_at": 1775434339,
	"ts_updated_at": 1775791464,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/8580b048d360cb93f0e029e00343e391a0e96291.pdf",
		"text": "https://archive.orkl.eu/8580b048d360cb93f0e029e00343e391a0e96291.txt",
		"img": "https://archive.orkl.eu/8580b048d360cb93f0e029e00343e391a0e96291.jpg"
	}
}