Back to Intelligence

WordPress 7.0.4 Security Release: Emergency Patching and Post-Exploitation Hunting Guide

SA
Security Arsenal Team
August 12, 2026
13 min read

WordPress.org has shipped version 7.0.4, a dedicated security release containing a fix for a vulnerability in WordPress core. The project is characteristically tight-lipped about technical specifics at release time — a deliberate embargo strategy that gives defenders a head start before exploit developers reverse-engineer the patch. That embargo window is precisely when your response matters most. Given that WordPress powers more than 40% of the public web, any core security fix should be treated as a drop-everything patching event for internet-facing instances.

The official guidance from WordPress.org is unambiguous: because this is a security release, update your sites immediately. I echo that, but with a practitioner's caveat — patching closes the door, it does not tell you whether anyone walked through it before you got there. This post covers both.

Technical Analysis

What We Know

  • Affected product: WordPress core, all versions prior to 7.0.4 on the 7.x branch. WordPress's security team historically backports core security fixes to all supported branches (back to 3.7.x), so sites pinned to older major versions should check their Dashboard for a corresponding backported release.
  • Fixed version: WordPress 7.0.4, available from WordPress.org or via Dashboard → Updates → Update Now.
  • Vulnerability details: Not publicly disclosed at release time. No CVE identifier has been published in the release announcement. This is standard practice for WordPress core security releases — the patch is shipped, auto-updates fire, and technical details follow after the bulk of the ecosystem has been updated.
  • Auto-update behavior: Sites configured to support automatic background updates (the default for minor/security releases) will have already begun receiving 7.0.4. Do not assume this means you are patched — auto-updates fail silently on hosts with restrictive file permissions, disabled cron, or modified core files.

Why Undisclosed Details Still Demand Urgency

Experienced defenders know that a WordPress core security release with embargoed details is a known-unknown with a predictable trajectory:

  1. Patch diffing begins within hours. Once the release is public, researchers and threat actors alike diff 7.0.3 against 7.0.4 to isolate the vulnerable code path. Working PoCs frequently emerge within 24–72 hours of a core release.
  2. Mass scanning follows. Historically, WordPress core and plugin flaws transition from disclosure to automated mass exploitation in days, not weeks.
  3. Pre-patch exploitation is possible. If the flaw was reported through coordinated disclosure, exploitation before the patch is less likely — but if the fix responds to observed activity, your window of exposure may already have been open.

The practical implication: treat this as a race condition between your patch cadence and exploit development, and assume any unpatched, internet-facing instance is a target.

Exploitation Status

  • In-the-wild exploitation: Not confirmed in the release announcement.
  • CISA KEV: Not listed at time of writing — monitor the CISA Known Exploited Vulnerabilities catalog over the coming days as details emerge.
  • PoC availability: None public at release time, but patch diffing makes this a near-certainty.

Detection & Response

Because the vulnerable code path is undisclosed, writing a signature for the exploit itself would be fabrication. What we can detect — and what actually catches WordPress compromises in real IR engagements — are the post-exploitation behaviors that follow virtually every successful WordPress intrusion: web shell drops, PHP interpreter spawning system commands, rogue admin account creation, and anomalous file modifications under wp-content.

These detections are tuned to fire on behavior that has almost no legitimate reason to occur on a production WordPress host.

Sigma Rules

YAML
---
title: Web Server Process Spawning System Shell (WordPress Post-Exploitation)
id: 3f8a1c72-9d4b-4e67-bc21-5e7a8f902345
status: experimental
description: Detects PHP-FPM, Apache, or Nginx worker processes spawning shells or system utilities — a hallmark of web shell execution following WordPress compromise. On a healthy web server, the PHP interpreter does not invoke bash, sh, curl, or wget against the OS.
references:
  - https://wordpress.org/news/2026/08/wordpress-7-0-4-release/
  - https://attack.mitre.org/techniques/T1059/004/
  - https://attack.mitre.org/techniques/T1505/003/
author: Security Arsenal
date: 2026/08/14
tags:
  - attack.execution
  - attack.t1059.004
  - attack.persistence
  - attack.t1505.003
logsource:
  category: process_creation
  product: linux
detection:
  selection_parent:
    ParentImage|endswith:
      - '/php-fpm'
      - '/php'
      - '/apache2'
      - '/httpd'
      - '/nginx'
  selection_child:
    Image|endswith:
      - '/bash'
      - '/sh'
      - '/dash'
      - '/zsh'
      - '/curl'
      - '/wget'
      - '/nc'
      - '/ncat'
      - '/python'
      - '/python3'
      - '/perl'
  condition: selection_parent and selection_child
