wp官网打了补丁,我试图去bypass补丁,但让我自以为成功的时候,发现我天真了,并没有成功绕过wp的补丁,但却发现了unserialize的一个小特性,在此和大家分享一下。
1.unserialize()函数相关源码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
17
18
|
if ((YYLIMIT - YYCURSOR) < 7) YYFILL(7); 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; } scfrd.info; |
上边这段代码是判断序列串的处理方式,如序列串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
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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.实际测试:
?
1
2
3
4
5
6
7
|
<?php var_dump(unserialize( 'O:+4:"test":1:{s:1:"a";s:3:"aaa";}' )); var_dump(unserialize( 'O:4:"test":1:{s:1:"a";s:3:"aaa";}' )); ?> 输出: 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的补丁:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
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 <
4 ) 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 <
3 ) return false; if (
false !== $brace && $brace <
4 ) 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' : |
补丁中的
return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
可以多一个'+'来绕过,虽然我们通过这个方法把序列值写入了数据库,但从数据库中提取数据,再次验证的时候却没法绕过了,我这个加号没能使数据进出数据库发生任何变化,我个人认为这个补丁绕过重点在于数据进出数据的前后变化。
4.总结
虽热没有绕过wp补丁,但这个unserialize()的小特性可能会被很多开发人员忽略,导致程序出现安全缺陷。
以上的分析有什么错误请留言指出。