diff --git a/innerbean/web_log.go b/innerbean/web_log.go index 78bcff3..27d8b4e 100644 --- a/innerbean/web_log.go +++ b/innerbean/web_log.go @@ -4,6 +4,7 @@ type WebLog struct { WafInnerDFlag string `json:"waf_inner_dflag"` //日志队列处理方式 HOST string `json:"host"` URL string `json:"url"` + RawQuery string `json:"raw_query"` //原始URL查询 REFERER string `json:"referer"` USER_AGENT string `json:"user_agent"` METHOD string `json:"method"` diff --git a/libinjection-go/dir_traversal.go b/libinjection-go/dir_traversal.go new file mode 100644 index 0000000..4ae9e80 --- /dev/null +++ b/libinjection-go/dir_traversal.go @@ -0,0 +1,58 @@ +package libinjection + +import ( + "net/url" + "regexp" + "strings" +) + +// HasDirTraversal 检测URL是否存在目录穿越漏洞 +func HasDirTraversal(rawURL string) bool { + // 解析URL + parsedURL, err := url.Parse(rawURL) + if err != nil { + return false + } + + // 定义路径穿越特征正则表达式 + pattern := `(\.\./|\.\.\\|%2e%2e/|%2e%2e\\)` + regex := regexp.MustCompile(pattern) + + // 检查URL路径部分 + path := parsedURL.Path + if checkComponent(path, regex) { + return true + } + + // 检查查询参数值 + query := parsedURL.Query() + for _, values := range query { + for _, value := range values { + // 解码URL编码后再检查(防止%2e%2e%2f绕过) + decodedValue, err := url.QueryUnescape(value) + if err != nil { + decodedValue = value // 如果解码失败,使用原始值 + } + if checkComponent(decodedValue, regex) { + return true + } + } + } + + return false +} + +// 检查单个组件是否包含恶意特征 +func checkComponent(component string, regex *regexp.Regexp) bool { + // 检查是否包含路径遍历模式 + if regex.MatchString(component) { + return true + } + + // 额外检查Windows路径特征 + if strings.Contains(component, "..\\") { + return true + } + + return false +} diff --git a/libinjection-go/dir_traversal_test.go b/libinjection-go/dir_traversal_test.go new file mode 100644 index 0000000..91f4db9 --- /dev/null +++ b/libinjection-go/dir_traversal_test.go @@ -0,0 +1,22 @@ +package libinjection + +import ( + "fmt" + "testing" +) + +func TestHasDirTraversal(t *testing.T) { + // 测试用例 + testURLs := []string{ + "http://example.com/download?file=../../etc/passwd", // 应检测到 + "http://example.com/?id=../../../../etc/passwd", // 应检测到 + "http://example.com/../../secret.txt", // 应检测到 + "http://example.com/?path=%2e%2e%2fetc%2fpasswd", // 应检测到(URL编码的../) + "http://example.com/valid?file=doc.pdf", // 正常URL + "http://example.com/?data=..\\Windows\\system.ini", // 检测Windows路径 + } + + for _, u := range testURLs { + fmt.Printf("检测URL: %-50s => 存在漏洞: %t\n", u, HasDirTraversal(u)) + } +} diff --git a/model/hosts.go b/model/hosts.go index ff7c3b0..fc0e6c2 100644 --- a/model/hosts.go +++ b/model/hosts.go @@ -34,10 +34,11 @@ type Hosts struct { } type HostsDefense struct { - DEFENSE_BOT int `json:"bot"` //防御-虚假BOT - DEFENSE_SQLI int `json:"sqli"` //防御-Sql注入 - DEFENSE_XSS int `json:"xss"` //防御-xss攻击 - DEFENSE_SCAN int `json:"scan"` //防御-scan工具扫描 - DEFENSE_RCE int `json:"rce"` //防御-scan工具扫描 - DEFENSE_SENSITIVE int `json:"sensitive"` //敏感词检测 + DEFENSE_BOT int `json:"bot"` //防御-虚假BOT + DEFENSE_SQLI int `json:"sqli"` //防御-Sql注入 + DEFENSE_XSS int `json:"xss"` //防御-xss攻击 + DEFENSE_SCAN int `json:"scan"` //防御-scan工具扫描 + DEFENSE_RCE int `json:"rce"` //防御-scan工具扫描 + DEFENSE_SENSITIVE int `json:"sensitive"` //敏感词检测 + DEFENSE_DIR_TRAVERSAL int `json:"traversal"` //目录穿越检测 } diff --git a/wafenginecore/check_dir_traversal.go b/wafenginecore/check_dir_traversal.go new file mode 100644 index 0000000..087c7a1 --- /dev/null +++ b/wafenginecore/check_dir_traversal.go @@ -0,0 +1,43 @@ +package wafenginecore + +import ( + "SamWaf/innerbean" + "SamWaf/libinjection-go" + "SamWaf/model/detection" + "SamWaf/model/wafenginmodel" + "net/http" + "net/url" +) + +// CheckDirTraversal 穿越漏洞检测 +func (waf *WafEngine) CheckDirTraversal(r *http.Request, weblogbean *innerbean.WebLog, formValue url.Values, hostTarget *wafenginmodel.HostSafe, globalHostTarget *wafenginmodel.HostSafe) detection.Result { + result := detection.Result{ + JumpGuardResult: false, + IsBlock: false, + Title: "", + Content: "", + } + var flag = false + //检测sql注入 + if libinjection.HasDirTraversal(weblogbean.RawQuery) || + libinjection.HasDirTraversal(weblogbean.BODY) { + flag = true + } + if flag == false { + for _, value := range formValue { + for _, v := range value { + if libinjection.HasDirTraversal(v) { + flag = true + } + } + } + } + if flag == true { + weblogbean.RISK_LEVEL = 2 + result.IsBlock = true + result.Title = "目录穿越漏洞" + result.Content = "请正确访问" + return result + } + return result +} diff --git a/wafenginecore/checksql.go b/wafenginecore/checksql.go index 1268d3b..08273bd 100644 --- a/wafenginecore/checksql.go +++ b/wafenginecore/checksql.go @@ -22,7 +22,7 @@ func (waf *WafEngine) CheckSql(r *http.Request, weblogbean *innerbean.WebLog, fo } var sqlFlag = false //检测sql注入 - if libinjection.IsSQLiNotReturnPrint(weblogbean.URL) || + if libinjection.IsSQLiNotReturnPrint(weblogbean.RawQuery) || libinjection.IsSQLiNotReturnPrint(weblogbean.BODY) || libinjection.IsSQLiNotReturnPrint(weblogbean.POST_FORM) { sqlFlag = true diff --git a/wafenginecore/checkxss.go b/wafenginecore/checkxss.go index ef28df8..aec7f20 100644 --- a/wafenginecore/checkxss.go +++ b/wafenginecore/checkxss.go @@ -21,7 +21,7 @@ func (waf *WafEngine) CheckXss(r *http.Request, weblogbean *innerbean.WebLog, fo Content: "", } var xssFlag = false - if libinjection.IsXSS(weblogbean.URL) || + if libinjection.IsXSS(weblogbean.RawQuery) || libinjection.IsXSS(weblogbean.POST_FORM) { xssFlag = true } diff --git a/wafenginecore/wafengine.go b/wafenginecore/wafengine.go index 117bb5e..336debe 100644 --- a/wafenginecore/wafengine.go +++ b/wafenginecore/wafengine.go @@ -183,11 +183,12 @@ func (waf *WafEngine) ServeHTTP(w http.ResponseWriter, r *http.Request) { currentDay, _ := strconv.Atoi(time.Now().Format("20060102")) //URL 解码 - enEscapeUrl := wafhttpcore.WafHttpCoreUrlEncode(r.RequestURI, 100) + deRawQueryUrl := wafhttpcore.WafHttpCoreUrlEncode(r.URL.RawQuery, 10) datetimeNow := time.Now() weblogbean := innerbean.WebLog{ HOST: host, - URL: enEscapeUrl, + URL: r.RequestURI, + RawQuery: deRawQueryUrl, REFERER: r.Referer(), USER_AGENT: r.UserAgent(), METHOD: r.Method, @@ -287,12 +288,13 @@ func (waf *WafEngine) ServeHTTP(w http.ResponseWriter, r *http.Request) { } hostDefense := model.HostsDefense{ - DEFENSE_BOT: 1, - DEFENSE_SQLI: 1, - DEFENSE_XSS: 1, - DEFENSE_SCAN: 1, - DEFENSE_RCE: 1, - DEFENSE_SENSITIVE: 1, + DEFENSE_BOT: 1, + DEFENSE_SQLI: 1, + DEFENSE_XSS: 1, + DEFENSE_SCAN: 1, + DEFENSE_RCE: 1, + DEFENSE_SENSITIVE: 1, + DEFENSE_DIR_TRAVERSAL: 1, } err := json.Unmarshal([]byte(hostTarget.Host.DEFENSE_JSON), &hostDefense) if err != nil { @@ -328,6 +330,12 @@ func (waf *WafEngine) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } } + //目录穿越检测 + if hostDefense.DEFENSE_DIR_TRAVERSAL == 1 { + if handleBlock(waf.CheckDirTraversal) { + return + } + } //检测CC if handleBlock(waf.CheckCC) { return diff --git a/wafenginecore/wafhttpcore/sql_test.go b/wafenginecore/wafhttpcore/sql_test.go index 6f0f8a6..08b626e 100644 --- a/wafenginecore/wafhttpcore/sql_test.go +++ b/wafenginecore/wafhttpcore/sql_test.go @@ -3,12 +3,22 @@ package wafhttpcore import ( "SamWaf/libinjection-go" "fmt" + "net/url" "testing" ) func TestSql(t *testing.T) { - payload := "data/index.html?id=1 and (select top 1 count(*) from admin where unicode(substring(a,1,1))=asc%E5%80%BC%20and%20id=1)%3E0" + payload := "id=1+and+1=2+union+select+1" + decodedValue, err := url.QueryUnescape(payload) - sqlB, sqlstring := libinjection.IsSQLi(payload) - fmt.Println(sqlB, sqlstring) + payLoadReturnPrint := libinjection.IsSQLiNotReturnPrint(payload) + fmt.Println(fmt.Sprintf("payload=%v Result:%v", payload, payLoadReturnPrint)) + + decodepayLoadReturnPrint := libinjection.IsSQLiNotReturnPrint(decodedValue) + if err != nil { + fmt.Println(fmt.Sprintf("decodePayload=%v Result:%v QueryUnescapeErr:%v", decodedValue, decodepayLoadReturnPrint, err)) + } else { + fmt.Println(fmt.Sprintf("decodePayload=%v Result:%v ", decodedValue, decodepayLoadReturnPrint)) + + } } diff --git a/wafenginecore/wafhttpcore/urlencode.go b/wafenginecore/wafhttpcore/urlencode.go index c9b1e64..65e1d6f 100644 --- a/wafenginecore/wafhttpcore/urlencode.go +++ b/wafenginecore/wafhttpcore/urlencode.go @@ -3,16 +3,10 @@ package wafhttpcore import ( "SamWaf/common/zlog" "net/url" - "strings" ) // 逆向编码处理 func WafHttpCoreUrlEncode(encoded string, maxDepth int) string { - // 如果没有编码格式,直接返回 - if !strings.Contains(encoded, "%") { - return encoded - } - // 限制递归的最大深度,防止无限递归 if maxDepth <= 0 { return encoded // 达到最大递归深度时返回原始字符串