Name changes to library, remove global config

This commit is contained in:
Alex 2017-10-14 19:54:37 -04:00
parent f99445c05b
commit 9bbc2ea8f4
7 changed files with 157 additions and 38 deletions

@ -13,7 +13,7 @@ import (
// clientAuthenticate authenticates with the remote server. See RFC 4252. // clientAuthenticate authenticates with the remote server. See RFC 4252.
func (c *connection) clientAuthenticate(config *ClientConfig) error { func (c *connection) clientAuthenticate(config *ClientConfig) error {
if c.transport.config.ConnLog != nil && !pkgConfig.CollectUserAuth { if c.transport.config.ConnLog != nil && !config.DontAuthenticate {
// Use ConnLog existence to indicate that this is a run and not testing // Use ConnLog existence to indicate that this is a run and not testing
return nil return nil
} }

@ -225,6 +225,14 @@ type Config struct {
// A pointer to the handshake log IOT allow incremental building // A pointer to the handshake log IOT allow incremental building
ConnLog *HandshakeLog ConnLog *HandshakeLog
// Whether or not the package should operate in verbose mode
// (save more output)
Verbose bool
GexMinBits uint
GexMaxBits uint
GexPreferredBits uint
} }
// SetDefaults sets sensible values for unset fields in config. This is // SetDefaults sets sensible values for unset fields in config. This is

@ -1,11 +1,74 @@
package ssh package ssh
import (
"errors"
"fmt"
"strings"
)
func MakeXSSHConfig() *ClientConfig { func MakeXSSHConfig() *ClientConfig {
ret := new(ClientConfig) ret := new(ClientConfig)
ret.DontAuthenticate = true // IOT scan ethically, never attempt to authenticate ret.DontAuthenticate = true // IOT scan ethically, never attempt to authenticate
ret.ClientVersion = pkgConfig.ClientID ret.HostKeyAlgorithms = supportedHostKeyAlgos
ret.HostKeyAlgorithms = pkgConfig.HostKeyAlgorithms.Get() ret.KeyExchanges = defaultKexAlgos
ret.KeyExchanges = pkgConfig.KexAlgorithms.Get() ret.Ciphers = defaultCiphers
ret.Ciphers = pkgConfig.Ciphers.Get()
return ret return ret
} }
func (c *ClientConfig) SetHostKeyAlgorithms(value string) error {
for _, alg := range strings.Split(value, ",") {
isValid := false
for _, val := range supportedHostKeyAlgos {
if val == alg {
isValid = true
break
}
}
if !isValid {
return errors.New(fmt.Sprintf(`host key algorithm not supported: "%s"`, alg))
}
c.HostKeyAlgorithms = append(c.HostKeyAlgorithms, alg)
}
return nil
}
func (c *ClientConfig) SetKexAlgorithms(value string) error {
for _, alg := range strings.Split(value, ",") {
isValid := false
for _, val := range allSupportedKexAlgos {
if val == alg {
isValid = true
break
}
}
if !isValid {
return errors.New(fmt.Sprintf(`DH KEX algorithm not supported: "%s"`, alg))
}
c.KeyExchanges = append(c.KeyExchanges, alg)
}
return nil
}
func (c *ClientConfig) SetCiphers(value string) error {
for _, inCipher := range strings.Split(value, ",") {
isValid := false
for _, knownCipher := range allSupportedCiphers {
if inCipher == knownCipher {
isValid = true
break
}
}
if !isValid {
return errors.New(fmt.Sprintf(`cipher not supported: "%s"`, inCipher))
}
c.Ciphers = append(c.Ciphers, inCipher)
}
return nil
}

@ -343,7 +343,7 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro
return err return err
} }
if pkgConfig.Verbose { if t.config.Verbose {
if t.config.ConnLog != nil { if t.config.ConnLog != nil {
t.config.ConnLog.ClientKex = myInit t.config.ConnLog.ClientKex = myInit
} }
@ -417,7 +417,7 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro
} else { } else {
result, err = t.client(kex, algs, &magics) result, err = t.client(kex, algs, &magics)
} }
if pkgConfig.Verbose { if t.config.Verbose {
if t.config.ConnLog != nil { if t.config.ConnLog != nil {
t.config.ConnLog.Crypto = result t.config.ConnLog.Crypto = result
} }
@ -452,12 +452,12 @@ func (t *handshakeTransport) server(kex kexAlgorithm, algs *algorithms, magics *
} }
} }
r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey) r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey, t.config)
return r, err return r, err
} }
func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) { func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) {
result, err := kex.Client(t.conn, t.config.Rand, magics) result, err := kex.Client(t.conn, t.config.Rand, magics, t.config)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -145,11 +145,11 @@ func LogServerHostKey(sshRawKey []byte) *ServerHostKeyJsonLog {
type kexAlgorithm interface { type kexAlgorithm interface {
// Server runs server-side key agreement, signing the result // Server runs server-side key agreement, signing the result
// with a hostkey. // with a hostkey.
Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error) Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer, c *Config) (*kexResult, error)
// Client runs the client-side key agreement. Caller is // Client runs the client-side key agreement. Caller is
// responsible for verifying the host key signature. // responsible for verifying the host key signature.
Client(p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) Client(p packetConn, rand io.Reader, magics *handshakeMagics, c *Config) (*kexResult, error)
// Create a JSON object for the kexAlgorithm group // Create a JSON object for the kexAlgorithm group
MarshalJSON() ([]byte, error) MarshalJSON() ([]byte, error)
@ -276,7 +276,7 @@ func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handsha
}, nil }, nil
} }
func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer, config *Config) (result *kexResult, err error) {
hashFunc := crypto.SHA1 hashFunc := crypto.SHA1
packet, err := c.readPacket() packet, err := c.readPacket()
if err != nil { if err != nil {
@ -381,10 +381,10 @@ func (kex *ecdh) GetNew(keyType string) kexAlgorithm {
return ret return ret
} }
func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics, config *Config) (*kexResult, error) {
kex.JsonLog.Parameters = new(ztoolsKeys.ECDHParams) kex.JsonLog.Parameters = new(ztoolsKeys.ECDHParams)
kex.JsonLog.Parameters.ServerPublic = new(ztoolsKeys.ECPoint) kex.JsonLog.Parameters.ServerPublic = new(ztoolsKeys.ECPoint)
if pkgConfig.Verbose { if config.Verbose {
kex.JsonLog.Parameters.ClientPublic = new(ztoolsKeys.ECPoint) kex.JsonLog.Parameters.ClientPublic = new(ztoolsKeys.ECPoint)
kex.JsonLog.Parameters.ClientPrivate = new(ztoolsKeys.ECDHPrivateParams) kex.JsonLog.Parameters.ClientPrivate = new(ztoolsKeys.ECDHPrivateParams)
} }
@ -394,7 +394,7 @@ func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (
return nil, err return nil, err
} }
if pkgConfig.Verbose { if config.Verbose {
kex.JsonLog.Parameters.ClientPublic.X = ephKey.PublicKey.X kex.JsonLog.Parameters.ClientPublic.X = ephKey.PublicKey.X
kex.JsonLog.Parameters.ClientPublic.Y = ephKey.PublicKey.Y kex.JsonLog.Parameters.ClientPublic.Y = ephKey.PublicKey.Y
kex.JsonLog.Parameters.ClientPrivate.Value = ephKey.D.Bytes() kex.JsonLog.Parameters.ClientPrivate.Value = ephKey.D.Bytes()
@ -494,7 +494,7 @@ func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool {
return true return true
} }
func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer, config *Config) (result *kexResult, err error) {
packet, err := c.readPacket() packet, err := c.readPacket()
if err != nil { if err != nil {
return nil, err return nil, err
@ -639,13 +639,13 @@ func (kp *curve25519KeyPair) generate(rand io.Reader) error {
// wrong order. // wrong order.
var curve25519Zeros [32]byte var curve25519Zeros [32]byte
func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics, config *Config) (*kexResult, error) {
var kp curve25519KeyPair var kp curve25519KeyPair
if err := kp.generate(rand); err != nil { if err := kp.generate(rand); err != nil {
return nil, err return nil, err
} }
if pkgConfig.Verbose { if config.Verbose {
kex.JsonLog.Parameters.ClientPublic = kp.pub[:] kex.JsonLog.Parameters.ClientPublic = kp.pub[:]
kex.JsonLog.Parameters.ClientPrivate = kp.priv[:] kex.JsonLog.Parameters.ClientPrivate = kp.priv[:]
} }
@ -700,7 +700,7 @@ func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handsh
}, nil }, nil
} }
func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer, config *Config) (result *kexResult, err error) {
packet, err := c.readPacket() packet, err := c.readPacket()
if err != nil { if err != nil {
return return

@ -83,12 +83,12 @@ func (gex *dhGEXSHA) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, e
return new(big.Int).Exp(theirPublic, myPrivate, gex.p), nil return new(big.Int).Exp(theirPublic, myPrivate, gex.p), nil
} }
func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshakeMagics, config *Config) (*kexResult, error) {
// Send GexRequest // Send GexRequest
kexDHGexRequest := kexDHGexRequestMsg{ kexDHGexRequest := kexDHGexRequestMsg{
MinBits: uint32(pkgConfig.GexMinBits), MinBits: uint32(config.GexMinBits),
PreferedBits: uint32(pkgConfig.GexPreferredBits), PreferedBits: uint32(config.GexPreferredBits),
MaxBits: uint32(pkgConfig.GexMaxBits), MaxBits: uint32(config.GexMaxBits),
} }
if err := c.writePacket(Marshal(&kexDHGexRequest)); err != nil { if err := c.writePacket(Marshal(&kexDHGexRequest)); err != nil {
return nil, err return nil, err
@ -111,7 +111,7 @@ func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshak
} }
// reject if p's bit length < pkgConfig.GexMinBits or > pkgConfig.GexMaxBits // reject if p's bit length < pkgConfig.GexMinBits or > pkgConfig.GexMaxBits
if kexDHGexGroup.P.BitLen() < int(pkgConfig.GexMinBits) || kexDHGexGroup.P.BitLen() > int(pkgConfig.GexMaxBits) { if kexDHGexGroup.P.BitLen() < int(config.GexMinBits) || kexDHGexGroup.P.BitLen() > int(config.GexMaxBits) {
return nil, fmt.Errorf("Server-generated gex p (dont't ask) is out of range (%d bits)", kexDHGexGroup.P.BitLen()) return nil, fmt.Errorf("Server-generated gex p (dont't ask) is out of range (%d bits)", kexDHGexGroup.P.BitLen())
} }
@ -128,7 +128,7 @@ func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshak
X: X, X: X,
} }
if gex.JsonLog != nil && pkgConfig.Verbose { if gex.JsonLog != nil && config.Verbose {
gex.JsonLog.Parameters.ClientPrivate = x gex.JsonLog.Parameters.ClientPrivate = x
gex.JsonLog.Parameters.ClientPublic = X gex.JsonLog.Parameters.ClientPublic = X
} }
@ -164,9 +164,9 @@ func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshak
h := gex.hashFunc.New() h := gex.hashFunc.New()
magics.write(h) magics.write(h)
writeString(h, kexDHGexReply.HostKey) writeString(h, kexDHGexReply.HostKey)
binary.Write(h, binary.BigEndian, uint32(pkgConfig.GexMinBits)) binary.Write(h, binary.BigEndian, uint32(config.GexMinBits))
binary.Write(h, binary.BigEndian, uint32(pkgConfig.GexPreferredBits)) binary.Write(h, binary.BigEndian, uint32(config.GexPreferredBits))
binary.Write(h, binary.BigEndian, uint32(pkgConfig.GexMaxBits)) binary.Write(h, binary.BigEndian, uint32(config.GexMaxBits))
writeInt(h, gex.p) writeInt(h, gex.p)
writeInt(h, gex.g) writeInt(h, gex.g)
writeInt(h, X) writeInt(h, X)
@ -184,7 +184,7 @@ func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshak
}, nil }, nil
} }
func (gex *dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { func (gex *dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer, config *Config) (result *kexResult, err error) {
// *Receive GexRequest* // *Receive GexRequest*
packet, err := c.readPacket() packet, err := c.readPacket()
if err != nil { if err != nil {
@ -196,11 +196,11 @@ func (gex *dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshak
} }
// smoosh the user's preferred size into our own limits // smoosh the user's preferred size into our own limits
if kexDHGexRequest.PreferedBits > uint32(pkgConfig.GexMaxBits) { if kexDHGexRequest.PreferedBits > uint32(config.GexMaxBits) {
kexDHGexRequest.PreferedBits = uint32(pkgConfig.GexMaxBits) kexDHGexRequest.PreferedBits = uint32(config.GexMaxBits)
} }
if kexDHGexRequest.PreferedBits < uint32(pkgConfig.GexMinBits) { if kexDHGexRequest.PreferedBits < uint32(config.GexMinBits) {
kexDHGexRequest.PreferedBits = uint32(pkgConfig.GexMinBits) kexDHGexRequest.PreferedBits = uint32(config.GexMinBits)
} }
// fix min/max if they're inconsistent. technically, we could just pout // fix min/max if they're inconsistent. technically, we could just pout
// and hang up, but there's no harm in giving them the benefit of the // and hang up, but there's no harm in giving them the benefit of the
@ -251,9 +251,9 @@ func (gex *dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshak
h := gex.hashFunc.New() h := gex.hashFunc.New()
magics.write(h) magics.write(h)
writeString(h, hostKeyBytes) writeString(h, hostKeyBytes)
binary.Write(h, binary.BigEndian, uint32(pkgConfig.GexMinBits)) binary.Write(h, binary.BigEndian, uint32(config.GexMinBits))
binary.Write(h, binary.BigEndian, uint32(pkgConfig.GexPreferredBits)) binary.Write(h, binary.BigEndian, uint32(config.GexPreferredBits))
binary.Write(h, binary.BigEndian, uint32(pkgConfig.GexMaxBits)) binary.Write(h, binary.BigEndian, uint32(config.GexMaxBits))
writeInt(h, gex.p) writeInt(h, gex.p)
writeInt(h, gex.g) writeInt(h, gex.g)
writeInt(h, kexDHGexInit.X) writeInt(h, kexDHGexInit.X)

@ -1,8 +1,13 @@
package modules package modules
import ( import (
"net"
"strconv"
"time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/zmap/zgrab2" "github.com/zmap/zgrab2"
"github.com/zmap/zgrab2/lib/ssh"
) )
type SSHFlags struct { type SSHFlags struct {
@ -11,6 +16,11 @@ type SSHFlags struct {
KexAlgorithms string `long:"kex-algorithms" description:"Set SSH Key Exchange Algorithms"` KexAlgorithms string `long:"kex-algorithms" description:"Set SSH Key Exchange Algorithms"`
HostKeyAlgorithms string `long:"host-key-algorithms" description:"Set SSH Host Key Algorithms"` HostKeyAlgorithms string `long:"host-key-algorithms" description:"Set SSH Host Key Algorithms"`
NegativeOne bool `long:"negative-one" description:"Set SSH DH kex value to -1 in the selected group"` NegativeOne bool `long:"negative-one" description:"Set SSH DH kex value to -1 in the selected group"`
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"`
} }
type SSHModule struct { type SSHModule struct {
@ -57,6 +67,44 @@ func (s *SSHScanner) InitPerSender(senderID int) error {
func (s *SSHScanner) GetName() string { func (s *SSHScanner) GetName() string {
return s.config.Name return s.config.Name
} }
func (s *SSHScanner) Scan(t zgrab2.ScanTarget, port uint) (interface{}, error) {
return nil, nil func (s *SSHScanner) makeSSHGrabber(hlog *ssh.HandshakeLog) func(string) error {
return func(netAddr string) error {
sshConfig := ssh.MakeSSHConfig()
sshConfig.Timeout = time.Duration(s.config.Timeout) * time.Second
sshConfig.ConnLog = hlog
sshConfig.ClientVersion = s.ClientID
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
_, err := ssh.Dial("tcp", netAddr, sshConfig)
if err != nil {
return err
}
return nil
}
}
func (s *SSHScanner) Scan(t zgrab2.ScanTarget) (interface{}, error) {
data := new(ssh.HandshakeLog)
sshGrabber := s.makeSSHGrabber(data)
//TODO: domain name?
port := strconv.FormatUint(uint64(s.config.Port), 10)
rhost := net.JoinHostPort(t.IP.String(), port)
err := sshGrabber(rhost)
return data, err
} }