Forum Moderators: coopster

Message Too Old, No Replies

Writing IPs from PHP to iptables

         

dstiles

8:56 am on Jul 8, 2021 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I've been trying to get this working for some time. I've tried several combinations of permissions and placement but nothing works.

The idea is for php to detect an IP that is trying to access (eg) wordpress files/folders and to push that IP into a table in iptables named Dynamic to prevent repeat IP hits. Later, at leisure, I can go through the Dynamic table, extract the IPs, convert any server-farm ones to its CIDR range and wipe the Dynamic table to begin again. Given the small size of my server this is not as onerous as it may seem.

The PHP code to write the IP is shown below; the commented-out lines are alternatives I've tried. To add a little safety it works via a bash script, but I've tried writing direct to iptables with similar results. I've tried placing the script in /home and /srv. The bash script is named phpiptable. I have altered the permissions on the bash script from (user) to root and www-data but no effect.
function IptablesDrop() {
$ip=$_SERVER['REMOTE_ADDR'];
#return(exec("sudo /usr/sbin/iptables -I Dynamic -s $ip -p tcp --dport 443 -j DROP"));
#return(exec("iptables -I Dynamic -s $ip -p tcp -j DROP"));
#return(exec("/home/linkcheck/prg/phpiptable $ip"));
return(exec("/srv/phpiptable $ip"));
}

The bash script is:
ip=$1
sudo /usr/sbin/iptables -I Dynamic -s $ip -p tcp -j DROP
#/usr/sbin/iptables -I Dynamic -s $ip -p tcp -j DROP

I have added the following lines to sudoers...
user ALL=(ALL:ALL) ALL
root ALL=NOPASSWD: /usr/sbin/iptables
www-data ALL=NOPASSWD: /usr/sbin/iptables

The most common result I get in the Apache error log is:
sudo: effective uid is not 0, is /usr/bin/sudo on a file system with the 'nosuid' option set or an NFS file system without root privileges

I have tried a test php script from the terminal and it works ok.
Can anyone help, please?

lucy24

4:27 pm on Jul 8, 2021 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



<tangent>
Dunno about anyone else, but the idea of using the “sudo” directive anywhere other than on your personal computer with commands typed by you in person on your personal keyboard ... makes my blood run cold.
</tangent>

dstiles

6:05 pm on Jul 8, 2021 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Up to a point, yes, Lucy. Do you have a better idea?

phranque

7:34 pm on Jul 8, 2021 (gmt 0)

WebmasterWorld Administrator 10+ Year Member Top Contributors Of The Month



assuming Apache, I would look at mod_suexec

dstiles

9:16 am on Jul 9, 2021 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Thanks. I've now started reading the apache doc on that. It'll take me a while to work it all out, though. :)

Jonesy

9:33 pm on Jul 10, 2021 (gmt 0)

10+ Year Member Top Contributors Of The Month



I have a somewhat similar process.
I have my PHP script write the IP to a file in /private_html/
I also have an every-5-minutes cron bash script that reads that file and pulls the IP and deletes the file. Further on processing in my case is for pf firewalling...
HTH, Jonesy

robzilla

9:58 pm on Jul 10, 2021 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I would probably also take Jonesy's route, where PHP only writes to a file and a separate process takes it from there. If you need it to act faster, you could look into something like incrond.

dstiles

12:57 pm on Jul 11, 2021 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I'd considered a temp file method but subsequent virus hits usually arrive immediately so the IPs need to be blocked asap. So far there is no fear of breaking into web sites but I resent the attempts and the resources expended upon criminals.

lucy24

4:47 pm on Jul 11, 2021 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Are your unwanted visitors blocked by the server or by a firewall? The exasperating aspect of so much of this stuff is that if a robot comes in with a shopping list--whether site-specific or generic--a faceful of 403s almost never stops them making every last request. So if you're not taking the firewall route, your server is still doing the work, whether it's sending out content or sending out a 403. (Granted, if you have a heavily CMS-based site, building a page could be vastly more work for the server than just spitting out a 403.)

csdude55

