Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

FTP maximum password attempts

1,725 views
Skip to first unread message

nx-...@winvoice.com

unread,
Sep 25, 2006, 10:26:11 AM9/25/06
to
I've done quite a bit of searching on this and I haven't found a
satisfactory answer besides a few expensive add-on packages. I'm
hoping someone has some ideas.

Basically, I'm getting brute forced to death on my IIS FTP sites. They
haven't found their way in yet, but, this weekend, I had 11 different
ip addresses(one in the US, several in Korea, one in Argentena, several
in China, etc) hitting my two different servers(on two different
networks) at a rate of about 25 password attempts per second. I came
in on saturday, blocked(via ip security policy) the 4 addresses that
where busy, then came in this morning(monday) and blocked the
remaining. This sitting around watching log files and blocking
addresses doesn't solve the underlying problem in that I need to be
able to just block an ip after so many attempts.

I used to use a 3rd party FTP server back on NT/2000 because I read at
how horrible IIS was, but as soon as I got Server 2003, I switched to
IIS's FTP. But good lord, this isn't what I call secure when its just
a matter of time before they hit the right combination, no matter how
long/complex I've made my users passwords(which, they've already
complained about).

If any of the IIS team are reading this, give us a "Maximum Password
Tries" and a "Block for how many minutes" capability. Reset the list
when IIS is restarted if you want, but, give us some simple to
configure tools to help curb this problem.

Funkadyleik Spynwhanker

unread,
Sep 25, 2006, 11:08:12 AM9/25/06
to
Simple? Don't use FTP. That's the only real solution that is also simple.

It _is_ only a matter of time. And if you leave the users up to change
their passwords they'll get in in seconds or minutes.

A couple of suggestions;

If you have no business need to have the pacific rim access your site via
FTP, block all their class As at the firewall. You can find lists of these
addresses at a lot of anti-spam sites. Don't mess around with single IPs,
they are compromised machines and will probably never come back. They'll
just go on to a different target and set up a new zombie to try your server.
With 10,000s of these machines in each country over there... you will get
tired of it long before they do. Typically, FTP is a "webmaster" tool, not
an end user tool. So you can do this and offer any downloads you need to
them via HTTP.

Better yet, find out the WAN IP addresses of the authorized users, and block
ALL access to FTP except for those. (Use entire C blocks for dynamic users.)

If you don't have access to a real firewall, you can use IIS "security
settings" to sort of do it there. It won't stop the traffic, but it will
keep them from guessing a password. Additional use, is doing that causes
the FTP log in attempt to log the password they are trying.

<nx-...@winvoice.com> wrote in message
news:1159194370.9...@m7g2000cwm.googlegroups.com...

nx-...@winvoice.com

unread,
Sep 26, 2006, 10:25:13 AM9/26/06
to
> Simple? Don't use FTP. That's the only real solution that is also simple.

Not very simple if you have old softwares that sending tracking
information via FTP up to the server that where written back in 1996
and do not lend themselves to be upgraded. Combined with several web
sites that we host that have various companies updating their sites.
We do have some of our demos available for FTP download, I could
certainly close down that FTP site, but it still leaves several others
that really have to stay online without deticating a huge amount of
resources to rewriting applications and retraining people that have
been happy and healthy for 10 years now.

Roger Abell [MVP]

unread,
Sep 26, 2006, 10:57:52 PM9/26/06
to
This is indeed a need without a defined response capability in Windows
(and not just for FTP auth attempts). Some form of host based IPS
(intrusion prevention) within Windows would be welcome.

However, I am concerned in that your post seemed to indicate a
worry that one of these may find the right password, which in turn
implies you are seeing attempts using valid account names.
I deal with runs of auth attempts, but they are dumb (even attempting
login with built-in group names!!) and mostly just bandwidth and cpu
cycle pests. If you must have the exposures the pests probe, and you
have no form of IPS that can detect and block, then you live with it.
If however you are seeing valid account names attempted you do
definitely need to examine all aspects of your exposure to the network.

