آسیب پذیری Capability Leaking چیست و چه خطری دارد؟

0 55

فهرست

در این مقاله قصد داریم راجع به Capability Leaking صحبت کنیم. آسیب پذیری Capability Leaking یک آسیب پذیری است که عمدتا توسط سهل انگاری توسعه دهنده پیش می آید و عواقب بسیاری خطرناکی برای سیستمی که برنامه ای با چنین آسیب پذیری ای را اجرا می کند دارد. مواردی که در این مقاله ذکر می شوند با تمرکز بر لینوکس و مکینتاش اما به راحتی برای سیستم عامل های دیگر مثل ویندوز نیز قابل تعمیم هستند.

اجرای اعمال ممتاز توسط کاربر عادی

در سیستم عامل کاربران در سطوح و گروه های مختلفی قرار می گیرند. برخی اعمال در یک سیستم عامل مثل تغییر کلمه عبور ورود به سیستم از اعمال ممتاز یا Privileged هستند که تنها توسط یک کاربر سطح بالا مثل root قابل انجام است. اجرای اعمال ممتاز توسط کاربران عادی مجاز نیست اما برخی از این اعمال نیاز یک کاربر است و ممکن است بارها در یک فعالیت نیاز داشته باشد تا آن ها را اجرا کند. برای مثال همین تغییر کلمه عبور را در نظر بگیرید. اگر دسترسی تغییر کلمه عبور به کاربر عادی داده شود می تواند کلمه عبور کاربران دیگر را تغییر داده یا حتی کاربر جدیدی با سطح root در سیستم تعریف کند. اگر به کاربر عادی دسترسی تغییر کلمه عبور ندهیم باید هربار درخواست خود را به یک کاربر سطح بالا مثل root ارائه دهد تا این عمل توسط root انجام شود که کار سخت و زمان بری است و از طرف دیگر نارضایتی کاربران را به همراه دارد. احتمالا باید مکانیزمی وجود داشته باشد که یک کاربر عادی بتواند برای تعدادی از اعمال ممتاز مجوز دریافت کند. درواقع باید برنامه ای که در سطح و مجوز کاربر عادی کار می کند برای عملیاتی دسترسی سطح بالا دریافت کرده و کار خود را انجام دهد.

Daemon و Set-UID

دو رویکرد معمول برای مساله ی مطرح شده وجود دارد: استفاده از Daemon و استفاده از مکانیزم Set-UID. هر یک را به طور جداگانه بررسی می کنیم.

Daemon

Daemon یک برنامه کامپیوتری است که در پس زمینه اجرا می شود. برای اجرای یک عمل مجاز، یک برنامه عادی باید یک درخواست به یک Daemon با دسترسی ممتاز (مثلا root Daemon) ارسال کند که Daemon عمل مورد نظر را از طرف برنامه کاربر انجام دهد. مجددا عمل تغییر کلمه عبور در سیستم را در نظر بگیرد. وقتی کاربر عادی قصد داشته باشد کلمه عبور خود را تغییر دهد، برنامه ی کاربر یک درخواست به Daemon ارسال می کند و Daemon فایل shadow را از طرف کاربر عادی تغییر می دهد و در نهایت کلمه عبور کاربر تغییر می کند. بسیاری از سیستم عامل ها برای انجام عملیات ممتاز از Daemon ها استفاده می کنند. در ویندوز به این برنامه ها سرویس (Service) گفته می شود که شبیه به Daemon در لینوکس عمل می کند.

Set-UID

این رویکرد در سیستم عامل ها بر پایه Unix استفاده می شود. این رویکرد برای هر برنامه یک بیت در نظر می گیرد که اگر این بیت ۱ باشد سیستم عامل آن برنامه را یک برنامه با مجوز عملیات ممتاز در نظر می گیرد. این رویکرد توسط دنیس ریچی (Dennis Ritchie) ابداع شد. در این رویکرد مجوز عمل ممتاز مستقیما به برنامه کاربر عادی اختصاص داده می شود که بتواند یک عمل ممتاز را انجام دهد اما این مجوز یک مجوز محدود است که تنها اجازه عمل مورد نظر (برای مثال تغییر کلمه عبور) را صادر می کند. به برنامه هایی که با استفاده از این مکانیزم مجوز عملیات ممتاز را دریافت می کنند برنامه های Set-UID می گویند.

