zgrab2/modules/ssh.go

131 lines
4.1 KiB
Go

package modules
import (
"net"
"strconv"
"strings"
log "github.com/sirupsen/logrus"
"github.com/zmap/zgrab2"
"github.com/zmap/zgrab2/lib/ssh"
)
type SSHFlags struct {
zgrab2.BaseFlags
ClientID string `long:"client" description:"Specify the client ID string to use" default:"SSH-2.0-Go"`
KexAlgorithms string `long:"kex-algorithms" description:"Set SSH Key Exchange Algorithms"`
HostKeyAlgorithms string `long:"host-key-algorithms" description:"Set SSH Host Key Algorithms"`
Ciphers string `long:"ciphers" description:"A comma-separated list of which ciphers to offer."`
CollectUserAuth bool `long:"userauth" description:"Use the 'none' authentication request to see what userauth methods are allowed"`
GexMinBits uint `long:"gex-min-bits" description:"The minimum number of bits for the DH GEX prime." default:"1024"`
GexMaxBits uint `long:"gex-max-bits" description:"The maximum number of bits for the DH GEX prime." default:"8192"`
GexPreferredBits uint `long:"gex-preferred-bits" description:"The preferred number of bits for the DH GEX prime." default:"2048"`
HelloOnly bool `long:"hello-only" description:"Limit scan to the initial hello message"`
Verbose bool `long:"verbose" description:"Output additional information, including SSH client properties from the SSH handshake."`
}
type SSHModule struct {
}
type SSHScanner struct {
config *SSHFlags
}
func init() {
var sshModule SSHModule
cmd, err := zgrab2.AddCommand("ssh", "SSH Banner Grab", sshModule.Description(), 22, &sshModule)
if err != nil {
log.Fatal(err)
}
s := ssh.MakeSSHConfig() //dummy variable to get default for host key, kex algorithm, ciphers
cmd.FindOptionByLongName("host-key-algorithms").Default = []string{strings.Join(s.HostKeyAlgorithms, ",")}
cmd.FindOptionByLongName("kex-algorithms").Default = []string{strings.Join(s.KeyExchanges, ",")}
cmd.FindOptionByLongName("ciphers").Default = []string{strings.Join(s.Ciphers, ",")}
}
func (m *SSHModule) NewFlags() interface{} {
return new(SSHFlags)
}
func (m *SSHModule) NewScanner() zgrab2.Scanner {
return new(SSHScanner)
}
// Description returns an overview of this module.
func (m *SSHModule) Description() string {
return "Fetch an SSH server banner and collect key exchange information"
}
func (f *SSHFlags) Validate(args []string) error {
return nil
}
func (f *SSHFlags) Help() string {
return ""
}
func (s *SSHScanner) Init(flags zgrab2.ScanFlags) error {
f, _ := flags.(*SSHFlags)
s.config = f
return nil
}
func (s *SSHScanner) InitPerSender(senderID int) error {
return nil
}
func (s *SSHScanner) GetName() string {
return s.config.Name
}
func (s *SSHScanner) GetTrigger() string {
return s.config.Trigger
}
func (s *SSHScanner) Scan(t zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) {
data := new(ssh.HandshakeLog)
var port uint
// If the port is supplied in ScanTarget, let that override the cmdline option
if t.Port != nil {
port = *t.Port
} else {
port = s.config.Port
}
portStr := strconv.FormatUint(uint64(port), 10)
rhost := net.JoinHostPort(t.Host(), portStr)
sshConfig := ssh.MakeSSHConfig()
sshConfig.Timeout = s.config.Timeout
sshConfig.ConnLog = data
sshConfig.ClientVersion = s.config.ClientID
sshConfig.HelloOnly = s.config.HelloOnly
if err := sshConfig.SetHostKeyAlgorithms(s.config.HostKeyAlgorithms); err != nil {
log.Fatal(err)
}
if err := sshConfig.SetKexAlgorithms(s.config.KexAlgorithms); err != nil {
log.Fatal(err)
}
if err := sshConfig.SetCiphers(s.config.Ciphers); err != nil {
log.Fatal(err)
}
sshConfig.Verbose = s.config.Verbose
sshConfig.DontAuthenticate = s.config.CollectUserAuth
sshConfig.GexMinBits = s.config.GexMinBits
sshConfig.GexMaxBits = s.config.GexMaxBits
sshConfig.GexPreferredBits = s.config.GexPreferredBits
sshConfig.BannerCallback = func(banner string) error {
data.Banner = strings.TrimSpace(banner)
return nil
}
_, err := ssh.Dial("tcp", rhost, sshConfig)
// TODO FIXME: Distinguish error types
status := zgrab2.TryGetScanStatus(err)
return status, data, err
}
// Protocol returns the protocol identifer for the scanner.
func (s *SSHScanner) Protocol() string {
return "ssh"
}