Okay, so the problem with that is that the 301 and 302 responses clue in the bot that it’s being redirected. So dispense with the code to rewrite the slashes, and do this instead:
# Honeypot for non-existent cms login attempts
RewriteCond %{REQUEST_URI} /admin.php [NC,OR]
RewriteCond %{REQUEST_URI} /install.php [NC,OR]
RewriteCond %{REQUEST_URI} /lequ.php [NC,OR]
RewriteCond %{REQUEST_URI} /login.php [NC,OR]
RewriteCond %{REQUEST_URI} /setup.php [NC,OR]
RewriteCond %{REQUEST_URI} /shell.php [NC,OR]
RewriteCond %{REQUEST_URI} /user.php [NC,OR]
RewriteCond %{REQUEST_URI} /webconfig.txt.php [NC,OR]
RewriteCond %{REQUEST_URI} /wlwmanifest.xml [NC,OR]
RewriteCond %{REQUEST_URI} /wp-config.php [NC,OR]
RewriteCond %{REQUEST_URI} /wp-contacts.php [NC,OR]
RewriteCond %{REQUEST_URI} /wp-login.php [NC,OR]
RewriteCond %{REQUEST_URI} /xmlrpc [NC,OR]
RewriteCond %{REQUEST_URI} /xmlrpc.php [NC,OR]
RewriteCond %{REQUEST_URI} ^/2020/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/2019/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/admin/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/administrator/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/config/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/blog/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/cms/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/data/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/demo/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/fckeditor/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/.git/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/inc/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/install/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/magento/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/manager/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/media/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/news/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/old/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/plus/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/setup/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/shop/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/site/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/sito/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/staging/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/templates/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/test/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/web/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/website/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/wordpress/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/wp/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/wp1/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/wp2/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/wp-admin/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/wp-contacts/(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/wp-includes/(.*)$ [NC]
RewriteRule .* /trap.php?req=%{THE_REQUEST} [PT,L]
So the request is passed through without a response code. Then once it’s too late for the bot to do about it, the trap page is executed and (optionally) a 401 or 403 returned for use in a firewall, etc.
<?php
header('X-PHP-Response-Code: 401', true, 401);
date_default_timezone_set('America/New_York');
$ip=$_SERVER['REMOTE_ADDR'];
//ip="127.0.0.2"; // for testing
$origRequest = $_GET['req'];
putenv("TZ=US/Eastern");
$timeNow = time();
$fresh = time() - 900;
$domain = "example.com"; // the domain the script is installed on
$currentDateTime = (date("M d, Y h:i:s a"));
$ports="80,443";
$categories="15,21"; // for AbuseIPDB Report
$con = mysqli_connect("example.com","database_user","password","database");
if (!$con) { die('Could not connect: ' . mysqli_error($con)); }
$result = mysqli_query($con, "SELECT * FROM table WHERE (ip LIKE '$ip' AND time >= '$fresh')");
$row = mysqli_fetch_array($result);
$reportDate = $row['datetime'];
if (empty($reportDate)) {
// sanitize
$timeNow = mysqli_real_escape_string($con, $timeNow);
$ip = mysqli_real_escape_string($con, $ip);
$domain = mysqli_real_escape_string($con, $domain);
$currentDateTime = mysqli_real_escape_string($con, $currentDateTime);
$origRequest = mysqli_real_escape_string($con, $origRequest);
$comment=$origRequest; // for AbuseIPDB Report and database entry
/* If origRequest is stripped out by mysqli_real_escape_string, it means it contained malicious SQL code. Therefore:*/
if (empty($origRequest)) {
$comment = "Web-based SQL injection attempt";
$categories = "15,16,21";
}
// insert to db
mysqli_select_db($con, "database");
$sql = "INSERT INTO reports (datetime, time, ip4, ports, categories, domain, origRequest, comment) VALUES ('$currentDateTime','$timeNow','$ip','$ports','$categories','$domain','$origRequest','$comment')";
if (!mysqli_query($con,$sql)) {
echo("Error description: " . mysqli_error($con));
}
// make report
$data = (array(
"ip" => $ip,
"categories" => $categories,
"comment" => $comment
));
$headers = array('Key: the-abuseipdb-key-goes-here', 'Accept: application/json');
$ch = curl_init("https://api.abuseipdb.com/api/v2/report");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 ); // Set to 0 for testing to display response from AbuseIPDB
curl_setopt($ch, CURLOPT_POST, 1 );
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$output=curl_exec($ch);
curl_close($ch);
}
?>
<!DOCTYPE HTML>
<html>
<head>
<title>Busted</title>
</head>
<body>
<p style="text-align: center"><img src="/Images/401.png" style="max-width: 90%" alt="Outstretched hand with palm facing user in a halt gesture inside a circle with a slash."></p>
</body>
</html>
A bot (or human) might not continue with the attack once they get a 301 or 302 response, so using the pass through (the PT in the [PT,L]
directive) prevents the response from being sent. It just executes the script and renders the content of trap.php
in the same browser window and URL. By that time they’ve been trapped and the report has been sent.
Hey, everyone needs a hobby.
Richard