Skip to content

Commit

Permalink
updated for SPEED
Browse files Browse the repository at this point in the history
  • Loading branch information
g0ldencybersec committed Apr 30, 2024
1 parent 52e1b5f commit d6bd7f9
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 50 deletions.
4 changes: 3 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ func main() {
flag.IntVar(&args.Timeout, "t", 4, "Timeout for TLS handshake")
flag.StringVar(&args.Input, "i", "NONE", "Either IPs & CIDRs separated by commas, or a file with IPs/CIDRs on each line")
flag.BoolVar(&args.Debug, "debug", false, "Add this flag if you want to see failures/timeouts")
flag.StringVar(&args.OutputFile, "o", "CaduceusResults.jsonl", "Output file to write results to")
flag.BoolVar(&args.Help, "h", false, "Show the program usage message")
flag.BoolVar(&args.JsonOutput, "j", false, "print cert data as jsonl")
flag.BoolVar(&args.PrintWildcards, "wc", false, "print wildcards to stdout")
//flag.BoolVar(&args.Help, "stats", false, "Print stats at the end")

flag.Parse()

Expand Down
67 changes: 30 additions & 37 deletions pkg/scrape/scrape.go
Original file line number Diff line number Diff line change
@@ -1,66 +1,59 @@
package scrape

import (
"bufio"
"encoding/json"
"fmt"
"net"
"os"
"sync"

"time"

"github.com/g0ldencybersec/Caduceus/pkg/stats"
"github.com/g0ldencybersec/Caduceus/pkg/types"
"github.com/g0ldencybersec/Caduceus/pkg/utils"
"github.com/g0ldencybersec/Caduceus/pkg/workers"
)

func RunScrape(args types.ScrapeArgs) {
var wg sync.WaitGroup

dialer := &net.Dialer{
Timeout: time.Duration(args.Timeout) * time.Second,
}

inputChannel := make(chan string)
resultChannel := make(chan types.Result)

stats := &stats.Stats{}
outputChannel := make(chan string, 1000)

workerPool := workers.NewWorkerPool(args.Concurrency, dialer, inputChannel, resultChannel)
workerPool.Start()
resultWorkerPool := workers.NewResultWorkerPool(10, resultChannel, outputChannel)

go utils.IntakeFunction(inputChannel, args.Ports, args.Input)
// Start worker pools
workerPool.Start()
resultWorkerPool.Start(args)

defer func() {
workerPool.Stop()
close(resultChannel)
// Handle input feeding
wg.Add(1)
go func() {
defer wg.Done()
utils.IntakeFunction(inputChannel, args.Ports, args.Input)
}()

file, err := os.Create(args.OutputFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create output file: %v\n", err)
return
}
defer file.Close()
writer := bufio.NewWriter(file)
defer writer.Flush()

for result := range resultChannel {

stats.Update(result)
stats.Display() // Display updated stats

if result.Hit {
outputJSON, _ := json.Marshal(result.Certificate)
writer.Write(outputJSON)
writer.WriteString("\n")
} else if args.Debug {
if result.Timeout {
fmt.Printf("Timed Out. No SSL certificate found for %s\n", result.IP)
}
if result.Error != nil {
fmt.Printf("Failed to get SSL certificate from %s: %v\n", result.IP, result.Error)
}
// Handle outputs
go func() {
for output := range outputChannel {
fmt.Println(output)
}
}
}()

// Wait for all inputs to be processed before closing inputChannel
wg.Wait()
close(inputChannel)
workerPool.Stop() // Wait internally for all workers to finish before closing resultChannel
close(resultChannel)
resultWorkerPool.Stop() // Wait internally for all result workers to finish before closing outputChannel
close(outputChannel)

// if args.PrintStats {
// stats.Display() // Display updated stats
// }

}
4 changes: 4 additions & 0 deletions pkg/stats/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package stats
import (
"fmt"
"os"
"sync"

"github.com/g0ldencybersec/Caduceus/pkg/types"
)
Expand All @@ -11,6 +12,7 @@ type Stats struct {
hits int64
misses int64
total int64
mu sync.Mutex
}

func (s *Stats) HitPercentage() float64 {
Expand All @@ -21,6 +23,8 @@ func (s *Stats) HitPercentage() float64 {
}

func (s *Stats) Update(result types.Result) {
s.mu.Lock()
defer s.mu.Unlock()
s.total++
if result.Hit {
s.hits++
Expand Down
27 changes: 15 additions & 12 deletions pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,25 @@ package types

// Scraper Arg types
type ScrapeArgs struct {
Concurrency int
Ports []string
Timeout int
PortList string
Help bool
Input string
Debug bool
OutputFile string
Concurrency int
Ports []string
Timeout int
PortList string
Help bool
Input string
Debug bool
JsonOutput bool
PrintWildcards bool
PrintStats bool
}

// Result Types
type CertificateInfo struct {
IP string `json:"ip"`
Organization string `json:"organization"`
CommonName string `json:"commonName"`
SAN string `json:"san"`
IP string `json:"ip"`
Organization string `json:"organization"`
CommonName string `json:"commonName"`
SAN string `json:"san"`
Domains []string `json:"domains"`
}

type Result struct {
Expand Down
12 changes: 12 additions & 0 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import (
"fmt"
"net"
"os"
"regexp"
"strings"
)

var domainRegex = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`)

func GetSSLCert(ip string, dialer *net.Dialer) (*x509.Certificate, error) {
conn, err := tls.DialWithDialer(dialer, "tcp", ip, &tls.Config{
InsecureSkipVerify: true,
Expand Down Expand Up @@ -121,3 +124,12 @@ func JoinNonEmpty(sep string, elements []string) string {
}
return result
}

func IsValidDomain(domain string) bool {
return domainRegex.MatchString(domain)
}

func IsWilcard(domain string) bool {
replaced := strings.Replace(domain, "*.", "", -1)
return (strings.Contains(domain, "*") && IsValidDomain(replaced))
}
74 changes: 74 additions & 0 deletions pkg/workers/workers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package workers

import (
"encoding/json"
"fmt"
"net"

"github.com/g0ldencybersec/Caduceus/pkg/types"
Expand Down Expand Up @@ -52,6 +54,7 @@ func (w *worker) run() {
Organization: utils.GetOrganization(org),
CommonName: names[0],
SAN: utils.JoinNonEmpty(", ", names[1:]),
Domains: names,
}

w.results <- types.Result{IP: ip, Hit: true, Certificate: &certInfo, Timeout: false}
Expand Down Expand Up @@ -80,3 +83,74 @@ func (wp *WorkerPool) Start() {
func (wp *WorkerPool) Stop() {
close(wp.input)
}

// Result Workers
type ResultWorker struct {
resultInput <-chan types.Result
outputChannel chan<- string
}

type ResultWorkerPool struct {
workers []*ResultWorker
resultInput chan types.Result
outputChannel chan string
}

func NewResultWorker(resultInput <-chan types.Result, outputChannel chan<- string) *ResultWorker {
return &ResultWorker{
resultInput: resultInput,
outputChannel: outputChannel,
}
}

func (rw *ResultWorker) Run(args types.ScrapeArgs) {
for result := range rw.resultInput {
if result.Hit {
if args.JsonOutput {
outputJSON, _ := json.Marshal(result.Certificate)
rw.outputChannel <- string(outputJSON)
} else {
for _, domain := range result.Certificate.Domains {
if args.PrintWildcards {
if utils.IsWilcard(domain) || utils.IsValidDomain(domain) {
rw.outputChannel <- domain
continue
}
}
if utils.IsValidDomain(domain) && !utils.IsWilcard(domain) {
rw.outputChannel <- domain
}
}
}
} else if args.Debug {
if result.Timeout {
rw.outputChannel <- fmt.Sprintf("Timed Out. No SSL certificate found for %s", result.IP)
}
if result.Error != nil {
rw.outputChannel <- fmt.Sprintf("Failed to get SSL certificate from %s: %v", result.IP, result.Error)
}
}
}
}

func NewResultWorkerPool(size int, resultInput chan types.Result, outputChannel chan string) *ResultWorkerPool {
rwp := &ResultWorkerPool{
workers: make([]*ResultWorker, size),
resultInput: resultInput,
outputChannel: outputChannel,
}
for i := range rwp.workers {
rwp.workers[i] = NewResultWorker(rwp.resultInput, rwp.outputChannel)
}
return rwp
}

func (rwp *ResultWorkerPool) Start(args types.ScrapeArgs) {
for _, worker := range rwp.workers {
go worker.Run(args)
}
}

func (rwp *ResultWorkerPool) Stop() {
close(rwp.resultInput)
}

0 comments on commit d6bd7f9

Please sign in to comment.