PrivHunterAI/scan.go
2025-04-25 12:55:25 +08:00

285 lines
9.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"strings"
"time"
aiapis "yuequanScan/AIAPIS"
"yuequanScan/config"
"github.com/lqqyt2423/go-mitmproxy/proxy"
)
type Result struct {
Method string `json:"method"`
Url string `json:"url"` // JSON 标签用于自定义字段名
// Reqbody string `json:"reqbody"`
RequestA string `json:"requestA"`
RequestB string `json:"requestB"`
RespBodyA string `json:"respBodyA"`
RespBodyB string `json:"respBodyB"`
Result string `json:"result"`
Reason string `json:"reason"`
Confidence string `json:"confidence"`
Timestamp string `json:"timestamp"`
}
// 扫描结果
type ScanResult struct {
Res string `json:"res"`
Reason string `json:"reason"`
Confidence string `json:"confidence"`
}
func scan() {
for {
time.Sleep(3 * time.Second)
logs.Range(func(key any, value any) bool {
// fmt.Println("The type of x is", reflect.TypeOf(value))
var r *RequestResponseLog
if rr, ok := value.(*RequestResponseLog); ok {
r = rr
} else {
fmt.Printf("Value is not of type RequestResponseLog\n")
}
//
if r.Request.Header != nil && r.Response.Header != nil && r.Response.Body != nil && r.Response.StatusCode == 200 {
// fmt.Println(r)
result, req1, req2, resp1, resp2, err := sendHTTPAndKimi(r) // 主要
if err != nil {
logs.Delete(key)
// fmt.Println(r)
fmt.Println(err)
} else {
var resultOutput Result
resultOutput.Method = TruncateString(r.Request.Method)
if r.Request.URL.RawQuery != "" {
resultOutput.Url = TruncateString(r.Request.URL.Scheme + "://" + r.Request.URL.Host + r.Request.URL.Path + "?" + r.Request.URL.RawQuery)
} else {
resultOutput.Url = TruncateString(r.Request.URL.Scheme + "://" + r.Request.URL.Host + r.Request.URL.Path)
}
// resultOutput.Reqbody = TruncateString(string(r.Request.Body))
resultOutput.RespBodyA = TruncateString(resp1)
httpRequest1, err := generateHTTPRequest(req1)
if err != nil {
fmt.Println("ErrorhttpRequest:", err)
}
httpRequest2, err := generateHTTPRequest(req2)
if err != nil {
fmt.Println("ErrorhttpRequest:", err)
}
resultOutput.RequestA = httpRequest1
resultOutput.RequestB = httpRequest2
resultOutput.RespBodyB = TruncateString(resp2)
//
result1, err := parseResponse(result)
if err != nil {
log.Fatalf("解析失败: %v", err)
}
var scanR ScanResult
err = json.Unmarshal([]byte(result1), &scanR)
if err != nil {
log.Println("解析 JSON 数据失败("+result+": )", err)
} else {
resultOutput.Result = scanR.Res
resultOutput.Reason = scanR.Reason
resultOutput.Confidence = scanR.Confidence
resultOutput.Timestamp = time.Now().Format("2006-01-02 15:04:05")
jsonData, err := json.Marshal(resultOutput)
if err != nil {
log.Fatalf("Error marshaling to JSON: %v", err)
}
log.Println(string(jsonData))
//--- 前端
var dataItem Result
// 解析 JSON 数据到结构体
err = json.Unmarshal([]byte(jsonData), &dataItem)
if err != nil {
log.Fatalf("Error parsing JSON: %v", err)
}
// 打印解析后的结构体内容
// fmt.Printf("Parsed DataItem: %+v\n", dataItem)
// if dataItem.RespBodyB{
// }
if dataItem.Result != "white" {
Resp = append(Resp, dataItem)
}
//---
log.Println(PrintYuequan(resultOutput.Result, resultOutput.Method, resultOutput.Url, resultOutput.Reason))
logs.Delete(key)
return true // 返回true继续遍历返回false停止遍历
}
}
} else {
// logs.Delete(key) // 不可以添加logs.Delete(key)
return true
}
return true
})
}
}
func sendHTTPAndKimi(r *RequestResponseLog) (result, reqA, reqB, respA, respB string, err error) {
r.Request.Header.Add("Host", r.Request.URL.Host)
jsonDataReq, err := json.Marshal(r.Request)
if err != nil {
fmt.Println("Error marshaling:", err)
return "", "", "", "", "", err // 返回错误
}
req1 := string(jsonDataReq)
resp1 := string(r.Response.Body)
fullURL := &url.URL{
Scheme: r.Request.URL.Scheme,
Host: r.Request.URL.Host,
Path: r.Request.URL.Path,
RawQuery: r.Request.URL.RawQuery,
}
// 达成这些要求进行越权扫描
if isNotSuffix(r.Request.URL.Path, config.GetConfig().Suffixes) && !containsString(r.Response.Header.Get("Content-Type"), config.GetConfig().AllowedRespHeaders) {
req, err := http.NewRequest(r.Request.Method, fullURL.String(), strings.NewReader(string(r.Request.Body)))
if err != nil {
fmt.Println("创建请求失败:", err)
return "", "", "", "", "", err // 返回错误
}
req.Header = r.Request.Header
// 增加其他头 2025 02 27
if config.GetConfig().Headers2 != nil {
for key, value := range config.GetConfig().Headers2 {
req.Header.Set(key, value)
}
}
// 2025 02 27 end
// req.Header.Set("Cookie", config.GetConfig().Cookie2)
// log.Println(req.Header)
requestInfo2 := proxy.Request{
Method: req.Method,
URL: req.URL,
Proto: req.Proto,
Header: req.Header,
Body: r.Request.Body,
}
jsonDataReq2, err := json.Marshal(requestInfo2)
if err != nil {
fmt.Println("Error marshaling:", err)
return "", "", "", "", "", err // 返回错误
}
req2 := string(jsonDataReq2)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("请求失败:", err)
return "", "", "", "", "", err // 返回错误
}
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response body:", err)
return "", "", "", "", "", err // 返回错误
}
// 将响应体转换为字符串
resp2 := string(bodyBytes)
if len(resp1+resp2) < 1048576 {
if !MatchString(config.GetConfig().RespBodyBWhiteList, resp2) {
similarity := StringSimilarity(resp1, resp2)
if similarity > 0.5 {
// 初始值
var resultDetect string
var detectErr error
maxRetries := 5
for i := 0; i < maxRetries; i++ {
resultDetect, detectErr = detectPrivilegeEscalation(config.GetConfig().AI, req1, resp1, resp2, resp.Status)
if detectErr == nil {
break // 成功退出循环
}
// 可选:增加延迟避免频繁请求
fmt.Println("AI分析异常重试中异常原因", detectErr)
time.Sleep(5 * time.Second) // 1秒延迟
}
if detectErr != nil {
fmt.Println("Error after retries:", detectErr)
return "", "", "", "", "", detectErr
}
return resultDetect, req1, req2, resp1, resp2, nil
} else {
return `{"res": "false", "reason": "相似度小于0.5(` + fmt.Sprint(similarity) + `)判断为未越权未消耗AI tokens","confidence":"100%"}`, req1, req2, resp1, resp2, nil
}
} else {
return `{"res": "false", "reason": "匹配到关键字判断为无越权未消耗AI tokens","confidence":"100%"}`, req1, req2, resp1, resp2, nil
}
} else {
return `{"res": "white", "reason": "请求包太大","confidence":"100%"}`, req1, req2, resp1, resp2, nil
}
}
return `{"res": "white", "reason": "白名单后缀或白名单Content-Type接口","confidence":"100%"}`, req1, "", resp1, "", nil
}
func detectPrivilegeEscalation(AI string, reqA, resp1, resp2, statusB string) (string, error) {
var result string
var err error
switch AI {
case "kimi":
model := "moonshot-v1-8k"
aiurl := "https://api.moonshot.cn/v1/chat/completions"
apikey := config.GetConfig().APIKeys.Kimi
result, err = aiapis.AIScan(model, aiurl, apikey, reqA, resp1, resp2, statusB) // 调用 kimi 检测是否越权
case "deepseek":
model := "deepseek-chat"
aiurl := "https://api.deepseek.com/v1/chat/completions"
apikey := config.GetConfig().APIKeys.DeepSeek
result, err = aiapis.AIScan(model, aiurl, apikey, reqA, resp1, resp2, statusB) // 调用 kimi 检测是否越权
case "qianwen":
model := "qwen-plus"
aiurl := "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions"
apikey := config.GetConfig().APIKeys.Qianwen
result, err = aiapis.AIScan(model, aiurl, apikey, reqA, resp1, resp2, statusB) // 调用 kimi 检测是否越权
case "hunyuan":
model := "hunyuan-turbo"
aiurl := "https://api.hunyuan.cloud.tencent.com/v1/chat/completions"
apikey := config.GetConfig().APIKeys.HunYuan
result, err = aiapis.AIScan(model, aiurl, apikey, reqA, resp1, resp2, statusB) // 调用 hunyuan 检测是否越权
case "glm":
model := "glm-4-air"
aiurl := "https://open.bigmodel.cn/api/paas/v4/chat/completions"
apikey := config.GetConfig().APIKeys.Glm
result, err = aiapis.AIScan(model, aiurl, apikey, reqA, resp1, resp2, statusB) // 调用 hunyuan 检测是否越权
case "gpt":
model := "gpt-4o"
aiurl := "https://open.bigmodel.cn/api/paas/v4/chat/completions"
apikey := config.GetConfig().APIKeys.Gpt
result, err = aiapis.AIScan(model, aiurl, apikey, reqA, resp1, resp2, statusB) // 调用 hunyuan 检测是否越权
default:
model := "moonshot-v1-8k"
aiurl := "https://api.moonshot.cn/v1/chat/completions"
apikey := config.GetConfig().APIKeys.Kimi
result, err = aiapis.AIScan(model, aiurl, apikey, reqA, resp1, resp2, statusB) // 调用 kimi 检测是否越权
}
if err != nil {
return "", err
}
return result, nil
}