# 第1章 介紹
本章內容 :
1. 建立Python 開發環境
2. Python 語言簡介
3. 變量,字符串,列表,字典介紹
4. 使用用網絡,迭代器,異常處理,模塊等
5. 寫第一個Python 程序,字典密碼破解器
6. 寫第二個Python 程序,壓縮文件密碼暴力破解
> 對我來說,武術的非凡之處在于它的簡單。簡單是最美的,而武術也沒有什么特別之處;以無法為有法,以有限為無限,是為武術最高境界!
> ——截拳道宗師 李小龍
## 引文:用python 進行的一次滲透測試
最近,我的一個朋友對一家世界財富500 強公司的計算機安全系統進行了滲透測試。雖然該公司已建立和保持一個了優秀的安全機制,但他最終還是發現了一個存在漏洞而未打補丁的服務器。幾分鐘之內,他用開源工具入侵了這個系統并獲得管理權。然后,他掃描了剩下的服務器以及客戶機,并沒有發現任何額外的漏洞。
從這一點看,他的測試似乎結束了,但是真正的滲透測試才剛剛開始。
他打開了自己常用的文本編輯器,寫下了一個Python 測試腳本,利用這個腳本發現了其余存在漏洞的服務器,幾分鐘后,他獲得了網絡上超過一千臺機器的管理權,然而,在這樣做時,他隨后產生了一個難以管理的問題。 他知道,系統管理員會注意到他的攻擊并拒絕再讓他訪問。所以,他趕緊想辦法在自己已經控制的服務器上,安裝永久的后門。
檢查了一下自己滲透測試用到的文件后,我的朋友意識到他的這臺客戶機存在著很重要的域控制器。以此得知,管理員使用了一個完全獨立的管理賬戶登陸域控制器,我的朋友寫了一個小腳本檢查1000 臺機器上已經登錄的用戶,過了一會,我的朋友被告知,域管理員登錄到了一個機器。他的監測基本完成,我的朋友現在知道在哪里繼續他的攻擊了。
我朋友的迅速反應和他在壓力下能創造性的思考的能力,促使他成為了一個滲透測試者。他為了成功入侵這個世界500 強公司,自己寫了腳本工具。一個小的Python 腳本幫助他入侵了一千多個工作站。另一個小腳本允許他在管理員發現前成功triage。一個真正的滲透測試者會編寫自己的工具來解決所遇到的問題。所以,讓我們以安裝開發環境為開始,學習如何打造自己的工具吧!
## 建立開發環境
Python 的下載網站( http://www.python.org/download/ )提供了Python 在 Windows,Mac OS X 和Linux 上的安裝包。如果您運行的是Mac OS X 或 Linux,Python 的解釋器已經預先安裝在了系統上。安裝包為程序開發者提供了Python 解釋器,標準庫和幾個內置模塊。 Python 標準庫和內置模塊提供的功能范圍廣泛,包括內建的數據類型,異常處理,數字和數學模塊,文件處理功能,如加密服務,與操作系統互操作性,網絡數據處理,并與IP 協議交互,還包括許多其他有用模塊。同時,程序開發者可以很容易地安裝任何第三方軟件包。第三方軟件包的完整列表可在 http://pypi.python.org/pypi/ 上看到
## 安裝第三方庫
在第二章中,我們將利用python 的`python-nmap` 包來處理的NMAP 的結果。下面的例子描述了如何下載和安裝`python-nmap` 包(或其他任何包,真的)。一旦我們已經保存了包到本地,我們解壓這個包,并進入壓縮后的目錄中。在工錄中,我們執行`python setup.py` 命令來安裝`python-nmap` 包。安裝大多數第三方包將遵循下載,解壓,執行`python setup.py` 命令進行安裝的相同的步驟。
```
programmer:~# wget http://xael.org/norman/python/python-nmap/pythonnmap-
0.2.4.tar.gz-On map.tar.gz
--2012-04-24 15:51:51--http://xael.org/norman/python/python-nmap/
python-nmap-0.2.4.tar.gz
Resolving xael.org... 194.36.166.10
Connecting to xael.org|194.36.166.10|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 29620 (29K) [application/x-gzip]
Saving to: 'nmap.tar.gz'
100%[==================================================
===================================================
=============>] 29,620 60.8K/s in 0.5s
2012-04-24 15:51:52 (60.8 KB/s) - 'nmap.tar.gz' saved [29620/29620]
programmer:~# tar -xzf nmap.tar.gz
programmer:~# cd python-nmap-0.2.4/
programmer:~/python-nmap-0.2.4# python setup.py install
running install
running build
running build_py
creating build
creating build/lib.linux-x86_64-2.6
creating build/lib.linux-x86_64-2.6/nmap
copying nmap/__init__.py -> build/lib.linux-x86_64-2.6/nmap
copying nmap/example.py -> build/lib.linux-x86_64-2.6/nmap
copying nmap/nmap.py -> build/lib.linux-x86_64-2.6/nmap
running install_lib
creating /usr/local/lib/python2.6/dist-packages/nmap
copying build/lib.linux-x86_64-2.6/nmap/__init__.py -> /usr/local/lib/
python2.6/dist-packages/nmap
copying build/lib.linux-x86_64-2.6/nmap/example.py -> /usr/local/lib/
python2.6/dist-packages/nmap
copying build/lib.linux-x86_64-2.6/nmap/nmap.py -> /usr/local/lib/
python2.6/dist-packages/nmap
byte-compiling /usr/local/lib/python2.6/dist-packages/nmap/__init__.py
to __init__.pyc
byte-compiling /usr/local/lib/python2.6/dist-packages/nmap/example.py
to example.pyc
byte-compiling /usr/local/lib/python2.6/dist-packages/nmap/nmap.py to
nmap.pyc
running install_egg_info
Writing /usr/local/lib/python2.6/dist-packages/python_nmap-0.2.4.egginfo
```
為了能夠更簡單的安裝python 的包,python 提供了`easy_install` 模塊。運行這個簡單的安裝程序,程序將會在python 庫中尋找這個包,如果發現則下載它并自動安裝
```
programmer:~ # easy_install python-nmap
Searching for python-nmap
Readinghttp://pypi.python.org/simple/python-nmap/
Readinghttp://xael.org/norman/python/python-nmap/
Best match: python-nmap 0.2.4
Downloadinghttp://xael.org/norman/python/python-nmap/python-nmap-
0.2.4.tar.gz
Processing python-nmap-0.2.4.tar.gz
Running python-nmap-0.2.4/setup.py -q bdist_egg --dist-dir /tmp/easy_
install-rtyUSS/python-nmap-0.2.4/egg-dist-tmp-EOPENs
zip_safe flag not set; analyzing archive contents...
Adding python-nmap 0.2.4 to easy-install.pth file
Installed /usr/local/lib/python2.6/dist-packages/python_nmap-0.2.4-
py2.6.egg
Processing dependencies for python-nmap
Finished processing dependencies for python-nmap
```
為了快速建立一個開發環境,我們建議您從 http://www.backtracklinux.org/downloads/ 下載最新的 BackTrack Linux 的滲透測試專版的復制版。他提供了豐富的滲透測試工具,例如forensic,Web,網絡分析和無線攻擊。之后的幾個例子中。可能會用到一些早已內置在BackTrack 的工具或庫。當在本書的例子中,需要用到標準庫和內置模塊之外的第三方包的時候,文章將會提供包的下載網站。設置一個開發環境時,提前下載好所有的這些第三方模塊會是有用的。在 BackTrack 上,您可以通過執行`easy_install` 命令來安裝額外需要的庫,這將會在 Linux 下,下載大多數例子中用到的庫。
```
programmer:~ # easy_install pyPdf python-nmap pygeoip mechanize BeautifulSoup4
```
第五章用到了一些明確的不能從`easy_install` 下載的的藍牙庫。您可以使用包管理器下載并安裝這些庫。
```
attacker# apt-get install python-bluez bluetooth python-obexftp
Reading package lists... Done
Building dependency tree
Reading state information... Done
<..SNIPPED..>
Unpacking bluetooth (from .../bluetooth_4.60-0ubuntu8_all.deb)
Selecting previously deselected package python-bluez.
Unpacking python-bluez (from .../python-bluez_0.18-1_amd64.deb)
Setting up bluetooth (4.60-0ubuntu8) ...
Setting up python-bluez (0.18-1) ...
Processing triggers for python-central
```
此外,第五章和第七章中的幾個例子需要一個Windows 版的Python 下載器。最新的Windows 版的Python 下載器,請訪問 http://www.python.org/getit/ 最近幾年python 源代碼已經延伸成了2.x 和3.x 兩個分支。Python 的原作者Guido van Rossum 試圖清理代碼使語言變得更一致,這個行為打破了python 2.x 版本與之后版本的兼容性,例如作者對`print` 語句的更改。在本書出版時,BackTrack 5 R2 把Python 2.6.5 作為穩定的python 版本。
```
programmer# python -V
Python 2.6.5
```
## 解釋型python VS 交互型python
與其他腳本語言類似,Python 是一種解釋型語言。在運行時,解釋器處理代碼并執行他,為了演示python 解釋器的使用,我們寫一個`.py` 文件來打印`"Hello World"`。為了解釋這個程序,我們調用python 解釋器創建一個新的腳本。
```
programmer# echo print \"Hello World\" > hello.py
programmer# python hello.py
Hello World
```
此外,python 具有交互能力,程序設計師可以調用python 解釋器,并直接與解釋器“交流”。要啟動解釋器,程序開發者要先不帶參數的執行python,接著解釋器會呈現一個`>>>`來提示程序設計師,他可以接收命令了。在這里,程序設計師輸入`print "Hello World"`。按下回車后,python 交互解釋器會立即執行該語句。
```
programmer# python
Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41)
[GCC 4.4.3] on linux2
>>>
>>> print "Hello World"
```
## Hello World
為了初步了解語言背后的含義,本章偶爾會用到python 解釋器的交互能力。你可以通過找尋`>>>`提示,來發現樣例中對解釋器的操作。因為我們要解釋下面章節中的樣例,所以我們將通過數個被稱為方法或函數的、有一定功能的代碼塊,來建立我們的腳本。每當我們完成一個腳本,我們將展示如何重新組合這些方法(函數)來讓他們在`main()`中被調用。試圖運行一個只有孤立的函數定義而不去調用他們的腳本是毫無意義的。大多數情況下,你都可以認出完整的腳本,因為他們都有定義好的`main()`函數。在我們開始寫我們的第一個程序前,我們將說明一些python 標準庫的重要組成部分。
## Python 語言
在下面的內容中,我們會講解變量,數據類型,字符串,復雜的數據結構,網絡,選擇,循環,文件處理,異常處理,與操作系統進行交互。為了顯示這一點,我們將構建一個簡單的TCP 類型的漏洞掃描器,讀取來自服務的提示消息,并把他們與已知的存在漏洞的服務版本做比較,作為一個有經驗的程序設計師,你可能會發現一些最初的示例代碼的設計非常難看,事實上,我們希望你能在我們的代碼基礎上進行發展,使他變得優雅。
那么,讓我們從任何編程語言的基礎——變量開始吧!
## 變量
在python 中,變量對應的數據存儲在內存中,這種在內存中的位置可以存儲不同的值,如整型,實數,布爾值,字符串,或更復雜的數據結構,例如列表或字典。在下面的代碼中,我們定義一個存儲整形的變量和一個存儲字符串的提示消息,為了把這兩個變量連接到一個字符串中,我們必須用`str()`函數。
```
>>> port = 21
>>> banner = "FreeFloat FTP Server"
>>> print "[+] Checking for "+banner+" on port "+str(port)
[+] Checking for FreeFloat FTP Server on port 21
```
當程序設計師聲明變量后,python 為這些變量保存了內存空間。程序設計師不必聲明變量的類型,相反,python 解釋器決定了變量類型何在內存中為他保留的空間的大小。思考下面的例子,我們正確的聲明了一個字符串,一個整數,一個列表和一個布爾值,解釋器都自動的正確的識別了每個變量的類型。
```
>>> banner = "FreeFloat FTP Server" # A string
>>> type(banner)
<type 'str'>
>>> port = 21 # An integer
>>> type(port)
<type 'int'>
>>> portList=[21,22,80,110] # A list
>>> type(portList)
<type 'list'>
>>> portOpen = True # A boolean
>>> type(portOpen)
<type 'bool'>
```
## 字符串
在python 中字符串模塊提供了一系列非常強大的字符串操作方法。閱讀 http://docs.python.org/library/string.html 上的用法列表的python 文檔。讓我們來看幾個常用的函數。思考下面這些函數的用法,`upper()` 方法將字符串中的小寫字母轉為大寫字母,`lower()`方法轉換字符串中所有大寫字母為小寫,`replace(old,new)`方法把字符串中的`old`(舊字符串) 替換成 `new`(新字符串),`find()`方法檢測字符串中是否包含指定的子字符串。
```
>>> banner = "FreeFloat FTP Server"
>>> print banner.upper()
FREEFLOAT FTP SERVER
>>> print banner.lower()
freefloat ftp server
>>> print banner.replace('FreeFloat','Ability')
Ability FTP Server
>>> print banner.find('FTP')
10
```
## 列表
Python 的數據結構——列表,提供了一種存儲一組數據的方式。程序設計師可以構建任何數據類型的列表。另外,有一些內置的操作列表的方法,例如添加,刪除,插入,彈出,獲取索引,排序,計數,排序和反轉。請看下面的例子,一個程序通過使用`append()`添加元素來建立一個列表,打印項目,然后在再次輸出前給他們排序。程序設計師可以找到特殊元素的索引(例如樣例中的80),此外,指定的元素也可以被移動。(例如樣例中的443)
```
>>> portList = []
>>> portList.append(21)
>>> portList.append(80)
>>> portList.append(443)
>>> portList.append(25)
>>> print portList
[21, 80, 443, 25]
>>> portList.sort()
>>> print 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)
>>> print portList
[21, 25, 80]
>>> cnt = len(portList)
>>> print "[+] Scanning "+str(cnt)+" Total Ports."
[+] Scanning 3 Total Ports.
```
## 字典
Python 的數據結構——字典,提供了一個可以存儲任何數量python 對象的哈希表。字典的元素由鍵和值組成,讓我們繼續用我們的漏洞掃描器的例子來講解python 的字典。當掃描指定的TCP 端口是,用字典包含每個端口對應的常見的服務名會很有用。建立一個字典,我們能查找像`ftp` 這樣的鍵并返回端口關聯的值21。當我們建立一個字典時,每一個鍵和他的用被冒號隔開,同時,我們用逗號分隔元素。注意,`.keys()`這個方法將返回字典的所有鍵的列表,`.items()`這個方法將返回字典的元素的一系列列表。接下來,我們驗證字典是否包含了指定的鍵(`ftp`),伴隨著鍵,值21 返回了。
```
>>> services = {'ftp':21,'ssh':22,'smtp':25,'http':80}
>>> services.keys()
['ftp', 'smtp', 'ssh', 'http']
>>> services.items()
[('ftp', 21), ('smtp', 25), ('ssh', 22), ('http', 80)]
>>> 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
```
## 網絡
套接字模塊提供了一個可以使python建立網絡連接的庫。讓我們快速的編寫一個獲取提示信息的腳本,連接到特定IP地址和端口后,我們的腳本將打印提示信息,之后,我們使用`connect()`函數連接到IP地址和端口。一旦連接成功,就可以通過套接字進行讀寫。這種`recv(1024)`的方法將讀取之后在套接字中1024字節的數據。我們把這種方式的結果存到一個變量中,然后打印到服務器。
```
>>> import socket
>>> socket.setdefaulttimeout(2)
>>> s = socket.socket()
>>> s.connect(("192.168.95.148",21))
>>> ans = s.recv(1024)
>>> print ans
220 FreeFloat Ftp Server (Version 1.00).
```
## 選擇
像大多數編程語言一樣,python提供了條件選擇的方式,通過if語句,計算一個邏輯表達式來判斷選擇的結果。繼續寫我們的腳本,我們想知道,是否指定的FTP服務器是容易受到攻擊的。要做到這一點,我們要拿我們的結果和已知的易受攻擊的FTP服務器版本作比較。
```
>>> import socket
>>> socket.setdefaulttimeout(2)
>>> s = socket.socket()
>>> s.connect(("192.168.95.148",21))
>>> ans = s.recv(1024)
>>> 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 "[+] 3CDaemon FTP Server is vulnerable."
... elif ("Ability Server 2.34" in banner):
... print "[+] Ability 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."
...
[+] FreeFloat FTP Server is vulnerable."
```
## 異常處理
即使一個程序設計師編寫的程序語法正確,該程序仍然可能在運行或執行時發生錯誤。考慮經典的一種運行錯誤——除以零。因為零不能做除數,所以python解釋器顯示一條消息,把錯誤信息告訴程序設計師:該錯誤使程序停止執行。
```
>>> print 1337/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by ze
```
如果我們想在我們預設的范圍內處理錯誤,會對運行的程序產生什么影響呢?python語言提供的異常處理能力就可以這樣做。讓我們來更新前面的例子,我們使用`try/except`進行異常處理。現在程序試圖除以零。當錯誤發生時,我們的異常處理捕獲錯誤并把錯誤信息打印到屏幕上。
```
>>> try:
... print "[+] 1337/0 = "+str(1337/0)
... except:
... print "[-] Error. "
...
[-] Error
>>>
```
不幸的是,這給了我們非常少的關于錯誤的異常處理的信息。但在對待特殊錯誤時,這可能很有用,要做到這一點,我們將存儲異常信息到一個變量中,來打印出異常信息。
```
>>> try:
... print "[+] 1337/0 = "+str(1337/0)
... except Exception, e:
... print "[-] Error = "+str(e)
...
[-] Error = integer division or modulo by zero
>>>
```
現在,讓我們用異常處理來更新我們的腳本,我們用異常處理把網絡連接代碼包裝起來,接下來,我們連接到一臺圍在TCP端口21上開放FTP服務的機器。如果我們等待連接超時,我們將看到一條信息來表明網絡連接操作超時。然后,我們的程序可以繼續運行。
```
>>> import socket
>>> socket.setdefaulttimeout(2)
>>> s = socket.socket()
>>> try:
... s.connect(("192.168.95.149",21))
... except Exception, e:
... print "[-] Error = "+str(e)
...
[-] Error = Operation timed out
```
在本書中,讓我們為你提供一個與異常處理有關的警告,為了清楚的說明各種各樣的概念,在下面的內容中,我們已經在最小的地方都添加了異常處理,但我們仍然歡迎你更新這新腳本,并把強化的異常處理代碼分享到配到網站上。
## 函數
在python中,函數提供了組建好的,可反復使用的代碼片段。通常,這允許程序設計師寫代碼來執行單獨或關聯的行為。
盡管python提供了許多內置函數,程序設計師仍然可以創建自定義的函數。關鍵字`def`開始了一個函數,程序設計師可以把任何變量放到括號里。這些變量隨后被傳遞,這意味著在函數內部對這些變量的任何變化,都將影響調用的函數的值。繼續以我們的FTP漏洞掃描器為例,讓我們創建一個函數來執行只連接到FTP服務器的操作并返回提示信息
```
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.95.148'
ip2 = '192.168.95.149'
port = 21
banner1 = retBanner(ip1, port)
if banner1:
print '[+] ' + ip1 + ': ' + banner1
banner2 = retBanner(ip2, port)
if banner2:
print '[+] ' + ip2 + ': ' + banner2
if __name__ == '__main__':
main()
```
在返回信息后,我們的腳本需要與已知存在漏洞的程序進行核對。這也反映了函數的單一性和相關性。該函數`checkVulns()`用獲得的信息來對服務器存在的漏洞進行判斷。
```
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 checkVulns(banner):
if 'FreeFloat Ftp Server (Version 1.00)' in banner:
print '[+] FreeFloat FTP Server is vulnerable.'
elif '3Com 3CDaemon FTP Server Version 2.0' in banner:
print '[+] 3CDaemon FTP Server is vulnerable.'
elif 'Ability Server 2.34' in banner:
print '[+] Ability 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.'
return
def main():
ip1 = '192.168.95.148'
ip2 = '192.168.95.149'
ip3 = '192.168.95.150'
port = 21
banner1 = retBanner(ip1, port)
if banner1:
print '[+] ' + ip1 + ': ' + banner1.strip('\n’)
checkVulns(banner1)
banner2 = retBanner(ip2, port)
if banner2:
print '[+] ' + ip2 + ': ' + banner2.strip('\n')
checkVulns(banner2)
banner3 = retBanner(ip3, port)
if banner3:
print '[+] ' + ip3 + ': ' + banner3.strip('\n')
checkVulns(banner3)
if __name__ == '__main__':
main()
```
## 迭代
上一章中,你可能會發現我們幾乎重復三次寫了相同的代碼,來檢測三個不同的IP地址。
代替反復做一件事,使用`for`循環便利多個元素會更加容易。舉個例子:如果我們想便利整個整個IP地址從`192.168.98.1`到`192.168.95.254`的子網,我們要用一個`for`循環從1到255進行遍歷,來打印出子網內的信息。
```
>>> for x in range(1,255):
... print "192.168.95."+str(x)
...
192.168.95.1
192.168.95.2
192.168.95.3
192.168.95.4
192.168.95.5
192.168.95.6
... <SNIPPED> ...
192.168.95.253
192.168.95.254
```
同樣,我們可能需要遍歷已知的端口列表來檢查漏洞。代替一系列的數字,我們可以通過一個元素列表遍歷他們。
```
>>> portList = [21,22,25,80,110]
>>> for port in portList:
... print port
...
21
22
25
80
110
```
嵌套了兩個`for`循環,現在我們可以打印出每個IP地址和端口了。
```
>>> for x in range(1,255):
... for port in portList:
... print "[+] Checking 192.168.95."\
+str(x)+": "+str(port)
...
[+] Checking 192.168.95.1:21
[+] Checking 192.168.95.1:22
[+] Checking 192.168.95.1:25
[+] Checking 192.168.95.1:80
[+] Checking 192.168.95.1:110
[+] Checking 192.168.95.2:21
[+] Checking 192.168.95.2:22
[+] Checking 192.168.95.2:25
[+] Checking 192.168.95.2:80
[+] Checking 192.168.95.2:110
<... SNIPPED ...>
```
隨著程序有了遍歷IP和端口的能力,我們也將個更新我們的漏洞檢測腳本,現在,我們的腳本將測試全部254個IP地址所提供的telnet, SSH, smtp, http,imap, and https服務。
```
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 checkVulns(banner):
if 'FreeFloat Ftp Server (Version 1.00)' in banner:
print '[+] FreeFloat FTP Server is vulnerable.'
elif '3Com 3CDaemon FTP Server Version 2.0' in banner:
print '[+] 3CDaemon FTP Server is vulnerable.'
elif 'Ability Server 2.34' in banner:
print '[+] Ability 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.'
return
def main():
portList = [21,22,25,80,110,443]
for x in range(1, 255):
ip = '192.168.95.' + str(x)
for port in portList:
banner = retBanner(ip, port)
if banner:
print '[+] ' + ip + ': ' + banner
checkVulns(banner)
if __name__ == '__main__':
main()
```
## 文件I/O
雖然我們的腳本已有了一些能幫助檢測漏洞信息的if語句,但加進一個漏洞列表會更好,舉個例子,假設我們有一個叫做`vuln_banners.txt`的文本文件。在每一行該文件列出了具體的服務版本和已知的之前的漏洞,我們不需要構建一個龐大的if語句,讓我們讀取這個文本文件,并用他來判斷是否我們的提示信息存在漏洞。
```
programmer$ cat vuln_banners.txt
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)
IMAP4rev1 MDaemon 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
WDaemon 6.8.5
WinGate 6.1.1
Xitami
YahooPOPs! Simple Mail Transfer Service Ready
```
我們將會把我們更新后的代碼放到函數`checkVulns()`中。在這里我們將用只讀模式(`'r'`)打開文本文件。然后使用函數`readlines()`遍歷文件的每一行,對每一行,我們把他與我們的提示信息作比較,注意我們必須用方法`.strip(‘\r’)`去掉每行的回車符,如果發現一對匹配了,我們打印出有漏洞的服務信息。
```
def checkVulns(banner):
f = open("vuln_banners.txt",'r')
for line in f.readlines():
if line.strip('\n') in banner:
print "[+] Server is vulnerable: "+banner.strip('\n')
```
## SYS模塊
內置的sys模塊提供訪問和維護python解釋器的能力。這包括了提示信息,版本,整數的最大值,可用模塊,路徑鉤子,標準錯誤,標準輸入輸出的定位和解釋器調用的命令行參數。你能夠在python的在線模塊文檔上找到更多與此相關的信息( http://docs.python.org/library/sys )。在創建python腳本時與sys模塊交互會十分有用。我們可以,例如,想在程序運行時解析命令行參數。
思考下我們的漏洞掃描器,如果我們想要把文本文件的名字作為命令行參數傳遞會怎么樣呢?領標`sys.argv`包含了全部的命令含參數。第一個索引`sys.argv[0]`包含了python腳本解釋器的名稱。列表中剩余的元素包含了以下全部的命令行參數。因此,如果我們只想傳遞附加的參數,`sys.argv`應該包含兩個元素。
```
import sys
if len(sys.argv)==2:
filename = sys.argv[1]
print "[+] Reading Vulnerabilities From: "+filename
```
運行我們的代碼片段,我們看到代碼成功的解析了命令行參數并把他打印到了屏幕上。你可以花時間來學習下全部的sys模塊提供給程序設計師的豐富的功能。
```
programmer$ python vuln-scanner.py vuln-banners.txt
[+] Reading Vulnerabilities From: vuln-banners.txt
```
## OS模塊
內置的OS模塊提供了豐富的與MAC,NT,Posix等操作系統進行交互的能力。這個模塊允許程序獨立的與操作系統環境。文件系統,用戶數據庫和權限進行交互。思考一下,比如,上一章中,用戶把文件名作為命令行參數來傳遞。他可以驗證文件是否存在以及當前用戶是否有權限都這個文件。如果失敗,他將顯示一條信息,來顯示一個適當的錯誤信息給用戶。
```
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.'
exit(0)
print '[+] Reading Vulnerabilities From: ' + filename
```
為了驗證我們的代碼,我們嘗試讀取一個不存在的文件,該文件使我們的程序打印出了錯誤信息,接下來,我們創建這個文件,我們的腳本成功的讀取了他。最后我們限制了權限,我們的腳本正確的打印了拒絕訪問的消息。
```
programmer$ python test.py vuln-banners.txt
[-] vuln-banners.txt does not exist.
programmer$ touch vuln-banners.txt
programmer$ python test.py vuln-banners.txt
[+] Reading Vulnerabilities From: vuln-banners.txt
programmer$ chmod 000 vuln-banners.txt
programmer$ python test.py vuln-banners.txt
[-] vuln-banners.txt access denied
```
現在我們可以重新組合漏洞掃描程序的各個零件。不用擔心他會錯誤終止或是在執行時缺少使用線程的能力或是更好的分析命令行的能力,我們將會在后面的章節繼續改進這個腳本
```
Import socket
import os
import sys
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(147, 150):
ip = '192.168.95.' + str(x)
for port in portList:
banner = retBanner(ip, port)
if banner:
print '[+] ' + ip + ': ' + banner
checkVulns(banner, filename)
if __name__ == '__main__':
main()
```
## 你的的第一個Python程序
隨著了解了如何構建python腳本,讓我們開始寫我們的第一個程序。在我們向前邁進前,我們將描述一些軼聞軼事,強調我們的腳本的需要。
為你的第一個程序設立個平臺:
## 杜鵑蛋的故事
C. Stoll的《杜鵑蛋》(1989)堪稱新派武俠的開山之作。它第一次把黑客活動與國家安全聯系在一起。黑客極具破壞性的黑暗面也浮出海面,并且永遠改變了黑客的形象。迄今仍是經久不衰的暢銷書。Stoll是勞倫斯伯克利實驗室的天文學家和系統管理員。1986年夏,一個區區75美分的帳目錯誤引起了他的警覺,在追查這次未經授權的入侵過程中,他開始卷入一個錯綜復雜的電腦間諜案。神秘的入侵者是西德混沌俱樂部的成員。他們潛入美國,竊取敏感的軍事和安全情報。出售給克格勃,以換取現金及可卡因。一場網絡跨國大搜索開始了,并牽涉出FBI、CIA、克格勃、西德郵電部等。《杜鵑蛋》為后來的黑客作品奠定了一個主題:追捕與反追捕的驚險故事。而且也開始了新模式:一個堅韌和智慧的孤膽英雄,成為國家安全力量的化身,與狡猾的對手展開傳奇的較量。
> 該故事已經有經典的翻譯版本,可以直接參考
> 下載地址:http://pan.baidu.com/s/1kTCNwMF 密碼ug42
## 你的第一個程序,一個UNIX密碼破解器!
我們只需要用標準庫中的`crypt`模塊的`crypt()`函數。傳入密碼和鹽即可。
讓我們趕快試一試用`crypt()`函數哈希一個密碼試試,我們輸入密碼`"egg"`和鹽`"HX"`,返回的哈希密碼值是`"HX9LLTdc/jiDE"`,現在我們可以遍歷整個字典,試圖用常用的鹽來匹配破解哈希密碼!
```
>>>import crypt
>>>crypt.crypt(‘egg’, ‘HX’)
“HX9LLTdc/jiDE”
>>>
```
注意:哈希密碼的前兩位就是鹽的前兩位,這里我們假設鹽只有兩位。
程序分兩部分,一部分是打開字典,另一部分是哈希匹配密碼,
代碼如下:
```
# coding=UTF-8
"""
暴力破解UNIX的密碼,需要輸入字典文件和UNIX的密碼文件
"""
import crypt
def testPass(cryptPass):
salt = cryptPass[0:2]
dictfile = open('dictionary.txt', 'r') #打開字典文件
for word in dictfile.readlines():
word = word.strip('\n') #保留原始的字符,不去空格
cryptWord = crypt.crypt(word, salt)
if cryptPass == cryptWord:
print('Found passed : ', word)
return
print('Password not found !')
return
def main():
passfile = open('passwords.txt', 'r') #讀取密碼文件
for line in passfile.readlines():
user = line.split(':')[0]
cryptPass = line.split(':')[1].strip('')
print("Cracking Password For :", user)
testPass(cryptPass)
if __name__ == '__main__':
main()
```
但是現代的×NIX系統將密碼存儲在`/etc/shadow`文件中,提供了個更安全的哈希散列算法SHA-512算法,Python的標準庫中`hashlib`模塊提供了此算法,我們可以更新我們的腳本,破解SHA-512哈希散列加密算法的密碼。
```
root@DJ-PC:/home/dj# cat /etc/shadow | grep root
root:$6$t0dy7TXs$mJxj1Ydfx83Eg0b7ry1etUQA8g7GliedT2DlnlLhiEunizJ1AAzSzQLfzV5J17D0MsZVwUVjP/0KHGV5Ue33F1:16411:0:99999:7:::
```
## 你的第二個程序:ZIP文件密碼破解
Python的標準庫提供了ZIP文件的提取壓縮模塊`zipfile`,現在讓我們試著用這個模塊,暴力破解出加密的ZIP文件!
我們可以用`extractall()`這個函數抽取文件,密碼正確則返回正確,密碼錯誤測拋出異常。
現在我們可以增加一些功能,將上面的單線程程序變成多線程的程序,來提高破解速度。
兩個程序代碼如下,注釋處為單線程代碼:
```
# coding=UTF-8
"""
用字典暴力破解ZIP壓縮文件密碼
"""
import zipfile
import threading
def extractFile(zFile, password):
try:
zFile.extractall(pwd=password)
print("Found Passwd : ", password)
return password
except:
pass
def main():
zFile = zipfile.ZipFile('unzip.zip')
passFile = open('dictionary.txt')
for line in passFile.readlines():
password = line.strip('\n')
t = threading.Thread(target=extractFile, args=(zFile, password))
t.start()
"""
guess = extractFile(zFile, password)
if guess:
print('Password = ', password)
return
else:
print("can't find password")
return
"""
if __name__ == '__main__':
main()
```
現在,我們想用戶可以指定要破解的文件和字典,我們需要借助Python標準庫中的`optparse`模塊來指定參數,具體的講解將在下一章講解,這里我們只提供本例的代碼:
```
# coding=UTF-8
"""
ZIP壓縮文件破解程序加強版,用戶可以自己指定想要破解的文件和破解字典,多線程破解
"""
import zipfile
import threading
import optparse
def extractFile(zFile, password):
try:
zFile.extractall(pwd=password)
print("Found Passwd : ", password)
except:
pass
def main():
parser = optparse.OptionParser('usage%prog -f <zipfile> -d <dictionary>')
parser.add_option('-f', dest='zname', type='string', help='specify zip file')
parser.add_option('-d', dest='dname', type='string', help='specify dictionary file')
options, args = parser.parse_args()
if options.zname == None | options.dname == None:
print(parser.usage)
exit(0)
else:
zname = options.zname
dname = options.dname
zFile = zipfile.ZipFile(zname)
dFile = open(dname, 'r')
for line in dFile.readlines():
password = line.strip('\n')
t = threading.Thread(target=extractFile, args=(zFile, password))
t.start()
if __name__ == '__main__':
main()
```
## 本章總結
本章我們就認識了Python的基本用法,寫了一個UNIX的密碼破解器和ZIP文件密碼破解器,下一章我們將用Python做進一步的滲透測試!