Skip to content

CVE-2026-48908: Verified Repro With Script Download

CVE-2026-48908: SP Page Builder for Joomla allows unauthenticated arbitrary file upload via asset.uploadCustomIcon, enabling PHP upload and remote code execution.

CVE-2026-48908 is verified against SP Page Builder (com_sppagebuilder) · Joomla extension. Affected versions: 1.0.0 through 6.6.1. Fixed in 6.6.2. Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00269.

REPRO-2026-00269 SP Page Builder (com_sppagebuilder) · Joomla extension RCE Variant found Jul 8, 2026 CVE entry ↗ .txt
Severity
CRITICAL
CVSS
10.0
Confidence
HIGH
Reproduced in
77m 51s
Tool calls
489
Spend
$10.73
01 · Overview

What Is CVE-2026-48908?

CVE-2026-48908 is a critical unauthenticated arbitrary file upload vulnerability in SP Page Builder (com_sppagebuilder) for Joomla, reachable via the asset.uploadCustomIcon task, that lets an unauthenticated attacker upload a web shell and achieve remote code execution. Pruva reproduced it (reproduction REPRO-2026-00269).

02 · Severity & CVSS

CVE-2026-48908 Severity & CVSS Score

CVE-2026-48908 is rated critical severity, with a CVSS base score of 10.0 out of 10.

CRITICAL threat level
10.0 / 10 CVSS base
Weakness CWE-284 Improper Access Control; NVD also lists CWE-434 Unrestricted Upload of File with Dangerous Type

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected SP Page Builder (com_sppagebuilder) Versions

SP Page Builder (com_sppagebuilder) · Joomla extension versions 1.0.0 through 6.6.1 are affected.

How to Reproduce CVE-2026-48908

$ pruva-verify REPRO-2026-00269
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00269/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-48908

Remote code execution — reproduced
  • 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
Trigger

multipart ZIP archive (custom_icon) containing .htaccess + uppercase .PHP web shell

Attack chain
  1. index.php?option=com_sppagebuilder&task=asset.uploadCustomIcon
Runnable proof: reproduction_steps.sh
Captured evidence: fixed pocfixed composevariant fixed testvariant fixed poc control
Variants tested

No bypass or distinct RCE variant found for CVE-2026-48908. The 6.6.2 auth/CSRF guard on the site SppagebuilderControllerAsset blocks all tested alternate entry points (missing controller prefix, direct unpack, admin editor.icons, and the read-only loadCustomIcons method).

How the agent worked 1,354 events · 489 tool calls · 1h 18m
1h 18mDuration
489Tool calls
423Reasoning steps
1,354Events
10Dead-ends
Agent activity over 1h 18m
Support
13
Hypothesis
2
Repro
1,027
Judge
36
Variant
272
0:0077:51

Root Cause and Exploit Chain for CVE-2026-48908

Versions: 1.0.0 – 6.6.1.

SP Page Builder for Joomla (com_sppagebuilder) exposes the controller task index.php?option=com_sppagebuilder&task=asset.uploadCustomIcon to upload a custom icon-font package. In versions 1.0.0 – 6.6.1 the task is reachable without authentication, authorization, or a valid anti-CSRF token. The uploaded ZIP archive is extracted and copied into the web root under /media/com_sppagebuilder/assets/iconfont/<name>/, including its fonts/ subdirectory. Because the archive entries are not validated against an extension allow-list, an attacker can package a web shell and a .htaccess file that registers an uppercase .PHP extension with the PHP handler, then execute the shell over HTTP. This yields unauthenticated remote code execution as the web-server user.

  • Package / component: SP Page Builder (com_sppagebuilder) by JoomShaper — site/controllers/asset.php (SppagebuilderControllerAsset::uploadCustomIcon).
  • Affected versions: 1.0.0 – 6.6.1.
  • Patched version: 6.6.2.
  • Risk level: Critical (CWE-284 → CWE-434 Unrestricted Upload of File with Dangerous Type; advisory CVSS 4.0 10.0). Any unauthenticated remote attacker can upload and execute attacker-controlled PHP code on the Joomla host.

