渗透测试并发框架POC-T源码阅读

前言

POC-T是@cdxy编写的一款渗透测试并发框架,专注于解决批量PoC验证扫描、数据搜集的的需求。对于新漏洞爆发时进行范围性扫描感知很有帮助,类似于知道创宇的Pocsuite,可以作为企业内部安全团队洞悉资产漏洞数据的工具。GitHub项目地址位于:POC-T

POC-T的需求和框架设计

POC-T解决的核心问题是数据的并发处理,用户端能够定制数据的PoC处理逻辑和数据来源,最后获得数据的处理结果。下面是在GitHub中给出的框架图:

POC-T运行的逻辑可以简化为:

输入目标数据 -> 选择PoC -> 并发扫描验证 -> 反馈结果

运行时的样子:

POC-T项目文件夹:

POC-T
├── POC-T.py # 入口文件
├── README.md
├── data # 资源库,用户可自定义
├── doc # 文档及版权声明
├── lib # 项目代码
├── output # 默认结果输出位置
├── plugin # 工具库,用户可自定义
├── requirement.txt
├── script # 验证脚本库(PoC),用户可自定义
├── thirdparty # 第三方模块
└── toolkit.conf

丢进数据

POC-T主文件是./lib/cli.py,其他核心模块文件集中在./lib/core/当中。cli.py中程序先获得项目根目录路径,随后引入了./lib/core/common.py中的setPath()方法对POC-T的根目录文件、文件夹参数进行了配置:

这些参数保存在./lib/core/data.py中的paths对象中。paths对象是一个重新被定义过的新字典AttribDict()。后续还会用到的同样的其他三个对象cmdLineOptions、conf、th:

这个AttribDict()类位于./lib/core/datatype.py,和SQLmap中的AttribDict()一样,能直接通过赋值增加属性并且通过点运算符得到属性值:

>>> foo = AttribDict()
>>> foo.bar = 1
>>> foo.bar
1

经过setPath()后的paths对象:

setPath()以后程序开始解释并读取用户端命令行的参数并存储在cmdLineOptions字典对象中,随后会根据cmdLineOptions对象对框架进行初始化配置initOptions(cmdLineOptions):

initOptions()方法位于./lib/core/option.py中,用户在shell中输入参数后从cmdLineOptions字典传递进入initOptions()先后会进行如下操作:检查POC-T是否需要更新、检查用户是否需要列出所有可用的PoC脚本、检查并配置扫描引擎、注册互斥量、扫描线程数,检查配置脚本项参数,检查配置被扫描的目标信息,检查API配置信息、检查输出配置信息等:

在这过程当中一旦用户提供的参数出现错误或者互斥,程序会调用sys.exit()提醒并且退出。POC-T框架进行并发处理前的数据来源也是从这里丢进的,程序提供了7种方式丢进数据:

-iS # 给出一个单一的扫描目标,例如www.baidu.com
-iF # 从文件批量读取扫描目标
-iN # 读取一个IP地址块
-iA # 根据用户给出的范围生成一个IP范围数组
-aZ # 使用Zoomeye API抓取信息
-aG # 使用Google API抓取信息
-aS # 使用Shodan API抓取信息

执行完initOptions()方法以后,conf对象会存储最终的最终的框架配置数据,当中囊括了用户定义的使用哪种并发扫描方式、指定的PoC脚本文件、用来输出结果的文件路径等等:

从用户端在命令行中输入参数数据到按下回车再到屏幕输出内容,POC-T的第一部分工作就是上述内容。

处理数据

这部分是POC-T最核心的功能:并发处理数据。

从上面得到conf对象后,程序接下来会开始输出内容。先是用Colorama插件解决Windows平台ASCII彩色字符显示问题,随后打印出“POC-T”彩色的logo。接着使用了loadMoudle()方法从./script/文件夹中加载PoC脚本文件,loadMoudle()位于./lib/controller/loader.py中:

这里会对PoC脚本文件做检查,如果当中存在poc()方法的话就使用imp模块将其导入到框架当中。loadMoudle()加载完PoC脚本后继续使用loadPayloads()方法去对待扫描的目标数据做预处理,将他们放入一个queue对象中:

经过loadMoudle()和loadPayloads()两步,就即将到达最核心的并发处理数据模块了,在这期间POC-T使用logging模块以彩色日志的形式输出过程信息:

这时候程序开始执行run()方法进行并发扫描处理,run()方法位于./lib/controller/engine.py。run()执行时会先执行initEngine()配置并发扫描引擎,其中会沿用一些conf对象的数据最后添加进入新的对象th,th中也包含了前面的被扫描目标数据对象queue和线程锁对象threading.Lock()等其他内容:

initEngine()方法初始化扫描引擎后,run()会根据./lib/core/enums.py中的状态信息判断用户选择是哪种并发模式。POC-T为用户提供了两种并发模式:

-eT # 多线程模式,也是默认模式,能搭配-t参数自定义线程数
-eG # Gevent单线程异步协程模式,也能搭配-t参数自定义concurrent数量
-t # 指定线程数或者concurrent数

根据用户的不同选择,程序生成一定数量的并发线程或concurrent,然后将scan()方法和th中的queue对象塞进去。来看看scan()方法:

可以看出加载好的PoC脚本文件在扫描queue对象中的目标时会根据扫描结果返回一个布尔值,程序再根据这个布尔值使用resultHandler()方法对结果进行判断处理:

如果返回的status为True就证明漏洞验证成功,然后将该目标打印出来,如果是False就什么也不做。如果PoC返回值不是一个布尔值而是和./lib/core/enums.py文件中POC_RESULT_STATUS.RETRAY=2相等,那就将这个目标重新放进th中的queue对象进行重新扫描。根据上面的三种情况,可以说明PoC文件中的poc()方法必须返回True(1)、False(0)或者2这三个值中的一个,这也是其他开发者自定义PoC文件要注意的地方。

输出结果

根据前面的并发处理,屏幕上会最终打印出经过PoC验证存在漏洞的目标。POC-T提供的输出方式有三种:

-o # 默认情况下会输出到一个txt文件至./output/文件夹
-oF # 不输出文件,只在屏幕上打印出结果信息
-oS # 不在屏幕上显示

其他

对于Zoomeye、Google、Shodan三种数据来源采集方式,用户可以在POC-T在./tookit.conf文件中填写配置信息,包括账户密码、API key等等,这样在采集时可更加方便。POC-T同时在./plugin/文件夹中给了一些编写PoC脚本的辅助工具,可以在简化脚本编写任务的同时为脚本增加更多功能。

* 转载请注明作者及来源