nuclei icon indicating copy to clipboard operation
nuclei copied to clipboard

[bug] An error occurs when running nuclei with multiple instances

Open hktalent opened this issue 1 year ago • 13 comments

fatal error: concurrent map iteration and map write

test code:

package main

import (
	"bytes"
	"fmt"
	"github.com/hktalent/scan4all/nuclei_Yaml"
	"net/http"
	_ "net/http/pprof"
	"os"
	"sync"
)


func DoNuclei(buf *bytes.Buffer, wg *sync.WaitGroup, oOpts *map[string]interface{}) {
	defer wg.Done()
	xx := make(chan bool)
	go nuclei_Yaml.RunNuclei(buf, xx, nil)
	<-xx
}

func main() {
	if true {
		go func() {
			http.ListenAndServe(":6060", nil)
		}()
		buf := bytes.Buffer{}
		var wg sync.WaitGroup
		wg.Add(1)
		buf.WriteString("http://192.168.10.31:8888\n")
		pwd, _ := os.Getwd()
		m1 := map[string]interface{}{"UpdateTemplates": false, "Templates": []string{pwd + "/config/nuclei-templates"}, "TemplatesDirectory": pwd + "/config/nuclei-templates", "NoUpdateTemplates": true}
		go DoNuclei(&buf, &wg, &m1)
		wg.Add(1)
		go DoNuclei(&buf, &wg, &m1)
		wg.Add(1)
		go DoNuclei(&buf, &wg, &m1)
		wg.Wait()
	}
}

error:

[0:00:25] | Templates: 3661 | Hosts: 1 | RPS: 102 | Matched: 19 | Errors: 92 | Requests: 2574/5043 (51%)
fatal error: concurrent map iteration and map write

goroutine 36114 [running]:
runtime.throw({0x52c27f3?, 0x1?})
        /usr/local/Cellar/go/1.18.4/libexec/src/runtime/panic.go:992 +0x71 fp=0xc010f3f288 sp=0xc010f3f258 pc=0x4038b51
runtime.mapiternext(0x5031840?)
        /usr/local/Cellar/go/1.18.4/libexec/src/runtime/map.go:871 +0x4eb fp=0xc010f3f2f8 sp=0xc010f3f288 pc=0x4013aeb
github.com/projectdiscovery/nuclei/v2/pkg/protocols/http.(*Request).executeRequest(0xc00ebe1680, {0xc0007cac60, 0x19}, 0xc01385f540, 0xc008d9d680?, 0x0, 0xc008d9d6b0, 0x0?)
        /Users/51pwn/MyWork/scan4all/vendor/github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/request.go:573 +0x28fb fp=0xc010f3ff38 sp=0xc010f3f2f8 pc=0x4d0f59b
github.com/projectdiscovery/nuclei/v2/pkg/protocols/http.(*Request).executeTurboHTTP.func1(0xc008412b40?)
        /Users/51pwn/MyWork/scan4all/vendor/github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/request.go:210 +0x9f fp=0xc010f3ffc8 sp=0xc010f3ff38 pc=0x4d0bbdf
github.com/projectdiscovery/nuclei/v2/pkg/protocols/http.(*Request).executeTurboHTTP.func2()
        /Users/51pwn/MyWork/scan4all/vendor/github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/request.go:216 +0x2a fp=0xc010f3ffe0 sp=0xc010f3ffc8 pc=0x4d0bb0a
runtime.goexit()
        /usr/local/Cellar/go/1.18.4/libexec/src/runtime/asm_amd64.s:1571 +0x1 fp=0xc010f3ffe8 sp=0xc010f3ffe0 pc=0x406b861
created by github.com/projectdiscovery/nuclei/v2/pkg/protocols/http.(*Request).executeTurboHTTP
        /Users/51pwn/MyWork/scan4all/vendor/github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/request.go:207 +0x377

goroutine 1 [chan receive]:
main.DoNuclei(0xc00093bd40, 0xc000869e30?, 0x5280fd1?)
        /Users/51pwn/MyWork/scan4all/test/nuclei/testNuclei.go:29 +0xca
main.main()
        /Users/51pwn/MyWork/scan4all/test/nuclei/testNuclei.go:45 +0x2c5



log.txt

options

