IAST 扫描器
背景
IAST(交互式扫描)技术是一种实时动态交互的漏洞检测技术,通过在服务端部署agent程序,收集、监控Web应用程序运行时函数执行、数据传输,并与扫描器端进行实时交互,高效、准确的识别安全缺陷及漏洞。目前OpenRASP项目已实现相当于IAST agent端的OpenRASP agent,在此基础上引入一个扫描端,即可实现一个完整的IAST扫描工具。
系统架构
灰盒扫描工具采用Python3实现,数据库采用MySQL,通讯采用HTTP+JSON的方式。整体架构图如下:
工具分为扫描器端和agent端,Agent端用于收集web应用的运行信息,扫描器端用于处理插件产生的请求信息,并完成整个IAST扫描逻辑
agent端{#iast-structure-agent}
由一个单独的OpenRASP插件构成,用于提取http请求中hook点产生的信息,并通过HTTP协议以JSON形式发送至扫描器端。
扫描器端{#iast-structure-scanner}
该部分是一个独立运行的扫描工具,借助从OpenRASP插件部分获取的信息完成扫描任务。
功能设计和模块划分
扫描模块包括三个模块:预处理模块(Preprocessor)、扫描模块(Scanner)、监控模块(Monitor)
预处理模块即图中HTTPServer部分,用于接收agent插件的http请求,处理、存储、分发http请求信息
扫描模块用于运行扫描插件,执行漏洞扫描逻辑
监控模块用于定期获取其他模块的运行时信息,调整参数,提供控制台的HTTP服务等
运行流程示例
一个典型扫描流程,以SQL注入的扫描过程为例:
1、运行扫描器端,初始化所有模块
2、测试人员发送了一条HTTP请求到web server,产生请求及其对应的HOOK信息被OpenRASP插件获取,发送至http_server
3、http_server发现请求不是扫描器发出的,对其进行去重后写入数据库
4、扫描模块从数据库获取一条HOOK信息,下发到所有扫描插件
5、sql注入扫描插件分析HOOK信息发现用户输入参数拼接进了sql查询,运行对应扫描逻辑
6、扫描插件生成扫描请求,把原始请求进入query的输入参数替换为单引号
7、扫描插件在请求头添加用于识别扫描请求的scan_request_id,发送扫描请求给web server
10、web server处理请求并返回结果,扫描插件获得http response,同时OpenRASP插件获取到请求hook信息,发送至http_server
11、http_server发现请求是扫描器发出的,将其写入rasp_result_queue队列
12、扫描模块读取rasp_result_queue队列,将rasp_result传给对应的扫描插件
13、扫描插件检查收到的hook信息,发现query逻辑被改变,认为存在SQL注入,将漏洞信息写入数据库
插件开发
获取代码
在安装了python3.7的环境中可以直接运行IAST
运行参数与pip安装相同
扫描插件
所有扫描插件均位于plugin/scanner目录下,所有扩展名为.py的文件都会在启动扫描器时被加载,ScanPluginBase.py为插件基础类,提供编写插件所需的一系列接口,具体参考接口文档
这里以编写一个简单的sql注入fuzz插件为例,介绍插件的编写方法:
首先在plugin/scanner目录下新建一个new_plugin.py文件,粘贴以下内容:
在plugin_info中填写插件基本信息:
实现mutant函数,生成扫描向量
每个被发送并获取结果的请求序列都会回调check函数,在check函数中来实现漏洞判定:
如果自行实现检测函数,需要在检测到漏洞时,使用set_vuln_hook标记有漏洞的hook点信息:
需要注意的是,扫描模块是基于asyncio实现的异步扫描,每个插件会以一个coroutine的形式运行,因此应避免在插件中使用同步的io操作以免影响扫描性能。在进行异常捕获时,如果要捕获全部异常,应单独处理asyncio.CancelledError异常,以免扫描任务无法被终止:
去重插件
所有扫描插件均位于plugin/deduplicate目录下,IAST启动时会加载core/config.py中指定的去重插件,去重插件是为了避免同一个请求被反复扫描,应当根据扫描目标的url结构、参数等特性来编写,DedupPluginBase.py为插件基础类,提供编写插件所需的一系列接口,具体参考接口文档
编写一个去重插件
在plugin/deduplicate目录下新建一个new_plugin.py文件,写入以下内容:
去重插件只需要实现一个get_hash方法即可,该方法传入当前待去重请求对应的raspResult类的实例,返回一个hash字符串,返回hash相同的请求会被视为重复请求,这里直接调用了默认的get_hash_default方法,其具体实现如下:
注意:如果插件运行中抛出异常,会自动调用get_hash_default函数生成默认的hash
Last updated