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):
if conf.get('no_check', False):
return 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
# 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}')
# The user has not provided a port in URL, dynamically switching to HTTPS's default port 443
pr = urlparse(self.url)
is_ipv6 = pr.netloc.startswith('[')
if ':' not in self.target.split('://')[-1] and pr.port == 80:
pr = pr._replace(scheme='https')
pr = pr._replace(netloc=f'[{pr.hostname}]:443' if is_ipv6 else f'{pr.hostname}:443')
urls.add(pr.geturl())
else:
urls.add(f'https://{netloc}')
for url in urls:
try:
time.sleep(0.1)
res = requests.get(url, allow_redirects=allow_redirects)
"""
https://github.com/knownsec/pocsuite3/issues/330
https://github.com/knownsec/pocsuite3/issues/356
status_code:
- 20x
- 30x
- 40x
- 50x
"""
# if HTTPS handshake is successful, return directly
if url.startswith('https://'):
break
# if we send an HTTP request to an HTTPS service, but the server may return 20x
for k in redirect_https_keyword:
if k.lower() in res.text.lower():
redirect_https_keyword_found = True
break
if redirect_https_keyword_found:
continue
# if we send an HTTP request to an HTTPS service, the server may return 30x, 40x, or 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 res.history:
self.url = res.history[0].request.url.rstrip('/')
if self.url.split('://')[0] != self.scheme:
self.url = self.build_url(self.url)
logger.warn(f'auto correct url: {mosaic(origin_url)} -> {mosaic(self.url)}')
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