Roger

<nx-...@winvoice.com> wrote in message
news:1159194370.9...@m7g2000cwm.googlegroups.com...

nx-...@winvoice.com

unread,
Oct 13, 2006, 12:15:37 AM10/13/06
to
Upon leaving for the day, I checked logs and promptly shut down another
attack, then, before I could leave, another ip address took over, and
another and another for about 20 addresses.

So, now, a couple of frustrating hours later, I'd like to share the
source code of what I pieced together from various code chunks around
the net. I've got the IISBlockFTPAttempts class used in another app
that runs on a schedule anyway but used this test console app when I
was debugging which someone could set up in scheduler or adapt it to
anything else. It simply runs through each of the FTP sites, checks
for bad password entries in the logs for the day, counts them up, if
the number of attempts from an ip address exceeds a given number, then
it adds that ip address to the denied addresses for all the FTP sites.

using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Xml;
using System.DirectoryServices;
using System.IO;

namespace IISBlock
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Console.WriteLine("Starting");
IISBlockFTPAttempts ftpb=new
IISBlockFTPAttempts("WEBSERVER1","MyAdmin","MyAdminPass",30);

ftpb.GetBlockList();
ftpb.FTPBlock();

//ftpb.Remove.Add("10.10.10.8");
//ftpb.FTPRemove();

//ftpb.Blocked.Add("10.10.10.8");
//ftpb.FTPBlock();

