Skip to content
Open
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
14 changes: 6 additions & 8 deletions cmd/nessusCsvSrv.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,25 @@ limitations under the License.
package cmd

import (
"fmt"

"github.com/pafussell/gophermap/parser"
"github.com/spf13/cobra"
)

// nessusCsvSrvCmd represents the nessusCsvSrv command
var nessusCsvSrvCmd = &cobra.Command{
Use: "nessus-csv-srv",
Short: "read the Nessus csv output and print out services found by the \"Service Detection\" plugin",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("nessus-csv-srv called")
p := parser.New(args[0], nil)
p.NessusPrettyServicesCSV()
RunE: func(cmd *cobra.Command, args []string) error {
p := parser.New(filePath, nil, Verbose)
if err := p.NessusPrettyServicesCSV(); err != nil {
return err
}
return nil
},
}

var filePath string

func init() {
rootCmd.AddCommand(nessusCsvSrvCmd)
nessusCsvSrvCmd.Flags().StringVarP(&filePath, "PATH", "f", "", "Nessus CSV file to parse")
}
6 changes: 2 additions & 4 deletions cmd/nessusCsvWeb.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package cmd

import (
"fmt"

"github.com/pafussell/gophermap/parser"
"github.com/spf13/cobra"
)
Expand All @@ -12,12 +10,12 @@ var nessusCsvWebCmd = &cobra.Command{
Short: "Read the Nessus csv output and print out all detected web servers",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("nessus-csv-web called")
p := parser.New(args[0], nil)
p := parser.New(filePath, nil, Verbose)
p.NessusPrettyWebCSV()
},
}

func init() {
rootCmd.AddCommand(nessusCsvWebCmd)
nessusCsvSrvCmd.Flags().StringVarP(&filePath, "PATH", "f", "", "Nessus CSV file to parse")
}
5 changes: 1 addition & 4 deletions cmd/nessusXml.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package cmd

import (
"fmt"

"github.com/pafussell/gophermap/parser"
"github.com/spf13/cobra"
)
Expand All @@ -12,8 +10,7 @@ var nessusXmlCmd = &cobra.Command{
Short: "Read the Nessus xml output and print out services found by the \"Service Detection\" plugin",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("nessus-xml called")
p := parser.New(args[0], nil)
p := parser.New(filePath, nil, Verbose)
p.NessusPrettyServiceXML()
},
}
Expand Down
5 changes: 1 addition & 4 deletions cmd/nessusXmlHigh.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package cmd

import (
"fmt"

"github.com/pafussell/gophermap/parser"
"github.com/spf13/cobra"
)
Expand All @@ -12,8 +10,7 @@ var nessusXmlHighCmd = &cobra.Command{
Short: "parse high/crit vulns from nessus xml",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("nessus-xml-high called")
p := parser.New(args[0], nil)
p := parser.New(filePath, nil, Verbose)
p.NessusPrettyHighCritXML()
},
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/nmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var nmapCmd = &cobra.Command{
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("nmap called, file = %v\n", args[0])
p := parser.New(args[0], nil)
p := parser.New(filePath, nil, Verbose)
p.NmapPrettyPrint()
},
}
Expand Down
24 changes: 6 additions & 18 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,37 +1,25 @@
package cmd

import (
"fmt"
"os"

"github.com/spf13/cobra"
)

var cfgFile string

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "gophermap",
Short: "A parser for various network and vulnerability tools and formats",
Long: `A parser for Nessus, NMap and Rumble scanner results file in various formats`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
var Verbose bool

