01 / Executive summary
On 22 October 2026, a deliberately vulnerable PHP/Apache container was tested from a separate Kali Linux VM using the PHP2Shell CVE-2026-109618/109619 research repository. A single exploit invocation disclosed memory addresses, delivered its final payload, and caused the target to write the output of the Linux id command to a dedicated proof volume. The output identified the executing account as www-data (UID 33).
At the same time, Apache/PHP syslog events were received by QRadar, including zend_mm_heap corrupted and an Apache worker abort. This provided a concrete example of how successful RCE can coexist with a normal-looking HTTP 200 response and how application logs alone may expose exploitation side effects rather than the executed command itself.
02 / Lab architecture and controls
192.168.1.101
192.168.1.27:18088
172.19.0.2:80
192.168.1.188:514/UDP
| Component | Configuration / purpose |
|---|---|
| Target image | Official php:8.4.26-apache, Apache prefork/mod_php; single-worker configuration. |
| Application | Minimal /index.php returning OK; no intentionally vulnerable CMS, LFI, or file upload application. |
| Exploit source | xuehuolll/php2shell-CVE-2026-109618-109619, pinned locally at commit 967585c. |
| Network | Dedicated Docker bridge cve_php2shell_isolated, subnet 172.19.0.0/16; target published only on host LAN address, TCP 18088. |
| Storage | Read-only root filesystem, restricted mounts, temporary writable areas, dedicated named volume php2shell_output at /out. |
| Logging | Docker syslog driver, RFC 5424, tag php2shell-rce-lab, facility local0, QRadar UDP/514. |
Containment validation
Dedicated DOCKER-USER forwarding rules allowed the intended Kali-to-target HTTP path and established return traffic while dropping other routed traffic to/from the target. A separate host INPUT rule blocked container-originated connections to the Docker host. Both rule sets were checked before exploitation; the Kali HTTP test and syslog ingestion were also verified.
03 / Exploitation and evidence
The local repository revision matched on Kali and Portainer. The PoC supported a 14-byte maximum command string and defaulted to three attempts; the exercise explicitly limited execution to one attempt. The benign command id>/out/p1 produced a persistent, easily inspected artifact without requesting a shell or network callback.
Observed exploit stages
[*] attempt 1/1
[*] rotating worker
[*] leak 1/2: chunk base
[+] chunk base = 0x7f61c6600000
[*] rotating worker
[*] leak 2/2: libphp base
[+] libphp base = 0x7f61c826d000
[*] rotating worker
[*] fire: HTDATA=0x7f61c66690d8 ZIF=0x7f61c86d2370
[*] fire sent; check the proof file on the targetThe client reported two address disclosures followed by a final payload delivery. Its completion message was not treated as proof of execution: the proof was checked independently inside the target container.
RCE PROOF FOUND:
uid=33(www-data) gid=33(www-data) groups=33(www-data)id in the PHP/Apache container as www-data. No persistence, lateral movement, data theft, or outbound command-and-control was part of the test.Event timeline (UTC)
Baseline connectivity: Kali requested GET /index.php; HTTP 200 and OK returned. QRadar received the Apache access event.
Exploit activity: QRadar recorded POST /index.php, HTTP 200, source 192.168.1.101.
Runtime instability: zend_mm_heap corrupted and AH00161 (MaxRequestWorkers reached) were logged.
Worker abort: Apache logged AH00051, child PID 18 exited with signal 6 (Abort).
Independent verification: /out/p1 contained the www-data identity output. The target container was then stopped.
Selected QRadar evidence
2026-10-22T20:55:06Z ... 192.168.1.101 ...
"POST /index.php HTTP/1.1" 200 693 "-" "-"
2026-10-22T20:55:06Z ... zend_mm_heap corrupted
2026-10-22T20:55:06Z ... [mpm_prefork:error] ...
AH00161: server reached MaxRequestWorkers setting
2026-10-22T20:55:07Z ... [core:notice] ...
AH00051: child pid 18 exit signal Abort (6), possible coredumpExcerpted from the supplied QRadar event output. Source IP is present in the Apache access line; the crash events do not independently identify the HTTP client. Correlation relies on the common server/log source and close timestamps.
04 / IBM QRadar detection engineering
Primary hunting hypothesis: A PHP memory-corruption error or Apache worker abort occurring near unexpected HTTP POST activity on the same web server warrants investigation. Treat the runtime errors as high-value indicators of abnormal behavior, not unique signatures for PHP2Shell.
AQL — search PHP corruption and Apache worker failures
SELECT
starttime,
LOGSOURCENAME(logsourceid) AS LogSource,
sourceip,
UTF8(payload) AS RawEvent
FROM events
WHERE
UTF8(payload) ILIKE '%zend_mm_heap corrupted%'
OR UTF8(payload) ILIKE '%AH00051%'
OR UTF8(payload) ILIKE '%AH00161%'
ORDER BY starttime DESC
LAST 24 HOURSUse an appropriate time window and scope to the PHP/Apache log source or Docker syslog tag. Depending on QRadar parsing, sourceip may be the syslog sender (Docker host), not the remote HTTP client; parse the Apache access message separately.
AQL — review nearby HTTP POST requests
SELECT
starttime,
LOGSOURCENAME(logsourceid) AS LogSource,
UTF8(payload) AS RawEvent
FROM events
WHERE
UTF8(payload) ILIKE '%"POST /index.php HTTP/%'
ORDER BY starttime DESC
LAST 24 HOURSRecommended CRE correlation logic
- Building block A — PHP runtime anomaly: event payload contains
zend_mm_heap corrupted, optionally enriched with other PHP allocator/segfault messages. - Building block B — worker failure: Apache event contains
AH00051or an equivalent worker abort/segfault event. - Building block C — HTTP context: POST activity against the affected host in the preceding ~2 minutes; prefer normalized web-server destination/asset identity over generic QRadar
sourceip. - Offense candidate: A + B on the same web server within 2 minutes; increase confidence when C also exists. Add exploit-request bursts, unexpected user agents, and suspicious process execution where telemetry exists.
Implement with CRE event tests, building blocks, and a common asset/log-source reference; validate exact UI tests and field mappings in the installed QRadar version. Do not join solely on the syslog sender IP when multiple containers share a Docker host.
Suggested severity
High when memory corruption and worker abort coincide with suspicious web activity on an exposed PHP service. Critical when paired with confirmed Apache-spawned shell/process activity, file-write proof, or other post-exploitation telemetry.
False positives
Genuine PHP defects, buggy extensions, malformed requests, worker configuration limits, routine restarts, and load tests can produce overlapping signals. AH00161 alone is not an exploit indicator.
05 / Microsoft Sentinel detection engineering
Sentinel was not connected to this lab during the exercise. The following queries are portable detection starting points, not validated outputs from the test. They assume Apache/PHP messages are ingested into a syslog table and may need adaptation for AMA transformations, DCR mappings, custom tables, and host naming.
KQL — PHP allocator and Apache crash signals
Syslog
| where TimeGenerated > ago(24h)
| where SyslogMessage has_any (
"zend_mm_heap corrupted", "AH00051", "AH00161")
| extend Signal = case(
SyslogMessage contains "zend_mm_heap corrupted", "PHP heap corruption",
SyslogMessage contains "AH00051", "Apache worker abort",
SyslogMessage contains "AH00161", "Apache worker limit",
"Other")
| project TimeGenerated, Computer, HostName, ProcessName,
Signal, SyslogMessage
| order by TimeGenerated descKQL — time-bucket correlation with POST activity
let Lookback = 24h;
let Window = 2m;
let WebLogs = Syslog
| where TimeGenerated > ago(Lookback)
| extend Server = tostring(Computer)
| project TimeGenerated, Server, SyslogMessage;
let Crashes = WebLogs
| where SyslogMessage contains "zend_mm_heap corrupted"
or SyslogMessage contains "AH00051"
| project CrashTime = TimeGenerated, Server,
CrashMessage = SyslogMessage;
let Posts = WebLogs
| where SyslogMessage contains '"POST '
| project PostTime = TimeGenerated, Server,
PostMessage = SyslogMessage;
Crashes
| join kind=inner Posts on Server
| where PostTime between ((CrashTime - Window) .. (CrashTime + Window))
| project Server, CrashTime, PostTime, CrashMessage, PostMessage
| order by CrashTime descThis is a hunting query, not a production analytic. It can produce many-to-many matches on busy hosts. In this lab, the RFC 5424 hostname was the Docker host; production environments should extract container/service identity (for example the syslog tag) and correlate on that instead of Computer alone. Confirm your ingestion maps the Apache text to SyslogMessage.
KQL — stronger process evidence when Defender telemetry exists
DeviceProcessEvents
| where Timestamp > ago(24h)
| where InitiatingProcessFileName in~ ("apache2", "httpd", "php-fpm")
| where FileName in~ ("sh", "bash", "dash", "curl", "wget", "python", "perl")
| project Timestamp, DeviceName, FileName, ProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessCommandLine, AccountName
| order by Timestamp descRequires Defender for Endpoint device telemetry and suitable Linux/container visibility; this was not present or demonstrated in the PoC. A command may execute through another process chain, so this is one coverage layer, not a complete RCE detector.
Suggested Sentinel analytics
- Scheduled rule: alert on heap corruption plus Apache worker abort within two minutes on the same server/service; run every five minutes with an appropriate lookback and duplicate suppression.
- Context enrichment: correlate access logs, WAF events, reverse-proxy requests, host process telemetry, and deployment/change windows.
- Incident entity mapping: affected host and HTTP client IP only when accurately parsed; do not map the Docker syslog sender as the attacker.
- Prioritization: elevate when an exposed server also shows suspicious child processes, anomalous outbound connections, or unauthorized file creation.
06 / SOC triage and response playbook
- Validate: identify the affected PHP service, version, exposure, recent changes, and whether the alert is from a real server or a known authorized test.
- Preserve: export raw access/error logs, SIEM events, process and network telemetry, relevant container metadata, image digests, and volatile evidence where feasible.
- Correlate: reconstruct client IPs, request paths, timestamps, WAF actions, worker crashes, child processes, and outbound connections. Note that HTTP 200 does not rule out compromise.
- Contain: if compromise is suspected, restrict external access and egress, isolate the affected workload under incident-response procedures, and preserve evidence before destructive changes.
- Remediate: consult authoritative vendor advisories for affected versions and fixed releases; rebuild from trusted images, rotate exposed secrets where warranted, and hunt for persistence or lateral movement.
- Improve: test the detections with authorized replay or controlled lab activity; record false positives, log-source blind spots, and time-to-detect.
07 / Lessons learned and next steps
What worked
- Dedicated network and firewall containment verified before execution.
- Simple application avoided conflating PHP-core behavior with CMS flaws.
- One controlled attempt produced independently verified command output.
- QRadar captured access logs, PHP allocator corruption, and Apache worker abort.
What to improve
- Add host/container process execution and file-write telemetry.
- Normalize Apache client IP and container identity at ingestion.
- Test correlations against benign crashes and legitimate workload spikes.
- Document immutable evidence exports, image digest, and cleanup completion.
Operational closure
The target container was stopped after validation. A separate teardown checklist should confirm removal of the lab-specific DOCKER-USER jump/chain, host INPUT rule, isolated Docker network, published port, and any no-longer-needed containers/volumes—without disturbing other Docker services. Retain evidence before deleting the proof volume.
References and provenance
- PHP2Shell research repository — locally tested revision
967585c; source of PoC and CVE labels. - IBM QRadar documentation — AQL, CRE and log-source configuration.
- Microsoft Sentinel documentation — analytics rules and KQL hunting.
- Defender XDR DeviceProcessEvents schema — endpoint process telemetry.
- Primary evidence: Kali PoC console output, Docker target proof-file output, and QRadar raw events captured during the authorized lab session on 22 October 2026.
Prepared as a reproducible defensive research case study. Example detection queries require environment-specific validation. IP addresses shown are private lab addresses; memory addresses are from one observed exploit run and are not reusable indicators of compromise.