iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 15
1
Security

資安0~100K只要30天系列 第 15

Day 15 - PHP 反序列化 (二)

  • 分享至 

  • xImage
  •  

Magic Methods

在昨天了解到了基本的序列化行為後
我們來看看 PHP 中的一些魔術方法
這些方法的出現是為了處理一些關於物件生成與銷毀時或是其他特定行為的函數
一共有這麼多種
今天會舉幾個常用且可能被作為攻擊途徑的危險方法

__construct() __set() __toString()
__destruct() __isset() __invoke()
__call() __unset() __set_state()
__callStatic() __sleep() __clone()
__get() __wakeup() __debugInfo()

__construct()

  • 在物件被生成時所觸發的方法

例如一個物件被 new 起來

<?php
class Test
{
    public function __construct()
    {
        echo "hello Test";
    }
};
$obj = new Test();
?>

__destruct()

  • 在物件被銷毀時觸發,通常在該 PHP 結束執行時觸發
<?php
class Test
{
    public function __destruct()
    {
        echo "bye Test";
    }
};
$obj = new Test();
?>

__sleep()

  • 該物件被 serialize() 時觸發
  • 需 return 一個包含物件中變數名稱的陣列讓 serialize() 知道要將那些變數序列化出來

從這個例子可以看到
我們 return 的陣列中只有 a 這個變數名稱
因此序列化出來的字串也只包含 a
這樣的功能對於一個龐大物件來說很方便
不需要將所有變數都輸出保存
可以選擇我們自己需要的就好

<?php
class Test
{
    public $a = '123';
    public $b = '456';
    
    public function __sleep()
    {
        return array('a');
    }
};
echo serialize(new Test());
?>

__wakeup()

  • 與 sleep 相反,是在 unserialize() 時觸發
  • 可存取物件中所有資源
<?php
class Test
{
    public $a = '123';
    public $b = '456';
    
    public function __wakeup()
    {
        echo 'wakup';
    }
};
unserialize('O:4:"Test":1:{s:1:"a";s:3:"123";}');
?>

__toString()

  • 在該物件被當作字串使用時觸發
  • 一定要 return 一個字串
<?php
class Test
{
    public function __toString()
    {
        return 'I am not a string';
    }
};
$obj = new Test();
echo $obj;
?>

__invoke()

  • 該物件被當作函數使用時觸發
  • 可以傳入參數做使用
<?php
class Test
{
    public function __invoke($input)
    {
        echo $input;
    }
};
$obj = new Test();
$obj(123);
?>

反序列化攻擊

  • 存在使用者可自由輸入的序列化字串
  • 利用程式本身存在的物件
  • 藉由串接這些物件中的魔術方法達到目的

在 PHP 的反序列化中有個經典的漏洞 CVE-2016-7124
可利用的版本為

  • PHP5 : 5.6.25 以下
  • PHP7 : 7.0.10 以下

該漏洞的形成是因為 unserialize() 在將字串還原成物件時
如果字串中對物件描述的變數數量不相符時
導致反序列化失敗而跳過執行 __wakeup()

<?php
    include("flag.php");

    class Flag{
        public $key = "deadbeef";
    
        function __destruct(){
            global $flag;
            echo $flag;
        }
    
        function __wakeup(){
            if($this->key !== $serect){
                global $flag;
                $flag = "You can not read the flag.";
            }
        }
    }

    show_source(__FILE__);
    $your_flag = unserialize($_GET["input"]);
?>

在這個例子中我們可以看到
當物件被還原時就一定會將 flag 覆蓋掉
因此我們需要利用上述所說的漏洞跳過 __wakeup() 的執行而取得真正的 flag

  • payload
    • O:4:"Flag":2:{s:3:"key";s:8:"deadbeef";}

原本物件中變數只有一個
我們將其修改成 2 造成解析錯誤
就可以觸發該漏洞

結語

在真實世界的 PHP 中的確也存在這類型的漏洞
只不過通常程式碼的量都非常龐大、物件非常多
因此要找到可攻擊的一條反序列化鍊並不像上面所看到的只有短短幾行
應該說任何現實世界的漏洞都是如此
都需要大量的耐心與毅力去找出可行的目標 XD


上一篇
Day 14 - PHP 反序列化 (一)
下一篇
Day 16 - SQLi (ㄧ)
系列文
資安0~100K只要30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言