Skip to content

Commit

Permalink
⭐️ nmap discovery
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-rock committed Dec 14, 2024
1 parent b554b17 commit e46224e
Show file tree
Hide file tree
Showing 8 changed files with 347 additions and 87 deletions.
2 changes: 1 addition & 1 deletion providers/nmap/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Nmap provider
# Nmap Provider

Nmap, short for Network Mapper, is a powerful and versatile open-source tool used for network discovery and security auditing. This tool is widely utilized by network administrators, security professionals, and penetration testers to map out network structures, discover hosts, identify services, and detect vulnerabilities.

Expand Down
36 changes: 31 additions & 5 deletions providers/nmap/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package config

import (
"go.mondoo.com/cnquery/v11/providers-sdk/v1/inventory"
"go.mondoo.com/cnquery/v11/providers-sdk/v1/plugin"
"go.mondoo.com/cnquery/v11/providers/nmap/connection"
"go.mondoo.com/cnquery/v11/providers/nmap/provider"
)

Expand All @@ -15,11 +17,35 @@ var Config = plugin.Provider{
ConnectionTypes: []string{provider.DefaultConnectionType},
Connectors: []plugin.Connector{
{
Name: "nmap",
Use: "nmap",
Short: "a Nmap network scanner",
Discovery: []string{},
Flags: []plugin.Flag{},
Name: "nmap",
Use: "nmap",
Short: "a Nmap network scanner",
MinArgs: 0,
MaxArgs: 2,
Discovery: []string{
connection.DiscoveryAll,
connection.DiscoveryAuto,
connection.DiscoveryHosts,
},
Flags: []plugin.Flag{
{
Long: "networks",
Type: plugin.FlagType_List,
Default: "",
Desc: "Only include repositories with matching names",
},
},
},
},
AssetUrlTrees: []*inventory.AssetUrlBranch{
{
PathSegments: []string{"technology=network", "category=nmap"},
Key: "kind",
Title: "Kind",
Values: map[string]*inventory.AssetUrlBranch{
"host": nil,
"domain": nil,
},
},
},
}
80 changes: 76 additions & 4 deletions providers/nmap/connection/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,30 @@
package connection

import (
"strings"

"go.mondoo.com/cnquery/v11/providers-sdk/v1/inventory"
"go.mondoo.com/cnquery/v11/providers-sdk/v1/plugin"
)

const (
DiscoveryAll = "all"
DiscoveryAuto = "auto"
DiscoveryHosts = "hosts"
)

type NmapConnection struct {
plugin.Connection
Conf *inventory.Config
asset *inventory.Asset
Conf *inventory.Config
asset *inventory.Asset
// Add custom connection fields here
}

func NewNmapConnection(id uint32, asset *inventory.Asset, conf *inventory.Config) (*NmapConnection, error) {
conn := &NmapConnection{
Connection: plugin.NewConnection(id, asset),
Conf: conf,
asset: asset,
Conf: conf,
asset: asset,
}

// initialize your connection here
Expand All @@ -35,3 +43,67 @@ func (c *NmapConnection) Asset() *inventory.Asset {
return c.asset
}

func nmapHostPlatform() *inventory.Platform {
return &inventory.Platform{
Name: "nmap-host",
Title: "Nmap Host",
Family: []string{"nmap"},
Kind: "api",
Runtime: "nmap",
TechnologyUrlSegments: []string{"network", "nmap", "host"},
}
}

func nmapDomainPlatform() *inventory.Platform {
return &inventory.Platform{
Name: "nmap-domain",
Title: "Nmap Domain",
Family: []string{"nmap"},
Kind: "api",
Runtime: "nmap",
TechnologyUrlSegments: []string{"network", "nmap", "domain"},
}
}

func nmapPlatform() *inventory.Platform {
return &inventory.Platform{
Name: "nmap-org",
Title: "Nmap",
Family: []string{"nmap"},
Kind: "api",
Runtime: "nmap",
TechnologyUrlSegments: []string{"network", "nmap", "org"},
}
}

func (c *NmapConnection) PlatformInfo() (*inventory.Platform, error) {
conf := c.asset.Connections[0]

if conf.Options != nil && conf.Options["search"] != "" {
search := conf.Options["search"]
switch search {
case "host":
return nmapHostPlatform(), nil
case "domain":
return nmapDomainPlatform(), nil
}
}
return nmapPlatform(), nil
}

func (c *NmapConnection) Identifier() string {
baseId := "//platformid.api.mondoo.app/runtime/nmap"

conf := c.asset.Connections[0]
if conf.Options != nil && conf.Options["search"] != "" {
search := conf.Options["search"]
switch search {
case "host":
return baseId + "/host/" + strings.ToLower(conf.Host)
case "domain":
return baseId + "/domain/" + strings.ToLower(conf.Host)
}
}

return baseId
}
76 changes: 66 additions & 10 deletions providers/nmap/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package provider
import (
"context"
"errors"
"strings"

"go.mondoo.com/cnquery/v11/llx"
"go.mondoo.com/cnquery/v11/providers-sdk/v1/inventory"
Expand Down Expand Up @@ -40,9 +41,48 @@ func (s *Service) ParseCLI(req *plugin.ParseCLIReq) (*plugin.ParseCLIRes, error)
Options: map[string]string{},
}

// Do custom flag parsing here
// discovery flags
discoverTargets := []string{}
if x, ok := flags["discover"]; ok && len(x.Array) != 0 {
for i := range x.Array {
entry := string(x.Array[i].Value)
discoverTargets = append(discoverTargets, entry)
}
} else {
discoverTargets = []string{"auto"}
}
conf.Discover = &inventory.Discovery{Targets: discoverTargets}

// nmap also supports the following sub-commands, those are optional
name := ""
if len(req.Args) > 0 {
switch req.Args[0] {
case "host":
conf.Host = req.Args[1]
conf.Options["search"] = "host"
case "domain":
conf.Host = req.Args[1]
conf.Options["search"] = "domain"
default:
return nil, errors.New("invalid nmap sub-command, supported are: host or domain")
}
} else {
name = "Nmap"
}

if networks, ok := flags["networks"]; ok {
if networks.Array != nil {
networksValues := []string{}
for _, network := range networks.Array {
networksValues = append(networksValues, string(network.Value))
}

conf.Options["networks"] = strings.Join(networksValues, ",")
}
}

asset := inventory.Asset{
Name: name,
Connections: []*inventory.Config{conf},
}

Expand All @@ -66,11 +106,16 @@ func (s *Service) Connect(req *plugin.ConnectReq, callback plugin.ProviderCallba
}
}

