این آسیب‌پذیری فقط بر روی OpenSSL نسخه ۱٫۱٫۰a که در تاریخ ۲۲ سپتامبر ۲۰۱۶ منتشر شده است، تأثیرگذار است. وصله‌ای که برای آسیب‌پذیری CVE-2016-6307 منتشر شده بود، دارای اشکالی بود که اگر یک پیام بزرگ‌تر از تقریباً ۱۶ کیلوبایت دریافت شود، درنتیجه بافر زمینه برای ذخیره پیام دریافتی مجدداً اختصاص داده شده و جابجا می‌شود. متأسفانه شاخص متصل به مکان قدیمی باقی‌مانده بود، که منجر به تلاشی برای نوشته شدن در مکان آزاد شده گذشته، می‌شد. این امر احتمالاً باعث crash شدن می‌شود، اگرچه پتانسیل این را دارد که منجر به اجرای یک کد دلخواه شود. این آسیب‌پذیری در تاریخ ۲۳ سپتامبر ۲۰۱۶ توسط یکی از اعضای تیم امنیتی گوگل به نام Robert Święcki گزارش شده است[۱].

  • این مشکل در OpenSSL 1.0b بر طرف شده است؛ اما نسخه‌ زیر آسیب‌پذیر است:

۱٫۱٫۰a

۱    میزان تأثیر آسیب‌پذیری CVE-2016-6309

۱-۱    جدول تأثیر آسیب‌پذیری بر اساس شاخص شدت و معیارهای CVSS نسخه ۲  [۲]

CVE-2016-6309

۲-۱    جدول تأثیر آسیب‌پذیری بر اساس شاخص شدت و معیارهای CVSS نسخه ۳ [۲]

۲    آنالیز آسیب‌پذیری CVE-2016-6309

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

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

OpenSSL از ساختار “ssl_st” برای اداره کردن یک جلسه SSL استفاده می‌کند که شامل دو اشاره‌گر بافر هم است: “init_buf” و init_msg. “init_buf” به بافری مربوط می‌شود که در هنگام راه‌اندازی مورد استفاده قرار می‌گیرد و init_msg به بدنه پیام handshake مربوط می‌شود که شامل اشاره‌گر بافر “init_buf” هم می‌شود.

این آسیب‌پذیری می‌تواند توسط دسترسی داشتن به اشاره‌گر “init_msg” نادرست بهره‌برداری شود که متقابلاً هنگامی‌که “init_buf”  بعد از جابجایی به‌روزرسانی می‌شود، مورد به‌روزرسانی قرار نمی‌گیرد. کدی که در ادامه آمده است قسمتی از کد اصلی است که از OpenSSL 1.1.0a استخراج شده است. در توابعی که در این کد آمده است “SSL *s” توسط  “struct ssl_st” تعریف شده است.

Crash زمانی رخ می‌دهد که OpenSSL بافر را آزاد می‌کند. عکسی که در ادامه آمده است مربوط به یک سرور OpenSSL است که تحت تأثیر این آسیب‌پذیری قرار گرفته است.

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

Fortinet یک امضای OpenSSL.Large.Message.Size.Handling.UAF را به منظور رسیدگی به این آسیب‌پذیری منتشر کرده است [۳].

۳   چگونگی محافظت از سیستم نسبت به آسیب‌پذیری CVE-2016-6309 موجود در OpenSSL 1.1.0a

اخیراً کتابخانه امنیتی OpenSSL موفق به دستیابی به یک وصله برای آسیب‌پذیری حیاتی CVE-2016-6309 که بر روی OpenSSL نسخه ۱٫۱٫۰a تأثیرگذار است، شده است. مهاجمان از راه دور می‌توانند منجر به crash کردن سرور OpenSSL شوند و یا اینکه یک کد دلخواه را بر روی سرور اجرا کنند. این کار از طریق فرستادن یک پاکت handshake با پیامی بزرگ‌تر از ۱۶ کیلوبایت انجام می‌شود. برای مقابله با این حمله کد مربوط به این آسیب‌پذیری به طور کامل آنالیز شده است و اثبات مبتنی بر ادعای مربوط به این آسیب‌پذیری نیز منتشر شده است.

