{
	"id": "ad7b0c99-d8b5-4f2f-bb57-7be84882cab4",
	"created_at": "2026-04-06T00:06:58.283271Z",
	"updated_at": "2026-04-10T03:30:33.017582Z",
	"deleted_at": null,
	"sha1_hash": "45dabdb86a07560f4fc63acd42d782e2c64b2c48",
	"title": "New Robust Technique for Reliably Identifying AsyncRAT/DcRAT/VenomRAT Servers",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 59064,
	"plain_text": "New Robust Technique for Reliably Identifying\r\nAsyncRAT/DcRAT/VenomRAT Servers\r\nBy Axel Mahr\r\nPublished: 2024-04-20 · Archived: 2026-04-05 21:19:21 UTC\r\nLooking for indicators in order to detect C2 servers of the QuasarRAT family is nothing particularly new. Up to\r\nnow, two main approaches are already known:\r\nIt is trivial to identify QuasarRAT/AsyncRAT/DcRAT/VenomRAT when default TLS certificates are used.\r\nThen, we can look at the certificate’s Subject/Issuer CN which directly gives us the information we look\r\nfor. E.g., for AsyncRAT, Issuer CN and Subject CN are AsyncRAT Server . This approach has been\r\nexplained in various reports and blog posts.\r\nFingerprints like JA3S, JA4S/JA4X and JARM also allow for detecting these RATs.\r\nHowever, both approaches have their drawbacks. Certificate-based indicators don’t work anymore once the server\r\ndoesn’t use the default certificate. And using a non-default certificates is not a high burden at all. E.g., for\r\nAsyncRAT, a Subject and Issuer CN different from AsyncRAT Server is even definable via the GUI.\r\nFingerprinting approaches may also be not sufficient since it has been shown that especially JA3S and JARM are\r\nheavily prone to false positives.\r\nHence, I looked for more robust and accurate ways for detecting these RATs through a scan from outside. Since\r\nAsyncRAT and DcRAT are open-source on Github (see here and here), I could easily look at the source code,\r\ndeploy a C2 server locally and play around a bit. While exploring, I found some weirdly interesting behavior for\r\nAsyncRAT, DcRAT and VenomRAT:\r\nIt is possible to send valid C2 packets to the server as an unauthenticated client, which are then blindly processed\r\nby the server. By that, we can send custom packets which provoke a certain server response. The response reliably\r\nindicates us, which RAT we are facing.\r\nFor C2 communication, AsyncRAT, DcRAT and VenomRAT use a custom packet format inside TLSv1. Each\r\npacket begins with four bytes indicating the size of the following payload. The payload itself begins with again\r\nfour bytes followed by gzip-compressed data which is in turn serialized using MessagePack. The serialized data\r\nare key-value pairs where the value for the key Packet / Pac_ket indicates the message type. That’s it. No\r\nfurther client authentication or encryption (besides the TLS stuff) is done. Thus, exemplarily with the following\r\ncode, we can get easily detect AsyncRAT (replace IP and PORT as you like):\r\nimport socket\r\nimport ssl\r\nimport gzip\r\nimport msgpack # pip install msgpack-python\r\nfrom pwn import p32\r\nhttps://axmahr.github.io/posts/asyncrat-detection/\r\nPage 1 of 3\n\n# init socket for connecting to AsyncRAT server\r\nsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\r\ncontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1)\r\ncontext.set_ciphers('DEFAULT:@SECLEVEL=0')\r\ncontext.verify_mode = ssl.CERT_NONE\r\n# connect\r\nwrapped_socket = context.wrap_socket(sock)\r\nwrapped_socket.connect((IP, PORT))\r\n# craft valid C2 packet of message type \"Ping\"\r\n# (for detecting DcRAT/VenomRAT, just change b\"Packet\" to b\"Pac_ket\")\r\npayload = gzip.compress(msgpack.packb({b\"Packet\": b\"Ping\"}))\r\npayload_header = p32(len(payload))\r\npayload = payload_header + payload\r\nping_packet = p32(len(payload)) + payload\r\n# send our crafted packet\r\nwrapped_socket.send(ping_packet)\r\n# get and decode response\r\nresponse = b\"\"\r\nresponse += wrapped_socket.recv(1280)\r\nresponse += wrapped_socket.recv(1280)\r\nresponse += wrapped_socket.recv(1280)\r\nresponse += wrapped_socket.recv(1280)\r\nprint(\"raw response:\", response)\r\npayload_size = int.from_bytes(response[:4], \"little\")\r\nprint(\"payload size:\", payload_size)\r\ncropped_payload = response[8:]\r\nprint(\"cropped payload (compressed):\", cropped_payload)\r\npayload_uncompressed = gzip.decompress(cropped_payload)\r\nprint(\"cropped payload (decompressed):\", payload_uncompressed)\r\nprint(\"\\n... and unpacked with msgpack:\", msgpack.unpackb(payload_uncompressed))\r\nThis is the response we then get from an AsyncRAT server:\r\nraw response: b'%\\x00\\x00\\x00\\r\\x00\\x00\\x00\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x04\\x00k\\\\\\x16\\x90\\x98\\x9c\\x9dZ\\xb2\r\npayload size: 37\r\ncropped payload (compressed): b'\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x04\\x00k\\\\\\x16\\x90\\x98\\x9c\\x9dZ\\xb2\\xa4 ?/\\x1d\\\r\ncropped payload (decompressed): b'\\x81\\xa6Packet\\xa4pong'\r\n... and unpacked with msgpack: {b'Packet': b'pong'}\r\nhttps://axmahr.github.io/posts/asyncrat-detection/\r\nPage 2 of 3\n\nWe see, the server responds with a “Pong”-Packet and doesn’t care whether the initial “Ping” message came from\r\nan actual client beacon of an infected system or just some other random client. The same works for\r\nDcRAT/VenomRAT when we change {b\"Packet\": b\"Ping\"} to {b\"Pac_ket\": b\"Ping\"} in the code above.\r\nAlso, by sending such a ping message, we remain completely unnoticed on the server-side since nothing w.r.t the\r\nping message is logged there.\r\nNote that through this approach, it is possible to identify these RATs also when e.g. non-default TLS certificates\r\nare used. This is the point where the other detectors, which only look at the ceritificates, fail.\r\nBy the way, the ability of sending valid C2 packets which are then regardlessly processed by the server, is not\r\nlimited to just ping messages. Indeed, we can send arbitrary messages, as long as they are message types which\r\nare actually handled by the server. That means, depending on the available message types, we can cause some\r\nwild things happening on the server-side. And all that as a random client from outside.\r\nSending valid C2 packets that are blindly processed by the server is only possible for\r\nAsyncRAT/DcRAT/VenomRAT. QuasarRAT on the other side requires that the client is actually a valid client\r\nbeacon. Nevertheless there is also another possibility to identify QuasarRAT: A QuasarRAT server doesn’t begin\r\nprocessing a packet sent by a client until 4 or more bytes are received since a QuasarRAT packet (inside TLS)\r\nalways begins with 4 bytes indicating the size of the following payload. And when these 4 bytes don’t match the\r\nactual payload size, the server disconnects. Thus, a possible indicator for identifying a QuasarRAT server is to\r\nsend 3 bytes of data to the server. When the connection keeps established and is closed by the server once a fourth\r\nbyte is sent, the server might be QuasarRAT.\r\nI combined this approach with other fingerprint- and ceritificate-based indicators to an overall detection tool\r\nwhich identifies QuasarRAT, AsyncRAT, DcRAT and VenomRAT servers. You can find it here:\r\nhttps://github.com/axmahr/QuasarRAT-Family-Detection\r\nReferences\r\nC2 Intel Feeds\r\nA Beginner’s Guide to Tracking Malware Infrastructure\r\nAnalysis - Identification of 64 Quasar Servers Using Shodan and Censys\r\nThe State of SSL/TLS Certificate Usage in Malware C\u0026C Communications\r\nQuasarRAT on Github\r\nAsyncRAT on Github\r\nDcRAT on Github\r\nSource: https://axmahr.github.io/posts/asyncrat-detection/\r\nhttps://axmahr.github.io/posts/asyncrat-detection/\r\nPage 3 of 3",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://axmahr.github.io/posts/asyncrat-detection/"
	],
	"report_names": [
		"asyncrat-detection"
	],
	"threat_actors": [
		{
			"id": "75108fc1-7f6a-450e-b024-10284f3f62bb",
			"created_at": "2024-11-01T02:00:52.756877Z",
			"updated_at": "2026-04-10T02:00:05.273746Z",
			"deleted_at": null,
			"main_name": "Play",
			"aliases": null,
			"source_name": "MITRE:Play",
			"tools": [
				"Nltest",
				"AdFind",
				"PsExec",
				"Wevtutil",
				"Cobalt Strike",
				"Playcrypt",
				"Mimikatz"
			],
			"source_id": "MITRE",
			"reports": null
		}
	],
	"ts_created_at": 1775434018,
	"ts_updated_at": 1775791833,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/45dabdb86a07560f4fc63acd42d782e2c64b2c48.pdf",
		"text": "https://archive.orkl.eu/45dabdb86a07560f4fc63acd42d782e2c64b2c48.txt",
		"img": "https://archive.orkl.eu/45dabdb86a07560f4fc63acd42d782e2c64b2c48.jpg"
	}
}