Skip to content

PoC 插件基类

为了简化 PoC 插件的编写,Pocsuite3 实现了 PoC 基类:POCBase,很多共用的代码片段都可以放到此基类中。我们编写 PoC 时,只需要继承该基类就可,比较常用的属性和方法如下:


常用属性:

self.url  # 目标 url
self.scheme  # 目标 url 的协议
self.rhost  # 目标 url 的主机名
self.rport  # 目标 url 的端口
self.host_ip  # 本机的 wan 口 ip

常用方法:

self._check()
self.get_option('key')  # 获取自定义命令行参数的值
self.parse_output({})  # 返回结果的方法,参数是一个字典,建议统一使用该方法返回结果

self._check() 方法代码如下,会进行端口开放检查、http/https 协议自动纠正,首页关键词 check,关键词蜜罐检查等功能。可以一定程度避免将 Payload 发送到蜜罐,减少误报。

def _check(self, dork='', allow_redirects=False, return_obj=False, is_http=True, honeypot_check=True):
    u = urlparse(self.url)
    # the port closed
    if u.port and not check_port(u.hostname, u.port):
        logger.debug(f'{mosaic(self.url)}, the port is closed.')
        return False

    if is_http is False or self.current_protocol != POC_CATEGORY.PROTOCOL.HTTP:
        return True

    res = None
    https_res = None
    # this only covers most cases
    redirect_https_keyword = [
        # https://www.zoomeye.org/searchResult?q=%22request%20was%20sent%20to%20HTTPS%20port%22
        'request was sent to https port',
        # https://www.zoomeye.org/searchResult?q=%22running%20in%20SSL%20mode.%20Try%22
        'running in ssl mode. try'
    ]
    redirect_https_keyword_found = False
    origin_url = self.url
    netloc = self.url.split('://', 1)[-1]
    urls = OrderedSet()
    urls.add(self.url)
    urls.add(f'http://{netloc}')
    urls.add(f'https://{netloc}')
    for url in urls:
        try:
            time.sleep(0.5)
            res = requests.get(url, allow_redirects=allow_redirects)
            if url.startswith('https'):
                https_res = res

            # redirect to https keyword found, continue to next loop
            for k in redirect_https_keyword:
                if k.lower() in res.text.lower():
                    redirect_https_keyword_found = True
                    res = https_res
                    break
            if redirect_https_keyword_found:
                continue

            """
            https://github.com/knownsec/pocsuite3/issues/330
            status_code:
                - 30x
                - 50x
            """
            if not str(res.status_code).startswith('20'):
                continue

            break
        except requests.RequestException:
            pass

    if not isinstance(res, requests.Response):
        return False

    self.url = res.request.url.rstrip('/')

    if self.url.split('://')[0] != self.scheme:
        logger.warn(f'auto correct url: {mosaic(origin_url)} -> {mosaic(self.url)}')
        self.scheme = 'https' if self.url.startswith('https') else 'http'
        port = urlparse(self.url).port
        self.rport = port if port else 443 if self.scheme.startswith('https') else 80
        self.netloc = f'{self.rhost}:{self.rport}'

    if return_obj:
        return res

    content = str(res.headers).lower() + res.text.lower()
    dork = dork.lower()

    if dork not in content:
        return False

    if not honeypot_check:
        return True

    is_honeypot = False

    # detect honeypot
    # https://www.zoomeye.org/searchResult?q=%22GoAhead-Webs%22%20%2B%22Apache-Coyote%22
    keyword = [
        'goahead-webs',
        'apache-coyote',
        'upnp/',
        'openresty',
        'tomcat'
    ]

    sin = 0
    for k in keyword:
        if k in content:
            sin += 1

    if sin >= 3:
        logger.debug(f'honeypot: sin({sin}) >= 3')
        is_honeypot = True

    # maybe some false positives
    elif len(re.findall('<title>(.*)</title>', content)) > 5:
        logger.debug('honeypot: too many title')
        is_honeypot = True

    elif len(re.findall('basic realm=', content)) > 5:
        logger.debug('honeypot: too many www-auth')
        is_honeypot = True

    elif len(re.findall('server: ', content)) > 5:
        logger.debug('honeypot: too many server')
        is_honeypot = True

    if is_honeypot:
        logger.warn(f'{mosaic(self.url)} is a honeypot.')

    return not is_honeypot

Released under the GPLv2 License.