Monday, August 10, 2009

[Windows] Automatically banning an IP address in IIS FTP

(Reposted from my mscorlib blog)
We've been having a horrible time with zombie attacks against our IIS FTP server for a long time now. Almost every Saturday night, like clockwork, they start their attack. They attempt to log in with names that systems may use: administrator, oracle, mysql, admin, etc. I don't care much since those accounts don't even exist. I just get annoyed with seeing them in my log files and filling up my event log. Up until today, I have been banning the IP addresses by hand. No more.

I had 100 megs in log files over the weekend. That was the last straw. After quite a bit of searching and messing around, I found a script to solve my problems. Below is a step-by-step walkthrough on how to set this script to run as a service.

The following is how I solved this problem on our Windows 2003 server (running the Web edition of Win2k3).

First off, you need to grab the script file from the aforementioned site (blog.netnerds.net) and stick it on your server. Mine is in c:\scripts. I named it BanIP.vbs before I realized there was a button on the site to download it as banftpips.vbs.

Next, you'll need two files from the MS resource kit, Srvany.exe and Instsrv.exe. You can get it from Microsoft. Download the executable and run it on your work computer or dev box. You just need the two files mentioned above, not the whole thing. Stick these two files in the same spot as the script to make it easier.

Open up a command prompt and change to the scripts directory. Enter the following command to register srvany as a service:

C:\Scripts\instsrv.exe BanIP C:\Scripts\Srvany.exe

This will create a registry setting. Use regedit to open up the following key:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\BanIP

Create a new key and call it Parameters

In the Parameters key, create a new String Value called Application. Set the value of Application to:

C:\Windows\System32\CScript.exe C:\Scripts\BanIP.vbs

Open up your services. You should see BanIP there. Click the start icon if it's not already going. Using a separate computer to test with, hit your ftp site and try to log in as administrator with a bad password. I can't stress enough, use a different computer than your primary box. The IP address will be banned for the entire site. You can see the ban under the routing tables by using 'route print' at the command prompt. You can delete the route with 'route delete '. There is also a new entry in the FTP security settings.

It's a great script, and fairly easy to modify. For more help on the script itself, check out the site and read the comments. Lots of good stuff there. Special thanks to the author, Chrissy. This will surely slow down our zombie problems.

Comments from mscorlib
You can set the service to start manually (or disable it). That will stop it. To clean it up, you'll need to use the 'route' command to remove the entries it made, as well as cleaning up the IIS FTP security stuff (where it blocks the IP addresses). Make sure you move the log files somewhere else first, however, since the script will look at the log files and reban the IP address if it sees it again (this caused me some headaches originally since I didn't know it did that).

I've been running it now since I posted this article, and the attempts at breaking in are way down. The routes look like they reset eventually, either via an update we did, or something else, but the IIS settings are still there. Our log files are much smaller, and I don't have to constantly check the site to see if someone is breaking in. I set it up to run off of several keywords and haven't had any problems.


I _think_ this is how it's working... like I mentioned, the actual script isn't mine. But basically the first spot you see that, it triggers the code that scans the log files (according to the notes at the very top of the script). The second spot is where it is actually looking for the usernames to ban. I put admin and administrator in the first spot:

If InStr(LCase(objObject.TargetInstance.Message),"administrator") > 0 OR InStr(LCase(objObject.TargetInstance.Message),"admin") > 0 Then

And in the second spot I put the rest of the keywords:

If sUsername = "administrator" OR sUsername = "mysql" OR sUsername = "admin" Then

So basically, if they do admin/administrator, it will trigger the log scanning, which will also ban any attempts at 'mysql'. But mysql will not trigger the script. Since admin/administrator were the two largest failed attempts, I used those to set things off. If we had more, I'd probably throw them in to both places just to be safe. If you have a massive list, you may want to consider using an array or just a delimited string and using instr on it. Might be easier to maintain.


http://blog.integrii.net/?p=18 - I created a remote installer (including registry entries) using psexec. This automates your whole process with a little tuning! Email me for blog link exchange! (I removed his email address to reduce his spam)