falsepositives:
  - Legitimate WordPress plugins that invoke system binaries (rare; e.g., image processing via exec) — investigate the full command line before whitelisting
  - Backup or migration plugins spawning curl/wget — whitelist by known plugin path
level: high
---
title: PHP File Created or Modified in WordPress Uploads Directory
id: 8b2d4e91-1a6c-4f58-ad39-7c4b9e012567
status: experimental
description: Detects creation of PHP files under wp-content/uploads. The uploads directory should contain media only — executable PHP in uploads is one of the most reliable web shell indicators in WordPress IR casework.
references:
  - https://wordpress.org/news/2026/08/wordpress-7-0-4-release/
  - https://attack.mitre.org/techniques/T1505/003/
author: Security Arsenal
date: 2026/08/14
tags:
  - attack.persistence
  - attack.t1505.003
logsource:
  category: file_event
  product: linux
detection:
  selection:
    TargetFilename|contains:
      - '/wp-content/uploads/'
    TargetFilename|endswith:
      - '.php'
      - '.phtml'
      - '.php5'
      - '.php7'
      - '.phar'
  condition: selection
falsepositives:
  - Extremely rare; a small number of poorly designed plugins write PHP to uploads. Any hit warrants manual review of file content and mtime against patch timeline
level: critical
---
title: WordPress Configuration File Accessed by Non-Web Process
id: 5c7e9a23-2b8d-4f19-be41-9d3c6f123890
status: experimental
description: Detects interactive or scripted access to wp-config.php by processes other than the web server or legitimate admin tooling. wp-config.php contains database credentials and is a primary target during post-compromise reconnaissance and credential harvesting.
references:
  - https://wordpress.org/news/2026/08/wordpress-7-0-4-release/
  - https://attack.mitre.org/techniques/T1552/001/
author: Security Arsenal
date: 2026/08/14
tags:
  - attack.credential_access
  - attack.t1552.001
logsource:
  category: process_creation
  product: linux
detection:
  selection:
    CommandLine|contains:
      - 'wp-config.php'
  filter_legit:
    Image|endswith:
      - '/php'
      - '/wp'
    CommandLine|contains:
      - 'wp-cli'
  condition: selection and not filter_legit
falsepositives:
  - Administrators editing wp-config.php during maintenance — correlate with change tickets and maintenance windows
level: medium

KQL (Microsoft Sentinel / Defender)

For environments ingesting web host telemetry via Syslog/CEF or running Defender for Endpoint on Linux servers, this hunt surfaces the post-exploitation chain: web server process spawning commands, PHP written to uploads, and suspicious outbound connections from the web tier.

KQL — Microsoft Sentinel / Defender
let Lookback = 7d;
let WebServerProcs = dynamic(["php-fpm", "php", "apache2", "httpd", "nginx", "www-data"]);
union isfuzzy=true
    (Syslog
    | where TimeGenerated > ago(Lookback)
    | where ProcessName in~ (WebServerProcs)
    | where SyslogMessage has_any ("/bin/bash", "/bin/sh", "curl ", "wget ", "nc -", "python", "base64 -d", "wp-content/uploads")
    | project TimeGenerated, Computer, ProcessName, SyslogMessage, HostIP),
    (DeviceProcessEvents
    | where TimeGenerated > ago(Lookback)
    | where InitiatingProcessFileName in~ (WebServerProcs)
    | where FileName in~ ("bash", "sh", "curl", "wget", "nc", "ncat", "python", "python3", "perl")
    | project TimeGenerated, DeviceName, InitiatingProcessFileName, FileName, ProcessCommandLine, AccountName),
    (DeviceFileEvents
    | where TimeGenerated > ago(Lookback)
    | where FolderPath has "wp-content/uploads"
    | where FileName endswith ".php" or FileName endswith ".phtml" or FileName endswith ".phar"
    | project TimeGenerated, DeviceName, FolderPath, FileName, InitiatingProcessFileName, SHA256)
| order by TimeGenerated desc

A second query worth running is a baseline-diff against your outbound connections — web shells and injected malware frequently beacon to attacker infrastructure from the web tier, which normally has a narrow, predictable egress profile (WordPress.org update servers, payment gateways, SMTP relays):

KQL — Microsoft Sentinel / Defender
let KnownGood = dynamic(["wordpress.org", "api.wordpress.org", "downloads.wordpress.org", "api.github.com"]);
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName in~ ("php-fpm", "php", "apache2", "httpd", "nginx")
| where not(RemoteUrl has_any (KnownGood))
| summarize ConnectionCount = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated)
  by DeviceName, RemoteIP, RemoteUrl, RemotePort
