漏洞验证框架的构思与实现(二)
字数 1285 2025-08-09 22:00:43
漏洞验证框架规则体系设计与实现
0x01 概述
本文详细讲解漏洞验证框架的核心模块——规则体系的设计与实现。该规则体系基于xray的规则进行扩展,既能兼容xray的POC规则,又加入了更细致的请求变形分类。
0x02 原始请求来源
漏洞验证的第一步是构造原始HTTP请求包,主要有以下几种来源:
1. 通过URL生成
根据输入的URL补充基础请求头,生成基础的GET HTTP请求:
func GenOriginalReq(url string) (*http.Request, error) {
// 生成原始请求,如果没有协议默认使用http
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
url = "http://" + url
}
originalReq, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Error("util/requests.go:GenOriginalReq original request gen error", url, err)
return nil, err
}
originalReq.Header.Set("Host", originalReq.Host)
originalReq.Header.Set("Accept-Encoding", "gzip, deflate")
originalReq.Header.Set("Accept", "*/*")
originalReq.Header.Set("User-Agent", conf.GlobalConfig.HttpConfig.Headers.UserAgent)
originalReq.Header.Set("Accept-Language", "en")
originalReq.Header.Set("Connection", "close")
return originalReq, nil
}
2. 通过HTTP报文文件生成
解析HTTP报文文件,生成HTTP请求:
func GenOriginalReqFromRaw(filePath string) (*http.Request, error) {
raw, err := ioutil.ReadFile(filePath)
if err != nil {
c.JSON(msg.ErrResp("请求报文文件解析失败"))
return nil, err
}
oreq, err := http.ReadRequest(bufio.NewReader(bytes.NewReader(raw)))
if err != nil {
return nil, err
}
if !oreq.URL.IsAbs() {
scheme := "http"
oreq.URL.Scheme = scheme
oreq.URL.Host = oreq.Host
}
return oreq, nil
}
3. 通过代理监听获取
如xray的浏览器代理模式,通过代理形式监听获取请求。
0x03 xray规则兼容实现
xray的规则体系基于Common Expression Language (CEL表达式)实现,使用cel-go包进行解析。
CEL表达式运行流程
- 构建CEL环境:初始化cel.Env
- 注入类型和方法:实现xray规则中所有的变量和方法
- 计算表达式:传入表达式、CEL环境和检测变量列表
关键实现点
-
Set中自定义变量处理:
- 自定义变量可能引用之前的变量,必须有序加载
- 建议使用
gopkg.in/yaml.v2包的MapSlice保存(map是无序的)
-
CEL控制器:管理整个CEL的生命周期
请求构造示例
以airflow未授权访问漏洞POC为例:
name: poc-yaml-airflow-unauth
rules:
- method: GET
path: /admin/
expression: |
response.status == 200 &&
response.body.bcontains(b"<title>Airflow - DAGs</title>") &&
response.body.bcontains(b"<h2>DAGs</h2>")
变形过程:
- 原始请求:
GET /aaa/bbb.html - 根据POC规则变形为:
GET /aaa/admin/
检测链控制
xray定义了两种检测链格式:
-
rules:rule组成的有序列表([]rule)
- 值为true的rule,如果后面还有其他rule,则继续执行
- 后续没有其他rule,则表示该POC结果为true
-
groups:rules组成的列表map[string]rules
- 只要有一组rules执行成功,就认为POC结果为true
执行优先级:先检查groups是否为空,非空才执行rules。
func ExecExpressionHandle(ctx controllerContext) {
var result bool
var err error
poc := ctx.GetPoc()
if poc == nil {
log.Error("[rule/handle.go:ExecExpressionHandle error]", "poc is nil")
return
}
if poc.Groups != nil {
result, err = ctx.Groups(ctx.IsDebug())
} else {
// 执行rules逻辑
}
if err != nil {
log.Error("[rule/handle.go:ExecExpressionHandle error]", err)
return
}
if result {
ctx.Abort()
}
return
}
POC运行流程
- 获取原始请求,根据规则进行变形,构造请求
- 初始化CEL环境:注入变量、方法
- 初始化CEL变量列表:注入set定义的自定义变量、注入当前请求
- 发起构造后的请求,并将响应写入CEL变量列表
- 执行表达式
0x04 xray规则扩展
在xray基础上增加了更细致的请求变形分类:
1. 规则类型分类
新增规则类型字段,根据不同类型执行不同变形逻辑:
// 根据原始请求 + rule 生成并发起新的请求
func (controller *PocController) DoSingleRuleRequest(rule *Rule) (*proto.Response, error) {
fastReq := controller.Request.Fast
fixedFastReq := fasthttp.AcquireRequest()
fastReq.CopyTo(fixedFastReq)
curPath := string(fixedFastReq.URI().Path())
affects := controller.Plugin.Affects
switch affects {
// 情况4 参数级
case AffectAppendParameter, AffectReplaceParameter:
for k, v := range rule.Headers {
fixedFastReq.Header.Set(k, v)
}
return util.DoFasthttpRequest(fixedFastReq, rule.FollowRedirects)
// 情况3 content级
case AffectContent:
return util.DoFasthttpRequest(fixedFastReq, rule.FollowRedirects)
// 情况1 dir级
case AffectDirectory:
// 目录级漏洞检测 判断是否以"/"结尾
if curPath != "" && strings.HasSuffix(curPath, "/") {
// 去掉规则中的的"/" 再拼
curPath = fmt.Sprint(curPath, strings.TrimPrefix(rule.Path, "/"))
} else {
curPath = fmt.Sprint(curPath, "/", strings.TrimPrefix(rule.Path, "/"))
}
// 情况2 server级
case AffectServer:
curPath = rule.Path
// url级(直接使用原始请求头,只替换路径和完整post参数)
case AffectURL:
default:
}
// 兼容xray: 某些POC没有区分path和query
curPath = strings.ReplaceAll(curPath, " ", "%20")
curPath = strings.ReplaceAll(curPath, "+", "%20")
fixedFastReq.URI().DisablePathNormalizing = true
fixedFastReq.URI().Update(curPath)
for k, v := range rule.Headers {
fixedFastReq.Header.Set(k, v)
}
fixedFastReq.Header.SetMethod(rule.Method)
// 处理multipart
contentType := string(fixedFastReq.Header.ContentType())
if strings.HasPrefix(strings.ToLower(contentType), "multipart/form-Data") &&
strings.Contains(rule.Body, "\n\n") {
multipartBody, err := util.DealMultipart(contentType, rule.Body)
if err != nil {
return nil, err
}
fixedFastReq.SetBody([]byte(multipartBody))
}
return util.DoFasthttpRequest(fixedFastReq, rule.FollowRedirects)
}
2. Multipart处理
处理文件上传等multipart请求时,需按照RFC规定将\n转成\r\n:
func DealMultipart(contentType string, ruleBody string) (result string, err error) {
errMsg := ""
// 处理multipart的/n
re := regexp.MustCompile(`(?m)multipart\/form-Data; boundary=(.*)`)
match := re.FindStringSubmatch(contentType)
if len(match) != 2 {
errMsg = "no boundary in content-type"
return "", errors.New(errMsg)
}
boundary := "--" + match[1]
multiPartContent := ""
// 处理rule
multiFile := strings.Split(ruleBody, boundary)
if len(multiFile) == 0 {
errMsg = "ruleBody.Body multi content format err"
return multiPartContent, errors.New(errMsg)
}
for _, singleFile := range multiFile {
// 处理单个文件
spliteTmp := strings.Split(singleFile, "\n\n")
if len(spliteTmp) == 2 {
fileHeader := spliteTmp[0]
fileBody := spliteTmp[1]
fileHeader = strings.Replace(fileHeader, "\n", "\r\n", -1)
multiPartContent += boundary + fileHeader + "\r\n\r\n" +
strings.TrimRight(fileBody, "\n") + "\r\n"
}
}
multiPartContent += boundary + "--" + "\r\n"
return multiPartContent, nil
}
0x05 性能优化
- 使用fasthttp替换原生http包,减少内存占用
- 请求变形时尽可能复用原始请求头
- 并行处理多个规则检测
0x06 总结
本文详细介绍了漏洞验证框架规则体系的设计与实现,主要特点包括:
- 兼容xray的YAML规则格式
- 基于CEL表达式的规则检测
- 多种原始请求来源支持
- 更细致的请求变形分类
- 完善的检测链控制
- 性能优化措施
该规则体系既保持了与xray的兼容性,又通过更细致的分类提高了检测的准确性和效率。