شناسه های کاربری یک برنامه در Unix

در Unix یک برنامه دارای سه شناسه کاربری است: شناسه کاربری واقعی یا Real userID، شناسه کاربری موثر یا Effective userID و شناسه کاربری ذخیره یا Saved userID. شناسه کاربری واقعی همان شناسه کاربری است که برنامه را اجرا می کند. شناسه کاربری موثر برای کنترل دسترسی به کار می رود و نشان می دهد برنامه چه سطحی از دسترسی را دارا است و شناسه کاربری ذخیره برای تغییر در سطح دسترسی به کار می رود.
برای برنامه عادی شناسه کاربری واقعی با شناسه کاربری موثر برابر است اما برای یک برنامه Set-UID یا ممتاز شناسه کاربری واقعی همان شناسه کاربر است اما شناسه کاربری موثر بستگی به کاربری دارد که عملیات ممتاز را اجرا می کند. اگر این کاربر root باشد شناسه کاربری موثر برنامه برابر صفر است.

یک مثال از شناسه های کاربری یک برنامه

برنامه id در لینوکس شناسه های کاربری برنامه در حال اجرا را نشان می دهد. اگر id را با یک کاربر عادی (غیر root) اجرا کنیم نتیجه مشابه شکل زیر است:

فرمان id در لینوکس

در این تصویر یک uid مشاهده می شود که برابر ۱۰۰۰ یعنی شناسه کاربر عادی است. عدم وجود شناسه موثر نشان می دهد که برنامه یک برنامه عادی است و ممتاز نیست. حال id را روی دسکتاپ کپی می کنیم و با دستور chown مالک آن را به root تغییر می دهیم که اگر id روی دسکتاپ را اجرا کنیم نتیجه زیر را مشاهده می کنیم:

فرمان id در لینوکس بعد از chown

این برنامه با اینکه متعلق به root است اما به دلیل ۱ نکردن بیت Set-UID هنوز یک برنامه ممتاز نیست. حال با دستوری chmod بیت Set-UID این برنامه را ۱ می کنیم (عدد ۴ این بیت را روشن می کند). نتیجه به صورت زیر است:

فرمان id بعد از set-uid

مشاهده می شود که یک euid که همان شناسه کاربری موثر یا Effective userID است با مقدار صفر، یعنی root ایجاد شده است و از حالا این برنامه یک برنامه ممتاز است.

یک مثال از یک برنامه ممتاز در لینوکس

فایل shadow در لینوکس مربوط به اطلاعات کاربران است که فایل بسیار حساسی می باشد چرا که با دسترسی به آن می توان به اطلاعات هویتی سایر کاربران دسترسی پیدا کرد و مهم تر از آن می توان روی آن نوشت و یک کاربر با سطح دسترسی ممتاز در سیستم ایجاد کرد. این فایل توسط برنامه های عادی حتی قابل خواندن هم نیست. اگر این فایل را با برنامه cat عادی باز کنیم نتیجه به صورت زیر خواهد بود:

خواندن فایل shadow

که مشاهده می کنیم این فایل توسط این برنامه قابل خواندن نیست چرا که این برنامه یک برنامه Set-UID نیست. حال این برنامه را روی دسکتاپ کپی می کنیم، مالک آن را root کرده و بیت Set-UID را روشن می کنیم. در نهایت اقدام به خواندن فایل shadow می کنیم. نتیجه به صورت زیر است:

خواندن فایل shadow

که مشاهده می شود پس از روش کردن بیت Set-UID این برنامه به یک برنامه ممتاز تغییر کرده و می توان با آن محتویات فایل shadow را خواند.

حمله به یک برنامه Set-UID

