Skip to content

Commit

Permalink
[IMP]upgrade CPE mod dependence, modify json format, pass struct poin…
Browse files Browse the repository at this point in the history
…ter instead
  • Loading branch information
randolphcyg committed Apr 3, 2023
1 parent e98c376 commit beaaca5
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 78 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
# vendor/

# do not upload convert result file
.json
*.json
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
func main() {
readable := true
srcFilePath := "nmap-service-probes"
probes, err := parser.ParseNmap(srcFilePath)
probes, err := parser.ParseNmapServiceProbe(srcFilePath)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -88,7 +88,7 @@ var (
ErrRecRsp = errors.New("Error receiving response")
)

func ServiceDetect(host string, port int, probe parser.Probe) (serviceName string, info parser.VInfo, err error) {
func ServiceDetect(host string, port int, probe *parser.Probe) (serviceName string, info *parser.VInfo, err error) {
addr := net.JoinHostPort(host, strconv.Itoa(port))
conn, err := net.Dial(strings.ToLower(probe.Protocol), addr)
if err != nil {
Expand Down Expand Up @@ -150,7 +150,7 @@ func ServiceDetect(host string, port int, probe parser.Probe) (serviceName strin

func main() {
srcFilePath := "nmap-service-probes"
probes, err := parser.ParseNmap(srcFilePath)
probes, err := parser.ParseNmapServiceProbe(srcFilePath)
if err != nil {
panic(err)
}
Expand All @@ -159,21 +159,21 @@ func main() {
port := 6379

serviceName := ""
info := parser.VInfo{}
info := parser.NewVInfo()
for _, probe := range probes {
serviceNameTmp, infoTmp, err := ServiceDetect(host, port, probe)
if err != nil {
continue
}

if serviceNameTmp != "" && !infoTmp.IsVInfoEmpty() {
if serviceNameTmp != "" && !infoTmp.IsEmpty() {
serviceName = serviceNameTmp
info = infoTmp
}

}

if serviceName != "" && !info.IsVInfoEmpty() {
if serviceName != "" && !info.IsEmpty() {
fmt.Println(serviceName, info)
} else {
fmt.Println("no match service!")
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.20

require (
github.com/pkg/errors v0.9.1
github.com/randolphcyg/cpe v1.0.4
github.com/randolphcyg/cpe v1.0.6
github.com/stretchr/testify v1.8.2
)

Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ github.com/randolphcyg/cpe v1.0.3 h1:Gy91SlIWvPPqIMF8gDq3tnhde6noO/AyKeeyLHp/tio
github.com/randolphcyg/cpe v1.0.3/go.mod h1:R+J264JmgNtHkgKkYS12TQNgp819+OnwFUJuZUkoyio=
github.com/randolphcyg/cpe v1.0.4 h1:yt8reQ+oLlw4bFAcRk9t2zLGE1/11RptF0AL0XT7s2s=
github.com/randolphcyg/cpe v1.0.4/go.mod h1:R+J264JmgNtHkgKkYS12TQNgp819+OnwFUJuZUkoyio=
github.com/randolphcyg/cpe v1.0.5 h1:WNIKWlLI4uFPLRbSiuPsS8MH4QBJC+kePdTSuZ9Ppds=
github.com/randolphcyg/cpe v1.0.5/go.mod h1:R+J264JmgNtHkgKkYS12TQNgp819+OnwFUJuZUkoyio=
github.com/randolphcyg/cpe v1.0.6 h1:6/jVTznDzniah986/31eGAy+STpkObmeSZ2wEQrtKP0=
github.com/randolphcyg/cpe v1.0.6/go.mod h1:R+J264JmgNtHkgKkYS12TQNgp819+OnwFUJuZUkoyio=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
Expand Down
131 changes: 70 additions & 61 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,65 @@ import (
"github.com/randolphcyg/cpe"
)

// VInfo version info, include six optional fields and CPE
type VInfo struct {
VendorProductName string `json:"vendorProductName"`
Version string `json:"version"`
Info string `json:"info"`
Hostname string `json:"hostname"`
OperatingSystem string `json:"operatingSystem"`
DeviceType string `json:"deviceType"`
Cpe []cpe.CPE `json:"cpe"`
type IProbe interface {
IsEmpty() bool
}

type Match struct {
Pattern string `json:"pattern"`
Name string `json:"name"`
PatternFlag string `json:"patternFlag"`
VersionInfo VInfo `json:"versionInfo"`
type IParser interface {
IsEmpty() bool
}

// Probe nmap service probe
type Probe struct {
Protocol string `json:"protocol"`
ProbeName string `json:"probeName"`
ProbeString string `json:"probeString"`
Ports []string `json:"ports"`
SslPorts []string `json:"sslPorts"`
TcpWrappedMs string `json:"tcpWrappedMs"`
TotalWaitMs string `json:"totalWaitMs"`
Rarity string `json:"rarity"`
Fallback string `json:"fallback"`
Matches []Match `json:"matches"`
ProbeString string `json:"probeString,omitempty"`
Ports []string `json:"ports,omitempty"`
SslPorts []string `json:"sslPorts,omitempty"`
TcpWrappedMs string `json:"tcpWrappedMs,omitempty"`
TotalWaitMs string `json:"totalWaitMs,omitempty"`
Rarity string `json:"rarity,omitempty"`
Fallback string `json:"fallback,omitempty"`
Matches []*Match `json:"matches"`
}

// Match nmap service probe match rule
type Match struct {
Pattern string `json:"pattern"`
Name string `json:"name"`
PatternFlag string `json:"patternFlag,omitempty"`
VersionInfo *VInfo `json:"versionInfo,omitempty"`
}

func (x Probe) IsProbeEmpty() bool {
return reflect.DeepEqual(x, Probe{})
// VInfo version info, include six optional fields and CPE
type VInfo struct {
VendorProductName string `json:"vendorProductName,omitempty"`
Version string `json:"version,omitempty"`
Info string `json:"info,omitempty"`
Hostname string `json:"hostname,omitempty"`
OperatingSystem string `json:"operatingSystem,omitempty"`
DeviceType string `json:"deviceType,omitempty"`
Cpe []*cpe.CPE `json:"cpe,omitempty"`
}

func (v VInfo) IsVInfoEmpty() bool {
return reflect.DeepEqual(v, VInfo{})
func NewProbe() *Probe {
return &Probe{}
}

func (x *Probe) IsEmpty() bool {
return reflect.DeepEqual(x, &Probe{})
}

func NewMatch() *Match {
return &Match{}
}

func NewVInfo() *VInfo {
return &VInfo{}
}

func (v *VInfo) IsEmpty() bool {
return reflect.DeepEqual(v, &VInfo{})
}

func handleVInfoField(src, flagStr string) (string, string, error) {
Expand All @@ -72,13 +94,12 @@ func handleVInfoField(src, flagStr string) (string, string, error) {
return ret, srcRet, nil
}

type fieldInfo struct {
field string
set func(string)
}

func HandleVInfo(src string) (vInfo VInfo, err error) {
fields := []fieldInfo{
func HandleVInfo(src string) (vInfo *VInfo, err error) {
vInfo = NewVInfo()
fields := []struct {
field string
set func(string)
}{
{"p/", func(v string) { vInfo.VendorProductName = v }},
{"v/", func(v string) { vInfo.Version = v }},
{"i/", func(v string) { vInfo.Info = v }},
Expand All @@ -102,13 +123,11 @@ func HandleVInfo(src string) (vInfo VInfo, err error) {

// CPE handle logic
cpeSrcStr := ""
cpeFlag := "cpe:/"
isCpe22FlagInSrc := strings.Index(src, cpeFlag)
isCpe22FlagInSrc := strings.Index(src, cpe.FlagCpe22)
if isCpe22FlagInSrc != -1 {
cpeSrcStr = src[isCpe22FlagInSrc : len(src)-1]
} else {
cpeFlag = "cpe:2.3"
isCpe23FlagInSrc := strings.LastIndex(src, cpeFlag)
isCpe23FlagInSrc := strings.LastIndex(src, cpe.FlagCpe23)
if isCpe23FlagInSrc != -1 {
cpeSrcStr = src[isCpe23FlagInSrc : len(src)-1]
}
Expand All @@ -121,14 +140,15 @@ func HandleVInfo(src string) (vInfo VInfo, err error) {
if err != nil {
continue
}
vInfo.Cpe = append(vInfo.Cpe, *cRet)
vInfo.Cpe = append(vInfo.Cpe, cRet)
}
}

return
}

func ParseMatch(line string) (m Match, err error) {
func ParseMatch(line string) (m *Match, err error) {
m = NewMatch()
line = strings.TrimSpace(line)
line = strings.Replace(line, "\n", "", -1)
matchSeg := strings.SplitN(line, " ", 3)
Expand All @@ -137,7 +157,7 @@ func ParseMatch(line string) (m Match, err error) {
pattern := regxSeg[1]

patternFlag := ""
var versionInfo VInfo
versionInfo := NewVInfo()
if len(regxSeg) >= 3 {
versionInfoSeg := strings.SplitN(regxSeg[2], " ", 2)

Expand All @@ -148,7 +168,7 @@ func ParseMatch(line string) (m Match, err error) {
if len(versionInfoSeg) >= 2 {
tmp, errVInfo := HandleVInfo(versionInfoSeg[1])
if err != nil {
m = Match{
m = &Match{
Pattern: pattern,
Name: name,
PatternFlag: patternFlag,
Expand All @@ -160,7 +180,7 @@ func ParseMatch(line string) (m Match, err error) {
}
}

m = Match{
m = &Match{
Pattern: pattern,
Name: name,
PatternFlag: patternFlag,
Expand All @@ -170,18 +190,18 @@ func ParseMatch(line string) (m Match, err error) {
return
}

func ParseNmap(srcFilePath string) (probes []Probe, err error) {
func ParseNmapServiceProbe(srcFilePath string) (probes []*Probe, err error) {
// Open the nmap-service-probes file
file, err := os.Open(srcFilePath)
if err != nil {
return
}
defer file.Close()

probes = make([]Probe, 0, 200)
probes = make([]*Probe, 0, 200)

// Create an empty probe to hold current probe being parsed
var currentProbe Probe
currentProbe := NewProbe()

// Create a scanner to read the file line by line; Loop through each line of the file
for scanner := bufio.NewScanner(file); scanner.Scan(); {
Expand All @@ -201,18 +221,7 @@ func ParseNmap(srcFilePath string) (probes []Probe, err error) {
probes = append(probes, currentProbe)
}
// Create a new probe with the name and default values
currentProbe = Probe{
ProbeName: "",
Protocol: "",
ProbeString: "",
Ports: nil,
SslPorts: nil,
TcpWrappedMs: "",
TotalWaitMs: "",
Rarity: "",
Fallback: "",
Matches: nil,
}
currentProbe = NewProbe()

lineSeg := strings.SplitN(line, " ", 4)
if lineSeg[1] != "TCP" && lineSeg[1] != "UDP" { // unsupported protocol
Expand Down Expand Up @@ -245,7 +254,7 @@ func ParseNmap(srcFilePath string) (probes []Probe, err error) {
}

// Append the last probe to the slice of probes
if currentProbe.IsProbeEmpty() {
if currentProbe.IsEmpty() {
return
}

Expand All @@ -266,9 +275,9 @@ func UnquoteRawString(rawStr string) (string, error) {
}

// FillVersionInfoFields Replace the versionInfo and CPE placeholder elements with the matched real values
func FillVersionInfoFields(src [][]byte, match Match) VInfo {
func FillVersionInfoFields(src [][]byte, match *Match) *VInfo {
versionInfo := match.VersionInfo
tmpVerInfo := VInfo{
tmpVerInfo := &VInfo{
VendorProductName: FillHelperFuncOrVariable(versionInfo.VendorProductName, src),
Version: FillHelperFuncOrVariable(versionInfo.Version, src),
Info: FillHelperFuncOrVariable(versionInfo.Info, src),
Expand All @@ -280,7 +289,7 @@ func FillVersionInfoFields(src [][]byte, match Match) VInfo {

if len(versionInfo.Cpe) > 0 {
for _, c := range versionInfo.Cpe {
tmpCPE := cpe.CPE{
tmpCPE := &cpe.CPE{
Version: FillHelperFuncOrVariable(c.Version, src),
Language: FillHelperFuncOrVariable(c.Language, src),
Vendor: FillHelperFuncOrVariable(c.Vendor, src),
Expand Down
13 changes: 9 additions & 4 deletions tests/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,19 @@ import (
"github.com/randolphcyg/nmap-parser"
)

func TestVInfoIsEmpty(t *testing.T) {
vInfo := parser.VInfo{}
assert.Equal(t, true, vInfo.IsEmpty())
}

func TestHandleVersionInfo(t *testing.T) {
_, err := parser.HandleVInfo("match activesync m|^.\\0\\x01\\0[^\\0]\\0[^\\0]\\0[^\\0]\\0[^\\0]\\0[^\\0]\\0.*\\0\\0\\0$|s p/Microsoft ActiveSync/ o/Windows/ cpe:/a:microsoft:activesync/ cpe:/o:microsoft:windows/a")
assert.ErrorIs(t, err, nil)
}

func TestParseNmap(t *testing.T) {
func TestParseNmapServiceProbe(t *testing.T) {
srcFilePath := "nmap-service-probes"
probes, err := parser.ParseNmap(srcFilePath)
probes, err := parser.ParseNmapServiceProbe(srcFilePath)
if err != nil {
panic(err)
}
Expand All @@ -30,10 +35,10 @@ func TestParseNmap(t *testing.T) {
fmt.Println(len(string(probesJSON)))
}

func TestParseNmapAndToJson(t *testing.T) {
func TestParseNmapServiceProbeToJson(t *testing.T) {
readable := true
srcFilePath := "nmap-service-probes"
probes, err := parser.ParseNmap(srcFilePath)
probes, err := parser.ParseNmapServiceProbe(srcFilePath)
if err != nil {
panic(err)
}
Expand Down
Loading

0 comments on commit beaaca5

Please sign in to comment.