{
  "Tags": [],
  "ExcludeTags": [],
  "Workflows": [],
  "WorkflowURLs": [],
  "Templates": [
    "/Users/51pwn/MyWork/scan4all/config/nuclei-templates"
  ],
  "TemplateURLs": [],
  "RemoteTemplateDomainList": [
    "api.nuclei.sh"
  ],
  "ExcludedTemplates": [],
  "ExcludeMatchers": null,
  "CustomHeaders": [],
  "Vars": {},
  "Severities": null,
  "ExcludeSeverities": null,
  "Authors": [],
  "Protocols": [
    1,
    2,
    3,
    4,
    5,
    6,
    7,
    8,
    9
  ],
  "ExcludeProtocols": null,
  "IncludeTags": [],
  "IncludeTemplates": [],
  "IncludeIds": [],
  "ExcludeIds": [],
  "InternalResolversList": null,
  "ProjectPath": "/var/folders/_l/pnb2t_9s0f192bqlz1348vpr0000gn/T/",
  "InteractshURL": "",
  "InteractshToken": "",
  "Targets": [
    "http://192.168.10.31:8888"
  ],
  "TargetsFilePath": "",
  "Resume": "",
  "Output": "",
  "ProxyInternal": false,
  "Proxy": [],
  "TemplatesDirectory": "/Users/51pwn/MyWork/scan4all/config/nuclei-templates",
  "TraceLogFile": "",
  "ErrorLogFile": "",
  "ReportingDB": "",
  "ReportingConfig": "",
  "MarkdownExportDirectory": "",
  "SarifExport": "",
  "ResolversFile": "",
  "StatsInterval": 5,
  "MetricsPort": 9092,
  "MaxHostError": 30,
  "BulkSize": 64,
  "TemplateThreads": 64,
  "HeadlessBulkSize": 10,
  "HeadlessTemplateThreads": 10,
  "Timeout": 5,
  "Retries": 1,
  "RateLimit": 150,
  "RateLimitMinute": 0,
  "PageTimeout": 20,
  "InteractionsCacheSize": 5000,
  "InteractionsPollDuration": 5,
  "InteractionsEviction": 60,
  "InteractionsCoolDownPeriod": 5,
  "MaxRedirects": 10,
  "FollowRedirects": false,
  "OfflineHTTP": false,
  "StatsJSON": false,
  "Headless": false,
  "ShowBrowser": false,
  "UseInstalledChrome": false,
  "SystemResolvers": false,
  "Metrics": false,
  "Debug": false,
  "DebugRequests": false,
  "DebugResponse": false,
  "LeaveDefaultPorts": false,
  "AutomaticScan": false,
  "Silent": false,
  "Version": false,
  "Validate": false,
  "NoStrictSyntax": false,
  "Verbose": false,
  "VerboseVerbose": false,
  "NoColor": false,
  "UpdateTemplates": false,
  "JSON": false,
  "JSONRequests": false,
  "EnableProgressBar": true,
  "TemplatesVersion": false,
  "TemplateList": false,
  "HangMonitor": false,
  "Stdin": false,
  "StopAtFirstMatch": false,
  "Stream": false,
  "NoMeta": false,
  "NoTimestamp": false,
  "Project": false,
  "NewTemplates": false,
  "NewTemplatesWithVersion": null,
  "NoInteractsh": false,
  "UpdateNuclei": false,
  "NoUpdateTemplates": true,
  "EnvironmentVariables": false,
  "MatcherStatus": false,
  "ClientCertFile": "",
  "ClientKeyFile": "",
  "ClientCAFile": "",
  "ZTLS": false,
  "ShowMatchLine": false,
  "EnablePprof": false,
  "StoreResponse": false,
  "StoreResponseDir": "output",
  "DisableRedirects": true,
  "SNI": "",
  "HealthCheck": false,
  "InputReadTimeout": 0,
  "DisableStdin": false
}

hktalent avatar Jul 18 '22 06:07 hktalent

image

hktalent avatar Jul 18 '22 06:07 hktalent

image

hktalent avatar Jul 18 '22 06:07 hktalent

@ehsandeep github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/request.go The analysis found: previousEvent finalEvent There are thread safety bugs Finally tracked here: type InternalEvent map[string]interface{}

The same bug exists in v2.7.4

fatal error: concurrent map writes

goroutine 21282 [running]:
runtime.throw({0x529fe07?, 0x40f2905?})
        /usr/local/Cellar/go/1.18.4/libexec/src/runtime/panic.go:992 +0x71 fp=0xc00776d290 sp=0xc00776d260 pc=0x403a471
runtime.mapassign_faststr(0x5261772?, 0x5?, {0xc00f19c510, 0xe})
        /usr/local/Cellar/go/1.18.4/libexec/src/runtime/map_faststr.go:212 +0x39c fp=0xc00776d2f8 sp=0xc00776d290 pc=0x40187fc
