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()的小特性可能会被很多开发人员忽略,导致程序出现安全缺陷。
以上的分析有什么错误请留言指出。