github.com/chainreactors/fingers@v1.2.1/wappalyzer/wappalyzer.go (about)

     1  package wappalyzer
     2  
     3  import (
     4  	"bytes"
     5  	"github.com/chainreactors/fingers/common"
     6  	"github.com/chainreactors/fingers/resources"
     7  	"github.com/chainreactors/utils/httputils"
     8  	"strings"
     9  )
    10  
    11  // Wappalyze is a client for working with tech detection
    12  type Wappalyze struct {
    13  	fingerprints *CompiledFingerprints
    14  }
    15  
    16  // NewWappalyzeEngine creates a new tech detection instance
    17  func NewWappalyzeEngine(data []byte) (*Wappalyze, error) {
    18  	wappalyze := &Wappalyze{
    19  		fingerprints: &CompiledFingerprints{
    20  			Apps: make(map[string]*CompiledFingerprint),
    21  		},
    22  	}
    23  
    24  	err := wappalyze.loadFingerprints(data)
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  
    29  	err = wappalyze.Compile()
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	return wappalyze, nil
    34  }
    35  
    36  func (engine *Wappalyze) Name() string {
    37  	return "wappalyzer"
    38  }
    39  
    40  func (engine *Wappalyze) Len() int {
    41  	return len(engine.fingerprints.Apps)
    42  }
    43  
    44  func (engine *Wappalyze) Compile() error {
    45  	return nil
    46  }
    47  
    48  // loadFingerprints loads the fingerprints and compiles them
    49  func (engine *Wappalyze) loadFingerprints(data []byte) error {
    50  	var fingerprintsStruct Fingerprints
    51  	err := resources.UnmarshalData(data, &fingerprintsStruct)
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	for app, fingerprint := range fingerprintsStruct.Apps {
    57  		engine.fingerprints.Apps[app] = compileFingerprint(app, fingerprint)
    58  	}
    59  	return nil
    60  }
    61  
    62  // WebMatch 实现Web指纹匹配
    63  func (engine *Wappalyze) WebMatch(content []byte) common.Frameworks {
    64  	resp := httputils.NewResponseWithRaw(content)
    65  	if resp != nil {
    66  		return engine.Fingerprint(resp.Header, httputils.ReadBody(resp))
    67  	}
    68  	return make(common.Frameworks)
    69  }
    70  
    71  // ServiceMatch 实现Service指纹匹配 - wappalyzer不支持Service指纹
    72  func (engine *Wappalyze) ServiceMatch(host string, portStr string, level int, sender common.ServiceSender, callback common.ServiceCallback) *common.ServiceResult {
    73  	// wappalyzer不支持Service指纹识别
    74  	return nil
    75  }
    76  
    77  func (engine *Wappalyze) Capability() common.EngineCapability {
    78  	return common.EngineCapability{
    79  		SupportWeb:     true,  // wappalyzer支持Web指纹
    80  		SupportService: false, // wappalyzer不支持Service指纹
    81  	}
    82  }
    83  
    84  // Fingerprint identifies technologies on a target,
    85  // based on the received response headers and body.
    86  //
    87  // Body should not be mutated while this function is being called, or it may
    88  // lead to unexpected things.
    89  func (engine *Wappalyze) Fingerprint(headers map[string][]string, body []byte) common.Frameworks {
    90  	uniqueFingerprints := make(common.Frameworks)
    91  
    92  	// Lowercase everything that we have received to check
    93  	normalizedBody := bytes.ToLower(body)
    94  	normalizedHeaders := engine.normalizeHeaders(headers)
    95  
    96  	// Run header based fingerprinting if the number
    97  	// of header checks if more than 0.
    98  	uniqueFingerprints.Merge(engine.checkHeaders(normalizedHeaders))
    99  
   100  	cookies := engine.findSetCookie(normalizedHeaders)
   101  	// Run cookie based fingerprinting if we have a set-cookie header
   102  	if len(cookies) > 0 {
   103  		uniqueFingerprints.Merge(engine.checkCookies(cookies))
   104  	}
   105  
   106  	// Check for stuff in the body finally
   107  	uniqueFingerprints.Merge(engine.checkBody(normalizedBody))
   108  	return uniqueFingerprints
   109  }
   110  
   111  // FingerprintWithTitle identifies technologies on a target,
   112  // based on the received response headers and body.
   113  // It also returns the title of the page.
   114  //
   115  // Body should not be mutated while this function is being called, or it may
   116  // lead to unexpected things.
   117  func (engine *Wappalyze) FingerprintWithTitle(headers map[string][]string, body []byte) (common.Frameworks, string) {
   118  	uniqueFingerprints := make(common.Frameworks)
   119  
   120  	// Lowercase everything that we have received to check
   121  	normalizedBody := bytes.ToLower(body)
   122  	normalizedHeaders := engine.normalizeHeaders(headers)
   123  
   124  	// Run header based fingerprinting if the number
   125  	// of header checks if more than 0.
   126  
   127  	uniqueFingerprints.Merge(engine.checkHeaders(normalizedHeaders))
   128  
   129  	cookies := engine.findSetCookie(normalizedHeaders)
   130  	// Run cookie based fingerprinting if we have a set-cookie header
   131  	if len(cookies) > 0 {
   132  		uniqueFingerprints.Merge(engine.checkCookies(cookies))
   133  	}
   134  
   135  	// Check for stuff in the body finally
   136  	if strings.Contains(normalizedHeaders["content-type"], "text/html") {
   137  		bodyTech := engine.checkBody(normalizedBody)
   138  		uniqueFingerprints.Merge(bodyTech)
   139  		title := engine.getTitle(body)
   140  		return uniqueFingerprints, title
   141  	}
   142  	return uniqueFingerprints, ""
   143  }
   144  
   145  // FingerprintWithInfo identifies technologies on a target,
   146  // based on the received response headers and body.
   147  // It also returns basic information about the technology, such as description
   148  // and website URL.
   149  //
   150  // Body should not be mutated while this function is being called, or it may
   151  // lead to unexpected things.
   152  func (engine *Wappalyze) FingerprintWithInfo(headers map[string][]string, body []byte) map[string]AppInfo {
   153  	apps := engine.Fingerprint(headers, body)
   154  	result := make(map[string]AppInfo, len(apps))
   155  
   156  	for app := range apps {
   157  		if fingerprint, ok := engine.fingerprints.Apps[app]; ok {
   158  			result[app] = AppInfo{
   159  				Description: fingerprint.description,
   160  				Website:     fingerprint.website,
   161  				CPE:         fingerprint.cpe,
   162  			}
   163  		}
   164  	}
   165  
   166  	return result
   167  }
   168  
   169  // FingerprintWithCats identifies technologies on a target,
   170  // based on the received response headers and body.
   171  // It also returns categories information about the technology, is there's any
   172  // Body should not be mutated while this function is being called, or it may
   173  // lead to unexpected things.
   174  func (engine *Wappalyze) FingerprintWithCats(headers map[string][]string, body []byte) map[string]CatsInfo {
   175  	apps := engine.Fingerprint(headers, body)
   176  	result := make(map[string]CatsInfo, len(apps))
   177  
   178  	for app := range apps {
   179  		if fingerprint, ok := engine.fingerprints.Apps[app]; ok {
   180  			result[app] = CatsInfo{
   181  				Cats: fingerprint.cats,
   182  			}
   183  		}
   184  	}
   185  
   186  	return result
   187  }