背景:
有没有一种可能,全自动扫描漏洞其实并不重要,能提出高级的风险点,用户可以自己去挖掘一些非常 “深入” 的 XSS 更合适?
1.yak.xhtml
提供HTML 相关的解析与定位辅助函数加速 XSS 漏洞检测2.yak.js.ASTWalk
提供针对 javascript 的词法解析与 AST 遍历服务,把遍历到的字面量 / 符号 / 错误信息作为结果返回。核心基础设施函数
yak.js.ASTWalk
*javascript.ASTWalkerResult 描述
type palm/common/javascript.(ASTWalkerResult) struct {
Fields(可用字段):
// 所有字符串字面量:所有的字符串字面量和字符串定义都会在这里被找到并总结
StringLiteral: []string
// 整数字面量
Int64Literal: []int64
// 浮点数字面量
Float64Literal: []float64
// ID: 被认为是符号的内容(被当作调用对象,或者 field)
Identifies: []string
// 出现语法错误的位置
BadSyntax: []string
}
如何编写启发式检测 XSS?
1.参数检测
参数位置
req = "<raw request>"
freq, err = fuzz.HTTPRequest(req)
if err!= nil{
println("new HTTPRequest error: %v",err)
return
}
params = freq.GetCommonParams() // 包含post json、post form、get参数、cookie参数(会自动过滤PHPSESSID、_ga、_gid等参数)
for _, param = range params {
printf("key: %s, value: %s, postion: %sn", param.Name(),param.Value(),param.PositionVerbose())
}
多参数如何测试
2.检测回显位置
str = str.RandStr(5) // RandStr生成的随机字符集是大小写字母
resp, err = param.Fuzz(randStr).Exec() // param 是freq.GetCommonParams()获取的参数对象,可以直接对某个参数fuzz
if err != nil {
println("Fuzz param %s error: %v",param.Name(), err)
return
}
rspo = <-resp
body, err = str.ExtractBodyFromHTTPResponseRaw(rspo.ResponseRaw)
if err != nil {
println("Get response body error: %v", err)
return
}
matchNodes = xhtml.FindNodeFromHtml(body, randStr)
xhtml.
FindNodeFromHtml
方法可以从 body 中查找回显出现的位置信息。3.生成 Paylaod
xhtml.
FindNodeFromHtml
查找出回显位置,生成相应 Payload,如:for _, matchNode = range matchNodes {
printf("Echo xpath postion: %s", matchNode.Xpath)
if matchNode.IsText() {
if matchNode.TagName == "script" {
//例:<script>a = '<参数>';</script>
payload = sprintf("';alert('Hello');'", matchNode.TagName)
payloads = append(payloads, payload)
} else {
//例:<div><参数></div>
payload = sprintf("</%s>Hello<%s>", matchNode.TagName)
payloads = append(payloads, payload)
}
} else if matchNode.IsAttr() {
//例:<div id="<参数>"></div>
payload = sprintf(""></%s>Hello<%s %s="%s", matchNode.TagName, matchNode.TagName, matchNode.Key, matchNode.Value)
payloads = append(payloads, payload)
} else if matchNode.IsCOMMENT() {
//例:<!-- <参数> -->
payload = sprintf("-->Hello<!--")
payloads = append(payloads, payload)
}
}
4.如何检测过滤字符?
agsAKdhjkTUI
,可疑字符:<,',",/
agsAKdhjkTUI<agsAKdhjkTUI'agsAKdhjkTUI"agsAKdhjkTUI/agsAKdhjkTUI
dangerousChars = ["<", ">", "/", "\", "'", """]
detectChars = []
for _, payload = range payloads {
for _, dangerousChar = range dangerousChars {
if utils.MatchAllOfGlob(payload, sprintf("*%s*", dangerousChar)) {
detectChars = append(detectChars, dangerousChar)
}
}
}
detectStr = randStr + strings.Join(detectChars, randStr) + randStr
resp, err = param.Fuzz(detectStr).Exec()
if err != nil {
println("Fuzz param %s error: %v", param.Name(), err)
return
}
rspo = <-resp
body, err = str.ExtractBodyFromHTTPResponseRaw(rspo.ResponseRaw)
if err != nil {
println("Get response body error: %v", err)
return
}
randStrFromIndex = body
passChars = []
i = 0
for {
n, btChar = xhtml.MatchBetween(randStrFromIndex, randStr, randStr, 50)
if n == -1 {
break
}
if i >= len(dangerousChars) {
break
}
if dangerousChars[i] == btChar {
passChars = append(passChars, btChar)
} else {
printf("Found characters to be filtered: %s->%s", dangerousChars[i], btChar)
}
randStrFromIndex = randStrFromIndex[n+len(randStr):]
i += 1
}
newPayloads = []
for _, payload = range payloads {
for _, filterChar = range passChars {
if str.MatchAllOfGlob(payload, sprintf("*%s*", filterChar)) {
newPayloads = append(newPayloads, payload)
}
}
}
5.判断成功
手工判断
xhtml.Find
查询回显位置,对比原请求的回显位置,针对每种payload对html的影响做相应检测。// <div><参数></div>
payload = "<script>asgdhFFASDljl</script>"
resp,err = param.Fuzz(sprintf("</div>%s<div>",payload)).Exec()
if err != nil {
println("Fuzz param %s error: %v", param.Name(), err)
return
}
rspo = <-resp
body, err = str.ExtractBodyFromHTTPResponseRaw(rspo.ResponseRaw)
if err != nil {
println("Get response body error: %v", err)
return
}
matchNodes = FindNodeFromHtml(body, "asgdhFFASDljl")
for _, matchNode = range matchNodes {
if matchNode.MatchText == "" && matchNode.TagName == "script"{
println("Found xss, payload: %s",payload)
}
}
// <div id="参数"></div>
resp,err = param.Fuzz(sprintf(""></div>Hello<div id="asgdhFFASDljl")).Exec()
if err != nil {
println("Fuzz param %s error: %v", param.Name(), err)
return
}
rspo = <-resp
body, err = str.ExtractBodyFromHTTPResponseRaw(rspo.ResponseRaw)
if err != nil {
println("Get response body error: %v", err)
return
}
matchNodes = FindNodeFromHtml(body, "asgdhFFASDljl")
for _, matchNode = range matchNodes {
if matchNode.IsAttr() && matchNode.Key == "id"{
println("Found xss")
}
}
DOM树判断
rawHtml = "<raw html>"
resp,err = param.Fuzz(sprintf(""></div>Hello<div id="asgdhFFASDljl")).Exec()
if err != nil {
println("Fuzz param %s error: %v", param.Name(), err)
return
}
rspo = <-resp
body, err = str.ExtractBodyFromHTTPResponseRaw(rspo.ResponseRaw)
if err != nil {
println("Get response body error: %v", err)
return
}
diffs,err = xhtml.CompareHtml(rawHtml,body)
if err != nil {
println("CompareHtml func error: %v", err)
return
}
for _, diff = range diffs {
if diff.Type == Tag {
printf("Found xss, XpathPos: %s, Reason: %s", diff.XpathPos, diff.Reason)
}
}
JavaScript 的 AST 抽象语法树判断
<script>var name = "<参数>";</script>
,如果payload不会产生新标签,那就需要对script标签内的代码进行分析,yak提供了js.ASTWalk方法,可以获取所有变量、Identifies,语法错误originJs = "console.log('<参数>');"
fuzzJs = "console.log(''); 2022-6-10; console.log('');"
result,err = js.ASTWalk(fuzzJs)
// result有五个数组类型成员:StringLiteral、Int64Literal、Float64Literal、Identifies、BadSyntax
// originJs的<参数>本应该是String类型,fuzzJs的payload是'); 2022-6-10 console.log(',
isContain = false
for _,s = range result.StringLiteral{
if str.StringContainsAnyOfSubString(s, "2022-6-10"){
isContain = true
break
}
}
if !isContain{
println("Found xss")
}
originJs = "console.log('<参数>');"
fuzzJs = "console.log(''');"
result1,err = js.ASTWalk(originJs)
result2,err = js.ASTWalk(fuzzJs)
if len(result1.BadSyntax) == 0 && len(result2.BadSyntax) != 0{
println("可能存在xss漏洞,但payload不对")
}
小总结
更新通知
Yakit 1.0.15-sp7 Feature
1. 优化 HTTP History 上滚动交互:会在恰当的时候自动加载数据,不需要每次都手动线上滚动~
2. 修复了某些时候 “上 / 下” 方向键切换不了 HTTPFlow 的 BUG
3. MITM 过滤器自动保存
4. MITM 交互界面的表格新增 “ID / Body Size” 字段
5. 右键 HTTP History 新增 “删除” 按钮
6. 数据包编辑器 / monaco editor 新增 “自动解码”,可恢复 (u0000) 格式的中文字符 @南风
7. Web Fuzzer 新增简易版本的 “导出” 功能,支持 CSV / JSON 两种格式。
yaklang 引擎 v1.0.15-sp13
1. 增加 gzip 支持:支持快捷解压/压缩接口
2. 增加 fuzz.UrlToHTTPRequest,可以使用 method + url 快速构建 fuzz.HTTPRequest
3. 增加 js.ASTWalk 遍历 javascript 中的符号和字面量
4. 增加通过 poc.UrlToHTTPRequest 快速构建一个原始数据包
5. 增加 codec.AutoDecode 以及 grpc autoDecode 支持,以便 yakit 今后提供智能解析的规则
6. 支持 fuzz {{int(1-9990|4)}} 语法来支持 fuzz zero padding
VSCode 插件:
1. 支持最新 v1.0.15-sp13
官网教程:
https://www.yaklang.io/products/intro
视频教程:
https://space.bilibili.com/437503777
下载地址:
https://github.com/yaklang/yakit
原文始发于微信公众号(Yak Project):XSS 启发式检测基础设施
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论