There aren’t any silver bullets that will protect a WordPress installation against every single attack, but adding a full featured IDPS solution like Suricata, is a good step in protecting not only that “all too many times vulnerable” WordPress installation but also other services like SSH.
Most WordPress installations are run as a single machine with a complete middleware stack from the web server down to the database. As such, what follows, is based on the following assumptions:
The first step is to replace the location section of the server block that listens to HTTPS and passes the requests to PHP, in order to reverse proxy them instead.
1# Replace the following
2location ~ \.php$ {
3 (...)
4}
5
6# With
7location / {
8 proxy_set_header X-Real-IP $remote_addr;
9 proxy_set_header X-Forwarded-Host $server_name;
10 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
11 proxy_set_header X-Forwarded-Proto $scheme;
12 proxy_pass http://localhost;
13}
$host
variable in the header X-Forwarded-Host
since its client/attacker controlled, possibly making your WordPress installation susceptible to host header poisoning. Use the $server_name
variable instead.Next we need to configure Nginx to listen locally for the decrypted traffic and to pass it to PHP.
1server {
2 listen localhost:80 default_server;
3
4 # WordPress installation root directory
5 root /usr/share/nginx/html;
6
7 # Index files
8 index index.php;
9
10 # Make nginx play nice with WordPress permanent links
11 location / {
12 try_files $uri $uri/ /index.php?$args;
13 }
14
15 # Pass the requests to PHP
16 location ~ \.php$ {
17 (...)
18 }
19}
After checking that the configuration is correct, restart Nginx. From this point onwards, there should be plain text HTTP traffic flowing to the socket listening locally (generate some traffic if needed). You can confirm this, by using tcpdump
.
1#!/bin/bash
2tcpdump -i lo "port 80"
The next step is to configure Netfilter using Nftables, in order for it to send traffic to Suricata. To do that, create a file with the rules
extension under /etc/nftables/
directory.
1#!/usr/sbin/nft -f
2table ip filter {
3 # Firewall
4 chain firewall-input {
5 type filter hook input priority 0;
6 (...)
7 }
8 chain firewall-output {
9 type filter hook output priority 0;
10 (...)
11 }
12 # IPS
13 chain ips-input {
14 type filter hook input priority 10;
15
16 # Queue input packets to Suricata
17 counter queue num 0-1 fanout, bypass
18 }
19 chain ips-output {
20 type filter hook output priority 10;
21
22 # Queue output packets to Suricata
23 counter queue num 0-1 fanout, bypass
24 }
25}
firewalld
is disabled.After starting the Nftables service, the next step is to configure Suricata. First edit the main Suricata configuration file (/etc/suricata/suricata.yaml
).
1# Activate workers run mode
2runmode: workers
3
4# Enable EVE logging with X-Forward-For support
5- eve-log:
6 enabled: yes
7 # ...
8 types:
9 - alert:
10 # ...
11 xff:
12 enabled: yes
13 # Two operation modes are available, "extra-data" and "overwrite".
14 mode: overwrite
15 # Two proxy deployments are supported, "reverse" and "forward". In
16 # a "reverse" deployment the IP address used is the last one, in a
17 # "forward" deployment the first IP address is used.
18 deployment: reverse
19 # Header name where the actual IP address will be reported, if more
20 # than one IP address is present, the last IP address will be the
21 # one taken into consideration.
22 header: X-Forwarded-For
23
24# Disable Netfilter queue fail open
25nfq:
26 fail-open: no
27
28# Configure CPU affinity
29threading:
30 # ...
31 set-cpu-affinity: yes
32 # Tune cpu affinity of suricata threads. Each family of threads can be bound
33 # on specific CPUs.
34 cpu-affinity:
35 - management-cpu-set:
36 cpu: [ 0, 1 ] # include only these cpus in affinity settings
37 mode: "balanced"
38 prio:
39 default: "high"
40 # ...
41 - detect-cpu-set:
42 cpu: [ 0, 1 ]
43 mode: "exclusive" # run detect threads in these cpus
44 # Use explicitely 3 threads and don't compute number by using
45 # detect-thread-ratio variable:
46 # threads: 3
47 prio:
48 # low: [ 0 ]
49 # medium: [ "1-2" ]
50 # high: [ 3 ]
51 default: "high"
52
53# Edit the HOME_NET to contain the localhost address
54vars:
55 # ...
56 address-groups:
57 HOME_NET: "[127.0.0.1,(...)]"
58
59# Edit the host OS policy to contain the localhost address
60host-os-policy:
61 # ...
62 linux: [127.0.0.1]
Second, edit the system Suricata configuration file (/etc/sysconfig/suricata
).
1# Make Suricata listen for packets in the Netfilter queues
2OPTIONS="-q 0 -q 1 "
After starting Suricata, check that everything worked out without errors and that packets are being received (check the /var/log/suricata/stats.log
file). To test the installation, use the following SQLi vector.
1https://external.domain/?p=')) UNION SELECT 1--
If everything worked as planned, Suricata should have created an entry in the EVE log (/var/log/suricata/eve.json
) reporting the attack.
The next step is to configure the rules to disable false positives:
2003508
- ET WEB_SPECIFIC_APPS WordPress wp-login.php redirect_to credentials stealing attempt
2012843
- ET POLICY Cleartext WordPress Login
2012998
- ET WEB_SERVER PHP Possible https Local File Inclusion Attempt
2013505
- ET POLICY GNU\Linux YUM User-Agent Outbound likely related to package management
And change some useful rules from alert
to drop
:
2001219
- ET SCAN Potential SSH Scan
2006546
- ET SCAN LibSSH Based Frequent SSH Connections Likely BruteForce Attack
2019876
- ET SCAN SSH BruteForce Tool with fake PUTTY version
ciarmy.rules
compromised.rules
drop.rules
dshield.rules
emerging-web_server.rules
emerging-web_specific_apps.rules
Restart Suricata and that’s it. It’s a good idea to update the rules every now and then so that Suricata can better protect WordPress :)