Skip to content

PoC Base Class

In order to simplify the writing of PoC scripts, Pocsuite3 implements the PoC base class: POCBase, where many common code can be placed. When writing PoC, we only need to inherit the base class, and write the core code of the Vulnerability. The commonly used properties and methods are as follows:


Common properties:

self.url # target url
self.scheme # the protocol of the target url
self.rhost # hostname of the target url
self.rport # port of the target url
self.host_ip # WAN ip address

Common method:

self._check()

# Get the value of the custom command line parameter
self.get_option('key')

# The method to return the result, the parameter is a dictionary,
# it is recommended to use this method to return the result
self.parse_output({})

The self._check() method code is as follows, it will perform port opening check, http/https protocol automatic correction, home page keyword check, honeypot check. It can avoid sending Payload to the honeypot, reducing false positives.

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.