github.com/chainreactors/fingers@v1.2.1/nmap/type-match.go (about)

     1  package gonmap
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"regexp"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/chainreactors/fingers/common"
    11  	"github.com/dlclark/regexp2"
    12  )
    13  
    14  type Match struct {
    15  	//match <Service> <pattern> <patternopt> [<versioninfo>]
    16  	Soft          bool            `json:"soft"`
    17  	Service       string          `json:"service"`
    18  	Pattern       string          `json:"pattern"`
    19  	PatternRegexp *regexp2.Regexp `json:"-"` // 不序列化正则对象
    20  	VersionInfo   *FingerPrint    `json:"version_info,omitempty"`
    21  }
    22  
    23  var matchLoadRegexps = []*regexp.Regexp{
    24  	regexp.MustCompile("^([a-zA-Z0-9-_./]+) m\\|([^|]+)\\|([is]{0,2})(?: (.*))?$"),
    25  	regexp.MustCompile("^([a-zA-Z0-9-_./]+) m=([^=]+)=([is]{0,2})(?: (.*))?$"),
    26  	regexp.MustCompile("^([a-zA-Z0-9-_./]+) m%([^%]+)%([is]{0,2})(?: (.*))?$"),
    27  	regexp.MustCompile("^([a-zA-Z0-9-_./]+) m@([^@]+)@([is]{0,2})(?: (.*))?$"),
    28  }
    29  
    30  var matchVersionInfoRegexps = map[string]*regexp.Regexp{
    31  	"PRODUCTNAME": regexp.MustCompile("p/([^/]+)/"),
    32  	"VERSION":     regexp.MustCompile("v/([^/]+)/"),
    33  	"INFO":        regexp.MustCompile("i/([^/]+)/"),
    34  	"HOSTNAME":    regexp.MustCompile("h/([^/]+)/"),
    35  	"OS":          regexp.MustCompile("o/([^/]+)/"),
    36  	"DEVICE":      regexp.MustCompile("d/([^/]+)/"),
    37  }
    38  
    39  // CPE解析正则表达式,匹配 cpe:/ 格式的CPE条目
    40  var matchCPERegexp = regexp.MustCompile(`cpe:/[^/\s]+/[^/\s]+/[^/\s\)]*(?:/[^/\s\)]*)*`)
    41  
    42  var matchVersionInfoHelperRegxP = regexp.MustCompile(`\$P\((\d)\)`)
    43  var matchVersionInfoHelperRegx = regexp.MustCompile(`\$(\d)`)
    44  
    45  func parseMatch(s string, soft bool) *Match {
    46  	var m = &Match{}
    47  	var regx *regexp.Regexp
    48  
    49  	for _, r := range matchLoadRegexps {
    50  		if r.MatchString(s) {
    51  			regx = r
    52  		}
    53  	}
    54  
    55  	if regx == nil {
    56  		panic(errors.New("match 语句参数不正确"))
    57  	}
    58  
    59  	args := regx.FindStringSubmatch(s)
    60  	m.Soft = soft
    61  	m.Service = args[1]
    62  	m.Service = FixProtocol(m.Service)
    63  	m.Pattern = args[2]
    64  	m.PatternRegexp = m.getPatternRegexp(m.Pattern, args[3])
    65  	m.VersionInfo = &FingerPrint{
    66  		ProbeName:        "",
    67  		MatchRegexString: "",
    68  		Service:          m.Service,
    69  		ProductName:      m.getVersionInfo(s, "PRODUCTNAME"),
    70  		Version:          m.getVersionInfo(s, "VERSION"),
    71  		Info:             m.getVersionInfo(s, "INFO"),
    72  		Hostname:         m.getVersionInfo(s, "HOSTNAME"),
    73  		OperatingSystem:  m.getVersionInfo(s, "OS"),
    74  		DeviceType:       m.getVersionInfo(s, "DEVICE"),
    75  		CPEs:             m.getCPEInfo(s),
    76  		CPEAttributes:    m.getCPEAttributes(s),
    77  	}
    78  	return m
    79  }
    80  
    81  func (m *Match) getPatternRegexp(pattern string, opt string) *regexp2.Regexp {
    82  	pattern = strings.ReplaceAll(pattern, `\0`, `\x00`)
    83  	if opt != "" {
    84  		if strings.Contains(opt, "i") == false {
    85  			opt += "i"
    86  		}
    87  		if pattern[:1] == "^" {
    88  			pattern = fmt.Sprintf("^(?%s:%s", opt, pattern[1:])
    89  		} else {
    90  			pattern = fmt.Sprintf("(?%s:%s", opt, pattern)
    91  		}
    92  		if pattern[len(pattern)-1:] == "$" {
    93  			pattern = fmt.Sprintf("%s)$", pattern[:len(pattern)-1])
    94  		} else {
    95  			pattern = fmt.Sprintf("%s)", pattern)
    96  		}
    97  	}
    98  	//pattern = regexp.MustCompile(`\\x[89a-f][0-9a-f]`).ReplaceAllString(pattern,".")
    99  	regex, err := regexp2.Compile(pattern, regexp2.None)
   100  	if err != nil {
   101  		panic(err)
   102  	}
   103  	return regex
   104  }
   105  
   106  func (m *Match) getVersionInfo(s string, regID string) string {
   107  	if matchVersionInfoRegexps[regID].MatchString(s) {
   108  		return matchVersionInfoRegexps[regID].FindStringSubmatch(s)[1]
   109  	} else {
   110  		return ""
   111  	}
   112  }
   113  
   114  // getCPEInfo 提取match语句中的所有CPE条目
   115  func (m *Match) getCPEInfo(s string) []string {
   116  	return matchCPERegexp.FindAllString(s, -1)
   117  }
   118  
   119  // getCPEAttributes 解析CPE条目为Attributes结构体
   120  func (m *Match) getCPEAttributes(s string) []*common.Attributes {
   121  	cpeStrings := m.getCPEInfo(s)
   122  	var attributes []*common.Attributes
   123  
   124  	for _, cpeStr := range cpeStrings {
   125  		attr := common.NewAttributesWithCPE(cpeStr)
   126  		if attr != nil {
   127  			attributes = append(attributes, attr)
   128  		}
   129  	}
   130  
   131  	return attributes
   132  }
   133  
   134  func (m *Match) makeVersionInfo(s string, f *FingerPrint) {
   135  	f.Info = m.makeVersionInfoSubHelper(s, m.VersionInfo.Info)
   136  	f.DeviceType = m.makeVersionInfoSubHelper(s, m.VersionInfo.DeviceType)
   137  	f.Hostname = m.makeVersionInfoSubHelper(s, m.VersionInfo.Hostname)
   138  	f.OperatingSystem = m.makeVersionInfoSubHelper(s, m.VersionInfo.OperatingSystem)
   139  	f.ProductName = m.makeVersionInfoSubHelper(s, m.VersionInfo.ProductName)
   140  	f.Version = m.makeVersionInfoSubHelper(s, m.VersionInfo.Version)
   141  	f.Service = m.makeVersionInfoSubHelper(s, m.VersionInfo.Service)
   142  
   143  	// 处理CPE信息,支持变量替换
   144  	f.CPEs = m.makeVersionInfoCPEHelper(s, m.VersionInfo.CPEs)
   145  	f.CPEAttributes = m.makeVersionInfoCPEAttributesHelper(s, f.CPEs)
   146  }
   147  
   148  func (m *Match) makeVersionInfoSubHelper(s string, pattern string) string {
   149  	match, _ := m.PatternRegexp.FindStringMatch(s)
   150  	if match == nil {
   151  		return pattern
   152  	}
   153  
   154  	// 构建匹配组数组
   155  	var sArr []string
   156  	sArr = append(sArr, match.String()) // 完整匹配
   157  	for i := 1; i < match.GroupCount(); i++ {
   158  		group := match.GroupByNumber(i)
   159  		if group != nil {
   160  			sArr = append(sArr, group.String())
   161  		} else {
   162  			sArr = append(sArr, "")
   163  		}
   164  	}
   165  
   166  	if len(sArr) == 1 {
   167  		return pattern
   168  	}
   169  	if pattern == "" {
   170  		return pattern
   171  	}
   172  
   173  	if matchVersionInfoHelperRegxP.MatchString(pattern) {
   174  		pattern = matchVersionInfoHelperRegxP.ReplaceAllStringFunc(pattern, func(repl string) string {
   175  			a := matchVersionInfoHelperRegxP.FindStringSubmatch(repl)[1]
   176  			return "$" + a
   177  		})
   178  	}
   179  
   180  	if matchVersionInfoHelperRegx.MatchString(pattern) {
   181  		pattern = matchVersionInfoHelperRegx.ReplaceAllStringFunc(pattern, func(repl string) string {
   182  			i, _ := strconv.Atoi(matchVersionInfoHelperRegx.FindStringSubmatch(repl)[1])
   183  			return sArr[i]
   184  		})
   185  	}
   186  	pattern = strings.ReplaceAll(pattern, "\n", "")
   187  	pattern = strings.ReplaceAll(pattern, "\r", "")
   188  	return pattern
   189  }
   190  
   191  // makeVersionInfoCPEHelper 处理CPE列表,支持变量替换
   192  func (m *Match) makeVersionInfoCPEHelper(s string, cpePatterns []string) []string {
   193  	var processedCPEs []string
   194  
   195  	for _, cpePattern := range cpePatterns {
   196  		processedCPE := m.makeVersionInfoSubHelper(s, cpePattern)
   197  		if processedCPE != "" {
   198  			processedCPEs = append(processedCPEs, processedCPE)
   199  		}
   200  	}
   201  
   202  	return processedCPEs
   203  }
   204  
   205  // makeVersionInfoCPEAttributesHelper 将处理后的CPE字符串转换为Attributes
   206  func (m *Match) makeVersionInfoCPEAttributesHelper(s string, cpeStrings []string) []*common.Attributes {
   207  	var attributes []*common.Attributes
   208  
   209  	for _, cpeStr := range cpeStrings {
   210  		attr := common.NewAttributesWithCPE(cpeStr)
   211  		if attr != nil {
   212  			attributes = append(attributes, attr)
   213  		}
   214  	}
   215  
   216  	return attributes
   217  }