بلاگ آزمایشگاه امنیت وایت لب بلاگ آزمایشگاه امنیت وایت لب

بهره‌برداری از آسیب‌پذیری‌ها با استفاده از باز‌کننده بسته‌بندی Phar در PHP

| زمان مطالعه : 9 دقیقه
بهره‌برداری از آسیب‌پذیری‌ها با استفاده از باز‌کننده بسته‌بندی Phar در PHP

مقدمه

بازکننده بسته‌بندی phar یا phar deserializer یک متد جدید در شاخه استفاده دوباره کد برای حمله است که به اصطلاح Code reuse attack نامیده می‌شود. این حمله به روی برنامه‌هایی با زبان PHP که به صورت شی گرا نوشته شده‌اند قابل پیاده‌سازی است. این حمله در گردهمایی ۲۰۱۸ مربوط به گروه Black Hat توسط سم توماس، معرفی عمومی گردید. همانند حمله ROP یا return-oriented programming یا برنامه‌نویسی بازگشتی بر روی فایل‌های باینری کامپایل شده، حمله از طریق phar نیز حاوی اشیا‌ای از PHP است که قابلیت تزریق در آن را دارا می‌باشد. شکلی دیگر از برنامه‌نویسی مالکیت‌گرا یا property-oriented programming در برنامه‌نویسی شی گرا PHP می‌باشد.

تازگی این حملات، باعث جلب توجه بسیاری از افراد در جامعه امنیت شده است. این توجه باعث پیدا شدن مشکلات امنیتی بسیار جدید و جدی در پلت‌فرم‌های مطرح و منتشر شده از جمله


1.Wordpress < 5.0.1 (CVE-2018-20148)
2.Drupal 8.6.x, 8.5.x, 7.x (CVE-2019-6339)
3.Prestashop 1.6.x, 1.7.x (CVE-2018-19126)
4.TCPDF < 6.2.19 (CVE-2018-17057)
5.PhpBB 3.2.3 (CVE-2018-19274)

شده است. در ادامه به توضیح نحوه کارکرد این آسیب‌پذیری بروی PhpBB 3.2.3 و بهره برداری از آن خواهیم پرداخت و در نهایت شاهد اجرای کد‌های دلخواه از راه دور بر روی این پلت‌فرم خواهیم بود.

درباره فایل‌های phar، Deserializerها و ها‌‌‌PHP Wrapper

برای درک بهتر نحوه حمله ابتدا می‌بایست درک صحیحی از فایل‌های phar، چگونگی کار حمله در باز‌کننده‌ها یا Deserializerها و در نهایت چیستی بسته‌بندی‌کننده‌ها یا wrapperها در PHP و رابطه متقابل این سه مفهوم داشته باشیم.

فایل‌های phar چه هستند؟

فایل‌های Phar، به منظور انتشار برنامه‌های PHP و کتاب‌خانه‌ها در یک فایل است(همانند فایل JAR در زبان جاوا). همچنین فایل‌های phar می‌توانند به طور مستقیم در کد‌های PHP شما استفاده شوند. ساختار فایل‌ها phar به گونه‌ای است که می‌توان گفت یک فایل بایگانی شده (archive) هستند که شامل بخش‌هایی هستند که در ادامه به آن خواهیم پرداخت.

  • هنگامی که یک فایل phar به صورت یک برنامه مستقل در نظر گرفته شود، حداقل نیاز به اجرای کد زیر است. این قطعه کد به منظور اجرا کننده اولیه فایل مورد استفاده قرار می‌گیرد.

    <?php __HALT_COMPILER();
  • توضیحات مربوط به کد‌های منبع موجود در فایل بایگانی شده. به صورت اختیاری می‌تواند شامل اطلاعات فراداده (meta-data) باشد (این تکه از فایل اهمیت بالایی در ایجاد زنجیره حمله خواهد داشت که در ادامه می‌بینیم).

  • کد‌های منبع و اصلی فایل (قسمت واقعی و اصلی مربوط به کارکرد فایل phar)
  • قسمتی دلخواه به منظور امضا برای بررسی صحت فایل

درک حمله در هنگام‌‌ Deserilizing

