Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package sshamble for openSUSE:Factory checked in at 2025-09-22 16:39:33 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/sshamble (Old) and /work/SRC/openSUSE:Factory/.sshamble.new.27445 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "sshamble" Mon Sep 22 16:39:33 2025 rev:3 rq:1306288 version:0.3.3 Changes: -------- --- /work/SRC/openSUSE:Factory/sshamble/sshamble.changes 2024-10-04 17:11:12.334494808 +0200 +++ /work/SRC/openSUSE:Factory/.sshamble.new.27445/sshamble.changes 2025-09-22 16:40:17.010933473 +0200 @@ -1,0 +2,31 @@ +Fri Sep 12 17:48:01 UTC 2025 - Martin Hauke <[email protected]> + +- Update to version 0.3.3 + This release improves interact handling and introduces two new + command-line options: + * --one-session-only This boolean option instructs sshamble to + skip additional checks after the first successful session for + that target. This is helpful for devices where many methods + bypass authentication. + * --session-poke This string option defines what bytes to send + into new sessions to elicit responses. This field can be plain + ascii or escaped hex strings. For exampleshow + version\r\n\x0a\x0d will send a show version command followed + by two CRLFs. Please take into account your local shell + handling when using this feature (place the argument in + single-quotes, etc.). + * Clarify that all output is JSONL (NDJSON) and update + dependencies. + * Update analyzers. +- Update to version 0.2.1 + * Dependency updates. +- Update to version 0.2.0 + * Switch to https://github.com/runZeroInc/excrypto for forked + dependencies. + * Automatic badkeys.info blocklist lookups. + * Additional authentication bypass methods. + * Wider algorithm and host key support. + * Experimental blind exec vuln checks. + * Target filtering with --skip-versions. + +------------------------------------------------------------------- Old: ---- sshamble-0.0.5.tar.gz New: ---- sshamble-0.3.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ sshamble.spec ++++++ --- /var/tmp/diff_new_pack.sipA0R/_old 2025-09-22 16:40:18.206983726 +0200 +++ /var/tmp/diff_new_pack.sipA0R/_new 2025-09-22 16:40:18.210983894 +0200 @@ -1,8 +1,8 @@ # # spec file for package sshamble # -# Copyright (c) 2024 SUSE LLC -# Copyright (c) 2024, Martin Hauke <[email protected]> +# Copyright (c) 2025 SUSE LLC and contributors +# Copyright (c) 2024-2025, Martin Hauke <[email protected]> # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ Name: sshamble -Version: 0.0.5 +Version: 0.3.3 Release: 0 Summary: Security testing toolset for SSH License: BSD-2-Clause @@ -55,6 +55,6 @@ %files %license LICENSE.md -%doc README.md README.crypto.md SECURITY.md +%doc README.md SECURITY.md %{_bindir}/sshamble ++++++ sshamble-0.0.5.tar.gz -> sshamble-0.3.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/README.crypto.md new/sshamble-0.3.3/README.crypto.md --- old/sshamble-0.0.5/README.crypto.md 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/README.crypto.md 1970-01-01 01:00:00.000000000 +0100 @@ -1,19 +0,0 @@ -# SSHamble Patches for x/crypto/ssh - -This repository includes a fork of the Go x/crypto package. - -To maintain this fork, first rediff against upstream: - -$ ./crypto.rediff.sh - -This creates a file named crypto.patch. - -Now resync with upstream using: - -$ ./crypto.resync.sh - -This applies the patch on top of upstream. - -Review the changes, fix conflicts, and commit the results in ./crypto - - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/README.md new/sshamble-0.3.3/README.md --- old/sshamble-0.0.5/README.md 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/README.md 2025-08-19 06:31:06.000000000 +0200 @@ -20,7 +20,7 @@ Binaries are available from the [releases page](https://github.com/runZeroInc/sshamble/releases). -To build SSHamble from source, ensure that you have a recent version of Go (1.22.6+) installed. +To build SSHamble from source, ensure that you have a recent version of Go (1.24+) installed. You can use Go to install a binary into the `bin` directory in your GOPATH. @@ -42,16 +42,15 @@ $ ./sshamble -h ``` -To enable experimental [badkeys](https://badkeys.info) support, run the generator first: +To enable [badkeys](https://badkeys.info) support, run `sshamble badkeys-update` first, then scan. ```shell $ git clone https://github.com/runZeroInc/sshamble $ cd sshamble $ go generate ./... $ go build -o sshamble -$ ./sshamble -h +$ ./sshamble badkeys-update ``` - ## Usage ```console @@ -72,19 +71,20 @@ Start a network scan using: -$ ./sshamble scan -o results.json 192.168.0.0/24 +$ ./sshamble scan -o results.jsonl 192.168.0.0/24 Analyze the results using: -$ ./sshamble analyze -o results-directory results.json +$ ./sshamble analyze -o results-directory results.jsonl Usage: sshamble [command] Available Commands: - analyze Analyzes a scan JSON output file and buckets results - help Help about any command - scan Enumerates a set of targets for SSH capabilities and exposures + analyze Analyzes a scan JSON output file and buckets results + badkeys-update Updates the badkeys.info blocklist cache. + help Help about any command + scan Enumerates a set of targets for SSH capabilities and exposures Flags: -h, --help help for sshamble @@ -96,15 +96,14 @@ ```console $ ./sshamble scan -h - Enumerates a set of targets for SSH capabilities and exposures Usage: - sshamble scan [-p 22] [-u root,admin] [-o scan.json] [-l scan.log] [--log-level trace] 192.168.0.0/24 ... [flags] + sshamble scan [-p 22] [-u root,admin] [-o scan.jsonl] [-l scan.log] [--log-level trace] 192.168.0.0/24 ... [flags] Flags: - --categories string The list of categories to include. (default "bypass,gssapi,keyboard,password,pubkey,userenum,vuln") - --checks string The list of checks to run. Non-default ("userenum-none-timing,userenum-password-timing,userenum-pubkey-timing") (default "gssapi-any,keyboard-any,keyboard-empty,keyboard-null,keyboard-user,password-any,password-change-empty,password-change-null,password-empty,password-null,password-user,pubkey-any,pubkey-bulkhalf,pubkey-hunt,pubkey-user,skip-auth,skip-auth-method-empty,skip-auth-method-null,skip-auth-none,skip-auth-pubkeyany,skip-auth-success,skip-ssh-userauth,vuln-generic-env,vuln-gogs-env,vuln-ruckus-password-escape,vuln-softserve-env,vuln-tcp-forward") + --categories string The list of categories to include. (default "bypass,gssapi,hostkey,keyboard,password,pubkey,userenum,vuln") + --checks string The list of checks to run. Non-default ("userenum-none-timing,userenum-password-timing,userenum-pubkey-timing,vuln-exec-skip-auth,vuln-exec-skip-userauth") (default "badkeys-blocklist,gssapi-any,keyboard-any,keyboard-empty,keyboard-null,keyboard-user,password-any,password-change-empty,password-change-null,password-empty,password-null,password-user,pubkey-any,pubkey-bulkhalf,pubkey-hunt,pubkey-user,skip-auth,skip-auth-method-empty,skip-auth-method-null,skip-auth-none,skip-auth-pubkeyany,skip-auth-success,skip-ssh-userauth,vuln-generic-env,vuln-gogs-env,vuln-ruckus-password-escape,vuln-softserve-env,vuln-tcp-forward") --client-version string The client version string to send (default "OpenSSH_9.8p1") --config string config file (default is $HOME/.sshamble.json) -h, --help help for scan @@ -114,6 +113,7 @@ -l, --log string The file to write logs to (default is stderr) (default "-") -L, --log-level string The log level to write (trace,debug,info,warn,error) (default "info") -m, --max-connections uint The maximum number of concurrent connections (default 5000) + --one-session-only Only open one session per target -o, --output string The destination file for JSON output (default "stdout") --password string An optional password to try for authentication --password-file string An optional file with clear-text passwords to try for authentication @@ -125,8 +125,10 @@ --pubkey-hunt-conn-limit uint The number of public keys to test in each connection (default 250000) --pubkey-hunt-file string The optional file containing public keys to hunt --retries uint The retry count for subsequent failed connections after an initial success (default 2) + --session-poke string A byte sequence sent to sessions to elicit further responses (hex or ascii) (default "\\x0a\\x0d\\r\\n") + --skip-versions string A regular expression of SSH versions to skip (ex: '(?i)openssh|dropbear)' --timeout uint The number of seconds to wait for a target to respond (default 5) - --userenum-max-per-session-count uint The maximum number of authentication attempts per session (default 1023) + --userenum-max-per-session-count uint The maximum number of authentication atempts per session (default 1023) --userenum-test-count uint The number of tests to apply during username enumeration (default 2500) -u, --users string The list of usernames to test on each target (comma-separated) (default "root") ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/auth/auth.go new/sshamble-0.3.3/auth/auth.go --- old/sshamble-0.0.5/auth/auth.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/auth/auth.go 2025-08-19 06:31:06.000000000 +0200 @@ -97,6 +97,8 @@ defer conn.Close() res.Stage = "connect" + options.Logger.Tracef("%s connection established %v", addr, conn.RemoteAddr()) + if options.StopStage == res.Stage { return res } @@ -141,6 +143,8 @@ res.HostKeys[uac.HostKeyType] = base64.StdEncoding.EncodeToString(uac.HostKey) res.KexInit = &uac.ServerKexInit res.Stage = "kex" + options.Logger.Tracef("%s kex completed", addr) + if options.StopStage == res.Stage { return res } @@ -150,6 +154,8 @@ exts := make(map[string][]byte) if !options.SkipStage("ssh-userauth") { + options.Logger.Tracef("%s sending ssh-userauth", addr) + // Request the ssh-userauth service exts, err = uac.RequestUserAuth() if err != nil { @@ -166,6 +172,8 @@ _ = conn.SetDeadline(time.Now().Add(options.Timeout * 3)) if !options.SkipStage("auth") { + options.Logger.Tracef("%s sending auth", addr) + // Authenticate using the callback err = AuthHandler(uac, exts, res) if err != nil { @@ -193,7 +201,7 @@ } // The server accepted our authentication - sconn, sshChans, sshReqs := uac.Mux() + sconn, sshChans, sshReqs := uac.Mux(options.ignoreChannelOpenReply) defer sconn.Close() // Use a multiple of the timeout for the session handler @@ -215,10 +223,13 @@ CloseAfterTimeout(authDoneCtx, options.Timeout*10, addr, conn, sconn, sclient) }() + options.Logger.Tracef("%s opening session", addr) + // Open a session res.Stage = "open-session" ses, err := sclient.NewSession() if err != nil { + options.Logger.Tracef("%s session error %v", addr, err) res.Error = err.Error() if merr := uac.MuxError(); merr != nil { res.Error = fmt.Sprintf("%v (mux: %v)", err, merr) @@ -234,7 +245,9 @@ // Run a custom session handler and let the caller set any timeouts if options.sessionHandler != nil { - // Disable the automatic socket close for custom session handers + options.Logger.Tracef("%s session handler running", addr) + + // Disable the automatic socket close for custom session handlers authDoneCancel() // Disable the socket deadline @@ -282,33 +295,43 @@ time.Sleep(time.Second) } + // Prod the session for more output if stdin is enabled if stdIn != nil { // Send input likely to trigger useful replies: _, err := stdIn.Write([]byte(options.SessionPoke)) if err != nil { options.Logger.Errorf("%s stdin write returned error: %v", addr, err) } - } - // Give the session a second to produce any output - time.Sleep(time.Second) + // Give the session a second to produce any output + time.Sleep(time.Second) - // Peek at the buffered output to determine what other input to send - peek := stdOut.Peek() - peek = append(peek, stdErr.Peek()...) + // Peek at the buffered output to determine what other input to send + peek := stdOut.Peek() + peek = append(peek, stdErr.Peek()...) + + lcVersion := strings.ToLower(res.Version) + + // Poke telnet-in-ssh specifically by trying to use the shell escape + if bytes.Contains(peek, []byte("scape character is")) { + _, err := stdIn.Write([]byte("\x1d!id||uname||sh\r\n")) + if err != nil { + options.Logger.Errorf("%s stdin write returned error: %v", addr, err) + } else { + time.Sleep(time.Second) + } + } - // Poke telnet-in-ssh specifically by trying to use the shell escape - if bytes.Contains(peek, []byte("Escape character is")) && stdIn != nil { - _, err := stdIn.Write([]byte("\x1d!id||uname||sh\r\n")) - if err != nil { - options.Logger.Errorf("%s stdin write returned error: %v", addr, err) - } else { - time.Sleep(time.Second) + // Poke various network devices with "show version\r\n" to get better proof data + if strings.Contains(lcVersion, "cisco") || strings.Contains(lcVersion, "raisecom") { + _, err := stdIn.Write([]byte("show version\r\n")) + if err != nil { + options.Logger.Errorf("%s stdin write returned error: %v", addr, err) + } else { + time.Sleep(time.Second) + } } - } - // Close stdin - if stdIn != nil { stdIn.Close() } @@ -378,6 +401,46 @@ if err == nil { time.Sleep(time.Second) } + + if stdIn != nil { + _, err := stdIn.Write([]byte(options.SessionPoke)) + if err != nil { + options.Logger.Errorf("%s stdin write returned error: %v", prefix, err) + } + } + + // Give the session a second to produce any output + time.Sleep(time.Second) + + // Peek at the buffered output to determine what other input to send + peek := stdOut.Peek() + peek = append(peek, stdErr.Peek()...) + + res.SessionOutput = CleanSessionOutput(peek) + return err +} + +func ScrapeExec(options *Options, prefix string, res *AuthResult, ses *ssh.Session, cmd string) error { + // Buffer stdout/stderr to mutex-protected byte array + stdOut := NewSyncByteBuffer(1024 * 16) + stdErr := NewSyncByteBuffer(1024 * 16) + ses.Stdout = stdOut + ses.Stderr = stdErr + stdIn, err := ses.StdinPipe() + if err != nil { + options.Logger.Errorf("%s failed to open stdin pipe: %v", prefix, err) + } + + // Try to run the specific command + err = ses.Start(cmd) + if err != nil { + options.Logger.Errorf("%s exec command returned error: %v", prefix, err) + } + + // Give the server a second to process the command + if err == nil { + time.Sleep(time.Second) + } if stdIn != nil { _, err := stdIn.Write([]byte(options.SessionPoke)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/auth/options.go new/sshamble-0.3.3/auth/options.go --- old/sshamble-0.0.5/auth/options.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/auth/options.go 2025-08-19 06:31:06.000000000 +0200 @@ -30,9 +30,10 @@ Logger *logrus.Logger SessionPoke string - skipStages []string - sessionHandler SessionHandler - postAuthHandler PostAuthHandler + skipStages []string + sessionHandler SessionHandler + postAuthHandler PostAuthHandler + ignoreChannelOpenReply bool } func (o *Options) WithRetries(limit uint) *Options { @@ -53,6 +54,12 @@ return &n } +func (o *Options) WithIgnoreChannelOpenReply(v bool) *Options { + n := *o + n.ignoreChannelOpenReply = v + return &n +} + func (o *Options) WithTimeout(d time.Duration) *Options { n := *o n.Timeout = d diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/badkeys/badkeys.go new/sshamble-0.3.3/badkeys/badkeys.go --- old/sshamble-0.0.5/badkeys/badkeys.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/badkeys/badkeys.go 2025-08-19 06:31:06.000000000 +0200 @@ -7,26 +7,83 @@ "os" "path/filepath" + "github.com/runZeroInc/excrypto/crypto/dsa" + "github.com/runZeroInc/excrypto/crypto/ecdh" + "github.com/runZeroInc/excrypto/crypto/ecdsa" + "github.com/runZeroInc/excrypto/crypto/ed25519" + + "github.com/runZeroInc/excrypto/crypto/rsa" "github.com/runZeroInc/excrypto/crypto/sha256" + "github.com/runZeroInc/excrypto/crypto/x509" "github.com/runZeroInc/excrypto/x/crypto/ssh" + + stddsa "crypto/dsa" + stdecdh "crypto/ecdh" + stdecdsa "crypto/ecdsa" + stded25519 "crypto/ed25519" + stdrsa "crypto/rsa" + + stdssh "golang.org/x/crypto/ssh" ) const BadKeysMetaURL = "https://update.badkeys.info/v0/badkeysdata.json" -func PrefixFromPublicKey(pub ssh.PublicKey) ([]byte, error) { - var res []byte - switch pub.Type() { - case ssh.KeyAlgoRSA: - pk, ok := pub.(ssh.RSAPublicKey) - if !ok { - return nil, fmt.Errorf("%s doesn't implement RSAPublicKey", pub.Type()) +// PrefixFromPublicKey implements the badkeys `blocklistmaker` hashing method +func PrefixFromPublicKey(pub any) ([]byte, error) { + var rawb []byte + switch pub := pub.(type) { + + case ssh.PublicKey: + if cpk, ok := pub.(ssh.CryptoPublicKey); ok { + return PrefixFromPublicKey(cpk.CryptoPublicKey()) + } + return nil, fmt.Errorf("unsupported excrypto ssh public key: %v", pub.Type()) + case stdssh.PublicKey: + if cpk, ok := pub.(stdssh.CryptoPublicKey); ok { + return PrefixFromPublicKey(cpk.CryptoPublicKey()) } - res = pk.ToRSAPublicKey().N.Bytes() + return nil, fmt.Errorf("unsupported stdlib ssh public key: %v", pub.Type()) + + case *rsa.PublicKey: + rawb = pub.N.Bytes() + case *stdrsa.PublicKey: + rawb = pub.N.Bytes() + + case *ecdsa.PublicKey: + rawb = pub.X.Bytes() + case *stdecdsa.PublicKey: + rawb = pub.X.Bytes() + + case ed25519.PublicKey: + rawb = pub + case stded25519.PublicKey: + rawb = pub + + case x509.X25519PublicKey: + rawb = pub + /* + // Not defined by stdlib + case stdx509.X25519PublicKey: + rawb = pub + */ + + case *ecdh.PublicKey: + rawb = pub.Bytes() // Verify + case *stdecdh.PublicKey: + rawb = pub.Bytes() // Verify + + case *dsa.PublicKey: + rawb = pub.Y.Bytes() + case *stddsa.PublicKey: + rawb = pub.Y.Bytes() + + case nil: + return nil, fmt.Errorf("unsupported nil key") default: - res = pub.Marshal() + return nil, fmt.Errorf("unsupported key: %T", pub) } - hash := sha256.Sum256(res) - return hash[0:15], nil + sha256sum := sha256.Sum256(rawb) + return sha256sum[0:15], nil } // GetExecutablePath returns the full path to the running binary diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/badkeys/blocklist.go new/sshamble-0.3.3/badkeys/blocklist.go --- old/sshamble-0.0.5/badkeys/blocklist.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/badkeys/blocklist.go 2025-08-19 06:31:06.000000000 +0200 @@ -72,11 +72,13 @@ } repo, ok := tset.Repos[int(block[15])] if !ok { - return nil, fmt.Errorf("repo %d is missing", block[15]) + // No repo ID, likely private + return &Result{Private: true}, nil } info, ok := tset.LookupMap[binary.BigEndian.Uint64(block[:8])] if !ok { - return nil, fmt.Errorf("lookup %x is missing", block[:8]) + // No lookup key, likely private + return &Result{Private: true, RepoID: int8(repo.ID)}, nil } parts := make([]string, len(info)) for i, lk := range info { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/badkeys/cache_test.go new/sshamble-0.3.3/badkeys/cache_test.go --- old/sshamble-0.0.5/badkeys/cache_test.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/badkeys/cache_test.go 2025-08-19 06:31:06.000000000 +0200 @@ -65,7 +65,7 @@ t.Errorf("unexpected result: %s", diff) } expURL := "https://github.com/badkeys/debianopenssl/blob/main/rsa3072/ssh/be32/29491.key" - resURL := res.ToURL() + resURL := res.GetURL() if resURL != expURL { t.Errorf("unexpected url %s got %s", expURL, resURL) } @@ -75,6 +75,21 @@ if err == nil { t.Fatalf("bad hash returned result: %v", res) } + + for i := range len(bl.Blocks) / 16 { + pre := bl.Blocks[(i * 16) : (i*16)+15] + res, err := bl.LookupPrefix(pre) + if err != nil { + t.Errorf("key %s returned error %v", hex.EncodeToString(pre), err) + continue + } + if res.Private { + continue + } + if res.Repo == "" || res.RepoID == 0 || res.RepoName == "" || res.RepoPath == "" || res.RepoType == "" { + t.Errorf("non-private key %s returned incomplete results %#v", hex.EncodeToString(pre), res) + } + } } // TestKeyDebOpenSSLRSA3072BE3229491 is the public key from https://github.com/badkeys/debianopenssl/blob/main/rsa3072/ssh/be32/29491.key diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/badkeys/result.go new/sshamble-0.3.3/badkeys/result.go --- old/sshamble-0.0.5/badkeys/result.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/badkeys/result.go 2025-08-19 06:31:06.000000000 +0200 @@ -1,6 +1,9 @@ package badkeys -import "path" +import ( + "path" + "strconv" +) type Result struct { Repo string @@ -9,11 +12,24 @@ RepoPath string RepoName string KeyPath string + Private bool + Hash string } -func (r *Result) ToURL() string { +func (r *Result) GetID() string { + if r.Private { + repStr := strconv.FormatUint(uint64(r.RepoID), 10) + return "badkeys-private-" + repStr + "-" + r.Hash + } + return "badkeys-" + r.RepoType + "-" + r.Repo + "-" + r.RepoPath + "-" + r.Hash +} + +func (r *Result) GetURL() string { + if r.Private { + return "unpublished://" + r.GetID() + "-" + r.Hash + } if r.RepoType != "github" { - return "" + return "https://" + r.RepoType + "/" + path.Join(r.Repo, "blob", r.RepoPath, r.KeyPath) } return "https://github.com/" + path.Join(r.Repo, "blob", r.RepoPath, r.KeyPath) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/cmd/check_bypass.go new/sshamble-0.3.3/cmd/check_bypass.go --- old/sshamble-0.0.5/cmd/check_bypass.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/cmd/check_bypass.go 2025-08-19 06:31:06.000000000 +0200 @@ -203,7 +203,7 @@ res := auth.SSHAuth(addr, options.WithIgnoreAuthError(), auth.SSHAuthHandlerSingle(customAuth)) if bypassAtInterestingStage(tname, addr, conf, res) { - conf.Logger.Warnf("%s %s provided a session with empty auth method '%s': %s", addr, tname, res.Stage, res.SessionOutput) + conf.Logger.Warnf("%s %s provided a session with NULL auth method '%s': %s", addr, tname, res.Stage, res.SessionOutput) res.SessionMethod = tname root.SessionMethod = tname root.SessionOutput = res.SessionOutput diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/cmd/check_hostkeys.go new/sshamble-0.3.3/cmd/check_hostkeys.go --- old/sshamble-0.0.5/cmd/check_hostkeys.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/cmd/check_hostkeys.go 2025-08-19 06:31:06.000000000 +0200 @@ -2,6 +2,8 @@ import ( "encoding/base64" + "encoding/hex" + "strconv" "github.com/runZeroInc/excrypto/x/crypto/ssh" "github.com/runZeroInc/sshamble/auth" @@ -19,7 +21,7 @@ return nil } - for hkt, hkv := range root.HostKeys { + for _, hkv := range root.HostKeys { raw, err := base64.StdEncoding.DecodeString(hkv) if err != nil { continue @@ -37,13 +39,23 @@ continue } - conf.Logger.Warnf("%s %s found compromised hostkey: %s", addr, tname, bkr.ToURL()) - - root.AddVuln(auth.VulnResult{ - ID: "badkeys-" + bkr.RepoType + "-" + bkr.Repo + "-" + bkr.RepoPath + "-" + hkt, - Ref: "https://badkeys.info/", - Proof: bkr.ToURL(), - }) + if bkr.Private { + repStr := strconv.FormatUint(uint64(bkr.RepoID), 10) + hexPre := hex.EncodeToString(hpre) + conf.Logger.Warnf("%s %s found compromised unpublished hostkey with repo %s and hash %s", addr, tname, repStr, hexPre) + root.AddVuln(auth.VulnResult{ + ID: bkr.GetID(), + Ref: "https://badkeys.info/", + Proof: repStr + "-" + hexPre, + }) + } else { + conf.Logger.Warnf("%s %s found compromised hostkey: %s", addr, tname, bkr.GetURL()) + root.AddVuln(auth.VulnResult{ + ID: bkr.GetID(), + Ref: "https://badkeys.info/", + Proof: bkr.GetURL(), + }) + } } return nil diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/cmd/check_pubkey.go new/sshamble-0.3.3/cmd/check_pubkey.go --- old/sshamble-0.0.5/cmd/check_pubkey.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/cmd/check_pubkey.go 2025-08-19 06:31:06.000000000 +0200 @@ -129,7 +129,7 @@ } defer hf.Close() - conf.Logger.Debugf("%s %s is running ", addr, tname) + conf.Logger.Debugf("%s %s is running", addr, tname) stime := time.Now() perSlice := int(gPubKeyHuntConnLimit) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/cmd/check_vuln.go new/sshamble-0.3.3/cmd/check_vuln.go --- old/sshamble-0.0.5/cmd/check_vuln.go 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.3/cmd/check_vuln.go 2025-08-19 06:31:06.000000000 +0200 @@ -0,0 +1,9 @@ +package cmd + +func initVulnChecks() { + // Register pre-session vulnerability checks + + // Disabled by default due to false positives today + registerCheck(checkVulnExecSkipUserAuth, "vuln", false, false) + registerCheck(checkVulnExecSkipAuth, "vuln", false, false) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/cmd/check_vuln_exec.go new/sshamble-0.3.3/cmd/check_vuln_exec.go --- old/sshamble-0.0.5/cmd/check_vuln_exec.go 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.3/cmd/check_vuln_exec.go 2025-08-19 06:31:06.000000000 +0200 @@ -0,0 +1,97 @@ +package cmd + +import ( + "fmt" + "io" + "net" + "strings" + "time" + + "github.com/runZeroInc/excrypto/x/crypto/ssh" + "github.com/runZeroInc/sshamble/auth" +) + +const checkVulnExecSkipUserAuth = "vuln-exec-skip-userauth" + +func sshCheckVulnExecSkipUserAuth(addr string, conf *ScanConfig, options *auth.Options, root *auth.AuthResult) *auth.AuthResult { + tname := checkVulnExecSkipUserAuth + return sshCheckVulnExecHelper(tname, addr, conf, options, root, []string{"ssh-userauth", "auth"}) +} + +const checkVulnExecSkipAuth = "vuln-exec-skip-auth" + +func sshCheckVulnExecSkipAuth(addr string, conf *ScanConfig, options *auth.Options, root *auth.AuthResult) *auth.AuthResult { + tname := checkVulnExecSkipUserAuth + conf.Logger.Debugf("%s %s is running for user %s", addr, tname, options.Username) + return sshCheckVulnExecHelper(tname, addr, conf, options, root, []string{"auth"}) +} + +func sshCheckVulnExecHelper(tname string, addr string, conf *ScanConfig, options *auth.Options, root *auth.AuthResult, skipStages []string) *auth.AuthResult { + if !conf.IsCheckEnabled(tname) { + return nil + } + conf.Logger.Debugf("%s %s is running for user %s", addr, tname, options.Username) + + /* + This test identifies cases where the server processes the channel open and exec commands + but does not send a reply to either request (ex: Erlang-SSHD). The theory is that non-vulnerable + servers will either reply with an error or close the socket. + */ + var maxWaitSeconds = 5 + options = options. + WithSkipStages(skipStages...). + WithIgnoreChannelOpenReply(true). + WithSessionHandler(func(c net.Conn, sclient *ssh.Client, ses *ssh.Session, r *auth.AuthResult) error { + _ = c.SetDeadline(time.Now().Add(time.Second * time.Duration(maxWaitSeconds))) + stime := time.Now() + err := ses.Start(`help`) + if err == nil { + conf.Logger.Debugf("%s %s completed exec without error", addr, tname) + return nil + } + if err == io.EOF && time.Since(stime) > time.Second*time.Duration(maxWaitSeconds-1) { + conf.Logger.Debugf("%s %s completed exec with timeout", addr, tname) + return nil + } + conf.Logger.Debugf("%s %s unlikely exec error %v after %s", addr, tname, err, time.Since(stime)) + return err + }) + + res := auth.SSHAuth(addr, options, auth.SSHAuthHandlerSingle(ssh.None())) + if bypassAtInterestingStage(tname, addr, conf, res) { + var proof = root.SessionOutput + if res.SessionOutput == "" { + proof = "timeout reached" + } + root.AddVuln(auth.VulnResult{ + ID: tname, + Ref: "https://www.openwall.com/lists/oss-security/2025/04/16/2", + Proof: fmt.Sprintf("exec may have been processed: %s (skipped stages: %v)", proof, strings.Join(skipStages, ",")), + }) + return res + } + + // An alternate implementation using raw messages instead + /* + cb := func(c net.Conn, uac *ssh.UnauthClientConn, r *auth.AuthResult) error { + _ = c.SetDeadline(time.Now().Add(time.Second * 15)) + raw := uac.BuildChannelOpen(0, "session", nil) + uac.WriteRaw(raw, false) + raw = uac.BuildChannelRequestString(0, "exec", "id", true) + uac.WriteRaw(raw, false) + for { + reply, err := uac.ReadRaw() + if err != nil { + conf.Logger.Warnf("%s %s got error %#v", addr, tname, err) + break + } + + } + return nil + } + options = options.WithSkipStages("ssh-userauth", "auth").WithIgnoreAuthError().WithPostAuthHandler(cb) + */ + + // Note: For Erlang-SSHD, the payload `ssh:stop().` kills the service + return nil +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/cmd/cmd_analyze.go new/sshamble-0.3.3/cmd/cmd_analyze.go --- old/sshamble-0.0.5/cmd/cmd_analyze.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/cmd/cmd_analyze.go 2025-08-19 06:31:06.000000000 +0200 @@ -21,9 +21,9 @@ // analyzeCmd processes a scan output file and buckets results var analyzeCmd = &cobra.Command{ - Use: "analyze -o results-directory scan.json ...", - Short: "Analyzes a scan JSON output file and buckets results", - Long: "Analyzes a scan JSON output file and buckets results", + Use: "analyze -o results-directory scan.jsonl ...", + Short: "Analyzes a scan JSONL output file and buckets results", + Long: "Analyzes a scan JSONL output file and buckets results", Run: runAnalyze, } @@ -172,14 +172,11 @@ return nil } - if isHoneypot(conf, res) { - conf.writeAnalysisRecord("honeypots", res) - } - if conf.BadKeyCache != nil && isBadKey(conf, res) { conf.writeAnalysisRecord("badkeys", res) } + // Only write a single record for each session if name := isKnownDevice(conf, res); name != "" { conf.writeAnalysisRecord("session_known_"+name, res) } else if res.SessionMethod != "" { @@ -223,7 +220,7 @@ } func (conf *ScanConfig) writeAnalysisRecord(name string, res *auth.AuthResult) { - fd, err := os.OpenFile(filepath.Join(gOutput, filepath.Base(name)+".json"), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600) + fd, err := os.OpenFile(filepath.Join(gOutput, filepath.Base(name)+".jsonl"), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600) if err != nil { conf.Logger.Fatalf("failed to write file %s: %v", name, err) } @@ -268,9 +265,19 @@ fd.Close() } +// Simple honeypot detection using common strings +// TODO: Replace these with regexes or heuristics +// TODO: Keep track of whether the kex init is sent before the client version since this is an obvious tell for fake OpenSSH. var commonHoneypotStrings = []string{ "The programs included with the Debian GNU/Linux system are free software;", + "ABSOLUTELY NO WARRANTY", "Welcome to Ubuntu", + "microsoft-standard-WSL2", + "ost ~]$", + "ost tmp]$", + " ~tmp]$", + " /tmp]$", + "HONEYPOT", } func isHoneypot(conf *ScanConfig, res *auth.AuthResult) bool { @@ -284,7 +291,7 @@ func isBadKey(conf *ScanConfig, res *auth.AuthResult) bool { found := 0 - for hkt, hkv := range res.HostKeys { + for _, hkv := range res.HostKeys { raw, err := base64.StdEncoding.DecodeString(hkv) if err != nil { continue @@ -301,18 +308,28 @@ if err != nil { continue } - res.AddVuln(auth.VulnResult{ - ID: "badkeys-" + bkr.RepoType + "-" + bkr.Repo + "-" + bkr.RepoPath + "-" + hkt, - Ref: "https://badkeys.info/", - Proof: bkr.ToURL(), - }) + if bkr.Private { + res.AddVuln(auth.VulnResult{ + ID: bkr.GetID(), + Ref: "https://badkeys.info/", + Proof: bkr.GetURL(), + }) + } else { + res.AddVuln(auth.VulnResult{ + ID: bkr.GetID(), + Ref: "https://badkeys.info/", + Proof: bkr.GetURL(), + }) + } found++ } return found != 0 } var commonDeviceStrings = map[string]string{ - "sonicwall": "SonicWall", + "sonicwall1": "SonicWall", + "sonicwall2": "SonicWALL", + "sonicwall3": "Sonicwall", "atos": "ATOSNT Remote CLI", // No password "yamaha-rtx": "Error: Login access is restricted", "dlink": "D-Link Corporation", @@ -326,21 +343,36 @@ "lancom": "Connection No.:", "realpresence": "Here is what I know about myself", "mikrotik": "MikroTik RouterOS", - "exceed": "exceeds the specificaitons", + "exceed": "exceeds the specificaitons", // Typo intentional "hpswitch": "HEWLETT-PACKARD COMPANY, 3000 Hanover St", "gitee": "GITEE.COM does not provide shell access", "tl1": "Starting Interactive TL1", "sshs": "SSHS>", "keenetic": "https://keenetic", "vstfs2": "Your Git command did not succeed", + "vstfs3": "remote: Shell access is not supported", "cellrtr": "Cellular Router>", "ruckus": "Please login: \r\nPlease login", "snips.sh": "snips.sh", "mioffice_mfa": "https://xiaomi.f.mioffice.cn", "l2switch": "Welcome to Layer 2 Managed Switch", + "listensocks": "listensocks", + "axgate": "AxGate", + "gitops": "This port is reserved for git operations", + "segfault": "segfault", + "telnet": "Escape character is", + "busybox": "built-in shell", + "juniper": "Juniper Networks, Inc. All rights reserved.\r\n\n\r\n\r\n\rUsername: \n\rPassword:", + "snips": "snips.sh", + "syncronet": "Synchronet BBS", + "raisecom": "<capability>urn:ietf:params:", + "abilis": "Abilis CPX", } func isKnownDevice(conf *ScanConfig, res *auth.AuthResult) string { + if isHoneypot(conf, res) { + return "honeypots" + } for k, v := range commonDeviceStrings { if strings.Contains(strings.ToLower(res.SessionOutput), strings.ToLower(v)) { return k diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/cmd/cmd_root.go new/sshamble-0.3.3/cmd/cmd_root.go --- old/sshamble-0.0.5/cmd/cmd_root.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/cmd/cmd_root.go 2025-08-19 06:31:06.000000000 +0200 @@ -21,7 +21,7 @@ // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ - Use: "sshamble {scan -o results.json 192.168.0.0/24, analyze results.json -d results-dir}", + Use: "sshamble {scan -o results.jsonl 192.168.0.0/24, analyze results.jsonl -d results-dir}", Short: "An exploration tool for (in)secure shell services", Long: ` @@ -41,11 +41,11 @@ Start a network scan using: -$ sshamble scan -o results.json 192.168.0.0/24 +$ sshamble scan -o results.jsonl 192.168.0.0/24 Analyze the results using: -$ sshamble analyze -o results-directory results.json +$ sshamble analyze -o results-directory results.jsonl `, } @@ -68,6 +68,7 @@ initUserEnumChecks() initGSSAPIChecks() initHostkeyChecks() + initVulnChecks() cobra.OnInitialize(initConfig) @@ -117,7 +118,7 @@ return err } line = bytes.ReplaceAll(line, []byte{0x00}, []byte{}) - if gStdinManager != nil && gStdinManager.IsRawMode() { + if m := GetStdinManager(); m != nil && !m.IsRawMode() { // Use CRLF for raw mode and don't filter escapes line = append([]byte{'\r', '\n'}, bytes.TrimSpace(line)...) line = append(line, '\r', '\n') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/cmd/cmd_scan.go new/sshamble-0.3.3/cmd/cmd_scan.go --- old/sshamble-0.0.5/cmd/cmd_scan.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/cmd/cmd_scan.go 2025-08-19 06:31:06.000000000 +0200 @@ -23,7 +23,7 @@ // scanCmd handles scanning var scanCmd = &cobra.Command{ - Use: "scan [-p 22] [-u root,admin] [-o scan.json] [-l scan.log] [--log-level trace] 192.168.0.0/24 ...", + Use: "scan [-p 22] [-u root,admin] [-o scan.jsonl] [-l scan.log] [--log-level trace] 192.168.0.0/24 ...", Short: "Enumerates a set of targets for SSH capabilities and exposures", Long: "Enumerates a set of targets for SSH capabilities and exposures", Run: runScan, @@ -54,6 +54,10 @@ gLogfile string gLogLevel string gPProfPort string + gSkipVersions string + gSkipVersionsRegex *regexp.Regexp + gOneSessionOnly bool + gSessionPoke string interactMutex sync.Mutex ) @@ -132,6 +136,10 @@ scanCmd.Flags().StringVarP(&gLogLevel, "log-level", "L", "info", "The log level to write (trace,debug,info,warn,error)") scanCmd.Flags().StringVar(&gPProfPort, "pprof", "", "Start a Go pprof debug listener on the provided port") scanCmd.Flags().UintVar(&gRetries, "retries", 2, "The retry count for subsequent failed connections after an initial success") + scanCmd.Flags().StringVar(&gSkipVersions, "skip-versions", "", "A regular expression of SSH versions to skip (ex: '(?i)openssh|dropbear)'") + scanCmd.Flags().BoolVar(&gOneSessionOnly, "one-session-only", false, "Only open one session per target") + scanCmd.Flags().StringVar(&gSessionPoke, "session-poke", "\\x0a\\x0d\\r\\n", "A byte sequence sent to sessions to elicit further responses (hex or ascii)") + } var TestKeyRSASizes = []int{1024, 2048, 4096} @@ -239,6 +247,13 @@ conf.Logger.Fatalf("unable to read targets from stdin while interact is enabled") } + if gSkipVersions != "" { + gSkipVersionsRegex, err = regexp.Compile(gSkipVersions) + if err != nil { + conf.Logger.Fatalf("invalid skip-versions regex '%s': %v", gSkipVersions, err) + } + } + // Configure private key var privateKey ssh.Signer if gPrivateKeyFile != "" { @@ -342,7 +357,7 @@ Timeout: time.Second * time.Duration(gTimeout), Logger: conf.Logger, ClientVersion: gClientVersion, - SessionPoke: "\r\n\r\n", + SessionPoke: string(processEscapedByteString(gSessionPoke)), } } @@ -385,7 +400,7 @@ // Start the stdin manager if interaction was requested if gInteract != "none" && gInteract != "" { conf.Logger.Debugf("interaction enabled, starting stdin manager...") - gStdinManager = NewStdinManager() + NewStdinManager() } // Add on any command-line targets @@ -473,6 +488,7 @@ // GetSession runs through all potential checks that can lead to a session func (conf *ScanConfig) GetSession(addr string, options *auth.Options, cached *auth.AuthResult) (root *auth.AuthResult) { + // Start with a required "none" authentication check to determine server capabilities root = auth.SSHAuthNone(addr, options) @@ -499,6 +515,12 @@ return } + // Exit early if the version matches the skip regex + if gSkipVersionsRegex != nil && gSkipVersionsRegex.MatchString(root.Version) { + conf.Logger.Debugf("%s version %s matches skip-version pattern", addr, root.Version) + return + } + var res *auth.AuthResult shouldInteract := func() { @@ -510,14 +532,23 @@ } shouldReturn := func() bool { + + // Service is no longer available, stop trying if res != nil && res.Unreachable { conf.Logger.Errorf("%s failed to reconnect: %v", addr, res.Error) return true } + + // Interact on the first session (skipping additional checks) if gInteract == "first" && root.SessionMethod != "" { conf.Logger.Warnf("%s returned a session, interacting via %s", addr, root.SessionMethod) return true } + + // Break on the first session for this target + if gOneSessionOnly && root.SessionMethod != "" { + return true + } return false } @@ -657,6 +688,14 @@ } } + // Process pre-session vulnerability checks + vulnChecks := []sshCheckFunc{ + sshCheckVulnExecSkipUserAuth, + sshCheckVulnExecSkipAuth, + } + for _, check := range vulnChecks { + _ = check(addr, conf, options, root) + } return } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/cmd/interact.go new/sshamble-0.3.3/cmd/interact.go --- old/sshamble-0.0.5/cmd/interact.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/cmd/interact.go 2025-08-19 06:31:06.000000000 +0200 @@ -68,6 +68,7 @@ } var gStdinManager *stdinManager +var gStdinManagerM sync.Mutex type stdinManager struct { sync.Mutex @@ -76,11 +77,22 @@ rawMode bool } -func NewStdinManager() *stdinManager { +func GetStdinManager() *stdinManager { + gStdinManagerM.Lock() + defer gStdinManagerM.Unlock() + return gStdinManager +} + +func NewStdinManager() { + gStdinManagerM.Lock() + defer gStdinManagerM.Unlock() + if gStdinManager != nil { + return + } m := &stdinManager{ output: nil, } - return m + gStdinManager = m } func (m *stdinManager) CleanTerminal() { @@ -277,7 +289,12 @@ return fmt.Errorf("interact mode requires a controlling terminal") } - go gStdinManager.Relay(conf, intDoneCtx) + m := GetStdinManager() + if m == nil { + return fmt.Errorf("failed to get stdin manager") + } + + go m.Relay(conf, intDoneCtx) state := &interactSessionState{} @@ -298,10 +315,10 @@ }() // Make sure our terminal is in raw mode - if !gStdinManager.IsRawMode() { - gStdinManager.SetRawTerminalMode() - } - defer gStdinManager.RestoreTerminalMode() + m.SetRawTerminalMode() + + // Restore the terminal mode after completion + defer m.RestoreTerminalMode() // Close the ssh.Client first (last defer) to avoid deadlocks defer sclient.Close() @@ -314,7 +331,7 @@ if cmd == "" { continue } - fmt.Printf(" sshamble> " + cmd + "\r\n") + fmt.Printf(" sshamble> %s\r\n", cmd) spawned, err := conf.InteractCommand(addr, []byte(cmd), ses, sclient, state, sesInput) if err != nil { conf.Logger.Errorf("%s command '%s' returned error: %v", addr, cmd, err) @@ -356,7 +373,6 @@ sclient.Close() sesInput.Close() } - gStdinManager.RestoreTerminalMode() return nil } } @@ -380,18 +396,21 @@ fmt.Printf(" send string - Send string to the session\r\n") fmt.Printf(" sendb string - Send string to the session one byte at a time\r\n") fmt.Printf(" wait cmd arg1 arg2 - Send another command and wait for a reply\r\n") + fmt.Printf(" sleep duration - Sleep for the specified duration (1s, 100ms)\r\n") fmt.Printf("\r\n\r\n") } func (conf *ScanConfig) InteractRelay(addr string, quit chan bool, shell io.WriteCloser, sclient *ssh.Client, ses *ssh.Session, state *interactSessionState) { input := make(chan []byte, 1) - gStdinManager.SetOutput(input) + + m := GetStdinManager() + m.SetOutput(input) defer func() { if r := recover(); r != nil { conf.Logger.Errorf("panic: interact relay: %v", r) } - gStdinManager.SetOutput(nil) + m.SetOutput(nil) sclient.Close() }() @@ -558,11 +577,11 @@ case "send": data := strings.Join(args[1:], " ") - _, err := shell.Write(processSendBytes(data)) + _, err := shell.Write(processEscapedByteString(data)) return false, err case "sendb": - data := processSendBytes(strings.Join(args[1:], " ")) + data := processEscapedByteString(strings.Join(args[1:], " ")) for i := 0; i < len(data); i++ { _, err := shell.Write(data[i : i+1]) if err != nil { @@ -679,6 +698,16 @@ c.Close() }() return false, nil + case "sleep": + if len(args) < 2 { + return false, fmt.Errorf("missing sleep duration") + } + duration, err := time.ParseDuration(args[1]) + if err != nil { + return false, fmt.Errorf("invalid sleep duration: %v", err) + } + time.Sleep(duration) + return false, nil } return false, fmt.Errorf("unknown command: %s", strings.Join(args, " ")) @@ -686,7 +715,7 @@ var patReplaceHexEscape = regexp.MustCompile(`(\\x[a-fA-F0-9]{2}|\\[rnt])`) -func processSendBytes(s string) []byte { +func processEscapedByteString(s string) []byte { s = patReplaceHexEscape.ReplaceAllStringFunc(s, func(h string) string { if len(h) == 4 { b, _ := hex.DecodeString(h[2:]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/cmd/interact_test.go new/sshamble-0.3.3/cmd/interact_test.go --- old/sshamble-0.0.5/cmd/interact_test.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/cmd/interact_test.go 2025-08-19 06:31:06.000000000 +0200 @@ -19,7 +19,7 @@ {"\\r\\nABC\\tDEFG", []byte("\r\nABC\tDEFG")}, } for _, tc := range testCases { - got := processSendBytes(tc.Input) + got := processEscapedByteString(tc.Input) if !bytes.Equal(tc.Expected, got) { t.Errorf("got %s for %s, expected %s", hex.EncodeToString(got), tc.Input, hex.EncodeToString(tc.Expected)) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/cmd/session.go new/sshamble-0.3.3/cmd/session.go --- old/sshamble-0.0.5/cmd/session.go 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/cmd/session.go 2025-08-19 06:31:06.000000000 +0200 @@ -17,7 +17,7 @@ } func initSessionChecks() { - // Register vulnerability checks + // Register session-based vulnerability checks registerCheck(checkVulnGogsEnv, "vuln", true, true) registerCheck(checkVulnRuckusPasswordEscape, "vuln", true, true) registerCheck(checkVulnSoftServe, "vuln", true, true) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/go.mod new/sshamble-0.3.3/go.mod --- old/sshamble-0.0.5/go.mod 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/go.mod 2025-08-19 06:31:06.000000000 +0200 @@ -1,40 +1,35 @@ module github.com/runZeroInc/sshamble -go 1.23.1 +go 1.24 require ( - github.com/google/go-cmp v0.6.0 + github.com/google/go-cmp v0.7.0 github.com/logrusorgru/aurora/v3 v3.0.0 github.com/mmcloughlin/professor v0.0.0-20170922221822-6b97112ab8b3 - github.com/runZeroInc/excrypto v0.0.0-20240929210559-608d208b7065 + github.com/runZeroInc/excrypto v0.0.0-20250629231451-7e62a70a4cc3 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.8.1 - github.com/spf13/viper v1.19.0 + github.com/spf13/cobra v1.9.1 + github.com/spf13/viper v1.20.1 github.com/ulikunitz/xz v0.5.12 - golang.org/x/net v0.29.0 - golang.org/x/sys v0.25.0 - golang.org/x/term v0.24.0 - gonum.org/v1/gonum v0.15.1 + golang.org/x/crypto v0.41.0 + golang.org/x/term v0.34.0 + gonum.org/v1/gonum v0.16.0 ) require ( - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect - github.com/sagikazarmark/locafero v0.6.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.7.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/sagikazarmark/locafero v0.10.0 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spf13/afero v1.14.0 // indirect + github.com/spf13/cast v1.9.2 // indirect + github.com/spf13/pflag v1.0.7 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/weppos/publicsuffix-go v0.40.2 // indirect - go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/text v0.18.0 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.0.5/go.sum new/sshamble-0.3.3/go.sum --- old/sshamble-0.0.5/go.sum 2024-09-30 18:50:03.000000000 +0200 +++ new/sshamble-0.3.3/go.sum 2025-08-19 06:31:06.000000000 +0200 @@ -2,15 +2,16 @@ github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -18,12 +19,11 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v50 v50.2.0/go.mod h1:VBY8FB6yPIjrtKhozXv4FQupxKLS6H4m6xFZlT43q8Q= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -32,48 +32,38 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/logrusorgru/aurora/v3 v3.0.0 h1:R6zcoZZbvVcGMvDCKo45A9U/lzYyzl5NfYIvznmDfE4= github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mmcloughlin/professor v0.0.0-20170922221822-6b97112ab8b3 h1:2YMbJ6WbdQI9K73chxh9OWMDsZ2PNjAIRGTonp3T0l0= github.com/mmcloughlin/professor v0.0.0-20170922221822-6b97112ab8b3/go.mod h1:LQkXsHRSPIEklPCq8OMQAzYNS2NGtYStdNE/ej1oJU8= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/runZeroInc/excrypto v0.0.0-20240929201155-1ec9f9e6504e h1:N9uY2fZNa44bqH3K5isLVZYelrzzY2pEYXVOXjOC+vc= -github.com/runZeroInc/excrypto v0.0.0-20240929201155-1ec9f9e6504e/go.mod h1:ai0QX/YMgRVCUSlcByz860UOclsOFHBlczfBaKy47vo= -github.com/runZeroInc/excrypto v0.0.0-20240929203415-13f233cb0836 h1:tnBRzDl1wcloV3+hVohnuNrw0v9UdnIKHnasChIYd4Y= -github.com/runZeroInc/excrypto v0.0.0-20240929203415-13f233cb0836/go.mod h1:ai0QX/YMgRVCUSlcByz860UOclsOFHBlczfBaKy47vo= -github.com/runZeroInc/excrypto v0.0.0-20240929210559-608d208b7065 h1:/Dj751CaMI0hup6t+/TFqW852KVrou6za887nT0lhtE= -github.com/runZeroInc/excrypto v0.0.0-20240929210559-608d208b7065/go.mod h1:ai0QX/YMgRVCUSlcByz860UOclsOFHBlczfBaKy47vo= +github.com/runZeroInc/excrypto v0.0.0-20250629231451-7e62a70a4cc3 h1:nHWx/OmoqfYMkTKY383N//ll3a36JjoszUqurhucPW4= +github.com/runZeroInc/excrypto v0.0.0-20250629231451-7e62a70a4cc3/go.mod h1:Kk/CHQ2eYnHRYmR1eT1mA6L8LbNXA10fg8m8CqF6s2E= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= -github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sagikazarmark/locafero v0.10.0 h1:FM8Cv6j2KqIhM2ZK7HZjm4mpj9NBktLgowT1aN9q5Cc= +github.com/sagikazarmark/locafero v0.10.0/go.mod h1:Ieo3EUsjifvQu4NZwV5sPd4dwvu0OCgEQV7vjc9yDjw= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= +github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= +github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= +github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= +github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= @@ -81,8 +71,6 @@ github.com/weppos/publicsuffix-go v0.40.2 h1:LlnoSH0Eqbsi3ReXZWBKCK5lHyzf3sc1JEHH1cnlfho= github.com/weppos/publicsuffix-go v0.40.2/go.mod h1:XsLZnULC3EJ1Gvk9GVjuCTZ8QUu9ufE4TZpOizDShko= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= @@ -90,10 +78,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= -golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= -golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -110,8 +96,10 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -133,8 +121,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -145,8 +133,8 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -158,8 +146,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -168,8 +156,8 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= -gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= @@ -178,8 +166,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ++++++ vendor.tar.gz ++++++ ++++ 65372 lines of diff (skipped)
