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 }