PHP2Shell Log Hunting: Which Logs to Check, How Far Back, and What to Grep Check your logs for PHP2Shell: go back to Oct 17, 2026, search Nginx, Apache, PHP-FPM and WAF logs with read-only grep commands, and preserve evidence first.
PHP2Shell Log Hunting: Which Logs to Check, How Far Back, and What to Grep
To check your logs for PHP2Shell exploitation, go back to at least October 17, 2026 , when Amazon first saw attempts, not October 21 when patches shipped. Work through Nginx or Apache, PHP-FPM, PHP, WAF and application logs. Before any grep, copy the logs and record their hashes; what logrotate deleted is gone from the server.
The sources we reviewed on October 23, 2026 give no access-log signature for a successful exploit and no PHP2Shell-specific web shell file name, path or hash. So this is anomaly hunting. For what the flaws are and which versions are affected, see the details of the PHP2Shell flaws .
How far back should you look?
Amazon Threat Intelligence saw an exploitation attempt in its honeypot network on October 17, four days before disclosure, and advises starting investigations there. Include October 16 as a buffer, and check each log source's timezone before correlating events: Nginx and Apache access logs, by default, record the server's local time with an offset (such as +0700), so UTC servers shift the dates.
Don't assume that week's logs remain. Logrotate removes files past the rotate count or maxage limit, so check the actual settings and files today. Gzipped rotations are readable with zgrep; ones deleted locally may survive in backups, archives or a downstream collector.
Service providers under Section 26 of Thailand's Computer Crime Act must keep traffic data for at least 90 days (log retention under the Act ), but logs on the web server itself last as long as logrotate allows.
Where are the logs, and what does each one show?
Log Where What it shows Limitation Nginx access / error access_log and error_log directives (defaults logs/access.log, logs/error.log)Method, path, status, user agent The combined format records no body and no Content-Type Apache access / error Debian/Ubuntu: /var/log/apache2/ · RHEL/Fedora: /var/log/httpd/ Same; also PHP errors under mod_php when PHP's error_log is unset and log_errors is on mod_php is the highest-risk setup; start here PHP-FPM error_log in php-fpm.conf; access.log and slowlog in the pool fileWorkers killed for running too long Pool access.log is off by default PHP error log error_log in php.ini or php_admin_value[error_log]Fatal errors such as memory exhaustion log_errors off: no new script errors recorded through it; older entries and other logs remainWAF / reverse proxy Product-specific A body, if that logging captured it Configuration-dependent Application logs Framework-specific Uploads, logins; body data if logged Varies by app Firewall egress logs Firewall or security group Who the web server connected to Must be enabled before the incident
For many servers, compare your collection scope, daily ingest volume and retention period with published zcrLog and zcrSIEM pricing .
Real paths are in the configuration: access_log/error_log in Nginx (including included files), CustomLog/ErrorLog in Apache (/etc/apache2 on Debian, /etc/httpd on RHEL), and error_log, access.log, slowlog in PHP-FPM.
Step 0: preserve the evidence first
NIST SP 800-86 recommends comparing message digests of the originals with those of the acquired copies, log files included. Copy current and rotated logs in one consistent acquisition; files on a live system can change while you copy, so note the time for files still being written. Move the copies off the server and search those. Filesystem, process and network checks belong to PHP2Shell incident response .
The durable fix is shipping logs off the host as they are written, the job of zcrLog, which collects syslog, retains logs for 90 days to 2 years and checks integrity with SHA-256 .
What signs can the logs show?
These commands only search existing logs (use the copies) and assume the shared combined format: field 4 is the timestamp, 6 the method, 7 the path, 9 the status. WIN matches only the timestamp field for October 16–23, 2026, not dates inside URLs, referrers or user agents. Adjust for other formats or dates.
WIN='^[^ ]+ [^ ]+ [^ ]+ \[(1[6-9]|2[0-3])/Oct/2026:'
1. POST requests to PHP scripts. AWS roots both flaws in PHP's parsing of multipart/form-data before application code runs, and Sansec flags such requests. Without Content-Type in combined, you see method and path only. Clean-URL apps route through index.php, so this doesn't filter on .php.
zgrep -hE "$WIN" access.log* \
| awk '$6 == "\"POST" {p=$7; sub(/\?.*/,"",p); print p}' | sort | uniq -c | sort -rn | head -30
2. 4xx/5xx spikes on one endpoint. AWS advises looking for repeated errors and timeouts on attacker-controlled routes; 5xx responses are their shadow.
zgrep -hE "$WIN" access.log* \
| awk '$9 ~ /^[45]/ {p=$7; sub(/\?.*/,"",p); print $9, p}' | sort | uniq -c | sort -rn | head -30
3. PHP paths requested by only one or two IPs. CISA notes frequency analysis can locate a web shell, usually visited only by its creator. NSA warns of false positives, and $1 may be a proxy address unless the real client IP is logged from a trusted proxy.
zgrep -hE "$WIN" access.log* \
| awk '{p=$7; sub(/\?.*/,"",p); if (p ~ /\.php$/) print $1, p}' | sort -u \
| awk '{print $2}' | sort | uniq -c | sort -n | head -30
4. Requests to .php files in upload or cache folders. CISA flags suspicious web-root files and file types generating unexpected traffic. In logs, look for .php requests inside upload folders (rename them to match your app).
zgrep -hE "$WIN" access.log* \
| awk '{p=$7; sub(/\?.*/,"",p); if (p ~ /\/(uploads?|cache|tmp)\// && p ~ /\.php$/) print $6, p, $9}' \
| sort | uniq -c | sort -rn
5. Repeated memory-exhaustion and timeout errors. Sansec points to fatal out-of-memory errors. The first two strings come from the PHP engine source; the third is PHP-FPM killing a worker that ran too long.
zgrep -HE 'Allowed memory size|Maximum execution time|execution timed out' \
error.log* php-fpm.log*
-H keeps file names for correlation. If grep reports a missing or unreadable file, notice it. Look for clustering on one route since October 16–17. Only "Allowed memory size" stops appearing, and only on workers really running with memory_limit=-1; timeout messages are separate, since PHP-FPM's request_terminate_timeout is its own control.
6. Child processes and outbound connections. Amazon and the Canadian Centre for Cyber Security advise checking unexpected processes, commands and outbound connections tied to PHP. Logs show these only if EDR or firewall egress logs were collected (forwarding firewall syslog ); on-host checks are in the incident-response article.
7. PHP_SESSION_UPLOAD_PROGRESS lives in the body, not the access log. Amazon recommends looking for POST form uploads containing it. It's the default field name of PHP's session upload progress feature (on by default) and travels in the POST body, which combined access logs don't record. A WAF or proxy log may show it if it captured bodies, as may application logging or other capture (what a WAF can and can't do ).
zgrep -F -c -- 'PHP_SESSION_UPLOAD_PROGRESS' '/path/to/body-capture.log.gz'
Substitute your own file. -c counts matching lines, not requests or attempts, and a hit isn't proof: legitimate forms use the name too. Amazon's published source IPs are shared anonymization networks; investigate, don't blindly block.
Limits
The sources we reviewed on October 23, 2026 give no access-log signature for a successful exploit and no PHP2Shell-specific web shell file name, path or hash, so the above surfaces suspicion, not confirmation; AWS says absence of errors is not proof of safety. Logs on a possibly compromised host can be altered or stopped by a privileged intruder, so OWASP recommends copying them to read-only storage quickly. Apache warns logs hold unescaped client data, possibly control characters: handle raw logs carefully.
If something looks wrong, stop hunting and move to PHP2Shell incident response .
Log more next time
# Nginx (http block): add Content-Type, request size and timing
log_format hunt '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" '
'"$content_type" $request_length $request_time';
access_log logs/access_hunt.log hunt;
# Apache: %I requires mod_logio
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" \"%{Content-Type}i\" %I %D" hunt
CustomLog "logs/access_hunt_log" hunt
For PHP, AWS recommends log_errors=On, error_reporting=E_ALL and a working error_log; enable the PHP-FPM pool access.log.
How far do free tools get you?
On a handful of servers, zgrep and awk search and summarise the evidence you have at no cost. They can't prove an exploit succeeded, rebuild bodies never recorded, or recover past process and network activity.
Shipping logs off the host reduces future loss and dependence on host-local evidence; it can't recover deleted records or guarantee a compromised host sent complete, truthful logs. Nginx logs to syslog via the syslog: prefix (UDP 514 by default); PHP and PHP-FPM's error_log = syslog goes to the local syslog service, which must be configured to forward remotely. One receiver is zcrLog , a compliance log-retention system certified to Thailand's มศอ. 4003.1-2560. The open question this time was whether October 17 was still on disk; the system archives older logs and can restore them, so the next look-back can start from a search of the raw logs you sent, though it doesn't replace patching PHP.
Searching everything at once is zcrSIEM 's job. It's sold separately and doesn't require zcrLog, with custom and Sigma detection rules and a hunting box that turns a plain-language question into a query you review before running. A signal can become a rule or query only where those events were collected. See the demo page and AI-assisted threat hunting .
zcr supplies the software; hands-on investigation goes through our partners.
📞 Free Consultation
Related articles
Related product
zcrLog · zcrSIEM · Pricing · Demo
Sources
Amazon Threat Intelligence identifies PHP2Shell exploitation prior to public disclosure in MadPot — AWS Security Blog, Oct 21, 2026: first attempt Oct 17, PHP_SESSION_UPLOAD_PROGRESS, anonymization IPs
Using AWS WAF to apply mitigations for PHP2Shell — AWS Security Blog, updated Oct 22, 2026: multipart root cause, repeated errors on routes, no errors is not proof, error-logging settings
PHP2Shell — Sansec, updated Oct 22, 2026: multipart requests to PHP endpoints, out-of-memory fatal errors
AL26-026 Critical vulnerabilities impacting PHP — Canadian Centre for Cyber Security, Oct 21, 2026
PHP security release 8.5.12, 8.4.27, 8.3.36, 8.2.35 — php.net, Oct 20–21, 2026: mod_php highest risk, memory_limit=-1 workaround
Compromised Web Servers and Web Shells — CISA
Mitigating Web Shells — NSA
NIST SP 800-86 — message digests of originals and copies, including log files; live files can change during acquisition
OWASP Logging Cheat Sheet
Nginx: ngx_http_log_module , error_log , core module variables , Logging to syslog
Apache: Log Files , mod_log_config , Distros default layout
PHP: PHP-FPM configuration , error handling configuration , memory_limit , session configuration , php-src (zend_alloc.c , zend_execute_API.c , fpm_request.c )
Man pages: logrotate , zgrep , grep , uniq , awk (POSIX)
Computer-Related Crime Act B.E. 2550 (consolidated English translation incl. 2017 amendment) — Ministry of Digital Economy and Society: Section 26, traffic data kept at least 90 days
Products | Pricing | Resources | Insights | Contact