OpenSSL در ارتباط با این آسیب‌پذیری توضیحات زیر را ارائه کرده است:

بافر مربوط به پیام‌های دریافتی ۱۶ کیلوبایت است. اگر یک پیام دریافتی بیش از این مقدار باشد، بافر مجدداً تخصیص داده خواهد شد. این امر می‌تواند منجر به تغییر مکان بافر پایه شود. هر چیزی که مربوط به مکان قبلی بافر باشد به داده‌‌های آزاد ارجاع داده خواهد شد.

از منبع کد OpenSSL می‌توان فهمید که از ساختار ssl_st برای ذخیره اطلاعات جلسه SSL استفاده می‌کند [۴]. هردوی init_msg و init_buf از اعضای ساختار ssl_st هستند. init_msg مربوط به بدنه پیام handshake می‌شود که شامل بافر مربوط به init_buf می‌شود. متغیر “s” (s->init_msg) یک نوع اشاره‌گر به ساختار ssl_st است.

از روی این اطلاعات می‌توان به یک پیام handshake واحد شک کرد که ممکن است باعث این آسیب‌پذیری شده باشد.

در اینجا فرمت مربوط پاکت handshake در پروتکل TLS آورده شده است:

———+———–+———–+———–+———–+——————————
| ۰x16 | 0x0301 | 0x4000 | 0x0100 | 0x5560 | DATA(length=0x5560) |
———+———–+————+———–+———–+——————————
|       |       |               |————–Length of challenge
|       |       |
|       |       |—————————————-Length handshake message
|       |—————————————————–Version
|——————————————————————Content type:Handshake

در اینجا یک پاکت handshake به صورت دستی ساخته شده است که بیش از ۱۶ کیلوبایت فضا اشغال می‌کند (۰x5560).

این handshake که دارای اندازه بیش از ۱۶ کیلوبایت است را به یک سرور OpenSSL آسیب‌پذیر ارسال می‌کنیم و این امر باعث crash کردن سرور می‌شود.

حال برای جزئیات بیشتر در ابتدا نگاهی به نحوه برطرف کردن این آسیب‌پذیری می‌کنیم. وصله مربوط به این آسیب‌پذیری در اینجا[۵] آورده شده است.

