yellow.git


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 }