بسته‌بندی (Serialize)، روالی برای ذخیره ویژگی‌های (properties) یک کلاس به صورت دودویی (binary) است که باعث می‌شود تا امکان انتقال روی شبکه و یا ذخیره این ویژگی‌ها بروی حافظه امکان‌پذیر باشد. عکس این روال باز‌کردن بسته‌بندی‌ها (Deserialize)، به منظور بازیابی اطلاعات و ویژگی‌های همان کلاس می‌باشد.
در زبان PHP روال بسته‌بندی تنها نام کلاس و همچنین ویژگی‌های آن را شامل می‌شود و این به معنی عدم بسته‌بندی توابع تعریف شده در کلاس است. این عمل نشانه طراحی دقیق و امنیتی روال بازکردن بسته‌بندی‌هاست. تنها یک مورد وجود دارد که عمل بازکردن بسته‌بندی‌ها را خطرناک می‌کند و آن صدا زدن توابع جادویی (Magic functions) است.
این توابع خاص هر کلاس است. شامل دو زیر خط در ابتدای نام که به صورت ضمنی و یا در هنگام اجرا ممکن است صدا زده شوند. به طور پیشفرض این توابع عملیاتی انجام نمی‌دهند و این وظیفه توسعه‌دهنده است که برای آن‌ها عملیات تعریف نماید. در این مقاله دو تابع هستند که ارزش اشاره کردن دارند، چرا که تنها آن‌ها هستند که در هنگام استفاده از بازکننده‌بسته‌های phar صدا زده می‌شوند.

  • __wakeup()

    به طور ضمنی در هنگام بازکردن بسته‌بندی صدا زده می‌شود.
  • destruct()

    هنگامی که دیگر یک شی مورد در کد مورد استفاده قرار نمی‌گیرد که در نهایت توسط جمع‌کننده زباله (Garbage Collector) از بین می‌روند.
    نگاهی به کد آسیب‌پذیر زیر بیاندازیم و مشاهده کنیم که چگونه از این آسیب‌پذیری می‌توان استفاده نمود.
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
28
29
30
31
32
33
# file: dummy_class.php
<?php
/* Let’s suppose some serialized data is written on the disk with
loose file permissions and gets read at a later time */
class Data {
# Some default data
public $data = array(“theme”=>”light”, “font”=>12);
public $wake_func = “print_r”;
public $wake_args = “The data has been read!\n”;

# magic method that is called on deserialization
public function __wakeup() {
call_user_func($this->wake_func, $this->wake_args);
}
}

# acting as main the conditional below gets executed only when file is called directly
if (basename($argv[0]) == basename(__FILE__)) {

# Serialize the object and dump it to the disk; also free memory
$data_obj = new Data();
$fpath = “/tmp/777_file”;

file_put_contents($fpath, serialize($data_obj));
echo “The data has been written.\n”;
unset($data_obj);

# Wait for 60 seconds, then retrieve it
echo “(sleeping for 60 seconds…)\n”;
sleep(60);

$new_obj = unserialize(file_get_contents($fpath));
}

همان‌طور که توضیح داده شد، تابع wakeup__ در هنگام باز‌کردن بسته‌بندی به صورت خودکار صدا زده می‌شود. با توجه به کد می‌توان گفت با فراخوانی تابع wakeup__ دستور print_r با آرگومان‌های موجود صدا زده خواهد شد که اشاره به دو ویژگی wake_func و wake_args دارد. اجرای ساده کد بالا خروجی زیر را تولید می‌کند.

1
2
3
4
$ php dummy_class.php 
The data has been written.
(sleeping for 60 seconds…)
The data has been read!

اما چه خواهد شد اگر در ۶۰ ثانیه‌ای که برنامه در حالت معلق (suspend) است، فایل بسته‌بندی را تغییر دهیم تا کنترل اجرای دستورات و روند اجرایی برنامه را به دست بگیریم؟ کد زیر نحوه انجام این مورد را مشخص می‌کند.

1
2
3
4
5
6
7
8
9
10
11
12
# file: exploit.php
<?php

require(‘dummy_class.php’);
# Using the existing class definition, we create a crafted object and overwrite the
# existing serialized data with our own
$bad_obj = new Data();
$bad_obj->wake_func = “passthru”;
$bad_obj->wake_args = “id”;
$fpath = “/tmp/777_file”;

file_put_contents($fpath, serialize($bad_obj));

اجرای قطعه کد بالا باعث می‌شود تا در زمان ۶۰ ثانیه‌ای که فایل dummy_class در حال انتظار است، به ما اجازه می‌دهد اجرای یک کد خوب را می‌دهد که بدون تغییر در کد dummy_class قابل انجام است. اجرای کد بالا باعث می‌شود تا مقادیر wake_func و wake_args به passthru(“id”) تغییر کند که در نهایت نتیجه نهایی به صورت زیر تغییر می‌کند.