func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
func Execute() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}
2 changes: 1 addition & 1 deletion cmd/rumble.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var rumbleCmd = &cobra.Command{
a lot of version data in banners in the JSON blob. Thus the dedicated format. `,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("rumble called")
p := parser.New(args[0], nil)
p := parser.New(filePath, nil, Verbose)
p.RumblePrettyPrint()
},
}
Expand Down
87 changes: 64 additions & 23 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,27 @@ import (

type Parser struct {
FilePath string
Logger io.Writer
Writer io.Writer
Verbose bool
}

func New(file string, out io.Writer) (p *Parser) {
func New(file string, out io.Writer, verbose bool) (p *Parser) {
if out != nil {
p = &Parser{FilePath: file, Logger: out}
p = &Parser{FilePath: file, Writer: out, Verbose: verbose}
} else {
p = &Parser{FilePath: file, Logger: os.Stdout}
p = &Parser{FilePath: file, Writer: os.Stdout, Verbose: verbose}
}
return
}

func (p *Parser) getNmapParser(w io.Writer) (*gn.NmapRun, error) {
fb, err := ioutil.ReadFile(p.FilePath)
if err != nil {
fmt.Fprintf(w, "Error opening scan file: %v\n", p.FilePath)
return nil, err
}

n, err := gn.Parse(fb)
if err != nil {
fmt.Fprintf(w, "Error parsing NMap file\n")
return nil, err
}
return n, nil
Expand All @@ -47,13 +46,11 @@ func (p *Parser) getNmapParser(w io.Writer) (*gn.NmapRun, error) {
func (p *Parser) getNessusParser(w io.Writer) (*gne.NessusData, error) {
fb, err := ioutil.ReadFile(p.FilePath)
if err != nil {
fmt.Fprintf(w, "Error opening Nessus file: %v\n", p.FilePath)
return nil, err
}

n, err := gne.Parse(fb)
if err != nil {
fmt.Fprintf(w, "Error parsing Nessus file\n")
return nil, err
}
return n, nil
Expand All @@ -62,7 +59,6 @@ func (p *Parser) getNessusParser(w io.Writer) (*gne.NessusData, error) {
func (p *Parser) getCsvRecords(w io.Writer) ([][]string, error) {
fb, err := ioutil.ReadFile(p.FilePath)
if err != nil {
fmt.Fprintf(w, "Error opening Nessus file: %v\n", p.FilePath)
return nil, err
}
br := bytes.NewReader(fb)
Expand All @@ -72,21 +68,46 @@ func (p *Parser) getCsvRecords(w io.Writer) ([][]string, error) {
return records, err
}

func (p *Parser) verboseNmapDump(np *gn.NmapRun) {
fmt.Fprintf(p.Writer, "Host count: %v\n", len(np.Hosts))
fmt.Fprintf(p.Writer, "Scanner: %v\n", np.Scanner)
fmt.Fprintf(p.Writer, "Profile name: %v\n", np.ProfileName)
fmt.Fprintf(p.Writer, "Scan start time: %v\n", np.Start)
}

func (p *Parser) verboseNessusDump(n *gne.NessusData) {
fmt.Fprintf(p.Writer, "Host count: %v\n", len(n.Report.ReportHosts))
fmt.Fprintf(p.Writer, "Report name: %v\n", n.Report.Name)
}

func (p *Parser) verboseNessusCsvDump(rr [][]string) {
if len(rr) > 0 {
fmt.Fprintf(p.Writer, "CSV record count: %v\n", len(rr))
fmt.Fprintf(p.Writer, "CSV column count: %v\n", len(rr[0]))
} else {
fmt.Fprintf(p.Writer, "No rows found\n")
}
}

// NmapPrettyPrint consumes NMap XML and prints
// formatted table of enumerated services
func (p *Parser) NmapPrettyPrint() (err error) {
np, err := p.getNmapParser(p.Logger)
np, err := p.getNmapParser(p.Writer)
if err != nil {
return err
}

if p.Verbose {
p.verboseNmapDump(np)
}

// In my previous nmap parser I built a lot more logic into output options I would like to add next
// eg. ouput live hosts, output just a selected port
for _, host := range np.Hosts {
for _, ip := range host.Addresses {
for _, port := range host.Ports {
// fmt.Println("| ", ip.Addr, " | ", port.PortId, " | ", port.Service.Product, port.Service.Version)
fmt.Fprintf(p.Logger, "| %18s | %8s | %6s | %-22s %-8s |\n", ip.Addr, strconv.Itoa(port.PortId), port.Protocol, port.Service.Product, port.Service.Version)
fmt.Fprintf(p.Writer, "| %18s | %8s | %6s | %-22s %-8s |\n", ip.Addr, strconv.Itoa(port.PortId), port.Protocol, port.Service.Product, port.Service.Version)
}

}
Expand All @@ -98,19 +119,23 @@ func (p *Parser) NmapPrettyPrint() (err error) {
// NessusPrettyServiceXML does a pretty print of nessus data
// and takes in the .nessus style file
func (p *Parser) NessusPrettyServiceXML() (err error) {
np, err := p.getNessusParser(p.Logger)
np, err := p.getNessusParser(p.Writer)
if err != nil {
return err
}

if p.Verbose {
p.verboseNessusDump(np)
}

// we might need some better logic here. Right now we just look for the plugin named
// Service Detection and output the plugin output with the port. Might be other plugins
// we want to add that have good data.
for _, host := range np.Report.ReportHosts {
for _, item := range host.ReportItems {
// change this...need to range over host properties to get tag == ip
if item.PluginName == "Service Detection" && item.PluginOutput[0:17] != "The service close" {
fmt.Fprintf(p.Logger, "| %18s | %8s | %-10s| %-32s |\n", host.Name, strconv.Itoa(item.Port), item.SvcName, item.PluginOutput[0:28])
fmt.Fprintf(p.Writer, "| %18s | %8s | %-10s| %-32s |\n", host.Name, strconv.Itoa(item.Port), item.SvcName, item.PluginOutput[0:28])
}
}
}
Expand All @@ -120,19 +145,23 @@ func (p *Parser) NessusPrettyServiceXML() (err error) {
// NessusPrettyHighCritXML does a pretty print of nessus data
// and takes in the .nessus style file; it prints all high and crit level findings
func (p *Parser) NessusPrettyHighCritXML() (err error) {
np, err := p.getNessusParser(p.Logger)
np, err := p.getNessusParser(p.Writer)
if err != nil {
return err
}

if p.Verbose {
p.verboseNessusDump(np)
}

// we might need some better logic here. Right now we just look for the plugin named
// Service Detection and output the plugin output with the port. Might be other plugins
// we want to add that have good data.
for _, host := range np.Report.ReportHosts {
for _, item := range host.ReportItems {
// change this...need to range over host properties to get tag == ip
if item.Severity == 3 || item.Severity == 4 {
fmt.Fprintf(p.Logger, "| %18s | %8s | %-28s \n", host.Name, strconv.Itoa(item.Port), item.PluginName)
fmt.Fprintf(p.Writer, "| %18s | %8s | %-28s \n", host.Name, strconv.Itoa(item.Port), item.PluginName)
}
}

Expand All @@ -143,14 +172,18 @@ func (p *Parser) NessusPrettyHighCritXML() (err error) {
// NessusPrettyServicesCSV consumes an nessus csv and
// prints out service and IP
func (p *Parser) NessusPrettyServicesCSV() (err error) {
records, err := p.getCsvRecords(p.Logger)
records, err := p.getCsvRecords(p.Writer)
if err != nil {
return err
}

if p.Verbose {
p.verboseNessusCsvDump(records)
}

for row := 0; row < len(records); row++ {
if records[row][7] == "Service Detection" {
fmt.Fprintf(p.Logger, "| %14s | %8s | %22s\n", records[row][4], records[row][6], records[row][12])
fmt.Fprintf(p.Writer, "| %14s | %8s | %22s\n", records[row][4], records[row][6], records[row][12])
}
}
return
Expand All @@ -159,17 +192,21 @@ func (p *Parser) NessusPrettyServicesCSV() (err error) {
// NessusPrettyWebCSV consumes an nessus csv and
// prints out service and IP
func (p *Parser) NessusPrettyWebCSV() (err error) {
records, err := p.getCsvRecords(p.Logger)
records, err := p.getCsvRecords(p.Writer)
if err != nil {
return err
}

if p.Verbose {
p.verboseNessusCsvDump(records)
}

for row := 0; row < len(records); row++ {
if records[row][7] == "HTTP Server Type and Version" {
re := regexp.MustCompile("\\n")
input := records[row][12]
input = re.ReplaceAllString(input, " ")
fmt.Fprintf(p.Logger, "| %14s | %8s | %22s\n", records[row][4], records[row][6], input)
fmt.Fprintf(p.Writer, "| %14s | %8s | %22s\n", records[row][4], records[row][6], input)
}
}
return
Expand All @@ -178,11 +215,15 @@ func (p *Parser) NessusPrettyWebCSV() (err error) {
// RumblePrettyPrint is for parsing
// Rumble scans in nmap xml format
func (p *Parser) RumblePrettyPrint() (err error) {
n, err := p.getNmapParser(p.Logger)
n, err := p.getNmapParser(p.Writer)
if err != nil {
return err
}

if p.Verbose {
p.verboseNmapDump(n)
}

for _, host := range n.Hosts {
for _, ip := range host.Addresses {
fmt.Println("|--------------|-------------------|--------------|------------------|")
Expand All @@ -192,16 +233,16 @@ func (p *Parser) RumblePrettyPrint() (err error) {
} else {
for _, port := range host.Ports {
if port.Service.Product != "" {
fmt.Fprintf(p.Logger, "| %18s | %8s | %6s | %-22s %-8s |\n", ip.Addr, strconv.Itoa(port.PortId), port.Protocol, port.Service.Product, port.Service.Version)
fmt.Fprintf(p.Writer, "| %18s | %8s | %6s | %-22s %-8s |\n", ip.Addr, strconv.Itoa(port.PortId), port.Protocol, port.Service.Product, port.Service.Version)
continue
}
m := make(map[string]string)
b := []byte(port.Scripts[0].Output)
if err := json.Unmarshal(b, &m); err != nil {
fmt.Fprintf(p.Logger, "Error parsing embedded JSON\n")
fmt.Fprintf(p.Writer, "Error parsing embedded JSON\n")
}
if banner, exists := m["banner"]; exists {
fmt.Fprintf(p.Logger, "| %18s | %8s | %6s | %-22s |\n", ip.Addr, strconv.Itoa(port.PortId), port.Protocol, banner)
fmt.Fprintf(p.Writer, "| %18s | %8s | %6s | %-22s |\n", ip.Addr, strconv.Itoa(port.PortId), port.Protocol, banner)
}
}
}
Expand Down
Loading