Console.WriteLine("Finished");
}
}
public class IISBlockFTPAttempts
{
private string ServerName;
private string ServerUserName;
private string ServerPassword;
private int AttemptsBeforeKill;
public ArrayList Blocked=new ArrayList();
public ArrayList Remove=new ArrayList();

public IISBlockFTPAttempts(string ServerName,string
ServerUserName,string ServerPassword,int AttemptsBeforeKill)
{
this.ServerName=ServerName;
this.ServerUserName=ServerUserName;
this.ServerPassword=ServerPassword;
this.AttemptsBeforeKill=AttemptsBeforeKill;
}
public void GetBlockList()
{
SortedList ht=new SortedList();
DateTime dt=DateTime.Now;
string
FileName="ex"+(dt.Year-2000).ToString("00")+dt.Month.ToString("00")+dt.Day.ToString("00")+".log";
DirectoryEntry root = new
DirectoryEntry("IIS://"+ServerName+"/MSFTPSVC",ServerUserName,ServerPassword);
foreach (DirectoryEntry IIS in root.Children)
{
if (IIS.SchemaClassName == "IIsFtpServer")
{
Console.WriteLine(IIS.Name);
string LogFilePath = System.IO.Path.Combine(
IIS.Properties["LogFileDirectory"].Value.ToString(),
"MSFTPSVC"+IIS.Name);
string LogFileName=LogFilePath+"\\"+FileName;
Console.WriteLine(LogFileName);
if(File.Exists(LogFileName))
{
FileStream fs=new
FileStream(LogFileName,FileMode.Open,FileAccess.ReadWrite);
StreamReader sr=new StreamReader(fs);
string s;
while((s=sr.ReadLine())!=null)
{
if(s.IndexOf("PASS - 530")!=-1)
{
string[] fields=s.Split(' ');
if(fields.Length>1)
{
string ip=fields[1].Trim();
if(ht.ContainsKey(ip))
ht[ip]=((int)ht[ip])+1;
else
ht.Add(ip,(int)1);
}
}
}
sr.Close();
fs.Close();
}
IIS.Close();
//IIS.Dispose();
}
}
root.Close();
//root.Dispose();
for(int i=0;i<ht.Count;i++)
{
string ip=(string)ht.GetKey(i);
if((int)ht[ip]>AttemptsBeforeKill)
Blocked.Add(ip);
}
}
public void FTPBlock()
{
DirectoryEntry root = new
DirectoryEntry("IIS://"+ServerName+"/MSFTPSVC",ServerUserName,ServerPassword);
foreach (DirectoryEntry site in root.Children)
{
if (site.SchemaClassName == "IIsFtpServer")
{
Console.WriteLine(site.Name);
DirectoryEntry IIS = new
DirectoryEntry("IIS://"+ServerName+"/MSFTPSVC/"+site.Name+"/root",ServerUserName,ServerPassword);
Type typ = IIS.Properties["IPSecurity"][0].GetType();
object IPSecurity = IIS.Properties["IPSecurity"][0];

Array origIPDenyList = (Array) typ.InvokeMember("IPDeny",
BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.GetProperty,
null, IPSecurity, null);

Array.Sort(origIPDenyList);

ArrayList ToAdd=new ArrayList();
for(int i=0;i<Blocked.Count;i++)
{
string block=Blocked[i]+", 255.255.255.255";
bool bAlreadyDone=false;
foreach(string s in origIPDenyList)
{
if(s==block)
{
Console.WriteLine(s+" = "+block);
bAlreadyDone=true;
}
}
if(bAlreadyDone==false)
{
ToAdd.Add(block);
Console.WriteLine("Adding "+block);
}
}
if(ToAdd.Count>0)
{
bool bGrantByDefault = (bool) typ.InvokeMember("GrantByDefault",
BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.GetProperty,
null, IPSecurity, null);

if(!bGrantByDefault)
{
typ.InvokeMember("GrantByDefault",
BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.SetProperty,
null, IPSecurity, new object[] {true});
}

object[] newIPDenyList = new
object[origIPDenyList.Length+ToAdd.Count];
int i=0;
foreach(string s in origIPDenyList)
{
newIPDenyList[i]=s;
i++;
}
for(int x=0;x<ToAdd.Count;x++)
{
newIPDenyList[i]=(string)ToAdd[x];
i++;
}
Array.Sort(newIPDenyList);

typ.InvokeMember("IPDeny",
BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.SetProperty,
null, IPSecurity, new object[] {newIPDenyList});

IIS.Properties["IPSecurity"][0] = IPSecurity;

// commit the changes
IIS.CommitChanges();
//IIS.RefreshCache();
IIS.Close();
//IIS.Dispose();
Console.WriteLine("Added");
}
else
Console.WriteLine("None to add");
}
}
root.CommitChanges();
root.Close();
//root.Dispose();
}
public void FTPRemove()
{
DirectoryEntry root = new
DirectoryEntry("IIS://"+ServerName+"/MSFTPSVC",ServerUserName,ServerPassword);
foreach (DirectoryEntry site in root.Children)
{
if (site.SchemaClassName == "IIsFtpServer")
{
Console.WriteLine(site.Name);
DirectoryEntry IIS = new
DirectoryEntry("IIS://"+ServerName+"/MSFTPSVC/"+site.Name+"/root",ServerUserName,ServerPassword);

Type typ = IIS.Properties["IPSecurity"][0].GetType();
object IPSecurity = IIS.Properties["IPSecurity"][0];

Array origIPDenyList = (Array) typ.InvokeMember("IPDeny",
BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.GetProperty,
null, IPSecurity, null);

Array.Sort(origIPDenyList);
ArrayList ToRemove=new ArrayList();
for(int i=0;i<Remove.Count;i++)
{
string block=Remove[i]+", 255.255.255.255";
bool bAlreadyDone=false;
foreach(string s in origIPDenyList)
{
if(s==block)
{
Console.WriteLine(s+" = "+block);
bAlreadyDone=true;
}
Console.WriteLine("* "+s);
}
if(bAlreadyDone==true)
{
ToRemove.Add(block);
Console.WriteLine("Removing "+block);
}
}
if(ToRemove.Count>0)
{
bool bGrantByDefault = (bool) typ.InvokeMember("GrantByDefault",
BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.GetProperty,
null, IPSecurity, null);

if(!bGrantByDefault)
{
typ.InvokeMember("GrantByDefault",
BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.SetProperty,
null, IPSecurity, new object[] {true});
}

object[] newIPDenyList = new
object[origIPDenyList.Length-ToRemove.Count];
int i=0;

foreach(string s in origIPDenyList)
{
if(!ToRemove.Contains(s))
{
newIPDenyList[i]=s;
i++;
}
}
Array.Sort(newIPDenyList);

typ.InvokeMember("IPDeny",
BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.SetProperty,
null, IPSecurity, new object[] {newIPDenyList});

IIS.Properties["IPSecurity"][0] = IPSecurity;

// commit the changes
IIS.CommitChanges();
//IIS.RefreshCache();
IIS.Close();
//IIS.Dispose();
Console.WriteLine("Removed");
}
else
Console.WriteLine("None to remove");
}
}
root.CommitChanges();
root.Close();
//root.Dispose();
}
}
}

