{
	"id": "cff441eb-8572-4177-82bf-fc578428e9cb",
	"created_at": "2026-04-06T00:12:56.231783Z",
	"updated_at": "2026-04-10T13:11:53.771906Z",
	"deleted_at": null,
	"sha1_hash": "10d738bdab6de4db12e846d514689129d80ac009",
	"title": "Breaking EvilQuest | Reversing A Custom macOS Ransomware File Encryption Routine - SentinelLabs",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1185526,
	"plain_text": "Breaking EvilQuest | Reversing A Custom macOS Ransomware\r\nFile Encryption Routine - SentinelLabs\r\nBy Jason Reaves\r\nPublished: 2020-07-07 · Archived: 2026-04-05 14:18:57 UTC\r\nExecutive Summary\r\nA new macOS ransomware threat uses a custom file encryption routine\r\nThe routine appears to be partly based on RC2 rather than public key encryption\r\nSentinelLabs has released a public decryptor for use with “EvilQuest” encrypted files\r\nBackground\r\nResearchers recently uncovered a new macOS malware threat[1], initially dubbed ‘EvilQuest’ and later\r\n‘ThiefQuest'[2]. The malware exhibits multiple behaviors, including file encryption, data exfiltration and\r\nkeylogging[3].\r\nOf particular interest from a research perspective is the custom encryption routine. A cursory inspection of the\r\nmalware code suggests that it is not related to public key encryption. At least part of it uses a table normally\r\nassociated with RC2. The possible usage of RC2 and time-based seeds for file encryption led me to look deeper at\r\nthe code, which allowed me to understand how to break the malware’s encryption routine. As a result, our team\r\ncreated a decryptor for public use.\r\nUncarving the Encryption Routine\r\nAs mentioned in other reports[4], the function responsible for file encryption is labelled internally as\r\ncarve_target.\r\nBefore encrypting the file, the function checks whether the file is already encrypted by comparing the last 4 bytes\r\nof the file to a hardcoded DWORD value.\r\nhttps://labs.sentinelone.com/breaking-evilquest-reversing-a-custom-macos-ransomware-file-encryption-routine/\r\nPage 1 of 10\n\nIf the test fails, then file encryption begins by generating a 128 byte key and calling the tpcrypt function, which\r\nbasically ends up calling generate_xkey. This function is the key expansion portion followed by tp_encrypt,\r\nwhich takes the expanded key and uses it to encrypt the data.\r\nhttps://labs.sentinelone.com/breaking-evilquest-reversing-a-custom-macos-ransomware-file-encryption-routine/\r\nPage 2 of 10\n\nFollowing this, the key will then be encoded, using time as a seed. A DWORD value will be generated and\r\nutilized.\r\nThe encoding routine is simply a ROL-based XOR loop:\r\nAt this point, we can see that something interesting happens, and I am unsure if it is intentional by the developer\r\nor not. The key generated is 128 bytes, as we previously mentioned.\r\nhttps://labs.sentinelone.com/breaking-evilquest-reversing-a-custom-macos-ransomware-file-encryption-routine/\r\nPage 3 of 10\n\nThe calculations then used for encoding the key end up performing the loop 4 extra times, producing 132 bytes.\r\nThis means that the clear text key used for encoding the file encryption key ends up being appended to the\r\nencoded file encryption key. Taking a look at a completely encrypted file shows that a block of data has been\r\nappended to it.\r\nReversing the File Encryption\r\nhttps://labs.sentinelone.com/breaking-evilquest-reversing-a-custom-macos-ransomware-file-encryption-routine/\r\nPage 4 of 10\n\nFortunately, we don’t have to reverse that much as the actor has left the decryption function, uncarve_target, in\r\nthe code. This function takes two parameters: a file location and a seed value that will be used to decode the\r\nonboard file key.\r\nAfter checking if the file is an encrypted file by examining the last 4 bytes, the function begins reading a structure\r\nof data from the end of the file.\r\nhttps://labs.sentinelone.com/breaking-evilquest-reversing-a-custom-macos-ransomware-file-encryption-routine/\r\nPage 5 of 10\n\nFollowing the code execution, we can statically rebuild a version of what this structure might look like:\r\nstruct data\r\n{\r\nenc blob[size+12]\r\nlong long size\r\nint marker\r\n}\r\nstruct enc\r\n{\r\nlong long val\r\nint val2                   \r\nlong long val3               \r\nchar encoded_blob[4 - val % 4 + val]    \r\n}\r\nhttps://labs.sentinelone.com/breaking-evilquest-reversing-a-custom-macos-ransomware-file-encryption-routine/\r\nPage 6 of 10\n\nThe encoded file key will then be decrypted and checked using the two values from the structure and the other\r\nseed value passed to uncarve_target. The file key will be decrypted by eip_decrypt, which is the encrypt-in-place decrypt routine.\r\nThe function eip_key will take the two DWORD values and the seed argument to generate the XOR key to\r\ndecode the filekey.\r\nNext, the file is set to the beginning and then a temporary file is opened for writing.\r\nThe file is then read into an allocated buffer and the key and encoded file data are passed to tpdcrypt.\r\nhttps://labs.sentinelone.com/breaking-evilquest-reversing-a-custom-macos-ransomware-file-encryption-routine/\r\nPage 7 of 10\n\nAs before, we have a key expansion followed this time by a call to tp_decrypt.\r\nA glance inside the key expansion function shows a reference to a hardcoded table which matches RC2 code that\r\ncan be found online.\r\nhttps://labs.sentinelone.com/breaking-evilquest-reversing-a-custom-macos-ransomware-file-encryption-routine/\r\nPage 8 of 10\n\nSo now we have enough information to recover the file key:\r\nimport struct\r\nimport sys\r\nrol = lambda val, r_bits, max_bits=32:\r\n(val \u003c\u003c r_bits%max_bits) \u0026 (2**max_bits-1) | ((val \u0026 (2**max_bits-1)) \u003e\u003e (max_bits-(r_bits%max_bits\r\ndata = open(sys.argv[1], 'rb').read()\r\ntest = data[-4:]\r\nif test != 'xbexbaxbexdd':\r\n print(\"Unknown version\")\r\n sys.exit(-1)\r\nappend_length = struct.unpack_from('\u003cI', data[-12:])[0]\r\nappend_struct = data[-(append_length+12):]\r\nkeySize = struct.unpack_from('\u003cI', append_struct)[0]\r\nif keySize != 0x80:\r\n print(\"Weird key?\")\r\n sys.exit(-1)\r\nencoded_data = append_struct[20:20+132]\r\nxorkey = struct.unpack_from('\u003cI', encoded_data[-4:])[0]\r\nhttps://labs.sentinelone.com/breaking-evilquest-reversing-a-custom-macos-ransomware-file-encryption-routine/\r\nPage 9 of 10\n\ndef decode(blob, key):\r\n out = \"\"\r\n for i in range(len(blob)/4):\r\ntemp = struct.unpack_from('\u003cI', blob[i*4:])[0]\r\ntemp ^= key\r\nkey = rol(key, 1)\r\nout += struct.pack('\u003cI', temp)\r\n return out[:0x80]\r\ntemp = decode(encoded_data, xorkey)\r\nprint(temp)\r\nAttempting to RC2 decrypt the data, however, only seems to work partially at this time using RC2 routines in both\r\nPython and Golang libraries. Further analysis will be needed to verify what is different.\r\nHowever, for the purpose of decrypting victim files, we need only take the file key and call the tp_decrypt\r\nfunction that is located inside the malware itself instead. Dumping the assembly for this function and building it\r\ninto a shared object to be executed using the recovered file key appears to work correctly.\r\nUsing this method, SentinelLabs created a public decryptor which is available here (this tool is released under the\r\nMIT software license).\r\nSample\r\nSHA-1: 178b29ba691eea7f366a40771635dd57d8e8f7e8\r\nSHA-256: f409b059205d9a7700d45022dad179f889f18c58c7a284673975271f6af41794\r\nReferences\r\n1: https://twitter.com/dineshdina04/status/1277668001538433025\r\n2: https://www.bleepingcomputer.com/news/security/thiefquest-ransomware-is-a-file-stealing-mac-wiper-in-disguise/\r\n3: https://blog.malwarebytes.com/mac/2020/06/new-mac-ransomware-spreading-through-piracy/\r\n4: https://objective-see.com/blog/blog_0x59.html\r\nSource: https://labs.sentinelone.com/breaking-evilquest-reversing-a-custom-macos-ransomware-file-encryption-routine/\r\nhttps://labs.sentinelone.com/breaking-evilquest-reversing-a-custom-macos-ransomware-file-encryption-routine/\r\nPage 10 of 10",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://labs.sentinelone.com/breaking-evilquest-reversing-a-custom-macos-ransomware-file-encryption-routine/"
	],
	"report_names": [
		"breaking-evilquest-reversing-a-custom-macos-ransomware-file-encryption-routine"
	],
	"threat_actors": [],
	"ts_created_at": 1775434376,
	"ts_updated_at": 1775826713,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/10d738bdab6de4db12e846d514689129d80ac009.pdf",
		"text": "https://archive.orkl.eu/10d738bdab6de4db12e846d514689129d80ac009.txt",
		"img": "https://archive.orkl.eu/10d738bdab6de4db12e846d514689129d80ac009.jpg"
	}
}