2

I get DDoS by the Wordpress Pingback BOTNET, now I want to block all client who contain Wordpress in there Useragents. For example:

WordPress/4.0; http://vk.lokos.net; verifying pingback from 107.158.239.82

I need to block for both HTTP port 80 and HTTPS port 443. How can I do this?

masegaloeh
  • 17,978
  • 9
  • 56
  • 104
user3135461
  • 61
  • 2
  • 5

3 Answers3

5

First: You don't want to do it this way, as I describe below.

Second: a very similar problem is answered here http://spamcleaner.org/en/misc/w00tw00t.html . I'm relaying their solution to your specific question. There is an iptables string module which you can use to match the browser agent. However, iptables will then inspect every packet ... we can optimize this with the connmark module. I haven't tried it so my answer is only a nudge in the right direction:

<other rules>
iptables -t mangle -A PREROUTING -m connmark --mark 0xBAD1 -j DROP
iptables -t mangle -A PREROUTING -m connmark --mark 0xBAD0 -j ACCEPT
iptables -t mangle -A PREROUTING -p tcp --dport 80 -m string --string "User-Agent: " -j CHECK_UAGENT
iptables -t mangle -A CHECK_UAGENT -m string --string "User-Agent: WordPress/4.0" -j CATCH
iptables -t mangle -A CHECK_UAGENT -j CONNMARK --set-xmark 0xBAD0
<otherrules>
iptables -t mangle -N CATCH 
iptables -t mangle -A CATCH -j LOG --log-level alert --log-prefix "WordPress attack "
iptables -t mangle -A CATCH -j CONNMARK --set-xmark 0xBAD1
iptables -t mangle -A CATCH -j DROP

Here's the idea. The connmark module and associated target will mark a packet as you wish, and any ensuing packets in that connection stream will be similarly marked. So we look for packets headed toward port 80 and have a "User Agent" string. If they have the undesirable User Agent, we mark it as 0xBAD1 -- blaclisting it. We then log it and drop it. Otherwise, once we see "User Agent" but not the undesirable one, it gets whitelisted with 0xBAD0. By whitelisting it, we reduce the load on the packet inspector (it's an optimization step). Otherwise, we'd be searching in every packet of every uploaded picture -- a pointless waste.