[C#] DayOfWeek as numeric

(Reposted from my mscorlib blog)

I'm writing a timecard program for our workplace. I was trying to get the day of the week as a number (today being Friday, I was looking for a '5'). I couldn't find much info on Google, aside from some article talking about something unrelated. I saw a snippit of code there. Too easy, I thought:

int dayofweek = (int)DateTime.Now.DayOfWeek;

And that's it. *slaps forehead* I suppose I should have tried that first since it's a two-second check. Oh well. Since I was searching Google without much luck, I hope to get this out there so others might see it.

As for finding a specific day of the week, I went with this for now:

int dayofweek = (int)DateTime.Now.DayOfWeek;
int offset = 0;
DateTime thisFriday;

if (dayofweek < 5)
offset = 5 - dayofweek;
else
offset = dayofweek - 5;
thisFriday = DateTime.Now.AddDays(offset);

I spent about 5 minutes doing that. It calculates when the next upcoming Friday is (if the current date is Friday, then it shows that). I've set it in to a for loop to spit out a month of Fridays by combining it with AddDays. As always, let me know if there's a better way.

Comments from mscorlib
I resolve with

(Int32)Enum.Parse(typeof(DayOfWeek), dpartenza.DayOfWeek.ToString())


[Javascript] Simple Javascript Validation

(Reposted from my MSCorlib blog)

I just had to fix this code to make it work in FireFox, so I thought I'd put it up here on MSCorlib also. I'm pretty sure I wrote this, but maybe I'm mental and found it somewhere on Google. It's been a few years since I touched it. At any rate, this is a quick article to show it works. Since most of you are probably on to ASP.Net, you won't need this. But it's still handy if you have to maintain some older classic ASP stuff, or some straight HTML/Javascript.

Basically, I had a big long function that went through each element on the form that I wanted to make required. So something like email, phone, name, etc. Each of these were hard coded in to the Javascript code. I decided I hated that since it was a pain in the ass to work with every time I had to modify it. To get around this, I created a single function that would go through each element and see if it was required. If it was, return an error message to the user to let them know what to fix.

The hard part about this was making a message that was easy to understand. There are two ways to do this. 1) use an attribute to store the "user-friendly" field name. or 2) Parse the name property of the element. Since I name all my elements in a similar fashion, I went with #2 (to be honest, option #1 didn't occur to me until about 5 minutes ago).

So the code. First, you need an input button on your form:

<input type="button" name="btnSubmit" value="Submit" onclick="SubmitPage();" />

This button is wired up to the SubmitPage function. That function will do the error check and then either submit the page or show the error:

function SubmitPage()
{
var sError, rExp
rExp = /_/g; // Regular expression to replace underscores globally

sError = ErrorCheck();
if(sError.length > 0) {
alert(sError.replace(rExp," "));
return false;
} else {
document.forms[0].submit();
}
}

To make this print out the correct name of the field, I replace underscores with a space. The first three letters are also trimmed off since I use those three letters as the type of field. For example, txtEmail, txtFirst_Name, etc. If you wanted to get rid of this code, you could modify it to use a 2nd parameter for the user to see instead of this hacked up way.

The function calls another function to get the error, if it exists:

function ErrorCheck()
{
var i, sField, sError, sFieldName;
sError = "";
for (i=0; i <> 0) {
sFieldName = document.forms[0].elements[i].name.substr(3)
sError = sError + sFieldName + " is required.\n";
}
}
}
return sError;
}

This function loops over every element in form[0] to see if it is required. If it is, the field name is appended to error message, and then the whole thing is returned to the calling function. If the calling function sees a length > 0, it does an alert and shows the user the problem. The final part of the code is just putting the required attribute on the input box:

<input type="text" name="txtName" required="true" />

And that's it. Any field with 'required=true' will be checked and spit out in the event of an error. It's quick, it's dirty. Make sure you do server-side checking as well since client-side only checking is easy to get around.

[Linux] Version Magic

(Reposted from my mscorlib account)

First off, some background information. I am an Oracle DBA, not a Red Hat admin. I only play one here at work because we don't have a real one. I'm running Red Hat ES v4 for an 11i install of Oracle. The server itself is an HP Blade server. The server is currently being built up to replace our existing version 10 Oracle Apps install, so it is not production. However, it may as well be since we have an offshore team that is working on it. When it's down, they twiddle their thumbs and I look stupid.

