目的:構建一個簡單的漏洞掃描器,它的主要功能是連接一個TCP套接字,從目標服務器上讀取banner,並將其與自定義的漏洞服務器版本相比較。
實施前奏:
變量:在python中,變量是指 可以存儲可變數據的標識體,這個數據可以是整數型、浮點型、字符型、布爾型,或者是列表、字典等更復雜的數據。接下來定義一個變量port用來存儲整型和一個變量banner用來存儲字符串。然后將這兩個變量進行拼接,因為某些特性這里將整型的port變量所存儲的值臨時轉換為字符型。
>>> port = 21 >>> banner = "FTP Server" >>> print "[+] Checking for "+banner+" on port "+str(port) [+] Checking for FTP Server on port 21
當我們聲明變量時python程序准備給變量預留內存空間,而解釋器會自動的為它們進行類型分配並確定通知后台程序具體分配多少內存空間,比如下面這個樣子:
>>> type(banner) <type 'str'> >>> type(port) <type 'int'> >>> portList = [21,22,80.445] >>> type(portList) <type 'list'> >>> portOpen = True >>> type(portOpen) <type 'bool'>
字符串:在python中有一個string模塊可以進行一系列的字符串處理。這里我們介紹string模塊中四個方法的使用,upper()將字符串轉換成大寫形式,lower()將字符串轉換成小寫形式,replace(old,new)用new字符串代替old字符串。find()返回字符在字符串中第一次出現時的偏移量。但是這種更改並不會改變變量空間的值
>>> print banner.upper() FTP SERVER >>> print banner.lower() ftp server >>> print banner.replace('ftp','Ability ftp') FTP Server >>> print banner.replace('FTP','Ability ftp') Ability ftp Server >>> print banner.find("ftp") -1 >>> print banner.find("Server") 4 >>> banner 'FTP Server'
List(列表):list數據結構是python中用來存儲對象數組的。在內置的list模塊中具有添加、插入、刪除、出站、索引、計數、排序、反轉等功能。
>>> portList = [] >>> portList.append(21) >>> portList.append(80) >>> portList.append(443) >>> portList.append(25) >>> portList [21, 80, 443, 25] >>> portList.sort() >>> portList [21, 25, 80, 443] >>> pos = portList.index(80) >>> print "[+] There are "+str(pos)+" ports to scan before 80." [+] There are 2 ports to scan before 80. >>> portList.remove(443) >>> portList [21, 25, 80] >>> cnt = len(portList) >>> print "[+] Scanning "+str(cnt)+" Total Ports." [+] Scanning 3 Total Ports.
字典:由n對鍵和值的項組成,是可以存儲任意數量的對象的hash表。在掃描指定的TCP端口時,包含有各個端口及對應的常用服務名的字典就很有用。如創建一個相關的字典之后,在我們查找ftp之類的關鍵字,就會返回與之關聯的端口值-21。
創建字典時,每個鍵和它的值都是以’:‘分隔同時以’,‘分隔各項。
>>> services = {'ftp':21,'ssh':22,'smtp':25,'http':80} >>> services.keys() ['ftp', 'http', 'smtp', 'ssh'] >>> services.items() [('ftp', 21), ('http', 80), ('smtp', 25), ('ssh', 22)] >>> services.has_key('ftp') True >>> services['ftp'] 21 >>> print "[+] Found vuln with FTP on port "+str(services['ftp']) [+] Found vuln with FTP on port 21
網絡:socket模塊是python中進行網絡連接的庫。下面編寫一個快速抓取banner的腳本,我們的腳本會在連上指定的IP地址和TCP端口后,將banner打印出來。
步驟-導入socket模塊->實例化一個變量接收socket類->使用connect方法連接IP和PORT->使用套接字進行讀寫操作,recv(1024)方法讀取->打印得到的結果。
>>> import socket >>> socket.setdefaulttimeout(2) >>> s = socket.socket() >>> s.connect(("192.168.44.132",21)) >>> ans = s.recv(1024) >>> print(ans) b'220 FileZilla Server 0.9.60 beta written by Tim Kosse (Tim.Kosse@gmx.de) Please visit http://sourceforge.\r\n'
錯誤示范:
>>> s.connect("192.168.44.132",21)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: connect() takes exactly one argument (2 given)
>>>
條件選擇語句:IF語句是對邏輯表達式進行求值,並根據求值結果做出決定。我們仍以抓取腳本為例:我們想要知道某個指定的FTP服務器中是否存在可以攻漏洞。這就需要將服務器的響應結果與已知存在的漏洞FTP服務版本進行比較。注意哈:python2中接收到的recv()會以str賦值給ans,而在python3中recv()是以byte賦值給ans。
>>> import socket >>> socket.setdefaulttimeout(2) >>> s = socket.socket() >>> s.connect(("192.168.44.132",21)) >>> ans = s.recv(1024) >>> print ans >>> if ("FreeFloat Ftp Server (Version 1.00)" in ans): ... print "[+] FreeFloat FTP Server is vulnerable." ... elif ("3Com 3CDaemon FTP Server Version 2.0" in banner): ... print "[+] 3CDeamon FTP Server is vulnerable." ... elif ("Sami FTP Server 2.0.2" in banner): ... print "[+] Sami FTP Server is vulnerable." ... else: ... print "[-] FTP Server is not vulnerable."
[-] FTP Server is not vulnerable.
異常處理:我們在編寫腳本的時候經常會碰到各種報錯,並且會終止程序的執行。但是如果我們不想就這樣簡單結束,就需要我們進行自定義獲取。如下面的兩種情況:
>>> print 123123/0 Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: integer division or modulo by zero >>> >>> try: ... print "[+] 123/0 = "+str(123/0) ... except: ... print "[-] Error." ... [-] Error. >>> try: ... print "[+] 123/0 = "+str(123/0) ... except Exception,e : ... print "[-] Error = "+str(e) ... [-] Error = integer division or modulo by zero
現在我們用異常來更新抓取banner的腳本。我們把網絡連接代碼寫在try語句內,接下來我們嘗試連接到一台沒有TCP端口21運行FTP服務器的機器。如果等待的連接超時,就會看到一條表明網絡連接操作超時的消息。
>>> import socket >>> socket.setdefaulttimeout(2) >>> s = socket.socket() >>> try: ... s.connect(("192.168.44.128",21)) ... except Exception,e: ... print "[-] Error = "+str(e) ... [-] Error = timed out
函數:關鍵字def()表示函數開始。我們可以在()中填寫任意變量,然后這些變量會被引用的方式傳遞給函數,也就是說,函數內對這些變量的任何更改都會影響它們在函數中的值。
>>> import socket >>> def retBanner(ip,port): ... try: ... socket.setdefaulttimeout(2) ... s = socket.socket() ... s.connect((ip,port)) ... banner = s.recv(1024) ... return banner ... except: ... return ... >>> def main(): ... ip1 = "192.168.44.124" ... ip2 = "192.168.44.132" ... port = 21 ... banner1 = retBanner(ip1,port) ... if banner1: ... print '[+] '+ip1+': '+banner1 ... banner2 = retBanner(ip2,port) ... if banner2: ... print '[+] '+ip2+': '+banner2 ... >>> if __name__ == '__main__': ... main() ... [+] 192.168.44.132: 220 FileZilla Server 0.9.60 beta written by Tim Kosse (Tim.Kosse@gmx.de) Please visit http://sourceforge.
迭代:當我們做重復的事情時,很自然的會感覺到煩躁,這是正常的。所以就引用出了迭代。它可以幫我們自行做很多重復的工作以降低我的壓力。
>>> for x in range(10,20): ... print "192.168.44."+str(x)+":21 " ... 192.168.44.10:21 192.168.44.11:21 192.168.44.12:21 192.168.44.13:21 192.168.44.14:21 192.168.44.15:21 192.168.44.16:21 192.168.44.17:21 192.168.44.18:21 192.168.44.19:21
文件輸入/輸出:當我們開始煩躁重復的向腳本添加新的具有漏洞的服務時,想到了為什么不可以外部引用。這就出現了讀取文件內容操作。我們事先寫好一個帶有漏洞的服務的文本。
3Com 3CDaemon FTP Server Version 2.0 Ability Server 2.34 CCProxy Telnet Service Ready ESMTP TABS Mail Server for Windows NT FreeFloat Ftp Server (Version 1.00) IMAP4revl MDaemin 9.6.4 ready MailEnable Service,Version: 0-1.54 NetDecision-HTTP-Server 1.0 PSO Proxy 0.9 SAMBAR Sami FTP Server 2.0.2 Spipe 1.0 TelSrv 1.5 MDaemon 6.8.5 WinGate 6.1.1 Xitami YahooPOPs! Simple Mail Transfer Service Ready
然后我對其經行讀取引用。這里以只讀模式(’r')打開文本文件,用.readlist()方法遍歷文件中的每一行,並將其與我們的banner進行比較。.strip('\n')方法將每一行的回車鍵去掉。
>>> def checkVulns(banner): ... f = open("C:\Users\HK\Desktop\note\bak\python_team\vuln_banners.txt",'r') ... for line in f.readlines(): ... if line.strip('\n') in banner: ... print "[+] Server is vulnerable: "+banner.strip('\n') ...
sys模塊:其包括了標志、版本、整型數的最大尺寸、可用的模塊、hook路徑、標准錯誤/輸入/輸出的位置,以及調用解釋器的命令行參數。
>>> import sys >>> if len(sys.argv)==2: ... filename = sys.argv[1] ... print "[+] Reading Vulnerabilities From:"+filename ...
C:\Users>python PortScan.py vuln_banners.txt
[+] Reading Vulnerabilities From: vuln_banners.txt
os模塊:本模塊允許程序獨立的與操作系統環境、文件系統、用戶數據庫以及權限進行交互。
import sys import os if len(sys.argv) == 2: filename = sys.argv[1] if not os.path.isfile(filename): print '[-] ' + filename + ' does not exist.' exit(0) if not os.access(filename,os.R_OK): print '[-] ' + filename + ' access denied.' exidt(0) print "[+] Reading Vulnerabilities From: "+filename
最終整合:將之前學到的進行整合修改后得到一個簡陋的FTP掃描腳本
import socket import sys import os
#連接函數 def retBanner(ip,port): try: socket.setdefaulttimeout(2) s = socket.socket() s.connect((ip,port)) banner = s.recv(1024) return banner except: return
#檢查漏洞函數 def checkVulns(banner,filename): f = open(filename,'r') for line in f.readlines(): if line.strip('\n') in banner: print '[+] Server is vulnerable.'\ +banner.strip('\n')
#主函數 def main(): if len(sys.argv) == 2: filename = sys.argv[1] if not os.path.isfile(filename): print '[-] ' + filename +\ ' does not exist.' exit(0) if not os.access(filename,os.R_OK): print '[-] ' + filename +\ ' access denied.' exit(0) else: print '[-] Usage: ' + str(sys.argv[0]) +\ ' <vuln filename.' exit(0) portlist = [21,22,25,80,110,443] for x in range(2,50): ip = '172.16.212.'+str(x) for port in portlist: banner = retBanner(ip,port) if banner: print '[+] ' + ip + ': '+banner checkVulns(banner,filename)
#調用 if __name__ == '__main__': main()
ENDING....