Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add port number to CSV input and output #397

Merged
merged 2 commits into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions input.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,31 @@ import (
"fmt"
"io"
"net"
"strconv"
"strings"

log "github.com/sirupsen/logrus"
)

// ParseCSVTarget takes a record from a CSV-format input file and
// returns the specified ipnet, domain, and tag, or an error.
// returns the specified ipnet, domain, tag and port or an error.
//
// ZGrab2 input files have three fields:
// IP, DOMAIN, TAG
// ZGrab2 input files have four fields:
//
// IP, DOMAIN, TAG, PORT
//
// Each line specifies a target to scan by its IP address, domain
// name, or both, as well as an optional tag used to determine which
// name or both, as well as an optional tag used to determine which
// scanners will be invoked.
//
// Port number has been added to the end of the line for compatibility reasons.
// A CIDR block may be provided in the IP field, in which case the
// framework expands the record into targets for every address in the
// block.
//
// Trailing empty fields may be omitted.
// Comment lines begin with #, and empty lines are ignored.
//
func ParseCSVTarget(fields []string) (ipnet *net.IPNet, domain string, tag string, err error) {
func ParseCSVTarget(fields []string) (ipnet *net.IPNet, domain string, tag string, port string, err error) {
for i := range fields {
fields[i] = strings.TrimSpace(fields[i])
}
Expand All @@ -47,7 +49,11 @@ func ParseCSVTarget(fields []string) (ipnet *net.IPNet, domain string, tag strin
if len(fields) > 2 {
tag = fields[2]
}
// Use string for port to allow empty port
if len(fields) > 3 {
port = fields[3]
}
if len(fields) > 4 {
err = fmt.Errorf("too many fields: %q", fields)
return
}
Expand Down Expand Up @@ -102,24 +108,41 @@ func GetTargetsCSV(source io.Reader, ch chan<- ScanTarget) error {
if len(fields) == 0 {
continue
}
ipnet, domain, tag, err := ParseCSVTarget(fields)
ipnet, domain, tag, port, err := ParseCSVTarget(fields)
if err != nil {
log.Errorf("parse error, skipping: %v", err)
continue
}
var ip net.IP
var port_uint uint
if port != "" {
port_int, err := strconv.Atoi(port)
port_uint = uint(port_int)
if err != nil {
log.Errorf("parse error, skipping: %v", err)
continue
}
}
if ipnet != nil {
if ipnet.Mask != nil {
// expand CIDR block into one target for each IP
for ip = ipnet.IP.Mask(ipnet.Mask); ipnet.Contains(ip); incrementIP(ip) {
ch <- ScanTarget{IP: duplicateIP(ip), Domain: domain, Tag: tag}
if port == "" {
ch <- ScanTarget{IP: duplicateIP(ip), Domain: domain, Tag: tag}
} else {
ch <- ScanTarget{IP: duplicateIP(ip), Domain: domain, Tag: tag, Port: &port_uint}
}
}
continue
} else {
ip = ipnet.IP
}
}
ch <- ScanTarget{IP: ip, Domain: domain, Tag: tag}
if port == "" {
ch <- ScanTarget{IP: ip, Domain: domain, Tag: tag}
} else {
ch <- ScanTarget{IP: ip, Domain: domain, Tag: tag, Port: &port_uint}
}
}
return nil
}
Expand Down
34 changes: 29 additions & 5 deletions input_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,27 @@ func TestParseCSVTarget(t *testing.T) {
ipnet *net.IPNet
domain string
tag string
port string
success bool
}{
// IP DOMAIN TAG PORT
{
fields: []string{"10.0.0.1", "example.com", "tag", "443"},
ipnet: parseIP("10.0.0.1"),
domain: "example.com",
tag: "tag",
port: "443",
success: true,
},
// IP DOMAIN TAG PORT
{
fields: []string{"10.0.0.1", "example.com", "tag"},
ipnet: parseIP("10.0.0.1"),
domain: "example.com",
tag: "tag",
port: "",
success: true,
},
// IP DOMAIN TAG
{
fields: []string{"10.0.0.1", "example.com", "tag"},
Expand Down Expand Up @@ -129,14 +148,14 @@ func TestParseCSVTarget(t *testing.T) {
}

for _, test := range tests {
ipnet, domain, tag, err := ParseCSVTarget(test.fields)
ipnet, domain, tag, port, err := ParseCSVTarget(test.fields)
if (err == nil) != test.success {
t.Errorf("wrong error status (got err=%v, success should be %v): %q", err, test.success, test.fields)
return
}
if err == nil {
if ipnetString(ipnet) != ipnetString(test.ipnet) || domain != test.domain || tag != test.tag {
t.Errorf("wrong result (got %v,%v,%v; expected %v,%v,%v): %q", ipnetString(ipnet), domain, tag, ipnetString(test.ipnet), test.domain, test.tag, test.fields)
if ipnetString(ipnet) != ipnetString(test.ipnet) || domain != test.domain || tag != test.tag || port != test.port {
t.Errorf("wrong result (got %v,%v,%v,%v ; expected %v,%v,%v,%v): %q", ipnetString(ipnet), domain, tag, port, ipnetString(test.ipnet), test.domain, test.tag, test.port, test.fields)
return
}
}
Expand All @@ -150,8 +169,11 @@ func TestGetTargetsCSV(t *testing.T) {
10.0.0.1
,example.com
example.com
2.2.2.2/30,, tag`

2.2.2.2/30,, tag
10.0.0.1,example.com,tag,443
10.0.0.1,,,443
`
port := uint(443)
expected := []ScanTarget{
ScanTarget{IP: net.ParseIP("10.0.0.1"), Domain: "example.com", Tag: "tag"},
ScanTarget{IP: net.ParseIP("10.0.0.1"), Domain: "example.com"},
Expand All @@ -162,6 +184,8 @@ example.com
ScanTarget{IP: net.ParseIP("2.2.2.1"), Tag: "tag"},
ScanTarget{IP: net.ParseIP("2.2.2.2"), Tag: "tag"},
ScanTarget{IP: net.ParseIP("2.2.2.3"), Tag: "tag"},
ScanTarget{IP: net.ParseIP("10.0.0.1"), Domain: "example.com", Tag: "tag", Port: &port},
ScanTarget{IP: net.ParseIP("10.0.0.1"), Port: &port},
}

ch := make(chan ScanTarget, 0)
Expand Down
7 changes: 6 additions & 1 deletion processing.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
// Grab contains all scan responses for a single host
type Grab struct {
IP string `json:"ip,omitempty"`
Port uint `json:"port,omitempty"`
Domain string `json:"domain,omitempty"`
Data map[string]ScanResponse `json:"data,omitempty"`
}
Expand Down Expand Up @@ -117,12 +118,16 @@ func (target *ScanTarget) OpenUDP(flags *BaseFlags, udp *UDPFlags) (net.Conn, er
// scan responses.
func BuildGrabFromInputResponse(t *ScanTarget, responses map[string]ScanResponse) *Grab {
var ipstr string

var port uint
if t.IP != nil {
ipstr = t.IP.String()
}
if t.Port != nil {
port = *t.Port
}
return &Grab{
IP: ipstr,
Port: port,
Domain: t.Domain,
Data: responses,
}
Expand Down
Loading