github.com/projectdiscovery/nuclei/v2/pkg/protocols/http.(*Request).executeRequest(0xc0095b2b60, {0xc0087c07a0, 0x19}, 0xc01253ef50, 0xc0088ba1b0?, 0x0, 0xc0088ba1e0, 0x0?)
        /Users/51pwn/MyWork/scan4all/vendor/github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/request.go:592 +0x2aef fp=0xc00776df38 sp=0xc00776d2f8 pc=0x4d1b94f
github.com/projectdiscovery/nuclei/v2/pkg/protocols/http.(*Request).executeTurboHTTP.func1(0xc0096665a0?)
        /Users/51pwn/MyWork/scan4all/vendor/github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/request.go:210 +0x9f fp=0xc00776dfc8 sp=0xc00776df38 pc=0x4d1791f
github.com/projectdiscovery/nuclei/v2/pkg/protocols/http.(*Request).executeTurboHTTP.func2()
        /Users/51pwn/MyWork/scan4all/vendor/github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/request.go:216 +0x2a fp=0xc00776dfe0 sp=0xc00776dfc8 pc=0x4d1784a
runtime.goexit()
        /usr/local/Cellar/go/1.18.4/libexec/src/runtime/asm_amd64.s:1571 +0x1 fp=0xc00776dfe8 sp=0xc00776dfe0 pc=0x406d181
created by github.com/projectdiscovery/nuclei/v2/pkg/protocols/http.(*Request).executeTurboHTTP
        /Users/51pwn/MyWork/scan4all/vendor/github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/request.go:207 +0x377

goroutine 1 [chan receive]:
main.DoNuclei(0xc005cfc9c0, 0xc005cfc9f0?, 0xc000010f00)
        /Users/51pwn/MyWork/scan4all/test/nuclei/testNuclei.go:29 +0xe5
main.main()
        /Users/51pwn/MyWork/scan4all/test/nuclei/testNuclei.go:45 +0x277

hktalent avatar Jul 18 '22 07:07 hktalent

@ehsandeep @forgedhallpass

Can you provide an http interface to submit asynchronous tasks?
1. Start the http receive task
2. The task contains url + template tag + option parameter, for example, you can specify url template data (yaml, zip)
post http://127.0.0.1/nuclei/v1/api/addTask

Parameter example:
url=http://127.0.0.1/test
templatesIds=[]strings{}
tags=[]strings{}
templates=[]strings{"./config/xx1.yaml","http://127.0.0.1/pocs/xx1.yaml","http://127.0.0.1/pocs/all.zip"}

hktalent avatar Jul 18 '22 14:07 hktalent

same err,how fix, thanks?

rijalrojan avatar Jul 20 '22 02:07 rijalrojan

Oh,I also encountered the same problem. Can you solve it as soon as possible.

l0n3rs avatar Jul 20 '22 02:07 l0n3rs

@ehsandeep bug2

go DoNuclei(&buf, &wg, &m1)
		wg.Add(1)
		go DoNuclei(&buf, &wg, &m1)
		wg.Add(1)
		go DoNuclei(&buf, &wg, &m1)

console log, you can see > 100%:

[0:01:00] | Templates: 3680 | Hosts: 1 | RPS: 89 | Matched: 26 | Errors: 186 | Requests: 5380/5094 (105%)
[0:01:05] | Templates: 3680 | Hosts: 1 | RPS: 58 | Matched: 17 | Errors: 174 | Requests: 3784/5094 (74%)
[0:01:05] | Templates: 3680 | Hosts: 1 | RPS: 75 | Matched: 23 | Errors: 136 | Requests: 4905/5094 (96%)
[0:01:05] | Templates: 3680 | Hosts: 1 | RPS: 82 | Matched: 26 | Errors: 189 | Requests: 5383/5094 (105%)
[0:01:10] | Templates: 3680 | Hosts: 1 | RPS: 54 | Matched: 17 | Errors: 174 | Requests: 3784/5094 (74%)
[0:01:10] | Templates: 3680 | Hosts: 1 | RPS: 70 | Matched: 23 | Errors: 136 | Requests: 4905/5094 (96%)
[0:01:10] | Templates: 3680 | Hosts: 1 | RPS: 76 | Matched: 26 | Errors: 192 | Requests: 5386/5094 (105%)
[0:01:12] | Templates: 3680 | Hosts: 1 | RPS: 67 | Matched: 23 | Errors: 136 | Requests: 4905/5094 (96%)
[0:01:15] | Templates: 3680 | Hosts: 1 | RPS: 50 | Matched: 17 | Errors: 174 | Requests: 3784/5094 (74%)
[0:01:15] | Templates: 3680 | Hosts: 1 | RPS: 71 | Matched: 26 | Errors: 194 | Requests: 5388/5094 (105%)
[0:01:20] | Templates: 3680 | Hosts: 1 | RPS: 47 | Matched: 17 | Errors: 174 | Requests: 3784/5094 (74%)
[0:01:20] | Templates: 3680 | Hosts: 1 | RPS: 67 | Matched: 26 | Errors: 196 | Requests: 5390/5094 (105%)
[0:01:25] | Templates: 3680 | Hosts: 1 | RPS: 44 | Matched: 17 | Errors: 174 | Requests: 3784/5094 (74%)
[0:01:25] | Templates: 3680 | Hosts: 1 | RPS: 63 | Matched: 26 | Errors: 196 | Requests: 5390/5094 (105%)
[0:01:25] | Templates: 3680 | Hosts: 1 | RPS: 44 | Matched: 17 | Errors: 174 | Requests: 3784/5094 (74%)
[0:01:25] | Templates: 3680 | Hosts: 1 | RPS: 63 | Matched: 26 | Errors: 196 | Requests: 5390/5094 (105%)

