
Detection rules for CVE-2026-23918 Apache http2 RCE - Credit: stringa.ai, isec.pl
Published: 2026-05-04
CVSSv3: 8.8 (High)
Type: Remote Code Execution / Denial of Service (Double-Free Memory Corruption)
Component: Apache HTTP Server mod_http2 (h2_mplx.c stream cleanup path)
Affected: Apache HTTP Server 2.4.66 with HTTP/2 enabled and multi-threaded MPM
References:
CVE-2026-23918 is a double-free memory corruption vulnerability in the HTTP/2 protocol implementation of Apache HTTP Server 2.4.66, affecting only the mod_http2 module's stream cleanup path in h2_mplx.c. It allows an unauthenticated remote attacker to crash Apache worker processes (Denial of Service) with a single TCP connection and two HTTP/2 frames. Under conditions present on Debian-derived systems and official Apache Docker images, the double-free can be shaped into full Remote Code Execution.
DoS exploitation has been confirmed in the wild. Large-scale internet scans targeting HTTP/2 endpoints have been observed. The RCE exploit has been proven viable in controlled environments, though there is no evidence of widespread public exploitation for RCE at this time.
MPM prefork is not affected — the vulnerability requires a multi-threaded MPM configuration (worker, event, or similar). CVE-2026-23918 affects only Apache HTTP Server version 2.4.66.
Attacker opens HTTP/2 connection to Apache 2.4.66 (mod_http2 loaded, multi-threaded MPM)
└─ Sends HTTP/2 HEADERS frame on stream N (opens the stream)
└─ Immediately sends RST_STREAM on stream N (non-zero error code)
└─ Sent BEFORE the multiplexer has registered the stream
Two nghttp2 callbacks fire in sequence:
├─ on_frame_recv_cb (RST received) → calls h2_mplx_c1_client_rst → m_stream_cleanup
└─ on_stream_close_cb (stream closed) → calls h2_mplx_c1_client_rst → m_stream_cleanup
Result: same h2_stream pointer pushed onto spurge[] cleanup array TWICE
c1_purge_streams() iterates spurge[] and calls h2_stream_destroy() on each entry:
├─ First call: valid — frees the stream
└─ Second call: DOUBLE-FREE — operates on already-freed memory → heap corruption
DoS path (trivial, in the wild):
└─ Heap corruption → SIGABRT in worker process → worker dies → service disruption
RCE path (requires mmap allocator — default on Debian/Ubuntu and official Docker):
└─ Attacker places fake h2_stream struct at freed virtual address via mmap reuse
└─ Points pool cleanup function pointer to system()
└─ Uses Apache scoreboard shared memory (fixed address, ASLR-resistant) as payload container
└─ c1_purge_streams() executes system() with attacker-controlled argument → RCE
Key asymmetry: The DoS path requires no heap manipulation skill and is actively being exploited. The RCE path is technically demanding but has been demonstrated in lab conditions and will almost certainly be weaponized in the near future given the scoreboard's ASLR-resistant fixed address.
This section explains why the detection tooling here differs substantially from a typical local privilege escalation package.
Copy Fail (CVE-2026-31431) was a host-side, post-access vulnerability. The attacker needed existing presence on the system. Detection lived primarily at the syscall layer (auditd, Wazuh) with YARA scanning for the PoC script on disk.
CVE-2026-23918 is a network-side, pre-access vulnerability. The exploit arrives as HTTP/2 protocol frames over the wire before any application code runs. This shifts the detection stack significantly:
The rule-of-thumb: for network-level RCE, work outward-in (network → WAF → host). For local privilege escalation, work from the host outward.
Read this before deploying any rules.
1. TLS terminates HTTP/2 visibility. Most production Apache deployments serve HTTPS. Suricata cannot inspect the contents of encrypted HTTP/2 frames without TLS decryption being configured. If your Suricata deployment does not have access to TLS session keys or a decryption mirror, the network-level rules below will only catch:
For HTTPS deployments, enable Suricata's TLS decryption via the tls-decrypt setting and session key logging, or rely on the WAF (ModSecurity/Coraza) and host-based (auditd/Wazuh) layers instead.
2. ModSecurity cannot block the exploit trigger. The double-free occurs inside the HTTP/2 frame parser, before a complete HTTP request is assembled and passed to ModSecurity. The WAF sees the request only after frame parsing completes — at which point the damage may already be done. ModSecurity in this package is used for anomaly detection, rate limiting, and post-exploitation detection, not as a blocker for the trigger.
3. MPM prefork is unaffected.
If your Apache deployment uses mpm_prefork_module (single-threaded), this vulnerability does not apply. The bug only manifests in multi-threaded MPMs (mpm_event_module or mpm_worker_module). Check with apachectl -V | grep MPM before deploying rules that would produce false positives on prefork servers.
4. RCE requires the mmap allocator. The RCE path (not the DoS path) requires APR's mmap allocator, which is the default on Debian-derived distributions and official Apache Docker images. RHEL/CentOS-based deployments using jemalloc or system malloc have reduced RCE risk, but are still fully vulnerable to DoS.
5. No stable post-exploitation IoCs yet. No vendor-published IoCs for post-exploitation activity exist as of this writing. The YARA rules and auditd rules targeting post-exploitation behavior are based on general web shell and privilege escalation patterns — they will catch common outcomes but not a sophisticated, bespoke payload.
Apply in order of preference. Each is more disruptive than the last, but each is more complete.
# Option 1 (Preferred): Upgrade to 2.4.67
# See Patching & Remediation section below
# Option 2: Disable HTTP/2 in Apache config (no reboot required, restart required)
# In httpd.conf or relevant VirtualHost / site config:
# Remove or comment out: Protocols h2 h2c http/1.1
# Replace with: Protocols http/1.1
# Then:
apachectl configtest && sudo systemctl restart apache2
# Option 3: Switch to MPM prefork (eliminates vulnerability entirely — more disruptive)
sudo a2dismod mpm_event mpm_worker
sudo a2enmod mpm_prefork
apachectl configtest && sudo systemctl restart apache2
# Option 4: Reverse proxy HTTP/2 termination
# If nginx, HAProxy, or a CDN is in front of Apache and terminates HTTP/2,
# Apache only receives HTTP/1.1 — confirm your proxy config explicitly:
# nginx: proxy_http_version 1.1; (already the default for upstream connections)
# HAProxy: use-server-close + http/1.1 on backend bind
# Verify with: curl -v --http2 https://your-origin-directly
Verify your mitigation: After disabling HTTP/2, confirm with:
curl -s -o /dev/null -w "%{http_version}" --http2 http://localhost/ # Should return "1.1", not "2" apachectl -M | grep http2 # Should produce no output
Save as cve-2026-23918.rules and reference from suricata.yaml.
Prerequisites:
- Suricata 6.0+ for
http2.frametype/http2.errorcodekeyword support (Suricata 7.x recommended)app-layer.protocols.http2.enabled: yesinsuricata.yaml- TLS decryption configured for HTTPS coverage (see Detection Limitations above)
$HTTP_SERVERSvariable set to include your Apache hosts- SIDs below are examples — adjust to fit your local SID policy
# =============================================================
# CVE-2026-23918 Apache HTTP/2 Double-Free — Suricata Rules
# =============================================================
# Rule overview:
# 9926231801 — HTTP/2 RST_STREAM with non-zero error code (app layer, high fidelity)
# 9926231802 — RST_STREAM flood threshold (DoS scanning pattern)
# 9926231803 — Raw HTTP/2 RST_STREAM frame detection (h2c / non-TLS fallback)
# 9926231804 — HEADERS+RST rapid sequence targeting HTTP/2 port (behavioral)
# 9926231805 — Apache worker crash signal (host-network correlation)
# 9926231806 — Outbound connection from Apache user post-RCE (lateral movement)
# =============================================================
# --- Rule 1: HTTP/2 RST_STREAM with non-zero error code (app layer) ---
# Requires: Suricata HTTP/2 app layer parsing, TLS decryption for HTTPS
# This is the highest-fidelity rule — targets the exact protocol condition that
# triggers the double-free. RST_STREAM with error code 0 (NO_ERROR) is normal
# and common; any non-zero error code in the early-reset context is suspicious.
# Expected false positives: legitimate HTTP/2 connection errors (network issues,
# client bugs). Tune threshold if noisy in your environment.
alert http2 $EXTERNAL_NET any -> $HTTP_SERVERS any \
(msg:"CVE-2026-23918 Apache mod_http2 Double-Free - RST_STREAM with non-zero error code"; \
flow:established,to_server; \
http2.frametype:3; \
http2.errorcode:!0; \
classtype:web-application-attack; \
reference:cve,2026-23918; \
sid:9926231801; rev:1;)
# --- Rule 2: RST_STREAM flood threshold (active DoS/scan pattern) ---
# Triggers after 10 RST_STREAM frames with non-zero error code from one source
# within 30 seconds. This matches the confirmed in-the-wild DoS scanning behavior.
# Lower threshold (e.g., count 5) for higher sensitivity in low-traffic environments.
alert http2 $EXTERNAL_NET any -> $HTTP_SERVERS any \
(msg:"CVE-2026-23918 Apache mod_http2 Double-Free - RST_STREAM flood (active DoS/exploit scan)"; \
flow:established,to_server; \
http2.frametype:3; \
http2.errorcode:!0; \
threshold: type both, track by_src, count 10, seconds 30; \
classtype:denial-of-service; \
reference:cve,2026-23918; \
sid:9926231802; rev:1;)
# --- Rule 3: Raw RST_STREAM frame detection (h2c cleartext / TLS fallback) ---
# Matches the raw HTTP/2 RST_STREAM frame header bytes in cleartext traffic.
# HTTP/2 RST_STREAM frame: 3-byte length (0x000004) | type (0x03) | flags (0x00)
# This does NOT require app-layer HTTP/2 parsing and catches h2c (non-TLS) traffic.
# Higher false positive rate than Rule 1 — use threshold in production.
# For h2c on non-standard ports, adjust destination ports accordingly.
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS [80,8080,8000,8443] \
(msg:"CVE-2026-23918 Apache mod_http2 - HTTP/2 RST_STREAM frame detected (cleartext)"; \
flow:established,to_server; \
content:"|00 00 04 03 00|"; depth:5; offset:0; \
threshold: type both, track by_src, count 5, seconds 30; \
classtype:web-application-attack; \
reference:cve,2026-23918; \
sid:9926231803; rev:1;)
# --- Rule 4: HTTP/2 connection preface followed by rapid RST (behavioral) ---
# HTTP/2 client preface begins with "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".
# Matching this followed by a rapid close is consistent with DoS scanning tooling
# that establishes a connection, sends the trigger, and moves to the next target.
# Most useful on cleartext h2c; for HTTPS this requires TLS decryption.
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS [80,8080,8000] \
(msg:"CVE-2026-23918 Apache mod_http2 - HTTP/2 client preface with rapid RST_STREAM (exploit pattern)"; \
flow:established,to_server; \
content:"PRI * HTTP/2.0|0d 0a 0d 0a|SM|0d 0a 0d 0a|"; depth:24; offset:0; \
content:"|00 00 04 03|"; distance:0; within:512; \
classtype:web-application-attack; \
reference:cve,2026-23918; \
sid:9926231804; rev:1;)
# --- Rule 5: Apache version string exposure (scanner pre-targeting) ---
# Attackers actively scanning for vulnerable Apache 2.4.66 servers will often
# trigger a version-identifying response. Alert on Apache/2.4.66 in server headers.
# Useful for identifying which of your servers are exposed AND being actively scanned.
# Note: ServerTokens Prod in Apache config suppresses the version string (recommended).
alert http $HTTP_SERVERS any -> $EXTERNAL_NET any \
(msg:"CVE-2026-23918 Apache 2.4.66 version string in response - vulnerable version exposed"; \
flow:established,to_client; \
http.header; content:"Apache/2.4.66"; \
classtype:policy-violation; \
reference:cve,2026-23918; \
sid:9926231805; rev:1;)
# --- Rule 6: Suspicious outbound connection from web server process port ---
# Post-RCE, an attacker will likely establish a reverse shell or exfiltrate data.
# This rule detects NEW outbound TCP connections originating FROM HTTP server ports
# to external destinations, which is anomalous for legitimate Apache behavior.
# Tune $HOME_NET and $HTTP_SERVERS to avoid false positives on proxy configurations.
# This rule pairs with the auditd rule monitoring www-data/apache outbound connects.
alert tcp $HTTP_SERVERS [80,443,8080,8443] -> $EXTERNAL_NET ![$HTTP_PORTS,443,80] \
(msg:"CVE-2026-23918 Apache possible post-RCE reverse shell - outbound from web server port"; \
flow:established,to_server; \
classtype:trojan-activity; \
reference:cve,2026-23918; \
sid:9926231806; rev:1;)
After deploying in alert mode for 24–48 hours, review hits on Rules 3 and 4 — legitimate HTTP/2 clients may trigger these in high-traffic environments. If Rule 1 (app layer) is catching sufficient signal, Rules 3 and 4 can move to lower severity or be dropped.
For Suricata deployments with stream-depth limits, ensure the HTTP/2 preface pattern in Rule 4 falls within the inspection window.
Prerequisites:
- ModSecurity 2.x (
libapache2-mod-security2) or Coraza (drop-in successor, actively maintained)- OWASP Core Rule Set (CRS) 4.x recommended: coreruleset.org/installation
SecRuleEngine On(orDetectionOnlyfor logging-only mode during initial tuning)
As noted in the Detection Limitations section, ModSecurity cannot intercept the double-free trigger because the exploit operates at the HTTP/2 frame layer. However, ModSecurity provides three meaningful layers of value for this CVE:
Add to httpd.conf or an include file. These are Apache directives, not ModSecurity rules, but they reduce the HTTP/2 attack surface:
# ============================================================
# CVE-2026-23918 Apache HTTP/2 Hardening Directives
# ============================================================
# Limit concurrent streams per HTTP/2 session.
# The exploit typically uses 1 stream, but limiting sessions
# reduces the rate at which a single client can attempt the trigger.
H2MaxSessionRequests 100
# Restrict H2 stream push (unused surface, reduce complexity)
H2Push Off
# Suppress version information in Server headers.
# Prevents trivial identification of vulnerable 2.4.66 instances.
ServerTokens Prod
ServerSignature Off
# Constrain HTTP/2 window size — reduces memory available for heap spray
H2WindowSize 65535
# If HTTP/2 is not required at all:
# Protocols http/1.1
Save these in your ModSecurity custom rules file (e.g., /etc/modsecurity/cve-2026-23918.conf):
# ============================================================
# CVE-2026-23918 ModSecurity Detection Rules
# ============================================================
# Rule IDs 9923918xx — adjust range to fit your local policy.
# ============================================================
# Initialize per-IP request counter in the IP collection
SecAction \
"id:9923918001,\
phase:1,\
nolog,\
pass,\
initcol:ip=%{REMOTE_ADDR},\
setvar:ip.http2_requests=+1,\
expirevar:ip.http2_requests=60"
# Rule 01: Rate limit — block IPs sending more than 30 requests per minute
# Tune the threshold to match your expected legitimate traffic volume.
# This catches automated DoS scanning tools that rapidly recycle connections.
SecRule ip:http2_requests "@gt 30" \
"id:9923918002,\
phase:1,\
deny,\
status:429,\
log,\
msg:'CVE-2026-23918: Rate limit exceeded - possible DoS/exploit scan',\
tag:'CVE-2026-23918',\
tag:'OWASP_CRS/DoS',\
severity:'CRITICAL'"
# Rule 02: Detect abnormal connection error rates from same IP
# Legitimate clients rarely produce rapid sequences of HTTP errors.
# Repeated 400-level errors suggest exploit scanning or fuzzing.
SecAction \
"id:9923918003,\
phase:1,\
nolog,\
pass,\
initcol:ip=%{REMOTE_ADDR}"
SecRule RESPONSE_STATUS "@rx ^(4|5)[0-9]{2}" \
"id:9923918004,\
phase:5,\
nolog,\
pass,\
setvar:ip.error_count=+1,\
expirevar:ip.error_count=120"
SecRule ip:error_count "@gt 20" \
"id:9923918005,\
phase:1,\
log,\
pass,\
msg:'CVE-2026-23918: Elevated error rate from source IP - possible exploit scanning',\
tag:'CVE-2026-23918',\
severity:'WARNING'"
# ============================================================
# POST-EXPLOITATION DETECTION
# The following rules detect outcomes of successful RCE:
# web shell deployment and in-request command execution.
# These are NOT specific to CVE-2026-23918 but are the most
# likely post-exploitation patterns given the Apache context.
# ============================================================
# Rule 03: Web shell detection in POST body — command execution patterns
# Catches PHP web shells that use $_GET/$_POST to pass OS commands.
# Note: if you use legitimate PHP applications, tune false positives carefully.
SecRule REQUEST_BODY \
"@rx (?:system|exec|passthru|shell_exec|popen|proc_open)\s*\(\s*(?:\$_(?:GET|POST|REQUEST|COOKIE)|base64_decode)" \
"id:9923918010,\
phase:2,\
deny,\
status:403,\
log,\
msg:'CVE-2026-23918: Possible web shell command execution in POST body',\
tag:'CVE-2026-23918',\
tag:'WEBSHELL',\
severity:'CRITICAL'"
# Rule 04: Web shell access pattern — direct GET parameter command execution
# Catches requests like: GET /shell.php?cmd=id
# These are the most common web shell interaction patterns.
SecRule ARGS \
"@rx (?:(?:^|[;&|`])\s*(?:id|whoami|uname|cat\s+/etc|ls\s+/|pwd|wget\s+http|curl\s+http|bash\s+-[ci]|nc\s+-[el]|python[23]?\s+-c|perl\s+-e|ruby\s+-e))" \
"id:9923918011,\
phase:2,\
deny,\
status:403,\
log,\
msg:'CVE-2026-23918: OS command injection pattern in request arguments - possible post-exploit web shell',\
tag:'CVE-2026-23918',\
tag:'WEBSHELL',\
severity:'CRITICAL'"
# Rule 05: PHP web shell upload detection
# Catches multipart file uploads containing PHP code.
# If your application accepts PHP file uploads legitimately, tune carefully.
SecRule FILES_TMPNAMES "@inspectFile /etc/modsecurity/util/php-filter.pm" \
"id:9923918012,\
phase:2,\
log,\
deny,\
status:403,\
msg:'CVE-2026-23918: PHP code detected in file upload - possible web shell deployment',\
tag:'CVE-2026-23918',\
tag:'WEBSHELL',\
severity:'CRITICAL'"
# Rule 06: Reverse shell patterns in request data
# Catches common reverse shell one-liners often placed in web shells.
SecRule REQUEST_BODY|ARGS \
"@rx (?:bash\s+-i\s+>&?\s*/dev/tcp|/dev/tcp/[0-9]{1,3}\.[0-9]{1,3}|nc\s+(?:-e|-c)\s+/bin/(?:bash|sh)|python[23]?\s+-c\s+['\"]import\s+socket)" \
"id:9923918013,\
phase:2,\
deny,\
status:403,\
log,\
msg:'CVE-2026-23918: Reverse shell pattern in request - possible post-exploit activity',\
tag:'CVE-2026-23918',\
tag:'REVERSE_SHELL',\
severity:'CRITICAL'"
For the highest anomaly signal without excessive false positives, deploy CRS at Paranoia Level 2 with anomaly scoring enabled. The triggering connection behavior (malformed HTTP/2 leading to HTTP/1.x fallback errors, repeated resets) will accumulate anomaly score under CRS rules 920xxx and 921xxx and may breach the default inbound_anomaly_score_threshold of 5, generating alerts without custom rules.
Save as /etc/audit/rules.d/cve-2026-23918.rules
Reload with: sudo augenrules --load
Design principle: Because the exploit trigger lives at the network/kernel HTTP/2 parsing layer, auditd cannot catch the trigger itself. These rules detect:
- The outcome of DoS exploitation (Apache worker crash signals)
- Post-exploitation activity if RCE is achieved (shell execution, file writes, outbound connects by the Apache user)
## ============================================================
## CVE-2026-23918 Apache HTTP/2 Double-Free — Auditd Rules
## ============================================================
## These rules detect the CONSEQUENCES of exploitation, not the
## trigger. The trigger is a network protocol event and is
## detected by Suricata. These rules catch:
## 1. Apache worker process crashes (DoS outcome)
## 2. Shell execution by the web server user (RCE outcome)
## 3. Web root file creation (web shell deployment)
## 4. Outbound network connections by web server process (reverse shell)
##
## Distribution notes for UID values:
## - Debian/Ubuntu: www-data = uid 33
## - RHEL/Rocky/CentOS: apache = uid 48
## Adjust -F uid= values for your distribution. Use `id www-data`
## or `id apache` to confirm the UID on your systems.
## ============================================================
## --- Apache worker SIGABRT detection (DoS exploitation outcome) ---
## A double-free that reaches the crash path generates SIGABRT (signal 6).
## Monitoring kill() syscalls with a1=6 (SIGABRT) targets abnormal process
## termination, which Apache itself triggers on double-free detection.
## Correlate with Apache error log entries (child exited with signal 6).
-a always,exit -F arch=b64 -S kill -F a1=6 -k cve_2026_23918_sigabrt
-a always,exit -F arch=b32 -S kill -F a1=6 -k cve_2026_23918_sigabrt
## --- SIGSEGV monitoring (alternative crash path) ---
## Depending on heap state, the double-free may produce a SIGSEGV (signal 11)
## rather than SIGABRT. Both are abnormal for production Apache workers.
-a always,exit -F arch=b64 -S kill -F a1=11 -k cve_2026_23918_sigsegv
-a always,exit -F arch=b32 -S kill -F a1=11 -k cve_2026_23918_sigsegv
## --- Shell execution by web server user (RCE outcome - Debian/Ubuntu) ---
## If RCE is achieved via the mmap allocator path, the attacker's payload
## runs as the Apache worker user (www-data on Debian/Ubuntu, uid=33).
## Legitimate Apache does not exec() a shell. Any execve() of bash/sh/dash
## by www-data is anomalous and warrants immediate investigation.
-a always,exit -F arch=b64 -S execve -F uid=33 -F exe=/bin/bash -k cve_2026_23918_rce_shell_deb
-a always,exit -F arch=b64 -S execve -F uid=33 -F exe=/bin/sh -k cve_2026_23918_rce_shell_deb
-a always,exit -F arch=b64 -S execve -F uid=33 -F exe=/bin/dash -k cve_2026_23918_rce_shell_deb
-a always,exit -F arch=b64 -S execve -F uid=33 -F exe=/usr/bin/python3 -k cve_2026_23918_rce_shell_deb
-a always,exit -F arch=b64 -S execve -F uid=33 -F exe=/usr/bin/perl -k cve_2026_23918_rce_shell_deb
## --- Shell execution by web server user (RCE outcome - RHEL/Rocky, uid=48) ---
-a always,exit -F arch=b64 -S execve -F uid=48 -F exe=/bin/bash -k cve_2026_23918_rce_shell_rhel
-a always,exit -F arch=b64 -S execve -F uid=48 -F exe=/bin/sh -k cve_2026_23918_rce_shell_rhel
## --- Web root file creation (web shell deployment) ---
## Post-RCE, the most common next step is writing a persistent web shell.
## Monitor web root directories for new file creation and write operations.
## Adjust paths for your DocumentRoot configuration.
-w /var/www/html -p wa -k cve_2026_23918_webroot_write
-w /var/www -p wa -k cve_2026_23918_webroot_write
-w /srv/www -p wa -k cve_2026_23918_webroot_write
-w /usr/share/apache2/default-site -p wa -k cve_2026_23918_webroot_write
## --- Outbound network connections by web server user (reverse shell) ---
## Apache workers do not normally initiate outbound TCP connections.
## connect() syscalls by www-data/apache indicate post-exploitation activity.
-a always,exit -F arch=b64 -S connect -F uid=33 -k cve_2026_23918_apache_outbound_deb
-a always,exit -F arch=b64 -S connect -F uid=48 -k cve_2026_23918_apache_outbound_rhel
## --- Apache config and module modification (persistence) ---
## An attacker with RCE may attempt to persist by modifying Apache config
## or dropping a malicious module. Watch for writes to config directories.
-w /etc/apache2 -p wa -k cve_2026_23918_apache_config
-w /etc/httpd -p wa -k cve_2026_23918_apache_config
-w /etc/apache2/mods-enabled -p wa -k cve_2026_23918_apache_mods
After deployment, use this ausearch one-liner to check for crash-then-shell sequences:
# Find all CVE-2026-23918 related auditd events from the past 24 hours
sudo ausearch -k cve_2026_23918_sigabrt \
-k cve_2026_23918_rce_shell_deb \
-k cve_2026_23918_rce_shell_rhel \
-k cve_2026_23918_webroot_write \
--start yesterday -i
# Look for www-data process trees that include shell execution
sudo ausearch -k cve_2026_23918_rce_shell_deb --start today -i | grep -A5 "exe="
Save as a custom rules file (e.g., /var/ossec/etc/rules/local_rules.xml).
Prerequisites:
- Auditd rules above deployed and Wazuh auditd decoder active
- Apache error log (
/var/log/apache2/error.logor/var/log/httpd/error_log) added to Wazuh monitored files- Apache access log monitored for HTTP/2 connection error patterns
<!-- ==============================================================
CVE-2026-23918 Apache HTTP/2 Double-Free — Wazuh Rules
Requires:
- auditd rules from cve-2026-23918.rules deployed
- Apache error log monitored by Wazuh agent
============================================================== -->
<!-- Level 10: Apache worker crash signal (SIGABRT) detected via auditd -->
<rule id="113001" level="10">
<if_group>auditd</if_group>
<field name="audit.key">cve_2026_23918_sigabrt</field>
<description>CVE-2026-23918: SIGABRT sent to process — possible Apache worker double-free crash (DoS exploitation)</description>
<group>cve,denial_of_service,apache,http2,</group>
</rule>
<!-- Level 10: SIGSEGV variant crash path -->
<rule id="113002" level="10">
<if_group>auditd</if_group>
<field name="audit.key">cve_2026_23918_sigsegv</field>
<description>CVE-2026-23918: SIGSEGV sent to process — possible Apache worker memory corruption crash</description>
<group>cve,denial_of_service,apache,http2,</group>
</rule>
<!-- Level 14 CRITICAL: Multiple worker crashes in short window — active DoS -->
<rule id="113003" level="14" frequency="3" timeframe="60">
<if_matched_sid>113001</if_matched_sid>
<description>CVE-2026-23918 CRITICAL: Multiple Apache worker SIGABRT crashes within 60 seconds — active DoS exploitation in progress</description>
<group>cve,denial_of_service,apache,http2,high_confidence,</group>
</rule>
<!-- Level 15 CRITICAL: Shell execution by web server user — RCE achieved -->
<rule id="113004" level="15">
<if_group>auditd</if_group>
<field name="audit.key">cve_2026_23918_rce_shell_deb|cve_2026_23918_rce_shell_rhel</field>
<description>CVE-2026-23918 CRITICAL: Shell executed by web server user (www-data/apache) — RCE likely achieved, immediate incident response required</description>
<group>cve,rce,privilege_escalation,apache,http2,high_confidence,</group>
</rule>
<!-- Level 14 CRITICAL: Web shell written to web root -->
<rule id="113005" level="14">
<if_group>auditd</if_group>
<field name="audit.key">cve_2026_23918_webroot_write</field>
<description>CVE-2026-23918: File written to web root directory — possible web shell deployment post-RCE</description>
<group>cve,rce,webshell,apache,</group>
</rule>
<!-- Level 13 CRITICAL: Outbound connection by Apache worker process -->
<rule id="113006" level="13">
<if_group>auditd</if_group>
<field name="audit.key">cve_2026_23918_apache_outbound_deb|cve_2026_23918_apache_outbound_rhel</field>
<description>CVE-2026-23918: Outbound TCP connection by web server user — possible reverse shell post-RCE</description>
<group>cve,rce,reverse_shell,apache,</group>
</rule>
<!-- Level 14: RCE shell followed by outbound connection (reverse shell confirmed) -->
<rule id="113007" level="14">
<if_matched_sid>113004</if_matched_sid>
<if_group>auditd</if_group>
<field name="audit.key">cve_2026_23918_apache_outbound_deb|cve_2026_23918_apache_outbound_rhel</field>
<description>CVE-2026-23918 CRITICAL: Shell execution AND outbound connection by web server user — reverse shell active</description>
<group>cve,rce,reverse_shell,apache,high_confidence,</group>
</rule>
<!-- Level 12: Apache config modified (persistence attempt) -->
<rule id="113008" level="12">
<if_group>auditd</if_group>
<field name="audit.key">cve_2026_23918_apache_config|cve_2026_23918_apache_mods</field>
<description>CVE-2026-23918: Apache config or module directory modified — possible attacker persistence attempt</description>
<group>cve,rce,persistence,apache,</group>
</rule>
<!-- Level 10: Apache error log — child process crash (log-based correlation) -->
<!-- Requires Apache error log monitored by Wazuh, decoded via apache decoder -->
<rule id="113009" level="10">
<decoded_as>apache-errorlog</decoded_as>
<match>child pid \d+ exit signal Aborted|child process \d+ still did not exit|segmentation fault</match>
<description>CVE-2026-23918: Apache child process crash in error log — possible double-free DoS exploitation</description>
<group>cve,denial_of_service,apache,http2,</group>
</rule>
<!-- Level 13: Multiple Apache child crashes in error log + auditd SIGABRT (high confidence) -->
<rule id="113010" level="13">
<if_matched_sid>113009</if_matched_sid>
<if_matched_sid>113001</if_matched_sid>
<description>CVE-2026-23918: Apache error log crash + auditd SIGABRT — high-confidence active DoS, investigate immediately</description>
<group>cve,denial_of_service,apache,http2,high_confidence,</group>
</rule>
Save as cve_2026_23918.yar
Important scope note: Unlike Copy Fail (CVE-2026-31431), YARA cannot detect the exploit trigger for this vulnerability. The trigger is two raw HTTP/2 frames sent over a network connection — there is no script or file to scan. The YARA rules below target:
- Post-exploitation web shells that may be deployed after a successful RCE
- Reverse shell one-liners and encoded payloads in web-accessible files
- The exploit tool itself if it is present on a pivot host or attacker staging server
Recommended scan scope: web root directories (
/var/www/,/srv/www/), Apache temporary directories (/tmp/,/var/tmp/), and recently created files owned bywww-dataorapache.
rule CVE_2026_23918_PostExploit_PHP_WebShell {
meta:
description = "Post-exploitation PHP web shell — possible CVE-2026-23918 outcome"
author = "Detection Engineering"
reference = "https://insomnisec.com/posts/2026-05-05-cve-2026-23918-apache-http2-rce_v2/"
cve = "CVE-2026-23918"
date = "2026-05-08"
severity = "Critical"
note = "Not specific to CVE-2026-23918 trigger — detects likely post-exploitation artifacts"
strings:
$php_open = "<?php" ascii nocase
$php_short = "<?" ascii nocase
// OS command execution functions
$sys = "system(" ascii nocase
$exec = "exec(" ascii nocase
$passthru = "passthru(" ascii nocase
$shell_exec = "shell_exec(" ascii nocase
$popen = "popen(" ascii nocase
$proc_open = "proc_open(" ascii nocase
// Parameter sourcing — required for command injection
$get_param = "$_GET[" ascii
$post_param = "$_POST[" ascii
$req_param = "$_REQUEST[" ascii
$cookie_param = "$_COOKIE[" ascii
$server_param = "$_SERVER[" ascii
// Obfuscation patterns common in web shells
$b64decode = "base64_decode(" ascii nocase
$str_rot13 = "str_rot13(" ascii nocase
$gzinflate = "gzinflate(" ascii nocase
$eval_call = "eval(" ascii nocase
// Common web shell capability strings
$phpinfo = "phpinfo()" ascii nocase
$file_put = "file_put_contents(" ascii nocase
condition:
filesize < 512KB and
(
// Classic command web shell: PHP + execution function + parameter input
($php_open or $php_short) and
any of ($sys, $exec, $passthru, $shell_exec, $popen, $proc_open) and
any of ($get_param, $post_param, $req_param, $cookie_param)
)
or
(
// Obfuscated web shell: eval + decode chain
($php_open or $php_short) and
$eval_call and
any of ($b64decode, $str_rot13, $gzinflate)
)
}
rule CVE_2026_23918_PostExploit_ReverseShell_InFile {
meta:
description = "Reverse shell one-liner in web-accessible file — possible post-RCE persistence"
author = "Detection Engineering"
cve = "CVE-2026-23918"
date = "2026-05-08"
severity = "Critical"
note = "Scan web directories and /tmp; may also appear in crontabs and rc.local"
strings:
// Bash TCP reverse shell
$bash_tcp = "/dev/tcp/" ascii
$bash_rev = "bash -i >&" ascii nocase
// Netcat reverse shell
$nc_e = "nc -e /bin/" ascii nocase
$nc_c = "nc -c /bin/" ascii nocase
$ncat_e = "ncat -e /bin/" ascii nocase
// Python reverse shell
$py_socket = "import socket,subprocess" ascii
$py_pty = "import pty;pty.spawn" ascii
// Perl reverse shell
$perl_rev = "perl -e 'use Socket" ascii
// Common reverse shell via curl/wget pipe to bash
$curl_bash = "curl http" ascii
$wget_bash = "wget -O- http" ascii
$bash_pipe = "|bash" ascii
condition:
filesize < 1MB and
(
($bash_tcp and $bash_rev)
or ($nc_e or $nc_c or $ncat_e)
or ($py_socket and $py_pty)
or $perl_rev
or ($curl_bash and $bash_pipe)
or ($wget_bash and $bash_pipe)
)
}
rule CVE_2026_23918_ExploitTool_Artifacts {
meta:
description = "CVE-2026-23918 exploit tool artifacts — for scanning attacker staging hosts or memory dumps"
author = "Detection Engineering"
reference = "https://hadrian.io/blog/cve-2026-23918-apache-http-server-double-free-rce-in-http-2-implementation"
cve = "CVE-2026-23918"
date = "2026-05-08"
severity = "High"
note = "Matches known PoC tool strings — not expected in production Apache environments"
strings:
// h2_mplx.c specific identifier from public PoC analysis
$mplx_ref = "h2_mplx_c1_client_rst" ascii
$spurge_ref = "c1_purge_streams" ascii
$stream_ref = "h2_stream_destroy" ascii
// CVE reference strings that appear in PoC tools
$cve_str = "CVE-2026-23918" ascii
$version_target = "Apache/2.4.66" ascii
// HTTP/2 HEADERS + RST_STREAM frame bytes (common in PoC HTTP/2 libraries)
// HTTP/2 HEADERS frame header: type=0x01
$h2_headers_frame = { 00 00 ?? 01 }
// HTTP/2 RST_STREAM frame header: type=0x03 with payload=4
$h2_rst_frame = { 00 00 04 03 00 }
// Python h2 library usage (hyper-h2) typical in PoC tools
$hyper_h2 = "import h2" ascii
$h2_connection = "H2Connection" ascii
condition:
(
($mplx_ref or $spurge_ref or $stream_ref)
or
($cve_str and $version_target)
or
($hyper_h2 and $h2_connection and $h2_rst_frame)
)
}
Save as misp_cve_2026_23918.json and import via MISP → Events → Import.
Replace placeholder UUIDs with freshly generated UUID4s before import.
{
"Event": {
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"info": "CVE-2026-23918 Apache mod_http2 Double-Free — Remote DoS and possible RCE",
"threat_level_id": "2",
"analysis": "2",
"date": "2026-05-04",
"Attribute": [
{
"type": "vulnerability",
"category": "External analysis",
"to_ids": false,
"uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"comment": "CVE identifier",
"value": "CVE-2026-23918"
},
{
"type": "text",
"category": "Other",
"to_ids": false,
"uuid": "c3d4e5f6-a7b8-9012-cdef-012345678902",
"comment": "Vulnerability description",
"value": "Double-free in Apache HTTP Server 2.4.66 mod_http2 h2_mplx.c stream cleanup path. Triggered by HTTP/2 HEADERS frame immediately followed by RST_STREAM with non-zero error code before stream registration. Results in DoS (confirmed in-wild) or RCE (lab-demonstrated) in multi-threaded MPM configurations."
},
{
"type": "text",
"category": "Other",
"to_ids": false,
"uuid": "d4e5f6a7-b8c9-0123-defa-123456789003",
"comment": "Affected component",
"value": "Apache HTTP Server 2.4.66, mod_http2 module, h2_mplx.c — multi-threaded MPM only (event, worker). MPM prefork is NOT affected."
},
{
"type": "text",
"category": "Other",
"to_ids": false,
"uuid": "e5f6a7b8-c9d0-1234-efab-234567890104",
"comment": "RCE precondition",
"value": "RCE requires APR mmap allocator (default on Debian/Ubuntu and official Apache Docker images). Scoreboard at fixed address bypasses ASLR for practical exploitation."
},
{
"type": "text",
"category": "Other",
"to_ids": false,
"uuid": "f6a7b8c9-d0e1-2345-fabc-345678901205",
"comment": "Fix commit — r1930444",
"value": "https://svn.apache.org/viewvc?view=revision&revision=1930444"
},
{
"type": "text",
"category": "Other",
"to_ids": false,
"uuid": "a7b8c9d0-e1f2-3456-abcd-456789012306",
"comment": "Fix commit — r1930796",
"value": "https://svn.apache.org/viewvc?view=revision&revision=1930796"
},
{
"type": "text",
"category": "Other",
"to_ids": true,
"uuid": "b8c9d0e1-f2a3-4567-bcde-567890123407",
"comment": "IoC: HTTP/2 frame trigger sequence",
"value": "HTTP/2 HEADERS frame (type=0x01) immediately followed by RST_STREAM (type=0x03) with non-zero error code, same stream ID, before multiplexer stream registration"
},
{
"type": "text",
"category": "Other",
"to_ids": true,
"uuid": "c9d0e1f2-a3b4-5678-cdef-678901234508",
"comment": "IoC: RST_STREAM frame bytes (raw)",
"value": "00 00 04 03 00 [stream_id 4 bytes] [non-zero error code 4 bytes]"
},
{
"type": "text",
"category": "Other",
"to_ids": true,
"uuid": "d0e1f2a3-b4c5-6789-defa-789012345609",
"comment": "IoC: Server response header (vulnerable version)",
"value": "Server: Apache/2.4.66"
},
{
"type": "text",
"category": "Other",
"to_ids": true,
"uuid": "e1f2a3b4-c5d6-7890-efab-890123456710",
"comment": "Exploitation status",
"value": "DoS exploitation confirmed in the wild. RCE demonstrated in lab conditions; widespread weaponization anticipated."
},
{
"type": "text",
"category": "Other",
"to_ids": false,
"uuid": "f2a3b4c5-d6e7-8901-fabc-901234567811",
"comment": "Immediate mitigation",
"value": "Disable mod_http2: remove 'Protocols h2 h2c' from Apache config and restart. Or switch to MPM prefork. Definitive fix: upgrade to Apache HTTP Server 2.4.67."
},
{
"type": "url",
"category": "External analysis",
"to_ids": false,
"uuid": "a3b4c5d6-e7f8-9012-abcd-012345678912",
"comment": "Apache official advisory",
"value": "https://httpd.apache.org/security/vulnerabilities_24.html"
},
{
"type": "url",
"category": "External analysis",
"to_ids": false,
"uuid": "b4c5d6e7-f8a9-0123-bcde-123456789013",
"comment": "oss-security disclosure",
"value": "https://seclists.org/oss-sec/2026/q2/387"
}
],
"Object": [
{
"name": "vulnerability",
"meta-category": "vulnerability",
"Attribute": [
{
"type": "vulnerability",
"object_relation": "id",
"value": "CVE-2026-23918"
},
{
"type": "cvss-score",
"object_relation": "cvss-score",
"value": "8.8"
},
{
"type": "text",
"object_relation": "summary",
"value": "Apache mod_http2 double-free via HTTP/2 early reset — remote DoS and possible RCE"
}
]
}
]
}
}
| Version | Status | Action |
|---|---|---|
| 2.4.67 | Patched | Target version |
| 2.4.66 | Vulnerable |
Distribution update commands:
After upgrading, verify:
apache2 -v # or httpd -v
# Should show: Apache/2.4.67
The 2.4.67 release addresses five CVEs. The two most significant alongside CVE-2026-23918 are:
mod_rewrite expression evaluation allows .htaccess authors to read arbitrary files as the httpd user (affects 2.4.66 and earlier, reported 2026-01-20)mod_proxy_ajp via crafted AJP messages from a malicious AJP backend (affects 2.4.66 and earlier)Upgrading to 2.4.67 remediates all five in a single action.
Detection package maintained against Apache HTTP Server security advisories at httpd.apache.org/security. If you observe exploitation variants or post-exploitation patterns not covered by these rules, please open an issue.
| Layer | Copy Fail (LPE) | CVE-2026-23918 (RCE) |
|---|
| Primary detection | Auditd syscall rules | Suricata network rules |
| WAF (ModSecurity) | Limited — can't see exploit | Relevant — anomaly + post-exploit |
| Auditd | Core detection | Outcome detection (crashes, post-exploit) |
| YARA | Scans for PoC script | Scans for web shells (post-exploit artifacts) |
| Network IDS | Not applicable | First-class detection layer |
| TLS inspection | N/A | Required for full Suricata coverage |
| Upgrade immediately |
| 2.4.65 and earlier | Not affected by this specific bug | May have other known CVEs — review advisory |
| Distribution | Command |
|---|
| Ubuntu / Debian | sudo apt-get update && sudo apt-get upgrade apache2 |
| RHEL / Rocky / AlmaLinux | sudo dnf update httpd |
| Amazon Linux | sudo dnf update httpd |
| SUSE / openSUSE | sudo zypper update apache2 |
| Arch Linux | sudo pacman -Syu |
| Indicator | Value | Confidence | Notes |
|---|
| Affected version | Apache/2.4.66 in Server header | High | Presence alone indicates exposure |
| HTTP/2 frame type | RST_STREAM (0x03) with non-zero error code | Medium | Legitimate connection errors produce same |
| Frame byte pattern | 00 00 04 03 00 (RST_STREAM header) | Medium | Combined with threshold = high |
| RST flood threshold | >10 RST_STREAM/non-zero error from same source in 30s | High | Consistent with in-wild DoS tooling |
| SIGABRT on Apache worker | signal 6 sent to httpd/apache2 PID | High | Normal workers do not abort |
| Shell exec by www-data | execve() of bash/sh by uid 33 or 48 | Critical | Strongly indicates RCE |
| Outbound connect by Apache user | connect() by uid 33 or 48 to external IP | Critical | Strongly indicates reverse shell |
| Web file creation in web root | New .php/.py/.sh written under /var/www | High | May indicate web shell deployment |
| MPM type | mpm_prefork | N/A — not affected | Verify with apachectl -V | grep MPM |
| RCE precondition | APR mmap allocator | Contextual | Default on Debian/Ubuntu; not RHEL default |