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