记一道有趣的sql注入和我的扩展探索
(0)

[SUCTF 2019]EasySQL WP 和我的扩展探索

原环境存在原码泄露,得到原码
可看出此题为sql注入

查看源码

<?php
 session_start();

 include_once "config.php";

 $post = array();
 $get = array();
 global $MysqlLink;

 //GetPara();
 $MysqlLink = mysqli_connect("localhost",$datauser,$datapass);
 if(!$MysqlLink){
     die("Mysql Connect Error!");
 }
 $selectDB = mysqli_select_db($MysqlLink,$dataName);
 if(!$selectDB){
     die("Choose Database Error!");
 }

 foreach ($_POST as $k=>$v){
     if(!empty($v)&&is_string($v)){
         $post[$k] = trim(addslashes($v));
     }
 }
 foreach ($_GET as $k=>$v){
     }
 }
 //die();
 ?>



<?php

 if(isset($post['query'])){
     $BlackList = "prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile
                 |readfile|where|from|union|update|delete|if|sleep|extractvalue|
                 updatexml|or|and|&|\"";
     //var_dump(preg_match("/{$BlackList}/is",$post['query']));
     if(preg_match("/{$BlackList}/is",$post['query'])){
         //echo $post['query'];
         die("Nonono.");
     }
     if(strlen($post['query'])>40){
         die("Too long.");
     }
     $sql = "select ".$post['query']."||flag from Flag";
     mysqli_multi_query($MysqlLink,$sql);
     do{
         if($res = mysqli_store_result($MysqlLink)){
             while($row = mysqli_fetch_row($res)){
                 print_r($row);
             }
         }
     }while(@mysqli_next_result($MysqlLink));

 }
 
 ?>

其中过滤了

"prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|\"" 

这么多关键字

其关键的sql语句是 $sql = "select ".$post['query']."||flag from Flag";

不太理解这条语句。我下载mysql做了实验。

select 数字 from table

得出一个行数和表行数一样的临时列,每行的列值是该数字

这里有几个相似的情况,做一个对比

select count(数字) from table

得到该表的行数

ps.
SELECT COUNT(column_name) FROM table_name函数返回指定列的值的数目(NULL 不计入)

select count(数字) from table

得出一个数,该数是表的行数sum括号里的值

ps.
SELECT SUM(column_name) FROM table_name函数返回数值列的总数(总额)

下面开始研究题目中的mysql语句 select *** || flag from table

可以看出这里的||作为逻辑或来使用,sql语句中原有的这个flag逻辑值为0,0或0=0;1或0=1;

这题的非预期解简单而实用 select *,任何数字||flag from table

还有一个非预期解 select flag,任何数字||flag from table

其相当于 select *,0 from table 可以得到所有列的值加上一列临时列0

测试中发现 select *** || *** from table *处必须为数字或者存在的列名。

在测试中产生了一个新的问题:为什么 * ||flag 中flag的逻辑值默认为0.我试了一下表的另一个列名id

可看出id的逻辑默认值是1。

插入一新列,值为NULL,数据类型为VARCHAR。测试在||中的逻辑值

发现这一新列在||下的逻辑值也为0,不过不显示0,显示NULL。

在新列insert了一个值

继续测试 select *** || *** from table ,发现结果很有意思。

NULL似乎也是以逻辑或来显示的,只要有NULL便不会有0。只要有1全是1。

新建了非空,数据类型为int的一列

继续测试其在||下的逻辑值

其结果和之前两列一样,似乎和数据类型无关。此时key2下的值为0,0,0。

修改key2的值。并测试

发现值不为0时,||下逻辑值为1。

修改flag的值为非零数字,继续测试。

此时发现值为数字的那一列在||下逻辑值判断为1。粗略得出结论,mysql中的||是根据是否为非零数字来判定逻辑值的。且如果有NULL。判定为NULL。

如果在字符串中包含数字呢?再测试一次

基本可以得出结论,只要含有非0数字,在||运算中逻辑值就判定为1。

还有一种解法是改变||符号的作用,查询到:在oracle 缺省支持 通过 ‘ || ’ 来实现字符串拼接,但在mysql 缺省不支持。需要调整mysql 的sql_mode 模式:pipes_as_concat 来实现oracle 的一些功能

payload: 1;set sql_mode=pipes_as_concat;select 1

构造语句 select 1;set sql_mode=pipes_as_concat;select 1 from table

此时成功

题外
关于Mysql的模式设置,可以替换为其他的模式,传统的,标准的,严格的,等等

我们常设置的 sql_mode 是 ANSI、STRICT_TRANS_TABLES、TRADITIONAL,ansi和traditional是上面的几种组合。

ANSI:更改语法和行为,使其更符合标准SQL
如REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE

TRADITIONAL:更像传统SQL数据库系统,该模式的简单描述是当在列中插入不正确的值时“给出错误而不是警告”。
如STRICT_TRANS_TABLES, STRICT_ALL_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER, NO_ENGINE_SUBSTITUTION

ORACLE:如 PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS, NO_TABLE_OPTIONS, NO_FIELD_OPTIONS, NO_AUTO_CREATE_USER

本文为作者silent666发布,未经允许禁止转载!
上一篇 下一篇
评论
暂无评论 >_<
加入评论