Files
Doğan Can Bakır 81461d35e8 feat: add passive CPE and WordPress detection
Add support for passive detection of CPE (Common Platform Enumeration)
identifiers and WordPress plugins/themes using awesome-search-queries.

CPE Detection (-cpe flag):
- Matches response title, body, and favicon hash against patterns
- Extracts product, vendor, and generates CPE 2.3 identifiers
- Uses patterns from Shodan, FOFA, Google dorks

WordPress Detection (-wp flag):
- Detects plugins via /wp-content/plugins/[name]/ patterns
- Detects themes via /wp-content/themes/[name]/ patterns
- Validates against known plugins/themes list

New CLI flags in PROBES group:
- -cpe: display CPE based on awesome-search-queries
- -wp, -wordpress: display WordPress plugins and themes

Both are automatically included in JSON/CSV output.

Closes #1975
2026-01-06 13:22:43 +03:00

119 lines
2.7 KiB
Go

package runner
import (
"bufio"
"bytes"
"regexp"
"strings"
awesomesearchqueries "github.com/projectdiscovery/awesome-search-queries"
)
type WordPressInfo struct {
Plugins []string `json:"plugins,omitempty"`
Themes []string `json:"themes,omitempty"`
}
type WordPressDetector struct {
knownPlugins map[string]struct{}
knownThemes map[string]struct{}
pluginRegex *regexp.Regexp
themeRegex *regexp.Regexp
}
func NewWordPressDetector() (*WordPressDetector, error) {
detector := &WordPressDetector{
knownPlugins: make(map[string]struct{}),
knownThemes: make(map[string]struct{}),
}
var err error
detector.pluginRegex, err = regexp.Compile(`/wp-content/plugins/([a-zA-Z0-9_-]+)/`)
if err != nil {
return nil, err
}
detector.themeRegex, err = regexp.Compile(`/wp-content/themes/([a-zA-Z0-9_-]+)/`)
if err != nil {
return nil, err
}
pluginsData, err := awesomesearchqueries.GetWordPressPlugins()
if err != nil {
return nil, err
}
if err := detector.loadList(pluginsData, detector.knownPlugins); err != nil {
return nil, err
}
themesData, err := awesomesearchqueries.GetWordPressThemes()
if err != nil {
return nil, err
}
if err := detector.loadList(themesData, detector.knownThemes); err != nil {
return nil, err
}
return detector, nil
}
func (d *WordPressDetector) loadList(data []byte, target map[string]struct{}) error {
scanner := bufio.NewScanner(bytes.NewReader(data))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line != "" {
target[line] = struct{}{}
}
}
return scanner.Err()
}
func (d *WordPressDetector) Detect(body string) *WordPressInfo {
if body == "" {
return nil
}
info := &WordPressInfo{}
seenPlugins := make(map[string]struct{})
seenThemes := make(map[string]struct{})
if matches := d.pluginRegex.FindAllStringSubmatch(body, -1); len(matches) > 0 {
for _, match := range matches {
if len(match) > 1 {
plugin := match[1]
if _, seen := seenPlugins[plugin]; !seen {
if _, known := d.knownPlugins[plugin]; known {
info.Plugins = append(info.Plugins, plugin)
seenPlugins[plugin] = struct{}{}
}
}
}
}
}
if matches := d.themeRegex.FindAllStringSubmatch(body, -1); len(matches) > 0 {
for _, match := range matches {
if len(match) > 1 {
theme := match[1]
if _, seen := seenThemes[theme]; !seen {
if _, known := d.knownThemes[theme]; known {
info.Themes = append(info.Themes, theme)
seenThemes[theme] = struct{}{}
}
}
}
}
}
if len(info.Plugins) == 0 && len(info.Themes) == 0 {
return nil
}
return info
}
func (w *WordPressInfo) HasData() bool {
return w != nil && (len(w.Plugins) > 0 || len(w.Themes) > 0)
}