5:49 pm on Jul 11, 2021 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I went through a similar process, and ultimately decided it was better to restrict WP files to devices with a specific cookie that's already set and give the rest a forbidden error.

At /etc/apache2/conf.d/userdata, I created "exploits.conf" and wrote:

RewriteEngine on

<Directory />
AllowOverride none
</Directory>

RewriteCond %{HTTP_COOKIE} !keyname=[random 32 character key that I preset with a PHP script]

#List of potential attacks; this is just a short list of what I have
RewriteCond %{REQUEST_URI} ^/wp [NC,OR]
RewriteCond %{REQUEST_URI} ^/wordpress [NC,OR]
RewriteCond %{REQUEST_URI} /wp[2-]+\w+\.php [NC]

RewriteRule ^ - [F]


Before this, I used PHP to write the IP to a MySQL table called "blacklist", then on every page of the site I checked to see if the user's IP was listed in the table; if so, I sent them to a 404 page. But that extra query slowed the site down a little on every pageload, which is why I moved it to Apache.

dstiles

8:02 am on Jul 13, 2021 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Lucy - I drop IPs (usually a range) into iptables which acts as a firewall. What I want to do is automate adding spurious bad IPs into iptables immediately and later, when time permits, analysing them with a view to removing them (wayward/compromised DSL) or blocking a CIDR range (eg server farm).

csdude55 - they are already blocked on wording (.env, wp-* etc); as a backstop they are blocked by innapropriate headers; failing that I have a db of server-farm etc IPs. Yesterday I had about 30 successive hits from a single IP on one of the wording-blocked; I would have liked to iptable that IP so that it only transgressed once. I'm probably over-sensitive to this but I have only a limited-resource server and a low threshold of tolerance for criminals. :)

csdude55

5:29 pm on Jul 13, 2021 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I absolutely understand what you mean! Two servers ago I was pretty stressed for resources, too, so I went down the same path. I was getting closer to 1 million fake hits a day, though, so it was really dragging my resources! Page load time is critical for me, though, so I inevitably ended up upgrading servers.

