mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-18 05:05:23 +00:00
Merge branch 'dev' of https://github.com/projectdiscovery/nuclei into dev
This commit is contained in:
commit
428bb9b0b6
5
.github/dependabot.yml
vendored
5
.github/dependabot.yml
vendored
@ -11,6 +11,7 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
target-branch: "dev"
|
||||
commit-message:
|
||||
prefix: "chore"
|
||||
include: "scope"
|
||||
@ -20,6 +21,7 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
target-branch: "dev"
|
||||
commit-message:
|
||||
prefix: "chore"
|
||||
include: "scope"
|
||||
@ -29,6 +31,7 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
target-branch: "dev"
|
||||
commit-message:
|
||||
prefix: "chore"
|
||||
include: "scope"
|
||||
include: "scope"
|
||||
31
.github/workflows/build-test.yml
vendored
Normal file
31
.github/workflows/build-test.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: 🔨 Build Test
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Test Builds
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Test
|
||||
run: go test ./...
|
||||
working-directory: v2/
|
||||
|
||||
- name: Integration Tests
|
||||
run: bash run.sh
|
||||
working-directory: integration_tests/
|
||||
|
||||
- name: Build
|
||||
run: go build .
|
||||
working-directory: v2/cmd/nuclei/
|
||||
45
.github/workflows/build.yaml
vendored
45
.github/workflows/build.yaml
vendored
@ -1,45 +0,0 @@
|
||||
name: Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: golangci-lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||
version: latest
|
||||
args: --timeout 5m
|
||||
working-directory: v2/
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Test
|
||||
run: go test ./...
|
||||
working-directory: v2/
|
||||
|
||||
- name: Integration Tests
|
||||
run: bash run.sh
|
||||
working-directory: integration_tests/
|
||||
|
||||
- name: Build
|
||||
run: go build .
|
||||
working-directory: v2/cmd/nuclei/
|
||||
38
.github/workflows/codeql-analysis.yml
vendored
Normal file
38
.github/workflows/codeql-analysis.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
name: 🚨 CodeQL Analysis
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
39
.github/workflows/dockerhub-push.yml
vendored
39
.github/workflows/dockerhub-push.yml
vendored
@ -1,17 +1,34 @@
|
||||
name: ◎ Docker Push
|
||||
name: 🌥 Docker Push
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Publish to Dockerhub Registry
|
||||
uses: dawidd6/action-docker-publish-changed@v3
|
||||
with:
|
||||
name: projectdiscovery/nuclei
|
||||
username: ${{secrets.DOCKER_USERNAME}}
|
||||
password: ${{secrets.DOCKER_PASSWORD}}
|
||||
platforms: linux/amd64,linux/arm64,linux/arm
|
||||
tag: latest
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/arm
|
||||
push: true
|
||||
tags: projectdiscovery/nuclei:latest
|
||||
19
.github/workflows/lint-test.yml
vendored
Normal file
19
.github/workflows/lint-test.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
name: 🙏🏻 Lint Test
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout 5m
|
||||
working-directory: v2/
|
||||
@ -1,8 +1,9 @@
|
||||
name: Release
|
||||
name: 🎉 Release Binary
|
||||
on:
|
||||
create:
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
@ -17,7 +18,7 @@ jobs:
|
||||
name: "Set up Go"
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
go-version: 1.16
|
||||
-
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
@ -26,5 +27,4 @@ jobs:
|
||||
with:
|
||||
args: "release --rm-dist"
|
||||
version: latest
|
||||
workdir: v2/
|
||||
|
||||
workdir: v2/
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@ v2/cmd/nuclei/nuclei
|
||||
.idea
|
||||
integration_tests/integration-test
|
||||
integration_tests/nuclei
|
||||
v2/cmd/integration-test/integration-test
|
||||
v2/cmd/integration-test/integration-test
|
||||
bin
|
||||
82
README.md
82
README.md
@ -12,7 +12,7 @@
|
||||
<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>
|
||||
<a href="https://discord.gg/KECAGdH"><img src="https://img.shields.io/discord/695645237418131507.svg?logo=discord"></a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/actions/workflows/build.yaml"><img src="https://github.com/projectdiscovery/nuclei/actions/workflows/build.yaml/badge.svg?branch=master"></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>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@ -48,7 +48,7 @@ We have a [dedicated repository](https://github.com/projectdiscovery/nuclei-temp
|
||||
▶ GO111MODULE=on go get -v github.com/projectdiscovery/nuclei/v2/cmd/nuclei
|
||||
```
|
||||
|
||||
**More installation [methods can be found here](https://nuclei.projectdiscovery.io/nuclei/get-started/#nuclei-installation).**
|
||||
**More installation [methods can be found here](https://nuclei.projectdiscovery.io/nuclei/get-started.html).**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
@ -66,6 +66,84 @@ Nuclei is designed to used with custom templates according to the target and wor
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Usage
|
||||
|
||||
```sh
|
||||
nuclei -h
|
||||
```
|
||||
|
||||
This will display help for the tool. Here are all the switches it supports.
|
||||
|
||||
<details>
|
||||
<summary> 👉 nuclei help menu 👈</summary>
|
||||
|
||||
```
|
||||
Usage:
|
||||
nuclei [flags]
|
||||
|
||||
Flags:
|
||||
-H, -header value Custom Header.
|
||||
-biid, -burp-collaborator-biid string Burp Collaborator BIID
|
||||
-bs, -bulk-size int Maximum Number of hosts analyzed in parallel per template (default 25)
|
||||
-c, -concurrency int Maximum Number of templates executed in parallel (default 10)
|
||||
-config string Nuclei configuration file
|
||||
-debug Debugging request and responses
|
||||
-debug-req Debugging request
|
||||
-debug-resp Debugging response
|
||||
-et, -exclude value Templates to exclude, supports single and multiple templates using directory.
|
||||
-etags, -exclude-tags value Exclude templates with the provided tags
|
||||
-headless Enable headless browser based templates support
|
||||
-impact, -severity value Templates to run based on severity, supports single and multiple severity.
|
||||
-irr, -include-rr Write requests/responses for matches in JSON output
|
||||
-interactions-cache-size int Number of requests to keep in interactions cache (default 5000)
|
||||
-interactions-cooldown-period int Extra time for interaction polling before exiting (default 5)
|
||||
-interactions-eviction int Number of seconds to wait before evicting requests from cache (default 60)
|
||||
-interactions-poll-duration int Number of seconds before each interaction poll request (default 5)
|
||||
-interactsh-url string Self Hosted Interactsh Server URL (default https://interact.sh)
|
||||
-json Write json output to files
|
||||
-l, -list string List of URLs to run templates on
|
||||
-me, -markdown-export string Directory to export results in markdown format
|
||||
-metrics Expose nuclei metrics on a port
|
||||
-metrics-port int Port to expose nuclei metrics on (default 9092)
|
||||
-nc, -no-color Disable colors in output
|
||||
-nt, -new-templates Only run newly added templates
|
||||
-nm, -no-meta Don't display metadata for the matches
|
||||
-no-interactsh Do not use interactsh server for blind interaction polling
|
||||
-o, -output string File to write output to (optional)
|
||||
-page-timeout int Seconds to wait for each page in headless (default 20)
|
||||
-passive Enable Passive HTTP response processing mode
|
||||
-project Use a project folder to avoid sending same request multiple times
|
||||
-project-path string Use a user defined project folder, temporary folder is used if not specified but enabled
|
||||
-proxy-socks-url string URL of the proxy socks server
|
||||
-proxy-url string URL of the proxy server
|
||||
-r, -resolvers string File containing resolver list for nuclei
|
||||
-rl, -rate-limit int Maximum requests to send per second (default 150)
|
||||
-rc, -report-config string Nuclei Reporting Module configuration file
|
||||
-rdb, -report-db string Local Nuclei Reporting Database (Always use this to persistent report data)
|
||||
-retries int Number of times to retry a failed request (default 1)
|
||||
-se, -sarif-export string File to export results in sarif format
|
||||
-show-browser Show the browser on the screen
|
||||
-si, -stats-interval int Number of seconds between each stats line (default 5)
|
||||
-silent Show only results in output
|
||||
-spm, -stop-at-first-path Stop processing http requests at first match (this may break template/workflow logic)
|
||||
-stats Display stats of the running scan
|
||||
-system-resolvers Use system dns resolving as error fallback
|
||||
-t, -templates value Templates to run, supports single and multiple templates using directory.
|
||||
-tags value Tags to execute templates for
|
||||
-u, -target string URL to scan with nuclei
|
||||
-tv, -templates-version Shows the installed nuclei-templates version
|
||||
-timeout int Time to wait in seconds before timeout (default 5)
|
||||
-tl List available templates
|
||||
-trace-log string File to write sent requests trace log
|
||||
-ud, -update-directory string Directory storing nuclei-templates (default /Users/geekboy/nuclei-templates)
|
||||
-ut, -update-templates Download / updates nuclei community templates
|
||||
-v, -verbose Show verbose output
|
||||
-version Show version of nuclei
|
||||
-w, -workflows value Workflows to run for nuclei
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Running Nuclei
|
||||
|
||||
Scanning for CVEs on given list of URLs.
|
||||
|
||||
@ -17,4 +17,4 @@ requests:
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "This is test-raw-unsafe request matcher."
|
||||
- "This is test raw-unsafe-matcher test"
|
||||
@ -2,10 +2,10 @@
|
||||
|
||||
cd ../v2/cmd/nuclei
|
||||
go build
|
||||
cp nuclei ../../../integration_tests/nuclei
|
||||
mv nuclei ../../../integration_tests/nuclei
|
||||
cd ../integration-test
|
||||
go build
|
||||
cp integration-test ../../../integration_tests/integration-test
|
||||
mv integration-test ../../../integration_tests/integration-test
|
||||
cd ../../../integration_tests
|
||||
./integration-test
|
||||
if [ $? -eq 0 ]
|
||||
|
||||
@ -3,23 +3,29 @@ before:
|
||||
- go mod tidy
|
||||
|
||||
builds:
|
||||
- binary: nuclei
|
||||
main: cmd/nuclei/main.go
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- 386
|
||||
- arm
|
||||
- arm64
|
||||
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- windows
|
||||
- linux
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- 386
|
||||
- arm
|
||||
- arm64
|
||||
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: '386'
|
||||
|
||||
binary: '{{ .ProjectName }}'
|
||||
main: cmd/nuclei/main.go
|
||||
|
||||
archives:
|
||||
- id: tgz
|
||||
format: tar.gz
|
||||
replacements:
|
||||
darwin: macOS
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
- format: zip
|
||||
replacements:
|
||||
darwin: macOS
|
||||
|
||||
checksum:
|
||||
algorithm: sha256
|
||||
@ -474,8 +474,7 @@ func (h *httpRawUnsafeRequest) Execute(filePath string) error {
|
||||
|
||||
ts := testutils.NewTCPServer(func(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
_, _ = conn.Write([]byte("HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 40\r\nContent-Type: text/plain; charset=utf-8\r\nDate: Thu, 25 Feb 2021 17:17:28 GMT\r\n\r\nThis is test-raw-unsafe request matcher.\r\n"))
|
||||
_, _ = conn.Write([]byte("HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 36\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nThis is test raw-unsafe-matcher test"))
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ func main() {
|
||||
gologger.Fatal().Msgf("Could not create runner: %s\n", err)
|
||||
}
|
||||
nucleiRunner.RunEnumeration()
|
||||
nucleiRunner.Close()
|
||||
}
|
||||
|
||||
func readConfig() {
|
||||
@ -72,7 +73,6 @@ based on templates offering massive extensibility and ease of use.`)
|
||||
set.BoolVarP(&options.NoMeta, "no-meta", "nm", false, "Don't display metadata for the matches")
|
||||
set.BoolVarP(&options.TemplatesVersion, "templates-version", "tv", false, "Shows the installed nuclei-templates version")
|
||||
set.BoolVar(&options.OfflineHTTP, "passive", false, "Enable Passive HTTP response processing mode")
|
||||
set.StringVarP(&options.BurpCollaboratorBiid, "burp-collaborator-biid", "biid", "", "Burp Collaborator BIID")
|
||||
set.StringVarP(&options.ReportingConfig, "report-config", "rc", "", "Nuclei Reporting Module configuration file")
|
||||
set.StringVarP(&options.ReportingDB, "report-db", "rdb", "", "Local Nuclei Reporting Database (Always use this to persistent report data)")
|
||||
set.StringSliceVar(&options.Tags, "tags", []string{}, "Tags to execute templates for")
|
||||
@ -84,9 +84,10 @@ based on templates offering massive extensibility and ease of use.`)
|
||||
set.BoolVar(&options.SystemResolvers, "system-resolvers", false, "Use system dns resolving as error fallback")
|
||||
set.IntVar(&options.PageTimeout, "page-timeout", 20, "Seconds to wait for each page in headless")
|
||||
set.BoolVarP(&options.NewTemplates, "new-templates", "nt", false, "Only run newly added templates")
|
||||
set.StringVarP(&options.DiskExportDirectory, "disk-export", "de", "", "Directory on disk to export reports in markdown to")
|
||||
set.StringVarP(&options.DiskExportDirectory, "markdown-export", "me", "", "Directory to export results in markdown format")
|
||||
set.StringVarP(&options.SarifExport, "sarif-export", "se", "", "File to export results in sarif format")
|
||||
set.BoolVar(&options.NoInteractsh, "no-interactsh", false, "Do not use interactsh server for blind interaction polling")
|
||||
set.StringVar(&options.InteractshURL, "interactsh-url", "https://interact.sh", "Interactsh Server URL")
|
||||
set.StringVar(&options.InteractshURL, "interactsh-url", "https://interact.sh", "Self Hosted Interactsh Server URL")
|
||||
set.IntVar(&options.InteractionsCacheSize, "interactions-cache-size", 5000, "Number of requests to keep in interactions cache")
|
||||
set.IntVar(&options.InteractionsEviction, "interactions-eviction", 60, "Number of seconds to wait before evicting requests from cache")
|
||||
set.IntVar(&options.InteractionsPollDuration, "interactions-poll-duration", 5, "Number of seconds before each interaction poll request")
|
||||
|
||||
@ -23,6 +23,7 @@ require (
|
||||
github.com/miekg/dns v1.1.38
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/owenrumney/go-sarif v1.0.4
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/projectdiscovery/clistats v0.0.8
|
||||
github.com/projectdiscovery/collaborator v0.0.2
|
||||
@ -33,7 +34,7 @@ require (
|
||||
github.com/projectdiscovery/interactsh v0.0.3
|
||||
github.com/projectdiscovery/rawhttp v0.0.7
|
||||
github.com/projectdiscovery/retryabledns v1.0.10
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.1
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.2-0.20210524224054-9fbe1f2b0727
|
||||
github.com/remeh/sizedwaitgroup v1.0.0
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rs/xid v1.2.1
|
||||
@ -49,11 +50,9 @@ require (
|
||||
go.uber.org/multierr v1.6.0
|
||||
go.uber.org/ratelimit v0.1.0
|
||||
golang.org/x/crypto v0.0.0-20210218145215-b8e89b74b9df // indirect
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
|
||||
golang.org/x/net v0.0.0-20210521195947-fe42d452be8f
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
|
||||
golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b // indirect
|
||||
golang.org/x/text v0.3.4 // indirect
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
|
||||
26
v2/go.sum
26
v2/go.sum
@ -40,6 +40,7 @@ github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0
|
||||
github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
|
||||
github.com/andygrunwald/go-jira v1.13.0 h1:vvIImGgX32bHfoiyUwkNo+/YrPnRczNarvhLOncP6dE=
|
||||
github.com/andygrunwald/go-jira v1.13.0/go.mod h1:jYi4kFDbRPZTJdJOVJO4mpMMIwdB+rcZwSO58DzPd2I=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@ -206,6 +207,8 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/owenrumney/go-sarif v1.0.4 h1:0LFC5eHP6amc/9ajM1jDiE52UfXFcl/oozay+X3KgV4=
|
||||
github.com/owenrumney/go-sarif v1.0.4/go.mod h1:DXUGbHwQcCMvqcvZbxh8l/7diHsJVztOKZgmPt88RNI=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@ -226,17 +229,14 @@ github.com/projectdiscovery/hmap v0.0.1 h1:VAONbJw5jP+syI5smhsfkrq9XPGn4aiYy5pR6
|
||||
github.com/projectdiscovery/hmap v0.0.1/go.mod h1:VDEfgzkKQdq7iGTKz8Ooul0NuYHQ8qiDs6r8bPD1Sb0=
|
||||
github.com/projectdiscovery/interactsh v0.0.3 h1:PUkWk+NzSyd5glMqfORmuqizhsd7c3WdTYBOto/MQIU=
|
||||
github.com/projectdiscovery/interactsh v0.0.3/go.mod h1:dWnKO14d2FLP3kLhI9DecEsiAC/aZiJoUBGFjGhDskY=
|
||||
github.com/projectdiscovery/rawhttp v0.0.6 h1:HbgPB1eKXQVV5F9sq0Uxflm95spWFyZYD8dgFpeOC9M=
|
||||
github.com/projectdiscovery/rawhttp v0.0.6/go.mod h1:PQERZAhAv7yxI/hR6hdDPgK1WTU56l204BweXrBec+0=
|
||||
github.com/projectdiscovery/rawhttp v0.0.7-0.20210603051339-3582c0c9ea3e h1:it83hXLxlmjf9/nwwVt+yMN1wwOvWMFITXVFVvxYYqs=
|
||||
github.com/projectdiscovery/rawhttp v0.0.7-0.20210603051339-3582c0c9ea3e/go.mod h1:PQERZAhAv7yxI/hR6hdDPgK1WTU56l204BweXrBec+0=
|
||||
github.com/projectdiscovery/rawhttp v0.0.7 h1:5m4peVgjbl7gqDcRYMTVEuX+Xs/nh76ohTkkvufucLg=
|
||||
github.com/projectdiscovery/rawhttp v0.0.7/go.mod h1:PQERZAhAv7yxI/hR6hdDPgK1WTU56l204BweXrBec+0=
|
||||
github.com/projectdiscovery/retryabledns v1.0.7/go.mod h1:/UzJn4I+cPdQl6pKiiQfvVAT636YZvJQYZhYhGB0dUQ=
|
||||
github.com/projectdiscovery/retryabledns v1.0.10 h1:xJZ2aKoqrNg/OZEw1+4+QIOH40V/WkZDYY1ZZc+uphE=
|
||||
github.com/projectdiscovery/retryabledns v1.0.10/go.mod h1:4sMC8HZyF01HXukRleSQYwz4870bwgb4+hTSXTMrkf4=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.1 h1:V7wUvsZNq1Rcz7+IlcyoyQlNwshuwptuBVYWw9lx8RE=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.1/go.mod h1:SrN6iLZilNG1X4neq1D+SBxoqfAF4nyzvmevkTkWsek=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.2-0.20210524224054-9fbe1f2b0727 h1:CJHP3CLCc/eqdXQEvZy8KiiqtAk9kEsd1URtPyPAQ1s=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.2-0.20210524224054-9fbe1f2b0727/go.mod h1:dx//aY9V247qHdsRf0vdWHTBZuBQ2vm6Dq5dagxrDYI=
|
||||
github.com/prologic/smtpd v0.0.0-20210126001904-0893ad18168e h1:ZT3wZ92sp/EHEE/HcFCWCsYS3ROLjHb6EqSX8qYrgXw=
|
||||
github.com/prologic/smtpd v0.0.0-20210126001904-0893ad18168e/go.mod h1:GkLsdH1RZj6RDKeI9A05NGZYmEZQ/PbQcZPnZoSZuYI=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
@ -270,6 +270,8 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
||||
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ=
|
||||
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=
|
||||
github.com/xanzy/go-gitlab v0.44.0 h1:cEiGhqu7EpFGuei2a2etAwB+x6403E5CvpLn35y+GPs=
|
||||
@ -287,6 +289,8 @@ github.com/ysmood/leakless v0.6.12/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNq
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zclconf/go-cty v1.8.2 h1:u+xZfBKgpycDnTNjPhGiTEYZS5qS/Sb5MqSfm7vzcjg=
|
||||
github.com/zclconf/go-cty v1.8.2/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@ -368,8 +372,9 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210521195947-fe42d452be8f h1:Si4U+UcgJzya9kpiEUJKQvjr512OLli+gL4poHrz93U=
|
||||
golang.org/x/net v0.0.0-20210521195947-fe42d452be8f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -420,8 +425,8 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201113233024-12cec1faf1ba/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b h1:lAZ0/chPUDWwjqosYR0X4M490zQhMsiJ4K3DbA7o+3g=
|
||||
golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -429,8 +434,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@ -1,72 +0,0 @@
|
||||
package collaborator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/projectdiscovery/collaborator"
|
||||
)
|
||||
|
||||
var (
|
||||
// PollSeconds is the seconds to poll at.
|
||||
PollSeconds = 5
|
||||
// DefaultMaxBufferLimit is the default request buffer limit
|
||||
DefaultMaxBufferLimit = 150
|
||||
// DefaultPollInterval is the default poll interval for burp collabortor polling.
|
||||
DefaultPollInterval time.Duration = time.Second * time.Duration(PollSeconds)
|
||||
// DefaultCollaborator is the default burp collaborator instance
|
||||
DefaultCollaborator = &Collaborator{Collab: collaborator.NewBurpCollaborator()}
|
||||
)
|
||||
|
||||
// Collaborator is a client for recording burp collaborator interactions
|
||||
type Collaborator struct {
|
||||
sync.RWMutex
|
||||
options *Options // unused
|
||||
Collab *collaborator.BurpCollaborator
|
||||
}
|
||||
|
||||
// Options contains configuration options for collaborator client
|
||||
type Options struct {
|
||||
BIID string
|
||||
PollInterval time.Duration
|
||||
MaxBufferLimit int
|
||||
}
|
||||
|
||||
// New creates a new collaborator client
|
||||
func New(options *Options) *Collaborator {
|
||||
collab := collaborator.NewBurpCollaborator()
|
||||
collab.AddBIID(options.BIID)
|
||||
collab.MaxBufferLimit = options.MaxBufferLimit
|
||||
return &Collaborator{Collab: collab, options: options}
|
||||
}
|
||||
|
||||
// Poll initiates collaborator polling if any BIIDs were provided
|
||||
func (b *Collaborator) Poll() {
|
||||
// if no valid biids were provided just return
|
||||
if len(b.Collab.BIIDs) > 0 {
|
||||
go b.Collab.PollEach(DefaultPollInterval)
|
||||
}
|
||||
}
|
||||
|
||||
// Has checks if a collabrator hit was found for a URL
|
||||
func (b *Collaborator) Has(s string) bool {
|
||||
for _, r := range b.Collab.RespBuffer {
|
||||
for i := 0; i < len(r.Responses); i++ {
|
||||
// search in dns - http - smtp
|
||||
b.RLock()
|
||||
found := strings.Contains(r.Responses[i].Data.RawRequestDecoded, s) ||
|
||||
strings.Contains(r.Responses[i].Data.RequestDecoded, s) ||
|
||||
strings.Contains(r.Responses[i].Data.MessageDecoded, s)
|
||||
b.RUnlock()
|
||||
|
||||
if found {
|
||||
b.Lock()
|
||||
r.Responses = append(r.Responses[:i], r.Responses[i+1:]...)
|
||||
b.Unlock()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -7,11 +7,11 @@ const banner = `
|
||||
____ __ _______/ /__ (_)
|
||||
/ __ \/ / / / ___/ / _ \/ /
|
||||
/ / / / /_/ / /__/ / __/ /
|
||||
/_/ /_/\__,_/\___/_/\___/_/ v2.3.7
|
||||
/_/ /_/\__,_/\___/_/\___/_/ v2.3.8
|
||||
`
|
||||
|
||||
// Version is the current version of nuclei
|
||||
const Version = `2.3.7`
|
||||
const Version = `2.3.8`
|
||||
|
||||
// showBanner is used to show the banner to the user
|
||||
func showBanner() {
|
||||
|
||||
@ -11,7 +11,6 @@ import (
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/hmap/store/hybrid"
|
||||
"github.com/projectdiscovery/nuclei/v2/internal/collaborator"
|
||||
"github.com/projectdiscovery/nuclei/v2/internal/colorizer"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||
@ -24,6 +23,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/headless/engine"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/disk"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/sarif"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
"github.com/remeh/sizedwaitgroup"
|
||||
@ -66,13 +66,13 @@ func New(options *types.Options) (*Runner, error) {
|
||||
if err := runner.updateTemplates(); err != nil {
|
||||
gologger.Warning().Msgf("Could not update templates: %s\n", err)
|
||||
}
|
||||
|
||||
runner.catalog = catalog.New(runner.options.TemplatesDirectory)
|
||||
// Read nucleiignore file if given a templateconfig
|
||||
if runner.templatesConfig != nil {
|
||||
runner.readNucleiIgnoreFile()
|
||||
runner.catalog.AppendIgnore(runner.templatesConfig.IgnorePaths)
|
||||
}
|
||||
runner.catalog = catalog.New(runner.options.TemplatesDirectory)
|
||||
runner.catalog.AppendIgnore(runner.templatesConfig.IgnorePaths)
|
||||
|
||||
var reportingOptions *reporting.Options
|
||||
if options.ReportingConfig != "" {
|
||||
file, err := os.Open(options.ReportingConfig)
|
||||
@ -95,6 +95,14 @@ func New(options *types.Options) (*Runner, error) {
|
||||
reportingOptions.DiskExporter = &disk.Options{Directory: options.DiskExportDirectory}
|
||||
}
|
||||
}
|
||||
if options.SarifExport != "" {
|
||||
if reportingOptions != nil {
|
||||
reportingOptions.SarifExporter = &sarif.Options{File: options.SarifExport}
|
||||
} else {
|
||||
reportingOptions = &reporting.Options{}
|
||||
reportingOptions.SarifExporter = &sarif.Options{File: options.SarifExport}
|
||||
}
|
||||
}
|
||||
if reportingOptions != nil {
|
||||
if client, err := reporting.New(reportingOptions, options.ReportingDB); err != nil {
|
||||
gologger.Fatal().Msgf("Could not create issue reporting client: %s\n", err)
|
||||
@ -218,11 +226,6 @@ func New(options *types.Options) (*Runner, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Enable Polling
|
||||
if options.BurpCollaboratorBiid != "" {
|
||||
collaborator.DefaultCollaborator.Collab.AddBIID(options.BurpCollaboratorBiid)
|
||||
}
|
||||
|
||||
if options.RateLimit > 0 {
|
||||
runner.ratelimiter = ratelimit.New(options.RateLimit)
|
||||
} else {
|
||||
@ -288,7 +291,7 @@ func (r *Runner) RunEnumeration() {
|
||||
availableTemplates, _ := r.getParsedTemplatesFor(allTemplates, r.options.Severity, false)
|
||||
availableWorkflows, workflowCount := r.getParsedTemplatesFor(workflowPaths, r.options.Severity, true)
|
||||
|
||||
var unclusteredRequests int64 = 0
|
||||
var unclusteredRequests int64
|
||||
for _, template := range availableTemplates {
|
||||
// workflows will dynamically adjust the totals while running, as
|
||||
// it can't be know in advance which requests will be called
|
||||
@ -331,7 +334,7 @@ func (r *Runner) RunEnumeration() {
|
||||
finalTemplates = append(finalTemplates, workflows)
|
||||
}
|
||||
|
||||
var totalRequests int64 = 0
|
||||
var totalRequests int64
|
||||
for _, t := range finalTemplates {
|
||||
if len(t.Workflows) > 0 {
|
||||
continue
|
||||
@ -355,8 +358,6 @@ func (r *Runner) RunEnumeration() {
|
||||
|
||||
results := &atomic.Bool{}
|
||||
wgtemplates := sizedwaitgroup.New(r.options.TemplateThreads)
|
||||
// Starts polling or ignore
|
||||
collaborator.DefaultCollaborator.Poll()
|
||||
|
||||
// tracks global progress and captures stdout/stderr until p.Wait finishes
|
||||
r.progress.Init(r.inputCount, templateCount, totalRequests)
|
||||
@ -387,13 +388,8 @@ func (r *Runner) RunEnumeration() {
|
||||
r.issuesClient.Close()
|
||||
}
|
||||
if !results.Load() {
|
||||
if r.output != nil {
|
||||
r.output.Close()
|
||||
os.Remove(r.options.Output)
|
||||
}
|
||||
gologger.Info().Msgf("No results found. Better luck next time!")
|
||||
}
|
||||
|
||||
if r.browser != nil {
|
||||
r.browser.Close()
|
||||
}
|
||||
|
||||
@ -19,43 +19,42 @@ func Init(options *types.Options) {
|
||||
|
||||
// DefaultOptions is the default options structure for nuclei during mocking.
|
||||
var DefaultOptions = &types.Options{
|
||||
Metrics: false,
|
||||
Debug: false,
|
||||
DebugRequests: false,
|
||||
DebugResponse: false,
|
||||
Silent: false,
|
||||
Version: false,
|
||||
Verbose: false,
|
||||
NoColor: true,
|
||||
UpdateTemplates: false,
|
||||
JSON: false,
|
||||
JSONRequests: false,
|
||||
EnableProgressBar: false,
|
||||
TemplatesVersion: false,
|
||||
TemplateList: false,
|
||||
Stdin: false,
|
||||
StopAtFirstMatch: false,
|
||||
NoMeta: false,
|
||||
Project: false,
|
||||
MetricsPort: 0,
|
||||
BulkSize: 25,
|
||||
TemplateThreads: 10,
|
||||
Timeout: 5,
|
||||
Retries: 1,
|
||||
RateLimit: 150,
|
||||
BurpCollaboratorBiid: "",
|
||||
ProjectPath: "",
|
||||
Severity: []string{},
|
||||
Target: "",
|
||||
Targets: "",
|
||||
Output: "",
|
||||
ProxyURL: "",
|
||||
ProxySocksURL: "",
|
||||
TemplatesDirectory: "",
|
||||
TraceLogFile: "",
|
||||
Templates: []string{},
|
||||
ExcludedTemplates: []string{},
|
||||
CustomHeaders: []string{},
|
||||
Metrics: false,
|
||||
Debug: false,
|
||||
DebugRequests: false,
|
||||
DebugResponse: false,
|
||||
Silent: false,
|
||||
Version: false,
|
||||
Verbose: false,
|
||||
NoColor: true,
|
||||
UpdateTemplates: false,
|
||||
JSON: false,
|
||||
JSONRequests: false,
|
||||
EnableProgressBar: false,
|
||||
TemplatesVersion: false,
|
||||
TemplateList: false,
|
||||
Stdin: false,
|
||||
StopAtFirstMatch: false,
|
||||
NoMeta: false,
|
||||
Project: false,
|
||||
MetricsPort: 0,
|
||||
BulkSize: 25,
|
||||
TemplateThreads: 10,
|
||||
Timeout: 5,
|
||||
Retries: 1,
|
||||
RateLimit: 150,
|
||||
ProjectPath: "",
|
||||
Severity: []string{},
|
||||
Target: "",
|
||||
Targets: "",
|
||||
Output: "",
|
||||
ProxyURL: "",
|
||||
ProxySocksURL: "",
|
||||
TemplatesDirectory: "",
|
||||
TraceLogFile: "",
|
||||
Templates: []string{},
|
||||
ExcludedTemplates: []string{},
|
||||
CustomHeaders: []string{},
|
||||
}
|
||||
|
||||
// MockOutputWriter is a mocked output writer.
|
||||
|
||||
@ -17,7 +17,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Knetic/govaluate"
|
||||
"github.com/projectdiscovery/nuclei/v2/internal/collaborator"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
"github.com/spaolacci/murmur3"
|
||||
)
|
||||
@ -269,12 +268,6 @@ func HelperFunctions() map[string]govaluate.ExpressionFunction {
|
||||
time.Sleep(time.Duration(seconds) * time.Second)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Collaborator
|
||||
functions["collab"] = func(args ...interface{}) (interface{}, error) {
|
||||
// check if collaborator contains a specific pattern
|
||||
return collaborator.DefaultCollaborator.Has(types.ToString(args[0])), nil
|
||||
}
|
||||
return functions
|
||||
}
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ func (w *StandardWriter) formatScreen(output *ResultEvent) []byte {
|
||||
if len(output.Metadata) > 0 {
|
||||
builder.WriteString(" [")
|
||||
|
||||
var first bool = true
|
||||
var first bool
|
||||
for name, value := range output.Metadata {
|
||||
if !first {
|
||||
builder.WriteRune(',')
|
||||
|
||||
@ -54,6 +54,8 @@ type InternalWrappedEvent struct {
|
||||
type ResultEvent struct {
|
||||
// TemplateID is the ID of the template for the result.
|
||||
TemplateID string `json:"templateID"`
|
||||
// TemplatePath is the path of template
|
||||
TemplatePath string `json:"-"`
|
||||
// Info contains information block of the template for the result.
|
||||
Info map[string]interface{} `json:"info,inline"`
|
||||
// MatcherName is the name of the matcher matched if any.
|
||||
@ -82,6 +84,8 @@ type ResultEvent struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
// Interaction is the full details of interactsh interaction.
|
||||
Interaction *server.Interaction `json:"interaction,omitempty"`
|
||||
|
||||
FileToIndexPosition map[string]int `json:"-"`
|
||||
}
|
||||
|
||||
// NewStandardWriter creates a new output writer based on user configurations
|
||||
|
||||
@ -22,6 +22,7 @@ type Executer struct {
|
||||
|
||||
type clusteredOperator struct {
|
||||
templateID string
|
||||
templatePath string
|
||||
templateInfo map[string]interface{}
|
||||
operator *operators.Operators
|
||||
}
|
||||
@ -38,6 +39,7 @@ func NewExecuter(requests []*templates.Template, options *protocols.ExecuterOpti
|
||||
executer.operators = append(executer.operators, &clusteredOperator{
|
||||
templateID: req.ID,
|
||||
templateInfo: req.Info,
|
||||
templatePath: req.Path,
|
||||
operator: req.RequestsHTTP[0].CompiledOperators,
|
||||
})
|
||||
}
|
||||
@ -68,6 +70,7 @@ func (e *Executer) Execute(input string) (bool, error) {
|
||||
if matched && result != nil {
|
||||
event.OperatorsResult = result
|
||||
event.InternalEvent["template-id"] = operator.templateID
|
||||
event.InternalEvent["template-path"] = operator.templatePath
|
||||
event.InternalEvent["template-info"] = operator.templateInfo
|
||||
event.Results = e.requests.MakeResultEvent(event)
|
||||
results = true
|
||||
@ -95,6 +98,7 @@ func (e *Executer) ExecuteWithResults(input string, callback protocols.OutputEve
|
||||
if matched && result != nil {
|
||||
event.OperatorsResult = result
|
||||
event.InternalEvent["template-id"] = operator.templateID
|
||||
event.InternalEvent["template-path"] = operator.templatePath
|
||||
event.InternalEvent["template-info"] = operator.templateInfo
|
||||
event.Results = e.requests.MakeResultEvent(event)
|
||||
callback(event)
|
||||
|
||||
@ -3,6 +3,7 @@ package interactsh
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/karlseguin/ccache"
|
||||
@ -19,6 +20,7 @@ import (
|
||||
|
||||
// Client is a wrapped client for interactsh server.
|
||||
type Client struct {
|
||||
dotHostname string
|
||||
// interactsh is a client for interactsh server.
|
||||
interactsh *client.Client
|
||||
// requests is a stored cache for interactsh-url->request-event data.
|
||||
@ -27,11 +29,12 @@ type Client struct {
|
||||
interactions *ccache.Cache
|
||||
|
||||
options *Options
|
||||
matched bool
|
||||
dotHostname string
|
||||
eviction time.Duration
|
||||
pollDuration time.Duration
|
||||
cooldownDuration time.Duration
|
||||
|
||||
generated uint32 // decide to wait if we have a generated url
|
||||
matched bool
|
||||
}
|
||||
|
||||
var (
|
||||
@ -157,12 +160,13 @@ func (c *Client) processInteractionForRequest(interaction *server.Interaction, d
|
||||
|
||||
// URL returns a new URL that can be interacted with
|
||||
func (c *Client) URL() string {
|
||||
atomic.CompareAndSwapUint32(&c.generated, 0, 1)
|
||||
return c.interactsh.URL()
|
||||
}
|
||||
|
||||
// Close closes the interactsh clients after waiting for cooldown period.
|
||||
func (c *Client) Close() bool {
|
||||
if c.cooldownDuration > 0 {
|
||||
if c.cooldownDuration > 0 && atomic.LoadUint32(&c.generated) == 1 {
|
||||
time.Sleep(c.cooldownDuration)
|
||||
}
|
||||
c.interactsh.StopPolling()
|
||||
|
||||
@ -22,10 +22,7 @@ func Init(options *types.Options) error {
|
||||
if err := httpclientpool.Init(options); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := networkclientpool.Init(options); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return networkclientpool.Init(options)
|
||||
}
|
||||
|
||||
var userAgents = []string{
|
||||
|
||||
@ -6,8 +6,10 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
)
|
||||
|
||||
// Dialer is a shared fastdialer instance for host DNS resolution
|
||||
var Dialer *fastdialer.Dialer
|
||||
|
||||
// Init creates the Dialer instance based on user configuration
|
||||
func Init(options *types.Options) error {
|
||||
opts := fastdialer.DefaultOptions
|
||||
if options.SystemResolvers {
|
||||
@ -24,6 +26,7 @@ func Init(options *types.Options) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the global shared fastdialer
|
||||
func Close() {
|
||||
if Dialer != nil {
|
||||
Dialer.Close()
|
||||
|
||||
@ -103,6 +103,7 @@ func (r *Request) responseToDSLMap(req, resp *dns.Msg, host, matched string) out
|
||||
data["raw"] = rawData
|
||||
data["template-id"] = r.options.TemplateID
|
||||
data["template-info"] = r.options.TemplateInfo
|
||||
data["template-path"] = r.options.TemplatePath
|
||||
return data
|
||||
}
|
||||
|
||||
@ -137,6 +138,7 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
|
||||
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
||||
data := &output.ResultEvent{
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
|
||||
Type: "dns",
|
||||
Host: types.ToString(wrapped.InternalEvent["host"]),
|
||||
|
||||
@ -42,7 +42,7 @@ func TestResponseToDSLMap(t *testing.T) {
|
||||
resp.Answer = append(resp.Answer, &dns.A{A: net.ParseIP("1.1.1.1"), Hdr: dns.RR_Header{Name: "one.one.one.one."}})
|
||||
|
||||
event := request.responseToDSLMap(req, resp, "one.one.one.one", "one.one.one.one")
|
||||
require.Len(t, event, 11, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 12, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, dns.RcodeSuccess, event["rcode"], "could not get correct rcode")
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/operators/extractors"
|
||||
@ -71,6 +73,7 @@ func (r *Request) responseToDSLMap(raw, host, matched string) output.InternalEve
|
||||
data["raw"] = raw
|
||||
data["template-id"] = r.options.TemplateID
|
||||
data["template-info"] = r.options.TemplateInfo
|
||||
data["template-path"] = r.options.TemplatePath
|
||||
return data
|
||||
}
|
||||
|
||||
@ -99,16 +102,45 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
|
||||
data := r.makeResultEventItem(wrapped)
|
||||
results = append(results, data)
|
||||
}
|
||||
raw, ok := wrapped.InternalEvent["raw"]
|
||||
if !ok {
|
||||
return results
|
||||
}
|
||||
rawStr, ok := raw.(string)
|
||||
if !ok {
|
||||
return results
|
||||
}
|
||||
|
||||
// Identify the position of match in file using a dirty hack.
|
||||
for _, result := range results {
|
||||
for _, extraction := range result.ExtractedResults {
|
||||
scanner := bufio.NewScanner(strings.NewReader(rawStr))
|
||||
|
||||
line := 1
|
||||
for scanner.Scan() {
|
||||
if strings.Contains(scanner.Text(), extraction) {
|
||||
if result.FileToIndexPosition == nil {
|
||||
result.FileToIndexPosition = make(map[string]int)
|
||||
}
|
||||
result.FileToIndexPosition[result.Matched] = line
|
||||
continue
|
||||
}
|
||||
line++
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
||||
data := &output.ResultEvent{
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
|
||||
Type: "file",
|
||||
Path: types.ToString(wrapped.InternalEvent["path"]),
|
||||
Matched: types.ToString(wrapped.InternalEvent["matched"]),
|
||||
Host: types.ToString(wrapped.InternalEvent["matched"]),
|
||||
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ func TestResponseToDSLMap(t *testing.T) {
|
||||
|
||||
resp := "test-data\r\n"
|
||||
event := request.responseToDSLMap(resp, "one.one.one.one", "one.one.one.one")
|
||||
require.Len(t, event, 5, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 6, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, resp, event["raw"], "could not get correct resp")
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ func TestFileOperatorMatch(t *testing.T) {
|
||||
|
||||
resp := "test-data\r\n1.1.1.1\r\n"
|
||||
event := request.responseToDSLMap(resp, "one.one.one.one", "one.one.one.one")
|
||||
require.Len(t, event, 5, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 6, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, resp, event["raw"], "could not get correct resp")
|
||||
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
@ -122,7 +122,7 @@ func TestFileOperatorExtract(t *testing.T) {
|
||||
|
||||
resp := "test-data\r\n1.1.1.1\r\n"
|
||||
event := request.responseToDSLMap(resp, "one.one.one.one", "one.one.one.one")
|
||||
require.Len(t, event, 5, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 6, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, resp, event["raw"], "could not get correct resp")
|
||||
|
||||
t.Run("extract", func(t *testing.T) {
|
||||
@ -187,7 +187,7 @@ func TestFileMakeResult(t *testing.T) {
|
||||
|
||||
resp := "test-data\r\n1.1.1.1\r\n"
|
||||
event := request.responseToDSLMap(resp, "one.one.one.one", "one.one.one.one")
|
||||
require.Len(t, event, 5, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 6, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, resp, event["raw"], "could not get correct resp")
|
||||
|
||||
finalEvent := &output.InternalWrappedEvent{InternalEvent: event}
|
||||
|
||||
@ -99,6 +99,7 @@ func (b *Browser) Close() {
|
||||
// headless process launch.
|
||||
func (b *Browser) killChromeProcesses() {
|
||||
newProcesses := b.findChromeProcesses()
|
||||
|
||||
for id := range newProcesses {
|
||||
if _, ok := b.previouspids[id]; ok {
|
||||
continue
|
||||
|
||||
@ -49,7 +49,6 @@ func (i *Instance) Run(baseURL *url.URL, actions []*Action, timeout time.Duratio
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
go router.Run()
|
||||
data, err := createdPage.ExecuteActions(baseURL, actions)
|
||||
if err != nil {
|
||||
|
||||
@ -23,6 +23,7 @@ func TestActionNavigate(t *testing.T) {
|
||||
|
||||
instance, err := browser.NewInstance()
|
||||
require.Nil(t, err, "could not create browser instance")
|
||||
defer instance.Close()
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, `
|
||||
|
||||
@ -72,6 +72,7 @@ func (r *Request) responseToDSLMap(resp, req, host, matched string) output.Inter
|
||||
data["data"] = resp
|
||||
data["template-id"] = r.options.TemplateID
|
||||
data["template-info"] = r.options.TemplateInfo
|
||||
data["template-path"] = r.options.TemplatePath
|
||||
return data
|
||||
}
|
||||
|
||||
@ -106,6 +107,7 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
|
||||
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
||||
data := &output.ResultEvent{
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
|
||||
Type: "headless",
|
||||
Host: types.ToString(wrapped.InternalEvent["host"]),
|
||||
|
||||
@ -24,7 +24,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
Dialer *fastdialer.Dialer
|
||||
// Dialer is a copy of the fatdialer from protocolstate
|
||||
Dialer *fastdialer.Dialer
|
||||
|
||||
rawhttpClient *rawhttp.Client
|
||||
poolMutex *sync.RWMutex
|
||||
normalClient *retryablehttp.Client
|
||||
|
||||
@ -105,6 +105,7 @@ func (r *Request) responseToDSLMap(resp *http.Response, host, matched, rawReq, r
|
||||
data["duration"] = duration.Seconds()
|
||||
data["template-id"] = r.options.TemplateID
|
||||
data["template-info"] = r.options.TemplateInfo
|
||||
data["template-path"] = r.options.TemplatePath
|
||||
return data
|
||||
}
|
||||
|
||||
@ -139,6 +140,7 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
|
||||
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
||||
data := &output.ResultEvent{
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
|
||||
Type: "http",
|
||||
Host: types.ToString(wrapped.InternalEvent["host"]),
|
||||
|
||||
@ -38,7 +38,7 @@ func TestResponseToDSLMap(t *testing.T) {
|
||||
matched := "http://example.com/test/?test=1"
|
||||
|
||||
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
|
||||
require.Len(t, event, 12, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 13, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
|
||||
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")
|
||||
}
|
||||
@ -68,7 +68,7 @@ func TestHTTPOperatorMatch(t *testing.T) {
|
||||
matched := "http://example.com/test/?test=1"
|
||||
|
||||
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
|
||||
require.Len(t, event, 12, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 13, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
|
||||
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")
|
||||
|
||||
@ -138,7 +138,7 @@ func TestHTTPOperatorExtract(t *testing.T) {
|
||||
matched := "http://example.com/test/?test=1"
|
||||
|
||||
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
|
||||
require.Len(t, event, 12, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 13, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
|
||||
require.Equal(t, "Test-Response", event["test_header"], "could not get correct resp for header")
|
||||
|
||||
@ -208,7 +208,7 @@ func TestHTTPMakeResult(t *testing.T) {
|
||||
matched := "http://example.com/test/?test=1"
|
||||
|
||||
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
|
||||
require.Len(t, event, 12, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 13, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
|
||||
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")
|
||||
|
||||
|
||||
@ -347,7 +347,9 @@ func (r *Request) executeRequest(reqURL string, request *generatedRequest, previ
|
||||
}
|
||||
data, err := ioutil.ReadAll(bodyReader)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not read http body")
|
||||
if !strings.Contains(err.Error(), "unexpected EOF") { // ignore EOF error
|
||||
return errors.Wrap(err, "could not read http body")
|
||||
}
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
|
||||
@ -3,6 +3,8 @@ package http
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"compress/zlib"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
@ -100,19 +102,23 @@ func handleDecompression(resp *http.Response, bodyOrig []byte) (bodyDec []byte,
|
||||
return bodyOrig, nil
|
||||
}
|
||||
|
||||
encodingHeader := strings.TrimSpace(strings.ToLower(resp.Header.Get("Content-Encoding")))
|
||||
if strings.Contains(encodingHeader, "gzip") {
|
||||
gzipreader, err := gzip.NewReader(bytes.NewReader(bodyOrig))
|
||||
if err != nil {
|
||||
return bodyOrig, err
|
||||
}
|
||||
defer gzipreader.Close()
|
||||
|
||||
bodyDec, err = ioutil.ReadAll(gzipreader)
|
||||
if err != nil {
|
||||
return bodyOrig, err
|
||||
}
|
||||
return bodyDec, nil
|
||||
var reader io.ReadCloser
|
||||
switch resp.Header.Get("Content-Encoding") {
|
||||
case "gzip":
|
||||
reader, err = gzip.NewReader(bytes.NewReader(bodyOrig))
|
||||
case "deflate":
|
||||
reader, err = zlib.NewReader(bytes.NewReader(bodyOrig))
|
||||
default:
|
||||
return bodyOrig, nil
|
||||
}
|
||||
return bodyOrig, nil
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
bodyDec, err = ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return bodyOrig, err
|
||||
}
|
||||
return bodyDec, nil
|
||||
}
|
||||
|
||||
@ -73,6 +73,7 @@ func (r *Request) responseToDSLMap(req, resp, raw, host, matched string) output.
|
||||
data["raw"] = raw // Raw is the full transaction data for network
|
||||
data["template-id"] = r.options.TemplateID
|
||||
data["template-info"] = r.options.TemplateInfo
|
||||
data["template-path"] = r.options.TemplatePath
|
||||
return data
|
||||
}
|
||||
|
||||
@ -107,6 +108,7 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
|
||||
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
||||
data := &output.ResultEvent{
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
|
||||
Type: "network",
|
||||
Host: types.ToString(wrapped.InternalEvent["host"]),
|
||||
|
||||
@ -32,7 +32,7 @@ func TestResponseToDSLMap(t *testing.T) {
|
||||
req := "test-data\r\n"
|
||||
resp := "resp-data\r\n"
|
||||
event := request.responseToDSLMap(req, resp, "test", "one.one.one.one", "one.one.one.one")
|
||||
require.Len(t, event, 7, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 8, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, resp, event["data"], "could not get correct resp")
|
||||
}
|
||||
|
||||
|
||||
@ -127,8 +127,9 @@ func (r *Request) executeAddress(actualAddress, address, input string, shouldUse
|
||||
r.options.Progress.IncrementRequests()
|
||||
|
||||
if r.options.Options.Debug || r.options.Options.DebugRequests {
|
||||
requestOutput := reqBuilder.String()
|
||||
gologger.Info().Str("address", actualAddress).Msgf("[%s] Dumped Network request for %s", r.options.TemplateID, actualAddress)
|
||||
gologger.Print().Msgf("%s", reqBuilder.String())
|
||||
gologger.Print().Msgf("%s\nHex: %s", requestOutput, hex.EncodeToString([]byte(requestOutput)))
|
||||
}
|
||||
|
||||
r.options.Output.Request(r.options.TemplateID, actualAddress, "network", err)
|
||||
@ -147,8 +148,9 @@ func (r *Request) executeAddress(actualAddress, address, input string, shouldUse
|
||||
responseBuilder.Write(final[:n])
|
||||
|
||||
if r.options.Options.Debug || r.options.Options.DebugResponse {
|
||||
responseOutput := responseBuilder.String()
|
||||
gologger.Debug().Msgf("[%s] Dumped Network response for %s", r.options.TemplateID, actualAddress)
|
||||
gologger.Print().Msgf("%s", responseBuilder.String())
|
||||
gologger.Print().Msgf("%s\nHex: %s", responseOutput, hex.EncodeToString([]byte(responseOutput)))
|
||||
}
|
||||
outputEvent := r.responseToDSLMap(reqBuilder.String(), string(final[:n]), responseBuilder.String(), input, actualAddress)
|
||||
outputEvent["ip"] = r.dialer.GetDialedIP(hostname)
|
||||
|
||||
@ -101,6 +101,7 @@ func (r *Request) responseToDSLMap(resp *http.Response, host, matched, rawReq, r
|
||||
data["duration"] = duration.Seconds()
|
||||
data["template-id"] = r.options.TemplateID
|
||||
data["template-info"] = r.options.TemplateInfo
|
||||
data["template-path"] = r.options.TemplatePath
|
||||
return data
|
||||
}
|
||||
|
||||
@ -135,6 +136,7 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
|
||||
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
|
||||
data := &output.ResultEvent{
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
|
||||
Type: "http",
|
||||
Path: types.ToString(wrapped.InternalEvent["path"]),
|
||||
|
||||
@ -34,7 +34,7 @@ func TestResponseToDSLMap(t *testing.T) {
|
||||
matched := "http://example.com/test/?test=1"
|
||||
|
||||
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
|
||||
require.Len(t, event, 12, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 13, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
|
||||
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")
|
||||
}
|
||||
@ -60,7 +60,7 @@ func TestHTTPOperatorMatch(t *testing.T) {
|
||||
matched := "http://example.com/test/?test=1"
|
||||
|
||||
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
|
||||
require.Len(t, event, 12, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 13, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
|
||||
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")
|
||||
|
||||
@ -126,7 +126,7 @@ func TestHTTPOperatorExtract(t *testing.T) {
|
||||
matched := "http://example.com/test/?test=1"
|
||||
|
||||
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
|
||||
require.Len(t, event, 12, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 13, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
|
||||
require.Equal(t, "Test-Response", event["test-header"], "could not get correct resp for header")
|
||||
|
||||
@ -191,7 +191,7 @@ func TestHTTPMakeResult(t *testing.T) {
|
||||
matched := "http://example.com/test/?test=1"
|
||||
|
||||
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
|
||||
require.Len(t, event, 12, "could not get correct number of items in dsl map")
|
||||
require.Len(t, event, 13, "could not get correct number of items in dsl map")
|
||||
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
|
||||
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")
|
||||
|
||||
|
||||
@ -58,3 +58,8 @@ func (i *Exporter) Export(event *output.ResultEvent) error {
|
||||
err := ioutil.WriteFile(path.Join(i.directory, finalFilename), data, 0644)
|
||||
return err
|
||||
}
|
||||
|
||||
// Close closes the exporter after operation
|
||||
func (i *Exporter) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
143
v2/pkg/reporting/exporters/sarif/sarif.go
Normal file
143
v2/pkg/reporting/exporters/sarif/sarif.go
Normal file
@ -0,0 +1,143 @@
|
||||
package sarif
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/owenrumney/go-sarif/sarif"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/format"
|
||||
)
|
||||
|
||||
// Exporter is an exporter for nuclei sarif output format.
|
||||
type Exporter struct {
|
||||
sarif *sarif.Report
|
||||
run *sarif.Run
|
||||
mutex *sync.Mutex
|
||||
|
||||
home string
|
||||
options *Options
|
||||
}
|
||||
|
||||
// Options contains the configuration options for sarif exporter client
|
||||
type Options struct {
|
||||
// File is the file to export found sarif result to
|
||||
File string `yaml:"file"`
|
||||
}
|
||||
|
||||
// New creates a new disk 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")
|
||||
}
|
||||
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get home dir")
|
||||
}
|
||||
templatePath := path.Join(home, "nuclei-templates")
|
||||
|
||||
run := sarif.NewRun("nuclei", "https://github.com/projectdiscovery/nuclei")
|
||||
return &Exporter{options: options, home: templatePath, sarif: report, run: run, mutex: &sync.Mutex{}}, nil
|
||||
}
|
||||
|
||||
// Export exports a passed result event to sarif structure
|
||||
func (i *Exporter) Export(event *output.ResultEvent) error {
|
||||
templatePath := strings.TrimPrefix(event.TemplatePath, i.home)
|
||||
|
||||
h := sha1.New()
|
||||
h.Write([]byte(event.Host))
|
||||
templateID := event.TemplateID + "-" + hex.EncodeToString(h.Sum(nil))
|
||||
|
||||
fullDescription := format.MarkdownDescription(event)
|
||||
sarifSeverity := getSarifSeverity(event)
|
||||
|
||||
var ruleName string
|
||||
if s, ok := event.Info["name"]; ok {
|
||||
ruleName = s.(string)
|
||||
}
|
||||
|
||||
var templateURL string
|
||||
if strings.HasPrefix(event.TemplatePath, i.home) {
|
||||
templateURL = "https://github.com/projectdiscovery/nuclei-templates/blob/master" + templatePath
|
||||
} else {
|
||||
templateURL = "https://github.com/projectdiscovery/nuclei-templates"
|
||||
}
|
||||
|
||||
var ruleDescription string
|
||||
if d, ok := event.Info["description"]; ok {
|
||||
ruleDescription = d.(string)
|
||||
}
|
||||
|
||||
i.mutex.Lock()
|
||||
defer i.mutex.Unlock()
|
||||
|
||||
_ = i.run.AddRule(templateID).
|
||||
WithDescription(ruleName).
|
||||
WithHelp(fullDescription).
|
||||
WithHelpURI(templateURL).
|
||||
WithFullDescription(sarif.NewMultiformatMessageString(ruleDescription))
|
||||
result := i.run.AddResult(templateID).
|
||||
WithMessage(sarif.NewMessage().WithText(event.Host)).
|
||||
WithLevel(sarifSeverity)
|
||||
|
||||
// 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.WithLocation(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)),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
result.WithLocation(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)),
|
||||
))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSarifSeverity returns the sarif severity
|
||||
func getSarifSeverity(event *output.ResultEvent) string {
|
||||
var ruleSeverity string
|
||||
if s, ok := event.Info["severity"]; ok {
|
||||
ruleSeverity = s.(string)
|
||||
}
|
||||
|
||||
switch ruleSeverity {
|
||||
case "info":
|
||||
return "note"
|
||||
case "low", "medium":
|
||||
return "warning"
|
||||
case "high", "critical":
|
||||
return "error"
|
||||
default:
|
||||
return "note"
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the exporter after operation
|
||||
func (i *Exporter) Close() error {
|
||||
i.mutex.Lock()
|
||||
defer i.mutex.Unlock()
|
||||
|
||||
i.sarif.AddRun(i.run)
|
||||
if len(i.run.Results) == 0 {
|
||||
return nil // do not write when no results
|
||||
}
|
||||
file, err := os.Create(i.options.File)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create sarif output file")
|
||||
}
|
||||
defer file.Close()
|
||||
return i.sarif.Write(file)
|
||||
}
|
||||
@ -44,6 +44,9 @@ func MarkdownDescription(event *output.ResultEvent) string {
|
||||
builder.WriteString(event.Timestamp.Format("Mon Jan 2 15:04:05 -0700 MST 2006"))
|
||||
builder.WriteString("\n\n**Template Information**\n\n| Key | Value |\n|---|---|\n")
|
||||
for k, v := range event.Info {
|
||||
if k == "reference" {
|
||||
continue
|
||||
}
|
||||
builder.WriteString(fmt.Sprintf("| %s | %s |\n", k, v))
|
||||
}
|
||||
if event.Request != "" {
|
||||
@ -60,11 +63,11 @@ func MarkdownDescription(event *output.ResultEvent) string {
|
||||
} else {
|
||||
builder.WriteString(event.Response)
|
||||
}
|
||||
builder.WriteString("\n```\n\n")
|
||||
builder.WriteString("\n```\n")
|
||||
}
|
||||
|
||||
if len(event.ExtractedResults) > 0 || len(event.Metadata) > 0 {
|
||||
builder.WriteString("**Extra Information**\n\n")
|
||||
builder.WriteString("\n**Extra Information**\n\n")
|
||||
if len(event.ExtractedResults) > 0 {
|
||||
builder.WriteString("**Extracted results**:\n\n")
|
||||
for _, v := range event.ExtractedResults {
|
||||
@ -110,6 +113,26 @@ func MarkdownDescription(event *output.ResultEvent) string {
|
||||
builder.WriteString("\n```\n")
|
||||
}
|
||||
}
|
||||
if d, ok := event.Info["reference"]; ok {
|
||||
builder.WriteString("\nReference: \n")
|
||||
|
||||
switch v := d.(type) {
|
||||
case string:
|
||||
if !strings.HasPrefix(v, "-") {
|
||||
builder.WriteString("- ")
|
||||
}
|
||||
builder.WriteString(v)
|
||||
case []interface{}:
|
||||
slice := types.ToStringSlice(v)
|
||||
for i, item := range slice {
|
||||
builder.WriteString("- ")
|
||||
builder.WriteString(item)
|
||||
if len(slice)-1 != i {
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.WriteString("\n---\nGenerated by [Nuclei](https://github.com/projectdiscovery/nuclei)")
|
||||
data := builder.String()
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/dedupe"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/disk"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/sarif"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/github"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/gitlab"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/jira"
|
||||
@ -28,6 +29,8 @@ type Options struct {
|
||||
Jira *jira.Options `yaml:"jira"`
|
||||
// DiskExporter contains configuration options for Disk Exporter Module
|
||||
DiskExporter *disk.Options `yaml:"disk"`
|
||||
// SarifExporter contains configuration options for Sarif Exporter Module
|
||||
SarifExporter *sarif.Options `yaml:"sarif"`
|
||||
}
|
||||
|
||||
// Filter filters the received event and decides whether to perform
|
||||
@ -79,6 +82,8 @@ type Tracker interface {
|
||||
|
||||
// Exporter is an interface implemented by an issue exporter
|
||||
type Exporter interface {
|
||||
// Close closes the exporter after operation
|
||||
Close() error
|
||||
// Export exports an issue to an exporter
|
||||
Export(event *output.ResultEvent) error
|
||||
}
|
||||
@ -129,6 +134,13 @@ func New(options *Options, db string) (*Client, error) {
|
||||
}
|
||||
client.exporters = append(client.exporters, exporter)
|
||||
}
|
||||
if options.SarifExporter != nil {
|
||||
exporter, err := sarif.New(options.SarifExporter)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not create exporting client")
|
||||
}
|
||||
client.exporters = append(client.exporters, exporter)
|
||||
}
|
||||
storage, err := dedupe.New(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -140,6 +152,9 @@ func New(options *Options, db string) (*Client, error) {
|
||||
// Close closes the issue tracker reporting client
|
||||
func (c *Client) Close() {
|
||||
c.dedupe.Close()
|
||||
for _, exporter := range c.exporters {
|
||||
exporter.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// CreateIssue creates an issue in the tracker
|
||||
|
||||
@ -159,6 +159,9 @@ func jiraFormatDescription(event *output.ResultEvent) string {
|
||||
builder.WriteString(event.Timestamp.Format("Mon Jan 2 15:04:05 -0700 MST 2006"))
|
||||
builder.WriteString("\n\n*Template Information*\n\n| Key | Value |\n")
|
||||
for k, v := range event.Info {
|
||||
if k == "reference" {
|
||||
continue
|
||||
}
|
||||
builder.WriteString(fmt.Sprintf("| %s | %s |\n", k, v))
|
||||
}
|
||||
builder.WriteString("\n*Request*\n\n{code}\n")
|
||||
@ -174,7 +177,7 @@ func jiraFormatDescription(event *output.ResultEvent) string {
|
||||
builder.WriteString("\n{code}\n\n")
|
||||
|
||||
if len(event.ExtractedResults) > 0 || len(event.Metadata) > 0 {
|
||||
builder.WriteString("*Extra Information*\n\n")
|
||||
builder.WriteString("\n*Extra Information*\n\n")
|
||||
if len(event.ExtractedResults) > 0 {
|
||||
builder.WriteString("*Extracted results*:\n\n")
|
||||
for _, v := range event.ExtractedResults {
|
||||
@ -220,6 +223,26 @@ func jiraFormatDescription(event *output.ResultEvent) string {
|
||||
builder.WriteString("\n{code}\n")
|
||||
}
|
||||
}
|
||||
if d, ok := event.Info["reference"]; ok {
|
||||
builder.WriteString("\nReference: \n")
|
||||
|
||||
switch v := d.(type) {
|
||||
case string:
|
||||
if !strings.HasPrefix(v, "-") {
|
||||
builder.WriteString("- ")
|
||||
}
|
||||
builder.WriteString(v)
|
||||
case []interface{}:
|
||||
slice := types.ToStringSlice(v)
|
||||
for i, item := range slice {
|
||||
builder.WriteString("- ")
|
||||
builder.WriteString(item)
|
||||
if len(slice)-1 != i {
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.WriteString("\n---\nGenerated by [Nuclei|https://github.com/projectdiscovery/nuclei]")
|
||||
data := builder.String()
|
||||
return data
|
||||
|
||||
@ -142,6 +142,7 @@ func Parse(filePath string, options protocols.ExecuterOptions) (*Template, error
|
||||
if template.Executer == nil && template.CompiledWorkflow == nil {
|
||||
return nil, errors.New("cannot create template executer")
|
||||
}
|
||||
template.Path = filePath
|
||||
return template, nil
|
||||
}
|
||||
|
||||
|
||||
@ -35,4 +35,6 @@ type Template struct {
|
||||
TotalRequests int `yaml:"-" json:"-"`
|
||||
// Executer is the actual template executor for running template requests
|
||||
Executer protocols.Executer `yaml:"-" json:"-"`
|
||||
|
||||
Path string `yaml:"-" json:"-"`
|
||||
}
|
||||
|
||||
@ -21,8 +21,6 @@ type Options struct {
|
||||
// Severity filters templates based on their severity and only run the matching ones.
|
||||
Severity goflags.StringSlice
|
||||
InternalResolversList []string // normalized from resolvers flag as well as file provided.
|
||||
// BurpCollaboratorBiid is the Burp Collaborator BIID for polling interactions.
|
||||
BurpCollaboratorBiid string
|
||||
// ProjectPath allows nuclei to use a user defined project folder
|
||||
ProjectPath string
|
||||
// InteractshURL is the URL for the interactsh server.
|
||||
@ -47,6 +45,8 @@ type Options struct {
|
||||
ReportingConfig string
|
||||
// DiskExportDirectory is the directory to export reports in markdown on disk to
|
||||
DiskExportDirectory string
|
||||
// SarifExport is the file to export sarif output format to
|
||||
SarifExport string
|
||||
// ResolversFile is a file containing resolvers for nuclei.
|
||||
ResolversFile string
|
||||
// StatsInterval is the number of seconds to display stats after
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user