برنامه های Set-UID پتانسیل حمله های زیادی را دارند. سطوح حمله یا Attack Surface در یک برنامه Set-UID می تواند شامل مواردی چون: ورودی کاربر یا User Input (ورودی مستقیم)، ورودی سیستم یا System Input (ورودی غیر مستقیم) و… باشد. منظور از ورودی کاربر، ورودی هایی است که برنامه به طور مستقیم از کاربر دریافت می کند و منظور از ورودی سیستم، ورودی هایی است که به طرق مختلف از سیستم دریافت می شود. یک مثال معروف از ورودی های غیر مستقیم، متغیرهای محیطی یا Environment Variable ها هستند که به آن ها ورودی های مخفی یا Hidden Input‌ گفته می شود که این متغیرها تا کنون موجب آسیب پذیری های متعددی در برنامه شده اند اما مشکل از خود متغیرها نیست و مشکل از استفاده از آن ها توسط توسعه دهنده است.
در ادامه قصد داریم یک آسیب پذیری بسیار خطرناک در مورد برنامه های Set-UID یعنی نشت قابلیت یا آسیب پذیری Capability Leaking را بررسی کنیم که در سزوج حمله، در قسمت ورودی های سیستمی قرار می گیرد.

Capability Leaking چیست؟

در موارد زیادی یک برنامه ممتاز، حین اجرا سطح خود را کاهش می دهد و پس از این عمل برنامه به صورت یک برنامه عادی غیرممتاز به کار خود ادامه می دهد. یک مثال معروف برنامه su است که برای سویچ بین دو حساب کاربری به کار می رود. این برنامه یک برنامه ممتاز است اما وقتی تایید اعتبار کاربر انجام می شود خود را به سطح کاربر دوم که کاربر اول به آن سویچ کرده تغییر می دهد. یکی از اتفاقاتی که در این عمل تغییر می تواند اتفاق بیافتد آسیب پذیری Capability Leaking است. مشکل اینجاست که ممکن است در این تغییر، برنامه قابلیت های خود را پاک نکرده باشد و با اینکه شناسه کاربری موثر برنامه یک شناسه عادی و غیرممتاز است اما برنامه هنوز قابلیت های ممتاز خود را دارد. این موضوع را در قالب یک مثال بررسی می کنیم.

مثالی از آسیب پذیری Capability Leaking با استفاده از زبان C

برنامه زیر را در نظر بگیرید:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

void main()
{
    int fd;
    char *x[2];

    fd = open("/etc/very_sensitive.file", O_RDWR | O_APPEND);
    printf("fd is %d\n", fd);

    setuid(getuid());

    x[0] = "/bin/sh"; x[1] = 0;
    execve(x[0], x, 0);
}

فرض کنید فایل very_sensitive.file یک فایل بسیار حساس است که تنها توسط root قابل نوشتن است یعنی مالک آن را root و مجوز آن را برابر ۰۶۴۴ قرار داده ایم. طبیعتا این فایل توسط یک برنامه عادی قابل نوشتن نیست. حال سورس را کامپیال کرده، مالک آن را root و آن را یک برنامه Set-UID قرار می دهیم:

$ gcc tgs_c_leak.c -o tgs_c_leak
$ sudo chown root tgs_c_leak
$ sudo chmod 4755 tgs_c_leak

حال محتویات فایل very_sensitive.file را مشاهده می‌کنیم:

$ cat /etc/very_sensitive.file
very sensitive content!

حال سعی می کنیم در آن بنویسیم:

$ echo somecontent > /etc/very_sensitive.file
bash: /etc/very_sensitive.file: Permission denied

که مشاهده می شود فایل توسط برنامه عادی قابل نوشتن نیست.
حال برنامه tgs_c_leak را اجرا کنید. توجه شود که این برنامه یک برنامه Set-UID است.

$ ./tgs_c_leak
fd is 3
$

همانطور که از کد متوجه شدید این برنامه یک فایل حساس را باز می کند و پس از آن خود را به سطح عادی نزول می دهد و یک شل عادی (شل کاربر عادی غیر root) در اختیار کاربر قرار می دهد.
اما این برنامه یک مشکل بد دارد. فایل حساس باز شده توسط برنامه که در این نقطه یک برنامه Set-UID بوده است بسته نشده است و بنابراین File Descriptor آن هنوز با مجوز نوشتن روی فایل سیستم باز است!!! حال سعی می کنیم با شل معمولی بدون مجوز نوشتن روی این فایل، روی فایل بنویسیم!!!

$ ./tgs_c_leak
fd is 3
$ echo maliciouscontent >& 3

می دانیم که با استفاده از & و عدد File Descriptor روی فایل با این File Descriptor (در اینجا ۳) نوشت.
حال فایل را باز کنید:

$ cat /etc/very_sensitive.file
very sensitive content!
maliciouscontent

