php反序列unserialize的一个小特性
这几天wordpress的那个反序列漏洞比较火,具体漏洞我就不做分析了,看这篇:http://drops.wooyun.org/papers/596,?你也可以去看英文的原文:http://vagosec.org/2013/09/wordpress-php-object-injection/。?wp官网打了补丁,我试图去bypass补丁,但让我自以为成功的时候,发现我天真了,并没有成功绕过wp的补丁,但却发现了unserialize的一个小特性,在此和大家分享一下。?1.unserialize()函数相关源码:?if ((yylimit - yycursor) yych = *yycursor;?switch (yych) {?case 'c':?case 'o':goto yy13;?case 'n':goto yy5;?case 'r':goto yy2;?case 's':goto yy10;?case 'a':goto yy11;?case 'b':goto yy6;?case 'd':goto yy8;?case 'i':goto yy7;?case 'o':goto yy12;?case 'r':goto yy4;?case 's':goto yy9;?case '}':goto yy14;?default:goto yy16;?}上边这段代码是判断序列串的处理方式,如序列串o:4:test:1:{s:1:a;s:3:aaa;},处理这个序列串,先获取字符串第一个字符为o,然后case 'o':goto yy13?yy13:?yych = *(yymarker = ++yycursor);?if (yych == ':') goto yy17;?goto yy3;从上边代码看出,指针移动一位指向第二个字符,判断字符是否为:,然后 goto yy17?yy17:?yych = *++yycursor;?if (yybm[0+yych] & 128) {?goto yy20;?}?if (yych == '+') goto yy19;?.......?yy19:?yych = *++yycursor;?if (yybm[0+yych] & 128) {?goto yy20;?}?goto yy18;从上边代码看出,指针移动,判断下一位字符,如果字符是数字直接goto yy20,如果是'+'就goto yy19,而yy19中是对下一位字符判断,如果下一位字符是数字goto yy20,不是就goto yy18,yy18是直接退出序列处理,yy20是对object性的序列的处理,所以从上边可以看出:?o:+4:test:1:{s:1:a;s:3:aaa;}?o:4:test:1:{s:1:a;s:3:aaa;}都能够被unserialize反序列化,且结果相同。?2.实际测试:?输出:?object(__php_incomplete_class)#1 (2) { [__php_incomplete_class_name]=> string(4) test [a]=> string(3) aaa }?object(__php_incomplete_class)#1 (2) { [__php_incomplete_class_name]=> string(4) test [a]=> string(3) aaa }其实,不光object类型处理可以多一个'+',其他类型也可以,具体测试不做过多描述。?3.我们看下wp的补丁:?function is_serialized( $data, $strict = true ) {?// if it isn't a string, it isn't serialized?if ( ! is_string( $data ) )?return false;?$data = trim( $data );? if ( 'n;' == $data )?return true;?$length = strlen( $data );?if ( $length return false;?if ( ':' !== $data[1] )?return false;?if ( $strict ) {//output?$lastc = $data[ $length - 1 ];?if ( ';' !== $lastc && '}' !== $lastc )?return false;?} else {//input?$semicolon = strpos( $data, ';' );?$brace = strpos( $data, '}' );?// either ; or } must exist.?if ( false === $semicolon && false === $brace )?return false;?// but neither must be in the first x characters.?if ( false !== $semicolon && $semicolon return false;?if ( false !== $brace && $brace return false;?}?$token = $data[0];?switch ( $token ) {?case 's' :?if ( $strict ) {?if ( '' !== $data[ $length - 2 ] )?return false;?} elseif ( false === strpos( $data, '' ) ) {?return false;?}?case 'a' :?case 'o' :?echo a;?return (bool) preg_match( /^{$token}:[0-9]+:/s, $data );?case 'b' :?case 'i' :?case 'd' :?$end = $strict ? '$' : '';?return (bool) preg_match( /^{$token}:[0-9.e-]+;$end/, $data );?}?return false;?}补丁中的?return (bool) preg_match( /^{$token}:[0-9]+:/s, $data );可以多一个'+'来绕过,虽然我们通过这个方法把序列值写入了数据库,但从数据库中提取数据,再次验证的时候却没法绕过了,我这个加号没能使数据进出数据库发生任何变化,我个人认为这个补丁绕过重点在于数据进出数据的前后变化。?4.总结?虽然没有绕过wp补丁,但这个unserialize()的小特性可能会被很多开发人员忽略,导致程序出现安全缺陷。?以上的分析有什么错误请留言指出。?5.参考?《wordpress http://vagosec.org/2013/09/wordpress-php-object-injection/?《var_unserializer.c源码》?https://github.com/php/php-src/blob/73cd2e0ab14d804c6bf0b689490bdd4fd6e969b1/ext/standard/var_unserializer.c?《php string序列化与反序列化语法解析不一致带来的安全隐患》?http://zone.wooyun.org/content/1664