Yesterday afternoon we installed the support pack for HPs Blade server. We did this because we were trying to set up disk mirroring, something that had been left off when the server was set up originally. The install had about 20 items to install. About 4 failed. There really wasn't much of an error given, and since they weren't related to what we needed, we ignored it and rebooted. That's when the fun began.

When the machine started back up, I knew something was wrong when it failed to detect the ethernet ports (eth0-eth3). After logging back in to the machine, the network would not activate. I had various error messages as I worked on the problem, from:

tg3 device eth1 does not seem to be present, delaying initialization

to an error message about the MAC address. The network card itself is a Broadcom Corporation NetXtreme BCM 5703. tg3 is the driver they have you use. eth1 is the port I was plugged in to.

After some poking around, I figured that the driver was some how corrupt or invalid. I thought it might be out of date, so I set out to find a new driver. I downloaded both a Broadcom driver and a tg3 driver. Both compiled and looked good, but when I tried to install, I had the version error in /var/log/messages:

tg3: version magic '2.6.9-22.ELsmp SMP 686 REGPARM 4KSTACKS gcc-3.2' should be 'tg3:version magic '2.6.9-22.ELsmp SMP686 REGPARM 4KSTACKS gcc-3.4'

Now, a seasoned Linux Guru would probably look at that and notice immediate what is wrong.... gcc is buggered. I didn't notice that, so I continued what I was doing. Let me just add that the HP guys aren't too good at Linux. This morning we found someone who may have been able to help, but we were able to fix it right as we got a hold of her.

Anyway, here's the point of this whole blog. When you install Oracle 11i, there's a step in the installation procedures that has you rename gcc and use an older version. Ah, now perhaps you see the problem. Because we're only using the server for Oracle, we never needed gcc and were still using an older version. After swapping versions to the newer one, we ran the HP support installer again. And what do you know, everything installed. All 20+ package had no problems at all.

I rebooted the server and the network cards were found once again. So there you have it. I'm hoping that other admins might find this on Google if they are struggling to solve the problem like I was. If you are running Oracle 11i, don't forget that you probably renamed the gcc file when you installed.

Comments from mscorlib

Hi there, we are having the exact same problem. What is the best way to rename gcc and use the older version?

In our case, we had the old files still, just renamed to something like gcc_bak. I just renamed the current version to something else, and renamed our backup to gcc. That fixed the problem. If you don't have a backup of gcc, you might be able to grab it off of a different server if you have the same version of OS (possibly hardware as well). Just make sure to backup your current version in case that's not the problem.


Glad I found this page, been hunting around for a solution for a few days.


I don't know who replaced gcc with the "old" version for oracle, but it would have been nice to know.

What confused me was that querying the installed version of the gcc packages reported all ok at version 3.4

If I'd thought about it a bit more I would have run the following command to check for file changes:

for i in $(rpm -qa | grep gcc); do rpm -V $i; done;
S.5....T /usr/bin/g++
S.5....T /usr/bin/gcc

For the non admin types:
rpm -qa | grep gcc lists for all packages with gcc in the name
rpm -V $i Verifies the files in each package found and lists files that have changed.

Hope this helps some one else!




Same problem on a Red Hat Enterprise Linux AS4 U4 with Oracle Application Server 10g (Update 9.0.4.2.0).
Solved installing gcc-3.4.6-9 and requirements (cpp-3.4.6-9, libgcc-3.4.6-9).
Thanks!



...and same for a RHEL3 U2 with Oracle Application Server 10g (Update 9.0.4.1.0). Required re-installation of gcc-3.2.3-39 and gcc-c++-3.2.3-39 and re-installation of Proliant Support Pack. Afterwards found that there existed both /usr/bin/gcc.backup and /usr/bin/g++.backup, probably renamed during OAS installation, so that it would only have been necessary to replace current gcc & g++ with .backup files before re-installing HP PSP.



One thing to keep in mind, is that when you go to patch the server, these files may be overwritten with new versions. When you go to install a patch (typically via adpatch, although I'm sure opatch could fail also), you will get another error about gcc. I've modified my patch notes that we use to include a step to check the filesizes of gcc. I have a backup copy now and if they are the wrong size, I just copy them back in before I start patching, whether they are needed or not. The last thing you want to see is a patch fail to install because of something so simple. Glad it is helping others though.