kiwi
项目是一个源代码安全审计工具。适用于黑客、安全研究人员、安全测试人员,支持多种语言的代码审计工作。
代码安全审计工作一般需要2个辅助工具:
- 代码审计(搜索)工具,能够从代码中找到可能存在安全问题的位置,并且生成可视化的报告。
- 一个方便阅读代码的工具,能够索引代码实现跳转。例如opengrok、SourceInsight、vim+ctags+cscope
kiwi
是一个基于文本的安全源码审计工具,它不会对源代码进行语法解析,而是使用简单的正则表达式方式搜索代码,同时提供了问题确认机制用于减少误报。
kiwi
工具可以和opengrok
工具很好得结合起来,使得扫描报告中的问题可以直接链接到相应的代码。如果经常进行源代码审计工作,强烈建议使用 kiwi + opengrok
的解决方案,
kiwi
是一个规则和框架完全分离的系统,这样用户可以方便得自定义规则,而不用涉及任何框架层面的修改。
在该项目中有3个目录,分别对应3个子项目:
- kiwi。该目录为
kiwi
工具的主体框架,需要安装到系统中。 - kiwi_data。该目录为
kiwi
的缺省规则目录,放到系统中任意位置即可,用户可在此目录修改、编写自己的搜索规则 - kiwilime。该目录提供了一个
sublime text3
插件,用于和kiwi
配合使用,可以高亮显示扫描结果,快捷键跳转到代码sublime text3
打开的代码目录相应代码行数等。
注: opengrok
工具是一个B/S架构的大型源码阅读工具,请参考 这里
目前主流的源码审计工具多采用 语法解析+插件检测 的方式实现,即工具会对目标代码做语法分析,生成语法树,然后遍历语法树的每个节点,对每个节点调用所有插件(插件实现检测语法节点是否存在安全漏洞)。
语法解析+插件检测 的这种方式更加精确,例如检测eval函数,正则表达式的方式可能会匹配注释,而语法解析的方式则不会,并且语法解析的方式可以回溯语法节点从而做到“数据流分析”。
但是 语法解析+插件检测 的实现方式太过复杂,需要为支持的每一种编程语言编写语法解析模块,代价很大,因此商业性的工具非常昂贵;而开源的工具支持的编程语言种类非常少,很难做到跨多种语言审计。
语法解析+插件检测 方式相对于高昂代价来说,带来的收益是有限的。实际上多数商业源码审计工具面向的都是非安全人员,而很多黑客仍在使用 grep 来做代码审计。
作者认为目前的这些工具不是很适合专业安全人员、黑客,对于源码审计来说,最具有技术含量,最具有创造性的是检测规则,专业安全人员在做源码审计的时候检测规则并非一成不变,在一次源码审计的过程中会不断发现一些新的 危险代码 ,需要不断调整检测规则来适应新的变化,因此自定义检测规则是对于专业安全人员、黑客来说才是最重要的。
kiwi
就是这样一个工具,使用它可以很方便得随时更新检测规则随时再次进行扫描,kiwi
虽然在实现原理上落后主流技术,但更加适合专业安全人员。
这里推荐两种解决方案来实现安全源码审计:
- kiwi + opengrok。该方案适合大规模的源码审计,可解决千万行级别、团队合作的源码审计需求(作者所在公司即采用该方案实现)。
- kiwi + sublime text3 + kiwilime。该方案适合个人桌面级使用,解决万行及以下、单人代码审计需求。
使用一下命令安装kiwi:
git clone https://github.com/alpha1e0/kiwi.git
cd kiwi/kiwi
python setup.py install
kiwi_data
无需要安装,放到磁盘中某个目录即可,在扫描的时候通过以下两种方式定位到kiwi_data
目录:
- 通过 KIWI_DATA_PATH 环境变量指定
kiwi_data
目录。(推荐的方式) - 在
kiwi
扫描命令行中通过 -f/--feature_dir 参数指定kiwi_data
目录
Step 1.
获取 kiwilime
git clone https://github.com/alpha1e0/kiwi.git
Step 2.
打开 Sublime Text 3 package-directory
Preferences --> Browse packages
Step 3.
将 kiwilime 目录整个 copy 到 package-directory
kiwilime 的运行依赖于 the_platinum_searcher 工具,从 https://github.com/monochromegane/the_platinum_searcher/releases 下载编译好的工具,并将其加入到环境变量。
kiwi
在安装后会生成两个 console-script:
- kiwi. kiwi的扫描入口命令
- kiwi-report. 用于查看db类型的扫描报告
可使用 kiwi -h 查看kiwi
的帮助信息
[Kiwi 代码安全扫描]
--------------------------------------------------------------------------------
usage: kiwi [-h] -t TARGET [-f FEATURE_DIR] [-i FEATURE_IDS [FEATURE_IDS ...]]
[-e EXTENSIONS [EXTENSIONS ...]] [--igexts IGEXTS [IGEXTS ...]]
[--excludes EXCLUDES [EXCLUDES ...]] [-c SCTX] [--ectx ECTX]
[-o OUTPUTS [OUTPUTS ...]] [-v]
Kiwi. 代码安全审计工具
optional arguments:
-h, --help show this help message and exit
-t TARGET, --target TARGET
指定待扫描的目录
-f FEATURE_DIR, --feature_dir FEATURE_DIR
指定漏洞特征定义目录
-i FEATURE_IDS [FEATURE_IDS ...], --feature_ids FEATURE_IDS [FEATURE_IDS ...]
指定加载哪些漏洞特征
-e EXTENSIONS [EXTENSIONS ...], --extensions EXTENSIONS [EXTENSIONS ...]
指定扫描哪些类型文件,例如-e php js则扫描.php .js文件
--igexts IGEXTS [IGEXTS ...]
指定忽略扫描哪些类型文件,例如--igexts php js则忽略扫描.php .js文件
--excludes EXCLUDES [EXCLUDES ...]
忽略扫描文件路径包含关键字的文件
-c SCTX, --sctx SCTX 指定扫描结果显示的上下文行数
--ectx ECTX 指定用于评估漏洞所需的上下文信息的文件行数
-o OUTPUTS [OUTPUTS ...], --outputs OUTPUTS [OUTPUTS ...]
指定输出报告文件,支持.txt/.html/.json/.db
-v, --verbose 详细模式,输出扫描过程信息
使用 kiwi-report -h 查看kiwi-report
的帮助信息
[Kiwi report browser.]
--------------------------------------------------------------------------------
usage: kiwi-report [-h] [-p PORT] [--ip IP] [-d REPORT_PATH]
Kiwi. Audit source code for security issuses
optional arguments:
-h, --help show this help message and exit
-p PORT, --port PORT 指定监听端口,默认为5000
--ip IP 指定监听IP
-d REPORT_PATH, --report_path REPORT_PATH
指定扫描报告目录
在一次扫描中,需要指定:
- 被扫描对象,即代码目录
- 使用的扫描规则目录。如果不希望每次都指定规则目录,则可以通过设置环境变量 KIWI_DATA_PATH 来永久指定。
扫描示例
kiwi -t /tmp/vulcodes2 -f /home/xxx/kiwi_data -o result.html
kiwi -t /tmp/vulcodes2 -o result.db
可通过查看 kiwi_data 目录中的内容来获取规则编写示例。
kiwi
的规则定义均使用yaml
语法编写,在规则编写中涉及以下几个简单概念:
1、规则文件
规则文件是指以 .feature 未后缀的文件,记录具体的代码搜索规则定义
2、评估函数 评估函数为一个原型固定的python函数,用于后期搜索结果确认。例如搜索到subprocess.check_call(cmd, shell=True),此时可在评估函数中确认参数shell是否为True,从而减小误报
3、map文件
kiwi
工具是跨语言的,因此需要区分不同编程语言的文件;而map文件就是用来解决编程语言区分的,例如.py文件被认为是python脚本。
4、senfiles规则文件
这是一个特殊的规则文件,它的规则仅用于匹配文件名。该规则文件用于帮助安全人员找到敏感文件,例如 xxx_upload.php
kiwi_data 的目录结构如下:
kiwi_data
.. features # 目录,存放规则文件
.. .. evals # 目录,存放评估函数
.. .. .. py_evaluate_funcs.py # 文件,评估函数脚本
.. .. python.feature # 文件,规则文件
.. .. java.feature
.. filemap # 文件,map文件
.. senfiles # 文件,senfiles文件
每当新增加一门编程语言的规则时,需要编辑map文件,例如:
extensions:
- pattern: "\\.py$"
scope: python
- pattern: "\\.php\\d+$"
scope: php
- pattern: "\\.java$"
scope: java
- pattern: "\\.sh$"
scope: linux-shell
metainfos:
- pattern: "#!/usr/bin\\w+python"
scope: python
- pattern: "<?php"
scope: php
- pattern: "#!/bin/\\w+sh"
scope: linux-shell
kiwi
通过两种方式识别文件类型,后缀名方式 和 metainfo方式。
其中 metainfo 是针对解释型语言的,此类语言往往会在文件的第一行声明使用什么解释器来解析执行,例如python代码文件第一行可能为
#!/usr/bin/env python
使用任一方式定义map规则即可
senfiles规则非常简单,仅仅是一系列的正则表达式,例如:
patterns:
- upload
- download
本节通过示例来说明规则文件的编写,以下规则为某个规则定义文件的部分内容:
version: 1.0 # 指定规则集的版本
scopes: # 指定该规则集适用于哪种场景(编程语言)
- python
features: # features是一个列表是规则定义的主题
- ID: PY_CMD_INJ_001 # 定义一个容易区分的ID,必选
name: "Use 'os/commands' module to execute command in shells" # 添加一句描述性的文本,必选
references: [] # 漏洞说明相关链接,可选
severity: High # 漏洞验证等级,可选
confidence: High # 漏洞确信度,可选
patterns: # 正则特征列表
- os.system
- popen2.Popen4
- commands.getoutput
- commands.getstatusoutput
- ID: PY_CMD_INJ_002
name: "Use 'subprocess' module with shell=True to execute command in shells"
severity: High
confidence: Medium
references: []
patterns:
- subprocess.call
- subprocess.Popen
- subprocess.check_call
- subprocess.check_output
- utils.execute
- utils.execute_with_timeout
evaluate: py_cmd_inject_0002 # 指定使用的评估函数
简单的规则定义是非常容易的,只需要ID
name
patterns
三个属性即可。
对于复杂的规则,需要定义评估函数
评估函数示例:
from kiwi.core.featuremgr import evaluate
@evaluate
def py_cmd_inject_0002(feature, matchctx):
if matchctx.contains("shell=True"):
return (feature['severity'], feature['confidence'])
else:
return None
评估函数的原型是固定的,使用 @evaluate
修饰器来限定,参数有两个:
- feature. feature即使用的规则定义,和规则文件内的内容完全对应
- matchctx. matchctx是一个
MatchContext
上下文对象,包含匹配上下文的一些信息
MatchContext
提供以下方法、属性供使用:
- 属性: filename. 匹配的代码文件文件名
- 属性: lineno. 匹配的代码文件匹配行行数
- 属性: match_line. 匹配行的代码字符串
- 属性: str_ctx. 匹配的上下文代码字符串(上下文行数和命令行参数--ectx对应)
- 方法: contains(keyword). 返回匹配行是否包含keyword关键字
- 方法: ctx_contains(keyword). 返回匹配上下文是否包含keyword关键字
目前kiwi
支持4中类型报告:
- html. html类型的报告。
- db. db类型的报告,db类型的报告无法直接查阅,需要使用
kiwi-report
来查看 - txt. text类型的报告。
- json. json类型的报告。
其中,db和html类型的报告是最推荐的,txt类型的报告可以使用kiwilime
这个sublime text查看查看,可以高亮显示。
html报告如下图所示
使用kiwi-report
来查看db类型报告时,可以对漏洞进行误报标记。如下图所示
kiwi
报告可以和opengrok
联动功能非常实用,即当点击kiwi
扫描报告(仅限html和db类型报告)中的 “目标文件” 链接时,可以直接跳转到相应代码位置(opengrok
对应代码的url)。
这里需要配置一个环境变量 KIWI_OPENGROK_BASE 该环境变量配置的是opengrok
工具的代码目录,kiwi
会使用这个目录作为基准目录来定位代码的位置