【安全开发】Nuclei源码分析-模板引擎实现(五)
目录
- Nuclei 模板引擎实现详解
- 1. 模板引擎概述
- 2. 核心数据结构
- 2.1 Template 结构体
- 2.2 Executer 接口
- 2.3 Request 接口
- 3. 模板引擎工作流程
- 3.1 模板加载
- 3.2 模板解析
- 3.3 模板编译
- 3.4 模板执行
- 4. 模板引擎特性
- 4.1 多协议支持
- 4.2 工作流支持
- 4.3 变量和表达式
- 4.4 缓存机制
- 5. 核心代码位置
- 6. 总结
Nuclei 模板引擎实现详解
Nuclei 的模板引擎是其核心组件,负责解析、编译和执行 YAML 格式的模板文件。模板引擎使 Nuclei 能够执行各种协议的漏洞扫描任务。本文将详细分析 Nuclei 模板引擎的实现机制。
1. 模板引擎概述
Nuclei 模板引擎主要由以下几个核心组件构成:
- 模板解析器 - 负责将 YAML 文件解析为内存中的数据结构
- 模板编译器 - 将解析后的模板编译为可执行的请求
- 模板执行器 - 执行编译后的模板请求 Executer
- 模板加载器 - 负责模板的加载、过滤和管理
- 协议执行器 - 实现各种协议的具体执行逻辑
2. 核心数据结构
2.1 Template 结构体
Template (nuclei\v2\pkg\templates\templates.go#L24-L74) 结构体是模板引擎的核心数据结构,定义在 v2/pkg/templates/templates.go 文件中:
type Template struct {ID string // 模板唯一标识符Info model.Info // 模板元数据信息RequestsHTTP []*http.Request // HTTP 请求定义RequestsDNS []*dns.Request // DNS 请求定义RequestsFile []*file.Request // 文件请求定义RequestsNetwork []*network.Request // 网络请求定义RequestsHeadless []*headless.Request // 无头浏览器请求定义workflows.Workflow // 内嵌工作流结构CompiledWorkflow *workflows.Workflow // 编译后的工作流SelfContained bool // 是否为自包含请求TotalRequests int // 模板总请求数Executer protocols.Executer // 模板执行器Path string // 模板文件路径
}
type Workflow struct {Workflows []*WorkflowTemplate Options *protocols.ExecuterOptions `yaml:"-"`
}
2.2 Executer 接口
Executer(nuclei\v2\pkg\protocols\protocols.go#L15-L22) 接口定义了模板执行器的行为,位于 v2/pkg/protocols/protocols.go:
type Executer interface {Compile() error // 编译模板Requests() int // 返回请求数量Execute(input string) (bool, error) // 执行模板ExecuteWithResults(input string, callback OutputEventCallback) error
}
2.3 Request 接口
Request (nuclei\v2\pkg\protocols\protocols.go#L47-L62) 接口定义了各种协议请求的行为:
type Request interface {Compile(options *ExecuterOptions) errorRequests() intGetID() stringMatch(data map[string]interface{}, matcher *matchers.Matcher) (bool, []string)Extract(data map[string]interface{}, matcher *extractors.Extractor) map[string]struct{}ExecuteWithResults(input string, dynamicValues, previous output.InternalEvent, callback OutputEventCallback) errorMakeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEventMakeResultEvent(wrapped *output.InternalWrappedEvent) []*output.ResultEventGetCompiledOperators() []*operators.Operators
}
3. 模板引擎工作流程
3.1 模板加载
模板加载过程由 loader.Store (nuclei\v2\pkg\catalog\loader\loader.go#L30-L40) 类负责,主要步骤包括:
- 路径解析 - 根据用户指定的模板路径查找模板文件
- 过滤处理 - 根据标签、作者、严重性等条件过滤模板
- 模板加载 - 使用 LoadTemplates (nuclei\v2\pkg\catalog\loader\loader.go#L162-L187) 方法加载模板
func (store *Store) LoadTemplates(templatesList []string) []*templates.Template {includedTemplates := store.config.Catalog.GetTemplatesPath(templatesList)templatePathMap := store.pathFilter.Match(includedTemplates)loadedTemplates := make([]*templates.Template, 0, len(templatePathMap))for templatePath := range templatePathMap {loaded, err := parsers.LoadTemplate(templatePath, store.tagFilter, nil)if err != nil {gologger.Warning().Msgf("Could not load template %s: %s\n", templatePath, err)}if loaded {parsed, err := templates.Parse(templatePath, store.preprocessor, store.config.ExecutorOptions)if err != nil {gologger.Warning().Msgf("Could not parse template %s: %s\n", templatePath, err)} else if parsed != nil {loadedTemplates = append(loadedTemplates, parsed)}}}return loadedTemplates
}
3.2 模板解析
模板解析由 templates.Parse (nuclei\v2\pkg\templates\compile.go#L33-L141) 函数完成,主要步骤包括:
- 文件读取 - 读取模板 YAML 文件内容
- 预处理 - 应用预处理器处理模板内容
- YAML 解析 - 使用 yaml.Unmarshal 将 YAML 内容解析为 Template 结构体
- 验证检查 - 验证模板必需字段是否完整
func Parse(filePath string, preprocessor Preprocessor, options protocols.ExecuterOptions) (*Template, error) {// 从缓存中获取或创建新模板template := &Template{}// 读取文件内容f, err := os.Open(filePath)if err != nil {return nil, err}defer f.Close()data, err := ioutil.ReadAll(f)if err != nil {return nil, err}// 预处理data = template.expandPreprocessors(data)if preprocessor != nil {data = preprocessor.Process(data)}// YAML 解析if err := yaml.Unmarshal(data, template); err != nil {return nil, err}// 验证必需字段if utils.IsBlank(template.Info.Name) {return nil, errors.New("no template name field provided")}// ... 其他验证return template, nil
}
3.3 模板编译
模板编译过程在 templates.Parse (fnuclei\v2\pkg\templates\compile.go#L33-L141) 函数中完成,主要包括:
- 工作流编译 - 如果模板包含工作流定义,则编译工作流
- 请求编译 - 编译各种协议的请求
- 执行器创建 - 为模板创建相应的执行器【执行器根据协议而不同,事先定义好的】
// 编译工作流
if len(template.Workflows) > 0 {compiled := &template.WorkflowcompileWorkflow(filePath, preprocessor, &options, compiled, options.WorkflowLoader)template.CompiledWorkflow = compiledtemplate.CompiledWorkflow.Options = &options
}// 编译各种协议请求
if len(template.RequestsHTTP) > 0 {// 创建 HTTP 请求执行器template.Executer = executer.NewExecuter(requests, &options)
}// 编译执行器
if template.Executer != nil {if err := template.Executer.Compile(); err != nil {return nil, errors.Wrap(err, "could not compile request")}template.TotalRequests += template.Executer.Requests()
}
3.4 模板执行
模板执行由 executer.Executer (nuclei\v2\pkg\protocols\common\executer\executer.go#L11-L14) 类负责,主要步骤包括:
- 请求执行 - 依次执行模板中的所有请求
- 结果匹配 - 使用匹配器和提取器处理响应
- 结果输出 - 将匹配结果输出到指定位置
func (e *Executer) Execute(input string) (bool, error) {var results booldynamicValues := make(map[string]interface{})previous := make(map[string]interface{})for _, req := range e.requests {req := reqerr := req.ExecuteWithResults(input, dynamicValues, previous, func(event *output.InternalWrappedEvent) {// 处理请求结果if event.OperatorsResult == nil {return}for _, result := range event.Results {if e.options.IssuesClient != nil {// 发送结果到问题跟踪系统}results = true_ = e.options.Output.Write(result)e.options.Progress.IncrementMatched()}})// 错误处理}return results, nil
}
4. 模板引擎特性
4.1 多协议支持
Nuclei 模板引擎支持多种协议:
- HTTP/HTTPS - Web 应用漏洞检测
- DNS - DNS 相关漏洞检测
- Network - 网络协议漏洞检测
- File - 文件系统相关检测
- Headless - 无头浏览器自动化检测
4.2 工作流支持
模板引擎支持复杂的工作流定义,可以编排多个模板的执行顺序和条件:
workflows:- template: technologies/tech-detect.yamlsubtemplates:- template: vulnerabilities/specific-vuln.yaml
4.3 变量和表达式
模板引擎支持丰富的变量系统和 DSL 表达式:
- 内置变量:{{BaseURL}}、{{Hostname}} 等
- 自定义变量:通过 -var 参数传递
- 环境变量:通过 -env-vars 启用
4.4 缓存机制
模板引擎使用缓存来提高性能:
var parsedTemplatesCache *cache.Templates
func init() {parsedTemplatesCache = cache.New()
}
5. 核心代码位置
- 模板定义:v2/pkg/templates/templates.go
- 模板解析和编译:v2/pkg/templates/compile.go
- 执行器实现:v2/pkg/protocols/common/executer/executer.go
- 协议请求接口:v2/pkg/protocols/protocols.go
- 模板加载器:v2/pkg/catalog/loader/loader.go
- 模板解析器:v2/pkg/parsers/parser.go
6. 总结
Nuclei 的模板引擎通过清晰的架构设计和模块化实现,提供了强大而灵活的漏洞扫描能力。其核心特点包括:
- 模块化设计 - 各组件职责清晰,易于扩展和维护
- 多协议支持 - 支持多种网络协议的漏洞检测
- 灵活的模板系统 - 支持复杂的工作流和条件执行
- 高性能 - 通过缓存和并发执行提高扫描效率
- 易于使用 - 基于 YAML 的模板格式简单直观
by 久违 2025.10.20
