# [CRIT] CISA Warns of Actively Exploited Joomla JCE Flaw Allowing PHP Code Execution

**Source:** The Hacker News
**Published:** 2026-06-17
**Article:** https://thehackernews.com/2026/06/cisa-warns-of-actively-exploited-joomla.html

## Threat Profile

CISA Warns of Actively Exploited Joomla JCE Flaw Allowing PHP Code Execution 
 Ravie Lakshmanan  Jun 17, 2026 Vulnerability / Supply Chain Attack 
The U.S. Cybersecurity and Infrastructure Security Agency (CISA) on Tuesday added a maximum-severity security flaw impacting Widget Factory Joomla Content Editor (JCE) to its Known Exploited Vulnerabilities (KEV) catalog, citing evidence of active exploitation.
The vulnerability, tracked as CVE-2026-48907 (CVSS score: 10.0), is a case of improper ac…

## Indicators of Compromise (high-fidelity only)

- **CVE:** `CVE-2026-48907`
- **IPv4 (defanged):** `84.201.6.54`
- **Domain (defanged):** `tidio.cc`

## MITRE ATT&CK Techniques

- **T1071.001** — Web Protocols
- **T1071.004** — DNS
- **T1071** — Application Layer Protocol
- **T1539** — Steal Web Session Cookie
- **T1555.003** — Credentials from Web Browsers
- **T1190** — Exploit Public-Facing Application
- **T1195.002** — Compromise Software Supply Chain
- **T1505.003** — Server Software Component: Web Shell
- **T1059.004** — Command and Scripting Interpreter: Unix Shell
- **T1136.001** — Create Account: Local Account
- **T1505** — Server Software Component
- **T1195.002** — Supply Chain Compromise: Compromise Software Supply Chain
- **T1071.001** — Application Layer Protocol: Web Protocols
- **T1568** — Dynamic Resolution

## Kill chain phases observed

_(none detected from narrative keywords)_

## Recommended hunts

### Joomla JCE editor profile creation and PHP upload via unauthenticated endpoint (CVE-2026-48907)

`UC_19_5` · phase: **exploit** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.http_method) as method values(Web.src) as src values(Web.dest) as dest values(Web.http_user_agent) as ua from datamodel=Web.Web where Web.http_method="POST" (Web.url="*option=com_jce*" OR Web.url="*task=plugin*" OR Web.url="*plugin=imgmanager*" OR Web.url="*index.php*jce*profile*") by Web.src Web.dest Web.url Web.http_method Web.status
| `drop_dm_object_name(Web)`
| where status<400
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
```

**Defender KQL:**
```kql
let JceEndpoints = dynamic(["com_jce","task=plugin","plugin=imgmanager","profile=Default"]);
let UploadExt = dynamic([".php",".phtml",".phar",".php5",".php7",".pht"]);
// Web shell file dropped under a Joomla content path by the web-server process
DeviceFileEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("w3wp.exe","httpd.exe","php-cgi.exe","php-fpm","nginx.exe","apache2")
| where FolderPath has_any (@"\images\", @"\components\com_jce\", @"\media\jce\", @"\tmp\", @"/images/", @"/components/com_jce/", @"/media/jce/", @"/tmp/")
| where FileName has_any (UploadExt)
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, FolderPath, FileName, SHA256, FileOriginUrl, FileOriginIP
| order by Timestamp desc
```

### PHP webshell file actions originating from web server process (Beloved PBN wp_posts shell)

`UC_19_6` · phase: **install** · confidence: **Medium** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=t count values(Filesystem.file_path) as file_path values(Filesystem.action) as action values(Filesystem.dest) as dest from datamodel=Endpoint.Filesystem where Filesystem.process_name IN ("w3wp.exe","httpd.exe","php-cgi.exe","php-fpm","nginx.exe") (Filesystem.file_name="*.php" OR Filesystem.file_name="*.phtml" OR Filesystem.file_name="*.phar" OR Filesystem.file_name=".htaccess") by Filesystem.dest Filesystem.process_name Filesystem.file_path Filesystem.action _time
| `drop_dm_object_name(Filesystem)`
| eventstats dc(file_path) as unique_paths by dest process_name
| where unique_paths >= 5
```

**Defender KQL:**
```kql
let WebServerProcs = dynamic(["w3wp.exe","httpd.exe","php-cgi.exe","php-fpm","nginx.exe","apache2"]);
let ShellExt = dynamic([".php",".phtml",".phar",".pht",".htaccess"]);
DeviceFileEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ (WebServerProcs)
| where ActionType in ("FileCreated","FileModified","FileRenamed","FileDeleted")
| where FileName has_any (ShellExt) or PreviousFileName has_any (ShellExt)
| where FolderPath !has @"\wp-content\plugins\" and FolderPath !has @"\wp-content\upgrade\"
| summarize WriteCount = count(), DistinctPaths = dcount(FolderPath), SamplePath = any(FolderPath), SampleFile = any(FileName), arg_max(Timestamp, *)
          by DeviceName, InitiatingProcessFileName, bin(Timestamp, 10m)
| where WriteCount >= 5 or DistinctPaths >= 3
| order by Timestamp desc
```

### WordPress backdoor admin account created via injected JavaScript (OptinMonster/TrustPulse supply chain)

`UC_19_7` · phase: **install** · confidence: **Medium** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.http_method) as method values(Web.user) as user values(Web.http_referrer) as referrer values(Web.src) as src from datamodel=Web.Web where Web.http_method="POST" (Web.url="*wp-admin/user-new.php*" OR Web.url="*wp-admin/users.php?action=createuser*" OR Web.url="*wp-admin/plugin-install.php*" OR Web.url="*wp-admin/update.php?action=install-plugin*") by Web.src Web.dest Web.url Web.http_referrer
| `drop_dm_object_name(Web)`
| eventstats dc(url) as actions by src
| where actions >= 2
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
```

**Defender KQL:**
```kql
let AdminCreateEndpoints = dynamic(["wp-admin/user-new.php","wp-admin/users.php","action=createuser","action=install-plugin","plugin-install.php"]);
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where ActionType == "HttpConnectionInspected" or RemoteUrl has_any (AdminCreateEndpoints)
| where RemoteUrl has_any (AdminCreateEndpoints)
| summarize HitCount=count(), Endpoints=make_set(RemoteUrl), Process=any(InitiatingProcessFileName)
        by DeviceName, RemoteIP, bin(Timestamp,5m)
