A public form endpoint that accepts .php as if it were a JPEG, gift-wrapped for anyone with curl
Balbooa Forms is a widely deployed Joomla forms/builder extension. Through version 2.4.0, its frontend attachment upload handler took files from anonymous visitors with no authentication, no CSRF token, and no allowlist on file type or extension. An attacker POSTs a PHP payload to the form endpoint, the file lands under the site's writable uploads directory, and then a second GET executes it as the web-server user. The fix ships in 2.4.1 (released 2026-07-09); everything ≤ 2.4.0 is exploitable.
The vendor CRITICAL / 9.8 rating is accurate and if anything conservative. The vector is AV:N/AC:L/PR:N/UI:N with full CIA impact, and CISA added it to KEV on the same day as disclosure because in-the-wild exploitation was already underway. There is no meaningful friction — no auth, no user interaction, no rare configuration. This is a single-request unauth RCE against internet-facing PHP.
5 steps from start to impact.
Enumerate Joomla sites running Balbooa Forms
/administrator/manifests/files/joomla.xml, then greps rendered pages for Balbooa Forms markers (com_baforms, baforms-wrapper, asset paths under /components/com_baforms/). Mass scanning with httpx + nuclei templates finds candidates in minutes.- Target site is internet-reachable
- Balbooa Forms component installed and enabled
- Sites behind WAF with virtual-patching may not respond to fingerprint markers
com_baforms in HTML bodies produce the candidate list; nuclei has templates for Joomla component enumeration.POST malicious file to unauth upload endpoint
multipart/form-data POST with a .php file (or double-extension like shell.php.jpg depending on server handler config). No session, no token, no MIME validation. Public PoCs are circulating; curl one-liners are trivial.- Form containing an attachment field is published on any public URL
- PHP writable upload directory
- Some deployments front Joomla with ModSecurity/CRS rules blocking
.phpin multipart bodies - Cloudflare/Sucuri WAF virtual patch may drop the request
/index.php?option=com_baforms&... with Content-Type: multipart/form-data; EDR on the LAMP host sees new .php files appearing under uploads/.Locate the dropped webshell
/images/baforms/ or /media/com_baforms/uploads/). Response body often echoes the stored filename. Attacker requests the file directly.- Uploaded file lands under docroot
- Apache/Nginx configured to execute
.phpin that directory
- Hardened configs block PHP execution in uploads via
.htaccessor nginxlocationdeny — but default Joomla installs do NOT ship this
Execute as web-server user (RCE)
www-data/apache/nobody. Attacker gains a foothold via China Chopper, WSO shell, or p0wny-shell. From here: exfil configuration.php (DB creds, secret), pivot to Joomla admin via password reset in DB, mine credentials, plant persistence.- PHP handler enabled for the upload directory
- SELinux/AppArmor profiles limiting httpd write/exec
- PHP
disable_functionsblockingexec/system/passthru
apache2 → sh → curl/wget is a classic webshell tell.Post-exploitation and lateral movement
configuration.php for DB creds, dumps #__users, adds a Super User row or resets an existing one. From admin panel they enable Joomla template editing to establish persistent RCE that survives extension removal. Also common: SEO-spam/skimmer injection, or pivoting to backend via credential reuse.- Local DB access from webshell
- Reused credentials or shared hosting
- Site isolation on managed hosting
- Read-only filesystem for templates
The supporting signals.
| In-the-wild status | Actively exploited — added to CISA KEV on disclosure day (2026-07-09). Zero-day exploitation confirmed before patch shipped. |
|---|---|
| Public PoC | PoC details published by mySites.guru; single curl one-liner reproducing upload is circulating in security Twitter/Mastodon. |
| EPSS | 0.00836 (low percentile despite KEV — EPSS lags reality here; treat KEV as the ground truth) |
| KEV | Listed 2026-07-09, FCEB due date per BOD 22-01 is 3 weeks out |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8 CRITICAL — no auth, no interaction, network reachable, full CIA |
| Affected versions | Balbooa Forms ≤ 2.4.0 on Joomla 3.x / 4.x / 5.x |
| Fixed version | 2.4.1 (released 2026-07-09) |
| Exposure population | Balbooa Forms is one of the top form extensions in the Joomla Extensions Directory; Shodan/Censys queries for com_baforms return tens of thousands of hosts |
| Disclosed | 2026-07-09 (coordinated with vendor patch) |
| CWE | CWE-434 — Unrestricted Upload of File with Dangerous Type |
noisgate verdict.
Unauthenticated single-request RCE against an internet-facing PHP application, with active in-the-wild exploitation confirmed by CISA KEV listing on the day of disclosure. There is no friction step in the chain that meaningfully narrows the exposed population — the decisive factor is the combination of AV:N/PR:N/UI:N reachability with confirmed mass exploitation.
Why this verdict
- No auth, no CSRF, no MIME check — the vendor advisory and PoC both confirm the endpoint accepts anonymous PHP uploads. There is no prerequisite compromise stage to soften severity.
- KEV as of disclosure day — CISA doesn't list on speculation. Active exploitation overrides EPSS's low 0.00836 score, which lags by weeks.
- Role multiplier — web-facing CMS component: the chain succeeds on the canonical deployment (public Joomla site with a contact/lead form). Blast radius: full site compromise → DB creds → potential lateral movement on shared hosting or reused admin creds → SEO poisoning, skimming, or foothold for further internal pivot.
- Compensating deployment hardening is rare — Joomla installs do not ship with PHP execution disabled in upload directories by default; SELinux/AppArmor is not the norm on shared LAMP hosting.
- Patch and mitigation are both cheap — 2.4.1 is a drop-in extension update. There is no operational reason to delay.
Why not higher?
9.8 is already at the CRITICAL ceiling for CVSS 3.1 in this configuration; the only headroom would be a Scope-changed variant, which is not applicable to a userland PHP webshell. There is no separate 'CRITICAL+' bucket in noisgate.
Why not lower?
Downgrading would require either evidence that the exposed population is trivial (it is not — Balbooa Forms is a mainstream Joomla forms extension) or evidence that exploitation requires a rare prerequisite (it does not — anonymous single-request). Neither condition holds.
What to do — in priority order.
- Disable the Balbooa Forms component in Joomla until 2.4.1 is applied — Site Admin → Extensions → Manage → set com_baforms to Disabled. This zeroes the attack surface immediately. Deploy within 3 days per noisgate mitigation SLA — but given KEV, do it in the next maintenance window today.
- Add a WAF virtual patch blocking multipart POSTs to Balbooa endpoints with executable extensions — In Cloudflare/AWS WAF/ModSecurity, block requests to
/index.php?option=com_baforms*that containContent-Disposition: filename=with.php,.phtml,.phar,.php3-8,.pht,.inc. Deploy within 24 hours if you cannot disable the component. - Deny PHP execution in Joomla upload directories — Drop
.htaccesswith<FilesMatch \.ph(p[3-8]?|tml|ar)$> Require all denied </FilesMatch>under/images/,/media/, and any Balbooa upload path. For nginx addlocation ~ ^/(images|media)/.*\.php$ { deny all; }. Do this now and keep it after patching. - Hunt for existing webshells — KEV-listed with pre-patch exploitation means you may already be popped. Grep uploads directories for
<?php, unusual filenames (high entropy, mixed case), files newer than 2026-06-01, and Joomla#__usersfor unexpected Super Users. Run within 24 hours. - Rotate secrets in configuration.php and DB creds — If you find any indicator of compromise, treat DB credentials, Joomla secret, API tokens, and any admin passwords as burned. Rotate before restoring the patched site.
- Relying on EPSS 0.00836 as low-risk — EPSS is a lagging model; KEV is the authoritative signal here.
- Reverse proxy / Cloudflare in front without a specific rule — a passthrough CDN does nothing against a legitimate-looking multipart POST unless you write the virtual patch above.
- Rate limiting — one HTTP request is enough for RCE. Rate limits don't help.
- Disabling Joomla registration — the endpoint is unauth on the frontend, not the admin panel.
Crowdsourced verification payload.
Run on each Joomla host as any user that can read the webroot. Invoke as ./check-baforms.sh /var/www/html (path to Joomla document root). Also runs a live HTTP probe against the site — provide site URL as SITE_URL=https://example.com ./check-baforms.sh /var/www/html. Prints VULNERABLE, PATCHED, or UNKNOWN and exits with 1/0/2 respectively.
#!/usr/bin/env bash
# noisgate: CVE-2026-56291 — Balbooa Forms unauth file upload
# Usage: ./check-baforms.sh <joomla-webroot>
# Optional: SITE_URL=https://site.tld ./check-baforms.sh <webroot>
set -u
ROOT="${1:-}"
if [[ -z "$ROOT" || ! -d "$ROOT" ]]; then
echo "UNKNOWN: pass Joomla webroot as arg 1" >&2
exit 2
fi
MANIFEST="$ROOT/administrator/components/com_baforms/baforms.xml"
ALT="$(find "$ROOT" -type f -name 'baforms.xml' 2>/dev/null | head -n1 || true)"
[[ -f "$MANIFEST" ]] || MANIFEST="$ALT"
if [[ -z "$MANIFEST" || ! -f "$MANIFEST" ]]; then
echo "UNKNOWN: Balbooa Forms not found under $ROOT"
exit 2
fi
VER=$(grep -oE '<version>[^<]+' "$MANIFEST" | head -n1 | sed 's/<version>//')
if [[ -z "$VER" ]]; then
echo "UNKNOWN: could not parse version from $MANIFEST"
exit 2
fi
FIXED="2.4.1"
# semver compare: is $VER < $FIXED?
lt() { [ "$1" = "$2" ] && return 1; [ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" = "$1" ]; }
if lt "$VER" "$FIXED"; then
STATUS="VULNERABLE"
RC=1
else
STATUS="PATCHED"
RC=0
fi
echo "Balbooa Forms version: $VER (fixed: $FIXED)"
echo "Manifest: $MANIFEST"
echo "Status: $STATUS"
# Optional live probe for stray webshells in common Balbooa upload dirs
for d in "$ROOT/images/baforms" "$ROOT/media/com_baforms/uploads" "$ROOT/images/com_baforms"; do
if [[ -d "$d" ]]; then
HITS=$(find "$d" -type f \( -iname '*.php*' -o -iname '*.phtml' -o -iname '*.phar' -o -iname '*.pht' -o -iname '*.inc' \) 2>/dev/null)
if [[ -n "$HITS" ]]; then
echo "WARNING: executable files present in upload dir $d:"
echo "$HITS"
RC=1
fi
fi
done
exit $RC
If you remember one thing.
#__users review, configuration.php secret rotation) in parallel. Assume public sites were probed before you patched.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.