I want to switch 2 files in the SYSVOL Folder. For that I need to Invoke-Command a script on a Domain Controller from our schedule server.
$Server = Get-ADDomainController | select Name
Invoke-Command -ComputerName $Server.Name -ScriptBlock {
$Path = "c:\Windows\SYSVOL\sysvol\domain.com\scripts\Folder"
$Source = "$Path\backgrounddefault1.JPG"
$trg = "$Path\backgrounddefault.JPG"
Copy-Item -Path $Source -Destination $trg -Force
}
However if I do not promote the Service Account to Domain Admin, the script will get an access denied.
Are there any other ways I could do this, or other groups that give him that specific right?
The group is called "Remote Management Users". Obviously, however, you'll also need permissions on the folder. Create a new group for those rights specifically, and make it a member of RMU. Make the service account a member of the new group. (Note that none of this is specific to DCs.)
If you wanted to do this without changing any permissions on a Sysvol subdirectory, the cleanest thing I can think of would be to create a scheduled task on the DC running under local system that performs only this operation, and grant the service account permission to start this task. (There is no interface for this, but you can manipulate the SecurityDescriptor of what you get with Get-ScheduledTask; see this question, for example).
If this task needs parameters/input, it gets trickier since you'll need to supply these in a file somehow. Because that task effectively has domain admin permissions, you'd have to take very good care to check your inputs and make sure the task has no exploitable vulnerabilities. Just tweaking the permissions on that one specific folder seems a lot easier and also safer to me.
Last but not least, when performing operations like these it's always worth investigating if what you're trying to do can't be done with Group Policy somehow, because it leaves a clear statement of intent (and an audit trail).
Related
This seems bonkers so I'm hoping I didn't find a big security gap... I have Powershell JEA (just enough administration) successfully set up on a server to allow only certain administrative functions. Specifically, I don't have the "net" command allowed at all. If I do the below:
Invoke-Command -computername MYSERVER -configurationname MYCONFIG -scriptblock {
net stop "My windows service"
}
Then I get the error below as expected:
The term 'net.exe' is not recognized as the name of a cmdlet,
function, script file, or operable program. Check the spelling of the
name, or if a path was included, verify that the path is correct and
try again.
+ CategoryInfo : ObjectNotFound: (net.exe:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
BUT, if I wrap my "net.exe" usage inside a function, it actually works:
Invoke-Command -computername MYSERVER -configurationname MYCONFIG -scriptblock {
function StopService($servicename) {
net stop "$($servicename)"
}
StopService "My windows service"
}
The above does not throw an error and actually stops the service. WTF?
This is more than the "net" command. Another example: "Out-File" is not allowed. The below code fails:
Invoke-Command -computername MYSERVER -configurationname MYCONFIG -scriptblock {
"hacked you" | Out-File C:\test.txt
}
With the error:
The term 'Out-File' is not recognized as the name of a cmdlet,
function, script file, or operable program. Check the spelling of the
name, or if a path was included, verify that the path is correct and
try again.
+ CategoryInfo : ObjectNotFound: (Out-File:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : vxcazdev01
But if I do it this way, it works:
Invoke-Command -computername MYSERVER -configurationname MYCONFIG -scriptblock {
function DoIt() {
"hacked you" | Out-File C:\test.txt
}
DoIt
}
Why is this happening? Am I missing something? The JEA project on github is now read-only so I can't open an issue there.
Edit to add: the same problem happens if I use Enter-PSSession instead of Invoke-Command.
Edit to add relevant session config pieces: My session config file only has a few customizations from the default file produced by the New-PSSessionConfigurationFile command:
SessionType = 'Default'
RunAsVirtualAccount = $true
RoleDefinitions = #{
'MYDOMAIN\MYADGROUP' = #{ RoleCapabilities = 'MyCustomRole' }
}
MYADGROUP is the only group my test user is a member of. And then this is registered on the server like so:
Register-PSSessionConfiguration -Path "C:\Program Files\WindowsPowerShell\Modules\MyJEAModule\VSM.pssc" -Name 'MYCONFIG' -Force
I'm just going to answer this question myself with the answer: security hole by design.
The documentation says this:
The body (script block) of custom functions runs in the default language mode for the system and isn't subject to JEA's language constraints.
It's rather surprising, to me at least, that JEA will let you lock down actions on a server in security sandbox, but as soon as one writes their own custom functions they have full administrative rights to the machine and have broken out of the box. Allowing or restricting the creation of custom functions via the language mode is one thing, but bypassing the set security permissions is another. In my opinion, user-written custom functions should be subject to full security limitations; custom functions written in the role capabilities files should have full admin rights as the documentation indicates.
The other answer by HAL9256 is great, but it describes the benefits of JEA, which is not the topic of this post.
I wouldn't say that it's a security hole, it's that you are clearly demonstrating what could happen on a system when you have not set up a fully secured configuration. Microsoft even states JEA doesn't protect against admins because "they could simply RDP in and change the configuration". We need the correct combination of SessionType and RoleDefinitions, and that they are meant for two different configurations.
Your example demonstrates a configuration setup where, even though we lock the front door of the house, we started off with a house that had all the windows and doors open. It is fully possible to get in through the back door, or reach through a window and unlock the front door, thus demonstrating the fruitlessness of locking the front door. For example, I don't need to run net stop I could just do a taskkill instead, or..., or..., etc.
Let's look at the overview of what JEA is designed for:
Reduce the number of administrators on your machines using virtual accounts or group-managed service accounts to perform privileged actions on behalf of regular users.
Limit what users can do by specifying which cmdlets, functions, and external commands they can run.
Better understand what your users are doing with transcripts and logs that show you exactly which commands a user executed during their session.
Reduce the number of administrators
We can use JEA to remove people from the local administrators group, or larger Domain Admin groups. They can then selectively get elevated Administrator rights when needed through Virtual Accounts.
If we set it up with the SessionType = 'Default' this enables all language features. We essentially can have a Jr. Technical Analyst without Domain Admin rights, without Local Admin rights, log on, and do Administrative duties. This is what the session type is meant for.
Limit what users can do
If we set it up with the SessionType = 'Default' this enables all language features. In mode it doesn't matter what commands we limit, all the doors and windows are open and we can pretty much do whatever we want. One rule in Windows is that there is always 3-4 different ways to do something. You just can't plug all the holes when everything is wide open.
#MathiasR.Jessen is right, the only way to Limit what users can do is to first lock down the system. Setting SessionType = 'RestrictedRemoteServer' locks down the session to:
Sessions of this type operate in NoLanguage mode and only have access
to the following default commands (and aliases):
Clear-Host (cls, clear)
Exit-PSSession (exsn, exit)
Get-Command (gcm)
Get-FormatData
Get-Help
Measure-Object (measure)
Out-Default
Select-Object (select)
No PowerShell providers are available, nor are any external programs
(executables or scripts).
This starts us out with a completely locked up house. We then selectively enable the needed commands. Ideally we should pre-create custom functions so that the custom function is the only thing they can run, they are technically not even allowed to execute the commands inside the function at all, it's all handled by the Virtual Account.
What you did was essentially exploiting this custom function capability, by "cheating" and creating our own "custom function" that will run in the Virtual Account scope, and not your own, which is why it was able to run "non-allowed" functions, and you were not. If the SessionType = 'RestrictedRemoteServer', you wouldn't be able to create scripts or custom functions like demonstrated, and hence, the "hole" would not be there.
Better understand what your users are doing
Finally the other benefit for JEA is that it can record a transcript of all the commands that are run. This might be needed for audit reasons or fed into a SIEM solution or to find out how your Jr. Technical Analyst messed up your system ;-).
I need the ability to have users run a script that requires the ActiveDirectory module. I copied over the following:
"C:\Windows\System32\WindowsPowerShell\v1.0\Modules\ActiveDirectory", "Microsoft.ActiveDirectory.Management.resources.dll", "Microsoft.ActiveDirectory.Management.dll".
The script runs two Get-ADUser commands, 1 without the -Server parameter and the other with. The issue is that the former is working but the latter is not.
Is there another module that I need to copy over?
I don't like the idea of installing administrative tools for non-admins. Even if you could get away with copying files and not doing the full-blown RSAT installation. Not the least of reasons is you are dramatically increasing the attack surface for malicious actors. The better solution is (Just Enough Administration) JEA, or a philosophically similar approach.
JEA / Contrained endpoints can get complicated, but a summary of what you can do looks something like this:
New-PSSessionConfigurationFile -Path 'C:\PSSessionConfigs\DemoPSEndpointConfig.pssc' -ModulesToImport ActiveDirectory -VisibleCmdlets "Get-ADUser"
Register-PSSessionConfiguration -Path 'C:\PSSessionConfigs\DemoPSEndpointConfig.pssc' -ShowSecurityDescriptorUI -Name DemoPSEndPoint
Run these commands on a system that has the ActiveDirectory module (likely the whole RSAT component) installed, it doesn't need to be a Domain Controller. It will create a new PowerShell remoting endpoint configuration that exposes only the commands you wish. The Register-PSSessionConfiguration command will display a security dialog where you can permission which users you want to allow to connect, you want to grant them read & execute permission. Once that's done, you can get the results with an Invoke-Command command like this:
Invoke-Command -ComputerName <ServerName> -ConfigurationName DemoPSEndPoint -ScriptBlock { Get-ADUser <UserName> }
You can add the -Server parameter in the command without issue. You can expand the cmdlets you are allowing in the New-PSSessionConfiguration command.
Again this is very much a summary of a more complex topic but should be enough to get what you want.
Personally, I don't use configuration files as much as I use startup scripts. I think the latter is more flexible. You can get some information about that here. If you really want to dig into this there are references at the end of the article including a link to the PowerShell JEA documentation. There's also a link to some of the MVP articles I used to develop my own endpoints.
The ActiveDirectory module is dependent on the RSAT (remote server administration tool). This is avalible to install/activate through powershell: https://mikefrobbins.com/2018/10/03/use-powershell-to-install-the-remote-server-administration-tools-rsat-on-windows-10-version-1809/
With this installed you automatically also get the Activedirectory module installed.
I want to check whether a given Active Directory user (specified by username and domain) has read/(write) permissions on a given folder. And this unrelated to having them granted directly by user name or indirectly by some group membership. However I've been googling like 5 hours by now to no avail.
I understand, that Get-Acl Cmdlet is used to read folder permissions. Most likely the user is member of some group, that indirectly grants it permissions.
So my idea was to just match the output of
(Get-Acl <Folder>).Access | ft
against the group membership of the user.
I collect the group Information using the command
Get-ADPrincipalGroupMembership "<Username>" | select name
but found out, that this features an entirely different set of group names. I assumed, that the groups outputted by Get-Acl are local ones, while the other ones were AD-sided groups.
I found the command whoami /groups, that prints all groups, but only for the currently logged in user.
After quite some additional time I figured out the command (based on .NET):
(New-Object Security.Principal.WindowsIdentity -ArgumentList #(,"<User#fullDomain.com>")).Claims | select Value
This however just prints SIDs, while
(Get-Acl <Folder>).Access | ft
displays some human friendly output like "NT SERVICE\TrustedInstaller" and such.
I believe I'm pretty close, but I just can't get it to work.
On another note: Does this really have to be that complicated?
Other solutions I found only work based on direct user permissions but do not check group permissions.
Oh and one thing: Everything has to run on PowerShell 4.0 on Windows Server 2012 R2 and the PowerShell script will be running locally. No NuGet packages or anything requiring an Internet connection are allowed.
Now, before I start, I will let you in on a secret: this is on a Domain Controller.*
*The above statement is stricken due to irrelevance as the only significant change that occurs to the Local Administrator account and the Local Administrator Group (within the context and scope of this question) is minimal and doesn't alter the outcome enough to require differentiation.
I didn't have this kind of trouble on any of the other servers and I am willing to bet that the reason behind it is because it is on a DC.*
*Same reason as stated above. The accepted answer explains the inconsistency and was an oversight on my part, not the architecture (read features) of Windows Security or Domain Controllers.
I have been playing around with a few ideas on how to check if a script has been called from an account that is either the local Administrator or, at the very least, called by an account that is part of the Local Administrators group.
I have renamed the local Admin account, however, I know that I can see if the script has been called by local Admin account by typing:
(New-Object System.Security.Principal.NTAccount('reserved')).Translate([System.Security.Principal.SecurityIdentifier]).Value
and I can see if the SID ends in -500.
The problem occurs when I run a condition to see if the calling account is part of the Administrators group (which is a larger scope) by typing:
PS> [bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).Groups -match "S-1-5-32-544")
PS> False
A quick check to see what account it was using:
PS> $env:username
PS> reserved
or the needlessly complicated way (though I prefer it sometimes):
PS> Write-Host ((Get-WmiObject Win32_Account | ?{$_.SID.Substring($_.SID.Length-4,4) -eq '-500'}).Caption).Split("\",2)[1] -fore GREEN
PS> reserved
and I even type:
PS> net user reserved
where it tells me Local Group Memberships *Administrators.
I pull up ADUC (dsa.msc) and I look in the Builtin container and double-click on the Administrators group. I select the Members tag and lo, and behold, reserved is actually a member!
So, a recap:
By typing net user reserved, I was able to verify it was part of the Local Administrators group
I looked in ADUC and verified reserved was a member of the builtin Administrators group
I ensured reserved was indeed the Local Administrator account by verifying the SID started with S-1-5... and ended with ...-500
To take it a step further, I made sure the SID matched the Active Directory Group named "Administrators" by typing Get-ADGroup -Identity "Administrators". I then typed Get-ADGroupMember -Identity "Administrators" and made sure reserved was listed (it was AND the SID matched!).
When I check to see if the well-known Administrators Group SID is found in the Groups of that account (by getting the current Windows Identity), it says that it isn't.
What gives?
Why do I get every indication that it actually is a member of the Local Administrators group but that SID isn't found in the account's groups?
When a computer is promoted to a domain controller, there are no more local users or groups on the machine. Member computers have local users and groups, and can also use domain users and groups for authentication, but on a DC, there are only domain objects.
See also: https://serverfault.com/a/264327/236470
I happened to stumble across something and I realized the answer to this question. For the sake of those that come here looking for assistance, here is the answer to my question:
Very simply--in regards to Powershell--if the Administrator Group SID (S-1-5-32-544) does not show up in the Groups of the user, that is a first-line indication that the script is not running with Administrative credentials.
For example, when I type out:
([Security.Principal.WindowsIdentity]::GetCurrent()).Groups
and I do not see the Administrator Group SID listed even though I know for a fact that the account I am signed into is a member of the Administrator Group, it means the current Powershell process does not have Administrative credentials.
If you click Run As Administrator and type the same as above, you will see that it lists the Administrator Group SID in Groups.
The reason why I was experiencing the inconsistency is simply because I was not running the Powershell process as an Administrator.
So, in short, there are a few ways you can check to verify if your current Powershell session has Administrator credentials. The first one is found in countless websites around the internet and is very common (I did not write this one):
$myWindowsID = [Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal = New-Object Security.Principal.WindowsPrincipal($myWindowsID)
$adminRole = [Security.Principal.WindowsBuiltInRole]::Administrator
if($myWindowsPrincipal.IsInRole($adminRole)) {
\\ TODO: Process is running as Administrator
Clear-Host
} else {
$newProcess = New-Object System.Diagnostics.ProcessStartInfo "Powershell"
$newProcess.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'"
$newProcess.Verb = "runas"
[System.Diagnostics.Process]::Start($newProcess)
exit
}
Here is another way (I did write this one):
[Security.Principal.WindowsIdentity]::GetCurrent() | %{
if($_.Groups -contains "S-1-5-32-544") {
\\ TODO: Process is running as Administrator
Clear-Host
} else {
Start Powershell -ArgumentList "& '$MyInvocation.MyCommand.Path'" -Verb runas
exit
}
}
# The Foreach-Object (%) could be replaced with another pipeline filter
I see a lot of people asking this question and because of Powershell appealing to many Systems Administrator (especially ones without a background in programming), I really think this will come in handy for others down the line.
I am writing a simple script to copy AD group membership from one user to the other. I am doing it using the ActiveDirectory module only.
The script looks like it would work and does work up until I try to ad the groups to the user.
Code:
import-module ActiveDirectory
$templateUser = get-ADUser user1
$targetUser = getADUser user2
$groups =get-adprincipalgroupmembership $templateUser
$groups2 = get-ADPrincipalGroupMembership $targetUser
foreach($group in $groups) {
add-adGroupMember $group $targetUser
}
Error:
Add-ADGroupMember : insufficient access rights to performt the operation
At line:9 char:18
+ FullyQualifiedErrorID : Insufficient access rights to perform the operation,Microsoft.ActiveDirectory.Management.Commands.AddADGroupMember
Notes/Thoughts:
I am logged in as a normal user, but I ran the powershell as a different user (my admin account). I am not a local admin, but I am an admin on the domain. I am able to add the user to groups if I launch the AD Tools and do it manually (I have permissions to add to those groups).
Edit:
Run the powershell as admin.
Run powershell as administrator.
I hit this today in Server 2012. I was running the powershell as Administrator, I was a domain admin, I was a local admin, I was every kind of admin I could find.
I "fixed" it by using the Active Directory Users and Computers tool, adding myself as the Manager of the AD groups I was trying to add users to, and ticked the box to allow the manager to change membership. I could then run AD-AddGroupMember happily.
I ran into this problem as well using Powershell remoting to connect to a domain controller.
In my case it turned out Include inheritable permissions from this object's parent was turned off for the specific object I couldn't change.
I ran into this issue today where an automated system was using powershell scripts for various things.... It turned out to be executionpolicy.
We were running our script with the ExecutionPolicy Bypass flag, and even running the command directly in powershell outside of a script wouldn't work, but once we set executionpolicy to unrestricted, everything magically worked.
For us we were able to create security groups even, but not add users to groups via powershell, even though we could make the same changes in ADUC.