From 14fe8d4fd16e993672f08f7ee66dd1dbad7f546f Mon Sep 17 00:00:00 2001 From: mgeeky Date: Sun, 10 Jan 2021 10:11:00 -0800 Subject: [PATCH 1/9] Added support for Office 2007+ formats --- OfficePurge/App.config | 2 +- OfficePurge/OfficePurge.csproj | 18 ++- OfficePurge/Program.cs | 196 ++++++++++++++++++++++----------- OfficePurge/Utils.cs | 23 ++-- 4 files changed, 159 insertions(+), 80 deletions(-) diff --git a/OfficePurge/App.config b/OfficePurge/App.config index 9d2c7ad..bae5d6d 100644 --- a/OfficePurge/App.config +++ b/OfficePurge/App.config @@ -1,6 +1,6 @@ - + diff --git a/OfficePurge/OfficePurge.csproj b/OfficePurge/OfficePurge.csproj index bcfb810..f5aa554 100644 --- a/OfficePurge/OfficePurge.csproj +++ b/OfficePurge/OfficePurge.csproj @@ -1,5 +1,6 @@  + Debug @@ -8,7 +9,7 @@ Exe OfficePurge OfficePurge - v4.7 + v4.6.1 512 true true @@ -38,8 +39,15 @@ false + + ..\packages\Costura.Fody.3.3.3\lib\net40\Costura.dll + + + ..\packages\OpenMcdf.2.2.1.3\lib\net40\OpenMcdf.dll + + @@ -58,4 +66,12 @@ + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/OfficePurge/Program.cs b/OfficePurge/Program.cs index a6470ce..1b212c8 100644 --- a/OfficePurge/Program.cs +++ b/OfficePurge/Program.cs @@ -3,13 +3,13 @@ using System.Linq; using OpenMcdf; using System.IO; +using System.IO.Compression; namespace OfficePurge { class Program { - private static string document = ""; private static string filename = ""; private static string module = ""; private static bool list_modules = false; @@ -30,21 +30,6 @@ static void Main(string[] args) Dictionary argDict = Utils.ParseArgs(args); - if (argDict.ContainsKey("d")) - { - document = argDict["d"]; - if (document != "word" && document != "excel" && document != "publisher") - { - Console.WriteLine("\n[!] Unknown document type. Options are 'word', 'excel', or 'publisher'.\n"); - return; - } - } - else - { - Console.WriteLine("\n[!] Missing document type (-d)\n"); - return; - } - if (argDict.ContainsKey("f")) { filename = argDict["f"]; @@ -65,48 +50,75 @@ static void Main(string[] args) { module = argDict["m"]; } - else - { - Console.WriteLine("\n[!] Missing module (-m)\n"); - return; - } + else + { + Console.WriteLine("\n[.] Will automatically decide which modules to purge."); + } } - // VBA Purging - try + bool is_OpenXML = false; + + // Temp path to unzip OpenXML files to + String unzipTempPath = ""; + + string outFilename = Utils.getOutFilename(filename); + string oleFilename = outFilename; + + // VBA Purging + try { // Make a copy of document to VBA Purge if user is not listing modules if (!list_modules) { - string outFilename = Utils.getOutFilename(filename); - string oleFilename = outFilename; - if (File.Exists(outFilename)) File.Delete(outFilename); File.Copy(filename, outFilename); filename = outFilename; } + + try + { + unzipTempPath = CreateUniqueTempDirectory(); + ZipFile.ExtractToDirectory(filename, unzipTempPath); + + if (File.Exists(Path.Combine(unzipTempPath, "word", "vbaProject.bin"))) + { + oleFilename = Path.Combine(unzipTempPath, "word", "vbaProject.bin"); + } + else if (File.Exists(Path.Combine(unzipTempPath, "xl", "vbaProject.bin"))) + { + oleFilename = Path.Combine(unzipTempPath, "xl", "vbaProject.bin"); + } + + is_OpenXML = true; + } + catch (Exception) + { + // Not OpenXML format, Maybe 97-2003 format, Make a copy + if (File.Exists(outFilename)) File.Delete(outFilename); + File.Copy(filename, outFilename); + } - CompoundFile cf = new CompoundFile(filename, CFSUpdateMode.Update, 0); + CompoundFile cf = new CompoundFile(oleFilename, CFSUpdateMode.Update, 0); CFStorage commonStorage = cf.RootStorage; - if (document == "word") + if (cf.RootStorage.TryGetStorage("Macros") != null) { commonStorage = cf.RootStorage.GetStorage("Macros"); } - - else if (document == "excel") + + if (cf.RootStorage.TryGetStorage("_VBA_PROJECT_CUR") != null) { commonStorage = cf.RootStorage.GetStorage("_VBA_PROJECT_CUR"); } - else if (document == "publisher") - { - commonStorage = cf.RootStorage.GetStorage("VBA"); + var vbaStorage = commonStorage.GetStorage("VBA"); + if(vbaStorage == null) + { + throw new CFItemNotFound("Cannot find item"); } - // Grab data from "dir" module stream. Used to retrieve list of module streams in document. - byte[] dirStream = Utils.Decompress(commonStorage.GetStorage("VBA").GetStream("dir").GetData()); + byte[] dirStream = Utils.Decompress(vbaStorage.GetStream("dir").GetData()); List vbaModules = Utils.ParseModulesFromDirStream(dirStream); // Only list module streams in document and return @@ -114,94 +126,138 @@ static void Main(string[] args) { foreach (var vbaModule in vbaModules) { - Console.WriteLine("\n[*] VBA module name: " + vbaModule.moduleName); + Console.WriteLine("[*] VBA module name: " + vbaModule.moduleName); } - Console.WriteLine("\n[*] Finished listing modules\n"); - return; + Console.WriteLine("[*] Finished listing modules\n"); + + return; } + string [] dontPurgeTheseModules = { + "ThisDocument", + "ThisWorkbook", + "Sheet", + }; byte[] streamBytes; bool module_found = false; foreach (var vbaModule in vbaModules) { //VBA Purging begins - if (vbaModule.moduleName == module) + bool purge = true; + + if (module.Length > 0) + { + purge = vbaModule.moduleName == module; + } + else { - Console.WriteLine("\n[*] VBA module name: " + vbaModule.moduleName); - Console.WriteLine("\n[*] Offset for code: " + vbaModule.textOffset); - Console.WriteLine("\n[*] Now purging VBA code in module: " + vbaModule.moduleName); + foreach (string mod in dontPurgeTheseModules) + { + if (vbaModule.moduleName.StartsWith(mod)) purge = false; + } + } + + if (purge) + { + Console.WriteLine("\n[*] Purging VBA code in module: " + vbaModule.moduleName); + Console.WriteLine("[*] Offset for code: " + vbaModule.textOffset); // Get the CompressedSourceCode from module - streamBytes = commonStorage.GetStorage("VBA").GetStream(vbaModule.moduleName).GetData(); + streamBytes = vbaStorage.GetStream(vbaModule.moduleName).GetData(); string OG_VBACode = Utils.GetVBATextFromModuleStream(streamBytes, vbaModule.textOffset); // Remove P-code from module stream and set the module to only have the CompressedSourceCode streamBytes = Utils.RemovePcodeInModuleStream(streamBytes, vbaModule.textOffset, OG_VBACode); - commonStorage.GetStorage("VBA").GetStream(vbaModule.moduleName).SetData(streamBytes); + vbaStorage.GetStream(vbaModule.moduleName).SetData(streamBytes); module_found = true; - } } if (module_found == false) { Console.WriteLine("\n[!] Could not find module in document (-m). List all module streams with (-l).\n"); - cf.Commit(); - cf.Close(); - CompoundFile.ShrinkCompoundFile(filename); - File.Delete(filename); - return; - } + if (!is_OpenXML) + { + cf.Commit(); + cf.Close(); + CompoundFile.ShrinkCompoundFile(oleFilename); + File.Delete(oleFilename); + if (File.Exists(outFilename)) File.Delete(outFilename); + } + + return; + } // Change offset to 0 so that document can find compressed source code. - commonStorage.GetStorage("VBA").GetStream("dir").SetData(Utils.Compress(Utils.ChangeOffset(dirStream))); + vbaStorage.GetStream("dir").SetData(Utils.Compress(Utils.ChangeOffset(dirStream))); Console.WriteLine("\n[*] Module offset changed to 0."); // Remove performance cache in _VBA_PROJECT stream. Replace the entire stream with _VBA_PROJECT header. byte[] data = Utils.HexToByte("CC-61-FF-FF-00-00-00"); - commonStorage.GetStorage("VBA").GetStream("_VBA_PROJECT").SetData(data); - Console.WriteLine("\n[*] PerformanceCache removed from _VBA_PROJECT stream."); + vbaStorage.GetStream("_VBA_PROJECT").SetData(data); + Console.WriteLine("[*] PerformanceCache removed from _VBA_PROJECT stream."); // Check if document contains SRPs. Must be removed for VBA Purging to work. try { - commonStorage.GetStorage("VBA").Delete("__SRP_0"); - commonStorage.GetStorage("VBA").Delete("__SRP_1"); - commonStorage.GetStorage("VBA").Delete("__SRP_2"); - commonStorage.GetStorage("VBA").Delete("__SRP_3"); - Console.WriteLine("\n[*] SRP streams deleted!"); + for(int i = 0; i < 10; i++) + { + string srp = String.Format("__SRP_{1}", i); + var str = vbaStorage.TryGetStream(srp); + if (str != null) + { + vbaStorage.Delete(srp); + } + } + + Console.WriteLine("[*] SRP streams deleted!"); } catch (Exception) { - Console.WriteLine("\n[*] No SRP streams found."); + Console.WriteLine("[*] No SRP streams found."); } // Commit changes and close cf.Commit(); cf.Close(); - CompoundFile.ShrinkCompoundFile(filename); - Console.WriteLine("\n[*] VBA Purging completed successfully!\n"); + CompoundFile.ShrinkCompoundFile(oleFilename); + + // Zip the file back up as a docm or xlsm + if (is_OpenXML) + { + if (File.Exists(outFilename)) File.Delete(outFilename); + ZipFile.CreateFromDirectory(unzipTempPath, outFilename); + } + + Console.WriteLine("[+] VBA Purging completed successfully!\n"); } // Error handle for file not found catch (FileNotFoundException ex) when (ex.Message.Contains("Could not find file")) { - Console.WriteLine("\n[!] Could not find path or file (-f). \n"); + Console.WriteLine("[!] Could not find path or file (-f). \n"); } // Error handle when document specified and file chosen don't match catch (CFItemNotFound ex) when (ex.Message.Contains("Cannot find item")) { - Console.WriteLine("\n[!] File (-f) does not match document type selected (-d).\n"); + Console.WriteLine("[!] File (-f) does not contain macros.\n"); } // Error handle when document is not OLE/CFBF format catch (CFFileFormatException) { - Console.WriteLine("\n[!] Incorrect filetype (-f). Must be an OLE strucutred file. OfficePurge supports .doc, .xls, or .pub documents.\n"); + Console.WriteLine("[!] Incorrect filetype (-f). OfficePurge supports documents in .docm or .xlsm format as well as .doc/.xls/.pub in the Office 97-2003 format.\n"); } + finally + { + if (is_OpenXML) + { + Directory.Delete(unzipTempPath, true); + } + } } // Error handle for incorrect use of flags catch (IndexOutOfRangeException) @@ -209,5 +265,13 @@ static void Main(string[] args) Console.WriteLine("\n[!] Flags (-d), (-f), (-m) need an argument. Make sure you have provided these flags an argument.\n"); } } - } + + public static string CreateUniqueTempDirectory() + { + var uniqueTempDir = Path.GetFullPath(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())); + Directory.CreateDirectory(uniqueTempDir); + return uniqueTempDir; + } + + } } diff --git a/OfficePurge/Utils.cs b/OfficePurge/Utils.cs index b0894ee..98dcc8d 100644 --- a/OfficePurge/Utils.cs +++ b/OfficePurge/Utils.cs @@ -34,23 +34,22 @@ public static void HelpMenu() Console.WriteLine(" / \\( __)( __)( )/ __)( __)( _ \\/ )( \\( _ \\ / __)( __)"); Console.WriteLine("( O )) _) ) _) )(( (__ ) _) ) __/) \\/ ( ) /( (_ \\ ) _) "); Console.WriteLine(" \\__/(__) (__) (__)\\___)(____)(__) \\____/(__\\_) \\___/(____) v1.0"); - Console.WriteLine("\n\n Author: Andrew Oliveau\n\n"); + Console.WriteLine("\n\n Author: Andrew Oliveau\n"); Console.WriteLine(" DESCRIPTION:"); Console.WriteLine("\n\tOfficePurge is a C# tool that VBA purges malicious Office documents. "); - Console.WriteLine("\n\tVBA purging removes P-code from module streams within Office documents. "); - Console.WriteLine("\n\tDocuments that only contain source code and no compiled code are more"); - Console.WriteLine("\n\tlikely to evade AV detection and YARA rules.\n\n\t\n"); + Console.WriteLine("\tVBA purging removes P-code from module streams within Office documents. "); + Console.WriteLine("\tDocuments that only contain source code and no compiled code are more"); + Console.WriteLine("\tlikely to evade AV detection and YARA rules.\n\n"); Console.WriteLine(" USAGE:"); - Console.WriteLine("\n\t-d : Document type (word, excel, publisher)"); - Console.WriteLine("\n\t-f : Filename to VBA Purge"); - Console.WriteLine("\n\t-m : Module within document to VBA Purge"); - Console.WriteLine("\n\t-l : List module streams in document"); - Console.WriteLine("\n\t-h : Show help menu.\n\n"); + Console.WriteLine("\t-f : Filename to VBA Purge"); + Console.WriteLine("\t-m : Module within document to VBA Purge"); + Console.WriteLine("\t-l : List module streams in document"); + Console.WriteLine("\t-h : Show help menu.\n"); Console.WriteLine(" EXAMPLES:"); Console.WriteLine("\n\t .\\OfficePurge.exe -d word -f .\\malicious.doc -m NewMacros"); - Console.WriteLine("\n\t .\\OfficePurge.exe -d excel -f .\\payroll.xls -m Module1"); - Console.WriteLine("\n\t .\\OfficePurge.exe -d publisher -f .\\donuts.pub -m ThisDocument"); - Console.WriteLine("\n\t .\\OfficePurge.exe -d word -f .\\malicious.doc -l\n\n"); + Console.WriteLine("\t .\\OfficePurge.exe -d excel -f .\\payroll.xls -m Module1"); + Console.WriteLine("\t .\\OfficePurge.exe -d publisher -f .\\donuts.pub -m ThisDocument"); + Console.WriteLine("\t .\\OfficePurge.exe -d word -f .\\malicious.doc -l\n"); } public static List ParseModulesFromDirStream(byte[] dirStream) { From 6e46da0f07e5c2799e829a686ff8605afbc99388 Mon Sep 17 00:00:00 2001 From: mgeeky Date: Sun, 10 Jan 2021 10:39:22 -0800 Subject: [PATCH 2/9] oops, I'm breaking FireEye's FEYE_OLE_VBAPurged_2 YARA rule :( --- OfficePurge/Program.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/OfficePurge/Program.cs b/OfficePurge/Program.cs index 1b212c8..9f14210 100644 --- a/OfficePurge/Program.cs +++ b/OfficePurge/Program.cs @@ -195,7 +195,15 @@ static void Main(string[] args) Console.WriteLine("\n[*] Module offset changed to 0."); // Remove performance cache in _VBA_PROJECT stream. Replace the entire stream with _VBA_PROJECT header. - byte[] data = Utils.HexToByte("CC-61-FF-FF-00-00-00"); + + string b1 = "00"; + string b2 = "00"; + + Random rnd = new Random(); + b1 = String.Format("{0:X2}", rnd.Next(0, 255)); + b2 = String.Format("{0:X2}", rnd.Next(0, 255)); + + byte[] data = Utils.HexToByte(String.Format("CC-61-FF-FF-00-{0}-{1}", b1, b2)); vbaStorage.GetStream("_VBA_PROJECT").SetData(data); Console.WriteLine("[*] PerformanceCache removed from _VBA_PROJECT stream."); From 8fb155692cf5350abf52e3d0c3c3c5ab7302ba3e Mon Sep 17 00:00:00 2001 From: mgeeky Date: Sun, 10 Jan 2021 13:13:04 -0800 Subject: [PATCH 3/9] Oh you little pesky bug, get off my code --- OfficePurge/Program.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OfficePurge/Program.cs b/OfficePurge/Program.cs index 9f14210..224a07e 100644 --- a/OfficePurge/Program.cs +++ b/OfficePurge/Program.cs @@ -199,7 +199,7 @@ static void Main(string[] args) string b1 = "00"; string b2 = "00"; - Random rnd = new Random(); + Random rnd = new Random(); b1 = String.Format("{0:X2}", rnd.Next(0, 255)); b2 = String.Format("{0:X2}", rnd.Next(0, 255)); @@ -212,7 +212,7 @@ static void Main(string[] args) { for(int i = 0; i < 10; i++) { - string srp = String.Format("__SRP_{1}", i); + string srp = String.Format("__SRP_{0}", i); var str = vbaStorage.TryGetStream(srp); if (str != null) { @@ -222,7 +222,7 @@ static void Main(string[] args) Console.WriteLine("[*] SRP streams deleted!"); } - catch (Exception) + catch (Exception e) { Console.WriteLine("[*] No SRP streams found."); } From 148f7e84c2bcd521cf90de03c9f972710088db03 Mon Sep 17 00:00:00 2001 From: "Mariusz B. / mgeeky" Date: Thu, 19 May 2022 21:57:34 +0200 Subject: [PATCH 4/9] packages --- OfficePurge/OfficePurge.csproj.user | 6 ++++++ OfficePurge/packages.config | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 OfficePurge/OfficePurge.csproj.user create mode 100644 OfficePurge/packages.config diff --git a/OfficePurge/OfficePurge.csproj.user b/OfficePurge/OfficePurge.csproj.user new file mode 100644 index 0000000..4d10d3b --- /dev/null +++ b/OfficePurge/OfficePurge.csproj.user @@ -0,0 +1,6 @@ + + + + -d publisher -f .\output-files\test2.pub -l + + \ No newline at end of file diff --git a/OfficePurge/packages.config b/OfficePurge/packages.config new file mode 100644 index 0000000..1bc0cfa --- /dev/null +++ b/OfficePurge/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From c4bb347a602e70a3a925e98c307efa8da48fae08 Mon Sep 17 00:00:00 2001 From: "Mariusz B. / mgeeky" Date: Thu, 19 May 2022 21:58:18 +0200 Subject: [PATCH 5/9] fix --- OfficePurge/OfficePurge.csproj.user | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 OfficePurge/OfficePurge.csproj.user diff --git a/OfficePurge/OfficePurge.csproj.user b/OfficePurge/OfficePurge.csproj.user deleted file mode 100644 index 4d10d3b..0000000 --- a/OfficePurge/OfficePurge.csproj.user +++ /dev/null @@ -1,6 +0,0 @@ - - - - -d publisher -f .\output-files\test2.pub -l - - \ No newline at end of file From 5c60c2c7b01ada3dc7c22e82d98211e703e4fe8a Mon Sep 17 00:00:00 2001 From: "Mariusz B. / mgeeky" Date: Thu, 19 May 2022 22:19:14 +0200 Subject: [PATCH 6/9] Added support for Publisher VBA stream extraction. --- OfficePurge/Program.cs | 45 +++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/OfficePurge/Program.cs b/OfficePurge/Program.cs index 224a07e..8bf2794 100644 --- a/OfficePurge/Program.cs +++ b/OfficePurge/Program.cs @@ -20,6 +20,8 @@ public static void PrintHelp() } static void Main(string[] args) { + string outFilename = ""; + try { if (args.Length == 0 || args.Contains("-h")) @@ -32,7 +34,7 @@ static void Main(string[] args) if (argDict.ContainsKey("f")) { - filename = argDict["f"]; + filename = Path.GetFullPath(argDict["f"]); } else { @@ -61,7 +63,7 @@ static void Main(string[] args) // Temp path to unzip OpenXML files to String unzipTempPath = ""; - string outFilename = Utils.getOutFilename(filename); + outFilename = Utils.getOutFilename(filename); string oleFilename = outFilename; // VBA Purging @@ -74,6 +76,10 @@ static void Main(string[] args) File.Copy(filename, outFilename); filename = outFilename; } + else + { + outFilename = outFilename.Replace("_PURGED", ""); + } try { @@ -91,14 +97,23 @@ static void Main(string[] args) is_OpenXML = true; } - catch (Exception) + catch (Exception e) { - // Not OpenXML format, Maybe 97-2003 format, Make a copy - if (File.Exists(outFilename)) File.Delete(outFilename); - File.Copy(filename, outFilename); - } + Console.WriteLine("Input file seems to be a 97-2003 Office document (OLE)"); + + if (!list_modules) + { + // Not OpenXML format, Maybe 97-2003 format, Make a copy + if (File.Exists(outFilename)) File.Delete(outFilename); + File.Copy(filename, outFilename); + } + + oleFilename = outFilename; + } + + var mode = (list_modules) ? CFSUpdateMode.ReadOnly : CFSUpdateMode.Update; - CompoundFile cf = new CompoundFile(oleFilename, CFSUpdateMode.Update, 0); + CompoundFile cf = new CompoundFile(oleFilename, mode, 0); CFStorage commonStorage = cf.RootStorage; if (cf.RootStorage.TryGetStorage("Macros") != null) @@ -106,12 +121,18 @@ static void Main(string[] args) commonStorage = cf.RootStorage.GetStorage("Macros"); } - if (cf.RootStorage.TryGetStorage("_VBA_PROJECT_CUR") != null) + else if (cf.RootStorage.TryGetStorage("_VBA_PROJECT_CUR") != null) { commonStorage = cf.RootStorage.GetStorage("_VBA_PROJECT_CUR"); - } + } - var vbaStorage = commonStorage.GetStorage("VBA"); + else if (cf.RootStorage.TryGetStorage("VBA") != null) + { + // Publisher + commonStorage = cf.RootStorage.GetStorage("VBA"); + } + + var vbaStorage = commonStorage.GetStorage("VBA"); if(vbaStorage == null) { throw new CFItemNotFound("Cannot find item"); @@ -272,7 +293,7 @@ static void Main(string[] args) { Console.WriteLine("\n[!] Flags (-d), (-f), (-m) need an argument. Make sure you have provided these flags an argument.\n"); } - } + } public static string CreateUniqueTempDirectory() { From 3d0e5709398909bea9776ff506c7b8bff7dede99 Mon Sep 17 00:00:00 2001 From: "Mariusz B. / mgeeky" Date: Thu, 19 May 2022 22:52:35 +0200 Subject: [PATCH 7/9] bugfix --- OfficePurge/Program.cs | 46 +++++++++++++++++++++++++++++++++--------- OfficePurge/Utils.cs | 16 ++++++++++----- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/OfficePurge/Program.cs b/OfficePurge/Program.cs index 8bf2794..45fe0f6 100644 --- a/OfficePurge/Program.cs +++ b/OfficePurge/Program.cs @@ -74,7 +74,6 @@ static void Main(string[] args) { if (File.Exists(outFilename)) File.Delete(outFilename); File.Copy(filename, outFilename); - filename = outFilename; } else { @@ -92,8 +91,12 @@ static void Main(string[] args) } else if (File.Exists(Path.Combine(unzipTempPath, "xl", "vbaProject.bin"))) { - oleFilename = Path.Combine(unzipTempPath, "xl", "vbaProject.bin"); - } + oleFilename = Path.Combine(unzipTempPath, "xl", "vbaProject.bin"); + } + else if (File.Exists(Path.Combine(unzipTempPath, "ppt", "vbaProject.bin"))) + { + oleFilename = Path.Combine(unzipTempPath, "ppt", "vbaProject.bin"); + } is_OpenXML = true; } @@ -107,8 +110,10 @@ static void Main(string[] args) if (File.Exists(outFilename)) File.Delete(outFilename); File.Copy(filename, outFilename); } - - oleFilename = outFilename; + else + { + oleFilename = outFilename; + } } var mode = (list_modules) ? CFSUpdateMode.ReadOnly : CFSUpdateMode.Update; @@ -128,8 +133,18 @@ static void Main(string[] args) else if (cf.RootStorage.TryGetStorage("VBA") != null) { - // Publisher - commonStorage = cf.RootStorage.GetStorage("VBA"); + // Publisher: VBA -> VBA + var interimStorage = cf.RootStorage.GetStorage("VBA"); + + try + { + var foo = interimStorage.GetStream("dir"); + } + catch (CFItemNotFound ex) when (ex.Message.Contains("Cannot find item")) + { + Console.WriteLine("[.] Publisher identified."); + commonStorage = interimStorage; + } } var vbaStorage = commonStorage.GetStorage("VBA"); @@ -175,7 +190,10 @@ static void Main(string[] args) { foreach (string mod in dontPurgeTheseModules) { - if (vbaModule.moduleName.StartsWith(mod)) purge = false; + if (vbaModule.moduleName.StartsWith(mod)) + { + purge = false; + } } } @@ -193,6 +211,11 @@ static void Main(string[] args) vbaStorage.GetStream(vbaModule.moduleName).SetData(streamBytes); module_found = true; } + + if (module_found && module.Length > 0) + { + break; + } } if (module_found == false) @@ -284,7 +307,12 @@ static void Main(string[] args) { if (is_OpenXML) { - Directory.Delete(unzipTempPath, true); + try + { + Directory.Delete(unzipTempPath, true); + } + catch (Exception) + { } } } } diff --git a/OfficePurge/Utils.cs b/OfficePurge/Utils.cs index 98dcc8d..0b72a74 100644 --- a/OfficePurge/Utils.cs +++ b/OfficePurge/Utils.cs @@ -34,22 +34,28 @@ public static void HelpMenu() Console.WriteLine(" / \\( __)( __)( )/ __)( __)( _ \\/ )( \\( _ \\ / __)( __)"); Console.WriteLine("( O )) _) ) _) )(( (__ ) _) ) __/) \\/ ( ) /( (_ \\ ) _) "); Console.WriteLine(" \\__/(__) (__) (__)\\___)(____)(__) \\____/(__\\_) \\___/(____) v1.0"); - Console.WriteLine("\n\n Author: Andrew Oliveau\n"); + Console.WriteLine("\n\n Author: Andrew Oliveau, tweaked by Mariusz Banach (mgeeky)\n"); Console.WriteLine(" DESCRIPTION:"); Console.WriteLine("\n\tOfficePurge is a C# tool that VBA purges malicious Office documents. "); Console.WriteLine("\tVBA purging removes P-code from module streams within Office documents. "); Console.WriteLine("\tDocuments that only contain source code and no compiled code are more"); Console.WriteLine("\tlikely to evade AV detection and YARA rules.\n\n"); + Console.WriteLine(" SUPPORTED:"); + Console.WriteLine("\t- Word (pre-2007, 2007+)"); + Console.WriteLine("\t- Excel (pre-2007, 2007+)"); + Console.WriteLine("\t- Powerpoint (2007+)"); + Console.WriteLine("\t- Publisher (pre-2007)"); Console.WriteLine(" USAGE:"); Console.WriteLine("\t-f : Filename to VBA Purge"); Console.WriteLine("\t-m : Module within document to VBA Purge"); Console.WriteLine("\t-l : List module streams in document"); Console.WriteLine("\t-h : Show help menu.\n"); Console.WriteLine(" EXAMPLES:"); - Console.WriteLine("\n\t .\\OfficePurge.exe -d word -f .\\malicious.doc -m NewMacros"); - Console.WriteLine("\t .\\OfficePurge.exe -d excel -f .\\payroll.xls -m Module1"); - Console.WriteLine("\t .\\OfficePurge.exe -d publisher -f .\\donuts.pub -m ThisDocument"); - Console.WriteLine("\t .\\OfficePurge.exe -d word -f .\\malicious.doc -l\n"); + Console.WriteLine("\n\t .\\OfficePurge.exe -f .\\malicious.doc -m NewMacros"); + Console.WriteLine("\t .\\OfficePurge.exe -f .\\payroll.xls -m Module1"); + Console.WriteLine("\t .\\OfficePurge.exe -f .\\payroll.pptm -m Module1"); + Console.WriteLine("\t .\\OfficePurge.exe -f .\\donuts.pub -m ThisDocument"); + Console.WriteLine("\t .\\OfficePurge.exe -f .\\malicious.doc -l\n"); } public static List ParseModulesFromDirStream(byte[] dirStream) { From 221d5c4a1b54fdde683383e1739186e8cac3b917 Mon Sep 17 00:00:00 2001 From: "Mariusz B. / mgeeky" Date: Thu, 19 May 2022 22:54:46 +0200 Subject: [PATCH 8/9] readme --- README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cb830fb..e3f69fe 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,21 @@ # OfficePurge +[(This repository is my fork of mandiant/OfficePurge)](https://github.com/mandiant/OfficePurge) + VBA purge your Office documents with OfficePurge. VBA purging removes P-code from module streams within Office documents. Documents that only contain source code and no compiled code are more likely to evade AV detection and YARA rules. Read more here. OfficePurge supports VBA purging Microsoft Office Word (.doc), Excel (.xls), and Publisher (.pub) documents. Original and purged documents for each supported file type with a macro that will spawn calc.exe can be found in `sample-data` folder. Author: Andrew Oliveau (@AndrewOliveau) +Tweaked by: Mariusz Banach / mgeeky # INSTALLATION/BUILDING ## Pre-Compiled -* Use the pre-compiled binary in the Releases section +* Use the pre-compiled binary in the Releases section ## Building Yourself @@ -42,7 +45,6 @@ The below 3rd party libraries are used in this project. # ARGUMENTS/OPTIONS -* -d - Document type to VBA purge (word, excel, publisher) * -f - Document filename to VBA purge * -m - Module within document to VBA purge (ex. Module1) * -l - List modules in a document @@ -50,10 +52,49 @@ The below 3rd party libraries are used in this project. # EXAMPLES -* `OfficePurge.exe -d word -f .\malicious.doc -m NewMacros` -* `OfficePurge.exe -d excel -f .\payroll.xls -m Module1` -* `OfficePurge.exe -d publisher -f .\donuts.pub -m ThisDocument` -* `OfficePurge.exe -d word -f .\malicious.doc -l` +* `OfficePurge.exe -f .\malicious.doc -m NewMacros` +* `OfficePurge.exe -f .\payroll.xls -m Module1` +* `OfficePurge.exe -f .\donuts.pub -m ThisDocument` +* `OfficePurge.exe -f .\donuts.pptm` +* `OfficePurge.exe -f .\malicious.doc -l` + +# Full Usage + +``` + __ ____ ____ __ ___ ____ ____ _ _ ____ ___ ____ + / \( __)( __)( )/ __)( __)( _ \/ )( \( _ \ / __)( __) +( O )) _) ) _) )(( (__ ) _) ) __/) \/ ( ) /( (_ \ ) _) + \__/(__) (__) (__)\___)(____)(__) \____/(__\_) \___/(____) v1.0 + + + Author: Andrew Oliveau, tweaked by Mariusz Banach (mgeeky) + + DESCRIPTION: + + OfficePurge is a C# tool that VBA purges malicious Office documents. + VBA purging removes P-code from module streams within Office documents. + Documents that only contain source code and no compiled code are more + likely to evade AV detection and YARA rules. + + SUPPORTED: + - Word (pre-2007, 2007+) + - Excel (pre-2007, 2007+) + - Powerpoint (2007+) + - Publisher (pre-2007) + + USAGE: + -f : Filename to VBA Purge + -m : Module within document to VBA Purge + -l : List module streams in document + -h : Show help menu. + + EXAMPLES: + .\OfficePurge.exe -f .\malicious.doc -m NewMacros + .\OfficePurge.exe -f .\payroll.xls -m Module1 + .\OfficePurge.exe -f .\payroll.pptm -m Module1 + .\OfficePurge.exe -f .\donuts.pub -m ThisDocument + .\OfficePurge.exe -f .\malicious.doc -l +``` # REFERENCES * Didier Steven's VBA purging article here From 6066fd47e60e9c2ff787f5da60bdec6bd12aabc2 Mon Sep 17 00:00:00 2001 From: "Mariusz B. / mgeeky" Date: Thu, 19 May 2022 23:18:44 +0200 Subject: [PATCH 9/9] indent --- OfficePurge/Program.cs | 484 ++++++++++++++++++++--------------------- OfficePurge/Utils.cs | 376 ++++++++++++++++---------------- 2 files changed, 430 insertions(+), 430 deletions(-) diff --git a/OfficePurge/Program.cs b/OfficePurge/Program.cs index 45fe0f6..744044a 100644 --- a/OfficePurge/Program.cs +++ b/OfficePurge/Program.cs @@ -8,55 +8,55 @@ namespace OfficePurge { - class Program - { - private static string filename = ""; - private static string module = ""; - private static bool list_modules = false; - - public static void PrintHelp() - { - Utils.HelpMenu(); - } - static void Main(string[] args) - { - string outFilename = ""; - - try - { - if (args.Length == 0 || args.Contains("-h")) - { - PrintHelp(); - return; - } - - Dictionary argDict = Utils.ParseArgs(args); - - if (argDict.ContainsKey("f")) - { - filename = Path.GetFullPath(argDict["f"]); - } - else - { - Console.WriteLine("\n[!] Missing file (-f)\n"); - return; - } - - if (args.Contains("-l")) - { - list_modules = true; - } - else - { - if (argDict.ContainsKey("m")) - { - module = argDict["m"]; - } + class Program + { + private static string filename = ""; + private static string module = ""; + private static bool list_modules = false; + + public static void PrintHelp() + { + Utils.HelpMenu(); + } + static void Main(string[] args) + { + string outFilename = ""; + + try + { + if (args.Length == 0 || args.Contains("-h")) + { + PrintHelp(); + return; + } + + Dictionary argDict = Utils.ParseArgs(args); + + if (argDict.ContainsKey("f")) + { + filename = Path.GetFullPath(argDict["f"]); + } + else + { + Console.WriteLine("\n[!] Missing file (-f)\n"); + return; + } + + if (args.Contains("-l")) + { + list_modules = true; + } + else + { + if (argDict.ContainsKey("m")) + { + module = argDict["m"]; + } else { - Console.WriteLine("\n[.] Will automatically decide which modules to purge."); + Console.WriteLine("\n[.] Will automatically decide which modules to purge."); } - } + } bool is_OpenXML = false; @@ -68,30 +68,30 @@ static void Main(string[] args) // VBA Purging try - { - // Make a copy of document to VBA Purge if user is not listing modules - if (!list_modules) - { - if (File.Exists(outFilename)) File.Delete(outFilename); - File.Copy(filename, outFilename); - } - else + { + // Make a copy of document to VBA Purge if user is not listing modules + if (!list_modules) + { + if (File.Exists(outFilename)) File.Delete(outFilename); + File.Copy(filename, outFilename); + } + else { - outFilename = outFilename.Replace("_PURGED", ""); - } - + outFilename = outFilename.Replace("_PURGED", ""); + } + try { unzipTempPath = CreateUniqueTempDirectory(); ZipFile.ExtractToDirectory(filename, unzipTempPath); if (File.Exists(Path.Combine(unzipTempPath, "word", "vbaProject.bin"))) - { - oleFilename = Path.Combine(unzipTempPath, "word", "vbaProject.bin"); - } + { + oleFilename = Path.Combine(unzipTempPath, "word", "vbaProject.bin"); + } else if (File.Exists(Path.Combine(unzipTempPath, "xl", "vbaProject.bin"))) - { - oleFilename = Path.Combine(unzipTempPath, "xl", "vbaProject.bin"); + { + oleFilename = Path.Combine(unzipTempPath, "xl", "vbaProject.bin"); } else if (File.Exists(Path.Combine(unzipTempPath, "ppt", "vbaProject.bin"))) { @@ -102,225 +102,225 @@ static void Main(string[] args) } catch (Exception e) { - Console.WriteLine("Input file seems to be a 97-2003 Office document (OLE)"); - - if (!list_modules) - { - // Not OpenXML format, Maybe 97-2003 format, Make a copy - if (File.Exists(outFilename)) File.Delete(outFilename); - File.Copy(filename, outFilename); - } - else - { - oleFilename = outFilename; - } + Console.WriteLine("Input file seems to be a 97-2003 Office document (OLE)"); + + if (!list_modules) + { + // Not OpenXML format, Maybe 97-2003 format, Make a copy + if (File.Exists(outFilename)) File.Delete(outFilename); + File.Copy(filename, outFilename); + } + else + { + oleFilename = outFilename; + } } - var mode = (list_modules) ? CFSUpdateMode.ReadOnly : CFSUpdateMode.Update; + var mode = (list_modules) ? CFSUpdateMode.ReadOnly : CFSUpdateMode.Update; - CompoundFile cf = new CompoundFile(oleFilename, mode, 0); - CFStorage commonStorage = cf.RootStorage; + CompoundFile cf = new CompoundFile(oleFilename, mode, 0); + CFStorage commonStorage = cf.RootStorage; - if (cf.RootStorage.TryGetStorage("Macros") != null) - { - commonStorage = cf.RootStorage.GetStorage("Macros"); - } - - else if (cf.RootStorage.TryGetStorage("_VBA_PROJECT_CUR") != null) - { - commonStorage = cf.RootStorage.GetStorage("_VBA_PROJECT_CUR"); + if (cf.RootStorage.TryGetStorage("Macros") != null) + { + commonStorage = cf.RootStorage.GetStorage("Macros"); + } + + else if (cf.RootStorage.TryGetStorage("_VBA_PROJECT_CUR") != null) + { + commonStorage = cf.RootStorage.GetStorage("_VBA_PROJECT_CUR"); } - else if (cf.RootStorage.TryGetStorage("VBA") != null) + else if (cf.RootStorage.TryGetStorage("VBA") != null) { - // Publisher: VBA -> VBA - var interimStorage = cf.RootStorage.GetStorage("VBA"); + // Publisher: VBA -> VBA + var interimStorage = cf.RootStorage.GetStorage("VBA"); - try - { - var foo = interimStorage.GetStream("dir"); + try + { + var foo = interimStorage.GetStream("dir"); } catch (CFItemNotFound ex) when (ex.Message.Contains("Cannot find item")) { - Console.WriteLine("[.] Publisher identified."); - commonStorage = interimStorage; - } + Console.WriteLine("[.] Publisher identified."); + commonStorage = interimStorage; + } } var vbaStorage = commonStorage.GetStorage("VBA"); - if(vbaStorage == null) + if(vbaStorage == null) { - throw new CFItemNotFound("Cannot find item"); - } - - // Grab data from "dir" module stream. Used to retrieve list of module streams in document. - byte[] dirStream = Utils.Decompress(vbaStorage.GetStream("dir").GetData()); - List vbaModules = Utils.ParseModulesFromDirStream(dirStream); - - // Only list module streams in document and return - if (list_modules) - { - foreach (var vbaModule in vbaModules) - { - Console.WriteLine("[*] VBA module name: " + vbaModule.moduleName); - } - Console.WriteLine("[*] Finished listing modules\n"); + throw new CFItemNotFound("Cannot find item"); + } + + // Grab data from "dir" module stream. Used to retrieve list of module streams in document. + byte[] dirStream = Utils.Decompress(vbaStorage.GetStream("dir").GetData()); + List vbaModules = Utils.ParseModulesFromDirStream(dirStream); + + // Only list module streams in document and return + if (list_modules) + { + foreach (var vbaModule in vbaModules) + { + Console.WriteLine("[*] VBA module name: " + vbaModule.moduleName); + } + Console.WriteLine("[*] Finished listing modules\n"); return; - } - - string [] dontPurgeTheseModules = { - "ThisDocument", - "ThisWorkbook", - "Sheet", - }; - - byte[] streamBytes; - bool module_found = false; - foreach (var vbaModule in vbaModules) - { - //VBA Purging begins - bool purge = true; - - if (module.Length > 0) - { - purge = vbaModule.moduleName == module; - } - else - { - foreach (string mod in dontPurgeTheseModules) - { - if (vbaModule.moduleName.StartsWith(mod)) - { - purge = false; - } - } - } - - if (purge) - { - Console.WriteLine("\n[*] Purging VBA code in module: " + vbaModule.moduleName); - Console.WriteLine("[*] Offset for code: " + vbaModule.textOffset); - - // Get the CompressedSourceCode from module - streamBytes = vbaStorage.GetStream(vbaModule.moduleName).GetData(); - string OG_VBACode = Utils.GetVBATextFromModuleStream(streamBytes, vbaModule.textOffset); - - // Remove P-code from module stream and set the module to only have the CompressedSourceCode - streamBytes = Utils.RemovePcodeInModuleStream(streamBytes, vbaModule.textOffset, OG_VBACode); - vbaStorage.GetStream(vbaModule.moduleName).SetData(streamBytes); - module_found = true; - } - - if (module_found && module.Length > 0) - { - break; - } - } - - if (module_found == false) - { - Console.WriteLine("\n[!] Could not find module in document (-m). List all module streams with (-l).\n"); - - if (!is_OpenXML) - { - cf.Commit(); - cf.Close(); - CompoundFile.ShrinkCompoundFile(oleFilename); - File.Delete(oleFilename); - if (File.Exists(outFilename)) File.Delete(outFilename); - } + } + + string [] dontPurgeTheseModules = { + "ThisDocument", + "ThisWorkbook", + "Sheet", + }; + + byte[] streamBytes; + bool module_found = false; + foreach (var vbaModule in vbaModules) + { + //VBA Purging begins + bool purge = true; + + if (module.Length > 0) + { + purge = vbaModule.moduleName == module; + } + else + { + foreach (string mod in dontPurgeTheseModules) + { + if (vbaModule.moduleName.StartsWith(mod)) + { + purge = false; + } + } + } + + if (purge) + { + Console.WriteLine("\n[*] Purging VBA code in module: " + vbaModule.moduleName); + Console.WriteLine("[*] Offset for code: " + vbaModule.textOffset); + + // Get the CompressedSourceCode from module + streamBytes = vbaStorage.GetStream(vbaModule.moduleName).GetData(); + string OG_VBACode = Utils.GetVBATextFromModuleStream(streamBytes, vbaModule.textOffset); + + // Remove P-code from module stream and set the module to only have the CompressedSourceCode + streamBytes = Utils.RemovePcodeInModuleStream(streamBytes, vbaModule.textOffset, OG_VBACode); + vbaStorage.GetStream(vbaModule.moduleName).SetData(streamBytes); + module_found = true; + } + + if (module_found && module.Length > 0) + { + break; + } + } + + if (module_found == false) + { + Console.WriteLine("\n[!] Could not find module in document (-m). List all module streams with (-l).\n"); + + if (!is_OpenXML) + { + cf.Commit(); + cf.Close(); + CompoundFile.ShrinkCompoundFile(oleFilename); + File.Delete(oleFilename); + if (File.Exists(outFilename)) File.Delete(outFilename); + } return; - } + } - // Change offset to 0 so that document can find compressed source code. - vbaStorage.GetStream("dir").SetData(Utils.Compress(Utils.ChangeOffset(dirStream))); - Console.WriteLine("\n[*] Module offset changed to 0."); + // Change offset to 0 so that document can find compressed source code. + vbaStorage.GetStream("dir").SetData(Utils.Compress(Utils.ChangeOffset(dirStream))); + Console.WriteLine("\n[*] Module offset changed to 0."); - // Remove performance cache in _VBA_PROJECT stream. Replace the entire stream with _VBA_PROJECT header. + // Remove performance cache in _VBA_PROJECT stream. Replace the entire stream with _VBA_PROJECT header. - string b1 = "00"; - string b2 = "00"; + string b1 = "00"; + string b2 = "00"; - Random rnd = new Random(); - b1 = String.Format("{0:X2}", rnd.Next(0, 255)); - b2 = String.Format("{0:X2}", rnd.Next(0, 255)); + Random rnd = new Random(); + b1 = String.Format("{0:X2}", rnd.Next(0, 255)); + b2 = String.Format("{0:X2}", rnd.Next(0, 255)); - byte[] data = Utils.HexToByte(String.Format("CC-61-FF-FF-00-{0}-{1}", b1, b2)); - vbaStorage.GetStream("_VBA_PROJECT").SetData(data); - Console.WriteLine("[*] PerformanceCache removed from _VBA_PROJECT stream."); + byte[] data = Utils.HexToByte(String.Format("CC-61-FF-FF-00-{0}-{1}", b1, b2)); + vbaStorage.GetStream("_VBA_PROJECT").SetData(data); + Console.WriteLine("[*] PerformanceCache removed from _VBA_PROJECT stream."); - // Check if document contains SRPs. Must be removed for VBA Purging to work. - try - { - for(int i = 0; i < 10; i++) + // Check if document contains SRPs. Must be removed for VBA Purging to work. + try + { + for(int i = 0; i < 10; i++) { - string srp = String.Format("__SRP_{0}", i); - var str = vbaStorage.TryGetStream(srp); - if (str != null) + string srp = String.Format("__SRP_{0}", i); + var str = vbaStorage.TryGetStream(srp); + if (str != null) { - vbaStorage.Delete(srp); - } - } - - Console.WriteLine("[*] SRP streams deleted!"); - } - catch (Exception e) - { - Console.WriteLine("[*] No SRP streams found."); - } - - // Commit changes and close - cf.Commit(); - cf.Close(); - CompoundFile.ShrinkCompoundFile(oleFilename); + vbaStorage.Delete(srp); + } + } + + Console.WriteLine("[*] SRP streams deleted!"); + } + catch (Exception e) + { + Console.WriteLine("[*] No SRP streams found."); + } + + // Commit changes and close + cf.Commit(); + cf.Close(); + CompoundFile.ShrinkCompoundFile(oleFilename); // Zip the file back up as a docm or xlsm if (is_OpenXML) { - if (File.Exists(outFilename)) File.Delete(outFilename); - ZipFile.CreateFromDirectory(unzipTempPath, outFilename); + if (File.Exists(outFilename)) File.Delete(outFilename); + ZipFile.CreateFromDirectory(unzipTempPath, outFilename); } Console.WriteLine("[+] VBA Purging completed successfully!\n"); - } - - // Error handle for file not found - catch (FileNotFoundException ex) when (ex.Message.Contains("Could not find file")) - { - Console.WriteLine("[!] Could not find path or file (-f). \n"); - } - - // Error handle when document specified and file chosen don't match - catch (CFItemNotFound ex) when (ex.Message.Contains("Cannot find item")) - { - Console.WriteLine("[!] File (-f) does not contain macros.\n"); - } - - // Error handle when document is not OLE/CFBF format - catch (CFFileFormatException) - { - Console.WriteLine("[!] Incorrect filetype (-f). OfficePurge supports documents in .docm or .xlsm format as well as .doc/.xls/.pub in the Office 97-2003 format.\n"); - } - finally + } + + // Error handle for file not found + catch (FileNotFoundException ex) when (ex.Message.Contains("Could not find file")) + { + Console.WriteLine("[!] Could not find path or file (-f). \n"); + } + + // Error handle when document specified and file chosen don't match + catch (CFItemNotFound ex) when (ex.Message.Contains("Cannot find item")) + { + Console.WriteLine("[!] File (-f) does not contain macros.\n"); + } + + // Error handle when document is not OLE/CFBF format + catch (CFFileFormatException) + { + Console.WriteLine("[!] Incorrect filetype (-f). OfficePurge supports documents in .docm or .xlsm format as well as .doc/.xls/.pub in the Office 97-2003 format.\n"); + } + finally { if (is_OpenXML) { - try - { - Directory.Delete(unzipTempPath, true); - } - catch (Exception) - { } + try + { + Directory.Delete(unzipTempPath, true); + } + catch (Exception) + { } } } - } - // Error handle for incorrect use of flags - catch (IndexOutOfRangeException) - { - Console.WriteLine("\n[!] Flags (-d), (-f), (-m) need an argument. Make sure you have provided these flags an argument.\n"); - } + } + // Error handle for incorrect use of flags + catch (IndexOutOfRangeException) + { + Console.WriteLine("\n[!] Flags (-d), (-f), (-m) need an argument. Make sure you have provided these flags an argument.\n"); + } } public static string CreateUniqueTempDirectory() diff --git a/OfficePurge/Utils.cs b/OfficePurge/Utils.cs index 0b72a74..cf1f04b 100644 --- a/OfficePurge/Utils.cs +++ b/OfficePurge/Utils.cs @@ -9,194 +9,194 @@ namespace OfficePurge { class Utils { - public static Dictionary ParseArgs(string[] args) - { - Dictionary ret = new Dictionary(); - if (args.Length > 0) - { - for (int i = 0; i < args.Length; i += 2) - { - if (args[i].Substring(1).ToLower() == "l") - { - ret.Add(args[i].Substring(1).ToLower(), "true"); - } - else - { - ret.Add(args[i].Substring(1).ToLower(), args[i + 1]); - } - } - } - return ret; - } - public static void HelpMenu() - { - Console.WriteLine("\n __ ____ ____ __ ___ ____ ____ _ _ ____ ___ ____ "); - Console.WriteLine(" / \\( __)( __)( )/ __)( __)( _ \\/ )( \\( _ \\ / __)( __)"); - Console.WriteLine("( O )) _) ) _) )(( (__ ) _) ) __/) \\/ ( ) /( (_ \\ ) _) "); - Console.WriteLine(" \\__/(__) (__) (__)\\___)(____)(__) \\____/(__\\_) \\___/(____) v1.0"); - Console.WriteLine("\n\n Author: Andrew Oliveau, tweaked by Mariusz Banach (mgeeky)\n"); - Console.WriteLine(" DESCRIPTION:"); - Console.WriteLine("\n\tOfficePurge is a C# tool that VBA purges malicious Office documents. "); - Console.WriteLine("\tVBA purging removes P-code from module streams within Office documents. "); - Console.WriteLine("\tDocuments that only contain source code and no compiled code are more"); - Console.WriteLine("\tlikely to evade AV detection and YARA rules.\n\n"); - Console.WriteLine(" SUPPORTED:"); - Console.WriteLine("\t- Word (pre-2007, 2007+)"); - Console.WriteLine("\t- Excel (pre-2007, 2007+)"); - Console.WriteLine("\t- Powerpoint (2007+)"); - Console.WriteLine("\t- Publisher (pre-2007)"); - Console.WriteLine(" USAGE:"); - Console.WriteLine("\t-f : Filename to VBA Purge"); - Console.WriteLine("\t-m : Module within document to VBA Purge"); - Console.WriteLine("\t-l : List module streams in document"); - Console.WriteLine("\t-h : Show help menu.\n"); - Console.WriteLine(" EXAMPLES:"); - Console.WriteLine("\n\t .\\OfficePurge.exe -f .\\malicious.doc -m NewMacros"); - Console.WriteLine("\t .\\OfficePurge.exe -f .\\payroll.xls -m Module1"); + public static Dictionary ParseArgs(string[] args) + { + Dictionary ret = new Dictionary(); + if (args.Length > 0) + { + for (int i = 0; i < args.Length; i += 2) + { + if (args[i].Substring(1).ToLower() == "l") + { + ret.Add(args[i].Substring(1).ToLower(), "true"); + } + else + { + ret.Add(args[i].Substring(1).ToLower(), args[i + 1]); + } + } + } + return ret; + } + public static void HelpMenu() + { + Console.WriteLine("\n __ ____ ____ __ ___ ____ ____ _ _ ____ ___ ____ "); + Console.WriteLine(" / \\( __)( __)( )/ __)( __)( _ \\/ )( \\( _ \\ / __)( __)"); + Console.WriteLine("( O )) _) ) _) )(( (__ ) _) ) __/) \\/ ( ) /( (_ \\ ) _) "); + Console.WriteLine(" \\__/(__) (__) (__)\\___)(____)(__) \\____/(__\\_) \\___/(____) v1.0"); + Console.WriteLine("\n\n Author: Andrew Oliveau, tweaked by Mariusz Banach (mgeeky)\n"); + Console.WriteLine(" DESCRIPTION:"); + Console.WriteLine("\n\tOfficePurge is a C# tool that VBA purges malicious Office documents. "); + Console.WriteLine("\tVBA purging removes P-code from module streams within Office documents. "); + Console.WriteLine("\tDocuments that only contain source code and no compiled code are more"); + Console.WriteLine("\tlikely to evade AV detection and YARA rules.\n\n"); + Console.WriteLine(" SUPPORTED:"); + Console.WriteLine("\t- Word (pre-2007, 2007+)"); + Console.WriteLine("\t- Excel (pre-2007, 2007+)"); + Console.WriteLine("\t- Powerpoint (2007+)"); + Console.WriteLine("\t- Publisher (pre-2007)"); + Console.WriteLine(" USAGE:"); + Console.WriteLine("\t-f : Filename to VBA Purge"); + Console.WriteLine("\t-m : Module within document to VBA Purge"); + Console.WriteLine("\t-l : List module streams in document"); + Console.WriteLine("\t-h : Show help menu.\n"); + Console.WriteLine(" EXAMPLES:"); + Console.WriteLine("\n\t .\\OfficePurge.exe -f .\\malicious.doc -m NewMacros"); + Console.WriteLine("\t .\\OfficePurge.exe -f .\\payroll.xls -m Module1"); Console.WriteLine("\t .\\OfficePurge.exe -f .\\payroll.pptm -m Module1"); Console.WriteLine("\t .\\OfficePurge.exe -f .\\donuts.pub -m ThisDocument"); - Console.WriteLine("\t .\\OfficePurge.exe -f .\\malicious.doc -l\n"); - } - public static List ParseModulesFromDirStream(byte[] dirStream) - { - // 2.3.4.2 dir Stream: Version Independent Project Information - // https://msdn.microsoft.com/en-us/library/dd906362(v=office.12).aspx - // Dir stream is ALWAYS in little endian - - List modules = new List(); - - int offset = 0; - UInt16 tag; - UInt32 wLength; - ModuleInformation currentModule = new ModuleInformation { moduleName = "", textOffset = 0 }; - - while (offset < dirStream.Length) - { - tag = GetWord(dirStream, offset); - wLength = GetDoubleWord(dirStream, offset + 2); - - // taken from Pcodedmp - if (tag == 9) - wLength = 6; - else if (tag == 3) - wLength = 2; - - switch (tag) - { - // MODULESTREAMNAME Record - case 26: - currentModule.moduleName = System.Text.Encoding.UTF8.GetString(dirStream, (int)offset + 6, (int)wLength); - break; - - // MODULEOFFSET Record - case 49: - currentModule.textOffset = GetDoubleWord(dirStream, offset + 6); - modules.Add(currentModule); - currentModule = new ModuleInformation { moduleName = "", textOffset = 0 }; - break; - } - - offset += 6; - offset += (int)wLength; - } - - return modules; - } - - public class ModuleInformation - { - // Name of VBA module stream - public string moduleName; - - // Offset of VBA CompressedSourceCode in VBA module stream - public UInt32 textOffset; - } - - public static UInt16 GetWord(byte[] buffer, int offset) - { - var rawBytes = new byte[2]; - Array.Copy(buffer, offset, rawBytes, 0, 2); - return BitConverter.ToUInt16(rawBytes, 0); - } - - public static UInt32 GetDoubleWord(byte[] buffer, int offset) - { - var rawBytes = new byte[4]; - Array.Copy(buffer, offset, rawBytes, 0, 4); - return BitConverter.ToUInt32(rawBytes, 0); - } - public static byte[] Compress(byte[] data) - { - var buffer = new DecompressedBuffer(data); - var container = new CompressedContainer(buffer); - return container.SerializeData(); - } - public static byte[] Decompress(byte[] data) - { - var container = new CompressedContainer(data); - var buffer = new DecompressedBuffer(container); - return buffer.Data; - } - public static string GetVBATextFromModuleStream(byte[] moduleStream, UInt32 textOffset) - { - string vbaModuleText = Encoding.UTF8.GetString(Decompress(moduleStream.Skip((int)textOffset).ToArray())); - return vbaModuleText; - } - public static byte[] RemovePcodeInModuleStream(byte[] moduleStream, UInt32 textOffset, string OG_VBACode) - { - return Compress(Encoding.UTF8.GetBytes(OG_VBACode)).ToArray(); - } - public static string getOutFilename(String filename) - { - string fn = Path.GetFileNameWithoutExtension(filename); - string ext = Path.GetExtension(filename); - string path = Path.GetDirectoryName(filename); - return Path.Combine(path, fn + "_PURGED" + ext); - } - public static byte[] HexToByte(string hex) - { - hex = hex.Replace("-", ""); - byte[] raw = new byte[hex.Length / 2]; - for (int i = 0; i < raw.Length; i++) - { - raw[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); - } - return raw; - } - public static byte[] ChangeOffset(byte[] dirStream) - { - int offset = 0; - UInt16 tag; - UInt32 wLength; - - // Change MODULEOFFSET to 0 - string zeros = "\0\0\0\0"; - - while (offset < dirStream.Length) - { - tag = GetWord(dirStream, offset); - wLength = GetDoubleWord(dirStream, offset + 2); - - // taken from Pcodedmp - if (tag == 9) - wLength = 6; - else if (tag == 3) - wLength = 2; - - switch (tag) - { - // MODULEOFFSET Record - case 49: - uint offset_change = GetDoubleWord(dirStream, offset + 6); - UTF8Encoding encoding = new UTF8Encoding(); - encoding.GetBytes(zeros, 0, (int)wLength, dirStream, (int)offset + 6); - break; - } - - offset += 6; - offset += (int)wLength; - } - return dirStream; - } - } + Console.WriteLine("\t .\\OfficePurge.exe -f .\\malicious.doc -l\n"); + } + public static List ParseModulesFromDirStream(byte[] dirStream) + { + // 2.3.4.2 dir Stream: Version Independent Project Information + // https://msdn.microsoft.com/en-us/library/dd906362(v=office.12).aspx + // Dir stream is ALWAYS in little endian + + List modules = new List(); + + int offset = 0; + UInt16 tag; + UInt32 wLength; + ModuleInformation currentModule = new ModuleInformation { moduleName = "", textOffset = 0 }; + + while (offset < dirStream.Length) + { + tag = GetWord(dirStream, offset); + wLength = GetDoubleWord(dirStream, offset + 2); + + // taken from Pcodedmp + if (tag == 9) + wLength = 6; + else if (tag == 3) + wLength = 2; + + switch (tag) + { + // MODULESTREAMNAME Record + case 26: + currentModule.moduleName = System.Text.Encoding.UTF8.GetString(dirStream, (int)offset + 6, (int)wLength); + break; + + // MODULEOFFSET Record + case 49: + currentModule.textOffset = GetDoubleWord(dirStream, offset + 6); + modules.Add(currentModule); + currentModule = new ModuleInformation { moduleName = "", textOffset = 0 }; + break; + } + + offset += 6; + offset += (int)wLength; + } + + return modules; + } + + public class ModuleInformation + { + // Name of VBA module stream + public string moduleName; + + // Offset of VBA CompressedSourceCode in VBA module stream + public UInt32 textOffset; + } + + public static UInt16 GetWord(byte[] buffer, int offset) + { + var rawBytes = new byte[2]; + Array.Copy(buffer, offset, rawBytes, 0, 2); + return BitConverter.ToUInt16(rawBytes, 0); + } + + public static UInt32 GetDoubleWord(byte[] buffer, int offset) + { + var rawBytes = new byte[4]; + Array.Copy(buffer, offset, rawBytes, 0, 4); + return BitConverter.ToUInt32(rawBytes, 0); + } + public static byte[] Compress(byte[] data) + { + var buffer = new DecompressedBuffer(data); + var container = new CompressedContainer(buffer); + return container.SerializeData(); + } + public static byte[] Decompress(byte[] data) + { + var container = new CompressedContainer(data); + var buffer = new DecompressedBuffer(container); + return buffer.Data; + } + public static string GetVBATextFromModuleStream(byte[] moduleStream, UInt32 textOffset) + { + string vbaModuleText = Encoding.UTF8.GetString(Decompress(moduleStream.Skip((int)textOffset).ToArray())); + return vbaModuleText; + } + public static byte[] RemovePcodeInModuleStream(byte[] moduleStream, UInt32 textOffset, string OG_VBACode) + { + return Compress(Encoding.UTF8.GetBytes(OG_VBACode)).ToArray(); + } + public static string getOutFilename(String filename) + { + string fn = Path.GetFileNameWithoutExtension(filename); + string ext = Path.GetExtension(filename); + string path = Path.GetDirectoryName(filename); + return Path.Combine(path, fn + "_PURGED" + ext); + } + public static byte[] HexToByte(string hex) + { + hex = hex.Replace("-", ""); + byte[] raw = new byte[hex.Length / 2]; + for (int i = 0; i < raw.Length; i++) + { + raw[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); + } + return raw; + } + public static byte[] ChangeOffset(byte[] dirStream) + { + int offset = 0; + UInt16 tag; + UInt32 wLength; + + // Change MODULEOFFSET to 0 + string zeros = "\0\0\0\0"; + + while (offset < dirStream.Length) + { + tag = GetWord(dirStream, offset); + wLength = GetDoubleWord(dirStream, offset + 2); + + // taken from Pcodedmp + if (tag == 9) + wLength = 6; + else if (tag == 3) + wLength = 2; + + switch (tag) + { + // MODULEOFFSET Record + case 49: + uint offset_change = GetDoubleWord(dirStream, offset + 6); + UTF8Encoding encoding = new UTF8Encoding(); + encoding.GetBytes(zeros, 0, (int)wLength, dirStream, (int)offset + 6); + break; + } + + offset += 6; + offset += (int)wLength; + } + return dirStream; + } + } }