inv, err := s.discover(conn)
if err != nil {
return nil, err
}

return &plugin.ConnectRes{
Id: conn.ID(),
Name: conn.Name(),
Asset: req.Asset,
Inventory: nil,
Inventory: inv,
}, nil
}

Expand Down Expand Up @@ -120,22 +165,33 @@ func (s *Service) connect(req *plugin.ConnectReq, callback plugin.ProviderCallba
}

func (s *Service) detect(asset *inventory.Asset, conn *connection.NmapConnection) error {
// TODO: adjust asset detection
asset.Id = conn.Conf.Type
asset.Name = conn.Conf.Host

asset.Platform = &inventory.Platform{
Name: "nmap",
Family: []string{"nmap"},
Kind: "api",
Title: "nmap",
platform, err := conn.PlatformInfo()
if err != nil {
return err
}

// TODO: Add platform IDs
asset.PlatformIds = []string{"//platformid.api.mondoo.app/runtime/nmap"}
asset.Platform = platform
asset.PlatformIds = []string{conn.Identifier()}
return nil
}

func (s *Service) discover(conn *connection.NmapConnection) (*inventory.Inventory, error) {
conf := conn.Asset().Connections[0]
if conf.Discover == nil {
return nil, nil
}

runtime, err := s.GetRuntime(conn.ID())
if err != nil {
return nil, err
}

return resources.Discover(runtime, conf.Options)
}

func (s *Service) MockConnect(req *plugin.ConnectReq, callback plugin.ProviderCallback) (*plugin.ConnectRes, error) {
return nil, errors.New("mock connect not yet implemented")
}
78 changes: 78 additions & 0 deletions providers/nmap/resources/discovery.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) Mondoo, Inc.
// SPDX-License-Identifier: BUSL-1.1

package resources

import (
"strings"

"go.mondoo.com/cnquery/v11/llx"
"go.mondoo.com/cnquery/v11/providers-sdk/v1/inventory"
"go.mondoo.com/cnquery/v11/providers-sdk/v1/plugin"
"go.mondoo.com/cnquery/v11/providers/nmap/connection"
"go.mondoo.com/cnquery/v11/utils/stringx"
)

func Discover(runtime *plugin.Runtime, opts map[string]string) (*inventory.Inventory, error) {
conn := runtime.Connection.(*connection.NmapConnection)
if conn == nil || conn.Asset() == nil || len(conn.Asset().Connections) == 0 {
return nil, nil
}

conf := conn.Asset().Connections[0]
targets := handleTargets(conf.Discover.Targets)
if !stringx.ContainsAnyOf(targets, connection.DiscoveryHosts, connection.DiscoveryAll, connection.DiscoveryAuto) {
return nil, nil
}

// we only need to discover when networks are specified
networkValue, ok := conf.Options["networks"]
if !ok || networkValue == "" {
return nil, nil
}
networks := strings.Split(networkValue, ",")
assetList := []*inventory.Asset{}

for i := range networks {
network := networks[i]

targetResource, err := runtime.CreateResource(runtime, "nmap.target ", map[string]*llx.RawData{
"target": llx.StringData(network),
})
if err != nil {
return nil, err
}
hosts := targetResource.(*mqlNmapTarget).GetHosts().Data
for i := range hosts {
entry := hosts[i]
host := entry.(*mqlNmapHost)

a := &inventory.Asset{
Name: host.GetName().Data,
Connections: []*inventory.Config{
{
Type: "nmap",
Host: host.GetName().Data,
Credentials: conf.Credentials,
},
},
}

assetList = append(assetList, a)
}
}

in := &inventory.Inventory{Spec: &inventory.InventorySpec{
Assets: assetList,
}}
return in, nil
}

func handleTargets(targets []string) []string {
if stringx.Contains(targets, connection.DiscoveryAll) {
return []string{
connection.DiscoveryHosts,
}
}
return targets
}
Loading

0 comments on commit e46224e

Please sign in to comment.