Kumpulan custom decoders, rules, auditd policies, dan SCA policies untuk Wazuh guna mendeteksi
eksploitasi PHP2Shell (CVE-2026-109618 / CVE-2026-109619) pada server web berbasis PHP
(Apache/mod_php, Nginx + PHP-FPM, maupun PHP di dalam container Docker).
| Item | Keterangan |
|---|---|
| Nama | Wazuh PHP2Shell Detection Rules |
| Jenis | Custom content Wazuh (decoders + rules + auditd + SCA) |
| Target | Wazuh Manager >= 4.9 dan Wazuh Agent Linux |
| Cakupan serangan | Reconnaissance, arming probe (log poisoning / session file), OOM/crash signal, ModSecurity, post-exploitation (reverse shell, download payload), FIM webshell |
| MITRE ATT&CK | T1190, T1595.002, T1592.004, T1499, T1505.003, T1059.004, T1005 |
| Referensi CVE | GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp |
| Compliance | NIST SP 800-53, PCI DSS v4.0, TSC |
wazuh_php2shell/
├── auditd/
│ └── php2shell.rules # Audit execve oleh user web (www-data)
├── decoders/
│ ├── 0170-nginx_decoders.xml # Decoder nginx error log + ModSecurity
│ ├── 0375-web-accesslog_decoders.xml # Decoder access log (dengan & tanpa params)
│ ├── php-fpm_decoders.xml # Decoder log PHP-FPM warning
│ └── php2shell_yara_decoders.xml # Decoder hasil scan YARA (wazuh-yara)
├── rules/
│ ├── 100-modsec_custom_rules.xml # Rule ModSecurity (id 100000-100008)
│ ├── 111-php2shell_rules.xml # Rule utama PHP2Shell (id 111000-111141)
│ └── php2shell_yara.xml # Rule integrasi YARA (id 108000, 111060-111072)
├── sca/
│ ├── sca_php2shell_php_runtime.yml # Hardening runtime PHP
│ ├── sca_php2shell_web_server.yml # Hardening web server & host
│ └── sca_php2shell_docker_runtime.yml # Variant untuk container Docker
├── yara/
│ └── yara.sh # Script active response YARA (agent)
└── log_samples/ # Contoh log untuk pengujian
| Rule ID | Level | Deteksi |
|---|---|---|
| 111000-111004 | 3-10 | Recon: scanner UA, akses phpinfo, parameter qXXXXXXX (transport 9bie), HTTP 500, path sesi di parameter |
| 111105 | 10 | Log poisoning (path menunjuk access.log/error.log) |
| 111020-111029 | 7-12 | Error PHP: body melebihi post_max_size, multipart flood, session rusak, heap corruption, OOM |
| 111040-111041 | 7 | ModSecurity menolak body multipart/JSON |
| 111050-111051 | 12 | Segfault PHP worker, OOM killer |
| 111052-111053 | 12 | Post-exploit: download payload & reverse shell via auditd |
| 111054-111055 | 8-10 | FIM: file PHP baru/berubah di /var/www |
| 111060-111062 | 0 | Pemicu scan YARA (level 0, tanpa alert) |
| 108000-108001, 111070-111072 | 8-15 | Hasil scan YARA: signature malware/webshell |
| 111100-111141 | 10-15 | Korelasi multiple event per source IP / host |
| 100000-100008 | 0-14 | Normalisasi event ModSecurity (Nginx) |
- Wazuh Manager dan Wazuh Agent sudah terpasang dan saling terhubung.
- Akses root (atau
sudo) pada manager dan agent. - Web server log tersedia: access log Apache/Nginx, error log, dan log PHP-FPM.
- (Opsional) ModSecurity + OWASP CRS aktif pada Nginx/Apache.
- (Opsional) Docker CLI bagi agent yang memantau container (SCA Docker).
sudo mkdir -p /root/wazuh-backup
sudo cp /var/ossec/etc/ossec.conf /root/wazuh-backup/sudo cp decoders/*.xml /var/ossec/etc/decoders/
sudo chown wazuh:wazuh /var/ossec/etc/decoders/*.xml
sudo chmod 660 /var/ossec/etc/decoders/*.xmlsudo cp rules/*.xml /var/ossec/etc/rules/
sudo chown wazuh:wazuh /var/ossec/etc/rules/*.xml
sudo chmod 660 /var/ossec/etc/rules/*.xmlRule
100-modsec_custom_rules.xmlbergantung pada decodernginx-errorlog-modsec, pastikan file decoder ikut di-deploy.
Tambahkan blok berikut di <decoders> pada /var/ossec/etc/ossec.conf agar decoder bawaan
0170-nginx_decoders.xml dan 0375-web-accesslog_decoders.xml tidak bentrok:
<decoder_exclude>0170-nginx_decoders.xml</decoder_exclude>
<decoder_exclude>0375-web-accesslog_decoders.xml</decoder_exclude>sudo cp auditd/php2shell.rules /etc/audit/rules.d/Sesuaikan UID user web pada file tersebut bila berbeda (euid=33 = www-data pada Debian/Ubuntu,
euid=48 = apache pada RHEL family). Cek dengan id -u www-data.
sudo augenrules --load
sudo systemctl enable --now auditd
sudo auditctl -l | grep php2shell_execTambahkan localfile di /var/ossec/etc/ossec.conf agent:
<localfile>
<log_format>apache</log_format>
<location>/var/log/nginx/access.log</location>
</localfile>
<localfile>
<log_format>syslog</log_format>
<location>/var/log/nginx/error.log</location>
</localfile>
<localfile>
<log_format>syslog</log_format>
<location>/var/log/php*-fpm.log</location>
</localfile>
<localfile>
<log_format>audit</log_format>
<location>/var/log/audit/audit.log</location>
</localfile>Aktifkan juga FIM untuk web root (diperlukan rule 111054/111055):
<syscheck>
<directories check_all="yes" report_changes="yes">/var/www</directories>
</syscheck>sudo cp sca/*.yml /var/ossec/etc/shared/default/
sudo chown wazuh:wazuh /var/ossec/etc/shared/default/*.yml
sudo chmod 660 /var/ossec/etc/shared/default/*.ymlPastikan agent berada pada group default dan blok <sca> aktif pada agent.conf/ossec.conf:
<sca>
<enabled>yes</enabled>
<scan_on_start>yes</scan_on_start>
<interval>12h</interval>
</sca>Untuk container, gunakan sca_php2shell_docker_runtime.yml dan pastikan agent dapat mengakses Docker socket.
sudo /var/ossec/bin/wazuh-logtest
sudo /var/ossec/bin/wazuh-analysisd -t
sudo systemctl restart wazuh-managerPada agent:
sudo systemctl restart wazuh-agentUji decoder & rule dengan sample log yang disediakan:
sudo /var/ossec/bin/wazuh-logtest -q < log_samples/apache.log
sudo /var/ossec/bin/wazuh-logtest -q < log_samples/nginx.log
sudo /var/ossec/bin/wazuh-logtest -q < log_samples/auditd.logHasil yang diharapkan muncul misalnya PHP2Shell: scanner user agent seen on the web server (rule 111000).
Rule PHP2Shell yang terpicu secara otomatis memicu active response firewall-drop, sehingga IP
penyerang diblokir pada firewall host selama durasi tertentu.
| Group Rule | Contoh Rule | Efek |
|---|---|---|
php2shell_recon |
111000, 111001, 111002, 111003, 111100 | IP scanner yang melakukan reconnaissance diblokir |
php2shell_http |
111002, 111003, 111004, 111105 | IP pengirim request berbahaya (LFI, log poisoning) diblokir |
modsecurity |
111040, 111041, 100002, 100004, 100005 | IP yang ditolak ModSecurity / Nginx diblokir |
firewall-dropmembutuhkansrcip, sehingga hanya rule dengan source IP yang dapat memicu. Rule berbasis kernel/auditd (php2shell_crash_signal,php2shell_post_exploit) tidak dapat memicufirewall-dropkarena tidak membawa IP sumber.
- Paket
iptablestersedia (ataunftablesjika konfigurasi Wazuh disesuaikan). - Script active response bawaan
firewall-dropada di/var/ossec/active-response/bin/firewall-drop. - Agent berjalan sebagai root agar dapat menulis rule iptables.
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_group>php2shell_recon|php2shell_http|modsecurity</rules_group>
<timeout>1800</timeout>
</active-response>Keterangan:
| Field | Nilai | Keterangan |
|---|---|---|
command |
firewall-drop |
Nama command active response |
location |
local |
Blokir pada host agent yang menerima event |
rules_group |
php2shell_recon|php2shell_http|modsecurity |
Group dipisah | (OR) |
timeout |
1800 |
Durasi blokir dalam detik (30 menit) |
Jika ingin hanya rule tertentu yang memblokir, gunakan <rules_id>:
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>111000,111002,111004,111105,100004</rules_id>
<timeout>1800</timeout>
</active-response>Agar berlaku untuk semua agent dalam group, letakkan pada /var/ossec/etc/shared/<group>/agent.conf:
<agent_config>
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_group>php2shell_recon|php2shell_http|modsecurity</rules_group>
<timeout>1800</timeout>
</active-response>
</agent_config># pada agent: pastikan rule iptables terbentuk setelah simulasi rule
sudo iptables -L -n | grep DROP
# pada manager: pastikan active response tercatat
sudo tail -f /var/ossec/logs/active-responses.logSetelah timeout habis, firewall-drop menghapus blokir secara otomatis.
- Whitelist IP internal (load balancer, monitoring, reverse proxy) di
/var/ossec/etc/lists/atau dengan rule level0untuk menghindari self-block. - Blokir pada
location localhanya melindungi host tersebut. Untuk memblokir di satu titik masuk, gunakanlocation serverpada manager dengan skenariofirewall-dropterpusat. modsecuritymencakup rule level rendah (100002 level 5, 100006 level 6); pastikan threshold tidak memblokir pengguna sah akibat false positive (aturrules_idbila perlu).
Sebagian rule dan SCA policy menghardcode path web root umum (/var/www, /srv/www,
/usr/share/nginx/html, /usr/local/www, /opt/lampp/htdocs). Jika instalasi Anda memakai path
lain (mis. /data/www, /home/site/public_html, /app/public, atau web root di dalam container),
lakukan penyesuaian berikut.
| File | Baris | Isi Saat Ini | Yang Perlu Diubah |
|---|---|---|---|
rules/111-php2shell_rules.xml |
257 | ^/var/www/.+\.(?:php...)$ (rule 111054, FIM file baru) |
Ganti ^/var/www/ dengan prefix web root Anda, mis. ^(?:/var/www|/data/www)/ |
rules/111-php2shell_rules.xml |
268 | ^/var/www/.+\.(?:php...)$ (rule 111055, FIM file diubah) |
Sama seperti di atas |
rules/111-php2shell_rules.xml |
50 | Regex 111004 mendeteksi path sesi di /tmp atau /var/lib/php |
Tambahkan prefix direktori sesi Anda (/run/php, /data/sessions, dst.) |
rules/php2shell_yara.xml |
23 | Regex 111060 (pemicu scan YARA, file PHP baru) | Samakan daftar path dengan 111054 di atas |
rules/php2shell_yara.xml |
30 | Regex 111061 (pemicu scan YARA, file PHP diubah) | Samakan daftar path dengan 111055 di atas |
rules/php2shell_yara.xml |
64 | Regex 111070 (yara_scanned_file di web root) |
Samakan daftar path agar match web root diklasifikasikan sebagai webshell, bukan 111071 |
rules/php2shell_yara.xml |
38 | <field name="file">/tmp/yara/malware/</field> (rule 111062, direktori uji) |
Ganti bila direktori uji/karantina berbeda |
rules/php2shell_yara.xml |
103 | -yara_rules /tmp/yara/rules/yara_rules.yar pada contoh <command> |
Ganti dengan path ruleset YARA di agent |
sca/sca_php2shell_web_server.yml |
53 | find /var/www /srv/www ... (check 42102) |
Tambahkan path web root Anda |
sca/sca_php2shell_web_server.yml |
77 | find /var/lib/php/sessions ... /tmp (check 42104) |
Tambahkan direktori sesi (lihat php -i | grep session.save_path) |
sca/sca_php2shell_web_server.yml |
107-111 | d:/var/www, d:/srv/www, ... (check 42106) |
Tambahkan baris d:<path> untuk web root Anda |
sca/sca_php2shell_php_runtime.yml |
135 | Regex `(^ | ;)/(var/www|usr/share/nginx|srv/www|opt/lampp/htdocs)` (check 42009) |
sca/sca_php2shell_docker_runtime.yml |
182 | Regex yang sama dengan 42009 | Tambahkan alternasi path web root di dalam container |
Rule FIM (default 2 path):
<field name="file" type="pcre2">(?i)^(?:/var/www|/data/www|/home/site/public_html)/.+\.(?:php[0-9]?|phtml|phar|pht|phps)$</field>SCA check 42102 (tambahkan path):
c:find /var/www /srv/www /usr/share/nginx/html /usr/local/www /opt/lampp/htdocs /data/www -type f \( -name "*.php" -o -name "*.phtml" -o -name "*.php5" -o -name "*.pht" \) -perm -0002 -print -quit 2>/dev/null -> r:.SCA check 42106 (tambahkan satu baris per web root):
- 'd:/data/www -> (^\.htaccess$|^\.user\.ini$) -> r:session\.upload_progress\.enabled\s*[= ]\s*(On|1|true)|auto_prepend_file\s*[= ]'FIM <syscheck> pada agent juga harus menunjuk path yang sama:
<syscheck>
<directories check_all="yes" report_changes="yes">/data/www</directories>
</syscheck># Nginx
grep -r "root " /etc/nginx/sites-enabled/ /etc/nginx/conf.d/
# Apache
grep -r "DocumentRoot" /etc/apache2/sites-enabled/ /etc/httpd/conf.d/
# Docker
docker inspect -f '{{.Name}} {{range .Mounts}}{{.Source}}->{{.Destination}} {{end}}' $(docker ps -q)
# Session path aktual
php -i | grep session.save_pathlog_samples/memakai/var/www/htmlsebagai contoh; tidak perlu diubah kecuali untuk pengujian.- Nonaktifkan
syscheckignore untuk direktori deploy/release baru, agar rule 111054 tidak menghasilkan alert berlebih saat deployment. - Path dengan karakter regex (spasi, titik) harus di-escape atau diberi
\Q...\Epada pola PCRE2. - Container: agent host biasanya hanya melihat path host (mis.
/srv/docker/app/html), bukan path di dalam container; gunakansca_php2shell_docker_runtime.ymluntuk membaca setting runtime. - Jika web root berada di luar
/var/www, rule FIM harus diperbarui dan tetap konsisten dengan<directories>padaossec.conf; jika tidak, rule tidak akan pernah terpicu.
YARA dipakai untuk memverifikasi apakah file PHP baru/berubah di web root merupakan webshell atau malware. Alurnya mengikuti Panduan Wazuh: Detecting malware using YARA integration:
FIM (rule 111054/111055) ──► Active Response `yara_linux` (yara.sh)
│ yara -w -r <rules> <file>
▼
/var/ossec/logs/active-responses.log
│
decoder yara_decoder (manager)
▼
rule 108001 ─► 111070 (webshell di web root) / 111071
| File | Tujuan |
|---|---|
yara/yara.sh |
Script active response (instal ke agent), varian PHP2Shell dari yara.sh Wazuh |
decoders/php2shell_yara_decoders.xml |
Decoder yara_decoder / yara_decoder1 untuk baris wazuh-yara: |
rules/php2shell_yara.xml |
Rule 108000/108001 dan 111060-111072, plus contoh blok <command>/<active-response> |
# Ubuntu/Debian
sudo apt update
sudo apt install -y make gcc autoconf libtool libssl-dev pkg-config jq curl
sudo curl -LO https://github.com/VirusTotal/yara/archive/v4.5.5.tar.gz
sudo tar -xvzf v4.5.5.tar.gz -C /usr/local/bin/ && rm -f v4.5.5.tar.gz
cd /usr/local/bin/yara-4.5.5/
sudo ./bootstrap.sh && sudo ./configure && sudo make && sudo make install && sudo make checkJika muncul libyara.so.<n>: cannot open shared object file:
echo "/usr/local/lib" | sudo tee -a /etc/ld.so.conf && sudo ldconfigRHEL: ganti paket dengan yum install -y make automake gcc autoconf libtool openssl-devel pkg-config jq
dan aktifkan epel-release.
sudo mkdir -p /tmp/yara/rules
sudo curl 'https://valhalla.nextron-systems.com/api/v1/get' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'demo=demo&apikey=1111111111111111111111111111111111111111111111111111111111111111&format=text' \
-o /tmp/yara/rules/yara_rules.yar
apikeydemo bersifat publik dan rate-limited. Untuk produksi gunakan ruleset milik sendiri, misalnya aturan webshell PHP (php2shell,webshell,c99,r57,b374k) di direktori/tmp/yara/rules/lalu arahkan-yara_ruleske file/direktori tersebut.Simpan ruleset di luar
/tmppada produksi, mis./var/ossec/etc/yara/rules.yar, karena/tmpbisa dibersihkan olehsystemd-tmpfiles.
sudo cp yara/yara.sh /var/ossec/active-response/bin/yara.sh
sudo chown root:wazuh /var/ossec/active-response/bin/yara.sh
sudo chmod 750 /var/ossec/active-response/bin/yara.sh/var/ossec/etc/ossec.conf:
<syscheck>
<directories realtime="yes" report_changes="yes">/var/www</directories>
<directories realtime="yes">/tmp/yara/malware</directories>
</syscheck>/tmp/yara/malware adalah direktori uji dari dokumentasi Wazuh. Sesuaikan /var/www dengan
web root Anda (lihat bagian Menyesuaikan Path Web Root).
Decoder berada di decoders/php2shell_yara_decoders.xml dan rule 108000/108001 serta
111060-111072 berada di rules/php2shell_yara.xml:
sudo cp decoders/php2shell_yara_decoders.xml /var/ossec/etc/decoders/
sudo cp rules/php2shell_yara.xml /var/ossec/etc/rules/112-php2shell_yara_rules.xml
sudo chown wazuh:wazuh /var/ossec/etc/decoders/php2shell_yara_decoders.xml \
/var/ossec/etc/rules/112-php2shell_yara_rules.xml
sudo chmod 660 /var/ossec/etc/decoders/php2shell_yara_decoders.xml \
/var/ossec/etc/rules/112-php2shell_yara_rules.xmlNama file rule di manager bebas, tetapi harus diakhiri
.xmldan tidak boleh sama dengan file di repo (php2shell_yara.xmljuga valid). Nama112-...dipakai agar urutan muat setelah111-php2shell_rules.xml.
Rule kunci:
| Rule ID | Level | Fungsi |
|---|---|---|
| 111060 | 0 | FIM file PHP baru di web root, memicu scan (tanpa alert) |
| 111061 | 0 | FIM file PHP diubah di web root, memicu scan (tanpa alert) |
| 111062 | 0 | Event direktori uji /tmp/yara/malware, memicu scan |
| 108000 | 0 | Grouping decoder YARA |
| 108001 | 12 | YARA positif match (bawaan dokumentasi) |
| 111070 | 14 | YARA positif match pada file di web root (indikasi webshell) |
| 111071 | 8 | YARA positif match di luar web root |
| 111072 | 15 | Korelasi 3+ match malware dalam 10 menit |
Rule 111060/111061 level
0hanya memicu active response tanpa menghasilkan alert, karena alert FIM sudah ditangani rule 111054/111055 (level 10/8). Jika ingin tanpa alert ganda, aktifkan hanya salah satu pasangan pada<rules_id>active response.
Tambahkan di dalam <ossec_config> pada /var/ossec/etc/ossec.conf manager:
<command>
<name>yara_linux</name>
<executable>yara.sh</executable>
<extra_args>-yara_path /usr/local/bin -yara_rules /tmp/yara/rules/yara_rules.yar</extra_args>
<timeout_allowed>no</timeout_allowed>
</command>
<active-response>
<disabled>no</disabled>
<command>yara_linux</command>
<location>local</location>
<rules_id>111054,111055,111060,111061,111062</rules_id>
</active-response>Untuk men-deploy ke banyak agent, letakkan blok <active-response> yang sama dalam
/var/ossec/etc/shared/<group>/agent.conf di dalam <agent_config>.
Restart service:
sudo systemctl restart wazuh-manager
# pada agent
sudo systemctl restart wazuh-agent| Gejala | Solusi |
|---|---|
| Rule tidak terpicu | Cek decoder dengan wazuh-logtest, pastikan decoder_exclude sudah di-set pada manager |
| Rule URL/params tidak match | Decoder access log baru mengisi field params; gunakan <field name="params"> bukan <url> |
| Rule auditd tidak terpicu | Pastikan auditd aktif, key php2shell_exec ter-load, dan log audit dikumpulkan agent |
| FIM tidak melapor | Pastikan /var/www ada di <syscheck><directories> |
| SCA tidak muncul | Cek status di dashboard, pastikan policy berada di folder shared group agent |
| Active response tidak memblokir | Cek iptables tersedia, agent berjalan sebagai root, dan rules_group tertulis benar |
| YARA tidak memindai | Cek jq terpasang, yara.sh executable (750 root:wazuh), path -yara_path/-yara_rules benar, dan agent menerima konfigurasi active response terbaru |
yara: error while loading shared libraries |
Tambahkan /usr/local/lib ke /etc/ld.so.conf lalu jalankan ldconfig |
Dirilis di bawah MIT License. Copyright (c) 2026 Ridwan Na'im.