| order by ConnectionCount asc

Low-frequency, first-seen destinations from your web processes are where web shell callbacks and crypto-miner pool connections hide.

Velociraptor VQL

For endpoint forensics on a suspected-compromised WordPress host, hunt for recently created or modified PHP files across the web root — particularly in directories that should be static — and cross-reference modification times against your patch window:

VQL — Velociraptor
-- Hunt for recently modified PHP files across WordPress web roots
-- Adjust the glob and time window to your patch timeline
SELECT FullPath, Size, Mtime, Atime, Ctime,
       timestamp(epoch=1554153600) AS BaselineNote
FROM glob(globs=['/var/www/**/wp-content/uploads/**/*.php',
                 '/var/www/**/wp-content/uploads/**/*.phtml',
                 '/var/www/**/wp-content/**/*.php',
                 '/srv/www/**/wp-content/uploads/**/*.php'])
WHERE NOT IsDir
  AND Mtime > (now() - 604800)
ORDER BY Mtime DESC
VQL — Velociraptor
-- Identify processes running under the web server user with suspicious children
SELECT Pid, Ppid, Name, Exe, CommandLine, Username, CreateTime
FROM pslist()
WHERE Username =~ 'www-data|apache|nginx'
  AND Name =~ 'bash|sh|curl|wget|nc|ncat|python|perl'

If either hunt returns results on a host that was unpatched prior to the 7.0.4 release, treat it as a confirmed compromise until proven otherwise: isolate the host, preserve the web root and database for forensics, and rotate every credential the application touched — database credentials in wp-config.php, WordPress salts and keys, any API keys stored in the database, and admin credentials.

Remediation & Verification Script

The following Bash script inventories WordPress installations on a host, reports their versions, applies the update via WP-CLI where available, and performs a quick post-patch integrity sweep for the indicators above:

Bash / Shell
#!/bin/bash
# WordPress 7.0.4 emergency patch & verification script
# Run as root or a user with write access to the web roots

MIN_VERSION="7.0.4"
REPORT="/tmp/wp-patch-report-$(date +%Y%m%d-%H%M%S).txt"

echo "=== WordPress 7.0.4 Patch & Verification Report ===" | tee "$REPORT"

# 1. Locate all WordPress installations on the host
echo -e "\n[*] Locating WordPress installations..." | tee -a "$REPORT"
WP_CONFIGS=$(find /var/www /srv/www /home -maxdepth 6 -name "wp-config.php" 2>/dev/null)

if [ -z "$WP_CONFIGS" ]; then
  echo "[!] No WordPress installations found. Verify web root paths." | tee -a "$REPORT"
fi

# 2. Check version and update each installation
for CONFIG in $WP_CONFIGS; do
  SITE_DIR=$(dirname "$CONFIG")
  VERSION_FILE="$SITE_DIR/wp-includes/version.php"
  if [ -f "$VERSION_FILE" ]; then
    CURRENT=$(grep -oP "\$wp_version = '\K[^']+" "$VERSION_FILE")
    echo "[+] $SITE_DIR — current version: $CURRENT" | tee -a "$REPORT"

    # Update via WP-CLI if present (preferred: atomic, checksum-verifiable)
    if command -v wp &> /dev/null; then
      echo "    [*] Updating via WP-CLI..." | tee -a "$REPORT"
      sudo -u www-data wp core update --path="$SITE_DIR" 2>&1 | tee -a "$REPORT"
      sudo -u www-data wp core update-db --path="$SITE_DIR" 2>&1 | tee -a "$REPORT"
      NEW_VERSION=$(grep -oP "\$wp_version = '\K[^']+" "$VERSION_FILE")
      echo "    [+] Post-update version: $NEW_VERSION" | tee -a "$REPORT"
    else
      echo "    [!] WP-CLI not found — update manually via Dashboard -> Updates" | tee -a "$REPORT"
    fi
  fi
done

# 3. Verify core file integrity against official checksums
echo -e "\n[*] Verifying core checksums (requires WP-CLI)..." | tee -a "$REPORT"
for CONFIG in $WP_CONFIGS; do
  SITE_DIR=$(dirname "$CONFIG")
  if command -v wp &> /dev/null; then
    echo "[+] $SITE_DIR" | tee -a "$REPORT"
    sudo -u www-data wp core verify-checksums --path="$SITE_DIR" 2>&1 | tee -a "$REPORT"
  fi
done