| where HitCount >= 2
| order by Timestamp desc
```

### Web-server process beacon to JCE/Beloved-PBN campaign IOC tidio.cc or 84.201.6.54

`UC_19_8` · phase: **c2** · confidence: **Medium** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest_ip) as dest_ip values(All_Traffic.dest) as dest values(All_Traffic.src) as src values(All_Traffic.process) as process values(All_Traffic.user) as user from datamodel=Network_Traffic.All_Traffic where (All_Traffic.dest_ip="84.201.6.54" OR All_Traffic.dest="*tidio.cc*") by All_Traffic.src All_Traffic.dest_ip All_Traffic.dest All_Traffic.process
| `drop_dm_object_name(All_Traffic)`
| appendpipe [| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(DNS.src) as src values(DNS.query) as query from datamodel=Network_Resolution.DNS where DNS.query="*tidio.cc" by DNS.src DNS.query | `drop_dm_object_name(DNS)`]
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
```

**Defender KQL:**
```kql
let IocIPs = dynamic(["84.201.6.54"]);
let IocDomains = dynamic(["tidio.cc"]);
union
(DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteIP in (IocIPs) or RemoteUrl has_any (IocDomains)
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName, RemoteIP, RemoteUrl, RemotePort, Protocol),
(DeviceEvents
| where Timestamp > ago(30d)
| where ActionType == "DnsQueryResponse"
| where RemoteUrl has_any (IocDomains) or AdditionalFields has_any (IocDomains)
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl, AdditionalFields)
| order by Timestamp desc
```

### Beaconing — periodic outbound to small set of destinations

`UC_BEACONING` · phase: **c2** · confidence: **Medium**

**Splunk SPL (CIM):**
```spl
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
    from datamodel=Network_Traffic.All_Traffic
    where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
    by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
```

**Defender KQL:**
```kql
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
         prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
    by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
```

### Infostealer — non-browser process accessing browser cookie/login DBs

`UC_BROWSER_STEALER` · phase: **actions** · confidence: **High**

**Splunk SPL (CIM):**
```spl
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
    from datamodel=Endpoint.Filesystem
    where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
        OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
        OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
        OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
        OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
      AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
    by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
```

**Defender KQL:**
```kql
DeviceFileEvents
| where Timestamp > ago(7d)
| where InitiatingProcessAccountName !endswith "$"
| where FolderPath has_any (@"\Google\Chrome\User Data\", @"\Microsoft\Edge\User Data\", @"\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
```

### Trusted vendor binary / installer launching unusual children

`UC_SUPPLY_CHAIN` · phase: **exploit** · confidence: **Medium**

**Splunk SPL (CIM):**
```spl
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
    from datamodel=Endpoint.Processes
    where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
      AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
    by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
```

**Defender KQL:**
```kql
DeviceProcessEvents
| where Timestamp > ago(7d)
| where AccountName !endswith "$"
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
```

### IOC-driven hunts (use shared templates)

These are standard IOC-substitution hunts — the canonical SPL and KQL live once in [`_TEMPLATES.md`](../_TEMPLATES.md), so we don't repeat the same boilerplate on every CVE / hash / network-IOC briefing.

- **Network connections to article IPs / domains** ([template](../_TEMPLATES.md#network-ioc)) — phase: **c2**, confidence: **High**
  - IP / domain IOC(s): `84.201.6.54`, `tidio.cc`

- **Asset exposure — vulnerability matches article CVE(s)** ([template](../_TEMPLATES.md#asset-exposure)) — phase: **recon**, confidence: **High**
  - CVE(s): `CVE-2026-48907`


## Why this matters

Severity classified as **CRIT** based on: CVE present, IOCs present, 9 use case(s) fired, 14 technique(s) inferred. Read the full article for actor attribution, tooling details, and any defanged IOCs in the body that aren't visible in the RSS summary.
