638 lines
13 KiB
Markdown
638 lines
13 KiB
Markdown
# Nuclei Template Development Guide
|
|
|
|
## Table of Contents
|
|
- [Template Structure](#template-structure)
|
|
- [Template Types](#template-types)
|
|
- [Matchers and Extractors](#matchers-and-extractors)
|
|
- [Advanced Techniques](#advanced-techniques)
|
|
- [Testing and Validation](#testing-and-validation)
|
|
- [Best Practices](#best-practices)
|
|
|
|
## Template Structure
|
|
|
|
### Basic Template Anatomy
|
|
|
|
```yaml
|
|
id: unique-template-id
|
|
info:
|
|
name: Human-readable template name
|
|
author: your-name
|
|
severity: critical|high|medium|low|info
|
|
description: Detailed description of what this template detects
|
|
reference:
|
|
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-XXXXX
|
|
- https://nvd.nist.gov/vuln/detail/CVE-2024-XXXXX
|
|
tags: cve,owasp,misconfig,custom
|
|
|
|
# Template type: http, dns, network, file, etc.
|
|
http:
|
|
- method: GET
|
|
path:
|
|
- "{{BaseURL}}/vulnerable-endpoint"
|
|
|
|
matchers:
|
|
- type: status
|
|
status:
|
|
- 200
|
|
|
|
- type: word
|
|
words:
|
|
- "vulnerable signature"
|
|
```
|
|
|
|
### Required Fields
|
|
|
|
- **id**: Unique identifier (kebab-case, organization-scoped for custom templates)
|
|
- **info.name**: Clear, descriptive name
|
|
- **info.author**: Template author
|
|
- **info.severity**: One of: critical, high, medium, low, info
|
|
- **info.description**: What vulnerability this detects
|
|
- **info.tags**: Searchable tags for filtering
|
|
|
|
### Optional but Recommended Fields
|
|
|
|
- **info.reference**: Links to CVE, advisories, documentation
|
|
- **info.classification**: CWE, CVE, OWASP mappings
|
|
- **info.metadata**: Additional metadata (max-request, verified, etc.)
|
|
|
|
## Template Types
|
|
|
|
### HTTP Templates
|
|
|
|
Most common template type for web application testing:
|
|
|
|
```yaml
|
|
id: http-example
|
|
info:
|
|
name: HTTP Template Example
|
|
author: security-team
|
|
severity: high
|
|
tags: web,http
|
|
|
|
http:
|
|
- method: GET
|
|
path:
|
|
- "{{BaseURL}}/api/users"
|
|
- "{{BaseURL}}/api/admin"
|
|
|
|
headers:
|
|
Authorization: "Bearer {{token}}"
|
|
|
|
matchers-condition: and
|
|
matchers:
|
|
- type: status
|
|
status:
|
|
- 200
|
|
|
|
- type: word
|
|
part: body
|
|
words:
|
|
- "\"role\":\"admin\""
|
|
- "sensitive_data"
|
|
|
|
extractors:
|
|
- type: regex
|
|
name: user_ids
|
|
regex:
|
|
- '"id":([0-9]+)'
|
|
```
|
|
|
|
### DNS Templates
|
|
|
|
Test for DNS misconfigurations and subdomain takeovers:
|
|
|
|
```yaml
|
|
id: dns-takeover-check
|
|
info:
|
|
name: DNS Subdomain Takeover Detection
|
|
author: security-team
|
|
severity: high
|
|
tags: dns,takeover
|
|
|
|
dns:
|
|
- name: "{{FQDN}}"
|
|
type: CNAME
|
|
|
|
matchers:
|
|
- type: word
|
|
words:
|
|
- "amazonaws.com"
|
|
- "azurewebsites.net"
|
|
- "herokuapp.com"
|
|
```
|
|
|
|
### Network Templates
|
|
|
|
TCP/UDP port scanning and service detection:
|
|
|
|
```yaml
|
|
id: exposed-redis
|
|
info:
|
|
name: Exposed Redis Instance
|
|
author: security-team
|
|
severity: critical
|
|
tags: network,redis,exposure
|
|
|
|
network:
|
|
- inputs:
|
|
- data: "*1\r\n$4\r\ninfo\r\n"
|
|
|
|
host:
|
|
- "{{Hostname}}"
|
|
- "{{Hostname}}:6379"
|
|
|
|
matchers:
|
|
- type: word
|
|
words:
|
|
- "redis_version"
|
|
```
|
|
|
|
## Matchers and Extractors
|
|
|
|
### Matcher Types
|
|
|
|
#### Status Matcher
|
|
```yaml
|
|
matchers:
|
|
- type: status
|
|
status:
|
|
- 200
|
|
- 201
|
|
condition: or
|
|
```
|
|
|
|
#### Word Matcher
|
|
```yaml
|
|
matchers:
|
|
- type: word
|
|
part: body # body, header, all
|
|
words:
|
|
- "error"
|
|
- "exception"
|
|
condition: and
|
|
case-insensitive: true
|
|
```
|
|
|
|
#### Regex Matcher
|
|
```yaml
|
|
matchers:
|
|
- type: regex
|
|
regex:
|
|
- "(?i)password\\s*=\\s*['\"]([^'\"]+)['\"]"
|
|
part: body
|
|
```
|
|
|
|
#### Binary Matcher
|
|
```yaml
|
|
matchers:
|
|
- type: binary
|
|
binary:
|
|
- "504B0304" # ZIP file signature (hex)
|
|
part: body
|
|
```
|
|
|
|
#### DSL Matcher (Dynamic Expressions)
|
|
```yaml
|
|
matchers:
|
|
- type: dsl
|
|
dsl:
|
|
- "status_code == 200 && len(body) > 1000"
|
|
- "contains(tolower(body), 'admin')"
|
|
```
|
|
|
|
### Matcher Conditions
|
|
|
|
- **and**: All matchers must match
|
|
- **or**: At least one matcher must match (default)
|
|
|
|
```yaml
|
|
matchers-condition: and
|
|
matchers:
|
|
- type: status
|
|
status:
|
|
- 200
|
|
- type: word
|
|
words:
|
|
- "admin"
|
|
```
|
|
|
|
### Extractors
|
|
|
|
Extract data from responses for reporting or chaining:
|
|
|
|
#### Regex Extractor
|
|
```yaml
|
|
extractors:
|
|
- type: regex
|
|
name: api_keys
|
|
part: body
|
|
regex:
|
|
- 'api[_-]?key["\s:=]+([a-zA-Z0-9_-]{32,})'
|
|
group: 1
|
|
```
|
|
|
|
#### JSON Extractor
|
|
```yaml
|
|
extractors:
|
|
- type: json
|
|
name: user_data
|
|
json:
|
|
- ".users[].email"
|
|
- ".users[].id"
|
|
```
|
|
|
|
#### XPath Extractor
|
|
```yaml
|
|
extractors:
|
|
- type: xpath
|
|
name: titles
|
|
xpath:
|
|
- "//title"
|
|
```
|
|
|
|
## Advanced Techniques
|
|
|
|
### Request Chaining (Workflows)
|
|
|
|
Execute templates in sequence, passing data between them:
|
|
|
|
```yaml
|
|
id: workflow-example
|
|
info:
|
|
name: Multi-Step Authentication Test
|
|
author: security-team
|
|
|
|
workflows:
|
|
templates:
|
|
- template: login.yaml
|
|
- template: fetch-user-data.yaml
|
|
```
|
|
|
|
**login.yaml**:
|
|
```yaml
|
|
id: login-template
|
|
info:
|
|
name: Login and Extract Token
|
|
author: security-team
|
|
severity: info
|
|
|
|
http:
|
|
- method: POST
|
|
path:
|
|
- "{{BaseURL}}/api/login"
|
|
|
|
body: '{"username":"test","password":"test"}'
|
|
|
|
extractors:
|
|
- type: json
|
|
name: auth_token
|
|
json:
|
|
- ".token"
|
|
internal: true # Pass to next template
|
|
```
|
|
|
|
### Variables and Helpers
|
|
|
|
Use dynamic variables and helper functions:
|
|
|
|
```yaml
|
|
http:
|
|
- method: GET
|
|
path:
|
|
- "{{BaseURL}}/api/users/{{username}}"
|
|
|
|
# Available variables:
|
|
# {{BaseURL}}, {{Hostname}}, {{Host}}, {{Port}}, {{Path}}
|
|
# {{RootURL}}, {{Scheme}}, {{username}} (from previous extractor)
|
|
|
|
matchers:
|
|
- type: dsl
|
|
dsl:
|
|
# Helper functions: len(), contains(), regex_match(), etc.
|
|
- 'len(body) > 500'
|
|
- 'contains(tolower(header), "x-api-key")'
|
|
- 'status_code >= 200 && status_code < 300'
|
|
```
|
|
|
|
### Payloads and Fuzzing
|
|
|
|
Use payload files for fuzzing:
|
|
|
|
```yaml
|
|
id: sqli-fuzzing
|
|
info:
|
|
name: SQL Injection Fuzzing
|
|
author: security-team
|
|
severity: critical
|
|
|
|
http:
|
|
- method: GET
|
|
path:
|
|
- "{{BaseURL}}/api/users?id={{payload}}"
|
|
|
|
payloads:
|
|
payload:
|
|
- "1' OR '1'='1"
|
|
- "1' UNION SELECT NULL--"
|
|
- "'; DROP TABLE users--"
|
|
|
|
matchers:
|
|
- type: word
|
|
words:
|
|
- "SQL syntax"
|
|
- "mysql_fetch"
|
|
- "ORA-01756"
|
|
```
|
|
|
|
Or use external payload file:
|
|
|
|
```yaml
|
|
payloads:
|
|
payload: payloads/sql-injection.txt
|
|
|
|
attack: clusterbomb # pitchfork, clusterbomb, batteringram
|
|
```
|
|
|
|
### Rate Limiting and Threads
|
|
|
|
Control request rate to avoid overwhelming targets:
|
|
|
|
```yaml
|
|
id: rate-limited-scan
|
|
info:
|
|
name: Rate-Limited Vulnerability Scan
|
|
author: security-team
|
|
severity: medium
|
|
metadata:
|
|
max-request: 50 # Maximum requests per template execution
|
|
|
|
http:
|
|
- method: GET
|
|
path:
|
|
- "{{BaseURL}}/api/endpoint"
|
|
|
|
threads: 5 # Concurrent requests (default: 25)
|
|
```
|
|
|
|
## Testing and Validation
|
|
|
|
### Local Testing
|
|
|
|
Test templates against local test servers:
|
|
|
|
```bash
|
|
# Test single template
|
|
nuclei -t custom-templates/my-template.yaml -u http://localhost:8080 -debug
|
|
|
|
# Validate template syntax
|
|
nuclei -t custom-templates/my-template.yaml -validate
|
|
|
|
# Test with verbose output
|
|
nuclei -t custom-templates/my-template.yaml -u https://target.com -verbose
|
|
```
|
|
|
|
### Template Validation
|
|
|
|
Use the bundled validation script:
|
|
|
|
```bash
|
|
python3 scripts/template_validator.py custom-templates/my-template.yaml
|
|
```
|
|
|
|
### Test Lab Setup
|
|
|
|
Create a vulnerable test application for template development:
|
|
|
|
```bash
|
|
# Use DVWA (Damn Vulnerable Web Application)
|
|
docker run -d -p 80:80 vulnerables/web-dvwa
|
|
|
|
# Or OWASP Juice Shop
|
|
docker run -d -p 3000:3000 bkimminich/juice-shop
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### 1. Accurate Severity Classification
|
|
|
|
- **Critical**: RCE, authentication bypass, full system compromise
|
|
- **High**: SQL injection, XSS, significant data exposure
|
|
- **Medium**: Missing security headers, information disclosure
|
|
- **Low**: Minor misconfigurations, best practice violations
|
|
- **Info**: Technology detection, non-security findings
|
|
|
|
### 2. Minimize False Positives
|
|
|
|
```yaml
|
|
# Use multiple matchers with AND condition
|
|
matchers-condition: and
|
|
matchers:
|
|
- type: status
|
|
status:
|
|
- 200
|
|
|
|
- type: word
|
|
words:
|
|
- "admin"
|
|
- "dashboard"
|
|
condition: and
|
|
|
|
- type: regex
|
|
regex:
|
|
- '<title>.*Admin.*Panel.*</title>'
|
|
case-insensitive: true
|
|
```
|
|
|
|
### 3. Clear Naming Conventions
|
|
|
|
- **id**: `organization-vulnerability-type-identifier`
|
|
- Example: `acme-api-key-exposure-config`
|
|
- **name**: Descriptive, clear purpose
|
|
- Example: "ACME Corp API Key Exposure in Config Endpoint"
|
|
|
|
### 4. Comprehensive Documentation
|
|
|
|
```yaml
|
|
info:
|
|
name: Detailed Template Name
|
|
description: |
|
|
Comprehensive description of what this template detects,
|
|
why it's important, and how it works.
|
|
|
|
References:
|
|
- CVE-2024-XXXXX
|
|
- Internal ticket: SEC-1234
|
|
|
|
reference:
|
|
- https://nvd.nist.gov/vuln/detail/CVE-2024-XXXXX
|
|
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-XXXXX
|
|
|
|
classification:
|
|
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
|
|
cvss-score: 9.8
|
|
cve-id: CVE-2024-XXXXX
|
|
cwe-id: CWE-89
|
|
|
|
metadata:
|
|
verified: true
|
|
max-request: 10
|
|
shodan-query: 'http.title:"Admin Panel"'
|
|
|
|
tags: cve,owasp,sqli,high-severity,verified
|
|
```
|
|
|
|
### 5. Responsible Testing Parameters
|
|
|
|
```yaml
|
|
# Avoid aggressive fuzzing in default templates
|
|
info:
|
|
metadata:
|
|
max-request: 10 # Limit total requests
|
|
|
|
http:
|
|
- method: GET
|
|
threads: 5 # Limit concurrent requests
|
|
|
|
# Use specific, targeted payloads
|
|
payloads:
|
|
test: ["safe-payload-1", "safe-payload-2"]
|
|
```
|
|
|
|
### 6. Error Handling
|
|
|
|
```yaml
|
|
http:
|
|
- method: GET
|
|
path:
|
|
- "{{BaseURL}}/api/test"
|
|
|
|
# Handle various response scenarios
|
|
matchers:
|
|
- type: dsl
|
|
dsl:
|
|
- "status_code == 200 && contains(body, 'vulnerable')"
|
|
- "status_code == 500 && contains(body, 'error')"
|
|
condition: or
|
|
|
|
# Negative matchers (must NOT match)
|
|
matchers:
|
|
- type: word
|
|
negative: true
|
|
words:
|
|
- "404 Not Found"
|
|
- "403 Forbidden"
|
|
```
|
|
|
|
### 7. Template Organization
|
|
|
|
```
|
|
custom-templates/
|
|
├── api/
|
|
│ ├── api-key-exposure.yaml
|
|
│ ├── graphql-introspection.yaml
|
|
│ └── rest-api-misconfig.yaml
|
|
├── cves/
|
|
│ ├── 2024/
|
|
│ │ ├── CVE-2024-12345.yaml
|
|
│ │ └── CVE-2024-67890.yaml
|
|
├── exposures/
|
|
│ ├── sensitive-files.yaml
|
|
│ └── backup-exposure.yaml
|
|
└── misconfig/
|
|
├── cors-misconfiguration.yaml
|
|
└── debug-mode-enabled.yaml
|
|
```
|
|
|
|
### 8. Version Control and Maintenance
|
|
|
|
- Use Git to track template changes
|
|
- Tag templates with version numbers in metadata
|
|
- Document changes in template comments
|
|
- Regularly test templates against updated applications
|
|
|
|
```yaml
|
|
info:
|
|
metadata:
|
|
version: 1.2.0
|
|
last-updated: 2024-11-20
|
|
changelog: |
|
|
1.2.0 - Added additional matcher for new vulnerability variant
|
|
1.1.0 - Improved regex pattern to reduce false positives
|
|
1.0.0 - Initial release
|
|
```
|
|
|
|
## Example: Complete Custom Template
|
|
|
|
```yaml
|
|
id: acme-corp-api-debug-exposure
|
|
info:
|
|
name: ACME Corp API Debug Endpoint Exposure
|
|
author: acme-security-team
|
|
severity: high
|
|
description: |
|
|
Detects exposed debug endpoint in ACME Corp API that leaks
|
|
sensitive configuration including database credentials,
|
|
API keys, and internal service URLs.
|
|
|
|
reference:
|
|
- https://internal-wiki.acme.com/security/SEC-1234
|
|
|
|
classification:
|
|
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
|
|
cvss-score: 7.5
|
|
cwe-id: CWE-200
|
|
|
|
metadata:
|
|
verified: true
|
|
max-request: 3
|
|
version: 1.0.0
|
|
|
|
tags: acme,api,exposure,debug,high-severity
|
|
|
|
http:
|
|
- method: GET
|
|
path:
|
|
- "{{BaseURL}}/api/v1/debug/config"
|
|
- "{{BaseURL}}/api/v2/debug/config"
|
|
- "{{BaseURL}}/debug/config"
|
|
|
|
matchers-condition: and
|
|
matchers:
|
|
- type: status
|
|
status:
|
|
- 200
|
|
|
|
- type: word
|
|
part: body
|
|
words:
|
|
- "database_url"
|
|
- "api_secret_key"
|
|
condition: or
|
|
|
|
- type: regex
|
|
part: body
|
|
regex:
|
|
- '"(password|secret|token)":\s*"[^"]+"'
|
|
|
|
extractors:
|
|
- type: regex
|
|
name: exposed_secrets
|
|
part: body
|
|
regex:
|
|
- '"(database_url|api_secret_key|jwt_secret)":\s*"([^"]+)"'
|
|
group: 2
|
|
|
|
- type: json
|
|
name: config_data
|
|
json:
|
|
- ".database_url"
|
|
- ".api_secret_key"
|
|
```
|
|
|
|
## Resources
|
|
|
|
- [Official Nuclei Template Guide](https://docs.projectdiscovery.io/templates/introduction)
|
|
- [Nuclei Templates Repository](https://github.com/projectdiscovery/nuclei-templates)
|
|
- [Template Editor](https://templates.nuclei.sh/)
|
|
- [DSL Functions Reference](https://docs.projectdiscovery.io/templates/reference/matchers#dsl-matcher)
|