{"id":3046,"date":"2019-07-31T22:33:57","date_gmt":"2019-08-01T03:33:57","guid":{"rendered":"http:\/\/osric.com\/chris\/accidental-developer\/?p=3046"},"modified":"2019-08-10T12:45:47","modified_gmt":"2019-08-10T17:45:47","slug":"block-wordpress-scanners-fail2ban","status":"publish","type":"post","link":"https:\/\/osric.com\/chris\/accidental-developer\/2019\/07\/block-wordpress-scanners-fail2ban\/","title":{"rendered":"Blocking WordPress scanners with fail2ban"},"content":{"rendered":"<p>My web logs are filled with requests for <code>\/wp-login.php<\/code> and <code>\/xmlrpc.php<\/code>, even on sites that aren&#8217;t running WordPress. Every one of these attempts is from a scanner trying to find, and possibly exploit, WordPress sites.<\/p>\n<p>Why not put those scanners in a <a href=\"https:\/\/www.fail2ban.org\/\">fail2ban<\/a> jail and block them from further communication with your web server?<br \/>\n<!--more--><\/p>\n<p>Fortunately, someone else had already done most of the work here:<br \/>\n<a href=\"https:\/\/blog.rimuhosting.com\/2016\/11\/02\/using-fail2ban-on-wordpress-wp-login-php-and-xmlrpc-php\/\">Using Fail2ban on wordpress wp-login.php and xmlrpc.php<\/a><\/p>\n<p>I made a few changes to the suggested filter. For example, on this site (osric.com) there is no <code>\/wp-login.php<\/code>, but there are other instances of <code>wp-login.php<\/code> in subdirectories. Additionally, I am seeing primarily spurious <code>GET<\/code> requests in my access logs. I modified <code>filter.d\/wordpress.conf<\/code> to look for both <code>GET<\/code> and <code>POST<\/code> requests for these WordPress-related files at the site root:<\/p>\n<pre><code>[Definition]\r\nfailregex = ^&lt;HOST&gt; .* \"(GET|POST) \/wp-login.php\r\n            ^&lt;HOST&gt; .* \"(GET|POST) \/xmlrpc.php<\/code><\/pre>\n<p>I also made a couple modifications to <code>jail.d\/wordpress.conf<\/code>:<\/p>\n<pre><code>[wordpress]\r\nenabled = true\r\nport = http,https\r\nfilter = wordpress\r\naction = iptables-multiport[name=wordpress, port=\"http,https\", protocol=tcp]\r\nlogpath = \/var\/log\/custom\/log\/path\/osric.com.access.log\r\nmaxretry = 1\r\nbantime = 3600<\/code><\/pre>\n<ol>\n<li>\nI set <code>maxretry<\/code> to 1 so that IP addresses will be banned after the first bad request.<\/li>\n<li>I removed <code>findtime = 600<\/code> because that&#8217;s the default setting in <a href=\"https:\/\/www.fail2ban.org\/wiki\/index.php\/MANUAL_0_8#Jail_Options\">fail2ban&#8217;s jail options<\/a>.<\/li>\n<li>I increased the <code>bantime<\/code> from the default (10 minutes) to 1 hour (3600 seconds).<\/li>\n<\/ol>\n<p>The other change I made was to add <code>\/wp-login.php<\/code> and <code>\/xmlrpc.php<\/code> to my <code>robots.txt<\/code> file:<\/p>\n<pre><code>User-agent: *\r\nDisallow: \/wp-login.php\r\nDisallow: \/xmlrpc.php<\/code><\/pre>\n<p>If a malicious user creates a hyperlink to <em>osric.com\/wp-login.php<\/em>, well-behaved robots should avoid it. This way I&#8217;m not inadvertently banning Googlebot, for example.<\/p>\n<p>It feels really good to slam the door in the face of these scanners! But unfortunately I have to ask&#8230;<\/p>\n<p><strong>Does this do any good?<\/strong><\/p>\n<p>I analyzed some of the server logs from before I implemented this block, and as it turns out, most of these are drive-by scanners: they are checking for the presence of a potentially vulnerable page, logging it, and moving on. They are basically gathering reconnaissance for future use.<\/p>\n<p>If a host makes a couple <code>GET<\/code> or <code>POST<\/code> requests in the span of one second and then leaves, banning the host&#8217;s IP won&#8217;t be very effective.<\/p>\n<p>Also, I know that fail2ban is useful for brute-force ssh attempts, but how useful is it for scanners requesting WordPress files? According to my fail2ban logs, over the month of July, 2019:<\/p>\n<ul>\n<li>sshd: 69,771 IPs banned<\/li>\n<li>WordPress: 4,538\u202c IPs banned<\/li>\n<\/ul>\n<p><code>wp-login.php<\/code> isn&#8217;t quite the target I thought it was. That is, not compared to <code>sshd<\/code>.<\/p>\n<p>In any case, it still <em>feels<\/em> good to block a questionable scanner. Running the following command and viewing the IP addresses that are currently in jail is satisfying:<\/p>\n<p><code>sudo iptables -v -L f2b-wordpress<\/code><\/p>\n<p>7 blocked right now! Take that, suspect IP addresses!<\/p>\n<p><strong>What else can we do to block or prevent WordPress scanners?<\/strong><\/p>\n<p>A few ideas, none of them earth-shattering:<\/p>\n<ul>\n<li>As mentioned in a previous post (<a href=\"https:\/\/osric.com\/chris\/accidental-developer\/2017\/09\/using-blocklist-de-with-fail2ban\/\">Using blocklist.de with fail2ban<\/a>), the scanner IPs could be shared with a service like <a href=\"https:\/\/www.blocklist.de\">blocklist.de<\/a><\/li>\n<li>Instead of blocking the IP, I could deliver a large file via a slow, throttled connection. This would slow down the scan, but not by much. (One of my colleagues informed me that this practice is called <a href=\"https:\/\/en.wikipedia.org\/wiki\/Tarpit_(networking)\">tarpitting<\/a>.)<\/li>\n<li>I could deliver a fake WordPress login form so that the scanners would accumulate unreliable data. Again, this might slow them down, but not by much.<\/li>\n<\/ul>\n<p>If you have other ideas (even if they aren&#8217;t very good!) let me know in the comments.<\/p>\n<p><strong>Thanks<\/strong><\/p>\n<p>I would like to thank <a href=\"https:\/\/twitter.com\/kentcdodds\">@kentcdodds<\/a> for inspiring me to dust off this blog post (out of my many unpublished drafts) with his tweet:<\/p>\n<blockquote class=\"twitter-tweet\">\n<p lang=\"en\" dir=\"ltr\">Hey, my site doesn&#39;t use WordPress, but I get a lot of traffic to wp-login.php anyway. So&#8230;<\/p>\n<p>Why don&#39;t you go ahead and try it. See what happens &#x1f618;<\/p>\n<p>&mdash; Kent C. Dodds (@kentcdodds) <a href=\"https:\/\/twitter.com\/kentcdodds\/status\/1156252891314708481?ref_src=twsrc%5Etfw\">July 30, 2019<\/a><\/p><\/blockquote>\n<p> <script async src=\"https:\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script> <\/p>\n<p>Go ahead. Visit <a href=\"https:\/\/kentcdodds.com\/wp-login.php\">https:\/\/kentcdodds.com\/wp-login.php<\/a>. I think you&#8217;ll enjoy it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Malicious scanners are constantly looking for WordPress sites to enumerate and exploit. Using fail2ban, I detect such scanners and block them from accessing the site.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[90],"tags":[452,538,359],"class_list":["post-3046","post","type-post","status-publish","format-standard","hentry","category-wordpress","tag-fail2ban","tag-robots-txt","tag-wordpress"],"_links":{"self":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts\/3046","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/comments?post=3046"}],"version-history":[{"count":17,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts\/3046\/revisions"}],"predecessor-version":[{"id":3099,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts\/3046\/revisions\/3099"}],"wp:attachment":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/media?parent=3046"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/categories?post=3046"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/tags?post=3046"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}