
استغلال لثغرة Outlook 2019 بدون نقرة واحدة CVE-2020-1349، باستخدام أخطاء تحليل رؤوس MIME لتحقيق تجاوز سعة الكومة والتحكم في EIP عبر الكتابة فوق جدول الوظائف الافتراضية (vftable).
تحدث هذه الثغرة في Outlook 2019 (الإصدار 16.0.12624.20424) المثبت على نظام Windows 10 1909 x64 باللغة الكورية (صفحة الترميز 949).

لم أتمكن من تجاوز ASLR باستخدام هذه الثغرة، لكنني كتبت استغلالًا لـ Outlook 2019 32 بت يقوم بفتح الحاسبة باستخدام عنوان مكتبة نظام ثابت عند الإقلاع. وهذه الثغرة هي ثغرة بدون نقرة. إذا كان حساب خدمة بريد غير "outlook.com" مرتبطًا بـ Outlook 2019 باستخدام IMAP، فإنها تُفعَّل بمجرد استلام البريد.
v26 = IsDBCSLeadByte(*v13);
v12 = 0;
if ( v26 )
{
LODWORD(v5) = v5 - 1;
*v11++ = *v13++;
--v8;
}
LODWORD(v5) = v5 - 1;
*v11++ = *v13++;
--v8;
goto LABEL_51;
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
يحدث الانهيار، والجزء الذي توجد فيه الثغرة هو ما سبق. عندما تكون سلسلة الرأس To أو From المُحللة متعددة البايت، يتم إنقاص المتغير الذي يحصي السلاسل المتبقية للتحليل بمقدار 2، ويحدث تجاوز سفلي للعدد الصحيح عند إنقاص المتغير بمقدار 2 عندما يكون المتغير 1.
if ( v23 != '=' )
{
if ( v23 == 34 )
v15 = 6;
goto LABEL_48;
}
v15 = 1;
}
if ( v15 )
goto LABEL_40;
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
LABEL_40:
v24 = v13[v14];
if ( v24 )
{
if ( v5 == v14 )
goto LABEL_52;
if ( v8 != v14 )
{
v25 = IsDBCSLeadByte(v24);
v12 = 0;
if ( v25 )
++v14;
++v14;
}
}
goto LABEL_51;
}
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
LABEL_51:
if ( v5 <= v14 )
goto EXIT_0;
}
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
if ( v15 )
{
if ( v15 == 1 )
{
v15 = 2;
v21 = v13[v14];
v22 = v14 + 1;
if ( v21 != '?' )
v22 = v14;
if ( v21 != '?' )
v15 = v12;
v14 = v22;
}
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
else if ( v15 > 1 )
{
if ( v15 <= 3 )
{
v18 = v13[v14];
v19 = v14 + 1;
if ( v18 != '?' )
v19 = v14;
v14 = v19;
v20 = v15 + 1;
if ( v18 != '?' )
v20 = v15;
v15 = v20;
}
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
if ( v15 == 4 )
{
if ( v13[v14] == '?' )
v15 = 5;
LABEL_40:
v24 = v13[v14];
if ( v24 )
{
if ( v5 == v14 )
goto LABEL_52;
if ( v8 != v14 )
{
v25 = IsDBCSLeadByte(v24);
v12 = 0;
if ( v25 )
++v14;
++v14;
}
}
goto LABEL_51;
}
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
if ( v15 == 5 )
{
v16 = v14;
v17 = v14 + 1;
v30 = v13[v16];
if ( v30 == '=' )
{
*v11++ = 34;
--v8;
}
memcpy(v11, v13, v17);
v13 += v17;
v11 += v17;
LODWORD(v5) = v5 - v17;
v8 -= v17;
if ( v30 == '=' )
{
*v11++ = 34;
--v8;
}
v12 = 0;
v14 = 0;
v15 = 0;
goto LABEL_51;
}
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
else
{
v23 = v13[v14];
if ( v23 != '=' )
{
if ( v23 == '"' )
v15 = 6;
goto LABEL_48;
}
v15 = 1;
}
if ( v15 )
goto LABEL_40;
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
LABEL_48:
v26 = IsDBCSLeadByte(*v13);
v12 = 0;
if ( v26 )
{
LODWORD(v5) = v5 - 1;
*v11++ = *v13++;
--v8;
}
LODWORD(v5) = v5 - 1;
*v11++ = *v13++;
--v8;
goto LABEL_51;
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
ولكن عند تشغيل تجاوز سعة الكومة باستخدام الخلل أعلاه، لم أستطع تلبية شرط الخروج من الحلقة مرة أخرى، لذا لم أتمكن من إجراء الكتابة الجزئية بالقدر المطلوب. لذلك اضطررت للبحث عن مدخل يحقق شرطًا محددًا للخروج من الحلقة. ولنفس السبب أعلاه، قمت بتدقيق صارم لجزء الكود المحيط، وبدلاً من العثور على قيمة المدخل التي يمكنها الهروب من الحلقة، اكتشفت خللًا يؤدي إلى الدخول في حلقة لا نهائية دون زيادة الفهرس أو الكتابة عند معالجة سلسلة خاصة.

بعد تنفيذ heap spray وheap feng shui، تم نجاح اختطاف EIP عن طريق الكتابة فوق جدول الوظائف الافتراضية (vftable) لكائن قيد التشغيل في سلسلة أخرى لم تعلق في الحلقة اللانهائية باستخدام الخلل أعلاه.