31Oct/0613
VBScript: Stop Dictionary FTP Attacks in IIS using VBScript
Spencer Ruport of netortech.com modified my FTP ban script into one that stops dictionary attacks. And he chooses not to ban via the problematic IIS way but instead creating a bad route for the offending IP address. Pretty darn ingenius.
Set objFTPSVC = GetObject("IIS://localhost/MSFTPSVC")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objLog = CreateObject("MSWC.IISLog")
Set objDictionary = CreateObject("Scripting.Dictionary")
Dim Attempts(), IP(), LastAttempt(), IPs
Dim AlreadyBanned, InProcess, Session
Dim CutOff, x, i
IPs = 0
'Iterate through each FTP site.
For Each objSITE in objFTPSVC
If lcase(objSITE.class) = "iisftpserver" Then
ftpLogFilePath = objSITE.LogFileDirectory & "\msftpsvc" & objSITE.Name
Set objFolder = objFSO.GetFolder(ftpLogFilePath)
Set objFiles = objFolder.Files
For Each fileName In objFiles
lastFile = fileName
Next
strLogFile = lastFile
Set file = Nothing
Set objFolder = Nothing
'Use the IIS log file parser provided by MSFT
objLog.OpenLogFile strLogFile, 1, "MSFTPSVC", 1, 0
'(FileName,IOMode,ServiceName,ServiceInstance,OutputLogFileFormat)
' 0 = NotApplicable, 1 = ForReading
Do While NOT objLog.AtEndOfLog
objLog.ReadLogRecord
If instr(ucase(objLog.Method), "USER") Then
'Someone is attempting to authenticate with the FTP server
InProcess = False
AlreadyBanned = False
For x = 0 to IPs - 1
'Check to see if this IP address has already attempted to log in.
If objLog.ClientIP = IP(x) Then
'To be nice this code will give the user
'another attempt every minute.
'This is to avoid banning a person who
'attempts to log in multiple times
'in a day but keeps forgetting their password.
Attempts(x) = Attempts(x) + datediff("n", objLog.DateTime, lastattempt(x))
LastAttempt(x) = objLog.DateTime
InProcess = True
End If
Next
If Not InProcess Then
'If we aren't keeping track of this IP
'it's possible the IP has already been
'added to the ban list.
AlreadyBanned = objDictionary.Exists(objLog.ClientIP)
End If
If Not InProcess And Not AlreadyBanned Then
'First authentication attempt by this IP
IPs = IPs + 1
Redim Preserve IP(IPs)
Redim Preserve Attempts(IPs)
Redim Preserve LastAttempt(IPs)
IP(IPs - 1) = objLog.ClientIP
Attempts(IPs - 1) = 1
LastAttempt(IPs - 1) = objLog.DateTime
End If
ElseIf instr(ucase(objLog.Method), "PASS") Then
'The server is responding to an authentication
'attempt.
For x = 0 to IPs - 1
'See if we're keeping track of this IP
If IP(x) = objLog.ClientIP Then
'Increment the authentication attempts
Attempts(x) = Attempts(x) + 1
If Attempts(x) = 10 Then
'Ban if necessary
WScript.Echo "Banning " & objLog.ClientIP & "..."
objDictionary.Add objLog.ClientIP, "255.255.255.255"
AddDeadRoute objLog.ClientIP
For i = x to (IPs - 2)
IP(i) = IP(i + 1)
Attempts(i) = Attempts(i + 1)
Next
IPs = IPs - 1
Redim Preserve IP(IPs)
Redim Preserve Attempts(IPs)
End If
End If
Next
End If
Loop
objLog.CloseLogFiles 1
End If
Next
Set objDictionary = Nothing
Set objLog = Nothing
Set objFSO = Nothing
Set objFTPSVC = Nothing
Function addDeadRoute(IP)
dim adoDBConn, sConnStr, objFSO, objBanBatch, cmdShell, WshShell
Dim fakeGateway
'I've been told that IPSec could be used to block certain IPs
'but 1) I'm not sure WMI can interface with it and 2) I don't
'know how to begin with. Someone gave me a much easier solution
'Simply add a route to the routing table pointing to a gateway
'that doesn't exist.
'Ensure that this IP is on the same subnet as the server
fakeGateway = "192.168.1.101"
Set WshShell = WScript.CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objBanBatch = objFSO.CreateTextFile("W:\config files\blocks\addRoute.bat", True)
objBanBatch.WriteLine "ROUTE ADD " & IP & " MASK 255.255.255.255 " & fakeGateWay
objBanBatch.Close
WshShell.Run """W:\config files\blocks\addRoute.bat""", 1, True
Set objFSO = Nothing
End Function