hktalent avatar Jul 20 '22 03:07 hktalent

@ehsandeep request.go

+ var someMapMutex = sync.RWMutex{}
// executeRequest executes the actual generated request and returns error if occurred
func (request *Request) executeRequest(reqURL string, generatedRequest *generatedRequest, previousEvent output.InternalEvent, hasInteractMatchers bool, callback protocols.OutputEventCallback, requestCount int) error {
	request.setCustomHeaders(generatedRequest)
.....
+  someMapMutex.Lock()
		for k, v := range previousEvent {
			finalEvent[k] = v
		}
		for k, v := range outputEvent {
			finalEvent[k] = v
		}

		// Add to history the current request number metadata if asked by the user.
		if request.ReqCondition {
			for k, v := range outputEvent {
				key := fmt.Sprintf("%s_%d", k, requestCount)
				previousEvent[key] = v
				finalEvent[key] = v
			}
		}
+ someMapMutex.Unlock()

now fix request.go:573

hktalent avatar Jul 20 '22 03:07 hktalent

@hktalent running multiple instances of nuclei at the same from the same system is not expected in general, but we will look into the crash.

ehsandeep avatar Jul 20 '22 15:07 ehsandeep

@ehsandeep image I would like to be able to support concurrent nuclei multi-instance, which is very valuable, in the case of multi-tasking, in the case of dynamically joining the target

thanks

hktalent avatar Jul 21 '22 08:07 hktalent

@ehsandeep It is recommended not to use internal. At present, I have encountered such a situation. I need to call nuclei runner.Close() externally to terminate the current task. Then I found that because of the use of internal, it violated the rules of golang code example (x1.(*runner2.Runner)).Close()

hktalent avatar Jul 26 '22 07:07 hktalent

@ehsandeep After each update package, it needs to be repaired manually, otherwise memory errors and abnormal exits will occur. Get rid of whether you can merge my PR #2308

request.go

+ var someMapMutex = sync.RWMutex{}
// executeRequest executes the actual generated request and returns error if occurred
func (request *Request) executeRequest(reqURL string, generatedRequest *generatedRequest, previousEvent output.InternalEvent, hasInteractMatchers bool, callback protocols.OutputEventCallback, requestCount int) error {
	request.setCustomHeaders(generatedRequest)
.....
+  someMapMutex.Lock()
		for k, v := range previousEvent {
			finalEvent[k] = v
		}
		for k, v := range outputEvent {
			finalEvent[k] = v
		}

		// Add to history the current request number metadata if asked by the user.
		if request.ReqCondition {
			for k, v := range outputEvent {
				key := fmt.Sprintf("%s_%d", k, requestCount)
				previousEvent[key] = v
				finalEvent[key] = v
			}
		}
+ someMapMutex.Unlock()

now fix request.go:573

hktalent avatar Jul 28 '22 09:07 hktalent

@ehsandeep Concurrent multi-instance, a large number of: “leveldb: closed” https://github.com/0xProject/0x-mesh/issues/319 image

hktalent avatar Jul 30 '22 19:07 hktalent

@hktalent It seems like that https://github.com/hktalent/scan4all/nuclei_Yaml doesn't exist anymore. Is this issue still reproducible? Can you provide me a link to where the nuclei runner is being used?

Mzack9999 avatar Jan 02 '23 12:01 Mzack9999

@Mzack9999 maybe https://github.com/hktalent/scan4all/tree/main/projectdiscovery/nuclei_Yaml is the new path for it?

jimen0 avatar Jan 02 '23 12:01 jimen0