Impact Parity

  • Disclosed / claimed maximum impact: Unauthenticated arbitrary file upload leading to remote code execution.
  • Reproduced impact from this run: Full parity.
    1. Unauthenticated upload: The PoC sent a multipart custom_icon ZIP to the asset.uploadCustomIcon task with no Joomla session cookie and no CSRF token; the server accepted it and extracted the contents under the web root.
    2. Code execution: The uploaded .htaccess plus uppercase .PHP shell executed the PoC’s echo 7*6 marker (SPPB-RCE-42) and then id, returning uid=33(www-data) gid=33(www-data) groups=33(www-data).
    3. Negative control: The same PoC against the fixed 6.6.2 install received HTTP 403 with the message You require admin access, confirming the patch closes the path.
  • Parity: full.
  • Not demonstrated: Further post-exploitation (e.g., privilege escalation beyond www-data) is not part of the CVE claim.

Root Cause

In the vulnerable build the SppagebuilderControllerAsset class (in site/controllers/asset.php) has no authentication, authorization, or CSRF logic in its constructor or in the uploadCustomIcon() method. The method reads the uploaded custom_icon file, validates only basic size/length limits, writes it to a temporary ZIP, and calls the local unpack() method:

$zip_file = $tmp_path . '/builderCustomIcon.zip';
...
if (File::upload($tmp_src, $zip_file, false, true))
{
    $extract = $this->unpack($zip_file);
    ...
    Folder::copy($extract_path . '/' . $font_path, $rootPath . '/' . $fontFamily . '/fonts');
    File::copy($extract_path . '/' . $font_css, $rootPath . '/' . $fontFamily . '/' . $font_css);
    ...
}

unpack() uses Joomla\Archive\Archive::extract() to unpack the ZIP into a subdirectory of the Joomla tmp_path. The controller then copies the entire fonts/ (and CSS) contents into the public web-root directory JPATH_ROOT/media/com_sppagebuilder/assets/iconfont/<fontFamily>/. No extension allow-list is enforced on the archive entries before copying, so the attacker can include arbitrary files such as .htaccess and .PHP.

The server-side extension filter that does exist is a case-sensitive block-list (rejects lowercase .php, .php3.php8, .pht, .phtml, .phar, etc.), but the block-list misses uppercase .PHP and .htaccess. A default Apache PHP handler (<FilesMatch "\.php$">) does not execute uppercase .PHP, so the PoC also drops a fonts/.htaccess containing:

AddType application/x-httpd-php .PHP

Where AllowOverride is permitted, this reconfigures the directory to treat .PHP as PHP. The same HTTP request that uploaded the shell can then fetch it from the web-accessible iconfont directory and execute it.

The 6.6.2 fix adds a constructor to SppagebuilderControllerAsset that blocks unauthenticated/unauthorized requests before any method runs:

public function __construct($config = [])
{
    parent::__construct($config);
    $user = Factory::getUser();
    $authorised = $user->authorise('core.admin', 'com_sppagebuilder')
                   || $user->authorise('core.manage', 'com_sppagebuilder');
    if (!$authorised)
    {
        ...
        $this->sendResponse($response, 403, true);
    }
    if (!$user->id)
    {
        ...
        $this->sendResponse($response, 401, true);
    }
    if (!Session::checkToken())
    {
        ...
        $this->sendResponse($response, 403, true);
    }
}

This requires the user to be logged in, to have admin/manage privileges for the component, and to present a valid Joomla CSRF token, making the upload task unreachable from an unauthenticated attacker.