if (!SSL_IS_DTLS(s)
&& s->s3->tmp.message_size > 0
–                    && !BUF_MEM_grow_clean(s->init_buf,
–                                           (int)s->s3->tmp.message_size –                                           + SSL3_HM_HEADER_LENGTH)) { +                    && !grow_init_buf(s, s->s3->tmp.message_size +                                         + SSL3_HM_HEADER_LENGTH)) {
ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
SSLerr(SSL_F_READ_STATE_MACHINE, ERR_R_BUF_LIB);
return SUB_STATE_ERROR;

در کد آورده شده در بالا، خطوطی که قبل از آن‌ها علامت “-” قرار دارد مربوط به کد قدیمی می‌باشد و خطوط مربوط به وصله ارائه شده در ابتدایشان علامت “+” قرار داده شده است.

این تابع جدید معرفی شده به صورت داخلی همچنان تابع قدیمی BUF_MEM_grow_clean را فراخوانی می‌کند، بعد از آن init_msg را مجدداً راه‌اندازی می‌کند.

در مرحله بعد بیایید به بخشی از کد آسیب‌پذیر از طریق debugger نگاهی بیندازیم:

شکل بالا این اطلاعات را در اختیار ما قرار می‌دهد:

  • s->init_msg=0x6c3184
  • s->init_buf.data=0x6c3180
  • length=0x4000 and is controllable by the attacker through the handshake message

حال، s->init_msg به پیام handshake اشاره دارد و در بافر موجود است و مربوط به s->init_buf.data می‌شود.

در این قسمت، در ظاهر  init_buf.data تغییر کرده است اما init_msg هنوز به ۰x6c3184 اشاره می‌کند.

  • s->init_msg=0x6c3184
  • s->init_buf.data=0x6ce7c0

حال بیاید به تابع BUF_MEM_grow_clean نگاهی بیندازیم تا بفهمیم چرا s->init_buf.data تغییر کرده است:

size_t BUF_MEM_grow_clean(BUF_MEM *str, size_t len)
{
char *ret;
size_t n;
if (str->length >= len) {
if (str->data != NULL)
memset(&str->data[len], 0, str->length – len);
str->length = len;
return (len);
}
if (str->max >= len) {
memset(&str->data[str->length], 0, len – str->length);
str->length = len;
return (len);
}
if (len > LIMIT_BEFORE_EXPANSION) {
BUFerr(BUF_F_BUF_MEM_GROW_CLEAN, ERR_R_MALLOC_FAILURE);
return 0;
}
n = (len + 3) / 3 * 4;
if ((str->flags & BUF_MEM_FLAG_SECURE))
ret = sec_alloc_realloc(str, n);       <—–realloc a new memory for init_buf
else
ret = OPENSSL_clear_realloc(str->data, str->max, n);
if (ret == NULL) {
BUFerr(BUF_F_BUF_MEM_GROW_CLEAN, ERR_R_MALLOC_FAILURE);
len = 0;
} else {
str->data = ret;
str->max = n;
memset(&str->data[str->length], 0, len – str->length);
str->length = len;
}
return (len);
}

در کد قبلی، محل‌هایی که حافظه مجدداً تخصیص داده شده است را مشخص کردیم. اگر ما بتوانیم از هر سه ckeck (“str->length >= len,” و “str->max >= len,” و “len > LIMIT_BEFORE_EXPANSION”) عبور کنیم، تابع در نهایت به s->init_buf.data (str->data) تغییر پیدا کرده و حافظه توسط sec_alloc_realloc (علامت‌گذاری شده) تخصیص داده می‌شود.

از روی شکل بالا، ما می‌توانیم به روشنی مقادیر متغیرها را مشاهده کرده در هنگامی‌که برنامه در حال اجرا داخل BUF_MEM_grow_clean است:

  • len=0x5564 (the packet’s real length)
  • str->length=0x4000 (the length of the handshake message)
  • str->max=0x5558 (the length of challenge)

به دلیل اینکه ما می‌توانیم مقدار هر سه متغیر را کنترل کنیم، این امر بدیهی است که از تمامی check ها عبور کرده و به تابع sec_alloc_realloc function برسیم.

درنهایت، اتفاقی که در داخل تابع sec_alloc_realloc می‌افتد از این قرار است:

حال بیاید به تابع BUF_MEM_grow_clean نگاهی بیندازیم تا بفهمیم چرا s->init_buf.data تغییر کرده است:

static char *sec_alloc_realloc(BUF_MEM *str, size_t len)
{
char *ret;
ret = OPENSSL_secure_malloc(len);          <———realloc memory
if (str->data != NULL) {
if (ret != NULL)
memcpy(ret, str->data, str->length);
OPENSSL_secure_free(str->data);       <———free old memory
}
return (ret);
}

تابع sec_alloc_realloc حافظه جدید را مجدداً اختصاص می‌دهد و سپس حافظه قدیمی (علامت‌گذاری شده) را آزاد می‌کند.

در این مرحله، همچنان s->init_msg به حافظه قدیمی که توسط sec_alloc_realloc آزاد شده است، اشاره دارد. بنابراین بعداً هنگامی‌که s->init_msg ارجاع داده می‌شود، برنامه امکان crash کردن دارد یا در بعضی موارد مجبور به در اختیار گذاشتن حافظه به مهاجم می‌شود که در نهایت منجر به اجرای کد خواهد شد.

وصله مورد نظر برای این آسیب‌پذیری، تابع جدید grow_init_buf را اضافه کرده تا تابع BUF_MEM_grow_clean را جایگزین کند و تابع جدید init_msg را هنگامی که OpenSSL حافظه جدید را مجدداً اختصاص می‌دهد، به‌روزرسانی خواهد کرد تا مطمئن شود که به حافظه معتبر اشاره می‌کند. برای درک بهتر، یک ویدیوی آموزشی تدارک دیده شده است تا این حمله را در عمل ثابت کند[۶]. (در این ویدیو، پنجره سمت راست سرور OpenSSL  را بر روی پورت ۴۴۳ با پارامترهای استاندارد نشان می‌دهد در پنجره سمت چپ، یک handshake دارای اندازه بیش ‌از حد مجاز به سرور آسیب‌پذیر ارسال شده است که منجر به crash کردن آن می‌شود.)

۴    تأثیر آسیب‌پذیری CVE-2016-6309

بیش از ۶۰ درصد از وب‌سایت‌های فعال از OpenSSL برای انتقال داده‌ استفاده می‌کنند. هر روز سایت‌های بیشتری به منظور محافظت از اطلاعات حساس کاربران خود مانند کلمات عبور، شناسه‌ها و نام‌های کاربری از OpenSSL استفاده می‌کنند. آمازون، بزرگ‌ترین فروشنده Cloud، به مشتریان خود OpenSSL را پیشنهاد می‌دهد[۷].

بسیاری از ما، حمله Heartbleed بر روی OpenSSL را که در آوریل سال ۲۰۱۴ اتفاق افتاد به خاطر داریم. در آن موقع، این حمله به بدترین کابوس اینترنت تبدیل شد. یک نظرسنجی که از جوانان آمریکایی در آوریل ۲۰۱۴ انجام شد نشان داد ۶۰ درصد از آن‌ها در مورد حمله Heartbleed اطلاع داشتند. در میان آن‌هایی که از اینترنت استفاده می‌کردند، ۳۹ درصد از طریق عوض کردن کلمات عبور یا بستن حساب‌های کاربری از حساب‌های کاربری آنلاین خود محافظت کرده‌‌اند. ۲۹ درصد از این افراد اعتقاد داشتند که اطلاعات شخصی آن‌ها به علت حمله Heartbleed در معرض خطر قرار گرفته است و ۶ درصد اعتقاد داشتند که اطلاعات شخصی آن‌ها دزدیده شده است. با توجه به این حمله و نتایج نظرسنجی، می‌توانیم متوجه شویم که یک اشکال ساده در OpenSSL می‌تواند تأثیر عمیقی بر روی اینترنت به علت گسترش یافتن OpenSSL داشته باشد. آسیب‌پذیری CVE-2016-6309 که در این مقاله در رابطه با آن بحث شد، با دیگر آسیب‌پذیری‌های موجود در OpenSSL فرق دارد. در این مورد، این مشکل زمانی رخ می‌دهد که یک سرور OpenSSL یک پیام handshake را که اولین پاکت از پروتکل TLS است، اداره ‌کند. این امر انجام این حمله را آسان می‌کند، چراکه مهاجم می‌تواند تنها با فرستادن یک پاکت و بدون نیاز به احراز هویت به سرور دسترسی داشته باشد[۸].

به‌روزرسانی مربوط به این آسیب‌پذیری در اینجا قرار داده شده است[۹].

۵    منابع

[۱] https://www.openssl.org/news/secadv/20160926.txt

[۲] https://access.redhat.com/security/cve/cve-2016-6309

[۳]https://blog.fortinet.com/2016/10/12/analysis-of-openssl-large-message-size-handling-use-after-free-cve-2016-6309

[۴] http://openxdas.sourceforge.net/doxygen/html/structssl__st.html

[۵]https://git.openssl.org/?p=openssl.git;a=commitdiff;h=acacbfa7565c78d2273c0b2a2e5e803f44afefeb;hp=df7681e46825d4a86df5dd73317d88923166a506

[۶]http://players.brightcove.net/21712694001/S1o50VS1l_default/index.html?videoId=5239287943001

[۷] https://aws.amazon.com/security/security-bulletins/openssl-security-advisory-may-2016/

[۸]https://securingtomorrow.mcafee.com/mcafee-labs/how-to-protect-against-openssl-1-1-0a-vulnerability-cve-2016-6309/

[۹] https://www.openssl.org/source/openssl-1.1.0b.tar.gz