# 4. Hunt for PHP files in uploads (web shell indicator)
echo -e "\n[*] Scanning uploads directories for PHP files..." | tee -a "$REPORT"
find /var/www /srv/www /home -path "*/wp-content/uploads/*" \( -name "*.php" -o -name "*.phtml" -o -name "*.phar" \) 2>/dev/null | while read -r F; do
  echo "[ALERT] Suspicious PHP in uploads: $F (mtime: $(stat -c %y "$F"))" | tee -a "$REPORT"
done

# 5. List recently modified PHP files across web roots (last 7 days)
echo -e "\n[*] PHP files modified in the last 7 days:..." | tee -a "$REPORT"
find /var/www /srv/www -name "*.php" -mtime -7 2>/dev/null | tee -a "$REPORT"

# 6. Audit WordPress admin accounts via WP-CLI
echo -e "\n[*] Administrator accounts per site (verify against known users)..." | tee -a "$REPORT"
for CONFIG in $WP_CONFIGS; do
  SITE_DIR=$(dirname "$CONFIG")
  if command -v wp &> /dev/null; then
    echo "[+] $SITE_DIR" | tee -a "$REPORT"
    sudo -u www-data wp user list --role=administrator --fields=user_login,user_email,user_registered --path="$SITE_DIR" 2>&1 | tee -a "$REPORT"
  fi
done

echo -e "\n[*] Report saved to $REPORT" | tee -a "$REPORT"
echo "[!] Any [ALERT] entries or unrecognized admin accounts = treat as compromise and escalate to IR."

For Windows-hosted WordPress (IIS), the equivalent verification is a sweep for PHP in uploads and recently modified core files:

PowerShell
# WordPress on IIS — post-patch verification sweep
$WebRoots = @("C:\inetpub\wwwroot", "C:\Sites")
$Report = "C:\Temp\wp-verify-$(Get-Date -Format 'yyyyMMdd-HHmmss').txt"

foreach ($Root in $WebRoots) {
    if (Test-Path $Root) {
        # PHP files in uploads directories — web shell indicator
        Get-ChildItem -Path $Root -Recurse -Directory -Filter "uploads" -ErrorAction SilentlyContinue |
            ForEach-Object {
                Get-ChildItem -Path $_.FullName -Recurse -Include *.php,*.phtml,*.phar -ErrorAction SilentlyContinue |
                    Select-Object FullName, LastWriteTime
            } | Tee-Object -FilePath $Report -Append

        # PHP files modified in the last 7 days across the site
        Get-ChildItem -Path $Root -Recurse -Filter *.php -ErrorAction SilentlyContinue |
            Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-7) } |
            Select-Object FullName, LastWriteTime | Tee-Object -FilePath $Report -Append
    }
}
Write-Output "Verification report: $Report — review any PHP in uploads as a suspected web shell."

Remediation

  1. Patch immediately. Update to WordPress 7.0.4 via Dashboard → Updates → Update Now, WP-CLI (wp core update && wp core update-db), or a manual download from WordPress.org. Sites on older branches should apply the corresponding backported security release.
  2. Verify auto-updates actually applied. Check wp-includes/version.php on every host. Auto-update failures are common on hosts with restrictive permissions, disabled WP-Cron, or modified core files — "auto-updates enabled" is not "patched."
  3. Inventory your exposure. Most organizations have forgotten WordPress instances — marketing microsites, staging environments, abandoned blogs — that are internet-facing and unmanaged. These are the instances that get popped. Run external attack surface discovery against your domains and IP ranges this week.
  4. Hunt before you assume clean. Run the detections above covering the window before patching. If the flaw was exploitable pre-patch, your logs are the only record of whether it was used against you.
  5. Rotate credentials on any host with suspicious findings. Database credentials, WordPress security keys/salts (in wp-config.php — rotating these invalidates all sessions), admin passwords, and any third-party API keys stored in wp_options.
  6. Harden going forward: block PHP execution in wp-content/uploads (via .htaccess/nginx config), disable the plugin/theme editor (define('DISALLOW_FILE_EDIT', true);), enforce 2FA on all admin accounts, restrict /wp-admin by IP where operationally feasible, and put the site behind a WAF with virtual patching capability to buy time on the next embargoed release.
  7. Monitor for the technical disclosure. When the vulnerability details are published post-embargo, re-run targeted hunts against the specific component and update detections accordingly. Watch the WordPress security news feed and the CISA KEV catalog.

The pattern here is one we've executed dozens of times in IR: the patch is the easy part. The organizations that get hurt are the ones that patched and never asked whether they were already compromised, or that forgot the staging server nobody owned. Treat every embargoed core security release as both a patching event and a lightweight threat hunt — because the window between release and mass exploitation is measured in hours, and the window between exploitation and detection, without hunting, is measured in months.

Related Resources

Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.