CSRF(跨站请求伪造)学习与理解
前言
发现团队里的同学进步都好快啊。我快要跟不上了,一直在做题,只能做动一些杂项和密码学的简单题,思路多少掌握一点,但是我学Web的,不会做Web题,现在还做不动简单题。有点发愁。做题暂时做不进去了,学点新东西缓解下疲劳。这次主要学习一下CSRF。并写下自己的学习过程和学习中对此知识的见解。
CSRF
首先了解一下什么是CSRF。
CSRF也叫跨站请求伪造。
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
攻击方式和危害
跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。
由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。
这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
从这里可以看出:
攻击者并不能通过CSRF攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户的浏览器,让其以用户的名义运行操作。
以上是维基百科内容:我个人认为讲述比较清晰,也易懂,于是就摘抄过来了。然后我就再说说我自己学习时的理解。
危害大概有,修改用户密码进行恶意登录、恶意转账、以受害者名义发送邮件,发消息,盗取受害者的账号,甚至购买商品,虚拟货币转账,修改受害者的网络配置。就是正常用户能使用这个账号进行什么操作,攻击者都能做。
个人理解
1.网站信任浏览器:受害者浏览过一个网页后,会在浏览器上保存有记录,而攻击者就是通过一些方法用受害者的浏览器,去访问受害者之前访问过并且保留有信息的网页。就拿我的电脑为例:我的电脑是登录并认证了CSDN的,所以我每次登录CSDN都不需要再次输入账户密码或者扫码什么的方式进入CSDN。到那时换个浏览器(比如用火狐),他还是会让我登录。这个就会有个问题,这样的话,CSDN是怎么辨别就是我登录的呢,这里就是上面提到的网站对用户浏览器的信任,所以在原来认证过的浏览器登录,网站会认为就是这个用户在使用。就直接同意使用该浏览器的人进入这个账户了。像我的CSDN,如果让我的朋友使用的话,他就可以直接进入我的CSDN账号,并做任何我的账户所能做的事。(这是个人理解,如有不对的地方,还请各位大佬指点。)
2.攻击:CSRF攻击分两种,一种是get型一种是post型。
首先get类型简单说明,用户修改密码的界面的URL被攻击者通过某种方式得到了,然后攻击者修改了这个URL(修改成他自己知道的密码)再次发给用户,(其过程可以将URL做一下伪装)然后用户点击,修改密码成功,攻击者就可以使用浏览器登录用户的账号进行操作(此时的密码已经是攻击者知道的那个密码了)。
自己理解过漏洞后,在靶场练习一下。
3.CSRF漏洞代码分析
<?php
//会话验证
$user = $_GET["user"];
$money = $_GET["money"];
//转账操作
?>
由此可以看出,CSRF成功利用的条件
一、用户已经登录系统
二、用户访问对应URL
靶场实验
靶场环境是DVWA
本地靶场实验原理
登录靶场
上面这个是在安全等级为impossible的程度下,可以看出修改密码的话要输入原密码的,这也是目前大多数网站的做法。奥这我就懂了,原来修改密码前输入原密码是为了防止CSRF攻击。
现在调到low级别的
可以看出,这个安全等级不需要输入原密码。修改一下密码,此刻充当受害者。
这里显示修改成功,URL是这样的,复制一下,
http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#
我做一下修改(此时我是攻击者),将这个URL改的密码修改成我要的密码。
http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=zxcv0221&password_conf=zxcv0221&Change=Change#
修改成功,当然我这是实验,受害人(我)比较傻,直接点了。
伪装的话,可以通过短网址生成,和简单的html来进行伪装。
我在靶机上构成的本地网址是没办法生成短网址的,就是127.0.0.1
这样的都没办法生成。所以就不放图了。
然后就可以通过攻击者修改的密码,来进行登录了。
get类型
get型CSRF利用,就是构造get型URL,提交username和password参数,新建一个对应的用户名和密码。
比如:
http://127.0.0.1/phpdemo/1.php?username=j4y&password=j4y
这是一个添加用户的URL,将它发送给管理员,管理员点击后就会添加这么一个用户。
当然,这样太明显,可以放在img标签的src属性里。这是隐蔽利用。
本地测试
下面一个测试CSRF的代码,可以自己试一下。(在网上找的本地靶场代码)
代码前面要连接数据库,本地mysql的用户名和密码都要输进去。
源代码:
<?php
header("Content-Type:text/html;charset=utf-8");
$username = $_GET['username'];
$password = $_GET['password'];
$conn = mysql_connect("localhost","root","root");
$db=mysql_select_db("csrf_test");
$query = mysql_query("SET NAMES 'gbk'");
$sql="INSERT INTO `test` (`username` ,`password`)VALUES ('$username' ,'$password')";
$row=mysql_query($sql);//执行sql插入语句
$sql="SELECT * FROM test";
if($row=mysql_query($sql)){
while ($row = mysql_fetch_array($row)){
echo "user:{$rows['username']}-----pass:{$rows['password']}"."<br/>";
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>CSRF测试平台</title>
</head>
<body>
<b><h2>GET型 CSRF</h2></b>
<p>需要GET提交 username password</p>
</body>
</html>
需要自己修改一下,改成和自己的配置能连接到一起的,直接复制代码可能会出错。
发现跑出来是这个样子。
于是上网查解决办法,发现是php连接mysql
报错,原因是权限问题,root帐户默认不开放远程访问权限,所以需要修改一下相关权限。
解决办法是
打开MySQL目录下的my.ini文件,在文件的最后添加一行“skip-grant-tables”,保存并关闭文件。
如果正常了,那很幸运,如果还是有报错,(截图忘存了)
Notice: Undefined index: username in D:\phpStudy\PHPTutorial\WWW\phpdemo\1.php on line 3
这样的报错,就在代码最上方,加一行
error_reporting(E_ALL ^ E_NOTICE);
解决后,终于正常了。
然后将代码隐藏在一个img标签中,这么写
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>中奖了</title>
</head>
<body>
<h1>中奖了</h1>
<img src="http://127.0.0.1/phpdemo/1.php?username=admin_test&password=admin_test">
</body>
</html>
访问,然后出像这样的页面。
这个就已经成功了。(下面这张图是教程上的图,因为我是在虚拟机里做的,做完实验之后,就直接恢复快照了,也没有截图,这个是复现的。也懒得再配置了,显示结果,就直接用教程上的图了。)
在线靶场实验恶意转账
已经转了10元钱给admin这个账户。
看下payload,
http://www.nanhack.com/payload/xss/csrf1.php?name=admin&money=10
修改payload
http://www.nanhack.com/payload/xss/csrf1.php?name=feiwu&money=100
嗯,好了,我决定转给feiwu100块钱,这个名字我喜欢。
然后我就发现我的账户上钱少了,
这个时候,feiwu的账户上就会多了100,我转给他的。
直接构造CSRF链接,成功率贼低。可以用HTML的一些方法进行伪装一下。比如可以将连接隐藏在图片里。
post类型
post型CSRF利用
这种类型的CSRF危害没有GET型的大,利用起来通常使用的是一个自动提交的表单。
访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST操作。
可以利用一个简单的CSRFform表单提交设置一下。
源代码:
<?php
header('Content-type:text/html;charset=utf-8');
if (isset($_POST['sub'])) {
$username = $_POST['username'];
$password = $_POST['password'];
$conn = mysql_connect("localhost","root","root");
$db = mysql_select_db("csrf_test");
$query = mysql_query("SET NAMES 'gbk'");
$sql = "INSERT INTO `test` (`username` , `password`)VALUES ('$username' , '$password')";
$row=mysql_query($sql);
$sql="SELECT * FROM test";
if ($row=mysql_query($sql)){
while($row = mysql_fetch_array($row)){
echo "user:{$rows['username']}-----pass:{$rows['password']}"."<br/>";
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>CSRF利用场所</title>
</head>
<body>
<form action="" method="post">
<b>user:<input type="text" name="username" /></b>
<b>pass:<input type="password" name="password" /></b>
<input type="submit" value="OK" name="sub" />
</form>
</body>
</html>
报错什么的基本在get类型的代码上解决完了,这个直接
点击“我要中奖了”,就成功了。