1
2
3
4
$ php dummy_class.php
The data has been written.
(sleeping for 60 seconds…)
uid=33(www-data) gid=33(www-data) groups=33(www-data),1001(nagios),1002(nagcmd)

همان‌طور که مشاهده می‌کنید دستور اجرا شده تغییر کرده است و تابع ما اجرا گردید.
در زمینه حمله تزریق شی در PHP، این دنباله از آسیب‌پذیری از کد به نام گجت‌ها یا زنجیره POP معروف است.

بسته‌بندی‌کننده‌های PHP

با توجه به اسناد PHP، جریان‌ها (streams) راه مناسبی به منظور تعمیم فایل‌ها، شبکه، متراکم‌سازی داده و دیگر عملیات به منظور به اشتراک گذاری مجموعه‌ای از توابع و استفاده از آن‌ها است. بسته‌بندی‌کننده‌های PHP مسئولیت مدیریت پروتکل‌های مختلف و ارائه یک رابط برای ایجاد جریان است. این جریان‌ها به طور معمول با توابعی مانند fopen یا copy و یا filesize مورد استفاده قرار می‌گیرند.
یک جریان به صورت یک شبیه URL به صورت wrapper://source است. جریان‌های معروف و کاربری که PHP ارائه می‌دهد در زیر آورده شده است.

  • file://
    دسترسی به فایل‌های سیستم
  • http://
    دسترسی به پروتکل HTTP
  • ftp://
    دسترسی به فایل‌های سیستم از طریق پروتکل FTP
  • php://
    دسترسی به جریان‌های مختلف I/O

جریانی که برای ما اهمیت دارد جریان ارائه شده توسط بسته‌بندی کنننده //:phar است. تعریف کلی این جریان به صورت phar://full/or/relative/path است که دو ویژگی خاص بسیار جالب دارد.
1.این جریان داده حساس به پسوند فایل‌هایی که به آن معرفی می‌شود نیست لذا یک نامزد مناسب برای اجرای فایل‌های چندزبانی (polyglot) است.
2.اگر یک تابع فایل سیستم به عنوان یک آرگومان از phar صدا زده بشود، طبق طراحی به صورت خودکار بازکردن دسته‌بندی روی آن اتفاق می‌افتد.
در زیر لیستی از توابعی که باعث اجرای بازکردن بسته‌بندی می‌شود، آورده شده است.

1
2
3
4
5
6
7
8
9
copy                    file_exists        file_get_contents       file_put_contents       file               fileatime
filectime filegroup fileinode
filemtime fileowner fileperms
filesize filetype fopen
is_dir is_executable is_file
is_link is_readable is_writable
lstat mkdir parse_ini_file
readfile rename rmdir
stat touch unlink

نحوه انجام حمله با استفاده از جریان phar

حال ما تمام موارد و عناصر لازم برای انجام یک حمله را داریم. نیازمندی‌ها برای شروع حمله به وسیله جریان بازکننده بسته‌بندی phar معمولاً شامل موارد زیر است.

  1. حضور یک زنجیره از از گجت‌ها که شامل کتاب‌خانه‌های خارجی استفاده شده در برنامه می‌گردد. این مورد قابل یافت شدن از طریق دیدن کد‌ها و خواندن آن‌ها است.
  2. قابلیت ایجاد و یا وارد کردن فایل phar به صورت محلی و یا از راه دور (مانند بارگذاری (upload) روی کارگزار (server) ).
  3. نقطه ورودی، جایی که یک تابع سیستمی فراخوانی شده توسط بسته‌بندی کننده phar تحت اختیار کاربر باشد (این مورد نیز با مشاهده کد امکان‌پذیر است).

برای مثال، محلی برای بارگذاری عکس برای یک کاربر باشد که عملیات خاصی برای جلوگیری از مشکلات در نظر گرفته نشده است. کاربر به جای استفاده از پروتکل HTTP در هنگام بارگذاری می‌تواند از پروتکل phar برای بارگذاری استفاده نماید. سمت کارگزار، در همین حین، یک تابع سیستمی مانند file_exists به منظور بررسی وجود فایل مورد استفاده قرار گیرد. این مورد باعث می‌شود تا بازکننده بسته‌بندی به صورت خودکار اجرا گردد که با وجود یک زنجیره از گجت‌ها و دستورات امکان به دست آوردن دسترسی به سیستم فراهم می‌شود.