Reproduction Steps

  1. Script: bundle/repro/reproduction_steps.sh (self-contained; exits 0 on confirmation, non-zero on failure).
  2. What it does:
    • Reads the durable project cache if one is configured, otherwise creates bundle/artifacts/sppb.
    • Clones the public PoC https://github.com/papageo75/CVE-2026-48908-PoC.git once and reuses it across runs.
    • For each role (vuln and fixed):
      • Builds a Docker Compose stack with joomla:5.2-php8.2-apache and mysql:8.0, downloads the SP Page Builder ZIP (6.6.1 for the vulnerable role, 6.6.2 for the fixed role), and installs it through Joomla’s web installer using the real administrator workflow.
      • Waits for the Joomla health check to return HTTP 200.
      • Runs the PoC against the running container from the same Docker network (http://joomla), using the real asset.uploadCustomIcon HTTP endpoint.
    • Writes bundle/repro/runtime_manifest.json and per-role evidence logs under bundle/logs/.
  3. Expected evidence of reproduction:
    • Vulnerable (6.6.1): PoC prints CODE EXECUTION CONFIRMED via '.htaccess+.PHP' and the output of id (uid=33(www-data)).
    • Fixed (6.6.2): PoC prints TARGET NOT VULNERABLE — SP Page Builder is patched and the first upload attempt returns 403 'require admin' = patched.

Evidence

Log file locations (written by reproduction_steps.sh):

  • bundle/logs/vuln_compose.log — Docker stack creation for the vulnerable run.
  • bundle/logs/vuln_poc.log — PoC transcript showing RCE on 6.6.1.
  • bundle/logs/fixed_compose.log — Docker stack creation for the fixed run.
  • bundle/logs/fixed_poc.log — PoC transcript showing 6.6.2 rejects the upload.
  • bundle/logs/reproduction_steps.log — full transcript of the latest run.
  • bundle/repro/runtime_manifest.json — structured runtime evidence.

Key excerpts (from the latest verification run):

Vulnerable 6.6.1 — code execution confirmed:

[*] try .htaccess+.PHP  -> EXECUTED
[+] CODE EXECUTION CONFIRMED via '.htaccess+.PHP' (echo 7*6 -> 42)
[+] webshell : http://joomla/media/com_sppagebuilder/assets/iconfont/.../fonts/....PHP?t=...&c=<cmd>
[*] running: id
------------------------------------------------------------
uid=33(www-data) gid=33(www-data) groups=33(www-data)
------------------------------------------------------------

Fixed 6.6.2 — upload now requires admin authentication:

[*] try .php            -> 403 'require admin' = patched
[-] TARGET NOT VULNERABLE — SP Page Builder is patched (6.6.2+); the upload task now requires authenticated admin access.

Environment: Joomla 5.2 with PHP 8.2 (Apache), MySQL 8.0, SP Page Builder 6.6.1 (vulnerable) and 6.6.2 (fixed), running in Docker on a single custom network. The PoC is executed from an ephemeral python:3-slim container on the same network, with no session state or credentials.

Recommendations / Next Steps

  • Upgrade to SP Page Builder 6.6.2 or later. The 6.6.2 patch adds the constructor checks described above, which are the primary fix.
  • Defence in depth on the web server:
    • Disable PHP execution in directories that are only expected to host static assets (/media/, /images/, /templates/*/css, etc.).
    • Set AllowOverride None on these directories so an uploaded .htaccess cannot re-register handlers.
    • Use a case-insensitive extension allow-list (e.g., only .ttf, .woff, .css, .json) on uploaded archive contents; never rely on a block-list of dangerous extensions.
  • Assume-breach review: search for unexpected .php/.PHP, .htaccess, and newly created directories under /media/com_sppagebuilder/assets/iconfont/, and look for new Super User accounts or planted web shells.
  • Backport: if a site cannot upgrade immediately, apply the 6.6.2 constructor guard (authorization + user session + Session::checkToken()) to site/controllers/asset.php.

Additional Notes

Variant Analysis & Alternative Triggers for CVE-2026-48908

Versions: versions (as tested): 6.6.1 vulnerable, 6.6.2 fixed.

This variant stage tested whether SP Page Builder 6.6.2 can be bypassed or whether the same underlying unauthenticated file-upload bug can be triggered from a different entry point. No bypass or distinct RCE variant was found. The vendor fix adds an authorization/CSRF guard to the front-end SppagebuilderControllerAsset class, and that guard blocks every tested unauthenticated path that reaches the same ZIP extraction sink. A sibling unauthenticated method (asset.loadCustomIcons) does exist on the vulnerable build, but it reads from the database rather than writing files, so it is not a variant of the file-upload/RCE vulnerability.

Fix Coverage / Assumptions

What the fix covers
  • The only unauthenticated path to the ZIP upload/extraction sink is the front-end task index.php?option=com_sppagebuilder&task=asset.uploadCustomIcon.
  • The 6.6.2 patch adds a constructor to site/controllers/asset.php that checks:
    1. core.admin or core.manage authorization for the component.
    2. A logged-in user ($user->id).
    3. A valid Joomla CSRF token via Session::checkToken().
  • The patch also removes the three non-upload CRUD methods (loadCustomIcons, deleteCustomIcon, changeCustomIconStatus) from the site controller and narrows unpack() from public static to private static, preventing direct task invocation.
What the fix does NOT cover
  • The upload logic itself is unchanged: the ZIP is still extracted and copied into the web root under /media/com_sppagebuilder/assets/iconfont/. The extension filter is still a case-sensitive block-list. An attacker with a valid administrator session and a CSRF token could still execute the same RCE chain.
  • The fix does not add a content allow-list, restrict the iconfont directory to static assets, or disable .htaccess / PHP execution in that directory. Those remain defense-in-depth gaps that the site administrator must address on the web server.
  • No code paths outside site/controllers/asset.php were modified, because every other file-upload or file-write controller already had authorization checks (media.php, ai_content.php, the admin editor.php, site/views/ajax/view.json.php, etc.).

Variant / Alternate Trigger

Five candidate entry points were tested against the vulnerable (6.6.1) and fixed (6.6.2) builds:

Candidate Vulnerable 6.6.1 Fixed 6.6.2 Assessment
asset.uploadCustomIcon (control) RCE confirmed 403 "require admin" The fix covers the original sink.
uploadCustomIcon without controller prefix 404 404 Joomla routes to the default controller, not the asset controller. Not a bypass.
asset.unpack (direct) 500 (missing argument) 403 The method was never reachable with attacker-controlled input; the fix blocks it anyway.
editor.icons via /administrator/ Blocked by auth Blocked by auth The admin IconsTrait is protected by the admin editor's own constructor.
asset.loadCustomIcons 200 (unauthenticated list) 403 Same controller, but the method only reads from the database; it does not upload or extract files, so it is not a variant of the file-upload/RCE bug.
Code paths involved
  • site/controllers/asset.phpSppagebuilderControllerAsset::__construct() (new guard) and uploadCustomIcon() (the sink).

  • admin/editor/traits/IconsTrait.phpuploadIcons() (same logic, but only used by the admin editor, which is protected by admin/controllers/editor.php).

  • admin/controllers/editor.phpSppagebuilderControllerEditor::__construct() (admin-side guard).

  • Package / component: SP Page Builder (com_sppagebuilder) by JoomShaper.

  • Affected versions (as tested): 6.6.1 vulnerable, 6.6.2 fixed.

  • Risk level: No new risk introduced by this variant search. The original unauthenticated RCE is fully mitigated by the 6.6.2 guard for the tested paths.

Impact Parity

  • Disclosed/claimed maximum impact: Unauthenticated arbitrary file upload leading to remote code execution.
  • Reproduced impact from this variant run: None — no distinct bypass or alternate RCE trigger was confirmed.
  • Parity: none for the variant stage. The original RCE was reproduced as a control on 6.6.1.
  • Not demonstrated: Further post-exploitation or privilege escalation beyond the reproduced www-data shell.

Root Cause

The root cause of the original CVE is missing authentication/authorization on the SppagebuilderControllerAsset class, which allowed any remote caller to reach uploadCustomIcon() and extract an attacker-controlled ZIP under the web root. The 6.6.2 fix removes that missing-auth condition by adding a constructor guard. Because the guard runs for every task of that class, no alternative task name or direct method invocation can bypass it from an unauthenticated session. The admin-side IconsTrait shares the same extraction logic but is already protected by the admin editor's constructor, so it cannot be used as an unauthenticated variant either.

Reproduction Steps

  1. Script: bundle/vuln_variant/reproduction_steps.sh (self-contained, idempotent; exits 1 when no bypass is found).
  2. What it does:
    • Creates two Docker Compose stacks (joomla:5.2-php8.2-apache + mysql:8.0) for 6.6.1 and 6.6.2.
    • Installs the corresponding SP Page Builder ZIP through the real Joomla web installer.
    • Runs a Python variant tester that uploads the same .htaccess + .PHP payload to each candidate endpoint and tries to execute the dropped shell.
    • Runs the original public PoC as a control for each build.
  3. Expected evidence:
    • Vulnerable 6.6.1: control endpoint prints RCE and the PoC prints CODE EXECUTION CONFIRMED.
    • Fixed 6.6.2: every candidate endpoint returns an auth-related block (403/"require admin") and the PoC prints TARGET NOT VULNERABLE.

Evidence

Log files produced by the variant script (both runs produced the same result):

  • bundle/logs/variant_vuln_test.log — variant matrix on the vulnerable build.
  • bundle/logs/variant_fixed_test.log — variant matrix on the fixed build.
  • bundle/logs/variant_vuln_poc_control.log — original PoC confirming RCE on 6.6.1.
  • bundle/logs/variant_fixed_poc_control.log — original PoC confirming 6.6.2 rejects the upload.
  • bundle/logs/variant_vuln_compose.log and bundle/logs/variant_fixed_compose.log — Docker stack creation.
  • bundle/vuln_variant/runtime_manifest.json — structured runtime evidence.

Key excerpts from the latest run:

Vulnerable 6.6.1 — control endpoint RCE:

{
  "control_asset.uploadCustomIcon": {
    "result": "RCE",
    "detail": "SPPB-VAR-42\n",
    "iconfont_dir": "media/com_sppagebuilder/assets/iconfont/icovoqhdk"
  }
}

Fixed 6.6.2 — all candidates blocked:

{
  "control_asset.uploadCustomIcon": { "result": "BLOCKED", "detail": "auth/csrf message", "status": 403 },
  "variant_task_no_prefix": { "result": "BLOCKED", "detail": "non-JSON response", "status": 404 },
  "variant_direct_unpack": { "result": "BLOCKED", "detail": "auth/csrf message", "status": 403 },
  "variant_admin_editor.icons": { "result": "BLOCKED", "detail": "auth/csrf message", "status": 200 },
  "variant_asset.loadCustomIcons": { "result": "BLOCKED", "detail": "auth/csrf message", "status": 403 }
}

Original PoC control on 6.6.2:

[*] try .php            -> 403 'require admin' = patched
[-] TARGET NOT VULNERABLE — SP Page Builder is patched (6.6.2+)

Environment: Joomla 5.2 + PHP 8.2 (Apache) + MySQL 8.0, SP Page Builder 6.6.1 and 6.6.2 in Docker, tested from an ephemeral python:3-slim container on the same network.

Recommendations / Next Steps

  • No additional code change is required for the unauthenticated vector. The 6.6.2 fix is complete for the claimed scope.
  • Defense in depth:
    • Add a strict extension allow-list to the icon-font upload (only .ttf, .woff, .woff2, .css, .json) instead of relying on the case-sensitive block-list.
    • Disable PHP execution and AllowOverride under /media/com_sppagebuilder/assets/iconfont/ at the web-server level.
  • Monitor for partial updates: A forum post suggested some 6.6.2 installations might still have an unpatched site/controllers/asset.php if the extension update was incomplete. Verify the patched file hash after upgrading.
  • Assume-breach review: As recommended in the original repro, check for unexpected .php/.PHP files, .htaccess, and newly created directories under the iconfont path, and look for rogue Super User accounts.

Additional Notes

  • Idempotency: The variant script was run twice consecutively. Both runs exited 1 with the same results: the vulnerable build reproduced the original RCE, the fixed build blocked all tested paths, and no bypass/variant was found.
  • Limitations: The variant search focused on entry points and task names within the SP Page Builder component. It did not attempt to bypass a valid Joomla admin session or CSRF token; such a scenario would be an authenticated compromise, not a bypass of the unauthenticated vulnerability.
  • Threat model: No SECURITY.md or explicit threat model was found in the package. The Joomla! Project CNA and vendor advisory scope the issue as unauthenticated remote code execution, and the 6.6.2 fix is scoped to that exact trust boundary.

CVE-2026-48908 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.

Event 1/40
0:003:34
0:00
session startedaccounts/fireworks/models/kimi-k2p7-code · CVE-2026-48908 · REPRO-20
0:04
0:06
web search
0:07
0:09
0:10
0:26
0:26
extract_facts
no facts extracted
0:27
0:27
0:27
supportrepro
2:59
3:06
3:06
3:10
3:10
3:14
3:14
3:15
3:16
3:16
3:19
3:19
3:24
3:26
web search
3:27
3:32
3:34
web search
08 · How to Fix

How to Fix CVE-2026-48908

Upgrade SP Page Builder (com_sppagebuilder) · Joomla extension to 6.6.2 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-48908 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-48908

How does the CVE-2026-48908 upload-to-RCE attack work?

An attacker submits a ZIP archive containing a PHP web shell plus a .htaccess file that registers an uppercase .PHP extension with the PHP handler (evading naive extension filters) to the unauthenticated uploadCustomIcon task; the archive is extracted under the web root and the shell is then invoked directly over HTTP to execute arbitrary PHP.

Which SP Page Builder versions are affected by CVE-2026-48908, and where is it fixed?

SP Page Builder 1.0.0 through 6.6.1 are affected; it is fixed in 6.6.2.

How severe is CVE-2026-48908?

Critical severity, CVSS 10.0 (maximum score), reflecting unauthenticated remote code execution requiring no privileges or user interaction.

How can I reproduce CVE-2026-48908?

Download the verified script and run it in an isolated environment against Joomla with SP Page Builder 1.0.0-6.6.1 installed; it sends an unauthenticated multipart ZIP upload to the asset.uploadCustomIcon task and demonstrates the extracted PHP shell executing under the web server user.
11 · References

References for CVE-2026-48908

Authoritative sources for CVE-2026-48908 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.