The next step I did, though, was to install CSF (I think it requires cPanel but I'm not sure), then you can write IPs to block directly to /etc/csf/csf.deny to permanently block them. Or if you don't have root access from the website then you can write them to a text file on the account (eg, /home/example/blocklist) and then add the full URL to the CSF configuration variable "GLOBAL_DENY".

It also has a CC_ALLOW_FILTER so that you can restrict access to pre-approved countries. My sites all target the US, so I set mine to "US,MP,PR,CA". That eliminated a LOT of problems!

dstiles

10:00 am on Jul 14, 2021 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I have ssh and remmina access to my servers so I can do most things.

One thing to note, by the way, is to block only port 443 in iptables (or any firewall) as port 80 is used by letsencrypt to check and update certificates.

dstiles

10:27 am on Nov 28, 2021 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



> Did you get the solution?

I still haven't had time to work on it, but I think the solution offered hereabouts should do the job. Meanwhile, I'm still inserting IP ranges into iptables manually via a bash script.

dstiles

2:06 pm on Apr 5, 2022 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I've had a few goes at this over the past months. Most times I've reviewed mod_suexec but each time found it too complex / beyond me.

This week, looking into it again, I found MerlinTheMagic's MTS-Shell on github. This is a specialized bash-like shell that works with root and other users from non-root sources (eg www-data). Not ideal and a bit under-documented but I got it working in linux (it also purports to work in windows). There is also a pseduo-browser in the package.

I tried to use the simple get_shell version of MTS-Shell but that failed (no idea why, I was more concerned with time-to-implement) so I ended up with the more complex shell call.

One of MTS-Shell's failings seems to be no way of passing variables to the shell, which is rather annoying as it's not feasible to instantiate a new call for each IP that has to be dropped by iptables.

I'd already tried to get around the original bash problem by saving the IP into a file and then calling a bash script to read the file. Manually, that worked fine but not from php.

Using this method with MTS-shell solves the problem, although I'd still prefer to send the IP direct to the bash-like shell. Still, it works. If anyone is interested, the method is as below.

The bash script (phptab in server root) is defined to parse a multiple-line file, although in the current usage it should not need to parse more than a single line:
#!/bin/bash
# script name: phptab
# read ip(s) from file and add to iptables; do not save dups

gotit=""
while read -r ip
do
if [ "$ip" != "" ]; then
if [ "$ip" != "$gotit" ]; then
gotit=$ip
# echo $ip
iptables -I Dynamic -s $ip -p tcp -j DROP
fi
fi
done < /srv/mts-ip.txt

The php code used to call that script is:
require_once("/var/www/tools/MTS/MTS/EnableMTS.php");
...
...
// =======================
function do_iptable($ip) {
$filspec="/srv/mts-ip.txt";
// save IP to file
$fh=fopen($filspec,"a");
fwrite($fh,"$ip\n");
fclose($fh);
// open shell...
$shellObj= \MTS\Factories::getDevices()->getLocalHost()->getShell('bash', false);
// Pass the shellObj to the following function with root credentials.
\MTS\Factories::getActions()->getRemoteUsers()->changeUser($shellObj, 'root', 'password');
// no actual return but in case future...
$rtn = $shellObj->exeCmd('/srv/phptab');
// delete temp file...
unlink($filspec);
return($rtn);
}
// =======================

The above php code is fed from a php-parsed file of kill-words (eg 'wp-') and kill-regexes.

At present the automatically-dropped IPs are accumulated until I decide they are probably no longer a menace (ie the IP's owner has fixed the virus). I will then flush the dynamic table through Terminal using:
iptables -F Dynamic

I hope the above may be some help to others.

dstiles

8:59 am on Apr 22, 2022 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I have obviated the need for writing to a file. I discovered I could feed the IP direct to MTS after all with...
$rtn = $shellObj->exeCmd("/srv/phptabip $ip");
where phptabip is the bash script that writes the IP to iptables.

I have also discovered a termination and added that after the exeCmd - no perceptible change in timing...
$shellObj->terminate();

The solution is phyrric, however. MTS takes too long to load and write to iptables - minimum 0.29 seconds and typically up to 0.37 for a single hit and over 0.9 for a multiple. A reasonably fast bot can punch through this, leaving several records in the logs.

dstiles

8:57 am on Apr 28, 2022 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I got around 50 attempts in 30 seconds to access mysql, phpadmin, sysadmin etc files this morning, all to the same domain - which seems to attract most of these attempts. All hits were written to iptables; I say "all" - there may have been some blocking of subsequent attempts. The hits were approx 0.01 seconds apart. The process time for each hit was between 0.33 and 0.97 seconds. Obviously there was going to be a delay in beginning to block but it seems the first one should have kicked in well before the half-way mark.

Can anyone suggest a reason why a) the process is taking so long and b) why the first entry in iptables failed to block at least some of the later ones, please?

Brett_Tabke

5:41 pm on Jun 2, 2022 (gmt 0)

WebmasterWorld Administrator 10+ Year Member Top Contributors Of The Month



Speaking of Wordpress prevention and ip blocking...I'm using Brute Force Login protection. It adds the ip to the .htaccess deny list...

(not sure why this is under review at Wordpress)
[wordpress.org...]

not2easy

6:24 pm on Jun 2, 2022 (gmt 0)

WebmasterWorld Administrator 10+ Year Member Top Contributors Of The Month



Here in the WP forum Charter [webmasterworld.com] we have a link to check plugins for vulnerabilities.

They are saying it is being reviewed since 4/19/22:
The plugin is vulnerable to Cross-Site Request Forgery (CSRF) vulnerability that could allow an unauthenticated attacker to add or remove arbitrary IP addresses from the block, allow lists. For a successful attack, a privileged authenticated WordPress user would need to visit a page the attack controls, for the CSRF attack to be executed.
(from https://wpscan.com/vulnerability/c736713a-3a40-4652-ad56-33c412240588 ) Apparently the risk is considered low because it would require an authenticated user to click a link.

I use a math captcha for WP sites and boot requests for wp-login files on static sites with an old but functional trap I found on WebmasterWorld forums: [webmasterworld.com...] many moons ago.