批量发包: 模糊测试批量发包
在 yak 中,批量发包的方式主要有两种
- 第一种是通过 
fuzz模块的fuzz.HTTPRequest函数构建模糊测试请求。 - 第二种是通过 
httpool模块的httpool.Pool函数进行请求批量渲染与发包。 
这两种方式适用的场景有一些细微差别。
httpool.Pool本质上核心发包函数和poc.HTTP是一致的,我们可以认为是poc.HTTP的批量进阶版。fuzz.HTTPRequest虽然最后是调用httpool.Pool实现发包的,但是会对数据包做一些预处理,例如:- 链式 API 调用可编程地处理数据包细节
 - 可以自动替换参数的内容。生成可以测试的参数模版请求。
 
对比总结
fuzz.HTTPRequest适合细节操作数据包,尤其是有逻辑的动态调整参数进行模糊测试或者漏洞检测。httpool.Pool适合大批量的发包,进行模版渲染后,修复数据包Content-Length再发送
与此同时,他们的返回值都是 chan *palm/common/mutate.(_httpResult) 对象。
其定义为:
type palm/common/mutate.(_httpResult) struct {  Fields(可用字段):      Url: string      Request: *http.Request      Error: error      RequestRaw: []uint8      ResponseRaw: []uint8      Response: *http.Response      DurationMs: int64      Timestamp: int64      Payloads: []string  StructMethods(结构方法/函数):  PtrStructMethods(指针结构方法/函数):}0x01 使用 httpool 进行模糊测试模版发包#
我们尝试对一个网站的 /target1 /target2 /target3 ... /target10 进行批量发包,如何实现呢?
res, err = httpool.Pool(`GET /target{{int(1-10)}} HTTP/1.1Host: www.baidu.com`)die(err)
loglevel("info")for result = range res {    header, body = poc.Split(result.ResponseRaw)    log.info("URL: %v", result.Url)}
/*OUTPUT:    ...    ...    [INFO] 2022-03-08 14:28:21 +0800 [yaki-code-383898351] URL: http://www.baidu.com/target5    [INFO] 2022-03-08 14:28:21 +0800 [yaki-code-383898351] URL: http://www.baidu.com/target10    [INFO] 2022-03-08 14:28:21 +0800 [yaki-code-383898351] URL: http://www.baidu.com/target8    [INFO] 2022-03-08 14:28:21 +0800 [yaki-code-383898351] URL: http://www.baidu.com/target1    [INFO] 2022-03-08 14:28:21 +0800 [yaki-code-383898351] URL: http://www.baidu.com/target2    [INFO] 2022-03-08 14:28:21 +0800 [yaki-code-383898351] URL: http://www.baidu.com/target6    [INFO] 2022-03-08 14:28:21 +0800 [yaki-code-383898351] URL: http://www.baidu.com/target7    [INFO] 2022-03-08 14:28:21 +0800 [yaki-code-383898351] URL: http://www.baidu.com/target9    [INFO] 2022-03-08 14:28:21 +0800 [yaki-code-383898351] URL: http://www.baidu.com/target3    [INFO] 2022-03-08 14:28:21 +0800 [yaki-code-383898351] URL: http://www.baidu.com/target4*/带参数渲染的批量发包模版#
res, err = httpool.Pool(`GET /target{{params(suffix)}} HTTP/1.1Host: www.baidu.com
{{params(body)}}`, httpool.fuzzParams({"suffix": ["1", "2", "3", "4"], "body": 123}))die(err)
loglevel("info")for result = range res {    header, body = poc.Split(result.ResponseRaw)    log.info("URL: %v", result.Url)    println(string(poc.Split(result.RequestRaw)[1]))}
/*OUTPUT:    ...    [INFO] 2022-03-08 14:32:03 +0800 [default:log.go:178] start to send to http://www.baidu.com/target1(:0) (packet mode)    [INFO] 2022-03-08 14:32:03 +0800 [default:log.go:178] start to send to http://www.baidu.com/target2(:0) (packet mode)    [INFO] 2022-03-08 14:32:03 +0800 [default:log.go:178] start to send to http://www.baidu.com/target3(:0) (packet mode)    [INFO] 2022-03-08 14:32:03 +0800 [default:log.go:178] start to send to http://www.baidu.com/target4(:0) (packet mode)    [INFO] 2022-03-08 14:32:03 +0800 [yaki-code-283417700] URL: http://www.baidu.com/target1    123
    [INFO] 2022-03-08 14:32:03 +0800 [yaki-code-283417700] URL: http://www.baidu.com/target2    123
    [INFO] 2022-03-08 14:32:03 +0800 [yaki-code-283417700] URL: http://www.baidu.com/target4    123
    [INFO] 2022-03-08 14:32:03 +0800 [yaki-code-283417700] URL: http://www.baidu.com/target3    123
*/使用 HTTPS 协议#
我们对上面的内容进行一点点修改,增加一些其他参数
res, err = httpool.Pool(`GET /target{{params(suffix)}} HTTP/1.1Host: www.baidu.com
{{params(body)}}`, httpool.fuzzParams({"suffix": ["1", "2", "3", "4"], "body": 123}), httpool.https(true))die(err)发送到指定主机端口#
这里我们以 Host 碰撞作为一个典型例子。
对于 Yak 来说,这并不是一个困难的事情,通过 httpool 的参数可以指定 host 实现针对 IP 的特定连接,再去查询绑定关系,从而碰撞出可以访问的内部资产。
domains = [    "m1.test.example.com",    "oa.example.com",    "oa2.example.com",    "oa3.example.com",    "ns1.test.example.com",    "ns2.test.example.com",    "test1.stage.example.com",    "test2.stage.example.com",    "test3.stage.example.com",    "crm.test.example.com",    "test1.dev.example.com",]
res, err = httpool.Pool(`GET /admin/ HTTP/1.1Host: {{params(domains)}}Uesr-Agent: test111
`, httpool.fuzzParams({"domains":domains}), httpool.https(true), httpool.host("cybertunnel.run", true/*type: isHttps*/))die(err)
loglevel("info")for result = range res {    header, body = poc.Split(result.ResponseRaw)    log.info("URL: %v", result.Url)    // 处理结果    if result.Response != nil {        if result.Response.StatusCode >= 200 && result.Response.StatusCode < 400 {            // 处理结果        }    }}Golang 标准库并不适合完成这项工作
对于 Golang 来说,会通过 (*http.Request).Host 等来决定真正访问的 IP。
我们需要指定 IP 并不能使用标准库。
0x02 使用 fuzz.HTTPRequest 来完成批量发包#
fuzz.HTTPRequest 更像是一个 "外科手术" 的库,虽然不能像 httpool 一样大开大合,但是更擅长操作数据包的细节与参数。
关于这个库,文档中已经有很多描述了。
可以参考如下链接
我们以一个简单例子快速预览一下这个库的核心功能
freq, err = fuzz.HTTPRequest(`GET / HTTP/1.1Host: www.example.com`)desc(freq)
freq = freq.FuzzPath("/specific-path1").FuzzMethod("POST").FuzzPostRaw(`{"a": 123}`).FuzzPostJsonParams("KEY", "123")freq.Show() // Display
/*OUTPUT:
    POST /specific-path1 HTTP/1.1    Host: www.example.com    Content-Length: 21
    {"KEY":"123","a":123}*/
res, err = freq.Exec()die(err)for result = range res {    // handle `palm/common/mutate.(_httpResult)`}
result, err = freq.ExecFirst()die(err)// handle `palm/common/mutate.(_httpResult)`freq 定义
我们在使用 *mutate.FuzzHTTPRequest 的时候,如果不知道他的定义是啥,有哪些可用方法,可以通过 desc(freq) 直接查看
type palm/common/mutate.(FuzzHTTPRequest) struct {  Fields(可用字段):      Opts: []mutate.BuildFuzzHTTPRequestOption  StructMethods(结构方法/函数):  PtrStructMethods(指针结构方法/函数):      func Exec(v1 ...func httpPoolConfigOption(v1: *mutate.httpPoolConfig) ) return(chan *mutate._httpResult, error)      func ExecFirst(v1 ...func httpPoolConfigOption(v1: *mutate.httpPoolConfig) ) return(*mutate._httpResult, error)      func FuzzCookie(v1: interface {}, v2: interface {}) return(mutate.FuzzHTTPRequestIf)      func FuzzCookieRaw(v1: interface {}) return(mutate.FuzzHTTPRequestIf)      func FuzzFormEncoded(v1: interface {}, v2: interface {}) return(mutate.FuzzHTTPRequestIf)      func FuzzGetParams(v1: interface {}, v2: interface {}) return(mutate.FuzzHTTPRequestIf)      func FuzzGetParamsRaw(v1 ...string) return(mutate.FuzzHTTPRequestIf)      func FuzzHTTPHeader(v1: interface {}, v2: interface {}) return(mutate.FuzzHTTPRequestIf)      func FuzzMethod(v1 ...string) return(mutate.FuzzHTTPRequestIf)      func FuzzPath(v1 ...string) return(mutate.FuzzHTTPRequestIf)      func FuzzPostJsonParams(v1: interface {}, v2: interface {}) return(mutate.FuzzHTTPRequestIf)      func FuzzPostParams(v1: interface {}, v2: interface {}) return(mutate.FuzzHTTPRequestIf)      func FuzzPostRaw(v1 ...string) return(mutate.FuzzHTTPRequestIf)      func FuzzUploadFile(v1: interface {}, v2: interface {}, v3: []uint8) return(mutate.FuzzHTTPRequestIf)      func FuzzUploadFileName(v1: interface {}, v2: interface {}) return(mutate.FuzzHTTPRequestIf)      func FuzzUploadKVPair(v1: interface {}, v2: interface {}) return(mutate.FuzzHTTPRequestIf)      func GetCommonParams() return([]*mutate.FuzzHTTPRequestParam)      func GetCookieParams() return([]*mutate.FuzzHTTPRequestParam)      func GetGetQueryParams() return([]*mutate.FuzzHTTPRequestParam)      func GetOriginHTTPRequest() return(*http.Request, error)      func GetPostJsonParams() return([]*mutate.FuzzHTTPRequestParam)      func GetPostParams() return([]*mutate.FuzzHTTPRequestParam)      func IsBodyFormEncoded() return(bool)      func IsBodyJsonEncoded() return(bool)      func IsBodyUrlEncoded() return(bool)      func IsEmptyBody() return(bool)      func ParamsHash() return(string, error)      func Repeat(v1: int) return(mutate.FuzzHTTPRequestIf)      func Results() return([]*http.Request, error)      func Show()}