Tổng quan về phương pháp tấn công Deserialization trong PHP (phần 2)

Tram Ho

Các phương pháp phát hiện lỗ hổng

  1. Phân tích lỗ hổng Deserialization trên nền tảng PHP
  • Phương thức Magiac
    Khái niệm lập trình hướng đối tượng (OOP) được tăng cường đáng kể từ phiên bản 5
    của PHP, với các khái niệm destructors, exceptions, interfaces,.. OOP cho phép đóng gói dữ
    liệu và chức năng vào đối tượng. Mỗi lớp có thể được khởi tạo thành một đối tượng, có chứa
    các thuộc tính và phương thức được định nghĩa trong lớp. Các thuộc tính này được gọi là các
    attributes (hoặc các fields), trong khi các phương thức mô tả một hàm có thể truy cập được
    vào một đối tượng.
    Phương thức magic (Magic method) là các function đặc biệt trong các lớp của PHP, tên
    của các function này có hai dấu gạch dưới đứng trước, nó sẽ được gọi ngầm ở một sự kiện cụ
    thể:
  • __construct(): Là magic method kế thừa hàm khởi tạo bên trong lớp và sẽ
    được gọi bất cứ khi nào có đối tượng của một lớp được tạo ra. Nó thường được sử
    dụng để khởi tạo các thuộc tính của đối tượng hoặc chạy mã khác trước khi đối
    tượng thực sự có thể được sử dụng.

  • __destruct(): Ngược lại với hàm __construct(), hàm __destruct() sẽ được
    thực thi khi đối tượng bị huỷ. Nó thường được sử dụng để gọi mã giúp dọn sạch dữ
    liệu đã sử dụng hoặc chấm dứt các kết nối có thể được thiết lập sau khi đối tượng
    được tạo.

  • __call(): Hàm sẽ luôn luôn được gọi khi chương trình gọi đến một phương
    thức không tồn tại trong đối tượng. Nó rất thuận tiện trong việc xử lý lỗi, vì việc
    truy cập các phương thức không hợp lệ thường dẫn đến lỗi nghiêm trọng và chấm
    dứt ứng dụng PHP.

  • __callStatic(): Tương đương với hàm __call(), được gọi khi ta gọi đến một
    phương thức tĩnh không tồn tại trong đối tượng.

  • __get($name): Phương thức __get () được gọi tự động khi đọc các thuộc
    tính private, protected hoặc không tồn tại của một đối tượng. Do các thuộc tính
    private và protected không thể được truy cập trực tiếp từ bên ngoài đối tượng, tham
    số $name được sử dụng để tham chiếu thuộc tính mong muốn.

  • __set ($name, $value): Phương thức __set () được gọi tự động khi cố gắng
    ghi vào các thuộc tính private, protected của một đối tượng.

  • __isset(): Phương thức __isset() sẽ được gọi khi chúng ta thực hiện kiểm tra
    một thuộc tính không được phép truy cập của một đối tượng, hay kiểm tra một
    thuộc tính không tồn tại trong đối tượng đó. Cụ thể là hàm isset() và hàm empty().

  • __unset(): được gọi khi hàm unset() được sử dụng trong một thuộc tính
    không được phép truy cập. Tương tự như hàm isset. Khi ta unset một thuộc tính
    không tồn tại thì method __unset() sẽ được gọi.

  • __sleep(): Được gọi khi serialize() một đối tượng. Thông thường khi chúng
    ta serialize() một đối tượng thì nó sẽ trả về tất cả các thuộc tính trong đối tượng đó.
    Nhưng nếu sử dụng __sleep() thì chúng ta có thể quy định được các thuộc tính có
    thể trả về.

  • __wakeup(): Ngược lại với hàm __sleep(), phương thức __wakeup() sẽ được
    gọi trực tiếp sau khi thực hiện deserialization. Nó thường được sử dụng để xác định
    lại trạng thái ứng dụng bị mất trong quá trình serialization, ví dụ như kết nối với cơ
    sở dữ liệu.

  • __toString(): Bất cứ khi nào một đối tượng được sử dụng trong chuỗi,
    phương thức này được gọi để trả về một chuỗi đại diện của đối tượng (Ví dụ echo
    đối tượng).

  • __invoke(): Phương thức này được gọi khi ta cố gắng gọi một đối tượng như
    một hàm.

  • __set_state(): Được sử dụng khi chúng ta gọi hàm var_export một đối tượng.
    Trong một ứng dụng, hàm var_export() được sử dụng để hiển thị bất kỳ loại dữ liệu
    10
    nào dưới dạng mã PHP có thể phân tích cú pháp. Nếu một đối tượng được sử dụng
    làm đối số, phương thức __set_state() được gọi để xác định thuộc tính nào được
    xuất.

  • __clone(): Được sử dụng khi chúng ta sao chép 1 đối tượng thành 1 đối
    tượng hoàn toàn mới không liên quan đến đối tượng cũ.

  • __debugInfo(): Được gọi khi chúng ta sử dụng hàm var_dump().
    Phần lớn trong số các function này sẽ không làm gì nếu không có sự khai báo, thiết lập
    của người lập trình. Ở đây có hai Magic method mà ta cần quan tâm đến là __wakeup(),
    __destruct().

  • Ví dụ:

  • Ta thấy hàm __wakeup() được gọi ngay sau khi một đối tượng thuộc lớp Test được
    deserialize, và khi kết thúc đoạn kịch bản PHP trên, function __destruct() cũng ngay lập tức
    được gọi ngầm.
  1. Serialization trong PHP
  • Quá trình serialization trong PHP được hỗ trợ qua hàm serialize(), và thực hiện quá trình
    deserialization thông qua hàm unserialize(). Định dạng của chuỗi sau khi thực hiện
    serialization như sau:

  • a : Định nghĩa tham số đã truyền là một mảng. Đi kèm với a là một giá trị số nguyên
    chỉ định kích thước của mảng.

  • i : Định nghĩa một giá trị số.

  • b : Định nghĩa một giá trị boolean.

  • s : Định nghĩa một chuỗi hằng, luôn được theo sau bởi một giá trị số khai báo độ
    dài của chuỗi.

  • S : Định nghĩa một chuỗi hằng trong định dạng được mã hóa.

  • O : Đại diện cho một đối tượng, theo là độ dài của tên lớp và tên lớp. Sau đó, theo
    sau là số lượng thuộc tính và các thuộc tính được xác định. Lưu ý rằng một thuộc tính cũng có thể bao gồm một đối tượng khác với các thuộc tính được xác định của
    nó.

  • Ví dụ về serialization. Ở dòng 1, ta có một mảng có 2 phần tử. Ở dòng 2, thực hiện quá
    trình serialize mảng trên, kết quả được hiển thị ở dòng 4.

  • Sau khi thực hiện serialization, ta nhận kết quả như sau:

  • {1}: Mảng có 2 phần tử.

  • {2}: Phần tử thứ 1 có giá trị số là 2.

  • {3}: Phần tử thứ 2 có giá trị là chuỗi “string” với độ dài là 6.

  • Với serialize mảng, một danh sách các cặp khóa – giá trị được chứa trong dấu ngoặc
    nhọn:

  • Để deserialize đối tượng, có hai cách thức. Cách thức thứ nhất, serialize các thuộc tính
    của đối tượng giống như mảng. Cơ chế này sử dụng O để định danh.

  • Ký tự ở trên là NULL bytes. Thuộc tính private có thêm tiền tố ClassName và
    thuộc tính protected có thêm tiền tố *.

  • Cách thức thứ hai, serialize với định dạng tuỳ chỉnh. Việc serialize sẽ thực hiện
    implements phương thức serialize trong interfaces serializable và sử dụng C để định nghĩa.

  1. Tấn công Deserialization trong PHP
  • Phân tích lỗ hổng Unserialize() RCE trên vBulletin 5.xx
  • Phân tích lỗ hổng Deserialize dữ liệu không an toàn trên nền tảng vBulletin ảnh hưởng
    đến các phiên bản 5.xx. Mức độ là cực kỳ nghiêm trọng, kẻ tấn công có thể thực thi mã từ xa.

  • Lỗ hổng nằm ở hàm decodeArguments($argumentes).

  • Hàm decodeArguments() nhận đầu vào từ người dùng nhập vào và thực hiện deserialize
    với hàm unserialize.

  • Hàm rewind() trong lớp vB_dB_Result. Chúng ta có thể điều khiển được giá trị của
    $this->recordset, để gọi đến hàm $this->db->free_result().

  • Giá trị nguyên gốc của $this->functions[‘free_result’] chính là hàm mysql_free_result.
    Tuy nhiên, thông qua quá trình deserialize, chúng ta có thể điều khiển được giá trị của hàm
    functions[‘free_result’] sang giá trị mong muốn, ví dụ sang hàm phpinfo để thực hiện hiển
    thông thông tin phiên bản php trên hệ thống

  • Và dưới đây là payload để tạo ra PoC:

  • Lỗ hổng Phar Deserialization:
  • File Phar trong PHP tương tự như file Jar trong Java là một gói định dạng cho phép ta
    gói nhiều các tập mã nguồn, các thư viện, hình ảnh,… vào một tệp.
    Cấu trúc một file Phar gồm có:
  • Stub: là một file PHP và ít nhất phải chứa đoạn code sau: <?php __HALT_COMPILER();
  • A manifest (bảng kê khai): miêu tả khái quát nội dung sẽ có trong file.
  • Nội dung chính của file.
  • Chữ ký: để kiểm tra tính toàn vẹn (phần này là tùy chọn).

  • Điểm đáng chú ý nhất trong cấu trúc của một file Phar đó là phần manifest, theo tài liệu
    của PHP thì trong mỗi một file Phar, phần bảng kê khai giữ các Meta-data đã được serialize.
    Nếu có một hàm filesystem gọi đến một file Phar thì tất cả các Meta-data trên sẽ được tự động
    unserialize.

  • Dưới đây là danh sách các hàm filesystem có thể sử dụng để thực hiện tấn công lỗ hổng
    này:

  • Để khai thác lỗ hổng này cần 3 điều kiện sau:
  • Tìm được POP chain trong trong mã nguồn cần khai thác.
  • Đưa được file Phar vào đối tượng cần khai thác.
    17
  • Tìm được điểm (entry point) có thể gửi mã độc lên, đó là những chỗ mà các hàm
    filesystem gọi tới các file Phar do người dùng kiểm soát.
  1. Phương pháp phát hiện
  • Phương pháp black-box:
  • Phương pháp kiểm thử hộp đen (black box) là một phương pháp kiểm thử mà việc kiểm
    tra các chứ năng của một ứng dụng không cần quan tâm đến cấu trúc/thiết kế nội bộ hoặc hoạt
    động của nó.

  • Đối với lỗ hổng này, người ta thường khó mà có thể thực hiện tấn công lỗ hổng nếu
    không có mã nguồn của ứng dụng mục tiêu, tuy nhiên các nhà nghiên cứu lỗ hổng đã đưa ra
    công cụ PHPGCC bao gồm các framwork, version, nơi gửi payload. Ta có thể sử dụng công
    cụ này để tiến hành kiểm thử hộp đen.
  • PHPGCC là một công cụ cực kỳ hữu ích để tạo ra các payload, có sẵn các gadget của
    thư viện, framework có thẻ khai thác.Hiện tạo, công cụ hỗ trợ các nền tảng : Codelgniter4,
    Doctrine, Drupal, Guheads , Laravel, Magento, Monolog, Phalcon, Podio, Slim,
    SwiftMailer,Wordpress, Yii và ZendFramwork.
  • Sử dụng công cụ
    Thực thi ./phpgcc -1 để hiển thị danh sách các chuỗi tiện ích (gadget chains) :

  • Các thành phần trong chuỗi tiện ích :
  • Name : Tên của thư viện hoặc framework.
  • Version : Phiên bản của framework hoặc thư viện sử dụng.
  • Type : Kiểu khai thác : Thực thi mã lệnh từ xa, đọc file, ghi file…
  • Vector : Magic method được sử dụng.
    19
  • I (Information): Các thông tin khác về chuỗi.
  • *Sử dụng –i để biết thêm thông tin về chuỗi :

  • Cú pháp tạo payload như sau: ./phpggc <gadget-chain> [parameters].
  • Ví dụ để tạo payload cho Monolog, cú pháp như nhau :

  • Phương pháp white – box:
  • White -box testing (kiểm thử hộp trắng ) là một phương pháp kiểm thử phần mềm, trong
    đó các thiết kế, cấu trúc giải thuật bên trong và việc thực hiện các công việc đều được biết đến
    bằng cách thử kiểm thử sự logic của chương trình.

  • White -box testing (kiểm thử hộp trắng ) là một phương pháp kiểm thử phần mềm, trong
    đó các thiết kế, cấu trúc giải thuật bên trong và việc thực hiện các công việc đều được biết đến
    bằng cách thử kiểm thử sự logic của chương trình.
  • Các phương pháp:
  • Kiểm thử giao diện lập tình ứng dụng – API testing.
    *Code coverage – Bao phủ mã lệnh.
  • Các phương pháp gán lỗi.
  • Các phương pháp kiểm thử hoán chuyển.
  • Kiểm thử tĩnh.
  • Với phương pháp white- box, chúng ta sẽ tìm trong mã nguồn những hàm nguy hiểm,
    dùng không đúng cách như unserialize() , file_exists(), file_get_contents()… hay các magic
    method như _detruct, _wakeup , _toString. Chúng ta sẽ sử dụng phương pháp white- box
    thông qua các ví dụ chứa lỗi bên dưới đây :
  • Ví dụ 1 : Khai thác thông qua phương thức __destruct

  • Payload khai thác :
  • Serialized dữ liệu:
  • a:2:{i:0;s:4:”XVWA”;i:1;s:33:”Xtreme Vulnerable Web Application”;}
  • Thực thi mã lệnh:
    string(68) “O:18:”PHPObjectInjection”:1:{s:6:”inject”;s:17:”system(‘whoami’);”;}”
  1. Phương pháp ngăn ngừa
  • Deserialization dữ liệu an toàn:
  • Không chấp nhận các đối tượng đã được serialize từ nguồn không đáng tin
    cậy. Đây là điều cần thiết trong môi trường ứng dụng. Bằng cách chỉ cho phép
    những người dùng và những tiến trình đã được chứng thực có quyền truy cập
    vào website của bạn. Đây là biện pháp giúp giảm thiểu khả năng bị khai thác
    thông qua lỗ hổng Deserialize. Biện pháp này không thể ngăn chặn hoàn toàn
    bởi vì tài khoản người dùng có thể bị hack và bị truy cập thông qua những
    con đường độc hại khác.
  • Quá trình serialize cần được mã hóa tránh việc khởi tạo đối tượng gây nguy
    hại và dữ liệu giả mạo có thể chạy trong hệ thống quá bạn. Biện pháp này sẽ
    yêu cầu một số cải tiến trong hệ thống.
  • Chạy mã deserialization với quyền truy cập hạn chế. Nếu một đối tượng gây
    nguy hại đã được serialize cố gắng khởi tạo một chương trình trong hệ thống
    hoặc là truy cập trái phép tài nguyên trong server. Đối tượng này sẽ bị từ chối,
    người quản trị hệ thống sẽ biết được và ngăn chặn những hành động độc hại
    tác động tới server.
  • Theo dõi quá trình serialize giúp nắm bắt được những đoạn mã độc hại. Cách
    này kiểm tra những đoạn mã được serialize trong thời gian thực, và lưu lại
    trong file log, sau khi quá trình serialize hoàn tất. File log sẽ được phân tích,
    những hành động độc hại gây nguy hiểm cho hệ thống sẽ được phát hiện và
    ngăn chặn.
  • Xác thực dữ liệu đầu vào của người dùng. Đây là bước quan trọng khi dữ liệu
    đầu vào được xử lí bằng quá trình serialize. Hacker có thể sử dụng những
    object giống như một cookie để chèn những dữ liệu nguy hiểm vào database
    để thay đổi quyền của người dùng. Mặt khác, Hacker cũng có thể nâng quyền
    hạn của mình lên quyền quản trị bằng cách sử dụng mật khẩu đã được băm
    tồn tại trước đó hoặc được lưu trong cache từ phiên làm việc trước. Từ đó,
    hacker có thể chạy DDOS, thực thi mã độc từ xa, hoặc bất cứ những gì mà
    hacker muốn từ Server.
  • Sử dụng Web Application Firewall để phát hiện mã độc và Deserialization
    không an toàn trái phép. Một WAP (Web Application Firewall) có thể là một
    thiết bị phần cứng, hoặc phần mềm hoặc là một bộ lọc có tác dụng kiểm soát
    luồng dữ liệu của phương thức HTTP để ngăn chặn những cuộc tấn công có
    thể xác định trước như là SQL injection, XSS, Deserialization dữ liệu không
    an toàn.
  • Sử dụng định dạng dữ liệu không theo chuẩn thông thường. Khi sử dụng định
    dạng dữ liệu không theo chuẩn thì sẽ hạn chế được deserialization không an toàn. Hacker sẽ không biết được những phương thức được sử dụng trong
    những đoạn code.Sử dụng một định dạng dữ liệu đặc biệt như JSON…
    *Cập nhật các thư viện, các framework
  • Ở những phiên bản framework cũ thường hay có lỗ hổng serialize, ở những bản cập nhật
    mới hơn, người phát triển đã có những biện pháp khắc phục lỗ hổng này. Vì vậy muốn những
    website không muốn bị tấn công thông qua lỗ hổng này thì hãy sử dụng những framework
    mới nhất để xây dựng website. Trước khi sử dụng một framework nào đó để xây dựng website,
    hãy tìm hiểu xem lỗ hổng này có xảy ra ở framework này không trên OWASP. Tìm hiểu những
    phiên bản framework bao nhiêu thì xảy ra lỗ hổng này, từ đó tìm cách khắc phục lỗ hổng hoặc
    là cập nhật framework liên phiên bản mới nhất.
  • Dưới đây là danh sách các framework cùng phiên bản đã phát hiện lỗ hổng

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo