chore: reordered needle again
This commit is contained in:
parent
4e4aceb092
commit
29c9a403f4
6 changed files with 106 additions and 110 deletions
|
@ -1,28 +1,6 @@
|
||||||
use nix::{unistd::Pid, Result, libc::{PROT_READ, MAP_PRIVATE, MAP_ANON, PROT_WRITE}, sys::{ptrace, wait::waitpid}};
|
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};
|
use crate::{syscalls::RemoteMMap, senders::write_buffer, injector::RemoteOperation};
|
||||||
|
|
||||||
pub struct RemoteString {
|
|
||||||
pub ptr: Option<usize>,
|
|
||||||
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<u64> {
|
|
||||||
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> {
|
pub struct RemoteShellcode<'a> {
|
||||||
code: &'a [u8],
|
code: &'a [u8],
|
||||||
|
@ -42,7 +20,7 @@ impl RemoteOperation for RemoteShellcode<'_> {
|
||||||
).inject(pid, syscall)?;
|
).inject(pid, syscall)?;
|
||||||
let mut shellcode = self.code.to_vec();
|
let mut shellcode = self.code.to_vec();
|
||||||
shellcode.push(0xCC); // is this the debugger trap?
|
shellcode.push(0xCC); // is this the debugger trap?
|
||||||
write_buffer(pid, ptr as usize, shellcode.as_slice(), 32)?; // TODO don't hardcode word size
|
write_buffer(pid, ptr as usize, shellcode.as_slice())?;
|
||||||
let mut regs = original_regs.clone();
|
let mut regs = original_regs.clone();
|
||||||
regs.rip = ptr;
|
regs.rip = ptr;
|
||||||
ptrace::setregs(pid, regs)?;
|
ptrace::setregs(pid, regs)?;
|
|
@ -1,5 +1,28 @@
|
||||||
use nix::{Result, unistd::Pid};
|
use std::ffi::c_void;
|
||||||
|
|
||||||
|
use nix::{Result, unistd::Pid, sys::{ptrace, wait::waitpid}};
|
||||||
|
|
||||||
pub trait RemoteOperation {
|
pub trait RemoteOperation {
|
||||||
fn inject(&mut self, pid: Pid, syscall: usize) -> Result<u64>;
|
fn inject(&mut self, pid: Pid, syscall: usize) -> Result<u64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn step_to_syscall(pid: Pid) -> Result<usize> {
|
||||||
|
let mut registers;
|
||||||
|
let mut addr;
|
||||||
|
let mut instructions;
|
||||||
|
|
||||||
|
// seek to syscall
|
||||||
|
loop {
|
||||||
|
registers = ptrace::getregs(pid)?;
|
||||||
|
addr = registers.rip as usize;
|
||||||
|
instructions = ptrace::read(pid, addr as *mut c_void)?;
|
||||||
|
// println!("@ 0x{:X} [{:x}]", insn_addr, curr_instr);
|
||||||
|
|
||||||
|
if instructions & 0xFFFF == 0x050F {
|
||||||
|
return Ok(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrace::step(pid, None)?;
|
||||||
|
waitpid(pid, None)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
mod syscalls;
|
mod syscalls;
|
||||||
mod rce;
|
mod executors;
|
||||||
mod operations;
|
mod senders;
|
||||||
mod injector;
|
mod injector;
|
||||||
|
|
||||||
use injector::RemoteOperation;
|
use injector::{RemoteOperation, step_to_syscall};
|
||||||
use nix::{Result, {sys::{ptrace, wait::waitpid}, unistd::Pid}};
|
use nix::{Result, {sys::{ptrace, wait::waitpid}, unistd::Pid}};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use operations::step_to_syscall;
|
|
||||||
use rce::{RemoteString, RemoteShellcode};
|
use executors::RemoteShellcode;
|
||||||
|
use senders::RemoteString;
|
||||||
use syscalls::RemoteWrite;
|
use syscalls::RemoteWrite;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
@ -15,45 +16,33 @@ use syscalls::RemoteWrite;
|
||||||
struct NeedleArgs {
|
struct NeedleArgs {
|
||||||
/// target process pid
|
/// target process pid
|
||||||
pid: i32,
|
pid: i32,
|
||||||
|
|
||||||
/// word size on OS (check with $ getconf WORD_BIT)
|
|
||||||
#[arg(long, default_value_t = 32)]
|
|
||||||
word: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nasty_stuff(pid: Pid, _word_size: usize) -> Result<()> {
|
pub fn nasty_stuff(pid: Pid) -> Result<()> {
|
||||||
let original_registers = ptrace::getregs(pid)?;
|
|
||||||
let syscall_addr = step_to_syscall(pid)?;
|
let syscall_addr = step_to_syscall(pid)?;
|
||||||
let mut msg = RemoteString::new("injected!\n\0".into());
|
let mut msg = RemoteString::new("injected!\n\0".into());
|
||||||
msg.inject(pid, syscall_addr)?;
|
msg.inject(pid, syscall_addr)?;
|
||||||
RemoteWrite::args(1, msg).inject(pid, syscall_addr)?;
|
RemoteWrite::args(1, msg).inject(pid, syscall_addr)?;
|
||||||
RemoteShellcode::new(&[0u8]).inject(pid, syscall_addr)?;
|
RemoteShellcode::new(&[0u8]).inject(pid, syscall_addr)?;
|
||||||
ptrace::setregs(pid, original_registers)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<()> {
|
||||||
let args = NeedleArgs::parse();
|
let args = NeedleArgs::parse();
|
||||||
let pid = Pid::from_raw(args.pid);
|
let pid = Pid::from_raw(args.pid);
|
||||||
|
|
||||||
if let Err(e) = ptrace::attach(pid) {
|
ptrace::attach(pid)?;
|
||||||
eprintln!("Could not attach to process : {}", e);
|
waitpid(pid, None)?;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = waitpid(pid, None) {
|
|
||||||
eprintln!("Failed waiting for process to stop : {}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Attached to process #{}", args.pid);
|
println!("Attached to process #{}", args.pid);
|
||||||
|
|
||||||
if let Err(e) = nasty_stuff(pid, args.word as usize) {
|
let regs = ptrace::getregs(pid)?;
|
||||||
eprintln!("Could not pwn : {}", e);
|
nasty_stuff(pid)?;
|
||||||
}
|
ptrace::setregs(pid, regs)?;
|
||||||
|
|
||||||
if let Err(e) = ptrace::detach(pid, None) {
|
ptrace::detach(pid, None)?;
|
||||||
eprintln!("Could not resume process : {}", e);
|
|
||||||
} else {
|
println!("Released process #{}", args.pid);
|
||||||
println!("Released process #{}", args.pid);
|
|
||||||
}
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
use std::ffi::c_void;
|
|
||||||
|
|
||||||
use nix::{Result, unistd::Pid, sys::{ptrace, wait::waitpid}};
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub fn read_buffer(pid: Pid, addr: usize, size: usize, word: u32) -> Result<Vec<u8>> {
|
|
||||||
let mut out = vec![];
|
|
||||||
|
|
||||||
for i in (0..size).step_by((word/8) as usize) {
|
|
||||||
let data = ptrace::read(pid, (addr + i) as *mut c_void)?;
|
|
||||||
for j in 0..(word/8) as usize {
|
|
||||||
out.push(((data >> (j * 8)) & 0xFF) as u8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_buffer(pid: Pid, addr: usize, payload: &[u8], word:u32) -> Result<()> {
|
|
||||||
let step = word / 8;
|
|
||||||
let mut at = addr;
|
|
||||||
|
|
||||||
for chunk in payload.chunks(step as usize) {
|
|
||||||
let mut buf : u64 = 0;
|
|
||||||
for (i, c) in chunk.iter().enumerate() {
|
|
||||||
buf |= (*c as u64) << (i * 8);
|
|
||||||
}
|
|
||||||
unsafe { ptrace::write(pid, at as *mut c_void, buf as *mut c_void)?; }
|
|
||||||
at += step as usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn step_to_syscall(pid: Pid) -> Result<usize> {
|
|
||||||
let mut registers;
|
|
||||||
let mut addr;
|
|
||||||
let mut instructions;
|
|
||||||
|
|
||||||
// seek to syscall
|
|
||||||
loop {
|
|
||||||
registers = ptrace::getregs(pid)?;
|
|
||||||
addr = registers.rip as usize;
|
|
||||||
instructions = ptrace::read(pid, addr as *mut c_void)?;
|
|
||||||
// println!("@ 0x{:X} [{:x}]", insn_addr, curr_instr);
|
|
||||||
|
|
||||||
if instructions & 0xFFFF == 0x050F {
|
|
||||||
return Ok(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
ptrace::step(pid, None)?;
|
|
||||||
waitpid(pid, None)?;
|
|
||||||
}
|
|
||||||
}
|
|
60
src/needle/senders.rs
Normal file
60
src/needle/senders.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use std::ffi::c_void;
|
||||||
|
|
||||||
|
use nix::{Result, unistd::Pid, sys::ptrace, libc::{PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_ANON}};
|
||||||
|
|
||||||
|
use crate::{injector::RemoteOperation, syscalls::RemoteMMap};
|
||||||
|
|
||||||
|
const WORD_SIZE : usize = 32;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn read_buffer(pid: Pid, addr: usize, size: usize) -> Result<Vec<u8>> {
|
||||||
|
let mut out = vec![];
|
||||||
|
|
||||||
|
for i in (0..size).step_by(WORD_SIZE/8) {
|
||||||
|
let data = ptrace::read(pid, (addr + i) as *mut c_void)?;
|
||||||
|
for j in 0..WORD_SIZE/8 {
|
||||||
|
out.push(((data >> (j * 8)) & 0xFF) as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_buffer(pid: Pid, addr: usize, payload: &[u8]) -> Result<()> {
|
||||||
|
let step = WORD_SIZE / 8;
|
||||||
|
let mut at = addr;
|
||||||
|
|
||||||
|
for chunk in payload.chunks(step as usize) {
|
||||||
|
let mut buf : u64 = 0;
|
||||||
|
for (i, c) in chunk.iter().enumerate() {
|
||||||
|
buf |= (*c as u64) << (i * 8);
|
||||||
|
}
|
||||||
|
unsafe { ptrace::write(pid, at as *mut c_void, buf as *mut c_void)?; }
|
||||||
|
at += step as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RemoteString {
|
||||||
|
pub ptr: Option<usize>,
|
||||||
|
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<u64> {
|
||||||
|
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())?;
|
||||||
|
self.ptr = Some(ptr as usize);
|
||||||
|
Ok(ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use nix::{libc::user_regs_struct, Result, sys::{ptrace, wait::waitpid}, unistd::Pid};
|
use nix::{libc::user_regs_struct, Result, sys::{ptrace, wait::waitpid}, unistd::Pid};
|
||||||
|
|
||||||
use crate::{rce::RemoteString, injector::RemoteOperation};
|
use crate::{injector::RemoteOperation, senders::RemoteString};
|
||||||
|
|
||||||
pub trait RemoteSyscall {
|
pub trait RemoteSyscall {
|
||||||
fn registers(&self, regs: &mut user_regs_struct);
|
fn registers(&self, regs: &mut user_regs_struct);
|
||||||
|
@ -73,7 +73,7 @@ impl RemoteSyscall for RemoteOpen {
|
||||||
|
|
||||||
pub struct RemoteWrite {
|
pub struct RemoteWrite {
|
||||||
fd: u64,
|
fd: u64,
|
||||||
buf: RemoteString, // TODO make remote slice or remote bytes
|
buf: RemoteString,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RemoteWrite {
|
impl RemoteWrite {
|
||||||
|
|
Loading…
Reference in a new issue