Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

الخلاصاتاتصالالخصوصية© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2020-28018 — استغلال لثغرة استخدام بعد التحرير في Exim (CVE-2020-28018) يحقق تنفيذ كود عن بُعد عبر بدائيات إفساد الذاكرة، بما في ذلك القراءة/الكتابة الاعتباطية وتسريب الكومة، مع سلسلة تصعيد صلاحيات محلية اختيارية. | Kitploit
أدوات/GitHubGitHub/dorkerdevil/cve-2020-28018
تصعيد الامتيازاتتحليل الذاكرة الجنائيتحليل الثغرات الأمنيةالاستغلالأداة الوصول عن بعدتطوير الحمولاتاستغلال الملفات الثنائية
GitHubdorkerdevil/cve-2020-28018

CVE-2020-28018

استغلال لثغرة استخدام بعد التحرير في Exim (CVE-2020-28018) يحقق تنفيذ كود عن بُعد عبر بدائيات إفساد الذاكرة، بما في ذلك القراءة/الكتابة الاعتباطية وتسريب الكومة، مع سلسلة تصعيد صلاحيات محلية اختيارية.

عرض المستودع
7123منذ 5 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2020-28018: ثغرة Use-after-free (UAF) في Exim تؤدي إلى RCE

مقدمة

توجد ثغرة من نوع Use-after-free (UAF) في tls-openssl.c تسمح للمهاجمين غير المصادق عليهم عن بُعد بإفساد بيانات الذاكرة الداخلية، وبالتالي تحقيق تنفيذ برمجيات عن بُعد في النهاية.

الأوليات:

  • تسريب الذاكرة
  • أولية قراءة تعسفية
  • أولية Write-What-Where

من خلال استخدام كل هذه الأوليات معًا في سلسلة واحدة، يمكن تجاوز جميع الحمايات (mitigations) المتاحة للاستغلال بالكامل، والوصول في النهاية إلى تنفيذ برمجيات عن بُعد بصلاحية مستخدم exim.

تم نشر هذه الثغرة ضمن قائمة ضخمة من الثغرات؛ يربط التقرير الرسمي من Qualys ثغرة Use-After-Free مع CVE-2020-28008 لتنفيذ تصعيد صلاحيات محلي (LPE) بعد تحقيق RCE.

المتطلبات الأساسية

يجب أن يكون exim مُعدًا / مُجمَّعًا بالطريقة التالية:

  • تفعيل TLS
  • استخدام OpenSSL (بدلاً من GnuTLS)
  • أن يكون exim أحد الإصدارات المتأثرة
  • تعطيل X_PIPE_CONNECT

يمكنك استخدام السكربت checker.py للتحقق مما إذا كان الخادم البعيد يعمل بإصدار متأثر، ويتوفر فيه بعض المتطلبات اللازمة ليكون قابلاً للاستغلال.

[!] لا يقوم checker.py باستغلال الثغرة، بل يتحقق فقط من الإصدار المتأثر وما إذا كان كل من PIPELINING وTLS مفعّلين. هذا يعني أن الفاحص لا يتحقق من وجود التصحيح، وبالتالي قد يُنتج نتائج إيجابية خاطئة.

الكود المعرّض للثغرة

كما نعرف بالفعل، توجد الثغرة في tls-openssl.c.

/*************************************************
*         Write bytes down TLS channel           *
*************************************************/

/*
Arguments:
  ct_ctx    client context pointer, or NULL for the one global server context
  buff      buffer of data
  len       number of bytes
  more	    further data expected soon

Returns:    the number of bytes after a successful write,
            -1 after a failed write

Used by both server-side and client-side TLS.
*/

int
tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
{
int outbytes, error, left;
SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
static gstring * corked = NULL;

DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
  buff, (unsigned long)len, more ? ", more" : "");

/* Lacking a CORK or MSG_MORE facility (such as GnuTLS has) we copy data when
"more" is notified.  This hack is only ok if small amounts are involved AND only
one stream does it, in one context (i.e. no store reset).  Currently it is used
for the responses to the received SMTP MAIL , RCPT, DATA sequence, only. */
/*XXX + if PIPE_COMMAND, banner & ehlo-resp for smmtp-on-connect. Suspect there's
a store reset there. */

if (!ct_ctx && (more || corked))
  {
#ifdef EXPERIMENTAL_PIPE_CONNECT
  int save_pool = store_pool;
  store_pool = POOL_PERM;
#endif

  corked = string_catn(corked, buff, len);

#ifdef EXPERIMENTAL_PIPE_CONNECT
  store_pool = save_pool;
#endif

  if (more)
    return len;
  buff = CUS corked->s;
  len = corked->ptr;
  corked = NULL;
  }

for (left = len; left > 0;)
  {
  DEBUG(D_tls) debug_printf("SSL_write(%p, %p, %d)\n", ssl, buff, left);
  outbytes = SSL_write(ssl, CS buff, left);
  error = SSL_get_error(ssl, outbytes);
  DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
  switch (error)
    {
    case SSL_ERROR_SSL:
      ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
      log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
      return -1;

    case SSL_ERROR_NONE:
      left -= outbytes;
      buff += outbytes;
      break;

    case SSL_ERROR_ZERO_RETURN:
      log_write(0, LOG_MAIN, "SSL channel closed on write");
      return -1;

    case SSL_ERROR_SYSCALL:
      log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s",
	sender_fullhost ? sender_fullhost : US"<unknown>",
	strerror(errno));
      return -1;

    default:
      log_write(0, LOG_MAIN, "SSL_write error %d", error);
      return -1;
    }
  }
