Skip to content

Commit 34cb817

Browse files
committed
feat: threaded domain fingerprinting
1 parent 4827202 commit 34cb817

File tree

4 files changed

+49
-20
lines changed

4 files changed

+49
-20
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,6 @@ require (
6363
github.com/projectdiscovery/subfinder/v2 v2.5.6
6464
github.com/stretchr/testify v1.8.2
6565
github.com/valyala/bytebufferpool v1.0.0 // indirect
66+
golang.org/x/sync v0.1.0
6667
golang.org/x/sys v0.4.0 // indirect
6768
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
144144
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
145145
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
146146
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
147+
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
148+
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
147149
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
148150
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
149151
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

pkg/domain/enumeration.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,34 @@ import (
66
"github.com/Escape-Technologies/goctopus/pkg/config"
77
"github.com/projectdiscovery/subfinder/v2/pkg/resolve"
88
"github.com/projectdiscovery/subfinder/v2/pkg/runner"
9+
log "github.com/sirupsen/logrus"
910
)
1011

11-
func EnumerateSubdomains(domain string, subDomains chan string) (err error) {
12+
var (
13+
runnerInstance *runner.Runner
14+
)
15+
16+
func EnumerateSubdomains(domain string, subDomains chan string, threads int) (err error) {
17+
log.Errorf("Enumerating subdomains for %s with %d threads", domain, threads)
1218
subDomains <- domain
1319
c := config.Get()
1420

1521
if !c.SubdomainEnumeration {
1622
return nil
1723
}
1824

19-
runnerInstance, _ := runner.NewRunner(&runner.Options{
20-
Threads: c.MaxWorkers, // Thread controls the number of threads to use for active enumerations
21-
Timeout: c.Timeout, // Timeout is the seconds to wait for sources to respond
22-
MaxEnumerationTime: 5, // MaxEnumerationTime is the maximum amount of time in mins to wait for enumeration
23-
Resolvers: resolve.DefaultResolvers, // Use the default list of resolvers by marshaling it to the config
24-
ResultCallback: func(s *resolve.HostEntry) { // Callback function to execute for available host
25-
subDomains <- s.Host
26-
},
27-
Silent: true,
28-
})
25+
if runnerInstance == nil {
26+
runnerInstance, _ = runner.NewRunner(&runner.Options{
27+
Threads: threads, // Thread controls the number of threads to use for active enumerations
28+
Timeout: c.Timeout, // Timeout is the seconds to wait for sources to respond
29+
MaxEnumerationTime: 5, // MaxEnumerationTime is the maximum amount of time in mins to wait for enumeration
30+
Resolvers: resolve.DefaultResolvers, // Use the default list of resolvers by marshaling it to the config
31+
ResultCallback: func(s *resolve.HostEntry) { // Callback function to execute for available host
32+
subDomains <- s.Host
33+
},
34+
Silent: true,
35+
})
36+
}
2937

3038
err = runnerInstance.EnumerateSingleDomain(domain, []io.Writer{})
3139
return err

pkg/goctopus/fingerprint.go

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package goctopus
22

33
import (
4+
"context"
45
"sync"
56

67
"github.com/Escape-Technologies/goctopus/internal/utils"
@@ -9,18 +10,20 @@ import (
910
"github.com/Escape-Technologies/goctopus/pkg/endpoint"
1011
"github.com/Escape-Technologies/goctopus/pkg/output"
1112
log "github.com/sirupsen/logrus"
13+
"golang.org/x/sync/semaphore"
1214
)
1315

14-
func worker(addresses chan string, output chan *output.FingerprintOutput, workerId int, wg *sync.WaitGroup) {
15-
defer wg.Done()
16+
func worker(addresses chan string, output chan *output.FingerprintOutput, workerId int, sem *semaphore.Weighted) {
1617
log.Debugf("Worker %d instantiated", workerId)
1718
for address := range addresses {
19+
sem.Acquire(context.Background(), 1)
1820
log.Debugf("Worker %d started on: %v", workerId, address)
1921
res, err := FingerprintAddress(address)
2022
if err == nil {
2123
log.Debugf("Worker %d found endpoint: %v", workerId, res)
2224
output <- res
2325
}
26+
sem.Release(1)
2427
}
2528
log.Debugf("Worker %d finished", workerId)
2629
}
@@ -34,36 +37,51 @@ func FingerprintAddress(address string) (*output.FingerprintOutput, error) {
3437
}
3538
}
3639

40+
func asyncEnumeration(address string, enumeratedAddresses chan string, threads int, sem *semaphore.Weighted, wg *sync.WaitGroup) {
41+
defer wg.Done()
42+
defer sem.Release(int64(threads))
43+
if err := domain.EnumerateSubdomains(address, enumeratedAddresses, threads); err != nil {
44+
log.Errorf("Error enumerating subdomains for %v: %v", address, err)
45+
}
46+
}
47+
3748
// An addresses can be a domain or an url
3849
func FingerprintAddresses(addresses chan string, output chan *output.FingerprintOutput) {
3950

4051
maxWorkers := config.Get().MaxWorkers
4152
enumeratedAddresses := make(chan string, config.Get().MaxWorkers)
4253

43-
workersWg := sync.WaitGroup{}
44-
workersWg.Add(maxWorkers)
54+
sem := semaphore.NewWeighted(int64(maxWorkers))
55+
enumerationWg := sync.WaitGroup{}
56+
enumerationThreads := utils.MinInt(maxWorkers, 10)
4557

4658
for i := 0; i < maxWorkers; i++ {
47-
go worker(enumeratedAddresses, output, i, &workersWg)
59+
go worker(enumeratedAddresses, output, i, sem)
4860
}
4961

5062
i := 1
5163
for address := range addresses {
5264
log.Debugf("(%d) Adding %v to the queue", i, address)
5365
// If the domain is a url, we don't need to crawl it
5466
if utils.IsUrl(address) {
67+
sem.Acquire(context.Background(), 1)
5568
enumeratedAddresses <- address
5669
} else {
57-
if err := domain.EnumerateSubdomains(address, enumeratedAddresses); err != nil {
58-
log.Errorf("Error enumerating subdomains for %v: %v", address, err)
59-
}
70+
// 10 threads for subdomain enumeration, unless maxWorkers is less than 10
71+
enumerationWg.Add(1)
72+
sem.Acquire(context.Background(), int64(enumerationThreads))
73+
log.Errorf("%v", address)
74+
go asyncEnumeration(address, enumeratedAddresses, enumerationThreads, sem, &enumerationWg)
6075
}
6176
i++
6277
}
6378

79+
log.Error("here")
80+
enumerationWg.Wait()
81+
log.Error("here 2")
6482
close(enumeratedAddresses)
6583
log.Debugf("Waiting for workers to finish...")
66-
workersWg.Wait()
84+
sem.Acquire(context.Background(), int64(maxWorkers))
6785
close(output)
6886
log.Debugf("All workers finished")
6987
}

0 commit comments

Comments
 (0)