diff --git a/README.md b/README.md index 5a0c3cb..d895a0e 100755 --- a/README.md +++ b/README.md @@ -10,57 +10,43 @@ SharpUp is licensed under the BSD 3-Clause license. ## Usage - C:\Temp>SharpUp.exe - - === SharpUp: Running Privilege Escalation Checks === - - - === Modifiable Services === - - Name : VulnSvc - DisplayName : VulnSvc - Description : - State : Stopped - StartMode : Auto - PathName : C:\Program Files\VulnSvc\VulnSvc.exe - - - === Modifiable Service Binaries === - - Name : VulnSvc2 - DisplayName : VulnSvc22 - Description : - State : Stopped - StartMode : Auto - PathName : C:\VulnSvc2\VulnSvc2.exe - - - === AlwaysInstallElevated Registry Keys === - - - - === Modifiable Folders in %PATH% === - - Modifable %PATH% Folder : C:\Go\bin - - - === Modifiable Registry Autoruns === - - - - === *Special* User Privileges === - - - - === Unattended Install Files === - - - - === McAfee Sitelist.xml Files === - - - - [*] Completed Privesc Checks in 11 seconds +``` +SharpUp.exe [audit] [check1] [check2]... + + audit - Specifies whether or not to enable audit mode. If enabled, SharpUp will run vulenrability checks + regardless if the process is in high integrity or the user is in the local administrator's group. + If no checks are specified, audit will run all checks. Otherwise, each check following audit will + be ran. + + check* - The individual vulnerability check to be ran. Must be one of the following: + + - AlwaysInstallElevated + - CachedGPPPassword + - DomainGPPPassword + - HijackablePaths + - McAfeeSitelistFiles + - ModifiableScheduledTask + - ModifiableServiceBinaries + - ModifiableServiceRegistryKeys + - ModifiableServices + - ProcessDLLHijack + - RegistryAutoLogons + - RegistryAutoruns + - TokenPrivileges + - UnattendedInstallFiles + - UnquotedServicePath + + + Examples: + SharpUp.exe audit + -> Runs all vulnerability checks regardless of integrity level or group membership. + + SharpUp.exe HijackablePaths + -> Check only if there are modifiable paths in the user's %PATH% variable. + + SharpUp.exe audit HijackablePaths + -> Check only for modifiable paths in the user's %PATH% regardless of integrity level or group membership. +``` ## Compile Instructions @@ -77,3 +63,8 @@ SharpUp incorporates various code C# snippets and bits of PoCs found throughout * [Rod Stephens' pattern for recursive file enumeration](http://csharphelper.com/blog/2015/06/find-files-that-match-multiple-patterns-in-c/) * [SwDevMan81's snippet for enumerating current token privileges](https://stackoverflow.com/questions/4349743/setting-size-of-token-privileges-luid-and-attributes-array-returned-by-gettokeni) * [Nikki Locke's code for querying service security descriptors](https://stackoverflow.com/questions/15771998/how-to-give-a-user-permission-to-start-and-stop-a-particular-service-using-c-sha/15796352#15796352) +* [Raika](https://github.com/Raikia) for providing example unquoted service path search code. +* [RemiEscourrou](https://github.com/RemiEscourrou) for contributing additional ACE checking code and example modifiable service registry key code. +* [Coder666](https://github.com/Coder666) for adding ACE filtering code to filter only ACEs with access allowed. +* [vysecurity](https://github.com/vysecurity) for providing Registry Auto Logon and Domain GPP Password example code. +* [djhohnstein](https://github.com/djhohnstein) for merging in several outdated PRs and refactoring the entire code base. \ No newline at end of file diff --git a/SharpUp/Checks/AlwaysInstallElevated.cs b/SharpUp/Checks/AlwaysInstallElevated.cs new file mode 100644 index 0000000..4578a5f --- /dev/null +++ b/SharpUp/Checks/AlwaysInstallElevated.cs @@ -0,0 +1,32 @@ +using SharpUp.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using static SharpUp.Utilities.RegistryUtils; + +namespace SharpUp.Checks +{ + public class AlwaysInstallElevated : VulnerabilityCheck + { + private static string _regPath = "Software\\Policies\\Microsoft\\Windows\\Installer"; + private static string _regName = "AlwaysInstallElevated"; + + public AlwaysInstallElevated() + { + _name = "Always Install Elevated"; + string AlwaysInstallElevatedHKLM = GetRegValue("HKLM", _regPath, _regName); + string AlwaysInstallElevatedHKCU = GetRegValue("HKCU", _regPath, _regName); + if (!string.IsNullOrEmpty(AlwaysInstallElevatedHKCU)) + { + _details.Add($"HKCU: {AlwaysInstallElevatedHKCU}"); + _isVulnerable = true; + } + if (!string.IsNullOrEmpty(AlwaysInstallElevatedHKLM)) + { + _details.Add($"HKLM: {AlwaysInstallElevatedHKLM}"); + _isVulnerable = true; + } + } + } +} diff --git a/SharpUp/Checks/CachedGPPPassword.cs b/SharpUp/Checks/CachedGPPPassword.cs new file mode 100644 index 0000000..958c905 --- /dev/null +++ b/SharpUp/Checks/CachedGPPPassword.cs @@ -0,0 +1,53 @@ +using SharpUp.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Xml; +using static SharpUp.Utilities.FileUtils; + +namespace SharpUp.Checks +{ + public class CachedGPPPassword : VulnerabilityCheck + { + private static string allUsers = System.Environment.GetEnvironmentVariable("ALLUSERSPROFILE"); + public CachedGPPPassword() + { + _name = "Cached GPP Password"; + try + { + if (!allUsers.Contains("ProgramData")) + { + // Before Windows Vista, the default value of AllUsersProfile was "C:\Documents and Settings\All Users" + // And after, "C:\ProgramData" + allUsers += "\\Application Data"; + } + allUsers += "\\Microsoft\\Group Policy\\History"; // look only in the GPO cache folder + + List files = FindFiles(allUsers, "*.xml"); + + // files will contain all XML files + foreach (string file in files) + { + if (!(file.Contains("Groups.xml") || file.Contains("Services.xml") + || file.Contains("Scheduledtasks.xml") || file.Contains("DataSources.xml") + || file.Contains("Printers.xml") || file.Contains("Drives.xml")) + || file.Contains("Registry.xml")) + { + continue; // uninteresting XML files, move to next + } + if (ParseGPPPasswordFromXml(file, out GPPPassword result)) + { + _isVulnerable = true; + _details.Add(result.ToString()); + } + } + } + catch (Exception ex) + { + _details.Add($"[X] Exception: {ex.Message}"); + } + } + } +} diff --git a/SharpUp/Checks/DomainGPPPassword.cs b/SharpUp/Checks/DomainGPPPassword.cs new file mode 100644 index 0000000..ab1b94a --- /dev/null +++ b/SharpUp/Checks/DomainGPPPassword.cs @@ -0,0 +1,57 @@ +using SharpUp.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; +using static SharpUp.Utilities.FileUtils; + +namespace SharpUp.Checks +{ + public class DomainGPPPassword : VulnerabilityCheck + { + private static string DNSDomain = System.Environment.GetEnvironmentVariable("USERDNSDOMAIN"); + public DomainGPPPassword() + { + _name = "GPP Password in SYSVOL"; + try + { + if (DNSDomain.Length > 1) + { + List files = FindFiles("\\\\" + DNSDomain + "\\SYSVOL", "*.xml"); + + // files will contain + foreach (string file in files) + { + if (file.Contains("Registry.xml") || + file.Contains("Groups.xml") || + file.Contains("Services.xml") || + file.Contains("ScheduledTasks.xml") || + file.Contains("DataSources.xml") || + file.Contains("Printers.xml") || + file.Contains("Drives.xml")) + { + if (ParseGPPPasswordFromXml(file, out GPPPassword result)) + { + _isVulnerable = true; + _details.Add(result.ToString()); + } + } + } + + } + else + { + _details.Add("Error: Machine is not a domain member or User is not a member of the domain."); + } + + + + } + catch (Exception ex) + { + _details.Add(String.Format("[X] Exception: {0}", ex.Message)); + } + } + } +} diff --git a/SharpUp/Checks/HijackablePaths.cs b/SharpUp/Checks/HijackablePaths.cs new file mode 100644 index 0000000..2347d8b --- /dev/null +++ b/SharpUp/Checks/HijackablePaths.cs @@ -0,0 +1,35 @@ +using SharpUp.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using static SharpUp.Utilities.RegistryUtils; +using static SharpUp.Utilities.FileUtils; + +namespace SharpUp.Checks +{ + public class HijackablePaths : VulnerabilityCheck + { + private static string _regPath = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"; + private static string _regName = "Path"; + public HijackablePaths() + { + _name = "Modifiable Folders in %PATH%"; + string path = GetRegValue("HKLM", _regPath, _regName); + if (string.IsNullOrEmpty(path)) + { + _isVulnerable = false; + return; + } + string[] pathFolders = path.Split(';'); + foreach (string folder in pathFolders) + { + if (CheckModifiableAccess(folder)) + { + _isVulnerable = true; + _details.Add(folder); + } + } + } + } +} diff --git a/SharpUp/Checks/McAfeeSitelistFiles.cs b/SharpUp/Checks/McAfeeSitelistFiles.cs new file mode 100644 index 0000000..be0b768 --- /dev/null +++ b/SharpUp/Checks/McAfeeSitelistFiles.cs @@ -0,0 +1,35 @@ +using SharpUp.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using static SharpUp.Utilities.FileUtils; + +namespace SharpUp.Checks +{ + public class McAfeeSitelistFiles : VulnerabilityCheck + { + private static string _drive = System.Environment.GetEnvironmentVariable("SystemDrive"); + private static string[] _searchLocations = + { + String.Format("{0}\\Program Files\\", _drive), + String.Format("{0}\\Program Files (x86)\\", _drive), + String.Format("{0}\\Documents and Settings\\", _drive), + String.Format("{0}\\Users\\", _drive) + }; + public McAfeeSitelistFiles() + { + _name = "McAfee SiteList.xml Files"; + foreach (string SearchLocation in _searchLocations) + { + List files = FindFiles(SearchLocation, "SiteList.xml"); + + foreach (string file in files) + { + _isVulnerable = true; + _details.Add(file); + } + } + } + } +} diff --git a/SharpUp/Checks/ModifiableScheduledTaskFile.cs b/SharpUp/Checks/ModifiableScheduledTaskFile.cs new file mode 100644 index 0000000..891bacf --- /dev/null +++ b/SharpUp/Checks/ModifiableScheduledTaskFile.cs @@ -0,0 +1,70 @@ +using SharpUp.Classes; +using System; +using System.IO; +using System.Xml; +using static SharpUp.Utilities.FileUtils; +using System.Security.AccessControl; + +namespace SharpUp.Checks +{ + public class ModifiableScheduledTaskFile : VulnerabilityCheck + { + public ModifiableScheduledTaskFile() + { + try + { + string tasksDir = Environment.GetEnvironmentVariable("SystemRoot"); + tasksDir += IntPtr.Size == 8 ? "\\System32\\Tasks" : "\\SysWOW64\\Tasks"; + string[] allfiles = Directory.GetFiles(tasksDir); + + foreach (string file in allfiles) + { + bool taskPerms = false; + bool binPerms = false; + // Check if task file is writable + taskPerms = CheckAccess(file, FileSystemRights.Write); + + try + { + // Load XML document + XmlDocument xmlDocument = new XmlDocument(); + xmlDocument.Load(file); + // Get the task name + XmlNodeList task = xmlDocument.GetElementsByTagName("URI"); + // Get the command (this is what will be executed) + XmlNodeList command = xmlDocument.GetElementsByTagName("Command"); + for (int i = 0; i < command.Count; i++) + { + string commandpath = command[i].InnerXml; + + // Check binary file permissions + binPerms = CheckAccess(commandpath, FileSystemRights.Write); + if (binPerms || taskPerms) + { + for (int a = 0; a < task.Count; a++) + { + string URIpath = task[a].InnerXml; + URIpath = task[a].InnerXml; + Console.WriteLine("\tTask Name : {0}", URIpath); + Console.WriteLine("\tTask Path : {0}", file); + Console.WriteLine("\tCommand : {0}", commandpath); + Console.WriteLine("\tTask XML Modifiable : {0}", taskPerms); + Console.WriteLine("\tTask Binary Modifiable : {0}", binPerms); + Console.WriteLine(); + } + } + } + } + catch + { + continue; + } + } + } + catch + { + Console.WriteLine("[!] Modifialbe scheduled tasks were not evaluated due to permissions."); + } + } + } +} \ No newline at end of file diff --git a/SharpUp/Checks/ModifiableServiceBinaries.cs b/SharpUp/Checks/ModifiableServiceBinaries.cs new file mode 100644 index 0000000..afaddc6 --- /dev/null +++ b/SharpUp/Checks/ModifiableServiceBinaries.cs @@ -0,0 +1,46 @@ +using SharpUp.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management; +using System.Text; +using System.Text.RegularExpressions; +using static SharpUp.Utilities.FileUtils; + +namespace SharpUp.Checks +{ + public class ModifiableServiceBinaries : VulnerabilityCheck + { + public ModifiableServiceBinaries() + { + _name = "Modifiable Service Binaries"; + try + { + // finds any service binaries that the current can modify + // TODO: or modify the parent folder + + ManagementObjectSearcher wmiData = new ManagementObjectSearcher(@"root\cimv2", "SELECT * FROM win32_service"); + ManagementObjectCollection data = wmiData.Get(); + + foreach (ManagementObject result in data) + { + if (result["PathName"] != null) + { + Match path = Regex.Match(result["PathName"].ToString(), @"^\W*([a-z]:\\.+?(\.exe|\.dll|\.sys))\W*", RegexOptions.IgnoreCase); + String binaryPath = path.Groups[1].ToString(); + + if (CheckModifiableAccess(binaryPath)) + { + _isVulnerable = true; + _details.Add($"Service '{result["Name"]}' (State: {result["State"]}, StartMode: {result["StartMode"]}) : {result["PathName"]}"); + } + } + } + } + catch (Exception ex) + { + _details.Add($"[X] Exception: {ex.Message}"); + } + } + } +} diff --git a/SharpUp/Checks/ModifiableServiceRegistryKeys.cs b/SharpUp/Checks/ModifiableServiceRegistryKeys.cs new file mode 100644 index 0000000..0ddd2fb --- /dev/null +++ b/SharpUp/Checks/ModifiableServiceRegistryKeys.cs @@ -0,0 +1,54 @@ +using Microsoft.Win32; +using SharpUp.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management; +using System.Security.AccessControl; +using System.Security.Principal; +using System.ServiceProcess; +using System.Text; +using static SharpUp.Utilities.RegistryUtils; + +namespace SharpUp.Checks +{ + public class ModifiableServiceRegistryKeys : VulnerabilityCheck + { + public ModifiableServiceRegistryKeys() + { + _name = "Services with Modifiable Registry Keys"; + // checks if the current user has rights to modify the given registry + + ServiceController[] scServices; + scServices = ServiceController.GetServices(); + + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + + foreach (ServiceController sc in scServices) + { + try + { + RegistryKey key = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\" + sc.ServiceName); + if (IsModifiableKey(key)) + { + ManagementObjectSearcher wmiData = new ManagementObjectSearcher(@"root\cimv2", String.Format("SELECT * FROM win32_service WHERE Name LIKE '{0}'", sc.ServiceName)); + ManagementObjectCollection data = wmiData.Get(); + + foreach (ManagementObject result in data) + { + _isVulnerable = true; + _details.Add($"Service '{result["Name"]}' (State: {result["State"]}, " + + $"StartMode: {result["StartMode"]}) : " + + $"{"SYSTEM\\CurrentControlSet\\Services\\" + sc.ServiceName}"); + } + } + } + catch (Exception ex) + { + _details.Add($"[X] Exception: {ex.Message}"); + } + } + + } + } +} diff --git a/SharpUp/Checks/ModifiableServices.cs b/SharpUp/Checks/ModifiableServices.cs new file mode 100644 index 0000000..8fb3f4e --- /dev/null +++ b/SharpUp/Checks/ModifiableServices.cs @@ -0,0 +1,107 @@ +using SharpUp.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security.AccessControl; +using System.Security.Principal; +using System.ServiceProcess; +using System.Text; +using static SharpUp.Native.Win32; + +namespace SharpUp.Checks +{ + public class ModifiableServices : VulnerabilityCheck + { + public ModifiableServices() + { + _name = "Modifiable Services"; + ServiceController[] scServices; + scServices = ServiceController.GetServices(); + + var GetServiceHandle = typeof(System.ServiceProcess.ServiceController).GetMethod("GetServiceHandle", BindingFlags.Instance | BindingFlags.NonPublic); + + object[] readRights = { 0x00020000 }; + + ServiceAccessRights[] ModifyRights = + { + ServiceAccessRights.ChangeConfig, + ServiceAccessRights.WriteDac, + ServiceAccessRights.WriteOwner, + ServiceAccessRights.GenericAll, + ServiceAccessRights.GenericWrite, + ServiceAccessRights.AllAccess + }; + + foreach (ServiceController sc in scServices) + { + try + { + IntPtr handle = (IntPtr)GetServiceHandle.Invoke(sc, readRights); + ServiceControllerStatus status = sc.Status; + byte[] psd = new byte[0]; + uint bufSizeNeeded; + bool ok = QueryServiceObjectSecurity(handle, SecurityInfos.DiscretionaryAcl, psd, 0, out bufSizeNeeded); + + if (!ok) + { + int err = Marshal.GetLastWin32Error(); + if (err == 122 || err == 0) + { // ERROR_INSUFFICIENT_BUFFER + // expected; now we know bufsize + psd = new byte[bufSizeNeeded]; + ok = QueryServiceObjectSecurity(handle, SecurityInfos.DiscretionaryAcl, psd, bufSizeNeeded, out bufSizeNeeded); + } + else + { + //throw new ApplicationException("error calling QueryServiceObjectSecurity() to get DACL for " + _name + ": error code=" + err); + continue; + } + } + if (!ok) + { + //throw new ApplicationException("error calling QueryServiceObjectSecurity(2) to get DACL for " + _name + ": error code=" + Marshal.GetLastWin32Error()); + continue; + } + + // get security descriptor via raw into DACL form so ACE ordering checks are done for us. + RawSecurityDescriptor rsd = new RawSecurityDescriptor(psd, 0); + RawAcl racl = rsd.DiscretionaryAcl; + DiscretionaryAcl dacl = new DiscretionaryAcl(false, false, racl); + + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + + foreach (System.Security.AccessControl.CommonAce ace in dacl) + { + if ((identity.Groups.Contains(ace.SecurityIdentifier) || ace.SecurityIdentifier == identity.User) && + ace.AceType == AceType.AccessAllowed) + { + ServiceAccessRights serviceRights = (ServiceAccessRights)ace.AccessMask; + foreach (ServiceAccessRights ModifyRight in ModifyRights) + { + if ((ModifyRight & serviceRights) == ModifyRight) + { + ManagementObjectSearcher wmiData = new ManagementObjectSearcher(@"root\cimv2", String.Format("SELECT * FROM win32_service WHERE Name LIKE '{0}'", sc.ServiceName)); + ManagementObjectCollection data = wmiData.Get(); + + foreach (ManagementObject result in data) + { + _isVulnerable = true; + _details.Add($"Service '{result["Name"]}' (State: {result["State"]}, StartMode: {result["StartMode"]})"); + } + break; + } + } + } + } + } + catch (Exception ex) + { + _details.Add($"[X] Exception: {ex.Message}"); + } + } + } + } +} diff --git a/SharpUp/Checks/ProcessDLLHijack.cs b/SharpUp/Checks/ProcessDLLHijack.cs new file mode 100644 index 0000000..e0617ff --- /dev/null +++ b/SharpUp/Checks/ProcessDLLHijack.cs @@ -0,0 +1,109 @@ +using SharpUp.Classes; +using System; +using System.IO; +using static SharpUp.Utilities.RegistryUtils; +using System.Diagnostics; +using System.Collections.Generic; +using static SharpUp.Utilities.FileUtils; +using System.Security.Principal; +using static SharpUp.Native.Win32; +using System.Security.AccessControl; + +namespace SharpUp.Checks +{ + public class ProcessDLLHijack : VulnerabilityCheck + { + private static string _regPath = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\KnownDlls"; + + // For future use + private static string GetProcessUser(Process process) + { + IntPtr processHandle = IntPtr.Zero; + try + { + OpenProcessToken(process.Handle, 8, out processHandle); + WindowsIdentity wi = new WindowsIdentity(processHandle); + string user = wi.Name; + return user.Contains(@"\") ? user.Substring(user.IndexOf(@"\") + 1) : user; + } + catch + { + return null; + } + finally + { + if (processHandle != IntPtr.Zero) + { + CloseHandle(processHandle); + } + } + } + + public ProcessDLLHijack() + { + // TODO: Take argements and add them to an array for scoping + //string[] options = args; + + // Registry key where known DLLs are listed + string[] keyname = GetRegSubkeys("HKLM", _regPath); + + // Create List for DLL values + List Dlls = new List(); + + // Get the value for each of the values and add it to the list + foreach (string valuename in keyname) + { + string dllname = valuename.ToLower(); + Dlls.Add(dllname); + } + + // Get all running processes + Process[] processes = Process.GetProcesses(); + + foreach (Process process in processes) + { + // Try to check the modules loaded for the process + try + { + // Go through each module loaded in the process + var processmodules = process.Modules; + foreach (ProcessModule module in processmodules) + { + string modules = module.ModuleName; + modules = modules.ToLower(); + string filepath = module.FileName.ToLower(); + bool writeperms = CheckAccess(filepath, FileSystemRights.Write); + // Exclude items that do not end with .dll, exclude known dlls, exclude items in c:\\windows, exclude files that do not have the correct permissions + if (module.FileName.EndsWith(".dll") && !Dlls.Contains(modules) && !filepath.Contains("c:\\windows") && writeperms == true) + { + if (filepath.Contains("c:\\program files")) + { + Console.WriteLine("[+] Potenatially Hijackable DLL: {0}\n" + + "[!] Files in C:\\Program Files and C:\\Program Files (x86) may be false positives. Permissions should be verified manually.\n" + + "[+] Associated Process is {1} with PID {2}", + module.FileName.ToString(), + process.ProcessName.ToString(), + process.Id.ToString()); + } + else + { + Console.WriteLine("[+] Hijackable DLL: {0}\n" + + "[+] Associated Process is {1} with PID {2} ", + module.FileName.ToString(), + process.ProcessName.ToString(), + process.Id.ToString()); + } + } + } + } + catch + { + // Output for when the current user doesn't have permissions for a process + // Console.WriteLine("[-] Access denied for {0} under PID {1}", process.ProcessName.ToString(), process.Id.ToString()); + continue; + + } + } + } + } +} diff --git a/SharpUp/Checks/RegistryAutoLogons.cs b/SharpUp/Checks/RegistryAutoLogons.cs new file mode 100644 index 0000000..dc7f52d --- /dev/null +++ b/SharpUp/Checks/RegistryAutoLogons.cs @@ -0,0 +1,51 @@ +using SharpUp.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using static SharpUp.Utilities.RegistryUtils; + +namespace SharpUp.Checks +{ + public class RegistryAutoLogons : VulnerabilityCheck + { + private static string _regWinlogon = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"; + public RegistryAutoLogons() + { + _name = "Registry AutoLogons"; + try + { + + string AutoAdminLogon = GetRegValue("HKLM", _regWinlogon, "AutoAdminLogon"); + + if (AutoAdminLogon.Equals("1")) + { + Console.WriteLine("Registry AutoLogon Found\r\n"); + string DefaultDomainName = GetRegValue("HKLM", _regWinlogon, "DefaultDomainName"); + string DefaultUserName = GetRegValue("HKLM", _regWinlogon, "DefaultUserName"); + string DefaultPassword = GetRegValue("HKLM", _regWinlogon, "DefaultPassword"); + string AltDefaultDomainName = GetRegValue("HKLM", _regWinlogon, "AltDefaultDomainName"); + string AltDefaultUserName = GetRegValue("HKLM", _regWinlogon, "AltDefaultUserName"); + string AltDefaultPassword = GetRegValue("HKLM", _regWinlogon, "AltDefaultPassword"); + + if (!DefaultUserName.Equals("") || !AltDefaultUserName.Equals("")) + { + _isVulnerable = true; + + _details.Add(string.Format("DefaultDomainName: {0}", DefaultDomainName)); + _details.Add(string.Format("DefaultUserName: {0}", DefaultUserName)); + _details.Add(string.Format("DefaultPassword: {0}", DefaultPassword)); + _details.Add(string.Format("AltDefaultDomainName: {0}", AltDefaultDomainName)); + _details.Add(string.Format("AltDefaultUserName: {0}", AltDefaultUserName)); + _details.Add(string.Format("AltDefaultPassword: {0}", AltDefaultPassword)); + } + } + + } + catch (Exception ex) + { + _details.Add(String.Format("[X] Exception: {0}", ex.Message)); + } + } + } +} diff --git a/SharpUp/Checks/RegistryAutoruns.cs b/SharpUp/Checks/RegistryAutoruns.cs new file mode 100644 index 0000000..838d8af --- /dev/null +++ b/SharpUp/Checks/RegistryAutoruns.cs @@ -0,0 +1,48 @@ +using SharpUp.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using static SharpUp.Utilities.FileUtils; +using static SharpUp.Utilities.RegistryUtils; + +namespace SharpUp.Checks +{ + public class RegistryAutoruns : VulnerabilityCheck + { + private static string[] _autorunLocations = new string[] { + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunService", + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnceService", + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunService", + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnceService" + }; + + public RegistryAutoruns() + { + _name = "Modifiable Registry AutoRun Files"; + foreach (string autorunLocation in _autorunLocations) + { + Dictionary settings = GetRegValues("HKLM", autorunLocation); + if ((settings != null) && (settings.Count != 0)) + { + foreach (KeyValuePair kvp in settings) + { + Match path = Regex.Match(kvp.Value.ToString(), @"^\W*([a-z]:\\.+?(\.exe|\.bat|\.ps1|\.vbs))\W*", RegexOptions.IgnoreCase); + String binaryPath = path.Groups[1].ToString(); + + if (CheckModifiableAccess(binaryPath)) + { + _isVulnerable = true; + _details.Add($"HKLM:\\{autorunLocation} : {binaryPath}"); + } + } + } + } + } + } +} diff --git a/SharpUp/Checks/TokenPrivileges.cs b/SharpUp/Checks/TokenPrivileges.cs new file mode 100644 index 0000000..f9fc34e --- /dev/null +++ b/SharpUp/Checks/TokenPrivileges.cs @@ -0,0 +1,57 @@ +using SharpUp.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Principal; +using System.Text; +using static SharpUp.Native.Win32; + +namespace SharpUp.Checks +{ + public class TokenPrivileges : VulnerabilityCheck + { + private static string[] _specialPrivileges = { + "SeSecurityPrivilege", "SeTakeOwnershipPrivilege", "SeLoadDriverPrivilege", + "SeBackupPrivilege", "SeRestorePrivilege", "SeDebugPrivilege", + "SeSystemEnvironmentPrivilege", "SeImpersonatePrivilege", "SeTcbPrivilege" + }; + public TokenPrivileges() + { + _name = "Abusable Token Privileges"; + + int TokenInfLength = 0; + IntPtr ThisHandle = WindowsIdentity.GetCurrent().Token; + GetTokenInformation(ThisHandle, TOKEN_INFORMATION_CLASS.TokenPrivileges, IntPtr.Zero, TokenInfLength, out TokenInfLength); + IntPtr TokenInformation = Marshal.AllocHGlobal(TokenInfLength); + if (GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenPrivileges, TokenInformation, TokenInfLength, out TokenInfLength)) + { + TOKEN_PRIVILEGES ThisPrivilegeSet = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(TokenInformation, typeof(TOKEN_PRIVILEGES)); + for (int index = 0; index < ThisPrivilegeSet.PrivilegeCount; index++) + { + LUID_AND_ATTRIBUTES laa = ThisPrivilegeSet.Privileges[index]; + System.Text.StringBuilder StrBuilder = new System.Text.StringBuilder(); + int LuidNameLen = 0; + IntPtr LuidPointer = Marshal.AllocHGlobal(Marshal.SizeOf(laa.Luid)); + Marshal.StructureToPtr(laa.Luid, LuidPointer, true); + LookupPrivilegeName(null, LuidPointer, null, ref LuidNameLen); + StrBuilder.EnsureCapacity(LuidNameLen + 1); + if (LookupPrivilegeName(null, LuidPointer, StrBuilder, ref LuidNameLen)) + { + string privilege = StrBuilder.ToString(); + foreach (string SpecialPrivilege in _specialPrivileges) + { + if (privilege == SpecialPrivilege) + { + _isVulnerable = true; + _details.Add($"{privilege}: {(LuidAttributes)laa.Attributes}"); + } + } + } + Marshal.FreeHGlobal(LuidPointer); + } + } + Marshal.FreeHGlobal(TokenInformation); + } + } +} diff --git a/SharpUp/Checks/UnattendedInstallFiles.cs b/SharpUp/Checks/UnattendedInstallFiles.cs new file mode 100644 index 0000000..9a6074d --- /dev/null +++ b/SharpUp/Checks/UnattendedInstallFiles.cs @@ -0,0 +1,38 @@ +using SharpUp.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SharpUp.Checks +{ + public class UnattendedInstallFiles : VulnerabilityCheck + { + private static string _windir = System.Environment.GetEnvironmentVariable("windir"); + private static string[] _searchLocations = + { + String.Format("{0}\\sysprep\\sysprep.xml", _windir), + String.Format("{0}\\sysprep\\sysprep.inf", _windir), + String.Format("{0}\\sysprep.inf", _windir), + String.Format("{0}\\Panther\\Unattended.xml", _windir), + String.Format("{0}\\Panther\\Unattend.xml", _windir), + String.Format("{0}\\Panther\\Unattend\\Unattend.xml", _windir), + String.Format("{0}\\Panther\\Unattend\\Unattended.xml", _windir), + String.Format("{0}\\System32\\Sysprep\\unattend.xml", _windir), + String.Format("{0}\\System32\\Sysprep\\Panther\\unattend.xml", _windir) + }; + public UnattendedInstallFiles() + { + _name = "Unattended Install Files"; + + foreach (string _searchLocation in _searchLocations) + { + if (System.IO.File.Exists(_searchLocation)) + { + _isVulnerable = true; + _details.Add(_searchLocation); + } + } + } + } +} diff --git a/SharpUp/Checks/UnquotedServicePath.cs b/SharpUp/Checks/UnquotedServicePath.cs new file mode 100644 index 0000000..318a95c --- /dev/null +++ b/SharpUp/Checks/UnquotedServicePath.cs @@ -0,0 +1,66 @@ +using Microsoft.Win32; +using SharpUp.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using static SharpUp.Utilities.FileUtils; + +namespace SharpUp.Checks +{ + public class UnquotedServicePath : VulnerabilityCheck + { + public UnquotedServicePath() + { + _name = "Services with Unquoted Paths"; + + RegistryKey services = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Services"); + foreach (string subkey in services.GetSubKeyNames()) + { + RegistryKey serviceKey = Registry.LocalMachine.OpenSubKey(string.Format(@"SYSTEM\CurrentControlSet\Services\{0}", subkey)); + string path = ((string)serviceKey.GetValue("ImagePath", "")).Trim(); + if (path != "" && + !path.StartsWith("\"") && + !path.StartsWith("'") && + path.Substring(0, path.ToLower().IndexOf(".exe") + 4).Contains(" ")) + { + string startType = "Disabled"; + switch ((int)serviceKey.GetValue("Start", 0)) + { + case 2: + startType = "Automatic"; + break; + case 3: + startType = "Manual"; + break; + case 4: + startType = "Disabled"; + break; + default: + startType = "Unknown"; + break; + } + List modPaths = new List(); + string executable_path = path.Substring(0, path.ToLower().IndexOf(".exe") + 4); + int num_spaces = executable_path.Split(' ').Length - 1; + int lastFound = 0; + for (int x = 0; x < num_spaces; ++x) + { + string new_path = path.Substring(0, path.ToLower().IndexOf(' ', lastFound)); + lastFound = path.ToLower().IndexOf(' ', lastFound) + 1; + string check_path = new_path.Substring(0, new_path.LastIndexOf('\\')) + "\\"; + if (CheckModifiableAccess(check_path, true)) + { + if (!modPaths.Contains(new_path)) + { + _isVulnerable = true; + _details.Add($"Service '{subkey}' (StartMode: {startType}) has executable '{path}', but '{new_path}' is modifable."); + } + } + } + } + + } + } + } +} diff --git a/SharpUp/Classes/VulnerabilityCheck.cs b/SharpUp/Classes/VulnerabilityCheck.cs new file mode 100644 index 0000000..7dfb726 --- /dev/null +++ b/SharpUp/Classes/VulnerabilityCheck.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using SharpUp.Interfaces; + +namespace SharpUp.Classes +{ + public abstract class VulnerabilityCheck : IVulnerabilityCheck + { + protected bool _isVulnerable = false; + protected List _details = new List(); + protected string _name; + public virtual bool IsVulnerable() + { + return _isVulnerable; + } + + public virtual string[] Details() + { + return _details.ToArray(); + } + + public virtual string Name() + { + return _name; + } + } +} diff --git a/SharpUp/Interfaces/IVulnerabilityCheck.cs b/SharpUp/Interfaces/IVulnerabilityCheck.cs new file mode 100644 index 0000000..f328568 --- /dev/null +++ b/SharpUp/Interfaces/IVulnerabilityCheck.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SharpUp.Interfaces +{ + public interface IVulnerabilityCheck + { + string Name(); + bool IsVulnerable(); + string[] Details(); + } +} diff --git a/SharpUp/Native/Win32.cs b/SharpUp/Native/Win32.cs new file mode 100644 index 0000000..f092d03 --- /dev/null +++ b/SharpUp/Native/Win32.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace SharpUp.Native +{ + public static class Win32 + { + [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool ConvertSidToStringSid(IntPtr pSID, out IntPtr ptrSid); + + [DllImport("kernel32.dll")] + public static extern IntPtr LocalFree(IntPtr hMem); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool GetTokenInformation( + IntPtr TokenHandle, + TOKEN_INFORMATION_CLASS TokenInformationClass, + IntPtr TokenInformation, + int TokenInformationLength, + out int ReturnLength); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool LookupPrivilegeName( + string lpSystemName, + IntPtr lpLuid, + System.Text.StringBuilder lpName, + ref int cchName); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool QueryServiceObjectSecurity( + IntPtr serviceHandle, + System.Security.AccessControl.SecurityInfos secInfo, + byte[] lpSecDesrBuf, + uint bufSize, + out uint bufSizeNeeded); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseHandle(IntPtr hObject); + + // PInvoke structures/contants + public const uint SE_GROUP_LOGON_ID = 0xC0000000; // from winnt.h + public const int TokenGroups = 2; // from TOKEN_INFORMATION_CLASS + public enum TOKEN_INFORMATION_CLASS + { + TokenUser = 1, + TokenGroups, + TokenPrivileges, + TokenOwner, + TokenPrimaryGroup, + TokenDefaultDacl, + TokenSource, + TokenType, + TokenImpersonationLevel, + TokenStatistics, + TokenRestrictedSids, + TokenSessionId, + TokenGroupsAndPrivileges, + TokenSessionReference, + TokenSandBoxInert, + TokenAuditPolicy, + TokenOrigin + } + + [StructLayout(LayoutKind.Sequential)] + public struct SID_AND_ATTRIBUTES + { + public IntPtr Sid; + public uint Attributes; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TOKEN_GROUPS + { + public int GroupCount; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] + public SID_AND_ATTRIBUTES[] Groups; + }; + + public struct TOKEN_PRIVILEGES + { + public UInt32 PrivilegeCount; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 35)] + public LUID_AND_ATTRIBUTES[] Privileges; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LUID_AND_ATTRIBUTES + { + public LUID Luid; + public UInt32 Attributes; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LUID + { + public uint LowPart; + public int HighPart; + } + + [Flags] + public enum LuidAttributes : uint + { + DISABLED = 0x00000000, + SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001, + SE_PRIVILEGE_ENABLED = 0x00000002, + SE_PRIVILEGE_REMOVED = 0x00000004, + SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000 + } + + [Flags] + public enum ServiceAccessRights : uint + { + QueryConfig = 0x00000001, + ChangeConfig = 0x00000002, + QueryStatus = 0x00000004, + EnumerateDependents = 0x00000008, + Start = 0x00000010, + Stop = 0x00000020, + PauseContinue = 0x00000040, + Interrogate = 0x00000080, + UserDefinedControl = 0x00000100, + Delete = 0x00010000, + ReadControl = 0x00020000, + WriteDac = 0x00040000, + WriteOwner = 0x00080000, + Synchronize = 0x00100000, + AccessSystemSecurity = 0x01000000, + GenericAll = 0x10000000, + GenericExecute = 0x20000000, + GenericWrite = 0x40000000, + GenericRead = 0x80000000, + AllAccess = 0x000F01FF, + } + } +} diff --git a/SharpUp/Program.cs b/SharpUp/Program.cs index 804db63..5cca3af 100755 --- a/SharpUp/Program.cs +++ b/SharpUp/Program.cs @@ -1,1076 +1,176 @@ +using SharpUp.Classes; using System; -using System.Collections; using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.Diagnostics.Eventing.Reader; -using System.IO; using System.Linq; -using System.Management; -using System.Net; -using System.Net.NetworkInformation; using System.Reflection; -using System.Runtime.InteropServices; -using System.Security.AccessControl; -using System.Security.Principal; -using System.ServiceProcess; -using System.Text; -using System.Text.RegularExpressions; -using System.Xml; -using Microsoft.Win32; -using System.Security.Cryptography; - +using System.Threading; +using static SharpUp.Utilities.IdentityUtils; namespace SharpUp { class Program { - [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)] - static extern bool ConvertSidToStringSid(IntPtr pSID, out IntPtr ptrSid); - - [DllImport("kernel32.dll")] - static extern IntPtr LocalFree(IntPtr hMem); - - [DllImport("advapi32.dll", SetLastError = true)] - static extern bool GetTokenInformation( - IntPtr TokenHandle, - TOKEN_INFORMATION_CLASS TokenInformationClass, - IntPtr TokenInformation, - int TokenInformationLength, - out int ReturnLength); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] - [return: MarshalAs(UnmanagedType.Bool)] - protected static extern bool LookupPrivilegeName( - string lpSystemName, - IntPtr lpLuid, - System.Text.StringBuilder lpName, - ref int cchName); - - [DllImport("advapi32.dll", SetLastError = true)] - static extern bool QueryServiceObjectSecurity( - IntPtr serviceHandle, - System.Security.AccessControl.SecurityInfos secInfo, - byte[] lpSecDesrBuf, - uint bufSize, - out uint bufSizeNeeded); - - - // PInvoke structures/contants - public const uint SE_GROUP_LOGON_ID = 0xC0000000; // from winnt.h - public const int TokenGroups = 2; // from TOKEN_INFORMATION_CLASS - enum TOKEN_INFORMATION_CLASS - { - TokenUser = 1, - TokenGroups, - TokenPrivileges, - TokenOwner, - TokenPrimaryGroup, - TokenDefaultDacl, - TokenSource, - TokenType, - TokenImpersonationLevel, - TokenStatistics, - TokenRestrictedSids, - TokenSessionId, - TokenGroupsAndPrivileges, - TokenSessionReference, - TokenSandBoxInert, - TokenAuditPolicy, - TokenOrigin - } - - [StructLayout(LayoutKind.Sequential)] - public struct SID_AND_ATTRIBUTES - { - public IntPtr Sid; - public uint Attributes; - } - - [StructLayout(LayoutKind.Sequential)] - public struct TOKEN_GROUPS - { - public int GroupCount; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] - public SID_AND_ATTRIBUTES[] Groups; - }; - - protected struct TOKEN_PRIVILEGES - { - public UInt32 PrivilegeCount; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 35)] - public LUID_AND_ATTRIBUTES[] Privileges; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct LUID_AND_ATTRIBUTES - { - public LUID Luid; - public UInt32 Attributes; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct LUID - { - public uint LowPart; - public int HighPart; - } - - [Flags] - public enum LuidAttributes : uint - { - DISABLED = 0x00000000, - SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001, - SE_PRIVILEGE_ENABLED = 0x00000002, - SE_PRIVILEGE_REMOVED = 0x00000004, - SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000 - } - - [Flags] - public enum ServiceAccessRights : uint - { - QueryConfig = 0x00000001, - ChangeConfig = 0x00000002, - QueryStatus = 0x00000004, - EnumerateDependents = 0x00000008, - Start = 0x00000010, - Stop = 0x00000020, - PauseContinue = 0x00000040, - Interrogate = 0x00000080, - UserDefinedControl = 0x00000100, - Delete = 0x00010000, - ReadControl = 0x00020000, - WriteDac = 0x00040000, - WriteOwner = 0x00080000, - Synchronize = 0x00100000, - AccessSystemSecurity = 0x01000000, - GenericAll = 0x10000000, - GenericExecute = 0x20000000, - GenericWrite = 0x40000000, - GenericRead = 0x80000000, - AllAccess = 0x000F01FF, - } - - - // helpers - public static string GetRegValue(string hive, string path, string value) - { - // returns a single registry value under the specified path in the specified hive (HKLM/HKCU) - string regKeyValue = ""; - if (hive == "HKCU") - { - var regKey = Registry.CurrentUser.OpenSubKey(path); - if (regKey != null) - { - regKeyValue = String.Format("{0}", regKey.GetValue(value)); - } - return regKeyValue; - } - else if (hive == "HKU") - { - var regKey = Registry.Users.OpenSubKey(path); - if (regKey != null) - { - regKeyValue = String.Format("{0}", regKey.GetValue(value)); - } - return regKeyValue; - } - else - { - var regKey = Registry.LocalMachine.OpenSubKey(path); - if (regKey != null) - { - regKeyValue = String.Format("{0}", regKey.GetValue(value)); - } - return regKeyValue; - } - } - - public static Dictionary GetRegValues(string hive, string path) + static bool auditMode = false; + public static void PrivescChecks(Type[] checks) { - // returns all registry values under the specified path in the specified hive (HKLM/HKCU) - Dictionary keyValuePairs = null; + bool isHighIntegrity = IsHighIntegrity(); + bool isLocalAdmin = IsLocalAdmin(); + bool shouldQuit = false; - if (hive == "HKCU") - { - using (var regKeyValues = Registry.CurrentUser.OpenSubKey(path)) - { - if (regKeyValues != null) - { - var valueNames = regKeyValues.GetValueNames(); - keyValuePairs = valueNames.ToDictionary(name => name, regKeyValues.GetValue); - } - } - } - else if (hive == "HKU") + if (isHighIntegrity) { - using (var regKeyValues = Registry.Users.OpenSubKey(path)) - { - if (regKeyValues != null) - { - var valueNames = regKeyValues.GetValueNames(); - keyValuePairs = valueNames.ToDictionary(name => name, regKeyValues.GetValue); - } - } + Console.WriteLine("\r\n[*] Already in high integrity, no need to privesc!"); + shouldQuit = true; } - else + else if (!isHighIntegrity && isLocalAdmin) { - using (var regKeyValues = Registry.LocalMachine.OpenSubKey(path)) - { - if (regKeyValues != null) - { - var valueNames = regKeyValues.GetValueNames(); - keyValuePairs = valueNames.ToDictionary(name => name, regKeyValues.GetValue); - } - } + Console.WriteLine("\r\n[*] In medium integrity but user is a local administrator- UAC can be bypassed."); + shouldQuit = true; } - return keyValuePairs; - } - public static string[] GetRegSubkeys(string hive, string path) - { - // returns an array of the subkeys names under the specified path in the specified hive (HKLM/HKCU/HKU) - try + // if already admin we can quit without running all checks + if (shouldQuit) { - Microsoft.Win32.RegistryKey myKey = null; - if (hive == "HKLM") - { - myKey = Registry.LocalMachine.OpenSubKey(path); - } - else if (hive == "HKU") + if (!auditMode) { - myKey = Registry.Users.OpenSubKey(path); + Console.WriteLine("\r\n[*] Quitting now, re-run with \"audit\" argument to run checks anyway (audit mode)."); + return; } else { - myKey = Registry.CurrentUser.OpenSubKey(path); - } - String[] subkeyNames = myKey.GetSubKeyNames(); - return myKey.GetSubKeyNames(); - } - catch - { - return new string[0]; - } - } - - public static bool IsHighIntegrity() - { - // returns true if the current process is running with adminstrative privs in a high integrity context - WindowsIdentity identity = WindowsIdentity.GetCurrent(); - WindowsPrincipal principal = new WindowsPrincipal(identity); - return principal.IsInRole(WindowsBuiltInRole.Administrator); - } - - public static string[] GetTokenGroupSIDs() - { - // Returns all SIDs that the current user is a part of, whether they are disabled or not. - - // adapted almost directly from https://stackoverflow.com/questions/2146153/how-to-get-the-logon-sid-in-c-sharp/2146418#2146418 - - int TokenInfLength = 0; - - // first call gets length of TokenInformation - bool Result = GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenGroups, IntPtr.Zero, TokenInfLength, out TokenInfLength); - IntPtr TokenInformation = Marshal.AllocHGlobal(TokenInfLength); - Result = GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenGroups, TokenInformation, TokenInfLength, out TokenInfLength); - - if (!Result) - { - Marshal.FreeHGlobal(TokenInformation); - return null; - } - - TOKEN_GROUPS groups = (TOKEN_GROUPS)Marshal.PtrToStructure(TokenInformation, typeof(TOKEN_GROUPS)); - string[] userSIDS = new string[groups.GroupCount]; - int sidAndAttrSize = Marshal.SizeOf(new SID_AND_ATTRIBUTES()); - for (int i = 0; i < groups.GroupCount; i++) - { - SID_AND_ATTRIBUTES sidAndAttributes = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure( - new IntPtr(TokenInformation.ToInt64() + i * sidAndAttrSize + IntPtr.Size), typeof(SID_AND_ATTRIBUTES)); - - IntPtr pstr = IntPtr.Zero; - ConvertSidToStringSid(sidAndAttributes.Sid, out pstr); - userSIDS[i] = Marshal.PtrToStringAuto(pstr); - LocalFree(pstr); - } - - Marshal.FreeHGlobal(TokenInformation); - return userSIDS; - } - - public static bool IsLocalAdmin() - { - // checks if the "S-1-5-32-544" in the current token groups set, meaning the user is a local administrator - string[] SIDs = GetTokenGroupSIDs(); - - foreach (string SID in SIDs) - { - if (SID == "S-1-5-32-544") - { - return true; - } - } - return false; - } - - public static bool CheckAccess(string Path, FileSystemRights AccessRight) - { - // checks if the current user has the specified AccessRight to the specified file or folder - // from https://stackoverflow.com/questions/1410127/c-sharp-test-if-user-has-write-access-to-a-folder/21996345#21996345 - - if (string.IsNullOrEmpty(Path)) return false; - - try - { - AuthorizationRuleCollection rules = Directory.GetAccessControl(Path).GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)); - WindowsIdentity identity = WindowsIdentity.GetCurrent(); - - foreach (FileSystemAccessRule rule in rules) - { - if (identity.Groups.Contains(rule.IdentityReference)) + // except if auditMode has explictly been asked + Console.WriteLine($"\r\n[*] Audit mode: running an additional {checks.Length} check(s)."); + if (isHighIntegrity) { - if ((AccessRight & rule.FileSystemRights) == AccessRight) - { - if (rule.AccessControlType == AccessControlType.Allow) - return true; - } + Console.WriteLine("[*] Note: Running audit mode in high integrity will yield a large number of false positives."); } } } - catch { } - - return false; - } - - public static bool CheckModifiableAccess(string Path) - { - // checks if the current user has rights to modify the given file/directory - // adapted from https://stackoverflow.com/questions/1410127/c-sharp-test-if-user-has-write-access-to-a-folder/21996345#21996345 - - if (string.IsNullOrEmpty(Path)) return false; - // TODO: check if file exists, check file's parent folder - - // rights that signify modiable access - FileSystemRights[] ModifyRights = - { - FileSystemRights.ChangePermissions, - FileSystemRights.FullControl, - FileSystemRights.Modify, - FileSystemRights.TakeOwnership, - FileSystemRights.Write, - FileSystemRights.WriteData, - FileSystemRights.CreateDirectories, - FileSystemRights.CreateFiles - }; - - ArrayList paths = new ArrayList(); - paths.Add(Path); - - try - { - FileAttributes attr = System.IO.File.GetAttributes(Path); - if ((attr & FileAttributes.Directory) != FileAttributes.Directory) - { - string parentFolder = System.IO.Path.GetDirectoryName(Path); - paths.Add(parentFolder); - } - } - catch - { - return false; - } - - - try + + List vulnerableChecks = new List(); + Mutex mtx = new Mutex(); + List runningThreads = new List(); + foreach(Type t in checks) { - foreach (string candidatePath in paths) + Thread vulnThread = new Thread(() => { - AuthorizationRuleCollection rules = Directory.GetAccessControl(candidatePath).GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)); - WindowsIdentity identity = WindowsIdentity.GetCurrent(); - - foreach (FileSystemAccessRule rule in rules) + try { - if (identity.Groups.Contains(rule.IdentityReference)) + VulnerabilityCheck c = (VulnerabilityCheck)Activator.CreateInstance(t); + if (c.IsVulnerable()) { - foreach (FileSystemRights AccessRight in ModifyRights) - { - if ((AccessRight & rule.FileSystemRights) == AccessRight) - { - if (rule.AccessControlType == AccessControlType.Allow) - return true; - } - } + mtx.WaitOne(); + vulnerableChecks.Add(c); + mtx.ReleaseMutex(); } - } - } - return false; - } - catch - { - return false; - } - } - - public static List FindFiles(string path, string patterns) - { - // finds files matching one or more patterns under a given path, recursive - // adapted from http://csharphelper.com/blog/2015/06/find-files-that-match-multiple-patterns-in-c/ - // pattern: "*pass*;*.png;" - - var files = new List(); - - try - { - // search every pattern in this directory's files - foreach (string pattern in patterns.Split(';')) - { - files.AddRange(Directory.GetFiles(path, pattern, SearchOption.TopDirectoryOnly)); - } - - // go recurse in all sub-directories - foreach (var directory in Directory.GetDirectories(path)) - files.AddRange(FindFiles(directory, patterns)); - } - catch (UnauthorizedAccessException) { } - catch (PathTooLongException) { } - - return files; - } - - - // privesc checks - public static void GetModifiableServiceBinaries() - { - try - { - // finds any service binaries that the current can modify - // TODO: or modify the parent folder - - ManagementObjectSearcher wmiData = new ManagementObjectSearcher(@"root\cimv2", "SELECT * FROM win32_service"); - ManagementObjectCollection data = wmiData.Get(); - - Console.WriteLine("\r\n\r\n=== Modifiable Service Binaries ===\r\n"); - - foreach (ManagementObject result in data) - { - if (result["PathName"] != null) + } catch (Exception ex) { - Match path = Regex.Match(result["PathName"].ToString(), @"^\W*([a-z]:\\.+?(\.exe|\.dll|\.sys))\W*", RegexOptions.IgnoreCase); - String binaryPath = path.Groups[1].ToString(); - - if (CheckModifiableAccess(binaryPath)) - { - Console.WriteLine(" Name : {0}", result["Name"]); - Console.WriteLine(" DisplayName : {0}", result["DisplayName"]); - Console.WriteLine(" Description : {0}", result["Description"]); - Console.WriteLine(" State : {0}", result["State"]); - Console.WriteLine(" StartMode : {0}", result["StartMode"]); - Console.WriteLine(" PathName : {0}", result["PathName"]); - } + Console.WriteLine("[X] Unhandled exception in {0}: {1}", t.Name, ex.Message); } - } + }); + vulnThread.Start(); + runningThreads.Add(vulnThread); } - catch (Exception ex) - { - Console.WriteLine(String.Format(" [X] Exception: {0}", ex.Message)); - } - } - - public static void GetAlwaysInstallElevated() - { - Console.WriteLine("\r\n\r\n=== AlwaysInstallElevated Registry Keys ===\r\n"); - - string AlwaysInstallElevatedHKLM = GetRegValue("HKLM", "Software\\Policies\\Microsoft\\Windows\\Installer", "AlwaysInstallElevated"); - string AlwaysInstallElevatedHKCU = GetRegValue("HKCU", "Software\\Policies\\Microsoft\\Windows\\Installer", "AlwaysInstallElevated"); - - if (!string.IsNullOrEmpty(AlwaysInstallElevatedHKLM)) + foreach(Thread t in runningThreads) { - Console.WriteLine(" HKLM: {0}", AlwaysInstallElevatedHKLM); + t.Join(); } - if (!string.IsNullOrEmpty(AlwaysInstallElevatedHKCU)) + if (vulnerableChecks.Count == 0) { - Console.WriteLine(" HKCU: {0}", AlwaysInstallElevatedHKCU); - } - } - - public static void GetPathHijacks() - { - Console.WriteLine("\r\n\r\n=== Modifiable Folders in %PATH% ===\r\n"); - - // grabbed from the registry instead of System.Environment.GetEnvironmentVariable to prevent false positives - string path = GetRegValue("HKLM", "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "Path"); - string[] pathFolders = path.Split(';'); - - foreach (string pathFolder in pathFolders) + Console.WriteLine($"\r\n[-] Not vulnerable to any of the {checks.Length} checked modules."); + } else { - if (CheckModifiableAccess(pathFolder)) + foreach(VulnerabilityCheck c in vulnerableChecks) { - Console.WriteLine(" Modifable %PATH% Folder : {0}", pathFolder); - } - } - } - - public static void GetModifiableRegistryAutoRuns() - { - Console.WriteLine("\r\n\r\n=== Modifiable Registry Autoruns ===\r\n"); - - string[] autorunLocations = new string[] { - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunService", - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnceService", - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunService", - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnceService" - }; - - foreach (string autorunLocation in autorunLocations) - { - Dictionary settings = GetRegValues("HKLM", autorunLocation); - if ((settings != null) && (settings.Count != 0)) - { - foreach (KeyValuePair kvp in settings) + Console.WriteLine($"\r\n=== {c.Name()} ==="); + foreach(string s in c.Details()) { - Match path = Regex.Match(kvp.Value.ToString(), @"^\W*([a-z]:\\.+?(\.exe|\.bat|\.ps1|\.vbs))\W*", RegexOptions.IgnoreCase); - String binaryPath = path.Groups[1].ToString(); - - if (CheckModifiableAccess(binaryPath)) - { - Console.WriteLine(String.Format(" HKLM:\\{0} : {1}", autorunLocation, binaryPath)); - } + Console.WriteLine($"\t{s}"); } + Console.WriteLine(); } } } - public static void GetSpecialTokenGroupPrivs() + static Type[] GetAvailableChecks() { - // Returns all "special" privileges that the current process/user possesses - // adapted from https://stackoverflow.com/questions/4349743/setting-size-of-token-privileges-luid-and-attributes-array-returned-by-gettokeni - - Console.WriteLine("\r\n\r\n=== *Special* User Privileges ===\r\n"); - - string[] SpecialPrivileges = { - "SeSecurityPrivilege", "SeTakeOwnershipPrivilege", "SeLoadDriverPrivilege", - "SeBackupPrivilege", "SeRestorePrivilege", "SeDebugPrivilege", - "SeSystemEnvironmentPrivilege", "SeImpersonatePrivilege", "SeTcbPrivilege" - }; - - int TokenInfLength = 0; - IntPtr ThisHandle = WindowsIdentity.GetCurrent().Token; - GetTokenInformation(ThisHandle, TOKEN_INFORMATION_CLASS.TokenPrivileges, IntPtr.Zero, TokenInfLength, out TokenInfLength); - IntPtr TokenInformation = Marshal.AllocHGlobal(TokenInfLength); - if (GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenPrivileges, TokenInformation, TokenInfLength, out TokenInfLength)) - { - TOKEN_PRIVILEGES ThisPrivilegeSet = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(TokenInformation, typeof(TOKEN_PRIVILEGES)); - for (int index = 0; index < ThisPrivilegeSet.PrivilegeCount; index++) - { - LUID_AND_ATTRIBUTES laa = ThisPrivilegeSet.Privileges[index]; - System.Text.StringBuilder StrBuilder = new System.Text.StringBuilder(); - int LuidNameLen = 0; - IntPtr LuidPointer = Marshal.AllocHGlobal(Marshal.SizeOf(laa.Luid)); - Marshal.StructureToPtr(laa.Luid, LuidPointer, true); - LookupPrivilegeName(null, LuidPointer, null, ref LuidNameLen); - StrBuilder.EnsureCapacity(LuidNameLen + 1); - if (LookupPrivilegeName(null, LuidPointer, StrBuilder, ref LuidNameLen)) - { - string privilege = StrBuilder.ToString(); - foreach (string SpecialPrivilege in SpecialPrivileges) - { - if (privilege == SpecialPrivilege) - { - Console.WriteLine(String.Format(" {0,43}: {1}", privilege, (LuidAttributes)laa.Attributes)); - } - } - } - Marshal.FreeHGlobal(LuidPointer); - } - } + return Assembly.GetExecutingAssembly().GetTypes() + .Where(t => t.Namespace == "SharpUp.Checks") + .ToArray(); } - public static void GetModifiableServices() + static Type[] GetChecksFromArgumentString(string[] args) { - // finds any services that the current can modify (or modify the parent folder) - // modified from https://stackoverflow.com/questions/15771998/how-to-give-a-user-permission-to-start-and-stop-a-particular-service-using-c-sha/15796352#15796352 - - ServiceController[] scServices; - scServices = ServiceController.GetServices(); - - var GetServiceHandle = typeof(System.ServiceProcess.ServiceController).GetMethod("GetServiceHandle", BindingFlags.Instance | BindingFlags.NonPublic); - - object[] readRights = { 0x00020000 }; - - ServiceAccessRights[] ModifyRights = + Type[] allChecks = GetAvailableChecks(); + List checks = new List(); + if (args.Contains("audit")) { - ServiceAccessRights.ChangeConfig, - ServiceAccessRights.WriteDac, - ServiceAccessRights.WriteOwner, - ServiceAccessRights.GenericAll, - ServiceAccessRights.GenericWrite, - ServiceAccessRights.AllAccess - }; - - - Console.WriteLine("\r\n\r\n=== Modifiable Services ===\r\n"); - - foreach (ServiceController sc in scServices) - { - try + auditMode = true; + if (args.Length == 1) { - IntPtr handle = (IntPtr)GetServiceHandle.Invoke(sc, readRights); - ServiceControllerStatus status = sc.Status; - byte[] psd = new byte[0]; - uint bufSizeNeeded; - bool ok = QueryServiceObjectSecurity(handle, SecurityInfos.DiscretionaryAcl, psd, 0, out bufSizeNeeded); - - if (!ok) - { - int err = Marshal.GetLastWin32Error(); - if (err == 122 || err == 0) - { // ERROR_INSUFFICIENT_BUFFER - // expected; now we know bufsize - psd = new byte[bufSizeNeeded]; - ok = QueryServiceObjectSecurity(handle, SecurityInfos.DiscretionaryAcl, psd, bufSizeNeeded, out bufSizeNeeded); - } - else - { - //throw new ApplicationException("error calling QueryServiceObjectSecurity() to get DACL for " + _name + ": error code=" + err); - continue; - } - } - if (!ok) - { - //throw new ApplicationException("error calling QueryServiceObjectSecurity(2) to get DACL for " + _name + ": error code=" + Marshal.GetLastWin32Error()); - continue; - } - - // get security descriptor via raw into DACL form so ACE ordering checks are done for us. - RawSecurityDescriptor rsd = new RawSecurityDescriptor(psd, 0); - RawAcl racl = rsd.DiscretionaryAcl; - DiscretionaryAcl dacl = new DiscretionaryAcl(false, false, racl); - - WindowsIdentity identity = WindowsIdentity.GetCurrent(); - - foreach (System.Security.AccessControl.CommonAce ace in dacl) - { - if (identity.Groups.Contains(ace.SecurityIdentifier)) - { - ServiceAccessRights serviceRights = (ServiceAccessRights)ace.AccessMask; - foreach (ServiceAccessRights ModifyRight in ModifyRights) - { - if ((ModifyRight & serviceRights) == ModifyRight) - { - ManagementObjectSearcher wmiData = new ManagementObjectSearcher(@"root\cimv2", String.Format("SELECT * FROM win32_service WHERE Name LIKE '{0}'", sc.ServiceName)); - ManagementObjectCollection data = wmiData.Get(); - - foreach (ManagementObject result in data) - { - Console.WriteLine(" Name : {0}", result["Name"]); - Console.WriteLine(" DisplayName : {0}", result["DisplayName"]); - Console.WriteLine(" Description : {0}", result["Description"]); - Console.WriteLine(" State : {0}", result["State"]); - Console.WriteLine(" StartMode : {0}", result["StartMode"]); - Console.WriteLine(" PathName : {0}", result["PathName"]); - } - break; - } - } - } - } - } - catch (Exception ex) - { - // Console.WriteLine("Exception: " + ex); + return allChecks; } } - } - public static void GetUnattendedInstallFiles() - { - try + foreach(string arg in args) { - Console.WriteLine("\r\n\r\n=== Unattended Install Files ===\r\n"); - - string windir = System.Environment.GetEnvironmentVariable("windir"); - string[] SearchLocations = - { - String.Format("{0}\\sysprep\\sysprep.xml", windir), - String.Format("{0}\\sysprep\\sysprep.inf", windir), - String.Format("{0}\\sysprep.inf", windir), - String.Format("{0}\\Panther\\Unattended.xml", windir), - String.Format("{0}\\Panther\\Unattend.xml", windir), - String.Format("{0}\\Panther\\Unattend\\Unattend.xml", windir), - String.Format("{0}\\Panther\\Unattend\\Unattended.xml", windir), - String.Format("{0}\\System32\\Sysprep\\unattend.xml", windir), - String.Format("{0}\\System32\\Sysprep\\Panther\\unattend.xml", windir) - }; - - foreach (string SearchLocation in SearchLocations) + foreach(Type t in allChecks) { - if (System.IO.File.Exists(SearchLocation)) + if (t.Name.ToLower() == arg.ToLower()) { - Console.WriteLine(" {0}", SearchLocation); + checks.Add(t); } } } - catch (Exception ex) - { - Console.WriteLine(String.Format(" [X] Exception: {0}", ex.Message)); - } + return checks.ToArray(); } - - public static void GetMcAfeeSitelistFiles() + static void Usage() { - try - { - Console.WriteLine("\r\n\r\n=== McAfee Sitelist.xml Files ===\r\n"); + Type[] checks = GetAvailableChecks(); + string[] checkNames = checks.Select(check => check.Name + "\n").ToArray(); - string drive = System.Environment.GetEnvironmentVariable("SystemDrive"); + string strCheck = string.Join(" - ", checkNames); + string usageString = @" +SharpUp.exe [audit] [check1] [check2]... - string[] SearchLocations = - { - String.Format("{0}\\Program Files\\", drive), - String.Format("{0}\\Program Files (x86)\\", drive), - String.Format("{0}\\Documents and Settings\\", drive), - String.Format("{0}\\Users\\", drive) - }; + audit - Specifies whether or not to enable audit mode. If enabled, SharpUp will run vulenrability checks + regardless if the process is in high integrity or the user is in the local administrator's group. + If no checks are specified, audit will run all checks. Otherwise, each check following audit will + be ran. - foreach (string SearchLocation in SearchLocations) - { - List files = FindFiles(SearchLocation, "SiteList.xml"); + check* - The individual vulnerability check to be ran. Must be one of the following: - foreach (string file in files) - { - Console.WriteLine(" {0}", file); - } - } - } - catch (Exception ex) - { - Console.WriteLine(String.Format(" [X] Exception: {0}", ex.Message)); - } - } - - - public static void GetCachedGPPPassword() - { - try - { - Console.WriteLine("\r\n\r\n=== Cached GPP Password ===\r\n"); - - string allUsers = System.Environment.GetEnvironmentVariable("ALLUSERSPROFILE"); - - if (!allUsers.Contains("ProgramData")) - { - // Before Windows Vista, the default value of AllUsersProfile was "C:\Documents and Settings\All Users" - // And after, "C:\ProgramData" - allUsers += "\\Application Data"; - } - allUsers += "\\Microsoft\\Group Policy\\History"; // look only in the GPO cache folder - - List files = FindFiles(allUsers, "*.xml"); - - // files will contain all XML files - foreach (string file in files) - { - if (!(file.Contains("Groups.xml") || file.Contains("Services.xml") - || file.Contains("Scheduledtasks.xml") || file.Contains("DataSources.xml") - || file.Contains("Printers.xml") || file.Contains("Drives.xml"))) - { - continue; // uninteresting XML files, move to next - } - - XmlDocument xmlDoc = new XmlDocument(); - xmlDoc.Load(file); - - if (!xmlDoc.InnerXml.Contains("cpassword")) - { - continue; // no "cpassword" => no interesting content, move to next - } - - Console.WriteLine("\r\n{0}", file); - - string cPassword = ""; - string UserName = ""; - string NewName = ""; - string Changed = ""; - if (file.Contains("Groups.xml")) - { - XmlNode a = xmlDoc.SelectSingleNode("/Groups/User/Properties"); - XmlNode b = xmlDoc.SelectSingleNode("/Groups/User"); - foreach (XmlAttribute attr in a.Attributes) - { - if (attr.Name.Equals("cpassword")) - { - cPassword = attr.Value; - } - if (attr.Name.Equals("userName")) - { - UserName = attr.Value; - } - if (attr.Name.Equals("newName")) - { - NewName = attr.Value; - } - } - foreach (XmlAttribute attr in b.Attributes) - { - if (attr.Name.Equals("changed")) - { - Changed = attr.Value; - } - } - //Console.WriteLine("\r\nA{0}", a.Attributes[0].Value); - } - else if (file.Contains("Services.xml")) - { - XmlNode a = xmlDoc.SelectSingleNode("/NTServices/NTService/Properties"); - XmlNode b = xmlDoc.SelectSingleNode("/NTServices/NTService"); - foreach (XmlAttribute attr in a.Attributes) - { - if (attr.Name.Equals("cpassword")) - { - cPassword = attr.Value; - } - if (attr.Name.Equals("accountName")) - { - UserName = attr.Value; - } - } - foreach (XmlAttribute attr in b.Attributes) - { - if (attr.Name.Equals("changed")) - { - Changed = attr.Value; - } - } - - } - else if (file.Contains("Scheduledtasks.xml")) - { - XmlNode a = xmlDoc.SelectSingleNode("/ScheduledTasks/Task/Properties"); - XmlNode b = xmlDoc.SelectSingleNode("/ScheduledTasks/Task"); - foreach (XmlAttribute attr in a.Attributes) - { - if (attr.Name.Equals("cpassword")) - { - cPassword = attr.Value; - } - if (attr.Name.Equals("runAs")) - { - UserName = attr.Value; - } - } - foreach (XmlAttribute attr in b.Attributes) - { - if (attr.Name.Equals("changed")) - { - Changed = attr.Value; - } - } - - } - else if (file.Contains("DataSources.xml")) - { - XmlNode a = xmlDoc.SelectSingleNode("/DataSources/DataSource/Properties"); - XmlNode b = xmlDoc.SelectSingleNode("/DataSources/DataSource"); - foreach (XmlAttribute attr in a.Attributes) - { - if (attr.Name.Equals("cpassword")) - { - cPassword = attr.Value; - } - if (attr.Name.Equals("username")) - { - UserName = attr.Value; - } - } - foreach (XmlAttribute attr in b.Attributes) - { - if (attr.Name.Equals("changed")) - { - Changed = attr.Value; - } - } - } - else if (file.Contains("Printers.xml")) - { - XmlNode a = xmlDoc.SelectSingleNode("/Printers/SharedPrinter/Properties"); - XmlNode b = xmlDoc.SelectSingleNode("/Printers/SharedPrinter"); - foreach (XmlAttribute attr in a.Attributes) - { - if (attr.Name.Equals("cpassword")) - { - cPassword = attr.Value; - } - if (attr.Name.Equals("username")) - { - UserName = attr.Value; - } - } - foreach (XmlAttribute attr in b.Attributes) - { - if (attr.Name.Equals("changed")) - { - Changed = attr.Value; - } - } - } - else - { - // Drives.xml - XmlNode a = xmlDoc.SelectSingleNode("/Drives/Drive/Properties"); - XmlNode b = xmlDoc.SelectSingleNode("/Drives/Drive"); - foreach (XmlAttribute attr in a.Attributes) - { - if (attr.Name.Equals("cpassword")) - { - cPassword = attr.Value; - } - if (attr.Name.Equals("username")) - { - UserName = attr.Value; - } - } - foreach (XmlAttribute attr in b.Attributes) - { - if (attr.Name.Equals("changed")) - { - Changed = attr.Value; - } - } - - } - - if (UserName.Equals("")) - { - UserName = "[BLANK]"; - } - - if (NewName.Equals("")) - { - NewName = "[BLANK]"; - } - - - if (cPassword.Equals("")) - { - cPassword = "[BLANK]"; - } - else - { - cPassword = DecryptGPP(cPassword); - } - - if (Changed.Equals("")) - { - Changed = "[BLANK]"; - } - - - Console.WriteLine("UserName: {0}", UserName); - Console.WriteLine("NewName: {0}", NewName); - Console.WriteLine("cPassword: {0}", cPassword); - Console.WriteLine("Changed: {0}", Changed); - } - } - catch (Exception ex) - { - Console.WriteLine(String.Format(" [X] Exception: {0}", ex.Message)); - } - } - - - public static string DecryptGPP(string cpassword) - { - int mod = cpassword.Length % 4; - - switch (mod) - { - case 1: - cpassword = cpassword.Substring(0, cpassword.Length - 1); - break; - case 2: - cpassword += "".PadLeft(4 - mod, '='); - break; - case 3: - cpassword += "".PadLeft(4 - mod, '='); - break; - default: - break; - } - - byte[] base64decoded = Convert.FromBase64String(cpassword); + - {0} - AesCryptoServiceProvider aesObject = new AesCryptoServiceProvider(); - - byte[] aesKey = { 0x4e, 0x99, 0x06, 0xe8, 0xfc, 0xb6, 0x6c, 0xc9, 0xfa, 0xf4, 0x93, 0x10, 0x62, 0x0f, 0xfe, 0xe8, 0xf4, 0x96, 0xe8, 0x06, 0xcc, 0x05, 0x79, 0x90, 0x20, 0x9b, 0x09, 0xa4, 0x33, 0xb6, 0x6c, 0x1b }; - byte[] aesIV = new byte[aesObject.IV.Length]; - - aesObject.IV = aesIV; - aesObject.Key = aesKey; - ICryptoTransform aesDecryptor = aesObject.CreateDecryptor(); - byte[] outBlock = aesDecryptor.TransformFinalBlock(base64decoded, 0, base64decoded.Length); + Examples: + SharpUp.exe audit + -> Runs all vulnerability checks regardless of integrity level or group membership. + + SharpUp.exe HijackablePaths + -> Check only if there are modifiable paths in the user's %PATH% variable. - return System.Text.UnicodeEncoding.Unicode.GetString(outBlock); + SharpUp.exe audit HijackablePaths + -> Check only for modifiable paths in the user's %PATH% regardless of integrity level or group membership. +"; + Console.WriteLine(string.Format(usageString, strCheck)); } - public static void PrivescChecks(bool auditMode) + static void Main(string[] args) { - bool isHighIntegrity = IsHighIntegrity(); - bool isLocalAdmin = IsLocalAdmin(); - bool shouldQuit = false; - - if (isHighIntegrity) + if (args.Length == 0 || args[0] == "-h" || args[0] == "--help") { - Console.WriteLine("\r\n[*] Already in high integrity, no need to privesc!"); - shouldQuit = true; + Usage(); + return; } - else if (!isHighIntegrity && isLocalAdmin) - { - Console.WriteLine("\r\n[*] In medium integrity but user is a local administrator- UAC can be bypassed."); - shouldQuit = true; - } - - // if already admin we can quit without running all checks - if (shouldQuit) - { - if (!auditMode) - { - Console.WriteLine("\r\n[*] Quitting now, re-run with \"audit\" argument to run all checks anyway (audit mode)."); - return; - } - else - { - // except if auditMode has explictly been asked - Console.WriteLine("\r\n[*] Audit mode: running all checks anyway."); - } - } - - GetModifiableServices(); - GetModifiableServiceBinaries(); - GetAlwaysInstallElevated(); - GetPathHijacks(); - GetModifiableRegistryAutoRuns(); - GetSpecialTokenGroupPrivs(); - GetUnattendedInstallFiles(); - GetMcAfeeSitelistFiles(); - GetCachedGPPPassword(); - } - - static void Main(string[] args) - { - bool auditMode = args.Contains("audit", StringComparer.CurrentCultureIgnoreCase); + Type[] checks = GetChecksFromArgumentString(args); var watch = System.Diagnostics.Stopwatch.StartNew(); Console.WriteLine("\r\n=== SharpUp: Running Privilege Escalation Checks ==="); - PrivescChecks(auditMode); + PrivescChecks(checks); watch.Stop(); Console.WriteLine(String.Format("\r\n\r\n[*] Completed Privesc Checks in {0} seconds\r\n", watch.ElapsedMilliseconds / 1000)); diff --git a/SharpUp/SharpUp.csproj b/SharpUp/SharpUp.csproj index 644cdd5..9ea7ba3 100755 --- a/SharpUp/SharpUp.csproj +++ b/SharpUp/SharpUp.csproj @@ -44,8 +44,29 @@ + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/SharpUp/Utilities/FileUtils.cs b/SharpUp/Utilities/FileUtils.cs new file mode 100644 index 0000000..fe5b3e3 --- /dev/null +++ b/SharpUp/Utilities/FileUtils.cs @@ -0,0 +1,404 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.AccessControl; +using System.Security.Cryptography; +using System.Security.Principal; +using System.Text; +using System.Xml; + +namespace SharpUp.Utilities +{ + public static class FileUtils + { + public struct GPPPassword + { + public string UserName; + public string NewName; + public string cPassword; + public string Changed; + + public override string ToString() + { + string _uname = UserName; + string _newname = NewName; + string _cp = cPassword; + string _changed = Changed; + if (string.IsNullOrEmpty(_uname)) + _uname = "[BLANK]"; + if (string.IsNullOrEmpty(_newname)) + _newname = "[BLANK]"; + if (string.IsNullOrEmpty(_cp)) + _cp = "[BLANK]"; + else + { + // will fail on certain XML files like Registry.xml + try + { + _cp = DecryptGPPPassword(_cp); + } catch { } + } + if (string.IsNullOrEmpty(_changed)) + _changed = "[BLANK]"; + return $"UserName: {_uname} | NewName: {_newname} | cPassword: {_cp} | Changed: {_changed}"; + } + } + + public static bool ParseGPPPasswordFromXml(string filePath, out GPPPassword result) + { + result = new GPPPassword(); + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.Load(filePath); + if (!xmlDoc.InnerXml.Contains("cpassword")) + { + return false; // no "cpassword" => no interesting content, move to next + } + if (filePath.Contains("Groups.xml")) + { + XmlNode a = xmlDoc.SelectSingleNode("/Groups/User/Properties"); + XmlNode b = xmlDoc.SelectSingleNode("/Groups/User"); + foreach (XmlAttribute attr in a.Attributes) + { + if (attr.Name.Equals("cpassword")) + { + result.cPassword = attr.Value; + } + if (attr.Name.Equals("userName")) + { + result.UserName = attr.Value; + } + if (attr.Name.Equals("newName")) + { + result.NewName = attr.Value; + } + } + foreach (XmlAttribute attr in b.Attributes) + { + if (attr.Name.Equals("changed")) + { + result.Changed = attr.Value; + } + } + //Console.WriteLine("\r\nA{0}", a.Attributes[0].Value); + } + else if (filePath.Contains("Services.xml")) + { + XmlNode a = xmlDoc.SelectSingleNode("/NTServices/NTService/Properties"); + XmlNode b = xmlDoc.SelectSingleNode("/NTServices/NTService"); + foreach (XmlAttribute attr in a.Attributes) + { + if (attr.Name.Equals("cpassword")) + { + result.cPassword = attr.Value; + } + if (attr.Name.Equals("accountName")) + { + result.UserName = attr.Value; + } + } + foreach (XmlAttribute attr in b.Attributes) + { + if (attr.Name.Equals("changed")) + { + result.Changed = attr.Value; + } + } + + } + else if (filePath.Contains("Registry.xml")) + { + XmlNodeList a = xmlDoc.GetElementsByTagName("Properties"); + + foreach (XmlNode b in a) + { + if (b.Name.Equals("DefaultPassword")) + { + result.cPassword += "," + b.Value; + } + if (b.Name.Equals("DefaultUsername")) + { + result.UserName += "," + b.Value; + } + } + } + else if (filePath.Contains("Scheduledtasks.xml")) + { + XmlNode a = xmlDoc.SelectSingleNode("/ScheduledTasks/Task/Properties"); + XmlNode b = xmlDoc.SelectSingleNode("/ScheduledTasks/Task"); + foreach (XmlAttribute attr in a.Attributes) + { + if (attr.Name.Equals("cpassword")) + { + result.cPassword = attr.Value; + } + if (attr.Name.Equals("runAs")) + { + result.UserName = attr.Value; + } + } + foreach (XmlAttribute attr in b.Attributes) + { + if (attr.Name.Equals("changed")) + { + result.Changed = attr.Value; + } + } + + } + else if (filePath.Contains("DataSources.xml")) + { + XmlNode a = xmlDoc.SelectSingleNode("/DataSources/DataSource/Properties"); + XmlNode b = xmlDoc.SelectSingleNode("/DataSources/DataSource"); + foreach (XmlAttribute attr in a.Attributes) + { + if (attr.Name.Equals("cpassword")) + { + result.cPassword = attr.Value; + } + if (attr.Name.Equals("username")) + { + result.UserName = attr.Value; + } + } + foreach (XmlAttribute attr in b.Attributes) + { + if (attr.Name.Equals("changed")) + { + result.Changed = attr.Value; + } + } + } + else if (filePath.Contains("Printers.xml")) + { + XmlNode a = xmlDoc.SelectSingleNode("/Printers/SharedPrinter/Properties"); + XmlNode b = xmlDoc.SelectSingleNode("/Printers/SharedPrinter"); + foreach (XmlAttribute attr in a.Attributes) + { + if (attr.Name.Equals("cpassword")) + { + result.cPassword = attr.Value; + } + if (attr.Name.Equals("username")) + { + result.UserName = attr.Value; + } + } + foreach (XmlAttribute attr in b.Attributes) + { + if (attr.Name.Equals("changed")) + { + result.Changed = attr.Value; + } + } + } + else if (filePath.Contains("Drives.xml")) + { + // Drives.xml + XmlNode a = xmlDoc.SelectSingleNode("/Drives/Drive/Properties"); + XmlNode b = xmlDoc.SelectSingleNode("/Drives/Drive"); + foreach (XmlAttribute attr in a.Attributes) + { + if (attr.Name.Equals("cpassword")) + { + result.cPassword = attr.Value; + } + if (attr.Name.Equals("username")) + { + result.UserName = attr.Value; + } + } + foreach (XmlAttribute attr in b.Attributes) + { + if (attr.Name.Equals("changed")) + { + result.Changed = attr.Value; + } + } + } else + { + throw new Exception("Unexpected code path."); + } + return true; + } + + public static string DecryptGPPPassword(string cpassword) + { + int mod = cpassword.Length % 4; + + switch (mod) + { + case 1: + cpassword = cpassword.Substring(0, cpassword.Length - 1); + break; + case 2: + cpassword += "".PadLeft(4 - mod, '='); + break; + case 3: + cpassword += "".PadLeft(4 - mod, '='); + break; + default: + break; + } + + byte[] base64decoded = Convert.FromBase64String(cpassword); + + AesCryptoServiceProvider aesObject = new AesCryptoServiceProvider(); + + byte[] aesKey = { 0x4e, 0x99, 0x06, 0xe8, 0xfc, 0xb6, 0x6c, 0xc9, 0xfa, 0xf4, 0x93, 0x10, 0x62, 0x0f, 0xfe, 0xe8, 0xf4, 0x96, 0xe8, 0x06, 0xcc, 0x05, 0x79, 0x90, 0x20, 0x9b, 0x09, 0xa4, 0x33, 0xb6, 0x6c, 0x1b }; + byte[] aesIV = new byte[aesObject.IV.Length]; + + aesObject.IV = aesIV; + aesObject.Key = aesKey; + + ICryptoTransform aesDecryptor = aesObject.CreateDecryptor(); + byte[] outBlock = aesDecryptor.TransformFinalBlock(base64decoded, 0, base64decoded.Length); + + return System.Text.UnicodeEncoding.Unicode.GetString(outBlock); + } + + public static bool CheckAccess(string Path, FileSystemRights AccessRight) + { + // checks if the current user has the specified AccessRight to the specified file or folder + // from https://stackoverflow.com/questions/1410127/c-sharp-test-if-user-has-write-access-to-a-folder/21996345#21996345 + + if (string.IsNullOrEmpty(Path)) return false; + + try + { + AuthorizationRuleCollection rules = Directory.GetAccessControl(Path).GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)); + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + WindowsPrincipal principal = new WindowsPrincipal(identity); + foreach (FileSystemAccessRule rule in rules) + { + + var ruleNtAccount = rule.IdentityReference.Translate(typeof(NTAccount)); + if (identity.Groups.Contains(rule.IdentityReference) || + principal.IsInRole(ruleNtAccount.Value) || + ruleNtAccount.Value == identity.Name) + { + if ((AccessRight & rule.FileSystemRights) == AccessRight) + { + if (rule.AccessControlType == AccessControlType.Allow) + return true; + } + } + } + } + catch { } + + return false; + } + + public static List FindFiles(string path, string patterns) + { + // finds files matching one or more patterns under a given path, recursive + // adapted from http://csharphelper.com/blog/2015/06/find-files-that-match-multiple-patterns-in-c/ + // pattern: "*pass*;*.png;" + + var files = new List(); + + try + { + // search every pattern in this directory's files + foreach (string pattern in patterns.Split(';')) + { + files.AddRange(Directory.GetFiles(path, pattern, SearchOption.TopDirectoryOnly)); + } + + // go recurse in all sub-directories + foreach (var directory in Directory.GetDirectories(path)) + files.AddRange(FindFiles(directory, patterns)); + } + catch (UnauthorizedAccessException) { } + catch (PathTooLongException) { } + + return files; + } + + public static bool CheckModifiableAccess(string Path, bool FileRightsOnly = false) + { + // checks if the current user has rights to modify the given file/directory + // adapted from https://stackoverflow.com/questions/1410127/c-sharp-test-if-user-has-write-access-to-a-folder/21996345#21996345 + + if (string.IsNullOrEmpty(Path)) return false; + // TODO: check if file exists, check file's parent folder + + // rights that signify modiable access + FileSystemRights[] ModifyRights = + { + FileSystemRights.ChangePermissions, + FileSystemRights.FullControl, + FileSystemRights.Modify, + FileSystemRights.TakeOwnership, + FileSystemRights.Write, + FileSystemRights.WriteData, + FileSystemRights.CreateDirectories, + FileSystemRights.CreateFiles + }; + + if (FileRightsOnly) + { + FileSystemRights[] ModifyRightsOnlyFiles = + { + FileSystemRights.FullControl, + FileSystemRights.Modify, + FileSystemRights.TakeOwnership, + FileSystemRights.Write, + FileSystemRights.WriteData, + FileSystemRights.CreateFiles + }; + ModifyRights = ModifyRightsOnlyFiles; + } + + ArrayList paths = new ArrayList(); + paths.Add(Path); + + try + { + FileAttributes attr = System.IO.File.GetAttributes(Path); + if ((attr & FileAttributes.Directory) != FileAttributes.Directory) + { + string parentFolder = System.IO.Path.GetDirectoryName(Path); + paths.Add(parentFolder); + } + } + catch + { + return false; + } + + + try + { + foreach (string candidatePath in paths) + { + AuthorizationRuleCollection rules = Directory.GetAccessControl(candidatePath).GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)); + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + + foreach (FileSystemAccessRule rule in rules) + { + if (identity.Groups.Contains(rule.IdentityReference) || + rule.IdentityReference == identity.User) + { + foreach (FileSystemRights AccessRight in ModifyRights) + { + if ((AccessRight & rule.FileSystemRights) == AccessRight) + { + if (rule.AccessControlType == AccessControlType.Allow) + return true; + } + } + } + } + } + return false; + } + catch + { + return false; + } + } + } +} diff --git a/SharpUp/Utilities/IdentityUtils.cs b/SharpUp/Utilities/IdentityUtils.cs new file mode 100644 index 0000000..fa690dd --- /dev/null +++ b/SharpUp/Utilities/IdentityUtils.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Principal; +using System.Text; +using static SharpUp.Native.Win32; + +namespace SharpUp.Utilities +{ + public static class IdentityUtils + { + public static bool IsHighIntegrity() + { + // returns true if the current process is running with adminstrative privs in a high integrity context + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + WindowsPrincipal principal = new WindowsPrincipal(identity); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + + public static bool IsLocalAdmin() + { + // checks if the "S-1-5-32-544" in the current token groups set, meaning the user is a local administrator + string[] SIDs = GetTokenGroupSIDs(); + + foreach (string SID in SIDs) + { + if (SID == "S-1-5-32-544") + { + return true; + } + } + return false; + } + + public static string[] GetTokenGroupSIDs() + { + // Returns all SIDs that the current user is a part of, whether they are disabled or not. + + // adapted almost directly from https://stackoverflow.com/questions/2146153/how-to-get-the-logon-sid-in-c-sharp/2146418#2146418 + + int TokenInfLength = 0; + + // first call gets length of TokenInformation + bool Result = GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenGroups, IntPtr.Zero, TokenInfLength, out TokenInfLength); + IntPtr TokenInformation = Marshal.AllocHGlobal(TokenInfLength); + Result = GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenGroups, TokenInformation, TokenInfLength, out TokenInfLength); + + if (!Result) + { + Marshal.FreeHGlobal(TokenInformation); + return null; + } + + TOKEN_GROUPS groups = (TOKEN_GROUPS)Marshal.PtrToStructure(TokenInformation, typeof(TOKEN_GROUPS)); + string[] userSIDS = new string[groups.GroupCount]; + int sidAndAttrSize = Marshal.SizeOf(new SID_AND_ATTRIBUTES()); + for (int i = 0; i < groups.GroupCount; i++) + { + SID_AND_ATTRIBUTES sidAndAttributes = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure( + new IntPtr(TokenInformation.ToInt64() + i * sidAndAttrSize + IntPtr.Size), typeof(SID_AND_ATTRIBUTES)); + + IntPtr pstr = IntPtr.Zero; + ConvertSidToStringSid(sidAndAttributes.Sid, out pstr); + userSIDS[i] = Marshal.PtrToStringAuto(pstr); + LocalFree(pstr); + } + + Marshal.FreeHGlobal(TokenInformation); + return userSIDS; + } + } +} diff --git a/SharpUp/Utilities/RegistryUtils.cs b/SharpUp/Utilities/RegistryUtils.cs new file mode 100644 index 0000000..2b4998a --- /dev/null +++ b/SharpUp/Utilities/RegistryUtils.cs @@ -0,0 +1,149 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.AccessControl; +using System.Security.Principal; +using System.Text; + +namespace SharpUp.Utilities +{ + public static class RegistryUtils + { + public static string GetRegValue(string hive, string path, string value) + { + // returns a single registry value under the specified path in the specified hive (HKLM/HKCU) + string regKeyValue = ""; + if (hive == "HKCU") + { + var regKey = Registry.CurrentUser.OpenSubKey(path); + if (regKey != null) + { + regKeyValue = String.Format("{0}", regKey.GetValue(value)); + } + return regKeyValue; + } + else if (hive == "HKU") + { + var regKey = Registry.Users.OpenSubKey(path); + if (regKey != null) + { + regKeyValue = String.Format("{0}", regKey.GetValue(value)); + } + return regKeyValue; + } + else + { + var regKey = Registry.LocalMachine.OpenSubKey(path); + if (regKey != null) + { + regKeyValue = String.Format("{0}", regKey.GetValue(value)); + } + return regKeyValue; + } + } + + public static Dictionary GetRegValues(string hive, string path) + { + // returns all registry values under the specified path in the specified hive (HKLM/HKCU) + Dictionary keyValuePairs = null; + + if (hive == "HKCU") + { + using (var regKeyValues = Registry.CurrentUser.OpenSubKey(path)) + { + if (regKeyValues != null) + { + var valueNames = regKeyValues.GetValueNames(); + keyValuePairs = valueNames.ToDictionary(name => name, regKeyValues.GetValue); + } + } + } + else if (hive == "HKU") + { + using (var regKeyValues = Registry.Users.OpenSubKey(path)) + { + if (regKeyValues != null) + { + var valueNames = regKeyValues.GetValueNames(); + keyValuePairs = valueNames.ToDictionary(name => name, regKeyValues.GetValue); + } + } + } + else + { + using (var regKeyValues = Registry.LocalMachine.OpenSubKey(path)) + { + if (regKeyValues != null) + { + var valueNames = regKeyValues.GetValueNames(); + keyValuePairs = valueNames.ToDictionary(name => name, regKeyValues.GetValue); + } + } + } + return keyValuePairs; + } + + public static string[] GetRegSubkeys(string hive, string path) + { + // returns an array of the subkeys names under the specified path in the specified hive (HKLM/HKCU/HKU) + try + { + Microsoft.Win32.RegistryKey myKey = null; + if (hive == "HKLM") + { + myKey = Registry.LocalMachine.OpenSubKey(path); + } + else if (hive == "HKU") + { + myKey = Registry.Users.OpenSubKey(path); + } + else + { + myKey = Registry.CurrentUser.OpenSubKey(path); + } + String[] subkeyNames = myKey.GetSubKeyNames(); + return myKey.GetSubKeyNames(); + } + catch + { + return new string[0]; + } + } + + public static bool IsModifiableKey(RegistryKey key) + { + RegistryRights[] ModifyRights = + { + RegistryRights.ChangePermissions, + RegistryRights.FullControl, + RegistryRights.TakeOwnership, + RegistryRights.SetValue, + RegistryRights.WriteKey + }; + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + + AuthorizationRuleCollection rules = key.GetAccessControl().GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)); + + foreach (RegistryAccessRule rule in rules) + { + if (identity.Groups.Contains(rule.IdentityReference) || rule.IdentityReference == identity.User) + { + foreach (RegistryRights AccessRight in ModifyRights) + { + if ((AccessRight & rule.RegistryRights) == AccessRight) + { + if (rule.AccessControlType == AccessControlType.Allow) + { + return true; + } + + } + } + } + } + return false; + } + + } +}