yellow~ opinionated web scanning pipeline for pentesters in a hurry |
| git clone https://github.com/djnnvx/yellow.git |
| log | files | refs | archive | README | LICENSE |
alterx.go (2807B)
1 package osint 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "sync" 8 "time" 9 10 "evil.djnn.sh/djnn/yellow/core" 11 "github.com/projectdiscovery/alterx" 12 "github.com/projectdiscovery/dnsx/libs/dnsx" 13 ) 14 15 const ( 16 alterxLimit = 5000 17 alterxWorkers = 40 18 alterxBudget = 5 * time.Minute 19 ) 20 21 type Alterx struct{} 22 23 func (*Alterx) Name() string { return "alterx" } 24 25 func (*Alterx) Run(ctx *core.Context) error { 26 fmt.Println("[+] Running alterx on", ctx.Domain) 27 if ctx.DryRun { 28 return nil 29 } 30 31 var inputs []string 32 for _, d := range ctx.Domains { 33 if d != "" && net.ParseIP(d) == nil { 34 inputs = append(inputs, d) 35 } 36 } 37 if len(inputs) == 0 { 38 fmt.Println("[!] alterx: no subdomains to permute, skipping") 39 return nil 40 } 41 42 mutator, err := alterx.New(&alterx.Options{Domains: inputs, Enrich: true}) 43 if err != nil { 44 fmt.Printf("[!] alterx: %v\n", err) 45 return nil 46 } 47 48 // Execute ignores Options.Limit; cap here, draining fully so the producer can't leak 49 var candidates []string 50 for c := range mutator.Execute(context.Background()) { 51 if len(candidates) < alterxLimit { 52 candidates = append(candidates, c) 53 } 54 } 55 if len(candidates) == 0 { 56 fmt.Println("[!] alterx: generated no candidates, skipping") 57 return nil 58 } 59 60 live := resolveLive(candidates) 61 62 existing := map[string]struct{}{} 63 for _, d := range ctx.Domains { 64 existing[d] = struct{}{} 65 } 66 var added []string 67 for _, h := range live { 68 if _, ok := existing[h]; ok { 69 continue 70 } 71 existing[h] = struct{}{} 72 added = append(added, h) 73 } 74 ctx.Domains = append(ctx.Domains, added...) 75 76 outfile := fmt.Sprintf("%s/alterx.txt", ctx.ScanPath) 77 if err := core.WriteLines(outfile, added); err != nil { 78 fmt.Printf("[!] alterx: could not write %s: %v\n", outfile, err) 79 } 80 81 fmt.Printf("[OSINT %s] alterx: %d candidates, %d live, %d new in %s\n", ctx.Domain, len(candidates), len(live), len(added), outfile) 82 return nil 83 } 84 85 func resolveLive(candidates []string) []string { 86 opts := dnsx.DefaultOptions 87 opts.MaxRetries = 2 88 client, err := dnsx.New(opts) 89 if err != nil { 90 fmt.Printf("[!] alterx: dns resolver init failed: %v\n", err) 91 return nil 92 } 93 94 ctx, cancel := context.WithTimeout(context.Background(), alterxBudget) 95 defer cancel() 96 97 jobs := make(chan string) 98 var ( 99 mu sync.Mutex 100 live []string 101 wg sync.WaitGroup 102 ) 103 for i := 0; i < alterxWorkers; i++ { 104 wg.Add(1) 105 go func() { 106 defer wg.Done() 107 for { 108 select { 109 case <-ctx.Done(): 110 return 111 case name, ok := <-jobs: 112 if !ok { 113 return 114 } 115 if ips, err := client.Lookup(name); err == nil && len(ips) > 0 { 116 mu.Lock() 117 live = append(live, name) 118 mu.Unlock() 119 } 120 } 121 } 122 }() 123 } 124 dispatch: 125 for _, c := range candidates { 126 select { 127 case <-ctx.Done(): 128 break dispatch 129 case jobs <- c: 130 } 131 } 132 close(jobs) 133 wg.Wait() 134 return live 135 }