{
	"id": "540e7dea-3037-4a3a-87d1-7a43154db001",
	"created_at": "2026-04-06T00:09:24.051599Z",
	"updated_at": "2026-04-10T13:12:10.034041Z",
	"deleted_at": null,
	"sha1_hash": "287395c229f032b857a726ce3c69f5bdc1a45a0f",
	"title": "When Threat Actors Fly Under the Radar: Vatet, PyXie and Defray777",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 846687,
	"plain_text": "When Threat Actors Fly Under the Radar: Vatet, PyXie and\r\nDefray777\r\nBy Ryan Tracey, Drew Schmitt\r\nPublished: 2020-11-07 · Archived: 2026-04-05 15:17:22 UTC\r\nNext Up: “PyXie Lite”\r\nAn earlier version of PyXie was previously covered in-depth by BlackBerry Cylance in December 2019. We will\r\nbe primarily covering an updated variant and some notable changes we have observed.\r\nSome of these changes include:\r\nHardened interpreter.\r\nNew remapped opcode table.\r\nRepurposed as a data theft and reconnaissance tool.\r\nExfiltration through internal servers.\r\nWe call this variant PyXie Lite because of the significantly smaller code base, but don’t let the name fool you. It\r\nstill packs a punch.\r\nLoader\r\nThe recent variant we analyzed was loaded by Vatet rather than the Goopdate.dll and LMIGuardianDll.dll side\r\nloaders seen with earlier versions of PyXie.\r\nThe decrypted Vatet payload contains the first stage of PyXie prepended by a shellcode loader responsible for\r\nmapping the first stage into memory and executing it.\r\nThe shellcode loader utilizes MurmurHash3 hashing to locate APIs needed during this process at runtime.\r\nDLL Function API Hash\r\nKernel32.dll GetProcAddress 0x261C88ED\r\nKernel32.dll VirtualAlloc 0xC17E7EB2\r\nKernel32.dll LoadLibraryExA 0x4B9B30B9\r\nTable 2. MurmurHash3 API hashes.\r\nStage 1\r\nThe purpose of the first stage is to decrypt the second stage payload and execute it in memory.\r\nhttps://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/\r\nPage 1 of 12\n\nMutex\r\nA mutex is created to prevent multiple instances from running at the same time. The following logic is used:\r\nRetrieve computer name with a call to GetComputerNameA. If that fails, fall back to\r\nDEFAULTCOMPNAME.\r\nCompute MD5 Hash of the computer name.\r\nXOR computed hash with 0x2.\r\nConvert the result to a string with StringFromGUID2.\r\nCreate mutex using the string with a call to CreateMutexW.\r\nString encryption\r\nSignificant strings are encrypted by a routine that increments each byte of the ciphertext by its index, masks the\r\nresult with 0x7F (highest value in the ASCII character set) and XORs it against a key of equal length.\r\nTable 3. String decryption example.\r\nDecrypted Strings\r\nuiAccess=true\"\r\n-q -s {%S} -p %u\r\nwerfault.exe\r\nvsjitdebugger.exe\r\nhttps://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/\r\nPage 2 of 12\n\ndvdplay.exe\r\nonedrivesetup.exe\r\nopenwith.exe\r\n%windir%\\syswow64\\\r\n%windir%\\system32\\\r\nkernel32.dll\r\nKiUserExceptionDispatcher\r\nRtlCreateUser\r\nIsWow64Process\r\n\\StringFileInfo\\%04x%04x\\ProductName\r\nTable 4. Decrypted first stage strings.\r\nPayload Decryption\r\nThe next stage payload is stored in an encrypted 7z archive located in the .gfids section of the binary. It is\r\ndecrypted with the modified RC4 algorithm previously discussed in the BlackBerry Cylance write-up using the\r\nhard-coded key: 2C01443389BDFC7330A3386981C43E154AE8B60EC6646D916F93D18137A53544\r\nFigure 12. Decrypted 7z archive.\r\nPayload Execution\r\nOpenProcessToken and GetTokenInformation are called to determine if the process is running under the\r\nLocalSystem account. This is used to determine how the next stage payload is executed.\r\nIf it is determined to be running as LocalSystem, the payload is injected into a newly spawned process chosen\r\nfrom the Windows directory. The command line for this process follows this format and can be used as an\r\nindicator:\r\n-q -s {{GUID}} -p NUMBER\r\nhttps://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/\r\nPage 3 of 12\n\nFigure 13. Command line argument.\r\nIf not found to be running as LocalSystem, the payload will execute in the memory space of the current process.\r\nStage 2\r\nThe second stage payload is a custom-compiled Python interpreter very similar to ones seen used with earlier\r\nvariants of PyXie.\r\nConfiguration\r\nThe configuration is stored in a zlib compressed json blob and is located in the .gfids section of the interpreter.\r\nUnlike previous versions of PyXie, it is not encrypted this time around. The variable sys.builtin_json_cfg is\r\ncreated with a call to PySys_SetObject and the compressed configuration blob is stored in it for later use by the\r\nfinal stage Python component.\r\nFigure 14. sys.builtin_json_cfg variable is created.\r\nDecrypted Strings\r\nhttps://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/\r\nPage 4 of 12\n\nThe second stage uses the same string encryption that was noted in the previous stage.\r\nkernel32.dll\r\nopenwith.exe\r\nonedrivesetup.exe\r\ndvdplay.exe\r\nvsjitdebugger.exe\r\nwerfault.exe\r\n-q -s {%S} -p %u\r\noleout32.dll\r\nVariantClear\r\nMozilla\\Firefox\r\nMozilla\\Firefox\\profiles.ini\r\nSOFTWARE\\Clients\\StartMenuInternet\\firefox.exe\\shell\\open\\command\r\nI_CryptUIProtect\r\ncryptui.dll\r\nRtlCreateUserThread\r\nimport core.modules.winapi_stubs as winapi_stubs\r\nimport core.zip_logs as zip_logs\r\nimport os\r\nzip_logs.send_zip_log(winapi_stubs.get_self_executable_path(), os.getpid(), 'CERTS', r'%s')\r\nKiUserExceptionDispatcher\r\nuiAccess=\"true\"\r\n\\StringFileInfo\\%04x%04x\\ProductName\r\n\\VarFileInfo\\Translation\r\n\\\\?\\globalroot\\systemroot\\system32\\drivers\\null.sys\r\nSystemDrive\r\nhttps://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/\r\nPage 5 of 12\n\nIsWow64Process\r\ncore.entry_point\r\nzipimporter\r\nmemzipimport\r\nlibs_zip_ctx\r\nstart_bind_port\r\nTable 5. Decrypted second stage strings.\r\nFinal Stage: Libs.zip\r\nThe final stage of PyXie bytecode is contained in an encrypted ZIP file embedded within the interpreter binary. As\r\nwith the earlier version of PyXie, the memzipimport library is used to import the bytecode from memory.\r\nPyXie Lite\r\nThe “core” modules in this variant consist of 41 files versus the 79 seen in the previous version of PyXie analyzed\r\nby BlackBerry Cylance. The discrepancy is due to a shift in functionality that we will cover in the next section.\r\nhttps://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/\r\nPage 6 of 12\n\nFigure 15. Bytecode listing.\r\nInterpreter hardening\r\nCursory analysis of the bytecode revealed that the headers had been stripped as with previous earlier versions of\r\nPyXie. Additionally, we found that the opcode table had once again been modified and the opcodes recovered\r\nfrom previous versions of PyXie could no longer be used to decompile this bytecode.\r\nFigure 16. Attempts to decompile bytecode with previously recovered PyXie opcodes resulted in an\r\nerror.\r\nKnowing this, we attempted to force the interpreter to import DeDrop's all.py with hopes of generating bytecode\r\nthat could be used for opcode recovery. To our dismay, we found that simply importing a script would no longer\r\ncause the interpreter to output bytecode.\r\nA closer look at the interpreter found that the sys.dont_write_bytecode variable had been set to true. This has the\r\neffect of preventing bytecode from being written to disk when modules are imported. Likely, this is something the\r\ndevelopers intentionally enabled to hinder analysis efforts.\r\nFigure 17. sys.dont_write_bytecode is set to True.\r\nHijacking the interpreter with a search order vulnerability\r\nDuring our analysis of the interpreter, we found that it attempted to load a number of modules from the current\r\nworking directory and was vulnerable to search order hijacking.\r\nhttps://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/\r\nPage 7 of 12\n\nFigure 18. PyXie attempting to load libraries from the current working directory.\r\nWe were able use this to our advantage by dropping a simple Python shell into one of the modules it attempted to\r\nimport. This gave us unfettered access to the interpreter, which enabled us to overwrite the\r\nsys.dont_write_bytecode variable, generate opcodes for modules on demand and even dump PyXie’s\r\nconfiguration.\r\nFigure 19. Search order vulnerability being used to gain control of the interpreter.\r\nOnce we were able to output bytecode for modules of our choosing, it was trivial to recover the remapped opcodes\r\nand decompile PyXie. A copy of the opcodes for this variant can be found in the appendix.\r\nFunctionality\r\nAs we previously mentioned, PyXie Lite has been repurposed to focus on the automated collection and exfiltration\r\nof data.\r\nUpon execution, it creates a staging directory whose name is based on the output of the\r\ntempfile.NamedTemporaryFile() command.\r\nTable 6. Example staging directory name.\r\nNext, it collects data from the system by running a combination of routines that are dictated by the user account\r\nPyXie is found to be running under. Table 7 breaks down each of these routines and the types of account they will\r\nrun under.\r\nhttps://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/\r\nPage 8 of 12\n\nRoutine\r\nUser\r\ntype\r\nDescription\r\n_mimi_redirector All\r\nRuns Mimikatz in memory. Injects into a newly spawned process from this\r\nlist: write.exe, notepad.exe, explorer.exe\r\n_main_routine All\r\nCollects basic details about the system\r\nInventories software\r\nCollects cookies\r\nCollects LogMeIn data\r\nCollects Citrix data\r\nCollects KeePass safes\r\n_save_sysinfo All Collects uninstall list from registry\r\n_get_passwords All Collects passwords with Lazagne\r\n_find_files System\r\nSearches for and collects files and directories based on keywords,\r\ndirectories and extensions specified in the configuration\r\n_scan_network System Runs network scans\r\n_run_shell_cmds System Runs a series of commands to gather details about the system\r\n_get_desktop_files User Similar to find_files but only searches the current user’s Desktop\r\n_take_screenshot User Takes a screenshot\r\n_get_ps_history User Collects Powershell history\r\nTable 7. PyXie routines.\r\nThe list of keywords and directories from configuration provides us some insight into the type of data the\r\nattackers are interested in:\r\npassw logins wallet private\r\nconfidential username wire access\r\ntreason vault operation bribery\r\ncontraband censored instruction credent\r\ncardholder secret explosive suspect\r\npersonal cyber restricted balance\r\npassport victim submarine checking\r\nhttps://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/\r\nPage 9 of 12\n\nsaving routing esxi vsphere\r\nspy admin newswire bitcoin\r\nethereum n-csr 10-sb 10-q\r\nconvict tactical engeneering military\r\ndisclosure attack infrastruct marketwired\r\nagreement illegal nda hidden\r\nprivacy fraud statement finance\r\nmarketwired clandestine compromate concealed\r\ninvestigation security\r\nTable 8. Keywords from PyXie Lite configuration (including misspellings).\r\nAs part of the data gathering routines, a number of commands are executed to collect details about the system.\r\nnetstat -an\r\nnet user\r\nnet use\r\nnet view /all\r\nnet view /all /domain\r\nnet share\r\nnet config workstation\r\nnet group \"Domain Admins\"\r\nnet group \"Enterprise Admins\"\r\nroute print\r\nnet localgroup\r\nipconfig /all\r\ntasklist /V\r\nwmic process\r\narp -a\r\nhttps://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/\r\nPage 10 of 12\n\ngpresult /z\r\ncmdkey /list\r\nnet config workstation\r\nnslookup -type=any %userdnsdomain%\r\nvssadmin List Shadows\r\nwmic qfe list\r\nklist\r\nmanage-bde -status\r\nnltest /domain_trusts\r\nnltest /domain_trusts /all_trusts\r\nqwinsta\r\nipconfig /displaydns\r\nsysteminfo\r\ndclist\r\nnet group \"domain admins\" /domain\r\nnet localgroup \"administrators\"\r\nwmic path win32_VideoController get name\r\nwmic cpu get name\r\nreg.exe save hklm\\security %LOCALAPPDATA%\\temp\\[RANDOM]\r\nreg.exe save hklm\\system %LOCALAPPDATA%\\temp\\[RANDOM]\r\nreg.exe save hklm\\sam %LOCALAPPDATA%\\temp\\[RANDOM]\r\nTable 9. Executed commands.\r\nhttps://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/\r\nPage 11 of 12\n\nFigure 20. Data collected from the routines in a staging directory prior to exfil.\r\nExfiltration\r\nThe staging directory containing the collected data is added to a compressed ZIP archive and encrypted before\r\nbeing sent to the server specified in the gates section of the config. The archive is encrypted with AES in CBC\r\nmode and THIS_KEY_IS_FOR_INTERNAL_USE_ONLY is used as the key. A random 16-byte initialization\r\nvector (IV) is used and is prepended to the encrypted archive. The exfil servers we have seen in samples to date\r\nhave typically been compromised internal servers on the victim’s networks listening on ports 31337, 900 and\r\n8443. Although we did not have visibility into how the attackers moved data off the victim network, in at least one\r\nincident the exfil server was running Cobalt Strike.\r\nContinue reading: Last, but Not Least: Defray777\r\nSource: https://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/\r\nhttps://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/\r\nPage 12 of 12",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://unit42.paloaltonetworks.com/vatet-pyxie-defray777/2/"
	],
	"report_names": [
		"2"
	],
	"threat_actors": [],
	"ts_created_at": 1775434164,
	"ts_updated_at": 1775826730,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/287395c229f032b857a726ce3c69f5bdc1a45a0f.pdf",
		"text": "https://archive.orkl.eu/287395c229f032b857a726ce3c69f5bdc1a45a0f.txt",
		"img": "https://archive.orkl.eu/287395c229f032b857a726ce3c69f5bdc1a45a0f.jpg"
	}
}