November 8th, 2006 - 12:08
Is this a modification of your script? I don’t see the event sink code in it. It would be nice if Spencer had provided a bit more info on usage. That said, I’m looking forward to putting it in place as we just started getting hit with an FTP attack.
Thanks
February 24th, 2007 - 20:06
Is this scripts free to use (no copyright restrictions?)
February 24th, 2007 - 20:09
Also do these routes stick after server reboot?
April 12th, 2007 - 12:51
For some reason I couldn’t get the event code to work on my server. I’m sure this was a mistake on my part but since my FTP logs are relatively small I just have the script execute every 10 minutes.
Also the routes don’t stick after a reboot. Most of these attacks are coming from dynamic IPs so I only want the blocks in place long enough for the attackers to give up.
There’s no copyright to the modifications I made.
April 14th, 2007 - 06:11
Has anyone gotten this to work? With no sink, am I just running this as a scheduled task to check connections?
I NEED THIS! MY FTP SERVER IS CONSTANTLY BEING ATTACKED!
May 27th, 2007 - 10:11
Ok… well
I’ve been playing with this script (and chrissy’s orignal) for quite a while now. After a LOT of experimenting, I decided I really don’t need the IP’s added to the ftp directory security table…. what’s the point? If the IP’s are added as a bad route… that handles it all.
I’ve also re-thought the code a few times.
I’ve deleted the generic ftp file path finder… you can add it back in if you need it. I just hard coded a path to the one ftp file on my server. It’s less flexible but MUCH simpler.
I added in the code to delete older log files.
I changed the ROUTE ADD statement to make it a persistent route.
Finally, I changed the way the dictionary objects were being used, to let them keep count of the login attempts in the log file.
Here’s where I’m at…. it’s working 100% for me….
‘ Push Event Viewer Alert
Set objWMIService = GetObject(“winmgmts:{(security)}!root/cimv2″)
Set eventSink = wscript.CreateObject(“WbemScripting.SWbemSink”, “EVSINK_”)
strWQL = “Select * from __InstanceCreationEvent where TargetInstance isa ‘Win32_NTLogEvent’ and TargetInstance.SourceName = ‘MSFTPSVC’ and TargetInstance.EventCode = 100″
objWMIService.ExecNotificationQueryAsync eventSink,strWQL
‘Keep it going forever
While (True)
Wscript.Sleep(1000)
Wend
Sub EVSINK_OnObjectReady(objObject, objAsyncContext)
Set objDictionary = CreateObject(“Scripting.Dictionary”)
Set objFSO = CreateObject(“Scripting.FileSystemObject”)
Set objLog = CreateObject(“MSWC.IISLog”)
Set WshShell = WScript.CreateObject(“WScript.Shell”)
serverIP = “65.23.156.121″
xMax = 3 ‘Max number of invalid login attempts
xLogFiles = 10 ‘Max number of log files to keep in the folder before deleting them
Set objFolder = objFSO.GetFolder(“C:\WINDOWS\system32\LogFiles\MSFTPSVC1\”)
Set objFiles = objFolder.Files
For Each fileName In objFiles
lastFile = fileName
Set f = objFSO.GetFile(fileName)
If f.DateCreated
May 27th, 2007 - 10:13
Not sure why my post was cut short. Here’s the rest of it… with a little overlap.
For Each fileName In objFiles
lastFile = fileName
Set f = objFSO.GetFile(fileName)
If f.DateCreated
May 27th, 2007 - 10:17
Chrissy… what must I do to post my code????
Tim
May 28th, 2007 - 03:58
OK… let’s try this once more using the quickcode tags….
‘ Push Event Viewer Alert
Set objWMIService = GetObject(“winmgmts:{(security)}!root/cimv2″)
Set eventSink = wscript.CreateObject(“WbemScripting.SWbemSink”, “EVSINK_”)
strWQL = “Select * from __InstanceCreationEvent where TargetInstance isa ‘Win32_NTLogEvent’ and TargetInstance.SourceName = ‘MSFTPSVC’ and TargetInstance.EventCode = 100″
objWMIService.ExecNotificationQueryAsync eventSink,strWQL
‘Keep it going forever
While (True)
Wscript.Sleep(1000)
Wend
Sub EVSINK_OnObjectReady(objObject, objAsyncContext)
Set objDictionary = CreateObject(“Scripting.Dictionary”)
Set objFSO = CreateObject(“Scripting.FileSystemObject”)
Set objLog = CreateObject(“MSWC.IISLog”)
Set WshShell = WScript.CreateObject(“WScript.Shell”)
serverIP = “65.23.156.121″
xMax = 3 ‘Max number of invalid login attempts
xLogFiles = 10 ‘Max number of log files to keep in the folder before deleting them
Set objFolder = objFSO.GetFolder(“C:\WINDOWS\system32\LogFiles\MSFTPSVC1\”)
Set objFiles = objFolder.Files
For Each fileName In objFiles
lastFile = fileName
Set f = objFSO.GetFile(fileName)
If f.DateCreated
May 28th, 2007 - 04:00
I give up. It seems your page won’t accept the less than character that immediately follows the DateCreated in the last line above. Visit my website (vizimetrics.com) for the code.
August 25th, 2007 - 22:29
Thanks Tim, I took the script from your site and it works flawlessly
September 17th, 2007 - 16:54
What’s the addroute.bat file on drive w: ?
January 9th, 2009 - 03:13
Hello Chrissy,
I used part of your code to create an OpenSource software called WinFail2Ban (detect IP from failed logins and ban IP).
The project page is: http://winfail2ban.sourceforge.net
Regards and thanks !!!
Vittorio