Skip to content

Commit

Permalink
TCP flags scan
Browse files Browse the repository at this point in the history
  • Loading branch information
v-byte-cpu committed Mar 22, 2021
1 parent aaa0a57 commit 85852ed
Show file tree
Hide file tree
Showing 9 changed files with 303 additions and 52 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Features:
* ARP scan
* TCP SYN scan
* TCP FIN / NULL / Xmas scans
* Custom TCP scans with any TCP flags

## Building

Expand Down
32 changes: 16 additions & 16 deletions command/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ var rootCmd = &cobra.Command{
}

var (
jsonFlag bool
interfaceFlag string
srcIPFlag string
srcMACFlag string
portsFlag string
cliJSONFlag bool
cliInterfaceFlag string
cliSrcIPFlag string
cliSrcMACFlag string
cliPortsFlag string
)

var (
Expand All @@ -41,10 +41,10 @@ var (
)

func init() {
rootCmd.PersistentFlags().BoolVar(&jsonFlag, "json", false, "enable JSON output")
rootCmd.PersistentFlags().StringVarP(&interfaceFlag, "iface", "i", "", "set interface to send/receive packets")
rootCmd.PersistentFlags().StringVar(&srcIPFlag, "srcip", "", "set source IP address for generated packets")
rootCmd.PersistentFlags().StringVar(&srcMACFlag, "srcmac", "", "set source MAC address for generated packets")
rootCmd.PersistentFlags().BoolVar(&cliJSONFlag, "json", false, "enable JSON output")
rootCmd.PersistentFlags().StringVarP(&cliInterfaceFlag, "iface", "i", "", "set interface to send/receive packets")
rootCmd.PersistentFlags().StringVar(&cliSrcIPFlag, "srcip", "", "set source IP address for generated packets")
rootCmd.PersistentFlags().StringVar(&cliSrcMACFlag, "srcmac", "", "set source MAC address for generated packets")
}

func Main() {
Expand Down Expand Up @@ -108,16 +108,16 @@ func parseScanRange(subnet string) (*scan.Range, error) {
}

srcIP := srcSubnet.IP
if len(srcIPFlag) > 0 {
srcIP = net.ParseIP(srcIPFlag)
if len(cliSrcIPFlag) > 0 {
srcIP = net.ParseIP(cliSrcIPFlag)
}
if srcIP == nil {
return nil, errSrcIP
}

srcMAC := iface.HardwareAddr
if len(srcMACFlag) > 0 {
if srcMAC, err = net.ParseMAC(srcMACFlag); err != nil {
if len(cliSrcMACFlag) > 0 {
if srcMAC, err = net.ParseMAC(cliSrcMACFlag); err != nil {
return nil, err
}
}
Expand Down Expand Up @@ -153,10 +153,10 @@ func parsePortRange(portsRange string) (startPort, endPort uint16, err error) {
}

func getSubnetInterface(dstSubnet *net.IPNet) (iface *net.Interface, srcSubnet *net.IPNet, err error) {
if len(interfaceFlag) == 0 {
if len(cliInterfaceFlag) == 0 {
return ip.GetSubnetInterface(dstSubnet)
}
if iface, err = net.InterfaceByName(interfaceFlag); err != nil {
if iface, err = net.InterfaceByName(cliInterfaceFlag); err != nil {
return
}
if srcSubnet, err = ip.GetSubnetInterfaceIP(iface, dstSubnet); err != nil {
Expand All @@ -167,7 +167,7 @@ func getSubnetInterface(dstSubnet *net.IPNet) (iface *net.Interface, srcSubnet *

func getLogger(name string, w io.Writer) (logger log.Logger, err error) {
opts := []log.LoggerOption{log.FlushInterval(1 * time.Second)}
if jsonFlag {
if cliJSONFlag {
opts = append(opts, log.JSON())
}
logger, err = log.NewLogger(w, name, opts...)
Expand Down
91 changes: 74 additions & 17 deletions command/tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,81 @@ import (
"runtime"
"strings"

"github.com/google/gopacket/layers"
"github.com/spf13/cobra"
"github.com/v-byte-cpu/sx/pkg/scan"
"github.com/v-byte-cpu/sx/pkg/scan/arp"
"github.com/v-byte-cpu/sx/pkg/scan/tcp"
)

var cliTCPPacketFlags string

const (
cliTCPSYNPacketFlag = "syn"
cliTCPACKPacketFlag = "ack"
cliTCPFINPacketFlag = "fin"
cliTCPRSTPacketFlag = "rst"
cliTCPPSHPacketFlag = "psh"
cliTCPURGPacketFlag = "urg"
cliTCPECEPacketFlag = "ece"
cliTCPCWRPacketFlag = "cwr"
cliTCPNSPacketFlag = "ns"
)

var (
errTCPflag = errors.New("invalid TCP packet flag")
)

func init() {
tcpCmd.PersistentFlags().StringVarP(&portsFlag, "ports", "p", "", "set ports to scan")
tcpCmd.PersistentFlags().StringVarP(&cliPortsFlag, "ports", "p", "", "set ports to scan")
if err := tcpCmd.MarkPersistentFlagRequired("ports"); err != nil {
golog.Fatalln(err)
}
tcpCmd.Flags().StringVar(&cliTCPPacketFlags, "flags", "", "set TCP flags")
rootCmd.AddCommand(tcpCmd)
}

var tcpCmd = &cobra.Command{
Use: "tcp [flags] subnet",
Example: strings.Join([]string{"tcp -p 22 192.168.0.1/24", "tcp -p 22-4567 10.0.0.1"}, "\n"),
Short: "Perform TCP scan",
Long: "Perform TCP scan. TCP SYN scan is used by default unless --flags option is specified",
Use: "tcp [flags] subnet",
Example: strings.Join([]string{
"tcp -p 22 192.168.0.1/24", "tcp -p 22-4567 10.0.0.1",
"tcp --flags fin,ack -p 22 192.168.0.3"}, "\n"),
Short: "Perform TCP scan",
Long: "Perform TCP scan. TCP SYN scan is used by default unless --flags option is specified",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("requires one ip subnet argument")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) (err error) {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()

if len(cliTCPPacketFlags) == 0 {
return startTCPSYNScan(ctx, args[0], cliPortsFlag)
}

var tcpFlags []string
if tcpFlags, err = parseTCPFlags(cliTCPPacketFlags); err != nil {
return err
}

var opts []tcp.PacketFillerOption
for _, flag := range tcpFlags {
opts = append(opts, tcpPacketFlagOptions[flag])
}

scanName := tcp.FlagsScanType
var conf *scanConfig
if conf, err = parseScanConfig(tcp.SYNScanType, args[0], portsFlag); err != nil {
if conf, err = parseScanConfig(scanName, args[0], cliPortsFlag); err != nil {
return
}

ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()

m := newTCPScanMethod(ctx, conf,
withTCPScanName(tcp.SYNScanType),
withTCPPacketFiller(tcp.NewPacketFiller(tcp.WithSYN())),
withTCPPacketFilterFunc(func(pkt *layers.TCP) bool {
// port is open
return pkt.SYN && pkt.ACK
}),
withTCPPacketFlags(tcp.EmptyFlags),
withTCPScanName(scanName),
withTCPPacketFiller(tcp.NewPacketFiller(opts...)),
withTCPPacketFilterFunc(tcp.TrueFilter),
withTCPPacketFlags(tcp.AllFlags),
)

return startEngine(ctx, &engineConfig{
Expand All @@ -63,6 +95,31 @@ var tcpCmd = &cobra.Command{
},
}

var tcpPacketFlagOptions = map[string]tcp.PacketFillerOption{
cliTCPSYNPacketFlag: tcp.WithSYN(),
cliTCPACKPacketFlag: tcp.WithACK(),
cliTCPFINPacketFlag: tcp.WithFIN(),
cliTCPRSTPacketFlag: tcp.WithRST(),
cliTCPPSHPacketFlag: tcp.WithPSH(),
cliTCPURGPacketFlag: tcp.WithURG(),
cliTCPECEPacketFlag: tcp.WithECE(),
cliTCPCWRPacketFlag: tcp.WithCWR(),
cliTCPNSPacketFlag: tcp.WithNS(),
}

func parseTCPFlags(tcpFlags string) ([]string, error) {
if len(tcpFlags) == 0 {
return []string{}, nil
}
flags := strings.Split(tcpFlags, ",")
for _, flag := range flags {
if _, ok := tcpPacketFlagOptions[flag]; !ok {
return nil, errTCPflag
}
}
return flags, nil
}

type tcpScanConfig struct {
scanName string
packetFiller scan.PacketFiller
Expand Down
12 changes: 7 additions & 5 deletions command/tcp_fin.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ var tcpfinCmd = &cobra.Command{
return nil
},
RunE: func(cmd *cobra.Command, args []string) (err error) {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()

scanName := tcp.FINScanType

var conf *scanConfig
if conf, err = parseScanConfig(tcp.SYNScanType, args[0], portsFlag); err != nil {
if conf, err = parseScanConfig(scanName, args[0], cliPortsFlag); err != nil {
return
}

ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()

m := newTCPScanMethod(ctx, conf,
withTCPScanName(tcp.FINScanType),
withTCPScanName(scanName),
withTCPPacketFiller(tcp.NewPacketFiller(tcp.WithFIN())),
withTCPPacketFilterFunc(tcp.TrueFilter),
withTCPPacketFlags(tcp.AllFlags),
Expand Down
12 changes: 7 additions & 5 deletions command/tcp_null.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ var tcpnullCmd = &cobra.Command{
return nil
},
RunE: func(cmd *cobra.Command, args []string) (err error) {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()

scanName := tcp.NULLScanType

var conf *scanConfig
if conf, err = parseScanConfig(tcp.NULLScanType, args[0], portsFlag); err != nil {
if conf, err = parseScanConfig(scanName, args[0], cliPortsFlag); err != nil {
return
}

ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()

m := newTCPScanMethod(ctx, conf,
withTCPScanName(tcp.NULLScanType),
withTCPScanName(scanName),
withTCPPacketFiller(tcp.NewPacketFiller()),
withTCPPacketFilterFunc(tcp.TrueFilter),
withTCPPacketFlags(tcp.AllFlags),
Expand Down
61 changes: 61 additions & 0 deletions command/tcp_syn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package command

import (
"context"
"errors"
"os"
"os/signal"
"strings"

"github.com/google/gopacket/layers"
"github.com/spf13/cobra"
"github.com/v-byte-cpu/sx/pkg/scan/tcp"
)

func init() {
tcpCmd.AddCommand(tcpsynCmd)
}

var tcpsynCmd = &cobra.Command{
Use: "syn [flags] subnet",
Example: strings.Join([]string{"tcp syn -p 22 192.168.0.1/24", "tcp syn -p 22-4567 10.0.0.1"}, "\n"),
Short: "Perform TCP SYN scan",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("requires one ip subnet argument")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) (err error) {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
return startTCPSYNScan(ctx, args[0], cliPortsFlag)
},
}

func startTCPSYNScan(ctx context.Context, subnet, ports string) (err error) {
scanName := tcp.SYNScanType

var conf *scanConfig
if conf, err = parseScanConfig(scanName, subnet, ports); err != nil {
return
}

m := newTCPScanMethod(ctx, conf,
withTCPScanName(scanName),
withTCPPacketFiller(tcp.NewPacketFiller(tcp.WithSYN())),
withTCPPacketFilterFunc(func(pkt *layers.TCP) bool {
// port is open
return pkt.SYN && pkt.ACK
}),
withTCPPacketFlags(tcp.EmptyFlags),
)

return startEngine(ctx, &engineConfig{
logger: conf.logger,
scanRange: conf.scanRange,
scanMethod: m,
// TODO SYN,ACK filter
bpfFilter: tcp.BPFFilter,
})
}
Loading

0 comments on commit 85852ed

Please sign in to comment.