@@ -23,6 +23,11 @@ so I created the NoUI flag. ALSO: When creating a process, the script will reque
23
23
This could show up in logs depending on the level of monitoring.
24
24
25
25
26
+ PERMISSIONS REQUIRED:
27
+ SeSecurityPrivilege: Needed if launching a process with a UI that needs to be rendered. Using the -NoUI flag blocks this.
28
+ SeAssignPrimaryTokenPrivilege : Needed if launching a process while the script is running in Session 0.
29
+
30
+
26
31
Important differences from incognito:
27
32
First of all, you should probably read the incognito white paper to understand what incognito does. If you use incognito, you'll notice it differentiates
28
33
between "Impersonation" and "Delegation" tokens. This is because incognito can be used in situations where you get remote code execution against a service
@@ -44,7 +49,7 @@ Author: Joe Bialek, Twitter: @JosephBialek
44
49
License: BSD 3-Clause
45
50
Required Dependencies: None
46
51
Optional Dependencies: None
47
- Version: 1.0
52
+ Version: 1.1
48
53
49
54
. DESCRIPTION
50
55
@@ -730,8 +735,8 @@ Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-toke
730
735
}
731
736
732
737
733
- # Enable SeSecurityPrivilege , needed to query security information for desktop DACL
734
- function Enable-SeSecurityPrivilege
738
+ # Enable SeAssignPrimaryTokenPrivilege , needed to query security information for desktop DACL
739
+ function Enable-SeAssignPrimaryTokenPrivilege
735
740
{
736
741
[IntPtr ]$ThreadHandle = $GetCurrentThread.Invoke ()
737
742
if ($ThreadHandle -eq [IntPtr ]::Zero)
@@ -772,7 +777,97 @@ Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-toke
772
777
$LuidObject = [System.Runtime.InteropServices.Marshal ]::PtrToStructure($LuidPtr , [Type ]$LUID )
773
778
[System.Runtime.InteropServices.Marshal ]::FreeHGlobal($LuidPtr )
774
779
775
- $Result = $LookupPrivilegeValue.Invoke ($null , " SeSecurityPrivilege" , [Ref ] $LuidObject )
780
+ $Result = $LookupPrivilegeValue.Invoke ($null , " SeAssignPrimaryTokenPrivilege" , [Ref ] $LuidObject )
781
+
782
+ if ($Result -eq $false )
783
+ {
784
+ Throw (New-Object ComponentModel.Win32Exception)
785
+ }
786
+
787
+ [UInt32 ]$LuidAndAttributesSize = [System.Runtime.InteropServices.Marshal ]::SizeOf([Type ]$LUID_AND_ATTRIBUTES )
788
+ $LuidAndAttributesPtr = [System.Runtime.InteropServices.Marshal ]::AllocHGlobal($LuidAndAttributesSize )
789
+ $LuidAndAttributes = [System.Runtime.InteropServices.Marshal ]::PtrToStructure($LuidAndAttributesPtr , [Type ]$LUID_AND_ATTRIBUTES )
790
+ [System.Runtime.InteropServices.Marshal ]::FreeHGlobal($LuidAndAttributesPtr )
791
+
792
+ $LuidAndAttributes.Luid = $LuidObject
793
+ $LuidAndAttributes.Attributes = $Win32Constants.SE_PRIVILEGE_ENABLED
794
+
795
+ [UInt32 ]$TokenPrivSize = [System.Runtime.InteropServices.Marshal ]::SizeOf([Type ]$TOKEN_PRIVILEGES )
796
+ $TokenPrivilegesPtr = [System.Runtime.InteropServices.Marshal ]::AllocHGlobal($TokenPrivSize )
797
+ $TokenPrivileges = [System.Runtime.InteropServices.Marshal ]::PtrToStructure($TokenPrivilegesPtr , [Type ]$TOKEN_PRIVILEGES )
798
+ [System.Runtime.InteropServices.Marshal ]::FreeHGlobal($TokenPrivilegesPtr )
799
+ $TokenPrivileges.PrivilegeCount = 1
800
+ $TokenPrivileges.Privileges = $LuidAndAttributes
801
+
802
+ $Global :TokenPriv = $TokenPrivileges
803
+
804
+ $Result = $AdjustTokenPrivileges.Invoke ($ThreadToken , $false , [Ref ] $TokenPrivileges , $TokenPrivSize , [IntPtr ]::Zero, [IntPtr ]::Zero)
805
+ if ($Result -eq $false )
806
+ {
807
+ Throw (New-Object ComponentModel.Win32Exception)
808
+ }
809
+
810
+ $CloseHandle.Invoke ($ThreadToken ) | Out-Null
811
+ }
812
+
813
+
814
+ # Enable SeSecurityPrivilege, needed to query security information for desktop DACL
815
+ function Enable-Privilege
816
+ {
817
+ Param (
818
+ [Parameter ()]
819
+ [ValidateSet (" SeAssignPrimaryTokenPrivilege" , " SeAuditPrivilege" , " SeBackupPrivilege" , " SeChangeNotifyPrivilege" , " SeCreateGlobalPrivilege" ,
820
+ " SeCreatePagefilePrivilege" , " SeCreatePermanentPrivilege" , " SeCreateSymbolicLinkPrivilege" , " SeCreateTokenPrivilege" ,
821
+ " SeDebugPrivilege" , " SeEnableDelegationPrivilege" , " SeImpersonatePrivilege" , " SeIncreaseBasePriorityPrivilege" ,
822
+ " SeIncreaseQuotaPrivilege" , " SeIncreaseWorkingSetPrivilege" , " SeLoadDriverPrivilege" , " SeLockMemoryPrivilege" , " SeMachineAccountPrivilege" ,
823
+ " SeManageVolumePrivilege" , " SeProfileSingleProcessPrivilege" , " SeRelabelPrivilege" , " SeRemoteShutdownPrivilege" , " SeRestorePrivilege" ,
824
+ " SeSecurityPrivilege" , " SeShutdownPrivilege" , " SeSyncAgentPrivilege" , " SeSystemEnvironmentPrivilege" , " SeSystemProfilePrivilege" ,
825
+ " SeSystemtimePrivilege" , " SeTakeOwnershipPrivilege" , " SeTcbPrivilege" , " SeTimeZonePrivilege" , " SeTrustedCredManAccessPrivilege" ,
826
+ " SeUndockPrivilege" , " SeUnsolicitedInputPrivilege" )]
827
+ [String ]
828
+ $Privilege
829
+ )
830
+
831
+ [IntPtr ]$ThreadHandle = $GetCurrentThread.Invoke ()
832
+ if ($ThreadHandle -eq [IntPtr ]::Zero)
833
+ {
834
+ Throw " Unable to get the handle to the current thread"
835
+ }
836
+
837
+ [IntPtr ]$ThreadToken = [IntPtr ]::Zero
838
+ [Bool ]$Result = $OpenThreadToken.Invoke ($ThreadHandle , $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES , $false , [Ref ]$ThreadToken )
839
+ $ErrorCode = [System.Runtime.InteropServices.Marshal ]::GetLastWin32Error()
840
+
841
+ if ($Result -eq $false )
842
+ {
843
+ if ($ErrorCode -eq $Win32Constants.ERROR_NO_TOKEN )
844
+ {
845
+ $Result = $ImpersonateSelf.Invoke ($Win32Constants.SECURITY_DELEGATION )
846
+ if ($Result -eq $false )
847
+ {
848
+ Throw (New-Object ComponentModel.Win32Exception)
849
+ }
850
+
851
+ $Result = $OpenThreadToken.Invoke ($ThreadHandle , $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES , $false , [Ref ]$ThreadToken )
852
+ if ($Result -eq $false )
853
+ {
854
+ Throw (New-Object ComponentModel.Win32Exception)
855
+ }
856
+ }
857
+ else
858
+ {
859
+ Throw ([ComponentModel.Win32Exception ] $ErrorCode )
860
+ }
861
+ }
862
+
863
+ $CloseHandle.Invoke ($ThreadHandle ) | Out-Null
864
+
865
+ $LuidSize = [System.Runtime.InteropServices.Marshal ]::SizeOf([Type ]$LUID )
866
+ $LuidPtr = [System.Runtime.InteropServices.Marshal ]::AllocHGlobal($LuidSize )
867
+ $LuidObject = [System.Runtime.InteropServices.Marshal ]::PtrToStructure($LuidPtr , [Type ]$LUID )
868
+ [System.Runtime.InteropServices.Marshal ]::FreeHGlobal($LuidPtr )
869
+
870
+ $Result = $LookupPrivilegeValue.Invoke ($null , $Privilege , [Ref ] $LuidObject )
776
871
777
872
if ($Result -eq $false )
778
873
{
@@ -796,20 +891,22 @@ Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-toke
796
891
797
892
$Global :TokenPriv = $TokenPrivileges
798
893
894
+ Write-Verbose " Attempting to enable privilege: $Privilege "
799
895
$Result = $AdjustTokenPrivileges.Invoke ($ThreadToken , $false , [Ref ] $TokenPrivileges , $TokenPrivSize , [IntPtr ]::Zero, [IntPtr ]::Zero)
800
896
if ($Result -eq $false )
801
897
{
802
898
Throw (New-Object ComponentModel.Win32Exception)
803
899
}
804
900
805
901
$CloseHandle.Invoke ($ThreadToken ) | Out-Null
902
+ Write-Verbose " Enabled privilege: $Privilege "
806
903
}
807
904
808
905
809
906
# Change the ACL of the WindowStation and Desktop
810
907
function Set-DesktopACLs
811
908
{
812
- Enable-SeSecurityPrivilege
909
+ Enable-Privilege - Privilege SeSecurityPrivilege
813
910
814
911
# Change the privilege for the current window station to allow full privilege for all users
815
912
$WindowStationStr = [System.Runtime.InteropServices.Marshal ]::StringToHGlobalUni(" WinSta0" )
@@ -1454,7 +1551,7 @@ Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-toke
1454
1551
[String ]
1455
1552
$ProcessArgs
1456
1553
)
1457
-
1554
+ Write-Verbose " Entering Create-ProcessWithToken "
1458
1555
# Duplicate the token so it can be used to create a new process
1459
1556
[IntPtr ]$NewHToken = [IntPtr ]::Zero
1460
1557
$Success = $DuplicateTokenEx.Invoke ($hToken , $Win32Constants.MAXIMUM_ALLOWED , [IntPtr ]::Zero, 3 , 1 , [Ref ]$NewHToken )
@@ -1473,14 +1570,30 @@ Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-toke
1473
1570
$ProcessInfoSize = [System.Runtime.InteropServices.Marshal ]::SizeOf([Type ]$PROCESS_INFORMATION )
1474
1571
[IntPtr ]$ProcessInfoPtr = [System.Runtime.InteropServices.Marshal ]::AllocHGlobal($ProcessInfoSize )
1475
1572
1476
- $ProcessNamePtr = [System.Runtime.InteropServices.Marshal ]::StringToHGlobalUni($ProcessName )
1573
+ $ProcessNamePtr = [System.Runtime.InteropServices.Marshal ]::StringToHGlobalUni(" $ProcessName " )
1477
1574
$ProcessArgsPtr = [IntPtr ]::Zero
1478
1575
if (-not [String ]::IsNullOrEmpty($ProcessArgs ))
1479
1576
{
1480
- $ProcessArgsPtr = [System.Runtime.InteropServices.Marshal ]::StringToHGlobalUni($ProcessArgs )
1577
+ $ProcessArgsPtr = [System.Runtime.InteropServices.Marshal ]::StringToHGlobalUni(" `" $ProcessName `" $ProcessArgs " )
1578
+ }
1579
+
1580
+ $FunctionName = " "
1581
+ if ([System.Diagnostics.Process ]::GetCurrentProcess().SessionId -eq 0 )
1582
+ {
1583
+ # Cannot use CreateProcessWithTokenW when in Session0 because CreateProcessWithTokenW throws an ACCESS_DENIED error. I believe it is because
1584
+ # this API attempts to modify the desktop ACL. I would just use this API all the time, but it requires that I enable SeAssignPrimaryTokenPrivilege
1585
+ # which is not ideal.
1586
+ Write-Verbose " Running in Session 0. Enabling SeAssignPrimaryTokenPrivilege and calling CreateProcessAsUserW to create a process with alternate token."
1587
+ Enable-Privilege - Privilege SeAssignPrimaryTokenPrivilege
1588
+ $Success = $CreateProcessAsUserW.Invoke ($NewHToken , $ProcessNamePtr , $ProcessArgsPtr , [IntPtr ]::Zero, [IntPtr ]::Zero, $false , 0 , [IntPtr ]::Zero, [IntPtr ]::Zero, $StartupInfoPtr , $ProcessInfoPtr )
1589
+ $FunctionName = " CreateProcessAsUserW"
1590
+ }
1591
+ else
1592
+ {
1593
+ Write-Verbose " Not running in Session 0, calling CreateProcessWithTokenW to create a process with alternate token."
1594
+ $Success = $CreateProcessWithTokenW.Invoke ($NewHToken , 0x0 , $ProcessNamePtr , $ProcessArgsPtr , 0 , [IntPtr ]::Zero, [IntPtr ]::Zero, $StartupInfoPtr , $ProcessInfoPtr )
1595
+ $FunctionName = " CreateProcessWithTokenW"
1481
1596
}
1482
-
1483
- $Success = $CreateProcessWithTokenW.Invoke ($NewHToken , 0x0 , $ProcessNamePtr , $ProcessArgsPtr , 0 , [IntPtr ]::Zero, [IntPtr ]::Zero, $StartupInfoPtr , $ProcessInfoPtr )
1484
1597
if ($Success )
1485
1598
{
1486
1599
# Free the handles returned in the ProcessInfo structure
@@ -1491,7 +1604,7 @@ Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-toke
1491
1604
else
1492
1605
{
1493
1606
$ErrorCode = [System.Runtime.InteropServices.Marshal ]::GetLastWin32Error()
1494
- Write-Warning " CreateProcessWithTokenW failed. Error code: $ErrorCode "
1607
+ Write-Warning " $FunctionName failed. Error code: $ErrorCode "
1495
1608
}
1496
1609
1497
1610
# Free StartupInfo memory and ProcessInfo memory
@@ -1540,20 +1653,18 @@ Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-toke
1540
1653
{
1541
1654
$AllTokens = @ ()
1542
1655
1543
- if ([Environment ]::UserName -ine " SYSTEM" )
1656
+ # First GetSystem. The script cannot enumerate all tokens unless it is system for some reason. Luckily it can impersonate a system token.
1657
+ # Even if already running as system, later parts on the script depend on having a SYSTEM token with most privileges, so impersonate the wininit token.
1658
+ $systemTokenInfo = Get-PrimaryToken - ProcessId (Get-Process wininit | where {$_.SessionId -eq 0 }).Id
1659
+ if ($systemTokenInfo -eq $null -or (-not (Invoke-ImpersonateUser - hToken $systemTokenInfo.hProcToken )))
1544
1660
{
1545
- # First GetSystem. The script cannot enumerate all tokens unless it is system for some reason. Luckily it can impersonate a system token.
1546
- $systemTokenInfo = Get-PrimaryToken - ProcessId (Get-Process wininit | where {$_.SessionId -eq 0 }).Id
1547
- if ($systemTokenInfo -eq $null -or (-not (Invoke-ImpersonateUser - hToken $systemTokenInfo.hProcToken )))
1548
- {
1549
- Write-Warning " Unable to impersonate SYSTEM, the script will not be able to enumerate all tokens"
1550
- }
1661
+ Write-Warning " Unable to impersonate SYSTEM, the script will not be able to enumerate all tokens"
1662
+ }
1551
1663
1552
- if ($systemTokenInfo -ne $null -and $systemTokenInfo.hProcToken -ne [IntPtr ]::Zero)
1553
- {
1554
- $CloseHandle.Invoke ($systemTokenInfo.hProcToken ) | Out-Null
1555
- $systemTokenInfo = $null
1556
- }
1664
+ if ($systemTokenInfo -ne $null -and $systemTokenInfo.hProcToken -ne [IntPtr ]::Zero)
1665
+ {
1666
+ $CloseHandle.Invoke ($systemTokenInfo.hProcToken ) | Out-Null
1667
+ $systemTokenInfo = $null
1557
1668
}
1558
1669
1559
1670
$ProcessIds = get-process | where {$_.name -inotmatch " ^csrss$" -and $_.name -inotmatch " ^system$" -and $_.id -ne 0 }
@@ -1639,7 +1750,12 @@ Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-toke
1639
1750
Write-Error " Script must be run as administrator" - ErrorAction Stop
1640
1751
}
1641
1752
1642
- $OriginalUser = [Environment ]::UserName
1753
+ # If running in session 0, force NoUI
1754
+ if ([System.Diagnostics.Process ]::GetCurrentProcess().SessionId -eq 0 )
1755
+ {
1756
+ Write-Verbose " Running in Session 0, forcing NoUI (processes in Session 0 cannot have a UI)"
1757
+ $NoUI = $true
1758
+ }
1643
1759
1644
1760
if ($PsCmdlet.ParameterSetName -ieq " RevToSelf" )
1645
1761
{
@@ -1727,10 +1843,7 @@ Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-toke
1727
1843
1728
1844
Create- ProcessWithToken - hToken $hToken - ProcessName $CreateProcess - ProcessArgs $ProcessArgs
1729
1845
1730
- if ($OriginalUser -ine " SYSTEM" )
1731
- {
1732
- Invoke-RevertToSelf
1733
- }
1846
+ Invoke-RevertToSelf
1734
1847
}
1735
1848
elseif ($ImpersonateUser )
1736
1849
{
@@ -1757,10 +1870,7 @@ Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-toke
1757
1870
Write-Output (Get-UniqueTokens - AllTokens $AllTokens ).TokenByUser.Values
1758
1871
}
1759
1872
1760
- if ($OriginalUser -ine " SYSTEM" )
1761
- {
1762
- Invoke-RevertToSelf
1763
- }
1873
+ Invoke-RevertToSelf
1764
1874
1765
1875
Free- AllTokens - TokenInfoObjs $AllTokens
1766
1876
}
0 commit comments