毕设扫描器参数Fuzz第一篇:数据的定义读取和装配(爬虫数据和Payload数据)
Posted 区块链市场观察家
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了毕设扫描器参数Fuzz第一篇:数据的定义读取和装配(爬虫数据和Payload数据)相关的知识,希望对你有一定的参考价值。
文章目录
字典文件的数据结构
在项目根目录创建 fuzzDicts
目录,创建 fuzzDicts/params.json
,用于测试参数。(使用 json 是因为常见/易读/复用性强)
参数测试Payload格式:不同的漏洞类型、漏洞类型下不同的软件系统、对应Payload。
一级数据是漏洞类型,如 SQLI、XSS等,数据结构是字典格式。(添加字段 description 描述payload,不需要专门使用一个字段进行分类。该部分是在读取 Json 文件时进行的修改)
二级数据是漏洞类型的下一级数据,可能需要根据不同软件类型选择 Payload,所以也要使用字典格式。二级数据如果不分软件类型,则定义键名为 (还要单独输入一个字段,而大多数情况都不知道软件类型,还是要枚举测试所有Payload,所以该字段不是很有必要)universal
。
二级数据是Payload,根据不同Payload需要选择不同的响应判断,如时间延迟注入的响应时间、响应内容中的关键字,所以使用字典格式。(Payload直接作为污染参数数据的键名即可)(实际读取 Json 文件时做出的优化)
一级数据是一个键 data
,其键值是数据类型,用于存储 payload 的字典信息。
二级数据是最底层的数据,注入参数可能有很多、响应时间是整数、关键词是字符串等,所以根据实际需要分别使用 List、Int、String等数据类型。
未考虑的场景问题
- 根据比较不同Payload下返回的响应信息来判断漏洞是否存在。
排版如下。
"data":[
"description": "SQLI-001: mysql-数字型延迟注入",
"payload": "/**/and/**/sleep(5)",
"time": 5,
"keyword": ""
,
"description": "SQLI-002: MySQL-字符型延迟注入",
"payload": "'/**/and/**/sleep(5)--+",
"time": 5,
"keyword": ""
,
"description": "SQLI-003: MySQL-字符型延迟注入",
"payload": "\\"/**/and/**/sleep(5)--+",
"time": 5,
"keyword": ""
,
"description": "SQLI-004: MySQL-报错注入",
"payload": "'\\"+,",
"keyword": "You have an error in your SQL syntax"
,
"description": "XSS-001: 跨站脚本攻击漏洞"
"payload": "<script>alert(/xss/)</script>",
"keyword": "xxx"
]
读取嵌套的 Json 数据
读取值为基本数据类型的Json文件
这里的单层是指,Json数据的值是基本数据类型,不是列表、字典等数据结构。
创建 test.json 文件,文件内容如下。
"name": "pumpkin",
"date": "2022-04-05"
查找网上文章 Go 对象序列化 等,编写读取Json文件代码。
-
读取配置文件的字节数据:bytesJSON, err := ioutil.ReadFile(“example.json”)
-
go 内置对 json 数据的处理包是
encoding/json
-
把字节数据解析成Json编码数据:json.Unmarshal
-
接收Json数据:需要事先定义一个结构体
成功运行解析的代码和打印结果如下。
/* 打印结果
pumpkin
2022-04-05
*/
//定义配置文件解析后的结构
type Test struct
Name string `json:"name"`
Date string `json:"date"`
func main()
var test Test
bytes, err := ioutil.ReadFile("./test.json")
if err != nil
fmt.Println("读取json文件失败", err)
return
err = json.Unmarshal(bytes, &test)
if err != nil
fmt.Println("解析数据失败", err)
return
fmt.Println(test.Name)
fmt.Println(test.Date)
读取值为数组的Json数据
test.json 配置文件的内容如下。
此时使用上一小节读取基本数据类型的代码,json.Unmarshal(bytes, &test)
会报错并退出程序,无法把数组结构解析成 Go 结构体的 string 类型:
解析数据失败 json: cannot unmarshal array into Go struct field Test.name of type string
"name": ["pumpkin", "watermelon"],
"date": "2022-04-05"
修改结构体的参数类型为字符串数组:[]string
。运行成功且打印结果如下。
/* 打印结果
[pumpkin watermelon]
2022-04-05
*/
//定义配置文件解析后的结构
type Test struct
Name []string `json:"name"`
Date string `json:"date"`
读取值为字典的Json文件
参考文章 golang 数据三 (字典),golang基本数据结构Map也叫字典,字典的声明格式是:map[KeyType]ValueType
。
此时 test.json 配置文件的内容如下。
"name": ["pumpkin", "watermelon"],
"date": "2022-04-05"
"sex":
"man":1,
"woman":0
定义接收 Json 数据的结构体如下,成功读取并打印 Json 数据内容。
//定义配置文件解析后的结构
type Test struct
Name []string `json:"name"`
Date string `json:"date"`
Sex map[string]int `json:"sex"`
// main()函数中的操作代码
func main()
var test Test
bytes, err := ioutil.ReadFile("./test.json")
if err != nil
fmt.Println("读取json文件失败", err)
return
err = json.Unmarshal(bytes, &test)
if err != nil
fmt.Println("解析数据失败", err)
return
fmt.Println(test.Name)
fmt.Println(test.Date)
fmt.Println("man: ", test.Sex["man"])
fmt.Println("woman: ", test.Sex["woman"])
读取包含两个层级键名的Json文件
此时 test.json 配置文件的内容如下。
"req_list": [
"url": "http://127.0.0.1/sqli-labs-master/Less-1/?id=1",
"method": "GET",
"headers":
"Spider-Name": "crawlergo",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/79.0.3945.0 Safari/537.36"
,
"data": "",
"source": "Target"
],
"all_req_list": [
"url": "http://127.0.0.1/sqli-labs-master/Less-1/?id=1",
"method": "GET",
"headers":
"Spider-Name": "crawlergo",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.0 Safari/537.36"
,
"data": "",
"source": "Target"
],
"all_domain_list": ["127.0.0.1"],
"sub_domain_list": ["127.0.0.1"]
尝试定义 req_list 的值为数组 array,json.Unmarshal()
函数报错:解析数据失败 json: cannot unmarshal object into Go struct field Crawler.req_list of type string。
type Crawler struct
ReqList []string `json:"req_list"`
AllReqList []string `json:"all_req_list"`
AllDomainList []string `json:"all_domain_list"`
SubDomainList []string `json:"sub_domain_list"`
数组的元素值类型应该是字典 map,尝试定义如下结构体,仍然是相同的报错信息。
type Crawler struct
ReqList []map[string]string `json:"req_list"`
AllReqList []map[string]string `json:"all_req_list"`
AllDomainList []map[string]string `json:"all_domain_list"`
SubDomainList []map[string]string `json:"sub_domain_list"`
由于 Json 文件包含不同层级的键名,所以定义结构体的结构也肯定不同,参考文章:Go 解析嵌套 json。
如果 Json 数据有多层键,那么每层键都需要定义一个结构体接收对应的键名,并且定义变量时需要嵌套结构体类型。
查看参考文章编写代码,成功读取嵌套 Json 文件的代码和打印结果如下。
/* 打印结果
http://127.0.0.1/sqli-labs-master/Less-1/?id=1
GET
Spider-Name crawlergo
User-Agent Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.0 Safari/537.36
*/
// 接收第一层 Json 数据
type CrawlerFirst struct
ReqList []CrawlerSecond `json:"req_list"`
AllReqList []CrawlerSecond `json:"all_req_list"`
AllDomainList []string `json:"all_domain_list"`
SubDomainList []string `json:"sub_domain_list"`
type CrawlerSecond struct
Url string `json:"url"`
Method string `json:"method"`
Headers map[string]string `json:"headers"`
Data string `json:"data"`
Source string `json:"source"`
// Test 定义配置文件解析后的结构
type Test struct
Name []string `json:"name"`
Date string `json:"date"`
Sex map[string]int `json:"sex"`
func main()
//var req model.Request
var crawlerFirst CrawlerFirst
bytes, err := ioutil.ReadFile("./debug.json")
if err != nil
fmt.Println("读取json文件失败", err)
return
err = json.Unmarshal(bytes, &crawlerFirst)
if err != nil
fmt.Println("解析数据失败", err)
return
for _,req:= range crawlerFirst.ReqList
fmt.Println(req.Url)
fmt.Println(req.Method)
fmt.Println(req.Data)
for key,value:= range req.Headers
fmt.Println(key, value)
准备数据阶段
修改爬虫保存结果(结合报错和实际情况未修改代码,可忽略)
读取爬虫结果的前提是,爬虫结果是我们需要的数据。原项目中把如下 4 种数据保存到一个 Json 文件,我们首先需要分离这些数据,使每种数据分别保存到各自的文件。
type Result struct
ReqList []Request `json:"req_list"`
AllReqList []Request `json:"all_req_list"`
AllDomainList []string `json:"all_domain_list"`
SubDomainList []string `json:"sub_domain_list"`
crawlergo_cmd.go 文件保存结果的相关代码,调试打印 result
变量值发现,此时爬虫结果按照 Result
结构体都已经填充内容。先酱紫,后续对爬虫结果结构体有想法,再另外进行修改。
考虑到报错、以及直接可以读取 Json 文件,不修改代码。
// 代码 350 行
// 输出结果
outputResult(result)
// 代码 403 行
func outputResult(result *pkg.Result)
// 输出结果
if outputMode == "json"
fmt.Println("--[Mission Complete]--")
resBytes := getJsonSerialize(result)
fmt.Println(string(resBytes))
else if outputMode == "console"
for _, req := range result.ReqList
req.FormatPrint()
// 写入文件的主要代码
if len(outputJsonPath) != 0
resBytes := getJsonSerialize(result)
tools.WriteFile(outputJsonPath, resBytes)
// 修改该部分主要代码为如下内容(不更改代码,直接读取 json 文件)
/*
注意,
1.结构体中的变量名需要开启调试查看、或从其他代码中找到
2.序列化结构体的变量时,报错如下。
在结构体定义代码中,前两个变量的类型是 []Request,域名变量是 []string
考虑到可以直接读取Json文件,不更改代码。
cannot use result.ReqList (type []*model.Request) as type *pkg.Result in argument to getJsonSerialize
cannot use result.AllDomainList (type []string) as type *pkg.Result in argument to getJsonSerialize
*/
if len(outputJsonPath) != 0
resBytes1 := getJsonSerialize(result.req_list)
resBytes2 := getJsonSerialize(result.all_req_list)
resBytes3 := getJsonSerialize(result.all_domain_list)
resBytes4 := getJsonSerialize(result.sub_domain_list)
tools.WriteFile("req_" + outputJsonPath, resBytes1)
tools.WriteFile("all_req_" + outputJsonPath, resBytes2)
tools.WriteFile("all_domain_" + outputJsonPath, resBytes3)
tools.WriteFile("sub_domain_" + outputJsonPath, resBytes4)
从 Json 文件读取需要的爬虫数据
关于嵌套 Json 文件数据的读取,在本文的上一章节《读取嵌套的 Json 数据》已完成了代码的编写和调试。
读取数据的任务完成了,Fuzz 需要用到哪些数据呢?
url
method
headers // 为了避免被反爬虫限制访问,必须使用合适的请求头
data
读取爬虫Json文件
// 接收第一层 Json 数据
type CrawlerFirst struct
ReqList []CrawlerSecond `json:"req_list"`
AllReqList []CrawlerSecond `json:"all_req_list"`
AllDomainList []string `json:"all_domain_list"`
SubDomainList []string `json:"sub_domain_list"`
type CrawlerSecond struct
Url string `json:"url"`
Method string `json:"method"`
Headers map[string]string `json:"headers"`
Data string `json:"data"`
Source string `json:"source"`
// 封装函数
func read_crawler() CrawlerFirst
var crawlerFirst CrawlerFirst
bytes, err := ioutil.ReadFile("./debug.json")
if err != nil
fmt.Println("读取json文件失败", err)
os.Exit(1)
err = json.Unmarshal(bytes, &crawlerFirst)
if err != nil
fmt.Println("解析数据失败", err)
os.Exit(1)
return crawlerFirst
读取 paramsFuzz.json 文件
读取代码改了又改,总是报错。无奈从基本数据类型开始编写 Json 文件进行读取,然后逐渐嵌套数据并完成读取 Json 数据,最终代码和打印结果如下。
读取的 Json 文件见本文第一章 字典文件的数据结构
。
/* 打印 sqli 键值的结果
// /**/and/**/sleep(5)
// MySQL-数字型延迟注入
// 5
// '/**/and/**/sleep(5)--+
// MySQL-字符型延迟注入
// 5
// "/**/and/**/sleep(5)--+
// MySQL-字符型延迟注入
// 5
// '"+,
// MySQL-报错注入
// 0
// You have an error in your SQL syntax
*/
// 定义配置文件解析后的结构
type ParamsFuzzJson struct
SqlInjection []ParamData `json:"sqli"`
Xss []ParamData `json:"xss"`
type ParamData struct
Payload string `json:"payload"`
Description string `json:"description"`
Time int `json:"time"`
Keyword string `json:"keyword"`
func main()
//var req model.Request
var paramsFuzzJson ParamsFuzzJson
bytes, err := ioutil.ReadFile("./fuzzDicts/paramsFuzz.json")
if err != nil
fmt.Println("读取json文件失败", err)
// os.Exit(1)
return
err = json.Unmarshal(bytes, ¶msFuzzJson)
if err != nil
fmt.Println("解析数据失败", err)
return
//os.Exit(1)
// fmt.Println(paramsFuzzJson.SqlInjection)
// 读取 Json 的 sqli 键
for _, param:= range paramsFuzzJson.SqlInjection
// for _,param:= range param.key
fmt.Println(param.Payload)
fmt.Println(param.Description)
fmt.Println(param.Time)
fmt.Println(param.Keyword)
装配数据阶段
配置请求对象 req
项目文件 crawlergo/pkg/model
定义的请求对象结构体包含 8 个变量,考虑只保留如下 5 个变量,重新在 paramsFuzz.go 文件定义请求对象结构体。
type Request struct
URL *URL
Method string
Headers map[string]interface
PostData string
Proxy string
明确参数 Fuzz 的需求
- 遍历 req_list:爬虫结果中 req_list 键的内容,如果存在Get参数或Post参数则进行 Fuzz
- 遍历 paramJson 中的每一个键:sqli、xss等
保存传递动态参数的 Url 信息
对于爬虫结果,要筛选出传递动态参数的URl进行参数Fuzz测试。
顺便把传递动态参数的Url保存到新的Json文件,方便人工查看,如下代码把传递动态参数的爬虫结果保存到指定文件 params_debug.json。
// 接收第一层 Json 数据
type CrawlerJson struct
ReqList []CrawlerData `json:"req_list"`
AllReqList []CrawlerData `json:"all_req_list"`
AllDomainList []string `json:"all_domain_list"`
SubDomainList []string `json:"sub_domain_list"`
type CrawlerData struct
Url string `json:"url"`
Method string `json:"method"`
Headers map[string]string `json:"headers"`
Data string `json:"data"`
Source string `json:"source"`
// 调用代码
for _,crawler:= range crawlerReq
outputParamsResult(crawler)
func outputParamsResult(result CrawlerData)
// 写入的文件名变量,在合并代码时需要调整修改
outputJsonPath := "debug.json"
// 输出结果
if len(outputJsonPath) != 0
resBytes := getParamsJsonSerialize(result)
tools.WriteFile("params_" + outputJsonPath, resBytes)
func getParamsJsonSerialize(paramsResult CrawlerData) []byte
resBytes, err := json.Marshal(paramsResult)
if err != nil
log.Fatal("Marshal result error")
return resBytes
装配数据
算法步骤:
- 准备数据1:读取爬虫结果,调用爬虫结构体的参数信息
- 准备数据2:读取参数 Fuzz 字典,调用结构体
- 筛选:遍历爬虫结构体,通过条件判断找出传递动态参数的Url,根据传参方式分为 Get 和 Post
- 准备数据3:逐个添加 Payload 到动态参数末尾,如果是 Get 方式则拼接 Url 和参数
- 装配:把爬虫结构体的信息、以及处理后的动态参数信息,装配到请求对象 req
- 运行和验证:发送请求,查看响应信息验证漏洞是否存在(运行阶段)
- 标记去重:如果动态参数测试过了,则加入 Array ,并且在测试动态参数前检查是否在 Array 中(验证阶段)
第一步
读取爬虫结果
// 接收第一层 Json 数据
type CrawlerJson struct
ReqList []CrawlerData `json:"req_list"`
AllReqList []CrawlerData `json:"all_req_list"`
AllDomainList []string `json:"all_domain_list"`
SubDomainList []string `json:"sub_domain_list"`
type CrawlerData struct
Url string `json:"url"`
Method string `json:"method"`
Headers map[string]string `json:"headers"`
Data string `json:"data"`
Source string `json:"source"`
func main()
// 1.读取爬虫结果
crawlerReq := read_crawlerReq()
func read_crawlerReq() [毕设扫描器参数Fuzz第二篇:动态爬虫的创建启动和协程池
毕设扫描器动态爬虫CrawlerGo源码分析1:cli库的使用