Roger Abell [MVP]

unread,
Oct 15, 2006, 10:51:29 AM10/15/06
to
Nice. You now have me thinking of extending this in two ways.
Reading other logs (security and IIS http), and adding address
to a "block pest" IPsec filter.

Thanks for sharing.


Roger
<nx-...@winvoice.com> wrote in message

news:1160712937.1...@i3g2000cwc.googlegroups.com...

nx-...@winvoice.com

unread,
Nov 14, 2006, 9:32:40 AM11/14/06
to
> Nice. You now have me thinking of extending this in two ways.
> Reading other logs (security and IIS http), and adding address
> to a "block pest" IPsec filter.

I did the IPSec stuff last week due to a new string of attacks...

using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Xml;
using System.DirectoryServices;
using System.IO;

using System.Text;
using IISLog;

namespace IISBlock
{
class Class1
{


[STAThread]
static void Main(string[] args)
{
Console.WriteLine("Starting");

FTPBlock ftpb=new FTPBlock("MyWebServer","MyAdmin","MyPassword");

ftpb.GetBlock(DateTime.Now,10);
ftpb.Block();

IPSecBlock ipsb=new IPSecBlock("MyIPSecRule");
ipsb.GetBlock(DateTime.Now,10);
ipsb.Block();

Console.WriteLine("Finished");
}
}
public class IPSecBlock
{
private string FilterList;
public ArrayList Blocked=new ArrayList();
public ArrayList Removed=new ArrayList();
public ArrayList Errors=new ArrayList();
public IPSecBlock(string FilterList)
{
this.FilterList=FilterList;
}
public void Block()
{
if(Blocked.Count==0)
return;


for(int i=0;i<Blocked.Count;i++)

AddFilter(FilterList,(string)Blocked[i]);
}
private void AddFilter(string FilterList,string ip)
{
ProcessStartInfo pi = new ProcessStartInfo("netsh");
pi.Arguments = "ipsec static add filter filterlist="+FilterList+"
\"description="+ip+" on

"+DateTime.Now.ToShortDateString()+"\" srcaddr="+ip+" dstaddr=Me
protocol=any mirrored=yes";
pi.WindowStyle = ProcessWindowStyle.Hidden;
Console.WriteLine("netsh "+pi.Arguments);
System.Diagnostics.Process.Start(pi);
}
public void GetBlock(DateTime dt,int AttemptsBeforeKill)
{
SortedList ht=new SortedList();
EventLog el=new EventLog("Security");
for(int i=0;i<el.Entries.Count;i++)
{
EventLogEntry en=el.Entries[i];
if(en.EventID==529 && en.TimeGenerated>=dt.AddMinutes(-15))
{
int g=en.Message.IndexOf("Source Network Address:");
if(g!=-1)
{
string ip=en.Message.Substring(g);
ip=ip.Replace("Source Network Address:","");
g=ip.IndexOf("\n");
if(g!=-1)
ip=ip.Substring(0,g);
g=ip.IndexOf("\r");
if(g!=-1)
ip=ip.Substring(0,g);
ip=ip.Trim();
if(ip.Length>0 && ip.IndexOf("10.")!=0) // filter out my internal
addresses
{


if(ht.ContainsKey(ip))
ht[ip]=((int)ht[ip])+1;
else
ht.Add(ip,(int)1);
}
}
}
}

for(int i=0;i<ht.Count;i++)
{
string ip=(string)ht.GetKey(i);
if((int)ht[ip]>AttemptsBeforeKill)
Blocked.Add(ip);
}
}
}

public class FTPBlock


{
private string ServerName;
private string ServerUserName;
private string ServerPassword;

public ArrayList Blocked=new ArrayList();
public ArrayList Removed=new ArrayList();
public ArrayList Errors=new ArrayList();

public FTPBlock(string ServerName,string ServerUserName,string
ServerPassword)
{
this.ServerName=ServerName;
this.ServerUserName=ServerUserName;
this.ServerPassword=ServerPassword;
}
public void GetBlock(DateTime dt,int AttemptsBeforeKill)
{
SortedList ht=new SortedList();
string

FileName="ex"+(dt.Year-2000).ToString("00")+dt.Month.ToString("00")+dt.Day.ToString("00")+".log";
DirectoryEntry root = new

DirectoryEntry("IIS://"+ServerName+"/MSFTPSVC",ServerUserName,ServerPassword);
foreach (DirectoryEntry IIS in root.Children)
{
if (IIS.SchemaClassName == "IIsFtpServer")
{

string LogFilePath = System.IO.Path.Combine(
IIS.Properties["LogFileDirectory"].Value.ToString(),
"MSFTPSVC"+IIS.Name);
string LogFileName=LogFilePath+"\\"+FileName;

if(File.Exists(LogFileName))
{
try
{
IISLog.LogScriptingClass l=new IISLog.LogScriptingClass();
l.OpenLogFile(LogFileName,IOMode.ForReading,"MSFTPSVC",1,"0");
while(!l.AtEndOfLog())
{
l.ReadLogRecord();
if(l.ProtocolStatus!=null)
{
if(l.ProtocolStatus.ToString()=="530")
{
string ip=l.ClientIP.ToString();


if(ht.ContainsKey(ip))
ht[ip]=((int)ht[ip])+1;
else
ht.Add(ip,(int)1);
}
}
}

l.CloseLogFiles(IOMode.AllOpenFiles);
}
catch(Exception ex)
{
Errors.Add(ex.Message.ToString());
}

/*
try
{
FileStream fs=new

FileStream(LogFileName,FileMode.Open,FileAccess.Read,FileShare.ReadWrite);
StreamReader sr=new

StreamReader(fs);
string s;


while((s=sr.ReadLine())!=null)
{


if(s.IndexOf("PASS - 530")!=-1)
{


string[] fields=s.Split(' ');


if(fields.Length>1)
{


string ip=fields[1].Trim();


if(ht.ContainsKey(ip))


ht[ip]=((int)ht[ip])+1;


else


ht.Add(ip,(int)1);
}
}
}
sr.Close();
fs.Close();
}
catch(Exception ex)
{
Errors.Add("Can't

open "+LogFileName"+ex.Message.ToString());
}
*/


}
IIS.Close();
//IIS.Dispose();
}
}
root.Close();
//root.Dispose();
for(int i=0;i<ht.Count;i++)
{
string ip=(string)ht.GetKey(i);
if((int)ht[ip]>AttemptsBeforeKill)
Blocked.Add(ip);
}
}

public void Block()
{
bool bDidOne=false;
if(Blocked.Count==0)
return;
DirectoryEntry root = new

DirectoryEntry("IIS://"+ServerName+"/MSFTPSVC",ServerUserName,ServerPassword);
foreach (DirectoryEntry site in root.Children)
{
if (site.SchemaClassName == "IIsFtpServer")
{

DirectoryEntry IIS = new

DirectoryEntry("IIS://"+ServerName+"/MSFTPSVC/"+site.Name+"/root",ServerUserName,ServerPassword);
Type typ = IIS.Properties["IPSecurity"][0].GetType();
object IPSecurity = IIS.Properties["IPSecurity"][0];

Array origIPDenyList = (Array) typ.InvokeMember("IPDeny",
BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.GetProperty,
null, IPSecurity, null);

Array.Sort(origIPDenyList);

ArrayList ToAdd=new ArrayList();
for(int i=0;i<Blocked.Count;i++)
{
string block=Blocked[i]+", 255.255.255.255";
bool bAlreadyDone=false;
foreach(string s in origIPDenyList)
{
if(s==block)

bAlreadyDone=true;
}
if(bAlreadyDone==false)
ToAdd.Add(block);

StopSite(site.Name);
StartSite(site.Name);
bDidOne=true;
}
}
}
root.CommitChanges();
root.Close();
//root.Dispose();
if(bDidOne==true)
{
StopSite("");
StartSite("");
}
}
public void Remove()
{
bool bDidOne=false;
if(Removed.Count==0)
return;
DirectoryEntry root = new

DirectoryEntry("IIS://"+ServerName+"/MSFTPSVC",ServerUserName,ServerPassword);
foreach (DirectoryEntry site in root.Children)
{
if (site.SchemaClassName == "IIsFtpServer")
{

DirectoryEntry IIS = new

DirectoryEntry("IIS://"+ServerName+"/MSFTPSVC/"+site.Name+"/root",ServerUserName,ServerPassword);

Type typ = IIS.Properties["IPSecurity"][0].GetType();
object IPSecurity = IIS.Properties["IPSecurity"][0];

Array origIPDenyList = (Array) typ.InvokeMember("IPDeny",
BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.GetProperty,
null, IPSecurity, null);

Array.Sort(origIPDenyList);
ArrayList ToRemove=new ArrayList();
for(int i=0;i<Removed.Count;i++)
{
string block=Removed[i]+", 255.255.255.255";


bool bAlreadyDone=false;
foreach(string s in origIPDenyList)
{
if(s==block)

bAlreadyDone=true;
}
if(bAlreadyDone==true)
ToRemove.Add(block);

StopSite(site.Name);
StartSite(site.Name);
bDidOne=true;
}
}
}
root.CommitChanges();
root.Close();
//root.Dispose();
if(bDidOne==true)
{
StopSite("");
StartSite("");
}
}

public void StartSite(string siteName)
{
try
{
string site="IIS://"+ServerName+"/MSFTPSVC/"+siteName;
if(siteName.Length==0)
site="IIS://"+ServerName+"/MSFTPSVC";
Console.WriteLine("Starting "+site);
DirectoryEntry siteEntry = new
DirectoryEntry(site,ServerUserName,ServerPassword);
siteEntry.Invoke("Start", new object[] {});
siteEntry.CommitChanges();
siteEntry.Close();
Console.WriteLine("Started "+site);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message.ToString());
}
}
public void StopSite(string siteName)
{
try
{
string site="IIS://"+ServerName+"/MSFTPSVC/"+siteName;
if(siteName.Length==0)
site="IIS://"+ServerName+"/MSFTPSVC";
Console.WriteLine("Stopping "+site);
DirectoryEntry siteEntry = new
DirectoryEntry(site,ServerUserName,ServerPassword);
siteEntry.Invoke("Stop", new object[] {});
siteEntry.CommitChanges();
siteEntry.Close();
Console.WriteLine("Stopped "+site);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message.ToString());
}
}

}

}

0 new messages