feat: Add Multi-Agent AI Orchestration with 8 specialized agents

- Implement 8 specialized AI agents (XSS, SQLi, Auth, API, Crypto, Secrets, Headers, General)
- Add fast type-based routing for finding classification
- Include OWASP-aligned knowledge bases per agent
- Add agent handoff logic for cross-vulnerability detection
- Optimize timeouts and parallelism for local LLM
- Add new modules: cache, network, fingerprint, secrets, cloud, API, discovery
- Update documentation with multi-agent feature
This commit is contained in:
Vyntral committed 2025-11-21 15:23:11 +01:00
1 parent 45295bb262
commit 812b3530b5
35 files changed
+9490 -341

No files matched your search

+3 -151
View File
@@ -19,44 +19,7 @@ func CheckOpenRedirect(subdomain string, timeout int) bool {
return http.ErrUseLastResponse
},
}
// Common open redirect parameters
testPayloads := []string{
"?url=https://evil.com",
"?redirect=https://evil.com",
"?next=https://evil.com",
"?return=https://evil.com",
"?dest=https://evil.com",
"?destination=https://evil.com",
"?rurl=https://evil.com",
"?target=https://evil.com",
}
baseURLs := []string{
fmt.Sprintf("https://%s", subdomain),
fmt.Sprintf("http://%s", subdomain),
}
for _, baseURL := range baseURLs {
for _, payload := range testPayloads {
testURL := baseURL + payload
resp, err := client.Get(testURL)
if err != nil {
continue
}
resp.Body.Close()
// Check if redirects to evil.com
if resp.StatusCode >= 300 && resp.StatusCode < 400 {
location := resp.Header.Get("Location")
if strings.Contains(location, "evil.com") {
return true
}
}
}
}
return false
return CheckOpenRedirectWithClient(subdomain, client)
}
// CheckCORS tests for CORS misconfiguration
@@ -67,51 +30,7 @@ func CheckCORS(subdomain string, timeout int) string {
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
urls := []string{
fmt.Sprintf("https://%s", subdomain),
fmt.Sprintf("http://%s", subdomain),
}
for _, url := range urls {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
continue
}
// Test with evil origin
req.Header.Set("Origin", "https://evil.com")
resp, err := client.Do(req)
if err != nil {
continue
}
resp.Body.Close()
acao := resp.Header.Get("Access-Control-Allow-Origin")
acac := resp.Header.Get("Access-Control-Allow-Credentials")
// Check for dangerous CORS configs
if acao == "*" {
if acac == "true" {
return "Wildcard + Credentials"
}
return "Wildcard Origin"
}
if acao == "https://evil.com" {
if acac == "true" {
return "Origin Reflection + Credentials"
}
return "Origin Reflection"
}
if strings.Contains(acao, "null") {
return "Null Origin Allowed"
}
}
return ""
return CheckCORSWithClient(subdomain, client)
}
// CheckHTTPMethods tests which HTTP methods are allowed
@@ -122,74 +41,7 @@ func CheckHTTPMethods(subdomain string, timeout int) (allowed []string, dangerou
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
urls := []string{
fmt.Sprintf("https://%s", subdomain),
fmt.Sprintf("http://%s", subdomain),
}
methods := []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "TRACE"}
dangerousMethods := map[string]bool{
"PUT": true,
"DELETE": true,
"TRACE": true,
"PATCH": true,
}
for _, url := range urls {
// First try OPTIONS to get Allow header
req, err := http.NewRequest("OPTIONS", url, nil)
if err != nil {
continue
}
resp, err := client.Do(req)
if err != nil {
continue
}
resp.Body.Close()
// Check Allow header
allowHeader := resp.Header.Get("Allow")
if allowHeader != "" {
for _, method := range strings.Split(allowHeader, ",") {
method = strings.TrimSpace(method)
allowed = append(allowed, method)
if dangerousMethods[method] {
dangerous = append(dangerous, method)
}
}
return allowed, dangerous
}
// If no Allow header, test each method
for _, method := range methods {
req, err := http.NewRequest(method, url, nil)
if err != nil {
continue
}
resp, err := client.Do(req)
if err != nil {
continue
}
resp.Body.Close()
// Method is allowed if not 405 Method Not Allowed
if resp.StatusCode != 405 {
allowed = append(allowed, method)
if dangerousMethods[method] {
dangerous = append(dangerous, method)
}
}
}
if len(allowed) > 0 {
return allowed, dangerous
}
}
return allowed, dangerous
return CheckHTTPMethodsWithClient(subdomain, client)
}
// WithClient versions for parallel execution with shared client
+4 -142
View File
@@ -19,43 +19,7 @@ func CheckAdminPanels(subdomain string, timeout int) []string {
return http.ErrUseLastResponse
},
}
// Generic admin paths (common across all platforms)
// Note: Removed platform-specific paths like /wp-admin, /admin.php, /phpmyadmin
// These generate false positives on non-PHP/WordPress sites
paths := []string{
"/admin", "/administrator",
"/login", "/signin", "/auth",
"/manager", "/console", "/dashboard",
"/admin/login", "/user/login",
}
var found []string
baseURLs := []string{
fmt.Sprintf("https://%s", subdomain),
fmt.Sprintf("http://%s", subdomain),
}
for _, baseURL := range baseURLs {
for _, path := range paths {
testURL := baseURL + path
resp, err := client.Get(testURL)
if err != nil {
continue
}
resp.Body.Close()
// Found if 200, 301, 302, 401, 403 (not 404)
if resp.StatusCode != 404 && resp.StatusCode != 0 {
found = append(found, path)
}
}
if len(found) > 0 {
break
}
}
return found
return CheckAdminPanelsWithClient(subdomain, client)
}
// CheckGitSvnExposure checks for exposed .git or .svn directories
@@ -66,38 +30,7 @@ func CheckGitSvnExposure(subdomain string, timeout int) (gitExposed bool, svnExp
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
baseURLs := []string{
fmt.Sprintf("https://%s", subdomain),
fmt.Sprintf("http://%s", subdomain),
}
for _, baseURL := range baseURLs {
// Check .git
resp, err := client.Get(baseURL + "/.git/config")
if err == nil {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1000))
resp.Body.Close()
if resp.StatusCode == 200 && strings.Contains(string(body), "[core]") {
gitExposed = true
}
}
// Check .svn
resp, err = client.Get(baseURL + "/.svn/entries")
if err == nil {
resp.Body.Close()
if resp.StatusCode == 200 {
svnExposed = true
}
}
if gitExposed || svnExposed {
break
}
}
return gitExposed, svnExposed
return CheckGitSvnExposureWithClient(subdomain, client)
}
// CheckBackupFiles checks for common backup files
@@ -108,41 +41,7 @@ func CheckBackupFiles(subdomain string, timeout int) []string {
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
// Common backup file patterns
paths := []string{
"/backup.zip", "/backup.tar.gz", "/backup.sql",
"/db.sql", "/database.sql", "/dump.sql",
"/site.zip", "/www.zip", "/public.zip",
"/config.bak", "/config.old", "/.env.bak",
"/index.php.bak", "/index.php.old", "/index.html.bak",
"/web.config.bak", "/.htaccess.bak",
}
var found []string
baseURLs := []string{
fmt.Sprintf("https://%s", subdomain),
fmt.Sprintf("http://%s", subdomain),
}
for _, baseURL := range baseURLs {
for _, path := range paths {
resp, err := client.Head(baseURL + path)
if err != nil {
continue
}
resp.Body.Close()
if resp.StatusCode == 200 {
found = append(found, path)
}
}
if len(found) > 0 {
break
}
}
return found
return CheckBackupFilesWithClient(subdomain, client)
}
// CheckAPIEndpoints checks for common API endpoints
@@ -156,44 +55,7 @@ func CheckAPIEndpoints(subdomain string, timeout int) []string {
return http.ErrUseLastResponse
},
}
// Common API endpoint patterns
paths := []string{
"/api", "/api/v1", "/api/v2", "/api/v3",
"/graphql", "/graphiql",
"/swagger", "/swagger-ui", "/swagger.json", "/swagger.yaml",
"/openapi.json", "/openapi.yaml",
"/docs", "/api-docs", "/redoc",
"/health", "/healthz", "/status",
"/metrics", "/actuator", "/actuator/health",
"/v1", "/v2", "/rest",
}
var found []string
baseURLs := []string{
fmt.Sprintf("https://%s", subdomain),
fmt.Sprintf("http://%s", subdomain),
}
for _, baseURL := range baseURLs {
for _, path := range paths {
resp, err := client.Get(baseURL + path)
if err != nil {
continue
}
resp.Body.Close()
// Found if not 404
if resp.StatusCode != 404 && resp.StatusCode != 0 {
found = append(found, path)
}
}
if len(found) > 0 {
break
}
}
return found
return CheckAPIEndpointsWithClient(subdomain, client)
}
// WithClient versions for parallel execution