CVE-2026-81578: Verified Reproduction
CVE-2026-81578: PaperCut NG/MF CVE-2026-81578 + CVE-2026-82078 unauthenticated RCE chain
CVE-2026-81578 is verified against PaperCut NG/MF · vendor. Affected versions: 25.0.11 build 75758 (tested). Fixed in 25.0.12 build 76510 (Emergency Patch Release 2). Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00340.
What Is CVE-2026-81578?
CVE-2026-81578 is a critical-severity RCE vulnerability affecting PaperCut NG/MF 25.0.11 build 75758 (tested). Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00340).
CVE-2026-81578 Severity & CVSS Score
CVE-2026-81578 is rated critical severity, with a CVSS base score of 9.4 out of 10.
Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.
Affected PaperCut NG/MF Versions
PaperCut NG/MF · vendor versions 25.0.11 build 75758 (tested) are affected.
How to Reproduce CVE-2026-81578
pruva-verify REPRO-2026-00340 curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00340/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-81578
- reached the target end-to-end
- full exploit chain demonstrated
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
unauthenticated HTTP requests to /app: Tapestry direct-service path (service=direct/1/Home/ConfigEditor/...), ConfigEditor form field values (config keys/values), and the web-login card number (inputUsername) which becomes the Derby export query
- POST /app?service=direct/1/Home/ConfigEditor/... (unauth config write of user-lookup.* / auth.web-login.card-id.*)
- POST /app service=direct/1/Home/$Form card login
- ExternalUserLookupDb CALL SYSCS_UTIL.SYSCS_EXPORT_QUERY_LOBS_TO_EXTFILE (file write as papercut)
- GET /app?service=page/Pwn3 (planted Tapestry page OGNL eval
- Runtime.exec)
- GET /pwn-proof.txt (remote command receipt)
How the agent worked
Root Cause and Exploit Chain for CVE-2026-81578
Stock PaperCut NG/MF 25.0.11 (build 75758) allows a fully unauthenticated remote
attacker to execute operating-system commands as the PaperCut application-service
account (papercut). The chain combines two defects reachable through the
pre-authentication application surface of the running Application Server:
- Tapestry 3 "complex" direct-service authorization bypass. Tapestry's
DirectServiceacceptsservice=direct/1/<RenderPage>/<ComponentPage>/<Component>. It callsvalidate()(which fires PaperCut'sBasePaperCutPage.pageValidateaccess-rights check) only on the render page, then triggers the listener on the component page without validating it. Using the public login page as render page (Home) and a privileged page as component page (ConfigEditor) yields unauthenticated arbitrary configuration writes (quickFindForm+$Form/$Form$0listeners invokeConfigManager.setString). - Unrestricted attacker-controlled SQL in the external Card/ID lookup.
ExternalUserLookupDb.lookupUserByExternalCardNumberopens a JDBC connection using theuser-lookup.db-driver/user-lookup.db-urlconfig keys and runs theuser-lookup.id-to-username-sqltemplate with the submitted card number bound as a parameter. With the bundled Apache Derby embedded driver the template may callSYSCS_UTIL.SYSCS_EXPORT_QUERY_LOBS_TO_EXTFILE, giving an attacker-controlled arbitrary file write as thepapercutuser. The lookup is reached before any login through the web card/ID login flow (auth.web-login.card-id.enable) —Home.login→AuthenticationManagerImpl.authenticateUserWithCard→UserManagerImpl.getUserByCardNumber→ExternalUserLookupManagerImpl→ExternalUserLookupDb.
File write is escalated to in-JVM code execution by planting a new Tapestry page
(WEB-INF/Pwn3.page declaring org.apache.tapestry.html.BasePage, plus
Pwn3.html containing an OGNL expression binding) into the live Jetty webapp
extraction directory (server/tmp/webapp-<build>/), then requesting
GET /app?service=page/Pwn3. Tapestry loads the new page specification from the
servlet context on first access and evaluates the OGNL expression
(@java.lang.Runtime@getRuntime().exec(...)), executing the command in the
Application Server JVM. The command's output is written into the webroot and
fetched back over plain HTTP — a complete remote command receipt.
The emergency 25.0.12 (build 76497) patch blocks the chain at the SQL layer:
ExternalUserLookupDb.createLookupSQLStatement now rejects templates matching
(?i)\b(CALL|EXEC|EXECUTE)\b|\bSYSCS_[A-Z0-9_]*\b with
ApplicationException("Unsafe external user lookup SQL blocked.").
- Package/component: PaperCut NG (and MF) Application Server, all 25.0.x <= 25.0.11 (and per the vendor advisory, effectively all supported branches until the emergency builds).
- Risk: critical. Unauthenticated remote OS command execution as the PaperCut
service account from the web port (9191/9192), including from the internet if
exposed. Post-exploitation runs as
pc-appchild processes, matching the vendor's IoCs.
Impact Parity
- Disclosed/claimed maximum impact: unauthenticated remote code execution as the PaperCut application-service account via a pre-auth card/ID entrypoint.
- Reproduced impact from this run: exactly that —
uid=1001(papercut) gid=1001(papercut)command output (id; uname -a; cat /proc/1/comm) executed inside the Application Server container and retrieved remotely over HTTP. - Parity: full.
- Nothing claimed was left undemonstrated.
Root Cause
Two chained defects:
biz/papercut/pcng/web/pages/BasePaperCutPage.pageValidate(25.0.11) only computes access rights forthis— the page being validated. Tapestry 3'sDirectService.service()validates only the render page (cycle.activate(pageName)) but then callscomponentPage.getNestedComponent (componentPath)anddirect.trigger(cycle)on a second page named later in the service path.POST /app?service=direct/1/Home/ConfigEditor/$Form$0(and/quickFindForm,/$Form) therefore fires ConfigEditor's form listeners (doAddNew,doConfigEdit→ConfigManager.setString) with no session at all. The 25.0.12 patch adds a loop overservice.split("/")accumulating the rights of every embedded page — but note this check still only runs inBasePaperCutPage.pageValidate, andHomeoverridespageValidatewithout callingsuper, so the config-write bypass viadirect/1/Home/...in fact still lands on 25.0.12 (observed at runtime; the RCE chain is nonetheless blocked by the SQL filter below).biz/papercut/pcng/service/impl/ExternalUserLookupDbbuilt aPreparedStatementdirectly from theuser-lookup.id-to-username-sqlconfig value with no statement-type restriction, and opened the connection from config-controlled driver/URL (DatabaseUtils.openConnection→Class.forName+DriverManager.getConnection). Because the whole statement is attacker-controlled and the bundled Derby embedded driver supportsCALL SYSCS_UTIL.SYSCS_EXPORT_QUERY_LOBS_TO_EXTFILE(?, ...), the card number (bound to the?) becomes an arbitrary SQL query whose CLOB result is written verbatim to an arbitrary filesystem path. The advisory IoCs fall out of this naturally: failed attempts logDatabase error looking up cardID: VALUES CAST...and driver probing logsNo suitable driver found for jdbc:....
Fix commit: no public commit (commercial product). Vendor advisory:
https://www.papercut.com/kb/Main/security-bulletin-27-aug-2026-urgent-security-advisory
(emergency builds 25.0.12 build 76497 / 26.x; the FAQ documents the new
EXEC/EXECUTE/CALL restriction).
Reproduction Steps
bundle/repro/reproduction_steps.sh(self-contained; requires docker + curl- python3; downloads the two official installers with vendor-published SHA-256 verification).
- The script builds
pcng-vuln(25.0.11.75758) andpcng-fixed(25.0.12.76497) images, starts both Application Servers, completes the stock setup wizard on each, performs one operator-side admin login + ConfigEditor render per server (models a normally operated server; warms Tapestry's pooled page/table state that the form-rewind path needs), and then runs the entire attack with unauthenticated remote HTTP requests only:- Phase A1: config write via
service=direct/1/Home/ConfigEditor/...(armsuser-lookup.*andauth.web-login.card-id.*). - Phase A2: two pre-auth card-login requests whose card number is
VALUES(CAST('<file content>' AS CLOB)); the Derby export plantsWEB-INF/Pwn3.pageandPwn3.html(OGNL payload) in the live webapp dir. - Phase A3:
GET /app?service=page/Pwn3evaluates the OGNL payload. - Phase A4:
GET /pwn-proof.txtretrieves the command output (remote receipt). - Phase B: identical request sequence against 25.0.12; verifies the armed
lookup reaches the new filter (
Unsafe external user lookup SQL blocked), no files are planted, no command receipt exists, and a benign non-CALL SQL template still executes (proving the feature works and only dangerous SQL is blocked).
- Phase A1: config write via
- Expected evidence: vuln proof file containing
PC0DAY-PROOF-BEGIN ... uid=1001(papercut) ... PC0DAY-PROOF-ENDfetched over HTTP; fixed build logs the SQL block and returns 404 for the proof file.
Exit code 0 = chain proven on 25.0.11 and blocked on 25.0.12.
Evidence
- Full run log:
bundle/logs/reproduction_steps.log(two consecutive clean runs, both exit 0). - Remote command receipt (fetched over HTTP):
bundle/artifacts/vuln-proof.txt. - Planted-page render response:
bundle/artifacts/vuln-render.html. - Vulnerable server.log IoC excerpts (attacker payload visible in
Database error looking up cardID: VALUES(CAST(...)):bundle/artifacts/vuln-serverlog-ioc.txt. - Fixed-build server.log excerpts showing
Unsafe external user lookup SQL blockedplus the benign-SQL Derby error:bundle/artifacts/fixed-serverlog-ioc.txt. - Per-request transcripts:
bundle/artifacts/*-wiz*.html(.headers),bundle/artifacts/fixed-configwrite.html(.headers),bundle/artifacts/vuln-proof.headers, etc. - Build identities:
bundle/artifacts/vuln-version.txt,bundle/artifacts/fixed-version.txt; installer SHA-256s verified against the vendor advisory page (vuln 64495771…817a, fixed 0782c1d6…c392). - Runtime manifest:
bundle/repro/runtime_manifest.json. - Patch localization (25.0.11 vs 25.0.12 class diff):
ExternalUserLookupDb(SQL filter — the effective fix),BasePaperCutPage(service-path rights),StandardRhinoContextFactory(new Rhino ClassShutter blockingjava.lang.Class/ClassLoader/reflect/invoke),WebConfig(path matching / response headers),RestrictApiAccessFilter(URI canonicalization).
Recommendations / Next Steps
- Upgrade to the emergency build (25.0.12 / 26.0.3+) per the vendor advisory.
- The 25.0.12 fix blocks the RCE at the SQL layer, but the unauthenticated
ConfigEditor write via Tapestry complex direct service was still observable on
25.0.12 in this lab (Home overrides
pageValidatewithout invoking the new service-path check). Recommend PaperCut route the rights check through a path that Home cannot skip (e.g., validate the component page inDirectServiceitself or move checks into a servlet filter), and audit every privileged page listener for the same pattern. - Defense-in-depth: disallow
CALL/EXEC/SYSCS_(already patched), consider allowing only plainSELECTtemplates; do not letuser-lookup.db-driveraccept arbitrary driver class names; disallow absolute paths in Derby export procedures via a Java SecurityManager-equivalent policy is no longer possible on JDK 21, so statement allow-listing is the right layer. - Network-level: keep the Application Server web interface off untrusted networks (vendor's immediate guidance).
Additional Notes
- Idempotency: the script was run end-to-end twice consecutively (fresh
containers each time), both runs exited 0. Derby export procedures refuse to
overwrite existing files, so each run uses fresh output paths
(
/tmp/pc0day-a.csv,pc0day-b.csv, unique per run) and fresh containers. - Precondition: the Tapestry form-rewind path used for the config write relies on the pooled ConfigEditor page/table state, which exists after the Config Editor page has been rendered once since server start (any normal admin UI usage). The script performs this as an operator-side action (setup wizard admin account); it is not part of the attacker's request sequence. On a production server that has ever opened Options → Config Editor this precondition holds naturally.
- The card number is bound as a JDBC parameter (
?), so no SQL-escaping of the payload is required; the entire export query rides in the card value. Content written to files must avoid single quotes or use Derby literal doubling; the OGNL payload sidesteps this by constructing strings from byte arrays. - The print/device-script (Rhino) route was not needed; 25.0.12 additionally
shutters Rhino reflection (
StandardRhinoContextFactory), closing the alternate script-based escalation.
CVE-2026-81578 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
grep -o '"prior_guidance_run[^,]*' bundle/ticket.json; echo ---; python3 -c "
import json
d=json.load(open('bundle/ticket.json'))
print(json.dumps(d, indent=1)[:3000])" | tail -60{"cwd":"/workspace","duration_ms":156,"exit_code":0,"stderr":"","stdout":"---\n{\n \"claim\": {\n \"argus_claim_ref\": null,\n \"attacker_control\": \"unauthenticated application requests, values accepted by the vulnerable privileged-action surface, and the submitted card value\",\n \"claimed_surface\": \"api_remote\",\n \"expected_impact\": \"code_execution\",\n \"finding_id\": null,\n \"id\": null,\n \"required_entrypoint_detail\": \"unauthenticated Release Station/MFD card-ID operation reaching the external card-lookup runtime\",\n \"required_entrypoint_kind\": \"endpoint\",\n \"submission_reason\": \"ticket_derived_llm\",\n \"trigger_class\": \"service_api\",\n \"upstream_verdicts\": {\n \"claim_extraction\": {\n \"confidence\": \"high\",\n \"model\": \"accounts/fireworks/models/kimi-k2p7-code\",\n \"reason\": \"Ticket explicitly claims an unauthenticated remote actor can execute an OS command as the PaperCut application-service account through a pre-authentication card/ID entrypoint on the Application Server, with a negative control on 25.0.12. Expected impact is code_execution, surface is api_remote, trigger class is service_api, and required entrypoint … [truncated]Artifacts and Evidence for CVE-2026-81578
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-81578
Upgrade PaperCut NG/MF · vendor to 25.0.12 build 76510 (Emergency Patch Release 2) or later.
FAQ: CVE-2026-81578
Is CVE-2026-81578 exploitable?
How severe is CVE-2026-81578?
What type of vulnerability is CVE-2026-81578?
Which versions of PaperCut NG/MF are affected by CVE-2026-81578?
Is there a fix for CVE-2026-81578?
How can I reproduce CVE-2026-81578?
Is the CVE-2026-81578 reproduction verified?
References for CVE-2026-81578
Authoritative sources for CVE-2026-81578 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.