Các phương pháp phát hiện lỗ hổng
- 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.
- 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.
- 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ốngVà 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.
- 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:
http://testsite.com/vuln.php?data=O:8:”Example1″:1:{s:10:”cache_file”;s:15:”../../index
.php”;} - Ví dụ 2 : Khai thác thông qua phương thức __wakeup trong hàm unserualize() .
- 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’);”;}”
- 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