VBScript: Stop Dictionary FTP Attacks in IIS using VBScript

Filed under: IIS, Quick Code, VBScript — Written by Chrissy on Tuesday, October 31st, 2006 @ 2:36 pm

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.

ftpdprotection.vbs

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
12 Comments   -
  • Comment by Dennis Pinckard | November 8, 2006 @ 12:08 pm

    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

  • Comment by M Mucklo | February 24, 2007 @ 8:06 pm

    Is this scripts free to use (no copyright restrictions?)

  • Comment by M Mucklo | February 24, 2007 @ 8:09 pm

    Also do these routes stick after server reboot?

  • Comment by Spencer Ruport | April 12, 2007 @ 12:51 pm

    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.

  • Comment by InTech | April 14, 2007 @ 6:11 am

    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!

  • Comment by Tim | May 27, 2007 @ 10:11 am

    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

  • Comment by Tim | May 27, 2007 @ 10:13 am

    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

  • Comment by Tim | May 27, 2007 @ 10:17 am

    Chrissy... what must I do to post my code????

    Tim

  • Comment by Tim | May 28, 2007 @ 3:58 am

    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

  • Comment by Tim | May 28, 2007 @ 4:00 am

    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.

  • Comment by Joost | August 25, 2007 @ 10:29 pm

    Thanks Tim, I took the script from your site and it works flawlessly :)

  • Comment by John Marston | September 17, 2007 @ 4:54 pm

    What's the addroute.bat file on drive w: ?

Leave your comment