mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-18 04:45:27 +00:00
Merge branch 'dev' of https://github.com/projectdiscovery/nuclei into cloud-templates-targets-sync
This commit is contained in:
commit
a3e3c1cf3d
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@ -22,7 +22,7 @@ updates:
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "v2/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: "weekly"
|
||||
target-branch: "dev"
|
||||
commit-message:
|
||||
prefix: "chore"
|
||||
|
||||
21
.github/workflows/build-test.yml
vendored
21
.github/workflows/build-test.yml
vendored
@ -1,25 +1,36 @@
|
||||
name: 🔨 Build Test
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
- 'v2/'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Test Builds
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
go-version: [1.18.x, 1.19.x]
|
||||
os: [ubuntu-latest, windows-latest, macOS-12]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Go Mod hygine
|
||||
run: |
|
||||
go clean -modcache
|
||||
go mod tidy
|
||||
working-directory: v2/
|
||||
|
||||
- name: Build
|
||||
run: go build .
|
||||
working-directory: v2/cmd/nuclei/
|
||||
@ -41,5 +52,5 @@ jobs:
|
||||
|
||||
- name: Race Condition Tests
|
||||
if: ${{ matrix.os != 'windows-latest' }} # known issue: https://github.com/golang/go/issues/46099
|
||||
run: go run -race . -u scanme.sh
|
||||
run: go run -race . -l ../functional-test/targets.txt -id tech-detect,tls-version
|
||||
working-directory: v2/cmd/nuclei/
|
||||
3
.github/workflows/codeql-analysis.yml
vendored
3
.github/workflows/codeql-analysis.yml
vendored
@ -1,11 +1,8 @@
|
||||
name: 🚨 CodeQL Analysis
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
|
||||
4
.github/workflows/dockerhub-push.yml
vendored
4
.github/workflows/dockerhub-push.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
- name: Get Github tag
|
||||
id: meta
|
||||
run: |
|
||||
echo "::set-output name=tag::$(curl --silent "https://api.github.com/repos/projectdiscovery/nuclei/releases/latest" | jq -r .tag_name)"
|
||||
curl --silent "https://api.github.com/repos/projectdiscovery/nuclei/releases/latest" | jq -r .tag_name | xargs -I {} echo TAG={} >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
@ -37,4 +37,4 @@ jobs:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: projectdiscovery/nuclei:latest,projectdiscovery/nuclei:${{ steps.meta.outputs.tag }}
|
||||
tags: projectdiscovery/nuclei:latest,projectdiscovery/nuclei:${{ steps.meta.outputs.TAG }}
|
||||
4
.github/workflows/functional-test.yml
vendored
4
.github/workflows/functional-test.yml
vendored
@ -1,7 +1,9 @@
|
||||
name: 🧪 Functional Test
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
|
||||
3
.github/workflows/lint-test.yml
vendored
3
.github/workflows/lint-test.yml
vendored
@ -1,7 +1,6 @@
|
||||
name: 🙏🏻 Lint Test
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
@ -17,7 +16,7 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3.3.0
|
||||
uses: golangci/golangci-lint-action@v3.3.1
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout 5m
|
||||
|
||||
14
.github/workflows/publish-docs.yaml
vendored
14
.github/workflows/publish-docs.yaml
vendored
@ -1,9 +1,9 @@
|
||||
name: ⏰ Publish Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@ -13,7 +13,7 @@ jobs:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.head_ref }}
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@ -32,11 +32,11 @@ jobs:
|
||||
go generate pkg/templates/templates.go
|
||||
go build -o "cmd/docgen/docgen" cmd/docgen/docgen.go
|
||||
./cmd/docgen/docgen ../SYNTAX-REFERENCE.md ../nuclei-jsonschema.json
|
||||
echo "::set-output name=changes::$(git status -s | wc -l)"
|
||||
git status -s | wc -l | xargs -I {} echo CHANGES={} >> $GITHUB_OUTPUT
|
||||
working-directory: v2
|
||||
|
||||
- name: Commit files
|
||||
if: steps.generate-docs.outputs.changes > 0
|
||||
if: steps.generate-docs.outputs.CHANGES > 0
|
||||
run: |
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
@ -44,7 +44,7 @@ jobs:
|
||||
git commit -m "Auto Generate Syntax Docs + JSONSchema [$(date)] :robot:" -a
|
||||
|
||||
- name: Push changes
|
||||
if: steps.generate-docs.outputs.changes > 0
|
||||
if: steps.generate-docs.outputs.CHANGES > 0
|
||||
uses: ad-m/github-push-action@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
1
.github/workflows/release-binary.yml
vendored
1
.github/workflows/release-binary.yml
vendored
@ -1,4 +1,5 @@
|
||||
name: 🎉 Release Binary
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
|
||||
8
.github/workflows/sonarcloud.yml
vendored
8
.github/workflows/sonarcloud.yml
vendored
@ -1,11 +1,9 @@
|
||||
name: 👮🏼♂️ Sonarcloud
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- '**.go'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
23
.github/workflows/template-validate.yml
vendored
23
.github/workflows/template-validate.yml
vendored
@ -1,6 +1,10 @@
|
||||
name: 🛠 Template Validate
|
||||
|
||||
on: [ push, pull_request ]
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -11,18 +15,9 @@ jobs:
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Cache Go
|
||||
id: cache-go
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /home/runner/go
|
||||
key: ${{ runner.os }}-go
|
||||
|
||||
- name: Installing Nuclei
|
||||
# if: steps.cache-go.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
go install github.com/projectdiscovery/nuclei/v2/cmd/nuclei@latest
|
||||
- name: Template Validation
|
||||
run: |
|
||||
nuclei -validate
|
||||
nuclei -validate -w ./workflows
|
||||
go run . -ut
|
||||
go run . -validate
|
||||
go run . -validate -w workflows
|
||||
working-directory: v2/cmd/nuclei/
|
||||
15
.run/DSLFunctionsIT.run.xml
Normal file
15
.run/DSLFunctionsIT.run.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="DSLFunctionsIT" type="GoApplicationRunConfiguration" factoryName="Go Application">
|
||||
<module name="nuclei" />
|
||||
<working_directory value="$PROJECT_DIR$/integration_tests" />
|
||||
<envs>
|
||||
<env name="DEBUG" value="true" />
|
||||
<env name="TESTS" value="http/dsl-functions.yaml" />
|
||||
</envs>
|
||||
<kind value="PACKAGE" />
|
||||
<package value="github.com/projectdiscovery/nuclei/v2/cmd/integration-test" />
|
||||
<directory value="$PROJECT_DIR$" />
|
||||
<filePath value="$PROJECT_DIR$/v2/cmd/integration-test/integration-test.go" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
102
DESIGN.md
102
DESIGN.md
@ -261,108 +261,6 @@ engine.SetExecuterOptions(executerOpts)
|
||||
results := engine.ExecuteWithOpts(finalTemplates, r.hmapInputProvider, true)
|
||||
```
|
||||
|
||||
### Using Nuclei From Go Code
|
||||
|
||||
An example of using Nuclei From Go Code to run templates on targets is provided below.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
|
||||
"github.com/projectdiscovery/goflags"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/disk"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/core"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/core/inputs"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/parsers"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/hosterrorscache"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/interactsh"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolinit"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
"github.com/projectdiscovery/ratelimit"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cache := hosterrorscache.New(30, hosterrorscache.DefaultMaxHostsCount)
|
||||
defer cache.Close()
|
||||
|
||||
mockProgress := &testutils.MockProgressClient{}
|
||||
reportingClient, _ := reporting.New(&reporting.Options{}, "")
|
||||
defer reportingClient.Close()
|
||||
|
||||
outputWriter := testutils.NewMockOutputWriter()
|
||||
outputWriter.WriteCallback = func(event *output.ResultEvent) {
|
||||
fmt.Printf("Got Result: %v\n", event)
|
||||
}
|
||||
|
||||
defaultOpts := types.DefaultOptions()
|
||||
protocolstate.Init(defaultOpts)
|
||||
protocolinit.Init(defaultOpts)
|
||||
|
||||
defaultOpts.Templates = goflags.StringSlice{"dns/cname-service.yaml"}
|
||||
defaultOpts.ExcludeTags = config.ReadIgnoreFile().Tags
|
||||
|
||||
interactOpts := interactsh.NewDefaultOptions(outputWriter, reportingClient, mockProgress)
|
||||
interactClient, err := interactsh.New(interactOpts)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create interact client: %s\n", err)
|
||||
}
|
||||
defer interactClient.Close()
|
||||
|
||||
home, _ := os.UserHomeDir()
|
||||
catalog := disk.NewCatalog(path.Join(home, "nuclei-templates"))
|
||||
executerOpts := protocols.ExecuterOptions{
|
||||
Output: outputWriter,
|
||||
Options: defaultOpts,
|
||||
Progress: mockProgress,
|
||||
Catalog: catalog,
|
||||
IssuesClient: reportingClient,
|
||||
RateLimiter: ratelimit.New(context.Background(), 150, time.Second),
|
||||
Interactsh: interactClient,
|
||||
HostErrorsCache: cache,
|
||||
Colorizer: aurora.NewAurora(true),
|
||||
ResumeCfg: types.NewResumeCfg(),
|
||||
}
|
||||
engine := core.New(defaultOpts)
|
||||
engine.SetExecuterOptions(executerOpts)
|
||||
|
||||
workflowLoader, err := parsers.NewLoader(&executerOpts)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create workflow loader: %s\n", err)
|
||||
}
|
||||
executerOpts.WorkflowLoader = workflowLoader
|
||||
|
||||
configObject, err := config.ReadConfiguration()
|
||||
if err != nil {
|
||||
log.Fatalf("Could not read config: %s\n", err)
|
||||
}
|
||||
store, err := loader.New(loader.NewConfig(defaultOpts, configObject, catalog, executerOpts))
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create loader client: %s\n", err)
|
||||
}
|
||||
store.Load()
|
||||
|
||||
input := &inputs.SimpleInputProvider{Inputs: []string{"docs.hackerone.com"}}
|
||||
_ = engine.Execute(store.Templates(), input)
|
||||
engine.WorkPool().Wait() // Wait for the scan to finish
|
||||
}
|
||||
```
|
||||
|
||||
### Adding a New Protocol
|
||||
|
||||
Protocols form the core of Nuclei Engine. All the request types like `http`, `dns`, etc. are implemented in form of protocol requests.
|
||||
|
||||
@ -2,7 +2,7 @@ FROM golang:1.19.3-alpine as build-env
|
||||
RUN apk add build-base
|
||||
RUN go install -v github.com/projectdiscovery/nuclei/v2/cmd/nuclei@latest
|
||||
|
||||
FROM alpine:3.16.2
|
||||
FROM alpine:3.17.0
|
||||
RUN apk add --no-cache bind-tools ca-certificates chromium
|
||||
COPY --from=build-env /go/bin/nuclei /usr/local/bin/nuclei
|
||||
ENTRYPOINT ["nuclei"]
|
||||
|
||||
151
README.md
151
README.md
@ -31,7 +31,8 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/master/README.md">English</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/master/README_CN.md">中文</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/master/README_KR.md">Korean</a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/master/README_KR.md">Korean</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/master/README_ID.md">Indonesia</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
@ -90,13 +91,15 @@ Nuclei is a fast, template based vulnerability scanner focusing
|
||||
on extensive configurability, massive extensibility and ease of use.
|
||||
|
||||
Usage:
|
||||
nuclei [flags]
|
||||
./nuclei [flags]
|
||||
|
||||
Flags:
|
||||
TARGET:
|
||||
-u, -target string[] target URLs/hosts to scan
|
||||
-l, -list string path to file containing a list of target URLs/hosts to scan (one per line)
|
||||
-resume string Resume scan using resume.cfg (clustering will be disabled)
|
||||
-u, -target string[] target URLs/hosts to scan
|
||||
-l, -list string path to file containing a list of target URLs/hosts to scan (one per line)
|
||||
-resume string resume scan using resume.cfg (clustering will be disabled)
|
||||
-sa, -scan-all-ips scan all the IPs associated with dns record
|
||||
-iv, -ip-version string[] IP version to scan of hostname (4,6) - (default 4)
|
||||
|
||||
TEMPLATES:
|
||||
-nt, -new-templates run only new templates added in latest nuclei-templates release
|
||||
@ -107,7 +110,8 @@ TEMPLATES:
|
||||
-w, -workflows string[] list of workflow or workflow directory to run (comma-separated, file)
|
||||
-wu, -workflow-url string[] list of workflow urls to run (comma-separated, file)
|
||||
-validate validate the passed templates to nuclei
|
||||
-nss, -no-strict-syntax Disable strict syntax check on templates
|
||||
-nss, -no-strict-syntax disable strict syntax check on templates
|
||||
-td, -template-display displays the templates content
|
||||
-tl list all available templates
|
||||
|
||||
FILTERING:
|
||||
@ -122,8 +126,8 @@ FILTERING:
|
||||
-em, -exclude-matchers string[] template matchers to exclude in result
|
||||
-s, -severity value[] templates to run based on severity. Possible values: info, low, medium, high, critical, unknown
|
||||
-es, -exclude-severity value[] templates to exclude based on severity. Possible values: info, low, medium, high, critical, unknown
|
||||
-pt, -type value[] templates to run based on protocol type. Possible values: dns, file, http, headless, network, workflow, ssl, websocket, whois
|
||||
-ept, -exclude-type value[] templates to exclude based on protocol type. Possible values: dns, file, http, headless, network, workflow, ssl, websocket, whois
|
||||
-pt, -type value[] templates to run based on protocol type. Possible values: dns, file, http, , headless, network, workflow, ssl, websocket, whois
|
||||
-ept, -exclude-type value[] templates to exclude based on protocol type. Possible values: dns, file, http, , headless, network, workflow, ssl, websocket, whois
|
||||
-tc, -template-condition string[] templates to run based on expression condition
|
||||
|
||||
OUTPUT:
|
||||
@ -135,7 +139,7 @@ OUTPUT:
|
||||
-json write output in JSONL(ines) format
|
||||
-irr, -include-rr include request/response pairs in the JSONL output (for findings only)
|
||||
-nm, -no-meta disable printing result metadata in cli output
|
||||
-nts, -no-timestamp disable printing timestamp in cli output
|
||||
-ts, -timestamp enable printing timestamp in cli output
|
||||
-rdb, -report-db string nuclei reporting database (always use this to persist report data)
|
||||
-ms, -matcher-status display match failure status
|
||||
-me, -markdown-export string directory to export results in markdown format
|
||||
@ -153,6 +157,7 @@ CONFIGURATIONS:
|
||||
-r, -resolvers string file containing resolver list for nuclei
|
||||
-sr, -system-resolvers use system DNS resolving as error fallback
|
||||
-passive enable passive HTTP response processing mode
|
||||
-fh2, -force-http2 force http2 connection on requests
|
||||
-ev, -env-vars enable environment variables to be used in template
|
||||
-cc, -client-cert string client certificate file (PEM-encoded) used for authenticating against scanned hosts
|
||||
-ck, -client-key string client key file (PEM-encoded) used for authenticating against scanned hosts
|
||||
@ -160,12 +165,13 @@ CONFIGURATIONS:
|
||||
-sml, -show-match-line show match lines for file templates, works with extractors only
|
||||
-ztls use ztls library with autofallback to standard one for tls13
|
||||
-sni string tls sni hostname to use (default: input domain name)
|
||||
-sandbox sandbox nuclei for safe templates execution
|
||||
-i, -interface string network interface to use for network scan
|
||||
-at, -attack-type string type of payload combinations to perform (batteringram,pitchfork,clusterbomb)
|
||||
-sip, -source-ip string source ip address to use for network scan
|
||||
-config-directory string Override the default config path ($home/.config)
|
||||
-config-directory string override the default config path ($home/.config)
|
||||
-rsr, -response-size-read int max response size to read in bytes (default 10485760)
|
||||
-rss, -response-size-save int max response size to read in bytes (default 1048576)
|
||||
-rss, -response-size-save int max response size to save in bytes (default 10485760)
|
||||
|
||||
INTERACTSH:
|
||||
-iserver, -interactsh-server string interactsh server url for self-hosted instance (default: oast.pro,oast.live,oast.site,oast.online,oast.fun,oast.me)
|
||||
@ -176,6 +182,14 @@ INTERACTSH:
|
||||
-interactions-cooldown-period int extra time for interaction polling before exiting (default 5)
|
||||
-ni, -no-interactsh disable interactsh server for OAST testing, exclude OAST based templates
|
||||
|
||||
UNCOVER:
|
||||
-uc, -uncover enable uncover engine
|
||||
-uq, -uncover-query string[] uncover search query
|
||||
-ue, -uncover-engine string[] uncover search engine (shodan,shodan-idb,fofa,censys,quake,hunter,zoomeye,netlas) (default shodan)
|
||||
-uf, -uncover-field string uncover fields to return (ip,port,host) (default "ip:port")
|
||||
-ul, -uncover-limit int uncover results to return (default 100)
|
||||
-ucd, -uncover-delay int delay between uncover query requests in seconds (0 to disable) (default 1)
|
||||
|
||||
RATE-LIMIT:
|
||||
-rl, -rate-limit int maximum number of requests to send per second (default 150)
|
||||
-rlm, -rate-limit-minute int maximum number of requests to send per minute
|
||||
@ -187,20 +201,21 @@ RATE-LIMIT:
|
||||
OPTIMIZATIONS:
|
||||
-timeout int time to wait in seconds before timeout (default 10)
|
||||
-retries int number of times to retry a failed request (default 1)
|
||||
-ldp, -leave-default-ports leave default HTTP/HTTPS ports (eg. host:80,host:443
|
||||
-ldp, -leave-default-ports leave default HTTP/HTTPS ports (eg. host:80,host:443)
|
||||
-mhe, -max-host-error int max errors for a host before skipping from scan (default 30)
|
||||
-project use a project folder to avoid sending same request multiple times
|
||||
-project-path string set a specific project path
|
||||
-spm, -stop-at-first-path stop processing HTTP requests after the first match (may break template/workflow logic)
|
||||
-spm, -stop-at-first-match stop processing HTTP requests after the first match (may break template/workflow logic)
|
||||
-stream stream mode - start elaborating without sorting the input
|
||||
-irt, -input-read-timeout duration timeout on input read (default 3m0s)
|
||||
-no-stdin Disable Stdin processing
|
||||
-nh, -no-httpx disable httpx probing for non-url input
|
||||
-no-stdin disable stdin processing
|
||||
|
||||
HEADLESS:
|
||||
-headless enable templates that require headless browser support (root user on linux will disable sandbox)
|
||||
-headless enable templates that require headless browser support (root user on Linux will disable sandbox)
|
||||
-page-timeout int seconds to wait for each page in headless mode (default 20)
|
||||
-sb, -show-browser show the browser on the screen when running templates with headless mode
|
||||
-sc, -system-chrome Use local installed chrome browser instead of nuclei installed
|
||||
-sc, -system-chrome use local installed Chrome browser instead of nuclei installed
|
||||
-lha, -list-headless-action list available headless actions
|
||||
|
||||
DEBUG:
|
||||
@ -217,6 +232,7 @@ DEBUG:
|
||||
-v, -verbose show verbose output
|
||||
-profile-mem string optional nuclei memory profile dump file
|
||||
-vv display templates loaded for scan
|
||||
-svd, -show-var-dump show variables dump for debugging
|
||||
-ep, -enable-pprof enable pprof debugging server
|
||||
-tv, -templates-version shows the version of the installed nuclei-templates
|
||||
-hc, -health-check run diagnostic check up
|
||||
@ -332,6 +348,109 @@ We have [a discussion thread around this](https://github.com/projectdiscovery/nu
|
||||
<a href="https://github.com/projectdiscovery/nuclei-action"><img src="static/learn-more-button.png" width="170px" alt="Learn More"></a>
|
||||
</h1>
|
||||
|
||||
### Using Nuclei From Go Code
|
||||
|
||||
An example of using Nuclei From Go Code to run templates on targets is provided below.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
|
||||
"github.com/projectdiscovery/goflags"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/disk"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/core"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/core/inputs"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/parsers"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/hosterrorscache"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/interactsh"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolinit"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
"github.com/projectdiscovery/ratelimit"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cache := hosterrorscache.New(30, hosterrorscache.DefaultMaxHostsCount)
|
||||
defer cache.Close()
|
||||
|
||||
mockProgress := &testutils.MockProgressClient{}
|
||||
reportingClient, _ := reporting.New(&reporting.Options{}, "")
|
||||
defer reportingClient.Close()
|
||||
|
||||
outputWriter := testutils.NewMockOutputWriter()
|
||||
outputWriter.WriteCallback = func(event *output.ResultEvent) {
|
||||
fmt.Printf("Got Result: %v\n", event)
|
||||
}
|
||||
|
||||
defaultOpts := types.DefaultOptions()
|
||||
protocolstate.Init(defaultOpts)
|
||||
protocolinit.Init(defaultOpts)
|
||||
|
||||
defaultOpts.Templates = goflags.StringSlice{"dns/cname-service.yaml"}
|
||||
defaultOpts.ExcludeTags = config.ReadIgnoreFile().Tags
|
||||
|
||||
interactOpts := interactsh.NewDefaultOptions(outputWriter, reportingClient, mockProgress)
|
||||
interactClient, err := interactsh.New(interactOpts)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create interact client: %s\n", err)
|
||||
}
|
||||
defer interactClient.Close()
|
||||
|
||||
home, _ := os.UserHomeDir()
|
||||
catalog := disk.NewCatalog(path.Join(home, "nuclei-templates"))
|
||||
executerOpts := protocols.ExecuterOptions{
|
||||
Output: outputWriter,
|
||||
Options: defaultOpts,
|
||||
Progress: mockProgress,
|
||||
Catalog: catalog,
|
||||
IssuesClient: reportingClient,
|
||||
RateLimiter: ratelimit.New(context.Background(), 150, time.Second),
|
||||
Interactsh: interactClient,
|
||||
HostErrorsCache: cache,
|
||||
Colorizer: aurora.NewAurora(true),
|
||||
ResumeCfg: types.NewResumeCfg(),
|
||||
}
|
||||
engine := core.New(defaultOpts)
|
||||
engine.SetExecuterOptions(executerOpts)
|
||||
|
||||
workflowLoader, err := parsers.NewLoader(&executerOpts)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create workflow loader: %s\n", err)
|
||||
}
|
||||
executerOpts.WorkflowLoader = workflowLoader
|
||||
|
||||
configObject, err := config.ReadConfiguration()
|
||||
if err != nil {
|
||||
log.Fatalf("Could not read config: %s\n", err)
|
||||
}
|
||||
store, err := loader.New(loader.NewConfig(defaultOpts, configObject, catalog, executerOpts))
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create loader client: %s\n", err)
|
||||
}
|
||||
store.Load()
|
||||
|
||||
input := &inputs.SimpleInputProvider{Inputs: []string{"docs.hackerone.com"}}
|
||||
_ = engine.Execute(store.Templates(), input)
|
||||
engine.WorkPool().Wait() // Wait for the scan to finish
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Resources
|
||||
|
||||
- [Finding bugs with Nuclei with PinkDraconian (Robbe Van Roey)](https://www.youtube.com/watch?v=ewP0xVPW-Pk) by **[@PinkDraconian](https://twitter.com/PinkDraconian)**
|
||||
|
||||
226
README_CN.md
226
README_CN.md
@ -7,12 +7,14 @@
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://goreportcard.com/report/github.com/projectdiscovery/nuclei"><img src="https://goreportcard.com/badge/github.com/projectdiscovery/nuclei"></a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/issues"><img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat"></a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases"><img src="https://img.shields.io/github/release/projectdiscovery/nuclei"></a>
|
||||
<a href="https://twitter.com/pdnuclei"><img src="https://img.shields.io/twitter/follow/pdnuclei.svg?logo=twitter"></a>
|
||||
<img src="https://img.shields.io/github/go-mod/go-version/projectdiscovery/nuclei?filename=v2%2Fgo.mod">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases"><img src="https://img.shields.io/github/downloads/projectdiscovery/nuclei/total">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/graphs/contributors"><img src="https://img.shields.io/github/contributors-anon/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases/"><img src="https://img.shields.io/github/release/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/issues"><img src="https://img.shields.io/github/issues-raw/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/discussions"><img src="https://img.shields.io/github/discussions/projectdiscovery/nuclei">
|
||||
<a href="https://discord.gg/projectdiscovery"><img src="https://img.shields.io/discord/695645237418131507.svg?logo=discord"></a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/actions/workflows/build-test.yml"><img src="https://github.com/projectdiscovery/nuclei/actions/workflows/build-test.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://twitter.com/pdnuclei"><img src="https://img.shields.io/twitter/follow/pdnuclei.svg?logo=twitter"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@ -28,14 +30,15 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/master/README.md">English</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/master/README_CN.md">中文</a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/master/README_CN.md">中文</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/master/README_KR.md">Korean</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
Nuclei使用零误报的定制模板向目标发送请求,同时可以对大量主机进行快速扫描。Nuclei提供TCP、DNS、HTTP、FILE等各类协议的扫描,通过强大且灵活的模板,可以使用Nuclei模拟各种安全检查。
|
||||
Nuclei使用零误报的定制模板向目标发送请求,同时可以对主机进行批量快速扫描。Nuclei提供TCP、DNS、HTTP、FILE等各类协议的扫描,通过强大且灵活的模板,可以使用Nuclei模拟各种安全检查。
|
||||
|
||||
我们的[模板仓库](https://github.com/projectdiscovery/nuclei-templates)包含**超过200**安全研究员和工程师提供的模板。
|
||||
我们的[模板仓库](https://github.com/projectdiscovery/nuclei-templates)包含**超过300**安全研究员和工程师提供的模板。
|
||||
|
||||
|
||||
|
||||
@ -65,7 +68,7 @@ go install -v github.com/projectdiscovery/nuclei/v2/cmd/nuclei@latest
|
||||
|
||||
自从[v2.5.2]((https://github.com/projectdiscovery/nuclei/releases/tag/v2.5.2))起,Nuclei就内置了自动下载和更新模板的功能。[**Nuclei模板**](https://github.com/projectdiscovery/nuclei-templates)仓库随时更新社区中可用的模板列表。
|
||||
|
||||
您仍然可以随时使用`update-templates`命令更新模板,您可以根据[模板指南](https://nuclei.projectdiscovery.io/templating-guide/)编写您自己的模板。
|
||||
您仍然可以随时使用`update-templates`命令更新模板,您可以根据[模板指南](https://nuclei.projectdiscovery.io/templating-guide/)为您的个人工作流和需求编写模板。
|
||||
|
||||
YAML的语法规范在[这里](SYNTAX-REFERENCE.md)。
|
||||
|
||||
@ -79,10 +82,10 @@ YAML的语法规范在[这里](SYNTAX-REFERENCE.md)。
|
||||
nuclei -h
|
||||
```
|
||||
|
||||
这将显示Nuclei的帮助,以下是所有支持的命令
|
||||
这将显示Nuclei的帮助,以下是所有支持的命令。
|
||||
|
||||
|
||||
```yaml
|
||||
```console
|
||||
Nuclei是一款注重于可配置性、可扩展性和易用性的基于模板的快速漏洞扫描器。
|
||||
|
||||
用法:
|
||||
@ -90,104 +93,146 @@ Nuclei是一款注重于可配置性、可扩展性和易用性的基于模板
|
||||
|
||||
命令:
|
||||
目标:
|
||||
-u, -target string[] 指定扫描的URL/主机
|
||||
-l, -list string 指定需要扫描的URL/主机文件(一行一个)
|
||||
-u, -target string[] 指定扫描的URL/主机
|
||||
-l, -list string 指定需要扫描的URL/主机文件(一行一个)
|
||||
-resume string 断点续扫(将禁用集群)
|
||||
|
||||
模板:
|
||||
-t, -templates string[] 指定需要扫描的模板或者模板的路径
|
||||
-nt, -new-templates 只扫描最新版本中添加的模板
|
||||
-ntv, -new-templates-version 运行在特定版本中添加的新模板
|
||||
-w, -workflows string[] 指定扫描中的工作流或者工作流目录
|
||||
-validate 验证通过的模板
|
||||
-tl 列出所有可用的模板
|
||||
-nt, -new-templates 只扫描最新版本中添加的模板
|
||||
-ntv, -new-templates-version string[] 运行在特定版本中添加的新模板
|
||||
-as, -automatic-scan 在自动web扫描中使用wappalyzer技术检测的指纹
|
||||
-t, -templates string[] 指定需要扫描的模板或者模板的路径(逗号分隔,文件)
|
||||
-tu, -template-url string[] 从URL加载模板(逗号分隔,文件)
|
||||
-w, -workflows string[] 指定扫描中的工作流或者工作流目录(逗号分隔,文件)
|
||||
-wu, -workflow-url string[] 从URL加载工作流(逗号分隔,文件)
|
||||
-validate 验证通过的模板
|
||||
-nss, -no-strict-syntax 禁用模板的严格检查
|
||||
-tl 列出所有可用的模板
|
||||
|
||||
过滤:
|
||||
-tags string[] 执行有标记的模板子集
|
||||
-etags, -exclude-tags string[] 执行标记为排除的模板
|
||||
-itags, -include-tags string[] 不执行具有攻击性的模板
|
||||
-et, -exclude-templates string[] 要排除的模板或者模板目录
|
||||
-it, -include-templates string[] 执行默认或配置中排除的模板
|
||||
-s, -severity value[] 根据严重程度运行模板,可候选的值有:info,low,medium,high,critical
|
||||
-es, -exclude-severity value[] 根据严重程度排除模板,可候选的值有:info,low,medium,high,critical
|
||||
-a, -author string[] 执行指定作者的模板
|
||||
-a, -author string[] 执行指定作者的模板(逗号分隔,文件)
|
||||
-tags string[] 执行有标记的模板子集(逗号分隔,文件)
|
||||
-etags, -exclude-tags string[] 执行标记为排除的模板(逗号分隔,文件)
|
||||
-itags, -include-tags string[] 执行默认或者配置排除的标记模板
|
||||
-id, -template-id string[] 执行指定ID的模板(逗号分隔,文件)
|
||||
-eid, -exclude-id string[] 执行排除指定ID的模板(逗号分隔,文件)
|
||||
-it, -include-templates string[] 执行默认或配置中排除的模板
|
||||
-et, -exclude-templates string[] 要排除的模板或者模板目录(逗号分隔,文件)
|
||||
-em, -exclude-matchers string[] 在结果中排除指定模板
|
||||
-s, -severity value[] 根据严重程度运行模板,可候选的值有:info,low,medium,high,critical
|
||||
-es, -exclude-severity value[] 根据严重程度排除模板,可候选的值有:info,low,medium,high,critical
|
||||
-pt, -type value[] 根据协议运行模板,可候选的值有:dns, file, http, headless, network, workflow, ssl, websocket, whois
|
||||
-ept, -exclude-type value[] 根据协议排除模板,可候选的值有:dns, file, http, headless, network, workflow, ssl, websocket, whois
|
||||
-tc, -template-condition string[] 根据表达式运行模板
|
||||
|
||||
|
||||
输出:
|
||||
-o, -output string 输出发现的问题到文件
|
||||
-silent 只显示结果
|
||||
-nc, -no-color 禁用输出内容着色(ANSI转义码)
|
||||
-json 输出为jsonL(ines)
|
||||
-irr, -include-rr 在JSONL中输出对应的请求和相应(仅结果)
|
||||
-nm, -no-meta 不显示匹配的元数据
|
||||
-nts, -no-timestamp 不在输出中显示时间戳
|
||||
-rdb, -report-db string 本地的Nuclei结果数据库(始终使用该数据库保存结果)
|
||||
-me, -markdown-export string 以markdown导出结果
|
||||
-se, -sarif-export string 以SARIF导出结果
|
||||
-o, -output string 输出发现的问题到文件
|
||||
-sresp, -store-resp 将nuclei的所有请求和响应输出到目录
|
||||
-srd, -store-resp-dir string 将nuclei的所有请求和响应输出到指定目录(默认:output)
|
||||
-silent 只显示结果
|
||||
-nc, -no-color 禁用输出内容着色(ANSI转义码)
|
||||
-json 输出为jsonL(ines)
|
||||
-irr, -include-rr 在JSONL中输出对应的请求和相应(仅结果)
|
||||
-nm, -no-meta 不显示匹配的元数据
|
||||
-nts, -no-timestamp 不在输出中显示时间戳
|
||||
-rdb, -report-db string 本地的Nuclei结果数据库(始终使用该数据库保存结果)
|
||||
-ms, -matcher-status 显示匹配失败状态
|
||||
-me, -markdown-export string 以markdown导出结果
|
||||
-se, -sarif-export string 以SARIF导出结果
|
||||
|
||||
配置:
|
||||
-config string 指定Nuclei的配置文件
|
||||
-rc, -report-config string 指定Nuclei报告模板文件
|
||||
-H, -header string[] 指定报告中的标题:value格式
|
||||
-V, -var value 通过var=value指定var值
|
||||
-r, -resolvers string 指定Nuclei的解析文件
|
||||
-sr, -system-resolvers 当DNS错误时使用系统DNS
|
||||
-passive 启用被动扫描处理HTTP响应
|
||||
-ev, env-vars 在模板中使用环境变量
|
||||
-config string 指定Nuclei的配置文件
|
||||
-fr, -follow-redirects 为HTTP模板启用重定向
|
||||
-fhr, -follow-host-redirects 在同一主机上重定向
|
||||
-mr, -max-redirects int HTTP模板最大重定向次数(默认:10)
|
||||
-dr, -disable-redirects 为HTTP模板禁用重定向
|
||||
-rc, -report-config string 指定Nuclei报告模板文件
|
||||
-H, -header string[] 指定header、cookie,以header:value的方式(cli,文件)
|
||||
-V, -var value 通过key=value指定var值
|
||||
-r, -resolvers string 指定Nuclei的解析文件
|
||||
-sr, -system-resolvers 当DNS错误时使用系统DNS
|
||||
-passive 启用被动扫描处理HTTP响应
|
||||
-ev, env-vars 在模板中使用环境变量
|
||||
-cc, -client-cert string 用于对扫描的主机进行身份验证的客户端证书文件(PEM 编码)
|
||||
-ck, -client-key string 用于对扫描的主机进行身份验证的客户端密钥文件(PEM 编码)
|
||||
-ca, -client-ca string 用于对扫描的主机进行身份验证的客户端证书颁发机构文件(PEM 编码)
|
||||
-sml, -show-match-line 显示文件模板的匹配值,只适用于提取器
|
||||
-ztls 对ztls自动退回到tls13
|
||||
-sni string 指定tls sni的主机名(默认为输入的域名)
|
||||
-i, -interface string 指定网卡
|
||||
-sip, -source-ip string 指定源IP
|
||||
-config-directory string 重写默认配置路径($home/.config)
|
||||
-rsr, -response-size-read int 最大读取响应大小(默认:10485760字节)
|
||||
-rss, -response-size-save int 最大储存响应大小(默认:10485760字节)
|
||||
|
||||
交互:
|
||||
-inserver, -ineractsh-server string 使用interactsh反连检测平台(默认为"https://interact.sh")
|
||||
-itoken, -interactsh-token string 指定反连检测平台的身份凭证
|
||||
-interactions-cache-size int 指定保存在交互缓存中的请求数(默认:5000)
|
||||
-interactions-eviction int 从缓存中删除请求前等待的时间(默认为60秒)
|
||||
-interactions-poll-duration int 每个轮询前等待时间(默认为5秒)
|
||||
-interactions-cooldown-period int 退出轮询前的等待时间(默认为5秒)
|
||||
-ni, -no-interactsh 禁用反连检测平台,同时排除基于反连检测的模板
|
||||
-inserver, -ineractsh-server string 使用interactsh反连检测平台(默认为oast.pro,oast.live,oast.site,oast.online,oast.fun,oast.me)
|
||||
-itoken, -interactsh-token string 指定反连检测平台的身份凭证
|
||||
-interactions-cache-size int 指定保存在交互缓存中的请求数(默认:5000)
|
||||
-interactions-eviction int 从缓存中删除请求前等待的时间(默认为60秒)
|
||||
-interactions-poll-duration int 每个轮询前等待时间(默认为5秒)
|
||||
-interactions-cooldown-period int 退出轮询前的等待时间(默认为5秒)
|
||||
-ni, -no-interactsh 禁用反连检测平台,同时排除基于反连检测的模板
|
||||
|
||||
限速:
|
||||
-rl, -rate-limit int 每秒最大请求量(默认:150)
|
||||
-rlm, -rate-limit-minute int 每分钟最大请求量
|
||||
-bs, -bulk-size int 每个模板最大并行检测数(默认:25)
|
||||
-c, -concurrency int 并行执行的最大模板数量(默认:25)
|
||||
-rl, -rate-limit int 每秒最大请求量(默认:150)
|
||||
-rlm, -rate-limit-minute int 每分钟最大请求量
|
||||
-bs, -bulk-size int 每个模板最大并行检测数(默认:25)
|
||||
-c, -concurrency int 并行执行的最大模板数量(默认:25)
|
||||
-hbs, -headless-bulk-size int 每个模板并行运行的无头主机最大数量(默认:10)
|
||||
-headc, -headless-concurrency int 并行指定无头主机最大数量(默认:10)
|
||||
|
||||
|
||||
优化:
|
||||
-timeout int 超时时间(默认为5秒)
|
||||
-retries int 重试次数(默认:1)
|
||||
-mhe, -max-host-error int 某主机扫描失败次数,跳过该主机(默认:30)
|
||||
-project 使用项目文件夹避免多次发送同一请求
|
||||
-project-path string 设置特定的项目文件夹
|
||||
-spm, -stop-at-first-path 得到一个结果后停止(或许会中断模板和工作流的逻辑)
|
||||
-stream 流模式 - 在不整理输入的情况下详细描述
|
||||
-timeout int 超时时间(默认为10秒)
|
||||
-retries int 重试次数(默认:1)
|
||||
-ldp, -leave-default-ports 指定HTTP/HTTPS默认端口(例如:host:80,host:443)
|
||||
-mhe, -max-host-error int 某主机扫描失败次数,跳过该主机(默认:30)
|
||||
-project 使用项目文件夹避免多次发送同一请求
|
||||
-project-path string 设置特定的项目文件夹
|
||||
-spm, -stop-at-first-path 得到一个结果后停止(或许会中断模板和工作流的逻辑)
|
||||
-stream 流模式 - 在不整理输入的情况下详细描述
|
||||
-irt, -input-read-timeout duration 输入读取超时时间(默认:3分钟)
|
||||
-no-stdin 禁用标准输入
|
||||
|
||||
无界面浏览器:
|
||||
-headless 启用需要无界面浏览器的模板
|
||||
-page-timeout int 在无界面下超时秒数(默认:20)
|
||||
-sb, -show-brower 在无界面浏览器运行模板时,显示浏览器
|
||||
-sc, -system-chrome 不使用Nuclei自带的浏览器,使用本地浏览器
|
||||
-headless 启用需要无界面浏览器的模板
|
||||
-page-timeout int 在无界面下超时秒数(默认:20)
|
||||
-sb, -show-brower 在无界面浏览器运行模板时,显示浏览器
|
||||
-sc, -system-chrome 不使用Nuclei自带的浏览器,使用本地浏览器
|
||||
-lha, -list-headless-action 列出可用的无界面操作
|
||||
|
||||
调试:
|
||||
-debug 显示所有请求和响应
|
||||
-debug-req 显示所有请求
|
||||
-debug-resp 显示所有响应
|
||||
-proxy, -proxy-url string 使用HTTP代理
|
||||
-proxy-socks-url string 使用SOCK5代理
|
||||
-tlog, -trace-log string 写入请求日志到文件
|
||||
-version 显示版本信息
|
||||
-v, -verbose 显示详细信息
|
||||
-vv 显示额外的详细信息
|
||||
-tv, -templates-version 显示已安装的模板版本
|
||||
-debug 显示所有请求和响应
|
||||
-dreq, -debug-req 显示所有请求
|
||||
-dresp, -debug-resp 显示所有响应
|
||||
-p, -proxy string[] 使用http/socks5代理(逗号分隔,文件)
|
||||
-pi, -proxy-internal 代理所有请求
|
||||
-ldf, -list-dsl-function 列出所有支持的DSL函数签名
|
||||
-tlog, -trace-log string 写入跟踪日志到文件
|
||||
-elog, -error-log string 写入错误日志到文件
|
||||
-version 显示版本信息
|
||||
-hm, -hang-monitor 启用Nuclei的监控
|
||||
-v, -verbose 显示详细信息
|
||||
-profile-mem string 将Nuclei的内存转储成文件
|
||||
-vv 显示额外的详细信息
|
||||
-ep, -enable-pprof 启用pprof调试服务器
|
||||
-tv, -templates-version 显示已安装的模板版本
|
||||
-hc, -health-check 运行诊断检查
|
||||
|
||||
升级:
|
||||
-update 更新Nuclei到最新版本
|
||||
-ut, -update-templates 更新Nuclei模板到最新版
|
||||
-ud, -update-directory string 覆盖安装模板
|
||||
-duc, -disable-update-check 禁用更新
|
||||
-update 更新Nuclei到最新版本
|
||||
-ut, -update-templates 更新Nuclei模板到最新版
|
||||
-ud, -update-directory string 覆盖安装模板
|
||||
-duc, -disable-update-check 禁用更新
|
||||
|
||||
统计:
|
||||
-stats 显示正在扫描的统计信息
|
||||
-sj, -stats-json 将统计信息以JSONL格式输出到文件
|
||||
-si, -stats-inerval int 显示统计信息更新的间隔秒数(默认:5)
|
||||
-m, -metrics 显示Nuclei端口信息
|
||||
-mp, -metrics-port int 更改Nuclei默认端口(默认:9092)
|
||||
-stats 显示正在扫描的统计信息
|
||||
-sj, -stats-json 将统计信息以JSONL格式输出到文件
|
||||
-si, -stats-inerval int 显示统计信息更新的间隔秒数(默认:5)
|
||||
-m, -metrics 显示Nuclei端口信息
|
||||
-mp, -metrics-port int 更改Nuclei默认端口(默认:9092)
|
||||
```
|
||||
|
||||
### 运行Nuclei
|
||||
@ -233,7 +278,7 @@ Nuclei提供了大量有助于安全工程师在工作流定制相关的功能
|
||||
|
||||
**对于赏金猎人:**
|
||||
|
||||
Nuclei允许您定制自己的测试方法,可以轻松的运行您的程序。此外Nuclei可以更容易的集成到您的漏扫设备中。
|
||||
Nuclei允许您定制自己的测试方法,可以轻松的运行您的程序。此外Nuclei可以更容易的集成到您的漏洞扫描工作流中。
|
||||
|
||||
- 可以集成到其他工作流中
|
||||
- 可以在几分钟处理上千台主机
|
||||
@ -267,7 +312,7 @@ Nuclei通过增加手动、自动的过程,极大地改变了安全评估的
|
||||
|
||||
Nuclei构建很简单,通过数百名安全研究员的社区模板,Nuclei可以随时扫描来了解安全威胁。Nuclei通常用来用于复测,以确定漏洞是否被修复。
|
||||
|
||||
- **CI/CD:**工程师已经支持了CI/CD,可以使用Nuclei来监控生产环境
|
||||
- **CI/CD:**工程师已经支持了CI/CD,可以通过Nuclei使用定制模板来监控模拟环境和生产环境
|
||||
- **周期性扫描:**使用Nuclei创建新发现的漏洞模板,通过Nuclei可以周期性扫描消除漏洞
|
||||
|
||||
我们有个[讨论组](https://github.com/projectdiscovery/nuclei-templates/discussions/693),黑客提交自己的模板后可以获得赏金,这可以减少资产的漏洞,并且减少重复。如果你想实行该计划,可以[联系我](mailto:contact@projectdiscovery.io)。我们非常乐意提供帮助,或者在[讨论组](https://github.com/projectdiscovery/nuclei-templates/discussions/693)中发布相关信息。
|
||||
@ -281,6 +326,11 @@ Nuclei构建很简单,通过数百名安全研究员的社区模板,Nuclei
|
||||
</h1>
|
||||
|
||||
### 资源
|
||||
|
||||
- [使用PinkDraconian发现Nuclei的BUG (Robbe Van Roey)](https://www.youtube.com/watch?v=ewP0xVPW-Pk) 作者:[@PinkDraconian](https://twitter.com/PinkDraconian)
|
||||
- [Nuclei: 强而有力的扫描器](https://bishopfox.com/blog/nuclei-vulnerability-scan) 作者:Bishopfox
|
||||
- [WAF有效性检查](https://www.fastly.com/blog/the-waf-efficacy-framework-measuring-the-effectiveness-of-your-waf) 作者:Fastly
|
||||
- [在CI/CD中使用Nuclei实时扫描网页应用](https://blog.escape.tech/devsecops-part-iii-scanning-live-web-applications/) 作者:[@TristanKalos](https://twitter.com/TristanKalos)
|
||||
- [使用Nuclei扫描](https://blog.projectdiscovery.io/community-powered-scanning-with-nuclei/)
|
||||
- [Nuclei Unleashed - 快速编写复杂漏洞](https://blog.projectdiscovery.io/nuclei-unleashed-quickly-write-complex-exploits/)
|
||||
- [Nuclei - FUZZ一切](https://blog.projectdiscovery.io/nuclei-fuzz-all-the-things/)
|
||||
|
||||
354
README_ID.md
Normal file
354
README_ID.md
Normal file
@ -0,0 +1,354 @@
|
||||
<h1 align="center">
|
||||
<br>
|
||||
<a href="https://nuclei.projectdiscovery.io"><img src="static/nuclei-logo.png" width="200px" alt="Nuclei"></a>
|
||||
</h1>
|
||||
|
||||
<h4 align="center">Pemindai kerentanan yang cepat dan dapat disesuaikan berdasarkan DSL berbasis YAML sederhana.</h4>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/github/go-mod/go-version/projectdiscovery/nuclei?filename=v2%2Fgo.mod">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases"><img src="https://img.shields.io/github/downloads/projectdiscovery/nuclei/total">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/graphs/contributors"><img src="https://img.shields.io/github/contributors-anon/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases/"><img src="https://img.shields.io/github/release/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/issues"><img src="https://img.shields.io/github/issues-raw/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/discussions"><img src="https://img.shields.io/github/discussions/projectdiscovery/nuclei">
|
||||
<a href="https://discord.gg/projectdiscovery"><img src="https://img.shields.io/discord/695645237418131507.svg?logo=discord"></a>
|
||||
<a href="https://twitter.com/pdnuclei"><img src="https://img.shields.io/twitter/follow/pdnuclei.svg?logo=twitter"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#cara-kerja">Cara Kerja</a> •
|
||||
<a href="#instalasi-nuclei">Instalasi</a> •
|
||||
<a href="#untuk-insinyur-keamanan">Untuk Teknisi Keamanan</a> •
|
||||
<a href="#untuk-pengembang-dan-organisasi">Untuk Pengembang</a> •
|
||||
<a href="https://nuclei.projectdiscovery.io/nuclei/get-started/">Dokumentasi</a> •
|
||||
<a href="#kredit">Kredit</a> •
|
||||
<a href="https://nuclei.projectdiscovery.io/faq/nuclei/">Tanya Jawab</a> •
|
||||
<a href="https://discord.gg/projectdiscovery">Gabung Discord</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/master/README.md">English</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/master/README_CN.md">中文</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/master/README_KR.md">Korean</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/master/README_ID.md">Indonesia</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
Nuclei digunakan untuk mengirim permintaan lintas target berdasarkan templat, yang menghasilkan nol positif palsu dan menyediakan pemindaian yang cepat pada banyak host. Nuclei menawarkan pemindaian untuk berbagai protokol, termasuk TCP, DNS, HTTP, SSL, File, Whois, Websocket, Headless, dll. Dengan templating yang kuat dan fleksibel, Nuclei dapat digunakan untuk memodelkan semua jenis pemeriksaan keamanan.
|
||||
|
||||
Kami memiliki [repositori khusus](https://github.com/projectdiscovery/nuclei-templates) yang menampung berbagai jenis templat kerentanan yang disumbangkan oleh **lebih dari 300** peneliti dan teknisi keamanan.
|
||||
|
||||
|
||||
## Cara Kerja
|
||||
|
||||
|
||||
<h3 align="center">
|
||||
<img src="static/nuclei-flow.jpg" alt="nuclei-flow" width="700px"></a>
|
||||
</h3>
|
||||
|
||||
|
||||
# Instalasi Nuclei
|
||||
|
||||
Nuclei membutuhkan **go1.18** agar dapat diinstall. Jalankan perintah berikut untuk menginstal versi terbaru -
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v2/cmd/nuclei@latest
|
||||
```
|
||||
|
||||
**Metode [instalasi lain dapat ditemukan di sini](https://nuclei.projectdiscovery.io/nuclei/get-started/).**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
### Nuclei Templates
|
||||
|
||||
Nuclei memiliki dukungan untuk unduhan/pembaruan templat otomatis sebagai bawaan sejak versi [v2.5.2](https://github.com/projectdiscovery/nuclei/releases/tag/v2.5.2). Proyek [**Nuclei-Templates**](https://github.com/projectdiscovery/nuclei-templates) menyediakan daftar template siap pakai yang dibuat oleh komunitas yang terus diperbarui.
|
||||
|
||||
Anda dapat menggunakan flag `-update-templates` untuk memperbarui templat inti kapan saja; Anda juga dapat menulis pemeriksaan Anda sendiri untuk alur kerja individu dan untuk kebutuhan Anda sendiri dengan mengikuti [panduan pembuatan templat Nuclei](https://nuclei.projectdiscovery.io/templating-guide/).
|
||||
|
||||
Untuk referensi penulisan sintaks DSL berbasis YAML tersedia [di sini](SYNTAX-REFERENCE.md).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Cara Pakai
|
||||
|
||||
```sh
|
||||
nuclei -h
|
||||
```
|
||||
|
||||
Ini akan menampilkan bantuan untuk alat tersebut. Berikut adalah semua flag yang didukungnya.
|
||||
|
||||
|
||||
```console
|
||||
Nuclei is a fast, template based vulnerability scanner focusing
|
||||
on extensive configurability, massive extensibility and ease of use.
|
||||
|
||||
Usage:
|
||||
nuclei [flags]
|
||||
|
||||
Flags:
|
||||
TARGET:
|
||||
-u, -target string[] target URLs/hosts to scan
|
||||
-l, -list string path to file containing a list of target URLs/hosts to scan (one per line)
|
||||
-resume string Resume scan using resume.cfg (clustering will be disabled)
|
||||
|
||||
TEMPLATES:
|
||||
-nt, -new-templates run only new templates added in latest nuclei-templates release
|
||||
-ntv, -new-templates-version string[] run new templates added in specific version
|
||||
-as, -automatic-scan automatic web scan using wappalyzer technology detection to tags mapping
|
||||
-t, -templates string[] list of template or template directory to run (comma-separated, file)
|
||||
-tu, -template-url string[] list of template urls to run (comma-separated, file)
|
||||
-w, -workflows string[] list of workflow or workflow directory to run (comma-separated, file)
|
||||
-wu, -workflow-url string[] list of workflow urls to run (comma-separated, file)
|
||||
-validate validate the passed templates to nuclei
|
||||
-nss, -no-strict-syntax Disable strict syntax check on templates
|
||||
-tl list all available templates
|
||||
|
||||
FILTERING:
|
||||
-a, -author string[] templates to run based on authors (comma-separated, file)
|
||||
-tags string[] templates to run based on tags (comma-separated, file)
|
||||
-etags, -exclude-tags string[] templates to exclude based on tags (comma-separated, file)
|
||||
-itags, -include-tags string[] tags to be executed even if they are excluded either by default or configuration
|
||||
-id, -template-id string[] templates to run based on template ids (comma-separated, file)
|
||||
-eid, -exclude-id string[] templates to exclude based on template ids (comma-separated, file)
|
||||
-it, -include-templates string[] templates to be executed even if they are excluded either by default or configuration
|
||||
-et, -exclude-templates string[] template or template directory to exclude (comma-separated, file)
|
||||
-em, -exclude-matchers string[] template matchers to exclude in result
|
||||
-s, -severity value[] templates to run based on severity. Possible values: info, low, medium, high, critical, unknown
|
||||
-es, -exclude-severity value[] templates to exclude based on severity. Possible values: info, low, medium, high, critical, unknown
|
||||
-pt, -type value[] templates to run based on protocol type. Possible values: dns, file, http, headless, network, workflow, ssl, websocket, whois
|
||||
-ept, -exclude-type value[] templates to exclude based on protocol type. Possible values: dns, file, http, headless, network, workflow, ssl, websocket, whois
|
||||
-tc, -template-condition string[] templates to run based on expression condition
|
||||
|
||||
OUTPUT:
|
||||
-o, -output string output file to write found issues/vulnerabilities
|
||||
-sresp, -store-resp store all request/response passed through nuclei to output directory
|
||||
-srd, -store-resp-dir string store all request/response passed through nuclei to custom directory (default "output")
|
||||
-silent display findings only
|
||||
-nc, -no-color disable output content coloring (ANSI escape codes)
|
||||
-json write output in JSONL(ines) format
|
||||
-irr, -include-rr include request/response pairs in the JSONL output (for findings only)
|
||||
-nm, -no-meta disable printing result metadata in cli output
|
||||
-nts, -no-timestamp disable printing timestamp in cli output
|
||||
-rdb, -report-db string nuclei reporting database (always use this to persist report data)
|
||||
-ms, -matcher-status display match failure status
|
||||
-me, -markdown-export string directory to export results in markdown format
|
||||
-se, -sarif-export string file to export results in SARIF format
|
||||
|
||||
CONFIGURATIONS:
|
||||
-config string path to the nuclei configuration file
|
||||
-fr, -follow-redirects enable following redirects for http templates
|
||||
-fhr, -follow-host-redirects follow redirects on the same host
|
||||
-mr, -max-redirects int max number of redirects to follow for http templates (default 10)
|
||||
-dr, -disable-redirects disable redirects for http templates
|
||||
-rc, -report-config string nuclei reporting module configuration file
|
||||
-H, -header string[] custom header/cookie to include in all http request in header:value format (cli, file)
|
||||
-V, -var value custom vars in key=value format
|
||||
-r, -resolvers string file containing resolver list for nuclei
|
||||
-sr, -system-resolvers use system DNS resolving as error fallback
|
||||
-passive enable passive HTTP response processing mode
|
||||
-ev, -env-vars enable environment variables to be used in template
|
||||
-cc, -client-cert string client certificate file (PEM-encoded) used for authenticating against scanned hosts
|
||||
-ck, -client-key string client key file (PEM-encoded) used for authenticating against scanned hosts
|
||||
-ca, -client-ca string client certificate authority file (PEM-encoded) used for authenticating against scanned hosts
|
||||
-sml, -show-match-line show match lines for file templates, works with extractors only
|
||||
-ztls use ztls library with autofallback to standard one for tls13
|
||||
-sni string tls sni hostname to use (default: input domain name)
|
||||
-i, -interface string network interface to use for network scan
|
||||
-sip, -source-ip string source ip address to use for network scan
|
||||
-config-directory string Override the default config path ($home/.config)
|
||||
-rsr, -response-size-read int max response size to read in bytes (default 10485760)
|
||||
-rss, -response-size-save int max response size to save in bytes (default 10485760)
|
||||
|
||||
INTERACTSH:
|
||||
-iserver, -interactsh-server string interactsh server url for self-hosted instance (default: oast.pro,oast.live,oast.site,oast.online,oast.fun,oast.me)
|
||||
-itoken, -interactsh-token string authentication token for self-hosted interactsh server
|
||||
-interactions-cache-size int number of requests to keep in the interactions cache (default 5000)
|
||||
-interactions-eviction int number of seconds to wait before evicting requests from cache (default 60)
|
||||
-interactions-poll-duration int number of seconds to wait before each interaction poll request (default 5)
|
||||
-interactions-cooldown-period int extra time for interaction polling before exiting (default 5)
|
||||
-ni, -no-interactsh disable interactsh server for OAST testing, exclude OAST based templates
|
||||
|
||||
RATE-LIMIT:
|
||||
-rl, -rate-limit int maximum number of requests to send per second (default 150)
|
||||
-rlm, -rate-limit-minute int maximum number of requests to send per minute
|
||||
-bs, -bulk-size int maximum number of hosts to be analyzed in parallel per template (default 25)
|
||||
-c, -concurrency int maximum number of templates to be executed in parallel (default 25)
|
||||
-hbs, -headless-bulk-size int maximum number of headless hosts to be analyzed in parallel per template (default 10)
|
||||
-headc, -headless-concurrency int maximum number of headless templates to be executed in parallel (default 10)
|
||||
|
||||
OPTIMIZATIONS:
|
||||
-timeout int time to wait in seconds before timeout (default 10)
|
||||
-retries int number of times to retry a failed request (default 1)
|
||||
-ldp, -leave-default-ports leave default HTTP/HTTPS ports (eg. host:80,host:443
|
||||
-mhe, -max-host-error int max errors for a host before skipping from scan (default 30)
|
||||
-project use a project folder to avoid sending same request multiple times
|
||||
-project-path string set a specific project path
|
||||
-spm, -stop-at-first-path stop processing HTTP requests after the first match (may break template/workflow logic)
|
||||
-stream stream mode - start elaborating without sorting the input
|
||||
-irt, -input-read-timeout duration timeout on input read (default 3m0s)
|
||||
-no-stdin Disable Stdin processing
|
||||
|
||||
HEADLESS:
|
||||
-headless enable templates that require headless browser support (root user on linux will disable sandbox)
|
||||
-page-timeout int seconds to wait for each page in headless mode (default 20)
|
||||
-sb, -show-browser show the browser on the screen when running templates with headless mode
|
||||
-sc, -system-chrome Use local installed chrome browser instead of nuclei installed
|
||||
-lha, -list-headless-action list available headless actions
|
||||
|
||||
DEBUG:
|
||||
-debug show all requests and responses
|
||||
-dreq, -debug-req show all sent requests
|
||||
-dresp, -debug-resp show all received responses
|
||||
-p, -proxy string[] list of http/socks5 proxy to use (comma separated or file input)
|
||||
-pi, -proxy-internal proxy all internal requests
|
||||
-ldf, -list-dsl-function list all supported DSL function signatures
|
||||
-tlog, -trace-log string file to write sent requests trace log
|
||||
-elog, -error-log string file to write sent requests error log
|
||||
-version show nuclei version
|
||||
-hm, -hang-monitor enable nuclei hang monitoring
|
||||
-v, -verbose show verbose output
|
||||
-profile-mem string optional nuclei memory profile dump file
|
||||
-vv display templates loaded for scan
|
||||
-ep, -enable-pprof enable pprof debugging server
|
||||
-tv, -templates-version shows the version of the installed nuclei-templates
|
||||
-hc, -health-check run diagnostic check up
|
||||
|
||||
UPDATE:
|
||||
-update update nuclei engine to the latest released version
|
||||
-ut, -update-templates update nuclei-templates to latest released version
|
||||
-ud, -update-directory string overwrite the default directory to install nuclei-templates
|
||||
-duc, -disable-update-check disable automatic nuclei/templates update check
|
||||
|
||||
STATISTICS:
|
||||
-stats display statistics about the running scan
|
||||
-sj, -stats-json write statistics data to an output file in JSONL(ines) format
|
||||
-si, -stats-interval int number of seconds to wait between showing a statistics update (default 5)
|
||||
-m, -metrics expose nuclei metrics on a port
|
||||
-mp, -metrics-port int port to expose nuclei metrics on (default 9092)
|
||||
```
|
||||
|
||||
### Menjalankan Nuclei
|
||||
|
||||
Memindai domain target dengan templat Nuclei yang [dikurasi oleh komunitas](https://github.com/projectdiscovery/nuclei-templates).
|
||||
|
||||
```sh
|
||||
nuclei -u https://example.com
|
||||
```
|
||||
|
||||
Memindai URL target dengan templat Nuclei yang [dikurasi oleh komunitas](https://github.com/projectdiscovery/nuclei-templates).
|
||||
|
||||
```sh
|
||||
nuclei -list urls.txt
|
||||
```
|
||||
|
||||
Contoh dari berkas `urls.txt`:
|
||||
|
||||
```yaml
|
||||
http://example.com
|
||||
http://app.example.com
|
||||
http://test.example.com
|
||||
http://uat.example.com
|
||||
```
|
||||
|
||||
**Contoh lebih detil tentang menjalankan Nuclei dapat ditemukan [di sini](https://nuclei.projectdiscovery.io/nuclei/get-started/#running-nuclei).**
|
||||
|
||||
# Untuk Teknisi Keamanan
|
||||
|
||||
Nuclei menawarkan sejumlah besar fitur yang berguna bagi teknisi keamanan untuk menyesuaikan alur kerja di organisasi mereka. Dengan berbagai kemampuan pemindaian (seperti misalnya DNS, HTTP, TCP), teknisi keamanan dapat dengan mudah membuat rangkaian pemeriksaan khusus mereka dengan Nuclei.
|
||||
|
||||
- Berbagai protokol yang didukung: TCP, DNS, HTTP, File, dll
|
||||
- Mencapai langkah-langkah kerentanan yang kompleks dengan alur kerja dan [permintaan dinamis](https://blog.projectdiscovery.io/nuclei-unleashed-quickly-write-complex-exploits/).
|
||||
- Mudah diintegrasikan ke dalam CI/CD, dirancang agar mudah diintegrasikan ke dalam siklus regresi untuk secara aktif memeriksa perbaikan dan kemunculan kerentanan kembali.
|
||||
|
||||
<h1 align="left">
|
||||
<a href="https://nuclei.projectdiscovery.io/nuclei/get-started/"><img src="static/learn-more-button.png" width="170px" alt="Pelajari Selengkapnya"></a>
|
||||
</h1>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
**Untuk Pemburu Celah Berhadiah:**
|
||||
|
||||
Nuclei memungkinkan Anda untuk menyesuaikan pendekatan pengujian Anda dengan rangkaian pemeriksaan Anda sendiri dan dengan mudah menjalankan program celah berhadiah Anda. Selain itu, Nuclei dapat dengan mudah diintegrasikan ke dalam alur kerja pemindaian berkelanjutan.
|
||||
|
||||
- Dirancang agar mudah diintegrasikan ke dalam alur kerja alat lainnya.
|
||||
- Dapat memproses ribuan host hanya dalam beberapa menit.
|
||||
- Mudah mengotomatiskan pendekatan pengujian khusus Anda dengan sintaks DSL berbasis YAML sederhana kami.
|
||||
|
||||
Silakan periksa proyek sumber terbuka kami yang lain yang mungkin cocok dengan alur kerja celah berhadiah Anda: [github.com/projectdiscovery](http://github.com/projectdiscovery), kami juga menyediakan [penyegaran data DNS di Chaos setiap hari](http://chaos.projectdiscovery.io).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
**Untuk Penguji Penetrasi:**
|
||||
|
||||
Nuclei sangat meningkatkan cara Anda mendekati penilaian keamanan dengan menambah proses manual yang berulang. Para konsultan sudah mengonversi langkah penilaian manual mereka dengan Nuclei, ini memungkinkan mereka untuk menjalankan serangkaian pendekatan penilaian khusus mereka di ribuan host secara otomatis.
|
||||
|
||||
Para penguji penetrasi mendapatkan kekuatan penuh dari templat publik dan kemampuan penyesuaian kami untuk mempercepat proses penilaian mereka, dan khususnya dengan siklus regresi di mana Anda dapat dengan mudah memverifikasi perbaikannya.
|
||||
|
||||
- Mudah untuk membuat daftar pemeriksa kepatuhan Anda, sederet standar (mis., OWASP 10 Teratas).
|
||||
- Dengan kemampuan seperti [fuzz](https://nuclei.projectdiscovery.io/templating-guide/#advance-fuzzing) dan [alur kerja](https://nuclei.projectdiscovery.io/templating-guide/#workflows), langkah manual yang rumit dan penilaian berulang dapat dengan mudah diotomatisasi dengan Nuclei.
|
||||
- Mudah untuk menguji ulang perbaikan kerentanan hanya dengan menjalankan ulang template.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
# Untuk Pengembang dan Organisasi
|
||||
|
||||
Nuclei dibangun dengan kesederhanaan dalam pemikiran, dengan templat yang didukung komunitas oleh ratusan peneliti keamanan, memungkinkan Anda untuk tidak tertinggal dengan ancaman keamanan terbaru menggunakan pemindaian Nuclei terus menerus pada host. Ini dirancang agar mudah diintegrasikan ke dalam siklus pengujian regresi, untuk memverifikasi perbaikan dan menghilangkan kerentanan agar tidak terjadi di masa mendatang.
|
||||
|
||||
- **CI/CD:** Pengembang sudah memanfaatkan Nuclei dalam aliran CI/CD mereka, ini memungkinkan mereka untuk terus memantau lingkungan pementasan dan produksi mereka dengan templat yang disesuaikan.
|
||||
- **Siklus Regresi Berkelanjutan:** Dengan Nuclei, Anda dapat membuat templat khusus pada setiap kerentanan baru yang teridentifikasi dan dimasukkan ke dalam mesin Nuclei untuk dihilangkan dalam siklus regresi berkelanjutan.
|
||||
|
||||
Kami memiliki [utas diskusi tentang ini](https://github.com/projectdiscovery/nuclei-templates/discussions/693), sudah ada beberapa program celah berhadiah yang memberikan insentif kepada peretas untuk menulis templat inti dengan setiap pengiriman, yang membantu mereka untuk menghilangkan kerentanan di semua aset mereka, serta untuk menghilangkan risiko masa depan yang muncul kembali pada lingkungan produksi. Jika Anda tertarik untuk menerapkannya di organisasi Anda, jangan ragu untuk [menghubungi kami](mailto:contact@projectdiscovery.io). Kami akan dengan senang hati membantu Anda dalam proses memulai, atau Anda juga dapat memposting ke [utas diskusi](https://github.com/projectdiscovery/nuclei-templates/discussions/693) untuk bantuan apapun.
|
||||
|
||||
<h3 align="center">
|
||||
<img src="static/regression-with-nuclei.jpg" alt="Siklus Regresi Berkelanjutan dengan Nuclei" width="1100px"></a>
|
||||
</h3>
|
||||
|
||||
<h1 align="left">
|
||||
<a href="https://github.com/projectdiscovery/nuclei-action"><img src="static/learn-more-button.png" width="170px" alt="Pelajari Selengkapnya"></a>
|
||||
</h1>
|
||||
|
||||
### Sumber Daya
|
||||
|
||||
- [Menemukan bug dengan menggunakan Nuclei dengan PinkDraconian (Robbe Van Roey)](https://www.youtube.com/watch?v=ewP0xVPW-Pk) oleh **[@PinkDraconian](https://twitter.com/PinkDraconian)**
|
||||
- [Nuclei: Mengemas Pukulan dengan Pemindaian Kerentanan](https://bishopfox.com/blog/nuclei-vulnerability-scan) oleh **Bishopfox**
|
||||
- [Kerangka kemanjuran WAF](https://www.fastly.com/blog/the-waf-efficacy-framework-measuring-the-effectiveness-of-your-waf) oleh **Fastly**
|
||||
- [Memindai Aplikasi Web Langsung dengan Nuclei di Aliran CI/CD](https://blog.escape.tech/devsecops-part-iii-scanning-live-web-applications/) oleh **[@TristanKalos](https://twitter.com/TristanKalos)**
|
||||
- [Pemindaian Bertenaga Komunitas dengan Nuclei](https://blog.projectdiscovery.io/community-powered-scanning-with-nuclei/)
|
||||
- [Nuclei Unleashed - Menulis eksploitasi kompleks dengan cepat](https://blog.projectdiscovery.io/nuclei-unleashed-quickly-write-complex-exploits/)
|
||||
- [Nuclei - Fuzz semua hal](https://blog.projectdiscovery.io/nuclei-fuzz-all-the-things/)
|
||||
- [Integrasi Nuclei + Interactsh untuk Mengotomatiskan Pengujian OOB](https://blog.projectdiscovery.io/nuclei-interactsh-integration/)
|
||||
- [Mempersenjatai Alur Kerja Nuclei untuk Menghancurkan Semua Hal](https://medium.com/@dwisiswant0/weaponizes-nuclei-workflows-to-pwn-all-the-things-cd01223feb77) oleh **[@dwisiswant0](https://github.com/dwisiswant0)**
|
||||
- [Bagaimana Memindai Terus-menerus dengan Nuclei?](https://medium.com/@dwisiswant0/how-to-scan-continuously-with-nuclei-fcb7e9d8b8b9) oleh **[@dwisiswant0](https://github.com/dwisiswant0)**
|
||||
- [Retas dengan Otomatisasi !!!](https://dhiyaneshgeek.github.io/web/security/2021/07/19/hack-with-automation/) oleh **[@DhiyaneshGeek](https://github.com/DhiyaneshGeek)**
|
||||
|
||||
### Kredit
|
||||
|
||||
Terima kasih kepada semua komunitas yang luar biasa yang [berkontribusi untuk mengirimkan PR](https://github.com/projectdiscovery/nuclei/graphs/contributors). Lihat juga proyek sumber-terbuka serupa di bawah ini yang mungkin sesuai dengan alur kerja Anda:
|
||||
|
||||
[FFuF](https://github.com/ffuf/ffuf), [Qsfuzz](https://github.com/ameenmaali/qsfuzz), [Inception](https://github.com/proabiral/inception), [Snallygaster](https://github.com/hannob/snallygaster), [Gofingerprint](https://github.com/Static-Flow/gofingerprint), [Sn1per](https://github.com/1N3/Sn1per/tree/master/templates), [Google tsunami](https://github.com/google/tsunami-security-scanner), [Jaeles](https://github.com/jaeles-project/jaeles), [ChopChop](https://github.com/michelin/ChopChop)
|
||||
|
||||
### Lisensi
|
||||
|
||||
Nuclei didistribusikan di bawah [Lisensi MIT](https://github.com/projectdiscovery/nuclei/blob/master/LICENSE.md)
|
||||
|
||||
<h1 align="left">
|
||||
<a href="https://discord.gg/projectdiscovery"><img src="static/Join-Discord.png" width="380" alt="Join Discord"></a> <a href="https://nuclei.projectdiscovery.io"><img src="static/check-nuclei-documentation.png" width="380" alt="Cek Dokumentasi Nuclei"></a>
|
||||
</h1>
|
||||
@ -106,6 +106,7 @@ TEMPLATES:
|
||||
-wu, -workflow-url string[] 실행할 워크플로 URL 목록(쉼표로 구분된 파일)
|
||||
-validate nuclei로 전달된 템플릿 검증
|
||||
-tl 사용 가능한 모든 템플릿 목록
|
||||
-td 템플릿 내용 표시
|
||||
|
||||
FILTERING:
|
||||
-a, -author string[] 작성자를 기준으로 실행할 템플릿(쉼표로 구분된 파일)
|
||||
@ -179,7 +180,7 @@ OPTIMIZATIONS:
|
||||
-mhe, -max-host-error int 스캔을 건너뛰기 전에 호스트에 대한 최대 오류 수 (기본 30)
|
||||
-project 프로젝트 폴더를 사용하여 동일한 요청을 여러 번 보내지 않음
|
||||
-project-path string 특정 프로젝트 경로 설정
|
||||
-spm, -stop-at-first-path 첫 번째 일치 후 HTTP 요청 처리 중지 (template/workflow 로직이 중단될 수 있음)
|
||||
-spm, -stop-at-first-match 첫 번째 일치 후 HTTP 요청 처리 중지 (template/workflow 로직이 중단될 수 있음)
|
||||
-stream stream 모드 - 입력을 정렬하지 않고 elaborating 시작
|
||||
|
||||
HEADLESS:
|
||||
|
||||
@ -3924,6 +3924,28 @@ Client Cipher Suites - auto if not specified.
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>scan_mode</code> <i>string</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
Tls Scan Mode - auto if not specified
|
||||
|
||||
|
||||
Valid values:
|
||||
|
||||
|
||||
- <code>ctls</code>
|
||||
|
||||
- <code>ztls</code>
|
||||
|
||||
- <code>auto</code>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ info:
|
||||
|
||||
requests:
|
||||
- raw:
|
||||
# Note for the integration test: dsl expression should not contain commas
|
||||
- |
|
||||
GET / HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
@ -90,9 +91,11 @@ requests:
|
||||
78: {{line_starts_with("Hi\nHello", "He")}}
|
||||
79: {{line_ends_with("Hello\nHi", "lo")}}
|
||||
80: {{sort("a1b2c3d4e5")}}
|
||||
81: {{join(" ", sort("b", "a", "2", "c", "3", "1", "d", "4"))}}
|
||||
82: {{uniq("abcabdaabbccd")}}
|
||||
81: {{uniq("abcabdaabbccd")}}
|
||||
82: {{join(" ", sort("b", "a", "2", "c", "3", "1", "d", "4"))}}
|
||||
83: {{join(" ", uniq("ab", "cd", "12", "34", "12", "cd"))}}
|
||||
84: {{split("ab,cd,efg", ",")}}
|
||||
85: {{split("ab,cd,efg", ",", 2)}}
|
||||
|
||||
extractors:
|
||||
- type: regex
|
||||
|
||||
@ -1121,7 +1121,7 @@
|
||||
"tls13"
|
||||
],
|
||||
"type": "string",
|
||||
"title": "TLS version",
|
||||
"title": "Min. TLS version",
|
||||
"description": "Minimum tls version - automatic if not specified."
|
||||
},
|
||||
"max_version": {
|
||||
@ -1133,7 +1133,7 @@
|
||||
"tls13"
|
||||
],
|
||||
"type": "string",
|
||||
"title": "TLS version",
|
||||
"title": "Max. TLS version",
|
||||
"description": "Max tls version - automatic if not specified."
|
||||
},
|
||||
"cipher_suites": {
|
||||
@ -1141,6 +1141,16 @@
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"scan_mode": {
|
||||
"enum": [
|
||||
"ctls",
|
||||
"ztls",
|
||||
"auto"
|
||||
],
|
||||
"type": "string",
|
||||
"title": "Scan Mode",
|
||||
"description": "Scan Mode - auto if not specified."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
||||
@ -25,8 +25,8 @@ docs:
|
||||
test:
|
||||
$(GOTEST) $(GOFLAGS) ./...
|
||||
integration:
|
||||
bash ../integration_tests/run.sh
|
||||
cd ../integration_tests; bash run.sh
|
||||
functional:
|
||||
bash cmd/functional-tests/run.sh
|
||||
cd cmd/functional-test; bash run.sh
|
||||
tidy:
|
||||
$(GOMOD) tidy
|
||||
$(GOMOD) tidy
|
||||
|
||||
4
v2/cmd/functional-test/targets.txt
Normal file
4
v2/cmd/functional-test/targets.txt
Normal file
@ -0,0 +1,4 @@
|
||||
scanme.sh
|
||||
scanme.sh?a=1
|
||||
scanme.sh?a=2
|
||||
scanme.sh?a=3
|
||||
@ -29,6 +29,12 @@ func main() {
|
||||
if err != nil || d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
pathIndex := path[strings.Index(path, "nuclei-templates/")+17:]
|
||||
pathIndex = strings.TrimPrefix(pathIndex, "nuclei-templates/")
|
||||
// Ignore items starting with dots
|
||||
if strings.HasPrefix(pathIndex, ".") {
|
||||
return nil
|
||||
}
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
@ -37,7 +43,7 @@ func main() {
|
||||
_, _ = io.Copy(h, bytes.NewReader(data))
|
||||
hash := hex.EncodeToString(h.Sum(nil))
|
||||
|
||||
_, _ = file.WriteString(path[strings.Index(path, "nuclei-templates/")+17:])
|
||||
_, _ = file.WriteString(pathIndex)
|
||||
_, _ = file.WriteString(":")
|
||||
_, _ = file.WriteString(hash)
|
||||
_, _ = file.WriteString("\n")
|
||||
|
||||
@ -8,7 +8,6 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/http/httputil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -16,6 +15,7 @@ import (
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/testutils"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
)
|
||||
|
||||
var httpTestcases = map[string]testutils.TestCase{
|
||||
@ -284,19 +284,22 @@ func (h *httpDSLFunctions) Execute(filePath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
resultPattern := regexp.MustCompile(`\[[^]]+] \[[^]]+] \[[^]]+] [^]]+ \[([^]]+)]`)
|
||||
submatch := resultPattern.FindStringSubmatch(results[0])
|
||||
if len(submatch) != 2 {
|
||||
return errors.New("could not parse the result")
|
||||
// get result part
|
||||
resultPart, err := stringsutil.After(results[0], ts.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
totalExtracted := strings.Split(submatch[1], ",")
|
||||
numberOfDslFunctions := 83
|
||||
if len(totalExtracted) != numberOfDslFunctions {
|
||||
// remove additional characters till the first valid result and ignore last ] which doesn't alter the total count
|
||||
resultPart = stringsutil.TrimPrefixAny(resultPart, "/", " ", "[")
|
||||
|
||||
extracted := strings.Split(resultPart, ",")
|
||||
numberOfDslFunctions := 85
|
||||
if len(extracted) != numberOfDslFunctions {
|
||||
return errors.New("incorrect number of results")
|
||||
}
|
||||
|
||||
for _, header := range totalExtracted {
|
||||
for _, header := range extracted {
|
||||
parts := strings.Split(header, ": ")
|
||||
index, err := strconv.Atoi(parts[0])
|
||||
if err != nil {
|
||||
|
||||
@ -18,6 +18,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/operators/common/dsl"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/uncover"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/http"
|
||||
templateTypes "github.com/projectdiscovery/nuclei/v2/pkg/templates/types"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
@ -124,9 +125,9 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.CreateGroup("input", "Target",
|
||||
flagSet.StringSliceVarP(&options.Targets, "target", "u", []string{}, "target URLs/hosts to scan", goflags.StringSliceOptions),
|
||||
flagSet.StringVarP(&options.TargetsFilePath, "list", "l", "", "path to file containing a list of target URLs/hosts to scan (one per line)"),
|
||||
flagSet.StringVar(&options.Resume, "resume", "", "Resume scan using resume.cfg (clustering will be disabled)"),
|
||||
flagSet.BoolVarP(&options.ScanAllIPs, "scan-all-ips", "sa", false, "Scan all the ip's associated with dns record"),
|
||||
flagSet.StringSliceVarP(&options.IPVersion, "ip-version", "iv", []string{"4"}, "IP version to scan of hostname (4,6) - (default 4)", goflags.CommaSeparatedStringSliceOptions),
|
||||
flagSet.StringVar(&options.Resume, "resume", "", "resume scan using resume.cfg (clustering will be disabled)"),
|
||||
flagSet.BoolVarP(&options.ScanAllIPs, "scan-all-ips", "sa", false, "scan all the IP's associated with dns record"),
|
||||
flagSet.StringSliceVarP(&options.IPVersion, "ip-version", "iv", []string{""}, "IP version to scan of hostname (4,6) - (default 4)", goflags.CommaSeparatedStringSliceOptions),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("templates", "Templates",
|
||||
@ -138,7 +139,8 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.StringSliceVarP(&options.Workflows, "workflows", "w", []string{}, "list of workflow or workflow directory to run (comma-separated, file)", goflags.FileCommaSeparatedStringSliceOptions),
|
||||
flagSet.StringSliceVarP(&options.WorkflowURLs, "workflow-url", "wu", []string{}, "list of workflow urls to run (comma-separated, file)", goflags.FileCommaSeparatedStringSliceOptions),
|
||||
flagSet.BoolVar(&options.Validate, "validate", false, "validate the passed templates to nuclei"),
|
||||
flagSet.BoolVarP(&options.NoStrictSyntax, "no-strict-syntax", "nss", false, "Disable strict syntax check on templates"),
|
||||
flagSet.BoolVarP(&options.NoStrictSyntax, "no-strict-syntax", "nss", false, "disable strict syntax check on templates"),
|
||||
flagSet.BoolVarP(&options.TemplateDisplay, "template-display", "td", false, "displays the templates content"),
|
||||
flagSet.BoolVar(&options.TemplateList, "tl", false, "list all available templates"),
|
||||
flagSet.StringSliceVarConfigOnly(&options.RemoteTemplateDomainList, "remote-template-domain", []string{"api.nuclei.sh"}, "allowed domain list to load remote templates from"),
|
||||
)
|
||||
@ -169,7 +171,7 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.BoolVar(&options.JSON, "json", false, "write output in JSONL(ines) format"),
|
||||
flagSet.BoolVarP(&options.JSONRequests, "include-rr", "irr", false, "include request/response pairs in the JSONL output (for findings only)"),
|
||||
flagSet.BoolVarP(&options.NoMeta, "no-meta", "nm", false, "disable printing result metadata in cli output"),
|
||||
flagSet.BoolVarP(&options.NoTimestamp, "no-timestamp", "nts", false, "disable printing timestamp in cli output"),
|
||||
flagSet.BoolVarP(&options.Timestamp, "timestamp", "ts", false, "enables printing timestamp in cli output"),
|
||||
flagSet.StringVarP(&options.ReportingDB, "report-db", "rdb", "", "nuclei reporting database (always use this to persist report data)"),
|
||||
flagSet.BoolVarP(&options.MatcherStatus, "matcher-status", "ms", false, "display match failure status"),
|
||||
flagSet.StringVarP(&options.MarkdownExportDirectory, "markdown-export", "me", "", "directory to export results in markdown format"),
|
||||
@ -188,6 +190,7 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.StringVarP(&options.ResolversFile, "resolvers", "r", "", "file containing resolver list for nuclei"),
|
||||
flagSet.BoolVarP(&options.SystemResolvers, "system-resolvers", "sr", false, "use system DNS resolving as error fallback"),
|
||||
flagSet.BoolVar(&options.OfflineHTTP, "passive", false, "enable passive HTTP response processing mode"),
|
||||
flagSet.BoolVarP(&options.ForceAttemptHTTP2, "force-http2", "fh2", false, "force http2 connection on requests"),
|
||||
flagSet.BoolVarP(&options.EnvironmentVariables, "env-vars", "ev", false, "enable environment variables to be used in template"),
|
||||
flagSet.StringVarP(&options.ClientCertFile, "client-cert", "cc", "", "client certificate file (PEM-encoded) used for authenticating against scanned hosts"),
|
||||
flagSet.StringVarP(&options.ClientKeyFile, "client-key", "ck", "", "client key file (PEM-encoded) used for authenticating against scanned hosts"),
|
||||
@ -195,10 +198,11 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.BoolVarP(&options.ShowMatchLine, "show-match-line", "sml", false, "show match lines for file templates, works with extractors only"),
|
||||
flagSet.BoolVar(&options.ZTLS, "ztls", false, "use ztls library with autofallback to standard one for tls13"),
|
||||
flagSet.StringVar(&options.SNI, "sni", "", "tls sni hostname to use (default: input domain name)"),
|
||||
flagSet.BoolVar(&options.Sandbox, "sandbox", false, "sandbox nuclei for safe templates execution"),
|
||||
flagSet.StringVarP(&options.Interface, "interface", "i", "", "network interface to use for network scan"),
|
||||
flagSet.StringVarP(&options.AttackType, "attack-type", "at", "", "type of payload combinations to perform (batteringram,pitchfork,clusterbomb)"),
|
||||
flagSet.StringVarP(&options.SourceIP, "source-ip", "sip", "", "source ip address to use for network scan"),
|
||||
flagSet.StringVar(&options.CustomConfigDir, "config-directory", "", "Override the default config path ($home/.config)"),
|
||||
flagSet.StringVar(&options.CustomConfigDir, "config-directory", "", "override the default config path ($home/.config)"),
|
||||
flagSet.IntVarP(&options.ResponseReadSize, "response-size-read", "rsr", 10*1024*1024, "max response size to read in bytes"),
|
||||
flagSet.IntVarP(&options.ResponseSaveSize, "response-size-save", "rss", 1*1024*1024, "max response size to read in bytes"),
|
||||
)
|
||||
@ -213,6 +217,15 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.BoolVarP(&options.NoInteractsh, "no-interactsh", "ni", false, "disable interactsh server for OAST testing, exclude OAST based templates"),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("uncover", "Uncover",
|
||||
flagSet.BoolVarP(&options.Uncover, "uncover", "uc", false, "enable uncover engine"),
|
||||
flagSet.StringSliceVarP(&options.UncoverQuery, "uncover-query", "uq", []string{}, "uncover search query", goflags.FileStringSliceOptions),
|
||||
flagSet.StringSliceVarP(&options.UncoverEngine, "uncover-engine", "ue", []string{}, fmt.Sprintf("uncover search engine (%s) (default shodan)", uncover.GetUncoverSupportedAgents()), goflags.FileStringSliceOptions),
|
||||
flagSet.StringVarP(&options.UncoverField, "uncover-field", "uf", "ip:port", "uncover fields to return (ip,port,host)"),
|
||||
flagSet.IntVarP(&options.UncoverLimit, "uncover-limit", "ul", 100, "uncover results to return"),
|
||||
flagSet.IntVarP(&options.UncoverDelay, "uncover-delay", "ucd", 1, "delay between uncover query requests in seconds (0 to disable)"),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("rate-limit", "Rate-Limit",
|
||||
flagSet.IntVarP(&options.RateLimit, "rate-limit", "rl", 150, "maximum number of requests to send per second"),
|
||||
flagSet.IntVarP(&options.RateLimitMinute, "rate-limit-minute", "rlm", 0, "maximum number of requests to send per minute"),
|
||||
@ -225,11 +238,11 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.CreateGroup("optimization", "Optimizations",
|
||||
flagSet.IntVar(&options.Timeout, "timeout", 10, "time to wait in seconds before timeout"),
|
||||
flagSet.IntVar(&options.Retries, "retries", 1, "number of times to retry a failed request"),
|
||||
flagSet.BoolVarP(&options.LeaveDefaultPorts, "leave-default-ports", "ldp", false, "leave default HTTP/HTTPS ports (eg. host:80,host:443"),
|
||||
flagSet.BoolVarP(&options.LeaveDefaultPorts, "leave-default-ports", "ldp", false, "leave default HTTP/HTTPS ports (eg. host:80,host:443)"),
|
||||
flagSet.IntVarP(&options.MaxHostError, "max-host-error", "mhe", 30, "max errors for a host before skipping from scan"),
|
||||
flagSet.BoolVar(&options.Project, "project", false, "use a project folder to avoid sending same request multiple times"),
|
||||
flagSet.StringVar(&options.ProjectPath, "project-path", os.TempDir(), "set a specific project path"),
|
||||
flagSet.BoolVarP(&options.StopAtFirstMatch, "stop-at-first-path", "spm", false, "stop processing HTTP requests after the first match (may break template/workflow logic)"),
|
||||
flagSet.BoolVarP(&options.StopAtFirstMatch, "stop-at-first-match", "spm", false, "stop processing HTTP requests after the first match (may break template/workflow logic)"),
|
||||
flagSet.BoolVar(&options.Stream, "stream", false, "stream mode - start elaborating without sorting the input"),
|
||||
flagSet.DurationVarP(&options.InputReadTimeout, "input-read-timeout", "irt", time.Duration(3*time.Minute), "timeout on input read"),
|
||||
flagSet.BoolVarP(&options.DisableHTTPProbe, "no-httpx", "nh", false, "disable httpx probing for non-url input"),
|
||||
@ -237,10 +250,10 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("headless", "Headless",
|
||||
flagSet.BoolVar(&options.Headless, "headless", false, "enable templates that require headless browser support (root user on linux will disable sandbox)"),
|
||||
flagSet.BoolVar(&options.Headless, "headless", false, "enable templates that require headless browser support (root user on Linux will disable sandbox)"),
|
||||
flagSet.IntVar(&options.PageTimeout, "page-timeout", 20, "seconds to wait for each page in headless mode"),
|
||||
flagSet.BoolVarP(&options.ShowBrowser, "show-browser", "sb", false, "show the browser on the screen when running templates with headless mode"),
|
||||
flagSet.BoolVarP(&options.UseInstalledChrome, "system-chrome", "sc", false, "Use local installed chrome browser instead of nuclei installed"),
|
||||
flagSet.BoolVarP(&options.UseInstalledChrome, "system-chrome", "sc", false, "use local installed Chrome browser instead of nuclei installed"),
|
||||
flagSet.BoolVarP(&options.ShowActions, "list-headless-action", "lha", false, "list available headless actions"),
|
||||
)
|
||||
|
||||
@ -258,7 +271,7 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.BoolVarP(&options.Verbose, "verbose", "v", false, "show verbose output"),
|
||||
flagSet.StringVar(&memProfile, "profile-mem", "", "optional nuclei memory profile dump file"),
|
||||
flagSet.BoolVar(&options.VerboseVerbose, "vv", false, "display templates loaded for scan"),
|
||||
flagSet.BoolVarP(&options.ShowVarDump, "show-var-dump", "sdp", false, "show variables dump for debugging"),
|
||||
flagSet.BoolVarP(&options.ShowVarDump, "show-var-dump", "svd", false, "show variables dump for debugging"),
|
||||
flagSet.BoolVarP(&options.EnablePprof, "enable-pprof", "ep", false, "enable pprof debugging server"),
|
||||
flagSet.BoolVarP(&options.TemplatesVersion, "templates-version", "tv", false, "shows the version of the installed nuclei-templates"),
|
||||
flagSet.BoolVarP(&options.HealthCheck, "health-check", "hc", false, "run diagnostic check up"),
|
||||
@ -268,8 +281,6 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.BoolVarP(&options.UpdateNuclei, "update", "un", false, "update nuclei engine to the latest released version"),
|
||||
flagSet.BoolVarP(&options.UpdateTemplates, "update-templates", "ut", false, "update nuclei-templates to latest released version"),
|
||||
flagSet.StringVarP(&options.TemplatesDirectory, "update-template-dir", "ud", "", "custom directory to install / update nuclei-templates"),
|
||||
flagSet.StringVarEnv(&options.GithubToken, "github-token", "gt", "", "GITHUB_TOKEN", "github token to download public/private templates (GITHUB_TOKEN)"),
|
||||
flagSet.StringSliceVarP(&options.GithubTemplateRepo, "github-template-repo", "gtr", []string{}, "github template repository to download / update (GITHUB_TEMPLATE_REPO)", goflags.FileCommaSeparatedStringSliceOptions),
|
||||
flagSet.BoolVarP(&options.NoUpdateTemplates, "disable-update-check", "duc", false, "disable automatic nuclei/templates update check"),
|
||||
flagSet.StringVarEnv(&options.AwsAccessKey, "aws-access-key", "aak", "", "AWS_ACCESS_KEY", "aws access key to download template from bucket (AWS_ACCESS_KEY)"),
|
||||
flagSet.StringVarEnv(&options.AwsSecretKey, "aws-secret-key", "ask", "", "AWS_SECRET_KEY", "aws secret key to download template from bucket (AWS_SECRET_KEY)"),
|
||||
@ -309,13 +320,6 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
if options.LeaveDefaultPorts {
|
||||
http.LeaveDefaultPorts = true
|
||||
}
|
||||
if os.Getenv("GITHUB_TEMPLATE_REPO") != "" {
|
||||
// there is no flag Env function for slice variable type yet.
|
||||
err := options.GithubTemplateRepo.Set(os.Getenv("GITHUB_TEMPLATE_REPO"))
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("Could not read GITHUB_TEMPLATE_REPO env variable: %s\n", err)
|
||||
}
|
||||
}
|
||||
if options.CustomConfigDir != "" {
|
||||
originalIgnorePath := config.GetIgnoreFilePath()
|
||||
config.SetCustomConfigDirectory(options.CustomConfigDir)
|
||||
|
||||
132
v2/go.mod
132
v2/go.mod
@ -12,64 +12,59 @@ require (
|
||||
github.com/bluele/gcache v0.0.2
|
||||
github.com/corpix/uarand v0.2.0
|
||||
github.com/go-playground/validator/v10 v10.11.1
|
||||
github.com/go-rod/rod v0.112.0
|
||||
github.com/go-rod/rod v0.112.2
|
||||
github.com/gobwas/ws v1.1.0
|
||||
github.com/google/go-github v17.0.0+incompatible
|
||||
github.com/itchyny/gojq v0.12.9
|
||||
github.com/itchyny/gojq v0.12.10
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
github.com/karlseguin/ccache v2.0.3+incompatible
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/owenrumney/go-sarif/v2 v2.1.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/projectdiscovery/clistats v0.0.8
|
||||
github.com/projectdiscovery/fastdialer v0.0.18-0.20221102102120-8e9343e8b0e0
|
||||
github.com/projectdiscovery/filekv v0.0.0-20210915124239-3467ef45dd08
|
||||
github.com/projectdiscovery/gologger v1.1.4
|
||||
github.com/projectdiscovery/clistats v0.0.9
|
||||
github.com/projectdiscovery/fastdialer v0.0.19
|
||||
github.com/projectdiscovery/gologger v1.1.5
|
||||
github.com/projectdiscovery/hmap v0.0.2
|
||||
github.com/projectdiscovery/interactsh v1.0.6-0.20220827132222-460cc6270053
|
||||
github.com/projectdiscovery/nuclei-updatecheck-api v0.0.0-20211006155443-c0a8d610a4df
|
||||
github.com/projectdiscovery/rawhttp v0.1.2
|
||||
github.com/projectdiscovery/rawhttp v0.1.4
|
||||
github.com/projectdiscovery/retryabledns v1.0.17
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.5-0.20221203124408-1d25b06b0572
|
||||
github.com/projectdiscovery/stringsutil v0.0.2 // indirect
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.6-0.20221206071935-7924d7d34953
|
||||
github.com/projectdiscovery/stringsutil v0.0.2
|
||||
github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211126104922-00d2c6bb43b6
|
||||
github.com/remeh/sizedwaitgroup v1.0.0
|
||||
github.com/rs/xid v1.4.0
|
||||
github.com/segmentio/ksuid v1.0.4
|
||||
github.com/shirou/gopsutil/v3 v3.22.10
|
||||
github.com/shirou/gopsutil/v3 v3.22.11
|
||||
github.com/spaolacci/murmur3 v1.1.0
|
||||
github.com/spf13/cast v1.5.0
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/tj/go-update v2.2.5-0.20200519121640-62b4b798fd68+incompatible
|
||||
github.com/valyala/fasttemplate v1.2.2
|
||||
github.com/weppos/publicsuffix-go v0.15.1-0.20220724114530-e087fba66a37
|
||||
github.com/xanzy/go-gitlab v0.74.0
|
||||
github.com/xanzy/go-gitlab v0.76.0
|
||||
go.uber.org/atomic v1.10.0
|
||||
go.uber.org/multierr v1.8.0
|
||||
golang.org/x/net v0.2.0
|
||||
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c
|
||||
golang.org/x/text v0.4.0
|
||||
golang.org/x/text v0.5.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
moul.io/http2curl v1.0.0
|
||||
)
|
||||
|
||||
require github.com/aws/aws-sdk-go v1.44.134
|
||||
|
||||
require (
|
||||
github.com/DataDog/gostackparse v0.6.0
|
||||
github.com/antchfx/xmlquery v1.3.12
|
||||
github.com/antchfx/xmlquery v1.3.13
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.1
|
||||
github.com/aws/aws-sdk-go-v2/config v1.17.10
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.12.23
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.37
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.2
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.4
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.4
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.43
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.29.5
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/go-git/go-git/v5 v5.4.2
|
||||
github.com/go-git/go-git/v5 v5.5.0
|
||||
github.com/h2non/filetype v1.1.3
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/klauspost/compress v1.15.12
|
||||
@ -77,19 +72,40 @@ require (
|
||||
github.com/mholt/archiver v3.1.1+incompatible
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/projectdiscovery/fasttemplate v0.0.2
|
||||
github.com/projectdiscovery/goflags v0.1.3
|
||||
github.com/projectdiscovery/filekv v0.0.0-20210915124239-3467ef45dd08
|
||||
github.com/projectdiscovery/goflags v0.1.5
|
||||
github.com/projectdiscovery/nvd v1.0.9
|
||||
github.com/projectdiscovery/ratelimit v0.0.0-20221004232058-7b82379157fa
|
||||
github.com/projectdiscovery/ratelimit v0.0.2
|
||||
github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917
|
||||
github.com/projectdiscovery/tlsx v0.0.7
|
||||
github.com/projectdiscovery/sarif v0.0.1
|
||||
github.com/projectdiscovery/tlsx v1.0.0
|
||||
github.com/projectdiscovery/uncover v1.0.1
|
||||
github.com/projectdiscovery/utils v0.0.4-0.20221201124851-f8524345b6d3
|
||||
github.com/projectdiscovery/wappalyzergo v0.0.67
|
||||
github.com/projectdiscovery/wappalyzergo v0.0.71
|
||||
github.com/stretchr/testify v1.8.1
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require github.com/projectdiscovery/asnmap v0.0.1 // indirect
|
||||
require (
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.20 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.2.0 // indirect
|
||||
github.com/bits-and-blooms/bloom/v3 v3.0.1 // indirect
|
||||
github.com/cloudflare/circl v1.1.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/karlseguin/expect v1.0.8 // indirect
|
||||
github.com/pjbgf/sha1cd v0.2.0 // indirect
|
||||
github.com/projectdiscovery/asnmap v0.0.1 // indirect
|
||||
github.com/projectdiscovery/sliceutil v0.0.1 // indirect
|
||||
github.com/skeema/knownhosts v1.1.0 // indirect
|
||||
github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4 // indirect
|
||||
gopkg.in/djherbis/times.v1 v1.3.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
git.mills.io/prologic/smtpd v0.0.0-20210710122116-a525b76c287a // indirect
|
||||
@ -104,8 +120,6 @@ require (
|
||||
github.com/andybalholm/cascadia v1.1.0 // indirect
|
||||
github.com/antchfx/xpath v1.2.1 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.2.0 // indirect
|
||||
github.com/bits-and-blooms/bloom/v3 v3.0.1 // indirect
|
||||
github.com/c4milo/unpackit v0.1.0 // indirect
|
||||
github.com/caddyserver/certmagic v0.16.3 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
@ -142,18 +156,17 @@ require (
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
|
||||
github.com/hdm/jarm-go v0.0.7 // indirect
|
||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect
|
||||
github.com/itchyny/timefmt-go v0.1.4 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/itchyny/timefmt-go v0.1.5 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mholt/acmez v1.0.4 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.21 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
@ -164,21 +177,21 @@ require (
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/projectdiscovery/blackrock v0.0.0-20220628111055-35616c71b2dc // indirect
|
||||
github.com/projectdiscovery/mapcidr v1.0.3
|
||||
github.com/projectdiscovery/networkpolicy v0.0.2-0.20220525172507-b844eafc878d // indirect
|
||||
github.com/projectdiscovery/networkpolicy v0.0.3
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
|
||||
github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a // indirect
|
||||
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||
github.com/trivago/tgo v1.0.7 // indirect
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/yl2chen/cidranger v1.0.2 // indirect
|
||||
github.com/ysmood/goob v0.4.0 // indirect
|
||||
github.com/ysmood/gson v0.7.1 // indirect
|
||||
github.com/ysmood/gson v0.7.3 // indirect
|
||||
github.com/ysmood/leakless v0.8.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect
|
||||
@ -186,7 +199,7 @@ require (
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.uber.org/zap v1.23.0 // indirect
|
||||
goftp.io/server/v2 v2.0.0 // indirect
|
||||
golang.org/x/crypto v0.1.0 // indirect
|
||||
golang.org/x/crypto v0.3.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221019170559-20944726eadf
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
@ -199,41 +212,36 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.4.16 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.11.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.17.1 // indirect
|
||||
github.com/aws/smithy-go v1.13.4 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/alecthomas/chroma v0.10.0
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.26 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.11.26 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.17.6 // indirect
|
||||
github.com/aws/smithy-go v1.13.5 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/nwaples/rardecode v1.1.2 // indirect
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
|
||||
github.com/projectdiscovery/cryptoutil v0.0.0-20210805184155-b5d2512f9345 // indirect; indirectdev
|
||||
github.com/projectdiscovery/fileutil v0.0.3
|
||||
github.com/projectdiscovery/iputil v0.0.2 // indirect
|
||||
github.com/projectdiscovery/sliceutil v0.0.1 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/src-d/gcfg v1.4.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.2 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
@ -12,14 +12,11 @@ var banner = fmt.Sprintf(`
|
||||
____ __ _______/ /__ (_)
|
||||
/ __ \/ / / / ___/ / _ \/ /
|
||||
/ / / / /_/ / /__/ / __/ /
|
||||
/_/ /_/\__,_/\___/_/\___/_/ %s
|
||||
/_/ /_/\__,_/\___/_/\___/_/ v%s
|
||||
`, config.Version)
|
||||
|
||||
// showBanner is used to show the banner to the user
|
||||
func showBanner() {
|
||||
gologger.Print().Msgf("%s\n", banner)
|
||||
gologger.Print().Msgf("\t\tprojectdiscovery.io\n\n")
|
||||
|
||||
gologger.Print().Label("WRN").Msgf("Use with caution. You are responsible for your actions.\n")
|
||||
gologger.Print().Label("WRN").Msgf("Developers assume no liability and are not responsible for any misuse or damage.\n")
|
||||
}
|
||||
|
||||
@ -3,9 +3,11 @@ package runner
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/corpix/uarand"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/hmap/store/hybrid"
|
||||
@ -29,7 +31,7 @@ func (r *Runner) initializeTemplatesHTTPInput() (*hybrid.HybridMap, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get http client")
|
||||
}
|
||||
gologger.Info().Msgf("Running httpx on input to execute http based template")
|
||||
gologger.Info().Msgf("Running httpx on input host")
|
||||
|
||||
var bulkSize = probeBulkSize
|
||||
if r.options.BulkSize > probeBulkSize {
|
||||
@ -56,7 +58,7 @@ func (r *Runner) initializeTemplatesHTTPInput() (*hybrid.HybridMap, error) {
|
||||
})
|
||||
swg.Wait()
|
||||
|
||||
gologger.Info().Msgf("Discovered %d URL from input", atomic.LoadInt32(&count))
|
||||
gologger.Info().Msgf("Found %d URL from httpx", atomic.LoadInt32(&count))
|
||||
return hm, nil
|
||||
}
|
||||
|
||||
@ -71,7 +73,13 @@ var (
|
||||
func probeURL(input string, httpclient *retryablehttp.Client) string {
|
||||
for _, scheme := range httpSchemes {
|
||||
formedURL := fmt.Sprintf("%s://%s", scheme, input)
|
||||
resp, err := httpclient.Get(formedURL)
|
||||
req, err := retryablehttp.NewRequest(http.MethodGet, formedURL, nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Set("User-Agent", uarand.GetRandom())
|
||||
|
||||
resp, err := httpclient.Do(req)
|
||||
if resp != nil {
|
||||
_, _ = io.CopyN(io.Discard, resp.Body, drainReqSize)
|
||||
resp.Body.Close()
|
||||
|
||||
@ -30,7 +30,11 @@ func ReadCatalogChecksum() map[string]string {
|
||||
if len(text) < 2 {
|
||||
continue
|
||||
}
|
||||
checksums[text[0]] = text[1]
|
||||
path := strings.TrimPrefix(text[0], "nuclei-templates/")
|
||||
if strings.HasPrefix(path, ".") {
|
||||
continue
|
||||
}
|
||||
checksums[path] = text[1]
|
||||
}
|
||||
return checksums
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/utils/vardump"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/headless/engine"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
"github.com/projectdiscovery/stringsutil"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
)
|
||||
|
||||
@ -40,6 +41,9 @@ func ParseOptions(options *types.Options) {
|
||||
// Check if stdin pipe was given
|
||||
options.Stdin = !options.DisableStdin && fileutil.HasStdin()
|
||||
|
||||
// Read the inputs from env variables that not passed by flag.
|
||||
readEnvInputVars(options)
|
||||
|
||||
// Read the inputs and configure the logging
|
||||
configureOutput(options)
|
||||
// Show the user the banner
|
||||
@ -61,7 +65,13 @@ func ParseOptions(options *types.Options) {
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("Could not read template configuration: %s\n", err)
|
||||
}
|
||||
gologger.Info().Msgf("Current nuclei-templates version: %s (%s)\n", configuration.TemplateVersion, configuration.TemplatesDirectory)
|
||||
gologger.Info().Msgf("Public nuclei-templates version: %s (%s)\n", configuration.TemplateVersion, configuration.TemplatesDirectory)
|
||||
if configuration.CustomS3TemplatesDirectory != "" {
|
||||
gologger.Info().Msgf("Custom S3 templates location: %s\n", configuration.CustomS3TemplatesDirectory)
|
||||
}
|
||||
if configuration.CustomGithubTemplatesDirectory != "" {
|
||||
gologger.Info().Msgf("Custom Github templates location: %s ", configuration.CustomGithubTemplatesDirectory)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
if options.ShowActions {
|
||||
@ -101,6 +111,13 @@ func ParseOptions(options *types.Options) {
|
||||
if options.GithubToken != "" && os.Getenv("GITHUB_TOKEN") != options.GithubToken {
|
||||
os.Setenv("GITHUB_TOKEN", options.GithubToken)
|
||||
}
|
||||
|
||||
if options.UncoverQuery != nil {
|
||||
options.Uncover = true
|
||||
if len(options.UncoverEngine) == 0 {
|
||||
options.UncoverEngine = append(options.UncoverEngine, "shodan")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validateOptions validates the configuration options passed
|
||||
@ -141,13 +158,28 @@ func validateOptions(options *types.Options) error {
|
||||
}
|
||||
validateCertificatePaths([]string{options.ClientCertFile, options.ClientKeyFile, options.ClientCAFile})
|
||||
}
|
||||
// Verify aws secrets are passed if s3 tempalte bucket passed
|
||||
if options.AwsBucketName != "" && (options.AwsAccessKey == "" || options.AwsSecretKey == "" || options.AwsRegion == "") {
|
||||
fmt.Printf("b: %s k: %s s: %s r: %s", options.AwsBucketName, options.AwsAccessKey, options.AwsSecretKey, options.AwsRegion)
|
||||
return errors.New("aws s3 bucket details are missing. Please provide region, access and secret key")
|
||||
// Verify aws secrets are passed if s3 template bucket passed
|
||||
if options.AwsBucketName != "" && options.UpdateTemplates {
|
||||
var missing []string
|
||||
if options.AwsAccessKey == "" {
|
||||
missing = append(missing, "AWS_ACCESS_KEY")
|
||||
}
|
||||
if options.AwsSecretKey == "" {
|
||||
missing = append(missing, "AWS_SECRET_KEY")
|
||||
}
|
||||
if options.AwsRegion == "" {
|
||||
missing = append(missing, "AWS_REGION")
|
||||
}
|
||||
if missing != nil {
|
||||
return fmt.Errorf("aws s3 bucket details are missing. Please provide %s", strings.Join(missing, ","))
|
||||
}
|
||||
}
|
||||
|
||||
// verify that a valid ip version type was selected (4, 6)
|
||||
if len(options.IPVersion) == 0 {
|
||||
// add ipv4 as default
|
||||
options.IPVersion = append(options.IPVersion, "4")
|
||||
}
|
||||
var useIPV4, useIPV6 bool
|
||||
for _, ipv := range options.IPVersion {
|
||||
switch ipv {
|
||||
@ -240,3 +272,16 @@ func validateCertificatePaths(certificatePaths []string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read the input from env and set options
|
||||
func readEnvInputVars(options *types.Options) {
|
||||
options.GithubToken = os.Getenv("GITHUB_TOKEN")
|
||||
repolist := os.Getenv("GITHUB_TEMPLATE_REPO")
|
||||
if repolist != "" {
|
||||
options.GithubTemplateRepo = append(options.GithubTemplateRepo, stringsutil.SplitAny(repolist, ",")...)
|
||||
}
|
||||
options.AwsAccessKey = os.Getenv("AWS_ACCESS_KEY")
|
||||
options.AwsSecretKey = os.Getenv("AWS_SECRET_KEY")
|
||||
options.AwsBucketName = os.Getenv("AWS_TEMPLATE_BUCKET")
|
||||
options.AwsRegion = os.Getenv("AWS_REGION")
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/hosterrorscache"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/interactsh"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolinit"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/uncover"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/utils/excludematchers"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/headless/engine"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/httpclientpool"
|
||||
@ -74,7 +75,7 @@ type Runner struct {
|
||||
hostErrors hosterrorscache.CacheInterface
|
||||
resumeCfg *types.ResumeCfg
|
||||
pprofServer *http.Server
|
||||
customTemplates *[]customtemplates.CustomTemplateProvider
|
||||
customTemplates []customtemplates.Provider
|
||||
cloudClient *nucleicloud.Client
|
||||
}
|
||||
|
||||
@ -185,7 +186,7 @@ func New(options *types.Options) (*Runner, error) {
|
||||
runner.hmapInputProvider = hmapInput
|
||||
|
||||
// Create the output file if asked
|
||||
outputWriter, err := output.NewStandardWriter(!options.NoColor, options.NoMeta, options.NoTimestamp, options.JSON, options.JSONRequests, options.MatcherStatus, options.StoreResponse, options.Output, options.TraceLogFile, options.ErrorLogFile, options.StoreResponseDir)
|
||||
outputWriter, err := output.NewStandardWriter(!options.NoColor, options.NoMeta, options.Timestamp, options.JSON, options.JSONRequests, options.MatcherStatus, options.StoreResponse, options.Output, options.TraceLogFile, options.ErrorLogFile, options.StoreResponseDir)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not create output file")
|
||||
}
|
||||
@ -256,9 +257,9 @@ func New(options *types.Options) (*Runner, error) {
|
||||
}
|
||||
|
||||
if options.RateLimitMinute > 0 {
|
||||
runner.ratelimiter = ratelimit.New(context.Background(), options.RateLimitMinute, time.Minute)
|
||||
runner.ratelimiter = ratelimit.New(context.Background(), uint(options.RateLimitMinute), time.Minute)
|
||||
} else if options.RateLimit > 0 {
|
||||
runner.ratelimiter = ratelimit.New(context.Background(), options.RateLimit, time.Second)
|
||||
runner.ratelimiter = ratelimit.New(context.Background(), uint(options.RateLimit), time.Second)
|
||||
} else {
|
||||
runner.ratelimiter = ratelimit.NewUnlimited(context.Background())
|
||||
}
|
||||
@ -443,8 +444,15 @@ func (r *Runner) RunEnumeration() error {
|
||||
}
|
||||
store.Load()
|
||||
|
||||
// add the hosts from the metadata queries of loaded templates into input provider
|
||||
if r.options.Uncover && len(r.options.UncoverQuery) == 0 {
|
||||
ret := uncover.GetUncoverTargetsFromMetadata(store.Templates(), r.options.UncoverDelay, r.options.UncoverLimit, r.options.UncoverField)
|
||||
for host := range ret {
|
||||
r.hmapInputProvider.Set(host)
|
||||
}
|
||||
}
|
||||
// list all templates
|
||||
if r.options.TemplateList {
|
||||
if r.options.TemplateList || r.options.TemplateDisplay {
|
||||
r.listAvailableStoreTemplates(store)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
@ -1,10 +1,15 @@
|
||||
package runner
|
||||
|
||||
import (
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader"
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/chroma/quick"
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/parsers"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
||||
@ -17,36 +22,73 @@ func (r *Runner) logAvailableTemplate(tplPath string) {
|
||||
if err != nil {
|
||||
gologger.Error().Msgf("Could not parse file '%s': %s\n", tplPath, err)
|
||||
} else {
|
||||
gologger.Print().Msgf("%s\n", templates.TemplateLogMessage(t.ID,
|
||||
types.ToString(t.Info.Name),
|
||||
t.Info.Authors.ToSlice(),
|
||||
t.Info.SeverityHolder.Severity))
|
||||
r.verboseTemplate(t)
|
||||
}
|
||||
}
|
||||
|
||||
// log available templates for verbose (-vv)
|
||||
func (r *Runner) verboseTemplate(tpl *templates.Template) {
|
||||
gologger.Print().Msgf("%s\n", templates.TemplateLogMessage(tpl.ID,
|
||||
types.ToString(tpl.Info.Name),
|
||||
tpl.Info.Authors.ToSlice(),
|
||||
tpl.Info.SeverityHolder.Severity))
|
||||
}
|
||||
|
||||
func (r *Runner) listAvailableStoreTemplates(store *loader.Store) {
|
||||
gologger.Print().Msgf(
|
||||
"\nListing available v.%s nuclei templates for %s",
|
||||
r.templatesConfig.TemplateVersion,
|
||||
r.templatesConfig.TemplatesDirectory,
|
||||
)
|
||||
extraFlags := r.options.Templates != nil || r.options.Authors != nil ||
|
||||
r.options.Tags != nil || len(r.options.ExcludeTags) > 3 ||
|
||||
r.options.IncludeTags != nil || r.options.IncludeIds != nil ||
|
||||
r.options.ExcludeIds != nil || r.options.IncludeTemplates != nil ||
|
||||
r.options.ExcludedTemplates != nil || r.options.ExcludeMatchers != nil ||
|
||||
r.options.Severities != nil || r.options.ExcludeSeverities != nil ||
|
||||
r.options.Protocols != nil || r.options.ExcludeProtocols != nil ||
|
||||
r.options.IncludeConditions != nil || r.options.TemplateList
|
||||
for _, tl := range store.Templates() {
|
||||
if extraFlags {
|
||||
path := strings.TrimPrefix(tl.Path, r.templatesConfig.TemplatesDirectory+string(filepath.Separator))
|
||||
gologger.Silent().Msgf("%s\n", path)
|
||||
for _, tpl := range store.Templates() {
|
||||
if hasExtraFlags(r.options) {
|
||||
if r.options.TemplateDisplay {
|
||||
colorize := !r.options.NoColor
|
||||
|
||||
path := tpl.Path
|
||||
tplBody, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
gologger.Error().Msgf("Could not read the template %s: %s", path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if colorize {
|
||||
path = aurora.Cyan(tpl.Path).String()
|
||||
tplBody, err = r.highlightTemplate(&tplBody)
|
||||
if err != nil {
|
||||
gologger.Error().Msgf("Could not hihglight the template %s: %s", tpl.Path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
gologger.Silent().Msgf("Template: %s\n\n%s", path, tplBody)
|
||||
} else {
|
||||
gologger.Silent().Msgf("%s\n", strings.TrimPrefix(tpl.Path, r.templatesConfig.TemplatesDirectory+string(filepath.Separator)))
|
||||
}
|
||||
} else {
|
||||
gologger.Print().Msgf("%s\n", templates.TemplateLogMessage(tl.ID,
|
||||
types.ToString(tl.Info.Name),
|
||||
tl.Info.Authors.ToSlice(),
|
||||
tl.Info.SeverityHolder.Severity))
|
||||
r.verboseTemplate(tpl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Runner) highlightTemplate(body *[]byte) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
// YAML lexer, true color terminar formatter and monokai style
|
||||
err := quick.Highlight(&buf, string(*body), "yaml", "terminal16m", "monokai")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func hasExtraFlags(options *types.Options) bool {
|
||||
return options.Templates != nil || options.Authors != nil ||
|
||||
options.Tags != nil || len(options.ExcludeTags) > 3 ||
|
||||
options.IncludeTags != nil || options.IncludeIds != nil ||
|
||||
options.ExcludeIds != nil || options.IncludeTemplates != nil ||
|
||||
options.ExcludedTemplates != nil || options.ExcludeMatchers != nil ||
|
||||
options.Severities != nil || options.ExcludeSeverities != nil ||
|
||||
options.Protocols != nil || options.ExcludeProtocols != nil ||
|
||||
options.IncludeConditions != nil || options.TemplateList
|
||||
}
|
||||
|
||||
@ -25,8 +25,9 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei-updatecheck-api/client"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/external/customtemplates"
|
||||
client "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/updatecheck"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/utils"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
folderutil "github.com/projectdiscovery/utils/folder"
|
||||
@ -37,12 +38,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
userName = "projectdiscovery"
|
||||
repoName = "nuclei-templates"
|
||||
nucleiIgnoreFile = ".nuclei-ignore"
|
||||
nucleiConfigFilename = ".templates-config.json"
|
||||
customGithubTemplateDirectory = "github"
|
||||
customS3TemplateDirectory = "s3"
|
||||
userName = "projectdiscovery"
|
||||
repoName = "nuclei-templates"
|
||||
nucleiIgnoreFile = ".nuclei-ignore"
|
||||
nucleiConfigFilename = ".templates-config.json"
|
||||
)
|
||||
|
||||
var reVersion = regexp.MustCompile(`\d+\.\d+\.\d+`)
|
||||
@ -105,11 +104,9 @@ func (r *Runner) updateTemplates() error { // TODO this method does more than ju
|
||||
}
|
||||
|
||||
// download | update the custom templates repos
|
||||
for _, ct := range *r.customTemplates {
|
||||
if r.options.UpdateTemplates {
|
||||
if r.options.UpdateTemplates {
|
||||
for _, ct := range r.customTemplates {
|
||||
ct.Update(r.templatesConfig.TemplatesDirectory, ctx)
|
||||
} else {
|
||||
ct.Download(r.templatesConfig.TemplatesDirectory, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,10 +130,13 @@ func (r *Runner) updateTemplates() error { // TODO this method does more than ju
|
||||
|
||||
// createDefaultConfig create template config file is template config is not found
|
||||
func (r *Runner) createDefaultConfig(defaultTemplatesDirectory string) error {
|
||||
if r.templatesConfig == nil {
|
||||
// TODO remove customTemplate check in next version.
|
||||
if r.templatesConfig == nil || r.templatesConfig.CustomGithubTemplatesDirectory == "" || r.templatesConfig.CustomS3TemplatesDirectory == "" {
|
||||
currentConfig := &config.Config{
|
||||
TemplatesDirectory: defaultTemplatesDirectory,
|
||||
NucleiVersion: config.Version,
|
||||
TemplatesDirectory: defaultTemplatesDirectory,
|
||||
NucleiVersion: config.Version,
|
||||
CustomS3TemplatesDirectory: filepath.Join(defaultTemplatesDirectory, customtemplates.CustomS3TemplateDirectory),
|
||||
CustomGithubTemplatesDirectory: filepath.Join(defaultTemplatesDirectory, customtemplates.CustomGithubTemplateDirectory),
|
||||
}
|
||||
r.templatesConfig = currentConfig
|
||||
if writeErr := config.WriteConfiguration(currentConfig); writeErr != nil {
|
||||
@ -175,7 +175,7 @@ func (r *Runner) freshTemplateInstallation(configDir string, ctx context.Context
|
||||
gologger.Info().Msgf("Successfully downloaded nuclei-templates (v%s) to %s. GoodLuck!\n", version.String(), r.templatesConfig.TemplatesDirectory)
|
||||
|
||||
// case where -gtr flag is passed for the first time installation
|
||||
for _, ct := range *r.customTemplates {
|
||||
for _, ct := range r.customTemplates {
|
||||
ct.Download(r.templatesConfig.TemplatesDirectory, ctx)
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -16,9 +16,13 @@ import (
|
||||
// Config contains the internal nuclei engine configuration
|
||||
type Config struct {
|
||||
TemplatesDirectory string `json:"nuclei-templates-directory,omitempty"`
|
||||
TemplateVersion string `json:"nuclei-templates-version,omitempty"`
|
||||
NucleiVersion string `json:"nuclei-version,omitempty"`
|
||||
NucleiIgnoreHash string `json:"nuclei-ignore-hash,omitempty"`
|
||||
|
||||
CustomS3TemplatesDirectory string `json:"custom-s3-templates-directory"`
|
||||
CustomGithubTemplatesDirectory string `json:"custom-github-templates-directory"`
|
||||
|
||||
TemplateVersion string `json:"nuclei-templates-version,omitempty"`
|
||||
NucleiVersion string `json:"nuclei-version,omitempty"`
|
||||
NucleiIgnoreHash string `json:"nuclei-ignore-hash,omitempty"`
|
||||
|
||||
NucleiLatestVersion string `json:"nuclei-latest-version"`
|
||||
NucleiTemplatesLatestVersion string `json:"nuclei-templates-latest-version"`
|
||||
@ -28,7 +32,7 @@ type Config struct {
|
||||
const nucleiConfigFilename = ".templates-config.json"
|
||||
|
||||
// Version is the current version of nuclei
|
||||
const Version = `2.7.8`
|
||||
const Version = `2.8.1`
|
||||
|
||||
var customConfigDirectory string
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ package loader
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
@ -291,6 +292,11 @@ func (store *Store) LoadTemplates(templatesList []string) []*templates.Template
|
||||
gologger.Warning().Msgf("Could not load template %s: %s\n", templatePath, err)
|
||||
}
|
||||
}
|
||||
|
||||
sort.SliceStable(loadedTemplates, func(i, j int) bool {
|
||||
return loadedTemplates[i].Path < loadedTemplates[j].Path
|
||||
})
|
||||
|
||||
return loadedTemplates
|
||||
}
|
||||
|
||||
|
||||
@ -31,6 +31,8 @@ type InputProvider interface {
|
||||
// Scan iterates the input and each found item is passed to the
|
||||
// callback consumer.
|
||||
Scan(callback func(value *contextargs.MetaInput) bool)
|
||||
// Set adds item to input provider
|
||||
Set(value string)
|
||||
}
|
||||
|
||||
// New returns a new Engine instance
|
||||
|
||||
@ -19,10 +19,11 @@ import (
|
||||
asn "github.com/projectdiscovery/mapcidr/asn"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/uncover"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
iputil "github.com/projectdiscovery/utils/ip"
|
||||
"github.com/projectdiscovery/utils/reader"
|
||||
readerutil "github.com/projectdiscovery/utils/reader"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
)
|
||||
|
||||
@ -91,13 +92,13 @@ func (i *Input) initializeInputSources(options *types.Options) error {
|
||||
case asn.IsASN(target):
|
||||
i.expandASNInputValue(target)
|
||||
default:
|
||||
i.normalizeStoreInputValue(target)
|
||||
i.Set(target)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle stdin
|
||||
if options.Stdin {
|
||||
i.scanInputFromReader(reader.TimeoutReader{Reader: os.Stdin, Timeout: time.Duration(options.InputReadTimeout)})
|
||||
i.scanInputFromReader(readerutil.TimeoutReader{Reader: os.Stdin, Timeout: time.Duration(options.InputReadTimeout)})
|
||||
}
|
||||
|
||||
// Handle target file
|
||||
@ -110,6 +111,16 @@ func (i *Input) initializeInputSources(options *types.Options) error {
|
||||
|
||||
i.scanInputFromReader(input)
|
||||
}
|
||||
if options.Uncover && options.UncoverQuery != nil {
|
||||
gologger.Info().Msgf("Running uncover query against: %s", strings.Join(options.UncoverEngine, ","))
|
||||
ch, err := uncover.GetTargetsFromUncover(options.UncoverDelay, options.UncoverLimit, options.UncoverField, options.UncoverEngine, options.UncoverQuery)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for c := range ch {
|
||||
i.Set(c)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -124,43 +135,30 @@ func (i *Input) scanInputFromReader(reader io.Reader) {
|
||||
case asn.IsASN(item):
|
||||
i.expandASNInputValue(item)
|
||||
default:
|
||||
i.normalizeStoreInputValue(item)
|
||||
i.Set(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// normalizeStoreInputValue normalizes and stores passed input values
|
||||
func (i *Input) normalizeStoreInputValue(value string) {
|
||||
// Set normalizes and stores passed input values
|
||||
func (i *Input) Set(value string) {
|
||||
URL := strings.TrimSpace(value)
|
||||
if URL == "" {
|
||||
return
|
||||
}
|
||||
|
||||
metaInput := &contextargs.MetaInput{Input: URL}
|
||||
keyURL, err := metaInput.MarshalString()
|
||||
if err != nil {
|
||||
gologger.Warning().Msgf("%s\n", err)
|
||||
return
|
||||
// actual hostname
|
||||
var host string
|
||||
// parse hostname if url is given
|
||||
parsedURL, err := url.Parse(value)
|
||||
if err == nil && parsedURL.Host != "" {
|
||||
host = parsedURL.Host
|
||||
} else {
|
||||
parsedURL = nil
|
||||
host = value
|
||||
}
|
||||
|
||||
if _, ok := i.hostMap.Get(keyURL); ok {
|
||||
i.dupeCount++
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case i.ipOptions.ScanAllIPs:
|
||||
// we need to resolve the hostname
|
||||
// check if it's an url
|
||||
var host string
|
||||
parsedURL, err := url.Parse(value)
|
||||
if err == nil && parsedURL.Host != "" {
|
||||
host = parsedURL.Host
|
||||
} else {
|
||||
parsedURL = nil
|
||||
host = value
|
||||
}
|
||||
|
||||
if i.ipOptions.ScanAllIPs {
|
||||
// scan all ips
|
||||
dnsData, err := protocolstate.Dialer.GetDNSData(host)
|
||||
if err == nil && (len(dnsData.A)+len(dnsData.AAAA)) > 0 {
|
||||
var ips []string
|
||||
@ -170,36 +168,63 @@ func (i *Input) normalizeStoreInputValue(value string) {
|
||||
if i.ipOptions.IPV6 {
|
||||
ips = append(ips, dnsData.AAAA...)
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
if ip == "" {
|
||||
continue
|
||||
}
|
||||
metaInput := &contextargs.MetaInput{Input: value, CustomIP: ip}
|
||||
key, err := metaInput.MarshalString()
|
||||
if err != nil {
|
||||
gologger.Warning().Msgf("%s\n", err)
|
||||
continue
|
||||
}
|
||||
_ = i.hostMap.Set(key, nil)
|
||||
if i.hostMapStream != nil {
|
||||
_ = i.hostMapStream.Set([]byte(key), nil)
|
||||
}
|
||||
i.setItem(metaInput)
|
||||
}
|
||||
break
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
i.setItem(keyURL)
|
||||
// failed to scanallips falling back to defaults
|
||||
gologger.Error().Msgf("failed to scan all ips reverting to default %v", err)
|
||||
}
|
||||
|
||||
ips := []string{}
|
||||
// only scan the target but ipv6 if it has one
|
||||
if i.ipOptions.IPV6 {
|
||||
dnsData, err := protocolstate.Dialer.GetDNSData(host)
|
||||
if err == nil && len(dnsData.AAAA) > 0 {
|
||||
// pick/ prefer 1st
|
||||
ips = append(ips, dnsData.AAAA[0])
|
||||
} else {
|
||||
gologger.Warning().Msgf("target does not have ipv6 address falling back to ipv4 %s\n", err)
|
||||
}
|
||||
}
|
||||
if i.ipOptions.IPV4 {
|
||||
// if IPV4 is enabled do not specify ip let dialer handle it
|
||||
ips = append(ips, "")
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
if ip != "" {
|
||||
metaInput := &contextargs.MetaInput{Input: URL, CustomIP: ip}
|
||||
i.setItem(metaInput)
|
||||
} else {
|
||||
metaInput := &contextargs.MetaInput{Input: URL}
|
||||
i.setItem(metaInput)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// setItem in the kv store
|
||||
func (i *Input) setItem(k string) {
|
||||
i.inputCount++
|
||||
_ = i.hostMap.Set(k, nil)
|
||||
func (i *Input) setItem(metaInput *contextargs.MetaInput) {
|
||||
key, err := metaInput.MarshalString()
|
||||
if err != nil {
|
||||
gologger.Warning().Msgf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if _, ok := i.hostMap.Get(key); ok {
|
||||
i.dupeCount++
|
||||
return
|
||||
}
|
||||
|
||||
i.inputCount++ // tracks target count
|
||||
_ = i.hostMap.Set(key, nil)
|
||||
if i.hostMapStream != nil {
|
||||
_ = i.hostMapStream.Set([]byte(k), nil)
|
||||
_ = i.hostMapStream.Set([]byte(key), nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
package hybrid
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/projectdiscovery/hmap/store/hybrid"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate"
|
||||
@ -46,8 +49,41 @@ func Test_expandCIDRInputValue(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type mockDnsHandler struct{}
|
||||
|
||||
func (this *mockDnsHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||
msg := dns.Msg{}
|
||||
msg.SetReply(r)
|
||||
switch r.Question[0].Qtype {
|
||||
case dns.TypeA:
|
||||
msg.Authoritative = true
|
||||
domain := msg.Question[0].Name
|
||||
msg.Answer = append(msg.Answer, &dns.A{
|
||||
Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60},
|
||||
A: net.ParseIP("128.199.158.128"),
|
||||
})
|
||||
case dns.TypeAAAA:
|
||||
msg.Authoritative = true
|
||||
domain := msg.Question[0].Name
|
||||
msg.Answer = append(msg.Answer, &dns.AAAA{
|
||||
Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60},
|
||||
AAAA: net.ParseIP("2400:6180:0:d0::91:1001"),
|
||||
})
|
||||
}
|
||||
_ = w.WriteMsg(&msg)
|
||||
}
|
||||
|
||||
func Test_scanallips_normalizeStoreInputValue(t *testing.T) {
|
||||
srv := &dns.Server{Addr: ":" + strconv.Itoa(61234), Net: "udp"}
|
||||
srv.Handler = &mockDnsHandler{}
|
||||
|
||||
go func() {
|
||||
err := srv.ListenAndServe()
|
||||
require.Nil(t, err)
|
||||
}()
|
||||
|
||||
defaultOpts := types.DefaultOptions()
|
||||
defaultOpts.InternalResolversList = []string{"127.0.0.1:61234"}
|
||||
_ = protocolstate.Init(defaultOpts)
|
||||
tests := []struct {
|
||||
hostname string
|
||||
@ -87,7 +123,7 @@ func Test_scanallips_normalizeStoreInputValue(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
input.normalizeStoreInputValue(tt.hostname)
|
||||
input.Set(tt.hostname)
|
||||
// scan
|
||||
got := []string{}
|
||||
input.hostMap.Scan(func(k, v []byte) error {
|
||||
|
||||
@ -19,3 +19,8 @@ func (s *SimpleInputProvider) Scan(callback func(value *contextargs.MetaInput) b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set adds item to input provider
|
||||
func (s *SimpleInputProvider) Set(value string) {
|
||||
s.Inputs = append(s.Inputs, &contextargs.MetaInput{Input: value})
|
||||
}
|
||||
|
||||
4
v2/pkg/external/customtemplates/github.go
vendored
4
v2/pkg/external/customtemplates/github.go
vendored
@ -24,7 +24,7 @@ type customTemplateGithubRepo struct {
|
||||
|
||||
// This function download the custom github template repository
|
||||
func (customTemplate *customTemplateGithubRepo) Download(location string, ctx context.Context) {
|
||||
downloadPath := filepath.Join(location, customGithubTemplateDirectory)
|
||||
downloadPath := filepath.Join(location, CustomGithubTemplateDirectory)
|
||||
clonePath := customTemplate.getLocalRepoClonePath(downloadPath)
|
||||
|
||||
if !fileutil.FolderExists(clonePath) {
|
||||
@ -39,7 +39,7 @@ func (customTemplate *customTemplateGithubRepo) Download(location string, ctx co
|
||||
}
|
||||
|
||||
func (customTemplate *customTemplateGithubRepo) Update(location string, ctx context.Context) {
|
||||
downloadPath := filepath.Join(location, customGithubTemplateDirectory)
|
||||
downloadPath := filepath.Join(location, CustomGithubTemplateDirectory)
|
||||
clonePath := customTemplate.getLocalRepoClonePath(downloadPath)
|
||||
|
||||
// If folder does not exits then clone/download the repo
|
||||
|
||||
@ -20,10 +20,10 @@ func TestDownloadCustomTemplatesFromGitHub(t *testing.T) {
|
||||
|
||||
options := testutils.DefaultOptions
|
||||
options.GithubTemplateRepo = []string{"projectdiscovery/nuclei-templates", "ehsandeep/nuclei-templates"}
|
||||
|
||||
options.GithubToken = os.Getenv("GITHUB_TOKEN")
|
||||
customTemplates := ParseCustomTemplates(options)
|
||||
|
||||
for _, ct := range *customTemplates {
|
||||
for _, ct := range customTemplates {
|
||||
ct.Download(templatesDirectory, context.Background())
|
||||
}
|
||||
|
||||
|
||||
8
v2/pkg/external/customtemplates/s3.go
vendored
8
v2/pkg/external/customtemplates/s3.go
vendored
@ -11,17 +11,19 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/stringsutil"
|
||||
)
|
||||
|
||||
type customTemplateS3Bucket struct {
|
||||
s3Client *s3.Client
|
||||
bucketName string
|
||||
prefix string
|
||||
Location string
|
||||
}
|
||||
|
||||
// download custom templates from s3 bucket
|
||||
func (bk *customTemplateS3Bucket) Download(location string, ctx context.Context) {
|
||||
downloadPath := filepath.Join(location, customS3TemplateDirectory, bk.bucketName)
|
||||
downloadPath := filepath.Join(location, CustomS3TemplateDirectory, bk.bucketName)
|
||||
|
||||
manager := manager.NewDownloader(bk.s3Client)
|
||||
paginator := s3.NewListObjectsV2Paginator(bk.s3Client, &s3.ListObjectsV2Input{
|
||||
@ -53,6 +55,10 @@ func (bk *customTemplateS3Bucket) Update(location string, ctx context.Context) {
|
||||
func downloadToFile(downloader *manager.Downloader, targetDirectory, bucket, key string) error {
|
||||
// Create the directories in the path
|
||||
file := filepath.Join(targetDirectory, key)
|
||||
// If empty dir in s3
|
||||
if stringsutil.HasSuffixI(key, "/") {
|
||||
return os.MkdirAll(file, 0775)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(file), 0775); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -9,23 +9,19 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
customGithubTemplateDirectory = "github"
|
||||
customS3TemplateDirectory = "s3"
|
||||
CustomGithubTemplateDirectory = "github"
|
||||
CustomS3TemplateDirectory = "s3"
|
||||
)
|
||||
|
||||
type CustomTemplateProvider interface {
|
||||
type Provider interface {
|
||||
Download(location string, ctx context.Context)
|
||||
Update(location string, ctx context.Context)
|
||||
}
|
||||
|
||||
// parseCustomTemplates function reads the options.GithubTemplateRepo list,
|
||||
// Checks the given repos are valid or not and stores them into runner.CustomTemplates
|
||||
func ParseCustomTemplates(options *types.Options) *[]CustomTemplateProvider {
|
||||
// do not sync templates in case of cloud
|
||||
if options.Cloud {
|
||||
return nil
|
||||
}
|
||||
var customTemplates []CustomTemplateProvider
|
||||
func ParseCustomTemplates(options *types.Options) []Provider {
|
||||
var customTemplates []Provider
|
||||
gitHubClient := getGHClientIncognito()
|
||||
|
||||
for _, repoName := range options.GithubTemplateRepo {
|
||||
@ -51,7 +47,7 @@ func ParseCustomTemplates(options *types.Options) *[]CustomTemplateProvider {
|
||||
s3c, err := getS3Client(context.TODO(), options.AwsAccessKey, options.AwsSecretKey, options.AwsRegion)
|
||||
if err != nil {
|
||||
gologger.Error().Msgf("error downloading s3 bucket %s %s", options.AwsBucketName, err)
|
||||
return &customTemplates
|
||||
return customTemplates
|
||||
}
|
||||
ctBucket := &customTemplateS3Bucket{
|
||||
bucketName: options.AwsBucketName,
|
||||
@ -64,5 +60,5 @@ func ParseCustomTemplates(options *types.Options) *[]CustomTemplateProvider {
|
||||
}
|
||||
customTemplates = append(customTemplates, ctBucket)
|
||||
}
|
||||
return &customTemplates
|
||||
return customTemplates
|
||||
}
|
||||
|
||||
@ -64,8 +64,9 @@ func (severity Severity) String() string {
|
||||
return severityMappings[severity]
|
||||
}
|
||||
|
||||
//nolint:exported,revive //prefer to be explicit about the name, and make it refactor-safe
|
||||
// Holder holds a Severity type. Required for un/marshalling purposes
|
||||
//
|
||||
//nolint:exported,revive //prefer to be explicit about the name, and make it refactor-safe
|
||||
type Holder struct {
|
||||
Severity Severity `mapping:"true"`
|
||||
}
|
||||
|
||||
@ -52,12 +52,16 @@ func (stringSlice *StringSlice) UnmarshalYAML(unmarshal func(interface{}) error)
|
||||
|
||||
result := make([]string, 0, len(marshalledSlice))
|
||||
for _, value := range marshalledSlice {
|
||||
result = append(result, strings.ToLower(strings.TrimSpace(value))) // TODO do we need to introduce RawStringSlice and/or NormalizedStringSlices?
|
||||
result = append(result, stringSlice.normalize(value))
|
||||
}
|
||||
stringSlice.Value = result
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stringSlice StringSlice) normalize(value string) string {
|
||||
return strings.ToLower(strings.TrimSpace(value))
|
||||
}
|
||||
|
||||
func (stringSlice StringSlice) MarshalYAML() (interface{}, error) {
|
||||
return stringSlice.Value, nil
|
||||
}
|
||||
@ -82,7 +86,7 @@ func (stringSlice *StringSlice) UnmarshalJSON(data []byte) error {
|
||||
switch {
|
||||
case len(marshalledValuesAsSlice) > 0:
|
||||
result = marshalledValuesAsSlice
|
||||
case utils.IsNotBlank(marshalledValueAsString):
|
||||
case !utils.IsBlank(marshalledValueAsString):
|
||||
result = strings.Split(marshalledValueAsString, ",")
|
||||
default:
|
||||
result = []string{}
|
||||
@ -90,7 +94,7 @@ func (stringSlice *StringSlice) UnmarshalJSON(data []byte) error {
|
||||
|
||||
values := make([]string, 0, len(result))
|
||||
for _, value := range result {
|
||||
values = append(values, strings.ToLower(strings.TrimSpace(value))) // TODO do we need to introduce RawStringSlice and/or NormalizedStringSlices?
|
||||
values = append(values, stringSlice.normalize(value))
|
||||
}
|
||||
stringSlice.Value = values
|
||||
return nil
|
||||
@ -112,7 +116,7 @@ func marshalStringToSlice(unmarshal func(interface{}) error) ([]string, error) {
|
||||
switch {
|
||||
case len(marshalledValuesAsSlice) > 0:
|
||||
result = marshalledValuesAsSlice
|
||||
case utils.IsNotBlank(marshalledValueAsString):
|
||||
case !utils.IsBlank(marshalledValueAsString):
|
||||
result = strings.Split(marshalledValueAsString, ",")
|
||||
default:
|
||||
result = []string{}
|
||||
|
||||
@ -62,7 +62,7 @@ var functionSignaturePattern = regexp.MustCompile(`(\w+)\s*\((?:([\w\d,\s]+)\s+(
|
||||
var dateFormatRegex = regexp.MustCompile("%([A-Za-z])")
|
||||
|
||||
type dslFunction struct {
|
||||
signature string
|
||||
signatures []string
|
||||
expressFunc govaluate.ExpressionFunction
|
||||
}
|
||||
|
||||
@ -88,8 +88,10 @@ func init() {
|
||||
"to_lower": makeDslFunction(1, func(args ...interface{}) (interface{}, error) {
|
||||
return strings.ToLower(types.ToString(args[0])), nil
|
||||
}),
|
||||
"sort": makeDslWithOptionalArgsFunction(
|
||||
"(args ...interface{}) interface{}",
|
||||
"sort": makeMultiSignatureDslFunction([]string{
|
||||
"(input string) string",
|
||||
"(input number) string",
|
||||
"(elements ...interface{}) []interface{}"},
|
||||
func(args ...interface{}) (interface{}, error) {
|
||||
argCount := len(args)
|
||||
if argCount == 0 {
|
||||
@ -110,8 +112,10 @@ func init() {
|
||||
}
|
||||
},
|
||||
),
|
||||
"uniq": makeDslWithOptionalArgsFunction(
|
||||
"(args ...interface{}) interface{}",
|
||||
"uniq": makeMultiSignatureDslFunction([]string{
|
||||
"(input string) string",
|
||||
"(input number) string",
|
||||
"(elements ...interface{}) []interface{}"},
|
||||
func(args ...interface{}) (interface{}, error) {
|
||||
argCount := len(args)
|
||||
if argCount == 0 {
|
||||
@ -410,12 +414,40 @@ func init() {
|
||||
return builder.String(), nil
|
||||
},
|
||||
),
|
||||
"join": makeDslWithOptionalArgsFunction(
|
||||
"split": makeMultiSignatureDslFunction([]string{
|
||||
"(input string, n int) []string",
|
||||
"(input string, separator string, optionalChunkSize) []string"},
|
||||
func(arguments ...interface{}) (interface{}, error) {
|
||||
argumentsSize := len(arguments)
|
||||
if argumentsSize == 2 {
|
||||
input := types.ToString(arguments[0])
|
||||
separatorOrCount := types.ToString(arguments[1])
|
||||
|
||||
count, err := strconv.Atoi(separatorOrCount)
|
||||
if err != nil {
|
||||
return strings.SplitN(input, separatorOrCount, -1), nil
|
||||
}
|
||||
return toChunks(input, count), nil
|
||||
} else if argumentsSize == 3 {
|
||||
input := types.ToString(arguments[0])
|
||||
separator := types.ToString(arguments[1])
|
||||
count, err := strconv.Atoi(types.ToString(arguments[2]))
|
||||
if err != nil {
|
||||
return nil, invalidDslFunctionError
|
||||
}
|
||||
return strings.SplitN(input, separator, count), nil
|
||||
} else {
|
||||
return nil, invalidDslFunctionError
|
||||
}
|
||||
},
|
||||
),
|
||||
"join": makeMultiSignatureDslFunction([]string{
|
||||
"(separator string, elements ...interface{}) string",
|
||||
"(separator string, elements []interface{}) string"},
|
||||
func(arguments ...interface{}) (interface{}, error) {
|
||||
argumentsSize := len(arguments)
|
||||
if argumentsSize < 2 {
|
||||
return nil, errors.New("incorrect number of arguments received")
|
||||
return nil, invalidDslFunctionError
|
||||
} else if argumentsSize == 2 {
|
||||
separator := types.ToString(arguments[0])
|
||||
elements, ok := arguments[1].([]string)
|
||||
@ -431,7 +463,6 @@ func init() {
|
||||
|
||||
stringElements := make([]string, 0, argumentsSize)
|
||||
for _, element := range elements {
|
||||
|
||||
if _, ok := element.([]string); ok {
|
||||
return nil, errors.New("cannot use join on more than one slice element")
|
||||
}
|
||||
@ -794,9 +825,18 @@ func init() {
|
||||
}
|
||||
|
||||
func makeDslWithOptionalArgsFunction(signaturePart string, dslFunctionLogic govaluate.ExpressionFunction) func(functionName string) dslFunction {
|
||||
return makeMultiSignatureDslFunction([]string{signaturePart}, dslFunctionLogic)
|
||||
}
|
||||
|
||||
func makeMultiSignatureDslFunction(signatureParts []string, dslFunctionLogic govaluate.ExpressionFunction) func(functionName string) dslFunction {
|
||||
return func(functionName string) dslFunction {
|
||||
methodSignatures := make([]string, 0, len(signatureParts))
|
||||
for _, signaturePart := range signatureParts {
|
||||
methodSignatures = append(methodSignatures, functionName+signaturePart)
|
||||
}
|
||||
|
||||
return dslFunction{
|
||||
functionName + signaturePart,
|
||||
methodSignatures,
|
||||
dslFunctionLogic,
|
||||
}
|
||||
}
|
||||
@ -806,7 +846,7 @@ func makeDslFunction(numberOfParameters int, dslFunctionLogic govaluate.Expressi
|
||||
return func(functionName string) dslFunction {
|
||||
signature := functionName + createSignaturePart(numberOfParameters)
|
||||
return dslFunction{
|
||||
signature,
|
||||
[]string{signature},
|
||||
func(args ...interface{}) (interface{}, error) {
|
||||
if len(args) != numberOfParameters {
|
||||
return nil, fmt.Errorf(invalidDslFunctionMessageTemplate, invalidDslFunctionError, signature)
|
||||
@ -843,7 +883,7 @@ func helperFunctions() map[string]govaluate.ExpressionFunction {
|
||||
func AddHelperFunction(key string, value func(args ...interface{}) (interface{}, error)) error {
|
||||
if _, ok := dslFunctions[key]; !ok {
|
||||
dslFunction := dslFunctions[key]
|
||||
dslFunction.signature = "(args ...interface{}) interface{}"
|
||||
dslFunction.signatures = []string{"(args ...interface{}) interface{}"}
|
||||
dslFunction.expressFunc = value
|
||||
return nil
|
||||
}
|
||||
@ -873,7 +913,7 @@ func getDslFunctionSignatures() []string {
|
||||
result := make([]string, 0, len(dslFunctions))
|
||||
|
||||
for _, dslFunction := range dslFunctions {
|
||||
result = append(result, dslFunction.signature)
|
||||
result = append(result, dslFunction.signatures...)
|
||||
}
|
||||
|
||||
return result
|
||||
@ -1028,6 +1068,25 @@ func stringNumberToDecimal(args []interface{}, prefix string, base int) (interfa
|
||||
return nil, fmt.Errorf("invalid number: %s", input)
|
||||
}
|
||||
|
||||
func toChunks(input string, chunkSize int) []string {
|
||||
if chunkSize <= 0 || chunkSize >= len(input) {
|
||||
return []string{input}
|
||||
}
|
||||
var chunks = make([]string, 0, (len(input)-1)/chunkSize+1)
|
||||
currentLength := 0
|
||||
currentStart := 0
|
||||
for i := range input {
|
||||
if currentLength == chunkSize {
|
||||
chunks = append(chunks, input[currentStart:i])
|
||||
currentLength = 0
|
||||
currentStart = i
|
||||
}
|
||||
currentLength++
|
||||
}
|
||||
chunks = append(chunks, input[currentStart:])
|
||||
return chunks
|
||||
}
|
||||
|
||||
type CompilationError struct {
|
||||
DslSignature string
|
||||
WrappedError error
|
||||
|
||||
@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -118,6 +117,7 @@ func TestGetPrintableDslFunctionSignatures(t *testing.T) {
|
||||
html_escape(arg1 interface{}) interface{}
|
||||
html_unescape(arg1 interface{}) interface{}
|
||||
join(separator string, elements ...interface{}) string
|
||||
join(separator string, elements []interface{}) string
|
||||
len(arg1 interface{}) interface{}
|
||||
line_ends_with(str string, suffix ...string) bool
|
||||
line_starts_with(str string, prefix ...string) bool
|
||||
@ -141,7 +141,11 @@ func TestGetPrintableDslFunctionSignatures(t *testing.T) {
|
||||
sha1(arg1 interface{}) interface{}
|
||||
sha256(arg1 interface{}) interface{}
|
||||
sha512(arg1 interface{}) interface{}
|
||||
sort(args ...interface{}) interface{}
|
||||
sort(elements ...interface{}) []interface{}
|
||||
sort(input number) string
|
||||
sort(input string) string
|
||||
split(input string, n int) []string
|
||||
split(input string, separator string, optionalChunkSize) []string
|
||||
starts_with(str string, prefix ...string) bool
|
||||
substr(str string, start int, optionalEnd int)
|
||||
to_lower(arg1 interface{}) interface{}
|
||||
@ -155,7 +159,9 @@ func TestGetPrintableDslFunctionSignatures(t *testing.T) {
|
||||
trim_right(arg1, arg2 interface{}) interface{}
|
||||
trim_space(arg1 interface{}) interface{}
|
||||
trim_suffix(arg1, arg2 interface{}) interface{}
|
||||
uniq(args ...interface{}) interface{}
|
||||
uniq(elements ...interface{}) []interface{}
|
||||
uniq(input number) string
|
||||
uniq(input string) string
|
||||
unix_time(optionalSeconds uint) float64
|
||||
url_decode(arg1 interface{}) interface{}
|
||||
url_encode(arg1 interface{}) interface{}
|
||||
@ -172,7 +178,6 @@ func TestGetPrintableDslFunctionSignatures(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDslExpressions(t *testing.T) {
|
||||
|
||||
dslExpressions := map[string]interface{}{
|
||||
`base64("Hello")`: "SGVsbG8=",
|
||||
`base64(1234)`: "MTIzNA==",
|
||||
@ -244,27 +249,27 @@ func TestDslExpressions(t *testing.T) {
|
||||
`substr('xxtestxxx',2)`: "testxxx",
|
||||
`substr('xxtestxxx',2,-2)`: "testx",
|
||||
`substr('xxtestxxx',2,6)`: "test",
|
||||
`sort(12453)`: "12345",
|
||||
`sort("a1b2c3d4e5")`: "12345abcde",
|
||||
`sort("b", "a", "2", "c", "3", "1", "d", "4")`: []string{"1", "2", "3", "4", "a", "b", "c", "d"},
|
||||
`split("abcdefg", 2)`: []string{"ab", "cd", "ef", "g"},
|
||||
`split("ab,cd,efg", ",", 1)`: []string{"ab,cd,efg"},
|
||||
`split("ab,cd,efg", ",", 2)`: []string{"ab", "cd,efg"},
|
||||
`split("ab,cd,efg", ",", "3")`: []string{"ab", "cd", "efg"},
|
||||
`split("ab,cd,efg", ",", -1)`: []string{"ab", "cd", "efg"},
|
||||
`split("ab,cd,efg", ",")`: []string{"ab", "cd", "efg"},
|
||||
`join(" ", sort("b", "a", "2", "c", "3", "1", "d", "4"))`: "1 2 3 4 a b c d",
|
||||
`uniq(123123231)`: "123",
|
||||
`uniq("abcabdaabbccd")`: "abcd",
|
||||
`uniq("ab", "cd", "12", "34", "12", "cd")`: []string{"ab", "cd", "12", "34"},
|
||||
`join(" ", uniq("ab", "cd", "12", "34", "12", "cd"))`: "ab cd 12 34",
|
||||
`join(", ", split(hex_encode("abcdefg"), 2))`: "61, 62, 63, 64, 65, 66, 67",
|
||||
}
|
||||
|
||||
testDslExpressionScenarios(t, dslExpressions)
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
if number, err := strconv.ParseInt("0o1234567", 0, 64); err == nil {
|
||||
fmt.Println(number)
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDateTimeDSLFunction(t *testing.T) {
|
||||
|
||||
testDateTimeFormat := func(t *testing.T, dateTimeFormat string, dateTimeFunction *govaluate.EvaluableExpression, expectedFormattedTime string, currentUnixTime int64) {
|
||||
dslFunctionParameters := map[string]interface{}{"dateTimeFormat": dateTimeFormat}
|
||||
|
||||
@ -302,7 +307,6 @@ func TestDateTimeDSLFunction(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDateTimeDslExpressions(t *testing.T) {
|
||||
|
||||
t.Run("date_time", func(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ var MatcherTypes = map[MatcherType]string{
|
||||
DSLMatcher: "dsl",
|
||||
}
|
||||
|
||||
//GetType returns the type of the matcher
|
||||
// GetType returns the type of the matcher
|
||||
func (matcher *Matcher) GetType() MatcherType {
|
||||
return matcher.Type.MatcherType
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package operators
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@ -91,6 +92,23 @@ type Result struct {
|
||||
LineCount string
|
||||
}
|
||||
|
||||
func (result *Result) HasMatch(name string) bool {
|
||||
return result.hasItem(name, result.Matches)
|
||||
}
|
||||
|
||||
func (result *Result) HasExtract(name string) bool {
|
||||
return result.hasItem(name, result.Extracts)
|
||||
}
|
||||
|
||||
func (result *Result) hasItem(name string, m map[string][]string) bool {
|
||||
for matchName := range m {
|
||||
if strings.EqualFold(name, matchName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MakeDynamicValuesCallback takes an input dynamic values map and calls
|
||||
// the callback function with all variations of the data in input in form
|
||||
// of map[string]string (interface{}).
|
||||
|
||||
@ -12,7 +12,7 @@ func (w *StandardWriter) formatScreen(output *ResultEvent) []byte {
|
||||
builder := &bytes.Buffer{}
|
||||
|
||||
if !w.noMetadata {
|
||||
if !w.noTimestamp {
|
||||
if w.timestamp {
|
||||
builder.WriteRune('[')
|
||||
builder.WriteString(w.aurora.Cyan(output.Timestamp.Format("2006-01-02 15:04:05")).String())
|
||||
builder.WriteString("] ")
|
||||
|
||||
@ -46,7 +46,7 @@ type Writer interface {
|
||||
type StandardWriter struct {
|
||||
json bool
|
||||
jsonReqResp bool
|
||||
noTimestamp bool
|
||||
timestamp bool
|
||||
noMetadata bool
|
||||
matcherStatus bool
|
||||
mutex *sync.Mutex
|
||||
@ -123,7 +123,7 @@ type ResultEvent struct {
|
||||
}
|
||||
|
||||
// NewStandardWriter creates a new output writer based on user configurations
|
||||
func NewStandardWriter(colors, noMetadata, noTimestamp, json, jsonReqResp, MatcherStatus, storeResponse bool, file, traceFile string, errorFile string, storeResponseDir string) (*StandardWriter, error) {
|
||||
func NewStandardWriter(colors, noMetadata, timestamp, json, jsonReqResp, MatcherStatus, storeResponse bool, file, traceFile string, errorFile string, storeResponseDir string) (*StandardWriter, error) {
|
||||
auroraColorizer := aurora.NewAurora(colors)
|
||||
|
||||
var outputFile io.WriteCloser
|
||||
@ -161,7 +161,7 @@ func NewStandardWriter(colors, noMetadata, noTimestamp, json, jsonReqResp, Match
|
||||
jsonReqResp: jsonReqResp,
|
||||
noMetadata: noMetadata,
|
||||
matcherStatus: MatcherStatus,
|
||||
noTimestamp: noTimestamp,
|
||||
timestamp: timestamp,
|
||||
aurora: auroraColorizer,
|
||||
mutex: &sync.Mutex{},
|
||||
outputFile: outputFile,
|
||||
|
||||
@ -7,7 +7,8 @@
|
||||
// which are then used as tags for the execution of the templates.
|
||||
//
|
||||
// Example -
|
||||
// "Amazon Web Services,Jenkins,Atlassian Jira" -> "amazon,web,services,jenkins,atlassian,jira".
|
||||
//
|
||||
// "Amazon Web Services,Jenkins,Atlassian Jira" -> "amazon,web,services,jenkins,atlassian,jira".
|
||||
//
|
||||
// Wappalyzergo (https://github.com/projectdiscovery/wappalyzergo) is used for wappalyzer tech
|
||||
// detection.
|
||||
|
||||
@ -16,7 +16,7 @@ type PayloadGenerator struct {
|
||||
}
|
||||
|
||||
// New creates a new generator structure for payload generation
|
||||
func New(payloads map[string]interface{}, attackType AttackType, templatePath string, catalog catalog.Catalog, customAttackType string) (*PayloadGenerator, error) {
|
||||
func New(payloads map[string]interface{}, attackType AttackType, templatePath, templateDirectory string, sandbox bool, catalog catalog.Catalog, customAttackType string) (*PayloadGenerator, error) {
|
||||
if attackType.String() == "" {
|
||||
attackType = BatteringRamAttack
|
||||
}
|
||||
@ -42,7 +42,7 @@ func New(payloads map[string]interface{}, attackType AttackType, templatePath st
|
||||
return nil, err
|
||||
}
|
||||
|
||||
compiled, err := generator.loadPayloads(payloadsFinal)
|
||||
compiled, err := generator.loadPayloads(payloadsFinal, templatePath, templateDirectory, sandbox)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ func TestBatteringRamGenerator(t *testing.T) {
|
||||
usernames := []string{"admin", "password"}
|
||||
|
||||
catalogInstance := disk.NewCatalog("")
|
||||
generator, err := New(map[string]interface{}{"username": usernames}, BatteringRamAttack, "", catalogInstance, "")
|
||||
generator, err := New(map[string]interface{}{"username": usernames}, BatteringRamAttack, "", "", false, catalogInstance, "")
|
||||
require.Nil(t, err, "could not create generator")
|
||||
|
||||
iterator := generator.NewIterator()
|
||||
@ -32,7 +32,7 @@ func TestPitchforkGenerator(t *testing.T) {
|
||||
passwords := []string{"password1", "password2", "password3"}
|
||||
|
||||
catalogInstance := disk.NewCatalog("")
|
||||
generator, err := New(map[string]interface{}{"username": usernames, "password": passwords}, PitchForkAttack, "", catalogInstance, "")
|
||||
generator, err := New(map[string]interface{}{"username": usernames, "password": passwords}, PitchForkAttack, "", "", false, catalogInstance, "")
|
||||
require.Nil(t, err, "could not create generator")
|
||||
|
||||
iterator := generator.NewIterator()
|
||||
@ -54,7 +54,7 @@ func TestClusterbombGenerator(t *testing.T) {
|
||||
passwords := []string{"admin", "password", "token"}
|
||||
|
||||
catalogInstance := disk.NewCatalog("")
|
||||
generator, err := New(map[string]interface{}{"username": usernames, "password": passwords}, ClusterBombAttack, "", catalogInstance, "")
|
||||
generator, err := New(map[string]interface{}{"username": usernames, "password": passwords}, ClusterBombAttack, "", "", false, catalogInstance, "")
|
||||
require.Nil(t, err, "could not create generator")
|
||||
|
||||
iterator := generator.NewIterator()
|
||||
|
||||
@ -3,6 +3,7 @@ package generators
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@ -10,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// loadPayloads loads the input payloads from a map to a data map
|
||||
func (generator *PayloadGenerator) loadPayloads(payloads map[string]interface{}) (map[string][]string, error) {
|
||||
func (generator *PayloadGenerator) loadPayloads(payloads map[string]interface{}, templatePath, templateDirectory string, sandbox bool) (map[string][]string, error) {
|
||||
loadedPayloads := make(map[string][]string)
|
||||
|
||||
for name, payload := range payloads {
|
||||
@ -21,6 +22,13 @@ func (generator *PayloadGenerator) loadPayloads(payloads map[string]interface{})
|
||||
if len(elements) >= 2 {
|
||||
loadedPayloads[name] = elements
|
||||
} else {
|
||||
if sandbox {
|
||||
pt = filepath.Clean(pt)
|
||||
templatePathDir := filepath.Dir(templatePath)
|
||||
if !(templatePathDir != "/" && strings.HasPrefix(pt, templatePathDir)) && !strings.HasPrefix(pt, templateDirectory) {
|
||||
return nil, errors.New("denied payload file path specified")
|
||||
}
|
||||
}
|
||||
payloads, err := generator.loadPayloadsFromFile(pt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not load payloads")
|
||||
|
||||
61
v2/pkg/protocols/common/generators/load_test.go
Normal file
61
v2/pkg/protocols/common/generators/load_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/disk"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLoadPayloads(t *testing.T) {
|
||||
tempdir, err := os.MkdirTemp("", "templates-*")
|
||||
require.NoError(t, err, "could not create temp dir")
|
||||
defer os.RemoveAll(tempdir)
|
||||
|
||||
generator := &PayloadGenerator{catalog: disk.NewCatalog(tempdir)}
|
||||
|
||||
fullpath := filepath.Join(tempdir, "payloads.txt")
|
||||
err = os.WriteFile(fullpath, []byte("test\nanother"), 0777)
|
||||
require.NoError(t, err, "could not write payload")
|
||||
|
||||
// Test sandbox
|
||||
t.Run("templates-directory", func(t *testing.T) {
|
||||
values, err := generator.loadPayloads(map[string]interface{}{
|
||||
"new": fullpath,
|
||||
}, "/test", tempdir, true)
|
||||
require.NoError(t, err, "could not load payloads")
|
||||
require.Equal(t, map[string][]string{"new": {"test", "another"}}, values, "could not get values")
|
||||
})
|
||||
t.Run("template-directory", func(t *testing.T) {
|
||||
values, err := generator.loadPayloads(map[string]interface{}{
|
||||
"new": fullpath,
|
||||
}, filepath.Join(tempdir, "test.yaml"), "/test", true)
|
||||
require.NoError(t, err, "could not load payloads")
|
||||
require.Equal(t, map[string][]string{"new": {"test", "another"}}, values, "could not get values")
|
||||
})
|
||||
t.Run("no-sandbox-unix", func(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
}
|
||||
_, err := generator.loadPayloads(map[string]interface{}{
|
||||
"new": "/etc/passwd",
|
||||
}, "/random", "/test", false)
|
||||
require.NoError(t, err, "could load payloads")
|
||||
})
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
values, err := generator.loadPayloads(map[string]interface{}{
|
||||
"new": "/etc/passwd",
|
||||
}, "/random", "/test", true)
|
||||
require.Error(t, err, "could load payloads")
|
||||
require.Equal(t, 0, len(values), "could get values")
|
||||
|
||||
values, err = generator.loadPayloads(map[string]interface{}{
|
||||
"new": fullpath,
|
||||
}, "/random", "/test", true)
|
||||
require.Error(t, err, "could load payloads")
|
||||
require.Equal(t, 0, len(values), "could get values")
|
||||
})
|
||||
}
|
||||
@ -127,7 +127,7 @@ func (c *Cache) MarkFailed(value string, err error) {
|
||||
_ = c.failedTargets.Set(finalValue, numberOfErrorsValue+1)
|
||||
}
|
||||
|
||||
var checkErrorRegexp = regexp.MustCompile(`(no address found for host|Client\.Timeout exceeded while awaiting headers|could not resolve host)`)
|
||||
var checkErrorRegexp = regexp.MustCompile(`(no address found for host|Client\.Timeout exceeded while awaiting headers|could not resolve host|connection refused)`)
|
||||
|
||||
// checkError checks if an error represents a type that should be
|
||||
// added to the host skipping table.
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"golang.org/x/net/proxy"
|
||||
|
||||
"github.com/projectdiscovery/fastdialer/fastdialer"
|
||||
"github.com/projectdiscovery/networkpolicy"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
)
|
||||
|
||||
@ -90,6 +91,9 @@ func Init(options *types.Options) error {
|
||||
if options.ResolversFile != "" {
|
||||
opts.BaseResolvers = options.InternalResolversList
|
||||
}
|
||||
if options.Sandbox {
|
||||
opts.Deny = append(networkpolicy.DefaultIPv4DenylistRanges, networkpolicy.DefaultIPv6DenylistRanges...)
|
||||
}
|
||||
opts.WithDialerHistory = true
|
||||
opts.WithZTLS = options.ZTLS
|
||||
opts.SNIName = options.SNI
|
||||
|
||||
238
v2/pkg/protocols/common/uncover/uncover.go
Normal file
238
v2/pkg/protocols/common/uncover/uncover.go
Normal file
@ -0,0 +1,238 @@
|
||||
package uncover
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
||||
"github.com/projectdiscovery/ratelimit"
|
||||
ucRunner "github.com/projectdiscovery/uncover/runner"
|
||||
"github.com/projectdiscovery/uncover/uncover"
|
||||
"github.com/projectdiscovery/uncover/uncover/agent/censys"
|
||||
"github.com/projectdiscovery/uncover/uncover/agent/fofa"
|
||||
"github.com/projectdiscovery/uncover/uncover/agent/hunter"
|
||||
"github.com/projectdiscovery/uncover/uncover/agent/netlas"
|
||||
"github.com/projectdiscovery/uncover/uncover/agent/quake"
|
||||
"github.com/projectdiscovery/uncover/uncover/agent/shodan"
|
||||
"github.com/projectdiscovery/uncover/uncover/agent/shodanidb"
|
||||
"github.com/projectdiscovery/uncover/uncover/agent/zoomeye"
|
||||
mapsutil "github.com/projectdiscovery/utils/maps"
|
||||
"github.com/remeh/sizedwaitgroup"
|
||||
)
|
||||
|
||||
const maxConcurrentAgents = 50
|
||||
|
||||
func GetUncoverSupportedAgents() string {
|
||||
uncoverSupportedAgents := []string{"shodan", "shodan-idb", "fofa", "censys", "quake", "hunter", "zoomeye", "netlas"}
|
||||
return strings.Join(uncoverSupportedAgents, ",")
|
||||
}
|
||||
|
||||
func GetTargetsFromUncover(delay, limit int, field string, engine, query []string) (chan string, error) {
|
||||
uncoverOptions := &ucRunner.Options{
|
||||
Provider: &ucRunner.Provider{},
|
||||
Delay: delay,
|
||||
Limit: limit,
|
||||
Query: query,
|
||||
Engine: engine,
|
||||
}
|
||||
for _, eng := range engine {
|
||||
err := loadKeys(eng, uncoverOptions)
|
||||
if err != nil {
|
||||
gologger.Error().Label("WRN").Msgf(err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
return getTargets(uncoverOptions, field)
|
||||
}
|
||||
|
||||
func GetUncoverTargetsFromMetadata(templates []*templates.Template, delay, limit int, field string) chan string {
|
||||
ret := make(chan string)
|
||||
var uqMap = make(map[string][]string)
|
||||
var eng, query string
|
||||
for _, template := range templates {
|
||||
for k, v := range template.Info.Metadata {
|
||||
switch k {
|
||||
case "shodan-query":
|
||||
eng = "shodan"
|
||||
case "fofa-query":
|
||||
eng = "fofa"
|
||||
case "censys-query":
|
||||
eng = "censys"
|
||||
case "quake-query":
|
||||
eng = "quake"
|
||||
case "hunter-query":
|
||||
eng = "hunter"
|
||||
case "zoomeye-query":
|
||||
eng = "zoomeye"
|
||||
case "netlas-query":
|
||||
eng = "netlas"
|
||||
default:
|
||||
continue
|
||||
}
|
||||
query = fmt.Sprintf("%v", v)
|
||||
uqMap[eng] = append(uqMap[eng], query)
|
||||
}
|
||||
}
|
||||
keys := mapsutil.GetKeys(uqMap)
|
||||
gologger.Info().Msgf("Running uncover query against: %s", strings.Join(keys, ","))
|
||||
var wg sync.WaitGroup
|
||||
go func() {
|
||||
for k, v := range uqMap {
|
||||
wg.Add(1)
|
||||
go func(engine, query []string) {
|
||||
ch, _ := GetTargetsFromUncover(delay, limit, field, engine, query)
|
||||
for c := range ch {
|
||||
ret <- c
|
||||
}
|
||||
wg.Done()
|
||||
}([]string{k}, v)
|
||||
}
|
||||
wg.Wait()
|
||||
close(ret)
|
||||
}()
|
||||
return ret
|
||||
}
|
||||
|
||||
func getTargets(uncoverOptions *ucRunner.Options, field string) (chan string, error) {
|
||||
var rateLimiter *ratelimit.Limiter
|
||||
// create rateLimiter for uncover delay
|
||||
if uncoverOptions.Delay > 0 {
|
||||
rateLimiter = ratelimit.New(context.Background(), 1, time.Duration(uncoverOptions.Delay))
|
||||
} else {
|
||||
rateLimiter = ratelimit.NewUnlimited(context.Background())
|
||||
}
|
||||
var agents []uncover.Agent
|
||||
// declare clients
|
||||
for _, engine := range uncoverOptions.Engine {
|
||||
var (
|
||||
agent uncover.Agent
|
||||
err error
|
||||
)
|
||||
switch engine {
|
||||
case "shodan":
|
||||
agent, err = shodan.NewWithOptions(&uncover.AgentOptions{RateLimiter: rateLimiter})
|
||||
case "censys":
|
||||
agent, err = censys.NewWithOptions(&uncover.AgentOptions{RateLimiter: rateLimiter})
|
||||
case "fofa":
|
||||
agent, err = fofa.NewWithOptions(&uncover.AgentOptions{RateLimiter: rateLimiter})
|
||||
case "shodan-idb":
|
||||
agent, err = shodanidb.NewWithOptions(&uncover.AgentOptions{RateLimiter: rateLimiter})
|
||||
case "quake":
|
||||
agent, err = quake.NewWithOptions(&uncover.AgentOptions{RateLimiter: rateLimiter})
|
||||
case "hunter":
|
||||
agent, err = hunter.NewWithOptions(&uncover.AgentOptions{RateLimiter: rateLimiter})
|
||||
case "zoomeye":
|
||||
agent, err = zoomeye.NewWithOptions(&uncover.AgentOptions{RateLimiter: rateLimiter})
|
||||
case "netlas":
|
||||
agent, err = netlas.NewWithOptions(&uncover.AgentOptions{RateLimiter: rateLimiter})
|
||||
default:
|
||||
err = errors.Errorf("%s unknown uncover agent type", engine)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
agents = append(agents, agent)
|
||||
}
|
||||
// enumerate
|
||||
swg := sizedwaitgroup.New(maxConcurrentAgents)
|
||||
ret := make(chan string)
|
||||
go func() {
|
||||
for _, q := range uncoverOptions.Query {
|
||||
uncoverQuery := &uncover.Query{
|
||||
Query: q,
|
||||
Limit: uncoverOptions.Limit,
|
||||
}
|
||||
for _, agent := range agents {
|
||||
swg.Add()
|
||||
go func(agent uncover.Agent, uncoverQuery *uncover.Query) {
|
||||
defer swg.Done()
|
||||
keys := uncoverOptions.Provider.GetKeys()
|
||||
session, err := uncover.NewSession(&keys, uncoverOptions.Retries, uncoverOptions.Timeout)
|
||||
if err != nil {
|
||||
gologger.Error().Label(agent.Name()).Msgf("couldn't create uncover new session: %s", err)
|
||||
}
|
||||
ch, err := agent.Query(session, uncoverQuery)
|
||||
if err != nil {
|
||||
gologger.Warning().Msgf("%s", err)
|
||||
return
|
||||
}
|
||||
for result := range ch {
|
||||
replacer := strings.NewReplacer(
|
||||
"ip", result.IP,
|
||||
"host", result.Host,
|
||||
"port", fmt.Sprint(result.Port),
|
||||
)
|
||||
ret <- replacer.Replace(field)
|
||||
}
|
||||
}(agent, uncoverQuery)
|
||||
}
|
||||
}
|
||||
swg.Wait()
|
||||
close(ret)
|
||||
}()
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func loadKeys(engine string, options *ucRunner.Options) error {
|
||||
switch engine {
|
||||
case "fofa":
|
||||
if email, exists := os.LookupEnv("FOFA_EMAIL"); exists {
|
||||
if key, exists := os.LookupEnv("FOFA_KEY"); exists {
|
||||
options.Provider.Fofa = append(options.Provider.Fofa, fmt.Sprintf("%s:%s", email, key))
|
||||
} else {
|
||||
return errors.New("missing FOFA_KEY env variable")
|
||||
}
|
||||
} else {
|
||||
return errors.Errorf("FOFA_EMAIL & FOFA_KEY env variables are not configured")
|
||||
}
|
||||
case "shodan":
|
||||
if key, exists := os.LookupEnv("SHODAN_API_KEY"); exists {
|
||||
options.Provider.Shodan = append(options.Provider.Shodan, key)
|
||||
} else {
|
||||
return errors.Errorf("SHODAN_API_KEY env variable is not configured")
|
||||
}
|
||||
case "censys":
|
||||
if id, exists := os.LookupEnv("CENSYS_API_ID"); exists {
|
||||
if secret, exists := os.LookupEnv("CENSYS_API_SECRET"); exists {
|
||||
options.Provider.Censys = append(options.Provider.Censys, fmt.Sprintf("%s:%s", id, secret))
|
||||
} else {
|
||||
return errors.New("missing CENSYS_API_SECRET env variable")
|
||||
}
|
||||
} else {
|
||||
return errors.Errorf("CENSYS_API_ID & CENSYS_API_SECRET env variable is not configured")
|
||||
}
|
||||
case "hunter":
|
||||
if key, exists := os.LookupEnv("HUNTER_API_KEY"); exists {
|
||||
options.Provider.Hunter = append(options.Provider.Hunter, key)
|
||||
} else {
|
||||
return errors.Errorf("HUNTER_API_KEY env variable is not configured")
|
||||
}
|
||||
case "zoomeye":
|
||||
if key, exists := os.LookupEnv("ZOOMEYE_API_KEY"); exists {
|
||||
options.Provider.ZoomEye = append(options.Provider.ZoomEye, key)
|
||||
} else {
|
||||
return errors.Errorf("ZOOMEYE_API_KEY env variable is not configured")
|
||||
}
|
||||
case "quake":
|
||||
if key, exists := os.LookupEnv("QUAKE_TOKEN"); exists {
|
||||
options.Provider.Quake = append(options.Provider.Quake, key)
|
||||
} else {
|
||||
return errors.Errorf("QUAKE_TOKEN env variable is not configured")
|
||||
}
|
||||
case "netlas":
|
||||
if key, exists := os.LookupEnv("NETLAS_API_KEY"); exists {
|
||||
options.Provider.Netlas = append(options.Provider.Netlas, key)
|
||||
} else {
|
||||
return errors.Errorf("NETLAS_API_KEY env variable is not configured")
|
||||
}
|
||||
default:
|
||||
return errors.Errorf("unknown uncover agent")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
83
v2/pkg/protocols/common/updatecheck/client.go
Normal file
83
v2/pkg/protocols/common/updatecheck/client.go
Normal file
@ -0,0 +1,83 @@
|
||||
package updatecheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
RegisterServer = "https://version-check.nuclei.sh/"
|
||||
VersionsCall = "versions"
|
||||
IgnoreCall = "ignore"
|
||||
)
|
||||
|
||||
var nucleiVersion string
|
||||
|
||||
// LatestVersion is the latest version info for nuclei and templates repos
|
||||
type LatestVersion struct {
|
||||
Nuclei string
|
||||
Templates string
|
||||
IgnoreHash string
|
||||
}
|
||||
|
||||
func InitNucleiVersion(version string) {
|
||||
nucleiVersion = version
|
||||
}
|
||||
|
||||
// GetLatestNucleiTemplatesVersion returns the latest version info for nuclei and templates repos
|
||||
func GetLatestNucleiTemplatesVersion() (*LatestVersion, error) {
|
||||
body, err := callRegisterServer(VersionsCall)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
data := make(map[string]string)
|
||||
if err := jsoniter.NewDecoder(body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LatestVersion{Nuclei: data["nuclei"], Templates: data["templates"], IgnoreHash: data["ignore-hash"]}, nil
|
||||
}
|
||||
|
||||
// GetLatestIgnoreFile returns the latest version of nuclei ignore
|
||||
func GetLatestIgnoreFile() ([]byte, error) {
|
||||
body, err := callRegisterServer(IgnoreCall)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// callRegisterServer makes a request to RegisterServer with a call.
|
||||
func callRegisterServer(call string) (io.ReadCloser, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, RegisterServer+call, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not make request")
|
||||
}
|
||||
if nucleiVersion != "" {
|
||||
query := make(url.Values, 1)
|
||||
query.Set("v", nucleiVersion)
|
||||
req.URL.RawQuery = query.Encode()
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not do request")
|
||||
}
|
||||
return resp.Body, nil
|
||||
}
|
||||
@ -17,7 +17,7 @@ type ExcludeMatchers struct {
|
||||
// <template-id>:<matcher-name> is the syntax. Wildcards can be specified
|
||||
// using * character for either value.
|
||||
//
|
||||
// Ex- http-missing-security-headers:* skips all http-missing-security-header templates
|
||||
// Ex- http-missing-security-headers:* skips all http-missing-security-header templates
|
||||
func New(values []string) *ExcludeMatchers {
|
||||
excludeMatchers := &ExcludeMatchers{
|
||||
values: make(map[string]struct{}),
|
||||
|
||||
@ -39,6 +39,7 @@ func newHttpClient(options *types.Options) (*http.Client, error) {
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
ForceAttemptHTTP2: options.ForceAttemptHTTP2,
|
||||
DialContext: dialer.Dial,
|
||||
DialTLSContext: dialer.DialTLS,
|
||||
MaxIdleConns: 500,
|
||||
|
||||
@ -424,7 +424,7 @@ func (p *Page) SelectInputElement(act *Action, out map[string]string /*TODO revi
|
||||
|
||||
// WaitLoad waits for the page to load
|
||||
func (p *Page) WaitLoad(act *Action, out map[string]string /*TODO review unused parameter*/) error {
|
||||
p.page.Timeout(2 * time.Second).WaitNavigation(proto.PageLifecycleEventNameDOMContentLoaded)()
|
||||
p.page.Timeout(2 * time.Second).WaitNavigation(proto.PageLifecycleEventNameFirstMeaningfulPaint)()
|
||||
|
||||
// Wait for the window.onload event and also wait for the network requests
|
||||
// to become idle for a maximum duration of 3 seconds. If the requests
|
||||
|
||||
@ -95,7 +95,7 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error {
|
||||
|
||||
if len(request.Payloads) > 0 {
|
||||
var err error
|
||||
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, options.TemplatePath, options.Catalog, options.Options.AttackType)
|
||||
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, options.TemplatePath, options.Options.TemplatesDirectory, options.Options.Sandbox, options.Catalog, options.Options.AttackType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not parse payloads")
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import (
|
||||
// are similar enough to be considered one and can be checked by
|
||||
// just adding the matcher/extractors for the request and the correct IDs.
|
||||
func (request *Request) CanCluster(other *Request) bool {
|
||||
if len(request.Payloads) > 0 || len(request.Raw) > 0 || len(request.Body) > 0 || request.Unsafe || request.NeedsRequestCondition() || request.Name != "" {
|
||||
if len(request.Payloads) > 0 || len(request.Fuzzing) > 0 || len(request.Raw) > 0 || len(request.Body) > 0 || request.Unsafe || request.NeedsRequestCondition() || request.Name != "" {
|
||||
return false
|
||||
}
|
||||
if request.Method != other.Method ||
|
||||
|
||||
@ -68,8 +68,6 @@ func (rule *Rule) buildQueryInput(input *ExecuteRuleInput, parsed url.URL, inter
|
||||
return err
|
||||
}
|
||||
req.Header.Set("User-Agent", uarand.GetRandom())
|
||||
req.Header.Set("Accept", "*/*")
|
||||
req.Header.Set("Accept-Language", "en")
|
||||
} else {
|
||||
req = input.BaseRequest.Clone(context.Background())
|
||||
req.URL = &parsed
|
||||
|
||||
@ -350,7 +350,7 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error {
|
||||
}
|
||||
|
||||
if len(request.Payloads) > 0 {
|
||||
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, request.options.Catalog, request.options.Options.AttackType)
|
||||
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, request.options.Options.TemplatesDirectory, request.options.Options.Sandbox, request.options.Catalog, request.options.Options.AttackType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not parse payloads")
|
||||
}
|
||||
|
||||
@ -203,6 +203,7 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
ForceAttemptHTTP2: options.ForceAttemptHTTP2,
|
||||
DialContext: Dialer.Dial,
|
||||
DialTLSContext: Dialer.DialTLS,
|
||||
MaxIdleConns: maxIdleConns,
|
||||
@ -211,6 +212,7 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl
|
||||
TLSClientConfig: tlsConfig,
|
||||
DisableKeepAlives: disableKeepAlives,
|
||||
}
|
||||
|
||||
if types.ProxyURL != "" {
|
||||
if proxyURL, err := url.Parse(types.ProxyURL); err == nil {
|
||||
transport.Proxy = http.ProxyURL(proxyURL)
|
||||
|
||||
@ -755,26 +755,19 @@ func (request *Request) handleSignature(generatedRequest *generatedRequest) erro
|
||||
case AWSSignature:
|
||||
var awsSigner signer.Signer
|
||||
vars := request.options.Options.Vars.AsMap()
|
||||
awsAccessKeyId := types.ToString(vars["aws-id"])
|
||||
awsSecretAccessKey := types.ToString(vars["aws-secret"])
|
||||
awsSignerArgs := signer.AwsSignerArgs{AwsId: awsAccessKeyId, AwsSecretToken: awsSecretAccessKey}
|
||||
service := types.ToString(generatedRequest.dynamicValues["service"])
|
||||
region := types.ToString(generatedRequest.dynamicValues["region"])
|
||||
// if region is empty use default value
|
||||
if region == "" {
|
||||
region = types.ToString(signer.AwsDefaultVars["region"])
|
||||
}
|
||||
awsSignatureArguments := signer.AwsSignatureArguments{
|
||||
Service: types.ToString(service),
|
||||
Region: types.ToString(region),
|
||||
Time: time.Now(),
|
||||
awsopts := signer.AWSOptions{
|
||||
AwsID: types.ToString(vars["aws-id"]),
|
||||
AwsSecretToken: types.ToString(vars["aws-secret"]),
|
||||
}
|
||||
// type ctxkey string
|
||||
ctx := context.WithValue(context.Background(), signer.SignerArg("service"), generatedRequest.dynamicValues["service"])
|
||||
ctx = context.WithValue(ctx, signer.SignerArg("region"), generatedRequest.dynamicValues["region"])
|
||||
|
||||
awsSigner, err := signerpool.Get(request.options.Options, &signerpool.Configuration{SignerArgs: awsSignerArgs})
|
||||
awsSigner, err := signerpool.Get(request.options.Options, &signerpool.Configuration{SignerArgs: &awsopts})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = awsSigner.SignHTTP(generatedRequest.request.Request, awsSignatureArguments)
|
||||
err = awsSigner.SignHTTP(ctx, generatedRequest.request.Request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ func TestRequestGeneratorClusterBombSingle(t *testing.T) {
|
||||
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`},
|
||||
}
|
||||
catalogInstance := disk.NewCatalog("")
|
||||
req.generator, err = generators.New(req.Payloads, req.AttackType.Value, "", catalogInstance, "")
|
||||
req.generator, err = generators.New(req.Payloads, req.AttackType.Value, "", "", false, catalogInstance, "")
|
||||
require.Nil(t, err, "could not create generator")
|
||||
|
||||
generator := req.newGenerator(false)
|
||||
@ -58,7 +58,7 @@ func TestRequestGeneratorClusterBombMultipleRaw(t *testing.T) {
|
||||
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`, `GET /{{username}}@{{password}} HTTP/1.1`},
|
||||
}
|
||||
catalogInstance := disk.NewCatalog("")
|
||||
req.generator, err = generators.New(req.Payloads, req.AttackType.Value, "", catalogInstance, "")
|
||||
req.generator, err = generators.New(req.Payloads, req.AttackType.Value, "", "", false, catalogInstance, "")
|
||||
require.Nil(t, err, "could not create generator")
|
||||
|
||||
generator := req.newGenerator(false)
|
||||
|
||||
123
v2/pkg/protocols/http/signer/aws-sign.go
Normal file
123
v2/pkg/protocols/http/signer/aws-sign.go
Normal file
@ -0,0 +1,123 @@
|
||||
package signer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
|
||||
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||
)
|
||||
|
||||
// AWSOptions
|
||||
type AWSOptions struct {
|
||||
AwsID string
|
||||
AwsSecretToken string
|
||||
Service string
|
||||
Region string
|
||||
}
|
||||
|
||||
// Validate Signature Arguments
|
||||
func (a *AWSOptions) Validate() error {
|
||||
if a.Service == "" {
|
||||
return errors.New("aws service cannot be empty")
|
||||
}
|
||||
if a.Region == "" {
|
||||
return errors.New("aws region cannot be empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AWS v4 signer
|
||||
type AWSSigner struct {
|
||||
creds *aws.Credentials
|
||||
signer *v4.Signer
|
||||
options *AWSOptions
|
||||
}
|
||||
|
||||
// SignHTTP
|
||||
func (a *AWSSigner) SignHTTP(ctx context.Context, request *http.Request) error {
|
||||
if region, ok := ctx.Value(SignerArg("region")).(string); ok && region != "" {
|
||||
a.options.Region = region
|
||||
}
|
||||
if service, ok := ctx.Value(SignerArg("service")).(string); ok && service != "" {
|
||||
a.options.Service = service
|
||||
}
|
||||
if err := a.options.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.signer.SignHTTP(ctx, *a.creds, request, a.getPayloadHash(request), a.options.Service, a.options.Region, time.Now())
|
||||
}
|
||||
|
||||
// getPayloadHash returns hex encoded SHA-256 of request body
|
||||
func (a *AWSSigner) getPayloadHash(request *http.Request) string {
|
||||
if request.Body == nil {
|
||||
// Default Hash of Empty Payload
|
||||
return "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
}
|
||||
|
||||
// no need to close request body since it is a reusablereadercloser
|
||||
bin, _ := io.ReadAll(request.Body)
|
||||
sha256Hash := sha256.Sum256(bin)
|
||||
return hex.EncodeToString(sha256Hash[:])
|
||||
}
|
||||
|
||||
// NewAwsSigner
|
||||
func NewAwsSigner(opts *AWSOptions) (*AWSSigner, error) {
|
||||
credcache := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(opts.AwsID, opts.AwsSecretToken, ""))
|
||||
awscred, err := credcache.Retrieve(context.TODO())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &AWSSigner{
|
||||
creds: &awscred,
|
||||
options: opts,
|
||||
signer: v4.NewSigner(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewAwsSignerFromConfig
|
||||
func NewAwsSignerFromConfig(opts *AWSOptions) (*AWSSigner, error) {
|
||||
/*
|
||||
NewAwsSignerFromConfig fetches credentials from both
|
||||
1. Environment Variables (old & new)
|
||||
2. Shared Credentials ($HOME/.aws)
|
||||
*/
|
||||
cfg, err := awsconfig.LoadDefaultConfig(context.TODO())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
credcache := aws.NewCredentialsCache(cfg.Credentials)
|
||||
awscred, err := credcache.Retrieve(context.TODO())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &AWSSigner{
|
||||
creds: &awscred,
|
||||
options: opts,
|
||||
signer: v4.NewSigner(func(signer *v4.SignerOptions) {
|
||||
// signer.DisableURIPathEscaping = true
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
var AwsSkipList = map[string]interface{}{
|
||||
"region": struct{}{},
|
||||
}
|
||||
|
||||
var AwsDefaultVars = map[string]interface{}{
|
||||
"region": "us-east-2",
|
||||
}
|
||||
|
||||
var AwsInternalOnlyVars = map[string]interface{}{
|
||||
"aws-id": struct{}{},
|
||||
"aws-secret": struct{}{},
|
||||
}
|
||||
@ -1,147 +0,0 @@
|
||||
package signer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
|
||||
)
|
||||
|
||||
type AwsSigner struct {
|
||||
creds *credentials.Credentials
|
||||
signer *v4.Signer
|
||||
}
|
||||
|
||||
type AwsSignerArgs struct {
|
||||
AwsId string
|
||||
AwsSecretToken string
|
||||
}
|
||||
|
||||
var credentialCreationError = errors.New("couldn't create the credentials structure")
|
||||
|
||||
func (awsSignerArgs AwsSignerArgs) Validate() error {
|
||||
if awsSignerArgs.AwsId == "" {
|
||||
return errors.New("empty id")
|
||||
}
|
||||
if awsSignerArgs.AwsSecretToken == "" {
|
||||
return errors.New("empty token")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type AwsSignatureArguments struct {
|
||||
Service string
|
||||
Region string
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
func (awsSignatureArguments AwsSignatureArguments) Validate() error {
|
||||
if awsSignatureArguments.Region == "" {
|
||||
return errors.New("empty region")
|
||||
}
|
||||
if awsSignatureArguments.Service == "" {
|
||||
return errors.New("empty service")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewAwsSigner(args AwsSignerArgs) (*AwsSigner, error) {
|
||||
if err := args.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
creds := credentials.NewStaticCredentials(args.AwsId, args.AwsSecretToken, "")
|
||||
if creds == nil {
|
||||
return nil, credentialCreationError
|
||||
}
|
||||
signer := v4.NewSigner(creds)
|
||||
return &AwsSigner{creds: creds, signer: signer}, nil
|
||||
}
|
||||
|
||||
func NewAwsSignerFromEnv() (*AwsSigner, error) {
|
||||
creds := credentials.NewEnvCredentials()
|
||||
if creds == nil {
|
||||
return nil, credentialCreationError
|
||||
}
|
||||
signer := v4.NewSigner(creds)
|
||||
return &AwsSigner{creds: creds, signer: signer}, nil
|
||||
}
|
||||
|
||||
func NewAwsSignerFromFile() (*AwsSigner, error) {
|
||||
creds := credentials.NewSharedCredentials("", "")
|
||||
if creds == nil {
|
||||
return nil, credentialCreationError
|
||||
}
|
||||
signer := v4.NewSigner(creds)
|
||||
return &AwsSigner{creds: creds, signer: signer}, nil
|
||||
}
|
||||
|
||||
func (awsSigner *AwsSigner) SignHTTP(request *http.Request, args interface{}) error {
|
||||
signatureArgs, err := awsSigner.checkSignatureArgs(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
awsSigner.prepareRequest(request)
|
||||
var body *bytes.Reader
|
||||
if request.Body != nil {
|
||||
bodyBytes, err := io.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.Body.Close()
|
||||
body = bytes.NewReader(bodyBytes)
|
||||
}
|
||||
if _, err := awsSigner.signer.Sign(request, body, signatureArgs.Service, signatureArgs.Region, signatureArgs.Time); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (awsSigner *AwsSigner) CalculateHTTPHeaders(request *http.Request, args interface{}) (map[string]string, error) {
|
||||
signatureArgs, err := awsSigner.checkSignatureArgs(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reqClone := request.Clone(context.Background())
|
||||
awsSigner.prepareRequest(reqClone)
|
||||
err = awsSigner.SignHTTP(reqClone, signatureArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headers := make(map[string]string)
|
||||
headers["X-Amz-Date"] = reqClone.Header.Get("X-Amz-Date")
|
||||
headers["Authorization"] = reqClone.Header.Get("Authorization")
|
||||
return headers, nil
|
||||
}
|
||||
|
||||
func (awsSigner *AwsSigner) checkSignatureArgs(args interface{}) (AwsSignatureArguments, error) {
|
||||
if signatureArgs, ok := args.(AwsSignatureArguments); ok {
|
||||
return signatureArgs, signatureArgs.Validate()
|
||||
}
|
||||
return AwsSignatureArguments{}, errors.New("wrong signature type")
|
||||
}
|
||||
|
||||
func (awsSigner *AwsSigner) prepareRequest(request *http.Request) {
|
||||
request.Header.Del("Host")
|
||||
}
|
||||
|
||||
var AwsSkipList = map[string]interface{}{
|
||||
"region": struct{}{},
|
||||
}
|
||||
|
||||
var AwsDefaultVars = map[string]interface{}{
|
||||
"region": "us-east-2",
|
||||
}
|
||||
|
||||
var AwsInternalOnlyVars = map[string]interface{}{
|
||||
"aws-id": struct{}{},
|
||||
"aws-secret": struct{}{},
|
||||
}
|
||||
@ -1,36 +1,30 @@
|
||||
package signer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// An Argument that can be passed to Signer
|
||||
type SignerArg string
|
||||
|
||||
type Signer interface {
|
||||
SignHTTP(request *http.Request, args interface{}) error
|
||||
CalculateHTTPHeaders(request *http.Request, args interface{}) (map[string]string, error)
|
||||
SignHTTP(ctx context.Context, request *http.Request) error
|
||||
}
|
||||
|
||||
type SignerArgs interface {
|
||||
Validate() error
|
||||
}
|
||||
|
||||
type SignatureArguments interface {
|
||||
Validate() error
|
||||
}
|
||||
|
||||
func NewSigner(args SignerArgs) (signer Signer, err error) {
|
||||
switch signerArgs := args.(type) {
|
||||
case AwsSignerArgs:
|
||||
case *AWSOptions:
|
||||
awsSigner, err := NewAwsSigner(signerArgs)
|
||||
if err != nil {
|
||||
// $HOME/.aws/credentials
|
||||
awsSigner, err = NewAwsSignerFromFile()
|
||||
awsSigner, err = NewAwsSignerFromConfig(signerArgs)
|
||||
if err != nil {
|
||||
// env variables
|
||||
awsSigner, err = NewAwsSignerFromEnv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return awsSigner, err
|
||||
|
||||
@ -184,7 +184,7 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error {
|
||||
}
|
||||
|
||||
if len(request.Payloads) > 0 {
|
||||
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, request.options.Catalog, request.options.Options.AttackType)
|
||||
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, request.options.Options.TemplatesDirectory, request.options.Options.Sandbox, request.options.Catalog, request.options.Options.AttackType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not parse payloads")
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ type Request struct {
|
||||
// - "tls11"
|
||||
// - "tls12"
|
||||
// - "tls13"
|
||||
MinVersion string `yaml:"min_version,omitempty" jsonschema:"title=TLS version,description=Minimum tls version - automatic if not specified.,enum=sslv3,enum=tls10,enum=tls11,enum=tls12,enum=tls13"`
|
||||
MinVersion string `yaml:"min_version,omitempty" jsonschema:"title=Min. TLS version,description=Minimum tls version - automatic if not specified.,enum=sslv3,enum=tls10,enum=tls11,enum=tls12,enum=tls13"`
|
||||
// description: |
|
||||
// Max tls version - auto if not specified.
|
||||
// values:
|
||||
@ -59,10 +59,17 @@ type Request struct {
|
||||
// - "tls11"
|
||||
// - "tls12"
|
||||
// - "tls13"
|
||||
MaxVersion string `yaml:"max_version,omitempty" jsonschema:"title=TLS version,description=Max tls version - automatic if not specified.,enum=sslv3,enum=tls10,enum=tls11,enum=tls12,enum=tls13"`
|
||||
MaxVersion string `yaml:"max_version,omitempty" jsonschema:"title=Max. TLS version,description=Max tls version - automatic if not specified.,enum=sslv3,enum=tls10,enum=tls11,enum=tls12,enum=tls13"`
|
||||
// description: |
|
||||
// Client Cipher Suites - auto if not specified.
|
||||
CiperSuites []string `yaml:"cipher_suites,omitempty"`
|
||||
// description: |
|
||||
// Tls Scan Mode - auto if not specified
|
||||
// values:
|
||||
// - "ctls"
|
||||
// - "ztls"
|
||||
// - "auto"
|
||||
ScanMode string `yaml:"scan_mode,omitempty" jsonschema:"title=Scan Mode,description=Scan Mode - auto if not specified.,enum=ctls,enum=ztls,enum=auto"`
|
||||
|
||||
// cache any variables that may be needed for operation.
|
||||
dialer *fastdialer.Dialer
|
||||
@ -93,9 +100,13 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error {
|
||||
Retries: request.options.Options.Retries,
|
||||
Timeout: request.options.Options.Timeout,
|
||||
Fastdialer: client,
|
||||
ClientHello: true,
|
||||
ServerHello: true,
|
||||
}
|
||||
if options.Options.ZTLS {
|
||||
tlsxOptions.ScanMode = "ztls"
|
||||
} else if request.ScanMode != "" {
|
||||
tlsxOptions.ScanMode = request.ScanMode
|
||||
}
|
||||
tlsxService, err := tlsx.New(tlsxOptions)
|
||||
if err != nil {
|
||||
@ -127,18 +138,18 @@ func (request *Request) GetID() string {
|
||||
|
||||
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
|
||||
func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicValues, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
|
||||
address, err := getAddress(input.MetaInput.Input)
|
||||
hostPort, err := getAddress(input.MetaInput.Input)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
hostname, port, _ := net.SplitHostPort(address)
|
||||
hostname, port, _ := net.SplitHostPort(hostPort)
|
||||
|
||||
requestOptions := request.options
|
||||
payloadValues := make(map[string]interface{})
|
||||
for k, v := range dynamicValues {
|
||||
payloadValues[k] = v
|
||||
}
|
||||
payloadValues["Hostname"] = address
|
||||
payloadValues["Hostname"] = hostPort
|
||||
payloadValues["Host"] = hostname
|
||||
payloadValues["Port"] = port
|
||||
|
||||
@ -163,15 +174,22 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
return errors.Wrap(err, "could not split input host port")
|
||||
}
|
||||
|
||||
response, err := request.tlsx.Connect(host, host, port)
|
||||
var hostIp string
|
||||
if input.MetaInput.CustomIP != "" {
|
||||
hostIp = input.MetaInput.CustomIP
|
||||
} else {
|
||||
hostIp = host
|
||||
}
|
||||
|
||||
response, err := request.tlsx.Connect(host, hostIp, port)
|
||||
if err != nil {
|
||||
requestOptions.Output.Request(requestOptions.TemplateID, input.MetaInput.Input, request.Type().String(), err)
|
||||
requestOptions.Progress.IncrementFailedRequestsBy(1)
|
||||
return errors.Wrap(err, "could not connect to server")
|
||||
}
|
||||
|
||||
requestOptions.Output.Request(requestOptions.TemplateID, address, request.Type().String(), err)
|
||||
gologger.Verbose().Msgf("Sent SSL request to %s", address)
|
||||
requestOptions.Output.Request(requestOptions.TemplateID, hostPort, request.Type().String(), err)
|
||||
gologger.Verbose().Msgf("Sent SSL request to %s", hostPort)
|
||||
|
||||
if requestOptions.Options.Debug || requestOptions.Options.DebugRequests || requestOptions.Options.StoreResponse {
|
||||
msg := fmt.Sprintf("[%s] Dumped SSL request for %s", requestOptions.TemplateID, input.MetaInput.Input)
|
||||
@ -193,7 +211,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
data["host"] = input
|
||||
data["matched"] = addressToDial
|
||||
if input.MetaInput.CustomIP != "" {
|
||||
data["ip"] = input.MetaInput.CustomIP
|
||||
data["ip"] = hostIp
|
||||
} else {
|
||||
data["ip"] = request.dialer.GetDialedIP(hostname)
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error {
|
||||
request.dialer = client
|
||||
|
||||
if len(request.Payloads) > 0 {
|
||||
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, options.Catalog, options.Options.AttackType)
|
||||
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, request.options.Options.TemplatesDirectory, request.options.Options.Sandbox, options.Catalog, options.Options.AttackType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not parse payloads")
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/corpix/uarand"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
@ -112,6 +113,7 @@ func (exporter *Exporter) Export(event *output.ResultEvent) error {
|
||||
if len(exporter.authentication) > 0 {
|
||||
req.Header.Add("Authorization", exporter.authentication)
|
||||
}
|
||||
req.Header.Set("User-Agent", uarand.GetRandom())
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
d := data{
|
||||
|
||||
@ -1,28 +1,23 @@
|
||||
package sarif
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/owenrumney/go-sarif/v2/sarif"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/format"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/utils"
|
||||
"github.com/projectdiscovery/sarif"
|
||||
)
|
||||
|
||||
// Exporter is an exporter for nuclei sarif output format.
|
||||
type Exporter struct {
|
||||
sarif *sarif.Report
|
||||
run *sarif.Run
|
||||
mutex *sync.Mutex
|
||||
|
||||
home string
|
||||
sarif *sarif.Report
|
||||
mutex *sync.Mutex
|
||||
rulemap map[string]*int // contains rule-id && ruleIndex
|
||||
rules []sarif.ReportingDescriptor
|
||||
options *Options
|
||||
}
|
||||
|
||||
@ -34,106 +29,168 @@ type Options struct {
|
||||
|
||||
// New creates a new sarif exporter integration client based on options.
|
||||
func New(options *Options) (*Exporter, error) {
|
||||
report, err := sarif.New(sarif.Version210)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not create sarif exporter")
|
||||
report := sarif.NewReport()
|
||||
exporter := &Exporter{
|
||||
sarif: report,
|
||||
mutex: &sync.Mutex{},
|
||||
rules: []sarif.ReportingDescriptor{},
|
||||
rulemap: map[string]*int{},
|
||||
options: options,
|
||||
}
|
||||
return exporter, nil
|
||||
}
|
||||
|
||||
// addToolDetails adds details of static analysis tool (i.e nuclei)
|
||||
func (exporter *Exporter) addToolDetails() {
|
||||
driver := sarif.ToolComponent{
|
||||
Name: "Nuclei",
|
||||
Organization: "ProjectDiscovery",
|
||||
Product: "Nuclei",
|
||||
ShortDescription: &sarif.MultiformatMessageString{
|
||||
Text: "Fast and Customizable Vulnerability Scanner",
|
||||
},
|
||||
FullDescription: &sarif.MultiformatMessageString{
|
||||
Text: "Fast and customizable vulnerability scanner based on simple YAML based DSL",
|
||||
},
|
||||
FullName: "Nuclei v" + config.Version,
|
||||
SemanticVersion: "v" + config.Version,
|
||||
DownloadURI: "https://github.com/projectdiscovery/nuclei/releases",
|
||||
Rules: exporter.rules,
|
||||
}
|
||||
exporter.sarif.RegisterTool(driver)
|
||||
|
||||
reportloc := sarif.ArtifactLocation{
|
||||
Uri: "file:///" + exporter.options.File,
|
||||
Description: &sarif.Message{
|
||||
Text: "Nuclei Sarif Report",
|
||||
},
|
||||
}
|
||||
|
||||
templatePath, err := utils.GetDefaultTemplatePath()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not template path")
|
||||
invocation := sarif.Invocation{
|
||||
CommandLine: os.Args[0],
|
||||
Arguments: os.Args[1:],
|
||||
ResponseFiles: []sarif.ArtifactLocation{reportloc},
|
||||
}
|
||||
exporter.sarif.RegisterToolInvocation(invocation)
|
||||
}
|
||||
|
||||
// getSeverity in terms of sarif
|
||||
func (exporter *Exporter) getSeverity(severity string) (sarif.Level, string) {
|
||||
switch severity {
|
||||
case "critical":
|
||||
return sarif.Error, "9.4"
|
||||
case "high":
|
||||
return sarif.Error, "8"
|
||||
case "medium":
|
||||
return sarif.Note, "5"
|
||||
case "low":
|
||||
return sarif.Note, "2"
|
||||
case "info":
|
||||
return sarif.None, "1"
|
||||
}
|
||||
|
||||
run := sarif.NewRunWithInformationURI("nuclei", "https://github.com/projectdiscovery/nuclei")
|
||||
return &Exporter{options: options, home: templatePath, sarif: report, run: run, mutex: &sync.Mutex{}}, nil
|
||||
return sarif.None, "9.5"
|
||||
}
|
||||
|
||||
// Export exports a passed result event to sarif structure
|
||||
func (exporter *Exporter) Export(event *output.ResultEvent) error {
|
||||
templatePath := strings.TrimPrefix(event.TemplatePath, exporter.home)
|
||||
|
||||
h := sha1.New()
|
||||
_, _ = h.Write([]byte(event.Host))
|
||||
templateID := event.TemplateID + "-" + hex.EncodeToString(h.Sum(nil))
|
||||
|
||||
var ruleName string
|
||||
if utils.IsNotBlank(event.Info.Name) {
|
||||
ruleName = event.Info.Name
|
||||
}
|
||||
|
||||
var templateURL string
|
||||
if strings.HasPrefix(event.TemplatePath, exporter.home) {
|
||||
templateURL = "https://github.com/projectdiscovery/nuclei-templates/blob/master" + templatePath
|
||||
} else {
|
||||
templateURL = "https://github.com/projectdiscovery/nuclei-templates"
|
||||
}
|
||||
|
||||
var ruleDescription string
|
||||
if utils.IsNotBlank(event.Info.Description) {
|
||||
ruleDescription = event.Info.Description
|
||||
}
|
||||
|
||||
exporter.mutex.Lock()
|
||||
defer exporter.mutex.Unlock()
|
||||
|
||||
_ = exporter.run.AddRule(templateID).
|
||||
WithDescription(ruleName).
|
||||
WithHelp(sarif.NewMarkdownMultiformatMessageString(format.MarkdownDescription(event))).
|
||||
WithHelpURI(templateURL).
|
||||
WithFullDescription(sarif.NewMultiformatMessageString(ruleDescription))
|
||||
severity := event.Info.SeverityHolder.Severity.String()
|
||||
resultHeader := fmt.Sprintf("%v (%v) found on %v", event.Info.Name, event.TemplateID, event.Host)
|
||||
resultLevel, vulnRating := exporter.getSeverity(severity)
|
||||
|
||||
result := sarif.NewRuleResult(templateID).
|
||||
WithMessage(sarif.NewTextMessage(event.Host)).
|
||||
WithLevel(getSarifSeverity(event))
|
||||
// Extra metdata if generated sarif is uploaded to github security page
|
||||
ghmeta := map[string]interface{}{}
|
||||
ghmeta["tags"] = []string{"security"}
|
||||
ghmeta["security-severity"] = vulnRating
|
||||
|
||||
exporter.run.AddResult(result)
|
||||
// rule contain details of template
|
||||
rule := sarif.ReportingDescriptor{
|
||||
Id: event.TemplateID,
|
||||
Name: event.Info.Name,
|
||||
FullDescription: &sarif.MultiformatMessageString{
|
||||
// Points to template URL
|
||||
Text: event.Info.Description + "\nMore details at\n" + event.TemplateURL + "\n",
|
||||
},
|
||||
Properties: ghmeta,
|
||||
}
|
||||
|
||||
// Also write file match metadata to file
|
||||
if event.Type == "file" && (event.FileToIndexPosition != nil && len(event.FileToIndexPosition) > 0) {
|
||||
for file, line := range event.FileToIndexPosition {
|
||||
result.AddLocation(sarif.NewLocation().WithMessage(sarif.NewMessage().WithText(ruleName)).WithPhysicalLocation(
|
||||
sarif.NewPhysicalLocation().
|
||||
WithArtifactLocation(sarif.NewArtifactLocation().WithUri(file)).
|
||||
WithRegion(sarif.NewRegion().WithStartColumn(1).WithStartLine(line).WithEndLine(line).WithEndColumn(32)),
|
||||
))
|
||||
// Github Uses ShortDescription as title
|
||||
if event.Info.Description != "" {
|
||||
rule.ShortDescription = &sarif.MultiformatMessageString{
|
||||
Text: resultHeader,
|
||||
}
|
||||
}
|
||||
|
||||
// If rule is added
|
||||
ruleIndex := len(exporter.rules) - 1
|
||||
if exporter.rulemap[rule.Id] == nil {
|
||||
exporter.rulemap[rule.Id] = &ruleIndex
|
||||
exporter.rules = append(exporter.rules, rule)
|
||||
} else {
|
||||
result.AddLocation(sarif.NewLocation().WithMessage(sarif.NewMessage().WithText(event.Host)).WithPhysicalLocation(
|
||||
sarif.NewPhysicalLocation().
|
||||
WithArtifactLocation(sarif.NewArtifactLocation().WithUri("README.md")).
|
||||
WithRegion(sarif.NewRegion().WithStartColumn(1).WithStartLine(1).WithEndLine(1).WithEndColumn(1)),
|
||||
))
|
||||
ruleIndex = *exporter.rulemap[rule.Id]
|
||||
}
|
||||
|
||||
// vulnerability target/location
|
||||
location := sarif.Location{
|
||||
Message: &sarif.Message{
|
||||
Text: path.Join(event.Host, event.Path),
|
||||
},
|
||||
PhysicalLocation: sarif.PhysicalLocation{
|
||||
ArtifactLocation: sarif.ArtifactLocation{
|
||||
// github only accepts file:// protocol and local & relative files only
|
||||
// to avoid errors // is used which also translates to file according to specification
|
||||
Uri: "/" + event.Path,
|
||||
Description: &sarif.Message{
|
||||
Text: path.Join(event.Host, event.Path),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// vulnerability report/result
|
||||
result := &sarif.Result{
|
||||
RuleId: rule.Id,
|
||||
RuleIndex: ruleIndex,
|
||||
Level: resultLevel,
|
||||
Kind: sarif.Open,
|
||||
Message: &sarif.Message{
|
||||
Text: resultHeader,
|
||||
},
|
||||
Locations: []sarif.Location{location},
|
||||
Rule: sarif.ReportingDescriptorReference{
|
||||
Id: rule.Id,
|
||||
},
|
||||
}
|
||||
|
||||
exporter.sarif.RegisterResult(*result)
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// getSarifSeverity returns the sarif severity
|
||||
func getSarifSeverity(event *output.ResultEvent) string {
|
||||
switch event.Info.SeverityHolder.Severity {
|
||||
case severity.Info:
|
||||
return "note"
|
||||
case severity.Low, severity.Medium:
|
||||
return "warning"
|
||||
case severity.High, severity.Critical:
|
||||
return "error"
|
||||
default:
|
||||
return "note"
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the exporter after operation
|
||||
// Close Writes data and closes the exporter after operation
|
||||
func (exporter *Exporter) Close() error {
|
||||
exporter.mutex.Lock()
|
||||
defer exporter.mutex.Unlock()
|
||||
|
||||
exporter.sarif.AddRun(exporter.run)
|
||||
if len(exporter.run.Results) == 0 {
|
||||
return nil // do not write when no results
|
||||
if len(exporter.rules) == 0 {
|
||||
// no output if there are no results
|
||||
return nil
|
||||
}
|
||||
file, err := os.Create(exporter.options.File)
|
||||
// links results and rules/templates
|
||||
exporter.addToolDetails()
|
||||
|
||||
bin, err := exporter.sarif.Export()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create sarif output file")
|
||||
return errors.Wrap(err, "failed to generate sarif report")
|
||||
}
|
||||
defer file.Close()
|
||||
return exporter.sarif.Write(file)
|
||||
if err := os.WriteFile(exporter.options.File, bin, 0644); err != nil {
|
||||
return errors.Wrap(err, "failed to create sarif file")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
@ -14,7 +14,6 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
)
|
||||
|
||||
|
||||
// Summary returns a formatted built one line summary of the event
|
||||
func Summary(event *output.ResultEvent) string {
|
||||
template := GetMatchedTemplate(event)
|
||||
@ -179,7 +178,7 @@ func ToMarkdownTableString(templateInfo *model.Info) string {
|
||||
insertionOrderedStringMap.ForEach(func(key string, value interface{}) {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if utils.IsNotBlank(value) {
|
||||
if !utils.IsBlank(value) {
|
||||
builder.WriteString(fmt.Sprintf("| %s | %s |\n", key, value))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1717,7 +1717,7 @@ func init() {
|
||||
Value: "Matched is the input which was matched upon",
|
||||
},
|
||||
}
|
||||
SSLRequestDoc.Fields = make([]encoder.Doc, 7)
|
||||
SSLRequestDoc.Fields = make([]encoder.Doc, 8)
|
||||
SSLRequestDoc.Fields[0].Name = "matchers"
|
||||
SSLRequestDoc.Fields[0].Type = "[]matchers.Matcher"
|
||||
SSLRequestDoc.Fields[0].Note = ""
|
||||
@ -1771,6 +1771,16 @@ func init() {
|
||||
SSLRequestDoc.Fields[6].Note = ""
|
||||
SSLRequestDoc.Fields[6].Description = "Client Cipher Suites - auto if not specified."
|
||||
SSLRequestDoc.Fields[6].Comments[encoder.LineComment] = "Client Cipher Suites - auto if not specified."
|
||||
SSLRequestDoc.Fields[7].Name = "scan_mode"
|
||||
SSLRequestDoc.Fields[7].Type = "string"
|
||||
SSLRequestDoc.Fields[7].Note = ""
|
||||
SSLRequestDoc.Fields[7].Description = "Tls Scan Mode - auto if not specified"
|
||||
SSLRequestDoc.Fields[7].Comments[encoder.LineComment] = "Tls Scan Mode - auto if not specified"
|
||||
SSLRequestDoc.Fields[7].Values = []string{
|
||||
"ctls",
|
||||
"ztls",
|
||||
"auto",
|
||||
}
|
||||
|
||||
WEBSOCKETRequestDoc.Type = "websocket.Request"
|
||||
WEBSOCKETRequestDoc.Comments[encoder.LineComment] = " Request is a request for the Websocket protocol"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// Package templates
|
||||
//nolint //do not lint as examples with no usage
|
||||
// nolint //do not lint as examples with no usage
|
||||
package templates
|
||||
|
||||
import (
|
||||
|
||||
@ -91,7 +91,7 @@ func NewMockExecuterOptions(options *types.Options, info *TemplateInfo) *protoco
|
||||
IssuesClient: nil,
|
||||
Browser: nil,
|
||||
Catalog: disk.NewCatalog(options.TemplatesDirectory),
|
||||
RateLimiter: ratelimit.New(context.Background(), options.RateLimit, time.Second),
|
||||
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
|
||||
}
|
||||
return executerOpts
|
||||
}
|
||||
|
||||
@ -167,6 +167,8 @@ type Options struct {
|
||||
// using same matchers/extractors from http protocol without the need
|
||||
// to send a new request, reading responses from a file.
|
||||
OfflineHTTP bool
|
||||
// Force HTTP2 requests
|
||||
ForceAttemptHTTP2 bool
|
||||
// StatsJSON writes stats output in JSON format
|
||||
StatsJSON bool
|
||||
// Headless specifies whether to allow headless mode templates
|
||||
@ -220,6 +222,8 @@ type Options struct {
|
||||
EnableProgressBar bool
|
||||
// TemplatesVersion shows the templates installed version
|
||||
TemplatesVersion bool
|
||||
// TemplateDisplay displays the template contents
|
||||
TemplateDisplay bool
|
||||
// TemplateList lists available templates
|
||||
TemplateList bool
|
||||
// HangMonitor enables nuclei hang monitoring
|
||||
@ -232,8 +236,8 @@ type Options struct {
|
||||
Stream bool
|
||||
// NoMeta disables display of metadata for the matches
|
||||
NoMeta bool
|
||||
// NoTimestamp disables display of timestamp for the matcher
|
||||
NoTimestamp bool
|
||||
// Timestamp enables display of timestamp for the matcher
|
||||
Timestamp bool
|
||||
// Project is used to avoid sending same HTTP request multiple times
|
||||
Project bool
|
||||
// NewTemplates only runs newly added templates from the repository
|
||||
@ -258,6 +262,8 @@ type Options struct {
|
||||
ClientCAFile string
|
||||
// Use ZTLS library
|
||||
ZTLS bool
|
||||
// Sandbox enables sandboxed nuclei template execution
|
||||
Sandbox bool
|
||||
// ShowMatchLine enables display of match line number
|
||||
ShowMatchLine bool
|
||||
// EnablePprof enables exposing pprof runtime information with a webserver.
|
||||
@ -290,6 +296,18 @@ type Options struct {
|
||||
IncludeConditions goflags.StringSlice
|
||||
// Custom Config Directory
|
||||
CustomConfigDir string
|
||||
// Enable uncover egine
|
||||
Uncover bool
|
||||
// Uncover search query
|
||||
UncoverQuery goflags.StringSlice
|
||||
// Uncover search engine
|
||||
UncoverEngine goflags.StringSlice
|
||||
// Uncover search field
|
||||
UncoverField string
|
||||
// Uncover search limit
|
||||
UncoverLimit int
|
||||
// Uncover search delay
|
||||
UncoverDelay int
|
||||
// ConfigPath contains the config path (used by healthcheck)
|
||||
ConfigPath string
|
||||
// ScanAllIPs associated to a dns record
|
||||
@ -299,7 +317,7 @@ type Options struct {
|
||||
// Github token used to clone/pull from private repos for custom templates
|
||||
GithubToken string
|
||||
// GithubTemplateRepo is the list of custom public/private templates github repos
|
||||
GithubTemplateRepo goflags.StringSlice
|
||||
GithubTemplateRepo []string
|
||||
// AWS access key for downloading templates from s3 bucket
|
||||
AwsAccessKey string
|
||||
// AWS secret key for downloading templates from s3 bucket
|
||||
|
||||
@ -14,10 +14,6 @@ func IsBlank(value string) bool {
|
||||
return strings.TrimSpace(value) == ""
|
||||
}
|
||||
|
||||
func IsNotBlank(value string) bool {
|
||||
return !IsBlank(value)
|
||||
}
|
||||
|
||||
func UnwrapError(err error) error {
|
||||
for { // get the last wrapped error
|
||||
unwrapped := errors.Unwrap(err)
|
||||
|
||||
@ -105,8 +105,8 @@ func (matcher *Matcher) Match(result *operators.Result) bool {
|
||||
}
|
||||
|
||||
for i, name := range names {
|
||||
_, matchOK := result.Matches[name]
|
||||
_, extractOK := result.Extracts[name]
|
||||
matchOK := result.HasMatch(name)
|
||||
extractOK := result.HasExtract(name)
|
||||
|
||||
if !matchOK && !extractOK {
|
||||
if matcher.condition == ANDCondition {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user