** Why the above is a bad idea** One: HTTPS cannot be decoded at the packet-filter level. Two: Because a DDOS attack with the above is possible. The connection starts, opens a connection on your web-server, then disappears (from your webserver's standpoint). It will wait for a long time before giving up on the client which is no longer allowed to get any more packets through. Meanwhile, more attempts will come through. Eventually, HTTP will run out of resources and no requests get through. (One way to fix that problem is by using the recent module. A more thorough way is to have a process monitoring the logfile for "WordPress attack", having it note the remote IP and port, and either forcing the connection to be closed with cutter, or cross-referencing that connection with the server PID associated with it and then killing that process. )

A similar question was posed and answered with : use a reverse proxy. That's the best option but requires lots of reconfiguration and maybe an intervening server.

Do it this way instead Use mod_rewrite (in Apache/*ngnx) to match the User-Agent string, set an environment variable for logging purposes, and return 403 error status. That shuts up the remote side. Now, to make it more permanent, have a separate process monitoring that log-file for such dropped connections, and add the remote-ip to the recent table, which iptables will drop new connections from for the next 5 minutes. So...

# Apache config
RewriteCond %{HTTP_USER_AGENT}  ^WordPress/4\.0
RewriteRule - [L,R=403,E=WordPress]
LogFormat "%t\t%a\t%{remote}p\t%{User-Agent}i"
CustomLog wordpress wordpress.log env=WordPress

The custom log format makes it easier for our external process to decode. The IPtables is just one rule:

iptables -A INPUT --syn -m recent --name WordPress --rcheck --seconds 300 -j DROP

and the external process (running as root) looks like this:

#!perl 
open(STDIN,"tail -f /var/log/http/wordpress.log|")
while (<>) {
   my ($time,$ip,$port,$useragent)=split('\t');
   open(RECENT,"> /proc/net/xt_recent/WordPress")
   print RECENT "+$ip\n"
   close RECENT
}

The timestamp and user-agent string are just so you can verify things are working/not-working as you expect. I will add that with the mod-rewrite way of doing things, you have lots more flexibility of what to reject/ban.

Otheus
  • 432
  • 3
  • 12
1

Running the following commands will block this specific attack:

iptables -N Wordpress-PingVerify
iptables -I INPUT -p tcp --dport 80 -m string --to 70 --algo bm --string 'GET /' -j Wordpress-PingVerify
iptables -A Wordpress-PingVerify -p tcp --dport 80 -m string --to 80 --algo bm ! --string 'User-Agent: WordPress/' -j RETURN
iptables -A Wordpress-PingVerify -p tcp --dport 80 -m string --to 300 --algo bm --string 'verifying pingback from' -j DROP
iptables -A Wordpress-PingVerify -j RETURN

The above rules assume that the attack is destined to HTTP (port 80).

As an alternative, you can use these rules to block ALL WordPress pingback requests – this will block not only the pingback verifications but pingbacks as well:

iptables -N Wordpress-PingBacks
iptables -I INPUT -p tcp --dport 80 -m string --to 70 --algo bm --string 'GET /' -j Wordpress-PingBacks
iptables -A Wordpress-PingBacks -p tcp --dport 80 -m string --to 80 --algo bm ! --string 'User-Agent: WordPress/' -j RETURN
iptables -A Wordpress-PingBacks -p tcp --dport 80 -j DROP
iptables -A Wordpress-PingBacks -j RETURN

Source : https://sysadminblog.net/2016/05/blocking-wordpress-pingback-verification-ddos/

0

As mentioned by others, in general, using -m string rules in your server is not very effective since it checks every single packet for that string, which is slow. But that's not all. Data can be broken up in separate packets on purpose (by hackers) to have say "Word" in the first packet and "press" in the second packet. So that wouldn't help. Not only that, we now have HTTP/3, which uses UDP packets... which can be encrypted.

On top of that, a real DDoS tends to happen really fast, so I think that you need to block the IP addresses as fast as possible, if you think it is really happening.

That means you want to send the IP to iptables as quickly as possible. There is a _new interface_¹ to the firewall which allows you to add up to 65535 IP addresses in a sorted table (very fast search). Since it is unlikely that the attacker has that many computers, it will work as expected.

First, in the Apache2 settings, you capture the word Wordpress in the User-Agent header:

RewriteEngine On

RewriteCond %{HTTP_USER_AGENT} Wordpress
RewriteRule !^/?scripts/ scripts/script-to-run.cgi [L]

Then in script-to-run.cgi you use ipset like so:

#!/bin/bash
ipset add unwanted $REMOTE_ADDR -exist

Source: How to run some code or command in Apache2 on condition?

Then you need a rule in your firewall to check the unwanted list of IPs, it looks like so:

iptables -A unwanted -m set --match-set unwanted src -j DROP

Note: Please make sure that your SSH users do not get blocked by accepting traffic on port 22 before that unwanted rule--this is safe if all your users have a static IP address.

However, you are likely to run in a problem trying to run the ipset command. It will be running as the Apache2 user and not root. One simple solution is to write a C++ process to which you can give root permissions. Something like this:

int main(int argc, char *argv[])
{
    if(setuid(0) != 0) return 1; // could not become root?!
    if(argc != 2) return 1; // exactly one parameter: IP address
    std::string ip(argv[1]);
    if(ip.find(' ') != std::string::npos) return 1; // IP can't include spaces
    if(ip.find('\'') != std::string::npos) return 1; // IP can't include quotes
    std::string cmd("ipset add unwanted '");
    cmd += ip;
    cmd += "' -exist";
    return system(cmd.c_str());
}

Note: It is safe because it only allows an "add" to the specific table "unwanted". Offering all the options that ipset itself supports would not be safe.

Compile with:

g++ safeipset.cpp -o safeipset

Copy result to /usr/bin and make sure it can become root:

sudo cp safeipset /usr/bin/.
sudo chmod u+s /usr/bin/safeipset

Use in your CGI script like so:

#!/bin/bash
safeipset $REMOTE_ADDR

The ipset table can be given a default timeout so the IP addresses that get blocked are removed after a given amount of time. You could first test without that timeout to see how many IPs get added.

Here you setup the time to 5 minutes:

ipset create unwanted hash:ip timeout 300

Please check the man ipset manual page for more info.

¹ ipset has been around in Linux 2.4.x which was released in 2001. So it's not that new but it was not used by anyone for a long time.

Alexis Wilke
  • 2,057
  • 1
  • 18
  • 33