بله! این یک آسیب پذیری Capability Leaking است که توانستیم روی یک فایل محافظت شده در مقابل نوشتن (Write Protected) با برنامه بدون مجوز بنویسیم!
برای برطرف کردن مشکل بالا باید قابلیت های برنامه قبل از نزول را از بین ببریم که در این مثال با بستن فایل این مشکل حل می شود:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

void main()
{
    int fd;
    char *x[2];

    fd = open("/etc/very_sensitive.file", O_RDWR | O_APPEND);
    printf("fd is %d\n", fd);

    close(fd);

    setuid(getuid());

    x[0] = "/bin/sh"; x[1] = 0;
    execve(x[0], x, 0);
}

یک آسیب پذیری Capability Leaking‌ در OS X Yosemite

در ماه جولای سال ۲۰۱۵ یک آسیب پذیری در سیستم عامل مکینتاش نسخه۱۰.۱۰ Yosmite کشف شد که منجر به یک ارتقاء سطح دسترسی یا Privilege Escalation میشد و این آسیب پذیری یک آسیب پذیری Capability Leaking بود.
در این نسخه اپل قابلیت هایی به لینکر پویا یا Dynamic Linker خود یعنی dyld اضافه کرده بود که یکی از این قابلیت ها اضافه کردن یک متغیر محیطی به نام DYLD_PRINLTO_FILE بود. کاربران می توانستند با تنظیم این متغیر محیطی به یک فایل مشخص کنند که اطلاعات مربوط به log در آن فایل ذخیره شود. لینکر پویا درون برنامه در حال اجرا، اجرا می شود که اگر این برنامه یک برنامه Set-UID باشد، این لینکر با مجوز root اجرا می شود. حال اگر این متغیر محیطی به فایلی مثل etc/passwd/ تنظیم میشد، لینکر می توانست این فایل را باز کرده و در آن بنویسد. متاسفانه داینامیک لینکر فایل مورد نظر را نمی بست! حال در اینجا دو حالت می توان در نظر گرفت:

  • برنامه Set-UID کارش تمام می شود و کلیه اطلاعات از جمله File Descriptor ها پاک می شوند که در این صورت مشکلی پیش نمی آید.
  • برنامه Set-UID تمام نمی شود (مثل su) و یک پراسس دیگر (عمدتا غیر قابل اعتماد) به عنوان پراسس فرزند یا Child فراخوانی میکند.

در حالت دوم پراسس فرزند می تواند موارد مربوط به والد یا Parent از جمله File Descriptor ها را به ارث ببرد! این موضوع باعث دسترسی پراسس فرزند به فایلی که در متغیر محیطی تنظیم شده می شود که این فایل می تواند etc/passwd/، /etc/sudoer یا etc/shadow/ باشد که در این صورت پراسس فرزند که دارای دسترسی عادی است می تواند یک کاربر root در سیستم ایجاد کند و این یک Privilege Escalation است.

تشخیص Capability Leaking

برای تشخیص آسیب پذیری Capability Leaking از روش های مختلفی مثل فازینگ (Fuzzing) یا تست فاز، Symbolic Execution و روش های پویا مثل Concolic Execution (Concolic Testing)  و روش های ترکیبی استفاده می شود. تشخیص این نوع آسیب پذیری ها یک مساله مهم و اساسی برای فازرهای مدرن است که یک مسیر پژوهشی مهیج و مهم را باز می کند.

سخن پایانی

در این مقاله از سری مقالات امنیت نرم افزار آکادمی ترجنس به یک آسیب پذیری مهم یعنی آسیب پذیری Capability Leaking پرداختیم. دیدیم که این آسیب پذیری که عمدتا به دلیل سهل انگاری توسعه دهنده رخ می دهد چطور می تواند منجر به حملات بزرگ و خطرناک شود. در مقالات آینده بیشتر در مورد آسیب پذیری های نرم افزاری صحبت خواهیم کرد.

درباره ما

ترجنس | thregence.ir
آکادمی ترجنس | edu.thregence.ir
دوره‌های آکادمی ترجنس | courses.thregence.ir
اینستاگرام | instagram.com/thregence
تلگرام | t.me/thregence
یوتوب | https://bit.ly/30mGowo
آپارات | aparat.com/thregence

ارسال یک پاسخ