diff --git a/src/needle/injector.rs b/src/needle/injector.rs new file mode 100644 index 0000000..b9ea462 --- /dev/null +++ b/src/needle/injector.rs @@ -0,0 +1,5 @@ +use nix::{Result, unistd::Pid}; + +pub trait RemoteOperation { + fn inject(&mut self, pid: Pid, syscall: usize) -> Result; +} diff --git a/src/needle/main.rs b/src/needle/main.rs index ae83947..94a77c0 100644 --- a/src/needle/main.rs +++ b/src/needle/main.rs @@ -1,10 +1,14 @@ mod syscalls; +mod rce; mod operations; +mod injector; +use injector::RemoteOperation; use nix::{Result, {sys::{ptrace, wait::waitpid}, unistd::Pid}}; use clap::Parser; use operations::step_to_syscall; -use syscalls::{RemoteString, RemoteWrite, RemoteSyscall}; +use rce::{RemoteString, RemoteShellcode}; +use syscalls::RemoteWrite; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -20,8 +24,10 @@ struct NeedleArgs { pub fn nasty_stuff(pid: Pid, _word_size: usize) -> Result<()> { let original_registers = ptrace::getregs(pid)?; let syscall_addr = step_to_syscall(pid)?; - let msg = RemoteString::new(pid, syscall_addr, "injected!\n\0".into())?; - RemoteWrite::args(1, msg).syscall(pid, syscall_addr)?; + let mut msg = RemoteString::new("injected!\n\0".into()); + msg.inject(pid, syscall_addr)?; + RemoteWrite::args(1, msg).inject(pid, syscall_addr)?; + RemoteShellcode::new(&[0u8]).inject(pid, syscall_addr)?; ptrace::setregs(pid, original_registers)?; Ok(()) } diff --git a/src/needle/rce.rs b/src/needle/rce.rs new file mode 100644 index 0000000..0b3516c --- /dev/null +++ b/src/needle/rce.rs @@ -0,0 +1,54 @@ +use nix::{unistd::Pid, Result, libc::{PROT_READ, MAP_PRIVATE, MAP_ANON, PROT_WRITE}, sys::{ptrace, wait::waitpid}}; + +use crate::{syscalls::RemoteMMap, operations::write_buffer, injector::RemoteOperation}; + +pub struct RemoteString { + pub ptr: Option, + pub txt: String, +} + +impl RemoteString { + pub fn new(txt: String) -> Self { + RemoteString { ptr: None, txt } + } +} + +impl RemoteOperation for RemoteString { + fn inject(&mut self, pid: Pid, syscall: usize) -> Result { + let ptr = RemoteMMap::args( + 0, self.txt.len(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0 + ).inject(pid, syscall)?; + write_buffer(pid, ptr as usize, self.txt.as_bytes(), 32)?; // TODO don't hardcode word size + self.ptr = Some(ptr as usize); + Ok(ptr) + } +} + +pub struct RemoteShellcode<'a> { + code: &'a [u8], +} + +impl<'a> RemoteShellcode<'a> { + pub fn new(code: &'a [u8]) -> Self { + RemoteShellcode { code } + } +} + +impl RemoteOperation for RemoteShellcode<'_> { + fn inject(&mut self, pid: Pid, syscall: usize) -> Result { + let original_regs = ptrace::getregs(pid)?; + let ptr = RemoteMMap::args( + 0, self.code.len(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0 + ).inject(pid, syscall)?; + let mut shellcode = self.code.to_vec(); + shellcode.push(0xCC); // is this the debugger trap? + write_buffer(pid, ptr as usize, shellcode.as_slice(), 32)?; // TODO don't hardcode word size + let mut regs = original_regs.clone(); + regs.rip = ptr; + ptrace::setregs(pid, regs)?; + ptrace::cont(pid, None)?; + waitpid(pid, None)?; + ptrace::setregs(pid, original_regs)?; + Ok(ptr) + } +} diff --git a/src/needle/syscalls.rs b/src/needle/syscalls.rs index d147d71..885e7e7 100644 --- a/src/needle/syscalls.rs +++ b/src/needle/syscalls.rs @@ -1,36 +1,10 @@ -use nix::{libc::{user_regs_struct, MAP_PRIVATE, MAP_ANON, PROT_READ, PROT_WRITE}, Result, sys::{ptrace, wait::waitpid}, unistd::Pid}; +use nix::{libc::user_regs_struct, Result, sys::{ptrace, wait::waitpid}, unistd::Pid}; -use crate::operations::write_buffer; - -pub struct RemoteString { - pub ptr: usize, - pub txt: String, -} - -impl RemoteString { - pub fn new(pid: Pid, syscall: usize, txt: String) -> Result { - let ptr = RemoteMMap::args( - 0, txt.len(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0xFFFFFFFFFFFFFFFF, 0 - ).syscall(pid, syscall)? as usize; - write_buffer(pid, ptr, txt.as_bytes(), 32)?; // TODO don't hardcode word size - Ok(RemoteString { ptr, txt }) - } -} +use crate::{rce::RemoteString, injector::RemoteOperation}; pub trait RemoteSyscall { fn registers(&self, regs: &mut user_regs_struct); - fn syscall(&self, pid: Pid, addr: usize) -> Result { - let mut regs = ptrace::getregs(pid)?; - regs.rip = addr as u64; - self.registers(&mut regs); - ptrace::setregs(pid, regs)?; - ptrace::step(pid, None)?; - waitpid(pid, None)?; - regs = ptrace::getregs(pid)?; - Ok(regs.rax) - } - fn prepare_registers(regs: &mut user_regs_struct, rax: u64, rdi: u64, rsi: u64, rdx: u64, r10: u64, r8: u64, r9: u64) { regs.rax = rax; regs.rdi = rdi; @@ -42,24 +16,39 @@ pub trait RemoteSyscall { } } +impl RemoteOperation for T where T: RemoteSyscall { + fn inject(&mut self, pid: Pid, syscall: usize) -> Result { + let mut regs = ptrace::getregs(pid)?; + regs.rip = syscall as u64; + self.registers(&mut regs); + ptrace::setregs(pid, regs)?; + ptrace::step(pid, None)?; + waitpid(pid, None)?; + regs = ptrace::getregs(pid)?; + Ok(regs.rax) + } +} + pub struct RemoteMMap { addr: u64, len: usize, prot: i32, flags: i32, - fd: u64, + fd: i64, off: u64, } + + impl RemoteMMap { - pub fn args(addr: u64, len: usize, prot: i32, flags: i32, fd: u64, off: u64) -> Self { + pub fn args(addr: u64, len: usize, prot: i32, flags: i32, fd: i64, off: u64) -> Self { RemoteMMap { addr, len, prot, flags, fd, off } } } impl RemoteSyscall for RemoteMMap { fn registers(&self, regs: &mut user_regs_struct) { - Self::prepare_registers(regs, 9, self.addr, self.len as u64, self.prot as u64, self.flags as u64, self.fd, self.off); + Self::prepare_registers(regs, 9, self.addr, self.len as u64, self.prot as u64, self.flags as u64, self.fd as u64, self.off); } } @@ -77,8 +66,8 @@ impl RemoteOpen { } impl RemoteSyscall for RemoteOpen { - fn registers(&self, regs: &mut user_regs_struct) { - Self::prepare_registers(regs, 2, self.filename.ptr as u64, self.flags, self.mode, 0, 0, 0); + fn registers(&self, regs: &mut user_regs_struct) { // TODO handle this unwrap + Self::prepare_registers(regs, 2, self.filename.ptr.unwrap() as u64, self.flags, self.mode, 0, 0, 0); } } @@ -95,7 +84,7 @@ impl RemoteWrite { impl RemoteSyscall for RemoteWrite { fn registers(&self, regs: &mut user_regs_struct) { - Self::prepare_registers(regs, 1, self.fd, self.buf.ptr as u64, self.buf.txt.len() as u64, 0, 0, 0); + Self::prepare_registers(regs, 1, self.fd, self.buf.ptr.unwrap() as u64, self.buf.txt.len() as u64, 0, 0, 0); } }