Skip to content

Commit

Permalink
TCP FIN scan
Browse files Browse the repository at this point in the history
  • Loading branch information
v-byte-cpu committed Mar 21, 2021
1 parent c5cf2bd commit c387209
Show file tree
Hide file tree
Showing 22 changed files with 1,006 additions and 471 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The goal of this project is to create the fastest network scanner with clean and
Features:
* ARP scan
* TCP SYN scan
* TCP FIN scan

## Building

Expand Down
3 changes: 2 additions & 1 deletion command/arp.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,6 @@ func newARPScanMethod(ctx context.Context) *arp.ScanMethod {
}
pktgen := scan.NewPacketMultiGenerator(arp.NewPacketFiller(), runtime.NumCPU())
psrc := scan.NewPacketSource(reqgen, pktgen)
return arp.NewScanMethod(ctx, psrc)
results := scan.NewResultChan(ctx, 1000)
return arp.NewScanMethod(psrc, results)
}
68 changes: 65 additions & 3 deletions command/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"io"
"net"
"os"
"strconv"
"strings"
"sync"
"time"

Expand All @@ -15,6 +17,7 @@ import (
"github.com/v-byte-cpu/sx/pkg/ip"
"github.com/v-byte-cpu/sx/pkg/packet/afpacket"
"github.com/v-byte-cpu/sx/pkg/scan"
"github.com/v-byte-cpu/sx/pkg/scan/arp"
)

var rootCmd = &cobra.Command{
Expand All @@ -28,6 +31,7 @@ var (
interfaceFlag string
srcIPFlag string
srcMACFlag string
portsFlag string
)

var (
Expand All @@ -49,6 +53,47 @@ func Main() {
}
}

type scanConfig struct {
logger log.Logger
scanRange *scan.Range
cache *arp.Cache
gatewayIP net.IP
}

func parseScanConfig(scanName, subnet, ports string) (c *scanConfig, err error) {
var r *scan.Range
if r, err = parseScanRange(subnet); err != nil {
return
}
if r.StartPort, r.EndPort, err = parsePortRange(ports); err != nil {
return
}

var logger log.Logger
if logger, err = getLogger(scanName, os.Stdout); err != nil {
return
}

// TODO file argument
// TODO handle pipes
cache := arp.NewCache()
if err = arp.FillCache(cache, os.Stdin); err != nil {
return
}

var gatewayIP net.IP
if gatewayIP, err = getGatewayIP(r); err != nil {
return
}
c = &scanConfig{
logger: logger,
scanRange: r,
cache: cache,
gatewayIP: gatewayIP,
}
return
}

func parseScanRange(subnet string) (*scan.Range, error) {
dstSubnet, err := ip.ParseIPNet(subnet)
if err != nil {
Expand All @@ -64,9 +109,7 @@ func parseScanRange(subnet string) (*scan.Range, error) {

srcIP := srcSubnet.IP
if len(srcIPFlag) > 0 {
if srcIP = net.ParseIP(srcIPFlag); srcIP == nil {
return nil, errSrcIP
}
srcIP = net.ParseIP(srcIPFlag)
}
if srcIP == nil {
return nil, errSrcIP
Expand All @@ -90,6 +133,25 @@ func parseScanRange(subnet string) (*scan.Range, error) {
SrcMAC: srcMAC}, nil
}

// TODO port ranges with tests
func parsePortRange(portsRange string) (startPort, endPort uint16, err error) {
ports := strings.Split(portsRange, "-")
var port uint64
if port, err = strconv.ParseUint(ports[0], 10, 16); err != nil {
return
}
startPort = uint16(port)
if len(ports) < 2 {
endPort = startPort
return
}
if port, err = strconv.ParseUint(ports[1], 10, 16); err != nil {
return
}
endPort = uint16(port)
return
}

func getSubnetInterface(dstSubnet *net.IPNet) (iface *net.Interface, srcSubnet *net.IPNet, err error) {
if len(interfaceFlag) == 0 {
return ip.GetSubnetInterface(dstSubnet)
Expand Down
113 changes: 113 additions & 0 deletions command/tcp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package command

import (
"context"
"errors"
golog "log"
"os"
"os/signal"
"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"
)

func init() {
tcpCmd.PersistentFlags().StringVarP(&portsFlag, "ports", "p", "", "set ports to scan")
if err := tcpCmd.MarkPersistentFlagRequired("ports"); err != nil {
golog.Fatalln(err)
}
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",
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) {
var conf *scanConfig
if conf, err = parseScanConfig(tcp.SYNScanType, args[0], portsFlag); 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),
)

return startEngine(ctx, &engineConfig{
logger: conf.logger,
scanRange: conf.scanRange,
scanMethod: m,
bpfFilter: tcp.BPFFilter,
})
},
}

type tcpScanConfig struct {
scanName string
packetFiller scan.PacketFiller
packetFilter tcp.PacketFilterFunc
packetFlags tcp.PacketFlagsFunc
}

type tcpScanConfigOption func(c *tcpScanConfig)

func withTCPScanName(scanName string) tcpScanConfigOption {
return func(c *tcpScanConfig) {
c.scanName = scanName
}
}

func withTCPPacketFiller(filler scan.PacketFiller) tcpScanConfigOption {
return func(c *tcpScanConfig) {
c.packetFiller = filler
}
}

func withTCPPacketFilterFunc(filter tcp.PacketFilterFunc) tcpScanConfigOption {
return func(c *tcpScanConfig) {
c.packetFilter = filter
}
}

func withTCPPacketFlags(packetFlags tcp.PacketFlagsFunc) tcpScanConfigOption {
return func(c *tcpScanConfig) {
c.packetFlags = packetFlags
}
}

func newTCPScanMethod(ctx context.Context, conf *scanConfig, opts ...tcpScanConfigOption) *tcp.ScanMethod {
c := &tcpScanConfig{}
for _, o := range opts {
o(c)
}
reqgen := arp.NewCacheRequestGenerator(
scan.RequestGeneratorFunc(scan.Requests), conf.gatewayIP, conf.cache)
pktgen := scan.NewPacketMultiGenerator(c.packetFiller, runtime.NumCPU())
psrc := scan.NewPacketSource(reqgen, pktgen)
results := scan.NewResultChan(ctx, 1000)
return tcp.NewScanMethod(
c.scanName, psrc, results,
tcp.WithPacketFilterFunc(c.packetFilter),
tcp.WithPacketFlagsFunc(tcp.EmptyFlags))
}
51 changes: 51 additions & 0 deletions command/tcp_fin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package command

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

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

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

var tcpfinCmd = &cobra.Command{
Use: "fin [flags] subnet",
Example: strings.Join([]string{"tcp fin -p 22 192.168.0.1/24", "tcp fin -p 22-4567 10.0.0.1"}, "\n"),
Short: "Perform TCP FIN 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) {
var conf *scanConfig
if conf, err = parseScanConfig(tcp.SYNScanType, args[0], portsFlag); err != nil {
return
}

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

m := newTCPScanMethod(ctx, conf,
withTCPScanName(tcp.FINScanType),
withTCPPacketFiller(tcp.NewPacketFiller(tcp.WithFIN())),
withTCPPacketFilterFunc(tcp.TrueFilter),
withTCPPacketFlags(tcp.AllFlags),
)

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

0 comments on commit c387209

Please sign in to comment.