yellow~ opinionated web scanning pipeline for pentesters in a hurry |
| git clone https://github.com/djnnvx/yellow.git |
| log | files | refs | archive | README | LICENSE |
wappalyzergo.go (3493B)
1 package scan 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "os" 8 "reflect" 9 "sort" 10 11 "evil.djnn.sh/djnn/yellow/core" 12 helper "evil.djnn.sh/djnn/yellow/helpers" 13 wappalyzer "github.com/projectdiscovery/wappalyzergo" 14 ) 15 16 type WappalyzerGo struct{} 17 18 func (*WappalyzerGo) Name() string { return "wappalyzergo" } 19 20 func (d *WappalyzerGo) Run(ctx *core.Context) error { 21 fmt.Println("[+] Running WappalyzerGo on", ctx.Domain) 22 if ctx.DryRun { 23 return nil 24 } 25 26 targetURL := ctx.Domain 27 client := d.newHTTPClient() 28 29 resp, body, err := d.fetchResponse(client, targetURL) 30 if err != nil { 31 fmt.Printf("error fetching %s: %v\n", targetURL, err) 32 fmt.Printf("[SCAN %s] WappalyzerGo scan for %s completed\n\n", targetURL, targetURL) 33 return nil 34 } 35 if resp != nil { 36 defer resp.Body.Close() 37 } 38 39 results, err := d.fingerprint(resp, body) 40 if err != nil { 41 fmt.Printf("error fingerprinting %s: %v\n", targetURL, err) 42 fmt.Printf("[SCAN %s] WappalyzerGo scan for %s completed\n\n", targetURL, targetURL) 43 return nil 44 } 45 46 names := techNamesFromResults(results) 47 filename, err := d.saveNames(names, ctx.ScanPath) 48 if err != nil { 49 fmt.Printf("error saving results for %s: %v\n", targetURL, err) 50 } else { 51 fmt.Printf("[SCAN %s] WappalyzerGo results are stored in %s\n", targetURL, filename) 52 } 53 54 fmt.Printf("%v\n", names) 55 fmt.Printf("[SCAN %s] WappalyzerGo scan for %s completed\n\n", targetURL, targetURL) 56 57 ctx.Techs = names 58 return nil 59 } 60 61 func (d *WappalyzerGo) newHTTPClient() *http.Client { 62 return helper.GetHttpClient(true) 63 } 64 65 func (d *WappalyzerGo) fetchResponse(client *http.Client, targetURL string) (*http.Response, []byte, error) { 66 req, err := http.NewRequest("GET", targetURL, nil) 67 if err != nil { 68 return nil, nil, fmt.Errorf("creating request: %w", err) 69 } 70 req.Header.Add("User-Agent", helper.GetUserAgent()) 71 72 resp, err := client.Do(req) 73 if err != nil { 74 return resp, nil, fmt.Errorf("performing request: %w", err) 75 } 76 77 if resp == nil { 78 return nil, nil, fmt.Errorf("no response received") 79 } 80 81 body, _, err := helper.ReadCappedBody(resp.Body) 82 if err != nil { 83 return resp, nil, fmt.Errorf("reading response body: %w", err) 84 } 85 86 return resp, body, nil 87 } 88 89 func (d *WappalyzerGo) fingerprint(resp *http.Response, body []byte) (any, error) { 90 wappalyzerClient, err := wappalyzer.New() 91 if err != nil { 92 return nil, fmt.Errorf("initializing wappalyzer client: %w", err) 93 } 94 95 results := wappalyzerClient.Fingerprint(resp.Header, body) 96 return results, nil 97 } 98 99 func (d *WappalyzerGo) saveNames(names []string, scanPath string) (string, error) { 100 data, err := json.MarshalIndent(names, "", "\t") 101 if err != nil { 102 return "", fmt.Errorf("marshal names: %w", err) 103 } 104 105 filename := scanPath + "/wappalyzer.json" 106 if err := os.WriteFile(filename, data, 0644); err != nil { 107 return "", fmt.Errorf("write file %s: %w", filename, err) 108 } 109 110 return filename, nil 111 } 112 113 func techNamesFromResults(results any) []string { 114 if s, ok := results.([]string); ok { 115 out := make([]string, len(s)) 116 copy(out, s) 117 sort.Strings(out) 118 return out 119 } 120 121 if si, ok := results.([]interface{}); ok { 122 out := make([]string, 0, len(si)) 123 for _, v := range si { 124 out = append(out, fmt.Sprint(v)) 125 } 126 sort.Strings(out) 127 return out 128 } 129 130 val := reflect.ValueOf(results) 131 if val.Kind() == reflect.Map { 132 out := make([]string, 0, val.Len()) 133 for _, k := range val.MapKeys() { 134 out = append(out, fmt.Sprint(k.Interface())) 135 } 136 sort.Strings(out) 137 return out 138 } 139 140 return []string{fmt.Sprint(results)} 141 }