CVE-2026-57518: Verified Reproduction
CVE-2026-57518: Pagekit CMS privilege escalation leading to RCE
CVE-2026-57518 is verified against pagekit/pagekit · github. Affected versions: <= 1.0.18. Fixed in None available; project archived. Vulnerability class: RCE. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00256.
What Is CVE-2026-57518?
CVE-2026-57518 is a high-severity privilege escalation vulnerability in Pagekit CMS 1.0.18 and earlier that lets an authenticated low-privileged user escalate to admin-level permissions and ultimately achieve remote code execution. Pruva reproduced it (reproduction REPRO-2026-00256).
CVE-2026-57518 Severity & CVSS Score
CVE-2026-57518 is rated high severity, with a CVSS base score of 8.8 out of 10.
High — serious impact or readily exploitable. Prioritize remediation.
Affected pagekit/pagekit Versions
pagekit/pagekit · github versions <= 1.0.18 are affected.
How to Reproduce CVE-2026-57518
pruva-verify REPRO-2026-00256 curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00256/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-57518
- 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
authenticated low-privileged user with 'user: manage users' permission self-assigns a custom role with 'system: manage packages' via POST /api/user/{id}, then uploads and installs a malicious PHP package
- POST /api/user/{id} with roles array containing custom role ID (not 3)
- POST /admin/system/package/upload
- POST /admin/system/package/install
- GET /packages/{name}/index.php?cmd=id
How the agent worked
Root Cause and Exploit Chain for CVE-2026-57518
Pagekit CMS 1.0.18 contains a privilege escalation vulnerability in
UserApiController::saveAction() that allows authenticated users holding the
user: manage users permission to self-assign arbitrary custom roles. The
authorization check only prevents assignment of the built-in Administrator role
(hard-coded ID=3); any custom role (ID ≥ 4) — including one with elevated
permissions such as system: manage packages — passes through unchecked. An
attacker can exploit this to gain the system: manage packages permission,
then upload and install a malicious PHP extension package containing a webshell
through the admin package installer, achieving remote code execution on the
server.
- Package/component affected:
pagekit/pagekit—app/system/modules/user/src/Controller/UserApiController.php(saveAction, lines 178–183) - Affected versions: Pagekit CMS ≤ 1.0.18 (project archived 2023-12-01; no patched release exists)
- Risk level: High (CVSS 8.8 — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)
- Consequences: Full server compromise. An authenticated low-privileged
user with
user: manage userscan escalate to arbitrary code execution as the web server user (www-data).
Impact Parity
- Disclosed/claimed maximum impact: Privilege escalation leading to remote code execution (code_execution)
- Reproduced impact from this run: Full privilege escalation to
system: manage packages, followed by upload and installation of a malicious PHP package, followed by confirmed remote code execution (uid=33(www-data)) - Parity:
full - Not demonstrated: N/A — the full chain from low-privilege user to arbitrary command execution was demonstrated end-to-end against the real running Pagekit service via its HTTP API.
Root Cause
The vulnerability is in UserApiController::saveAction() at
app/system/modules/user/src/Controller/UserApiController.php, lines 178–183:
$key = array_search(Role::ROLE_ADMINISTRATOR, @$data['roles'] ?: []);
$add = false !== $key && !$user->isAdministrator();
$remove = false === $key && $user->isAdministrator();
if (($self && $remove) || !App::user()->isAdministrator() && ($remove || $add)) {
App::abort(403, 'Cannot add/remove Admin Role.');
}
Role::ROLE_ADMINISTRATOR is a constant equal to 3 (defined in
app/system/modules/user/src/Model/Role.php). The authorization check
only tests whether the Administrator role (ID=3) is being added or
removed. Custom roles created by an administrator (which receive IDs 4, 5, 6,
…) are never subjected to any authorization check. Consequently, any user who
has the user: manage users permission (required by the @Access("user: manage users") annotation on UserApiController) can call
POST /api/user/{own_id} with a roles array that includes a custom role
with elevated permissions — for example, system: manage packages — and
the server will silently accept and persist the change.
Once the attacker holds system: manage packages (and system: access admin area), the admin package installer at POST /admin/system/package/upload
and POST /admin/system/package/install becomes available. The attacker
uploads a ZIP archive containing a composer.json (type
pagekit-extension) and a PHP webshell (index.php). The installer extracts
the package into the web-accessible packages/ directory, and the webshell
is immediately reachable at
/packages/pagekit/shell/index.php?cmd=<command>, executing arbitrary
commands as the web server user.
Fix commit: None — the Pagekit project was archived on 2023-12-01 and no patched version has been released.
Reproduction Steps
- Script:
bundle/repro/reproduction_steps.sh - What the script does:
- Downloads (or reuses a cached copy of) the official Pagekit 1.0.18
release ZIP (which includes all PHP dependencies in
app/vendor/). - Builds a Docker image based on
php:7.4-apachewith the required PHP extensions (gd, zip, pdo_sqlite, opcache) and deploys Pagekit with Apache + mod_rewrite. - Starts the container and installs Pagekit via its installer API
(
POST /installer/install) using a SQLite database, creating an administrator account. - As administrator, creates two custom roles:
- Package Manager (ID 4):
system: manage packages+system: access admin area - User Manager (ID 5):
user: manage users
- Package Manager (ID 4):
- As administrator, creates a low-privileged editor user (ID 2) with the User Manager role.
- As editor, calls
POST /api/user/2with{"user":{"id":2,"roles":[2,4]}}— self-assigning the Package Manager role. ThesaveActiononly blocks role ID 3 (Administrator); the custom role ID 4 passes through unchecked. - As the now-escalated editor, uploads a malicious extension ZIP
(containing a PHP webshell) via
POST /admin/system/package/uploadand installs it viaPOST /admin/system/package/install. - Accesses
/packages/pagekit/shell/index.php?cmd=idand verifies the output containsuid=, confirming remote code execution.
- Downloads (or reuses a cached copy of) the official Pagekit 1.0.18
release ZIP (which includes all PHP dependencies in
- Expected evidence of reproduction:
bundle/logs/reproduction_steps.log— full execution logbundle/logs/exploit_evidence.txt— structured evidence summarybundle/logs/rce_id_output.txt— output ofidcommand via webshell (uid=33(www-data) gid=33(www-data) groups=33(www-data))bundle/logs/rce_whoami_output.txt— output ofwhoami(www-data)bundle/repro/runtime_manifest.json— runtime evidence manifest
Evidence
Key excerpts
Privilege escalation (Step 3):
Editor BEFORE escalation: {"id":2,...,"roles":[2,5],...}
Escalation response: {"message":"success","user":{"id":2,...,"roles":[2,4],...}}
PRIVILEGE ESCALATION CONFIRMED — editor now has 'system: manage packages'
RCE (Step 6):
RCE output (id): uid=33(www-data) gid=33(www-data) groups=33(www-data)
RCE output (whoami): www-data
=== RCE CONFIRMED ===
Environment details
- OS: Ubuntu 26.04 LTS (host); Debian Bullseye (container)
- PHP: 7.4.33 (Apache, inside Docker container)
- Database: SQLite (
pagekit.db) - Pagekit: 1.0.18 (official release from GitHub)
- Web server: Apache 2.4.54 with mod_rewrite
Sandbox adaptations (non-security-relevant)
- Docker networking: The sandbox restricts host-to-container port
forwarding, so all HTTP requests to the running Pagekit service are made
via
docker exec(curl inside the container). This does not alter the product's request handling — the same HTTP requests reach the same Apache- PHP + Pagekit stack.
- Session bootstrap: A PHP helper (
create_session.php) creates a valid authenticated session directly in the database (session + auth tables) to bypass a PHP 7.4 / Symfony 3.0 session-storage interaction issue that prevents normal web login via curl. The auth state created is identical to a successful web login (same cookies, same database records, same CSRF token derivation). - Composer marketplace repository: The package installer's Composer
blueprint includes a remote
composerrepository pointing tohttps://pagekit.com. The sandbox has no outbound network, so this repository entry is removed from the blueprint, leaving only the localartifactrepository. This does not change any security-relevant code path — it only avoids a network fetch for the marketplace index that is irrelevant to the exploit.
Recommendations / Next Steps
Suggested fix
The authorization check in saveAction should validate all role
assignments, not just the Administrator role. A correct implementation would
verify that the calling user is permitted to assign each role in the
$data['roles'] array — for example, by checking that the calling user
either is an administrator or holds a permission that authorizes assigning
each specific role. At minimum, the check should prevent any non-administrator
from assigning roles with trusted permissions (such as
system: manage packages) to themselves.
Upgrade guidance
Pagekit is archived and unmaintained. Users should migrate to an actively
maintained CMS. If migration is not immediately feasible, a mitigation is to
never grant the user: manage users permission to non-administrator accounts,
and to avoid creating custom roles with trusted permissions.
Testing recommendations
- Add a unit test that verifies a non-administrator user with
user: manage userscannot self-assign a custom role withtrustedpermissions. - Add an integration test that verifies the package installer rejects uploads
from users without
system: manage packages.
Additional Notes
Idempotency confirmation
The script was run twice consecutively, both times producing the same result:
privilege escalation confirmed and RCE confirmed (uid=33(www-data)). Each
run creates a fresh Docker container with a clean Pagekit installation,
ensuring no state carries over between runs.
Limitations
- The project is archived; no fixed version exists for a negative control.
- The webshell executes as
www-data(the Apache user), not as root. Full root compromise would require a separate local privilege escalation, which is out of scope for this CVE. - The
user: manage usersprecondition is realistic: Pagekit supports delegating user management to non-owner accounts (e.g., a "User Manager" or "Editor" role), which is the exact scenario this vulnerability targets.
CVE-2026-57518 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.
Artifacts and Evidence for CVE-2026-57518
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-57518
Upgrade pagekit/pagekit · github to None available; project archived or later.
FAQ: CVE-2026-57518
How does the CVE-2026-57518 escalation-to-RCE chain work?
user: manage users self-assigns a custom role (ID >= 4) that carries system: manage packages, then uses the resulting admin package-installer functionality to upload and install a malicious PHP extension package containing a web shell, achieving remote code execution as the web server user.Which Pagekit versions are affected by CVE-2026-57518, and where is it fixed?
How severe is CVE-2026-57518?
How can I reproduce CVE-2026-57518?
user: manage users, self-assigns a custom role granting system: manage packages, then uploads a malicious PHP package via the admin installer to confirm code execution as www-data.References for CVE-2026-57518
Authoritative sources for CVE-2026-57518 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.