return len;
}

smtp_setup_msg() هي الوظيفة الرئيسية التي تقوم بقراءة الرسالة من العميل.

في حالات معيّنة، يتم استدعاء smtp_reset()، والتي تقوم بتنظيف جميع المخازن المؤقتة والقيم.

يمكن أن يحدث هذا في حالات مثل:

  • استلام HELO/EHLO
  • استلام STARTTLS
  • استلام RSET
  • في بداية smtp_setup_msg()

في نهاية smtp_reset()، يتم استدعاء store_reset().

store_reset هو ماكرو يغلّف الدالة store_reset_3().

دوال store هي مجرد دوال تدير الذاكرة الديناميكية.

يستخدم Exim مُخصِّص ذاكرة بالتجميع (pool allocator) على الكتل التي يتم الحصول عليها من malloc.

هناك أيضًا وظيفة مثيرة للاهتمام، وهي تنفيذ لسلسلة قابلة للنمو.

هيكل gstring:

typedef struct gstring {
  int   size;           /* Current capacity of string memory */
  int   ptr;            /* Offset at which to append further chars */
  uschar * s;           /* The string memory */
} gstring;

عند الحاجة إلى مساحة أكبر لدمج سلسلة جديدة، يتم استدعاء gstring_grow().

تحاول هذه الدالة أولاً استدعاء store_extend_3()، والتي تحاول توسيع الذاكرة داخل نفس كتلة التجمع.

يمكن أن يكون ذلك مفيدًا عندما يكون طول الإدخال غير معروف، لكن إذا تم تخصيص المزيد من الذاكرة بعدها، فلن نتمكن من توسيعها.

بعد ذلك تستدعي gstring_grow() الدالة store_newblock_3() التي تُعيد ذاكرة جديدة وتنسخ البايتات الموجودة في الذاكرة القديمة إلى الجديدة.

ثم يتم استعادة المؤشر g->s من gstring_catn().

في الدالة tls_write()، نرى متغيرًا من نوع BOOL يُسمى more.

يشير إلى ما إذا كانت هناك بيانات إضافية يجب نسخها إلى المخزن النصي قبل إعادة البيانات إلى المستخدم.

إذا كان الأمر كذلك، فلا يتم تعيين المؤشر إلى NULL.

إذا لم يكن كذلك، فسيتم إرجاع البيانات الموجودة في المخزن النصي إلى المستخدم.

تفتح هذه الوظيفة بعض الطرق المثيرة لتحفيز ثغرة Use-After-Free.

أولاً، يتم تخزين المؤشر إلى هيكل gstring في متغير ثابت (static)، مما يعني أننا سنتمكن من استخدامه في استدعاءات tls_write() اللاحقة.

كيف يمكننا تحرير المخزن المؤقت ثم استخدامه؟

نحتاج إلى جعل smtp_setup_msg() تستدعي smtp_reset() بينما لا يزال أحد المخازن المؤقتة لدينا في server_corked (لم يتم تعيينه إلى NULL).

بعد إعادة الضبط، إذا قمنا باستدعاء tls_write() بأي شكل، فسيظل المؤشر موجودًا، وبالتالي يسمح لنا باستخدامه بعد تحرير الذاكرة.

تعمل smtp_reset() على تحرير جميع ذاكرة POOL_MAIN، التي تحتوي على المخزن المؤقت الخاص بنا.

تحفيز Use-After-Free

للتحكم في ثغرة Use-After-Free، نحتاج أولاً إلى تهيئة اتصال جديد.

بما أننا نريد استغلال tls_write()، نحتاج أولاً إلى بدء جلسة TLS جديدة.

لذا نرسل أولاً أمر EHLO، يليه STARTTLS لبدء اتصال TLS.

ثم لجعل more تساوي 1، نُرسل أمرًا عبر خط الأنابيب (pipeline)، وسيكون الأمر الأخير هو نصف أمر NOOP.

نغلق اتصال TLS ونرسل بقية أمر NOOP.

نرسل الآن EHLO مرة أخرى، مما سيؤدي إلى استدعاء smtp_reset وتحرير المخزن المؤقت الخاص بنا.

نحتاج الآن إلى بدء اتصال TLS آخر لنتمكن من استخدام tls_write() مرة أخرى.

نرسل STARTTLS.

الآن، إرسال أي أمر إلى الخادم سيؤدي إلى استدعاء tls_write() لإرجاع استجابة.

لكن... لا يزال server_corked يحتوي على مؤشر إلى مكان ما في الذاكرة المحررة.

وقد يتم استخدام تلك البيانات بواسطة دوال أخرى لأنها محررة... لذلك سيتعرض هيكل gstring الخاص بنا للتلف ببيانات ثنائية عشوائية.

هذه نتيجة تحفيز UAF:

gef➤  p *corked
$1 = {
  size = 0x54595c9c, 
  ptr = 0xa7e800ba, 
  s = 0x7e35043433160bd3 <error: Cannot access memory at address 0x7e35043433160bd3>
}
gef➤  p corked
$2 = (gstring *) 0x555ad3be1b58
gef➤  

يكون الهيكل بهذا الشكل تمامًا عند الدخول إلى tls_write() للأمر الذي يلي STARTTLS.

من الواضح أنه عند محاولة الوصول إلى corked->s، يحدث انقطاع SIGSEGV.

الاستغلال

كما ذكرت Qualys، يستخدمون ثلاث خطوات لاستغلال الثغرة:

تنزيل الأداة