Warning
This is an experiment and I can personally guarantee it is unsafe. I describe below some of the unobvious (to me) issues I ended up facing. I'm keen to hear of any possible workarounds for these issues, just open a PR.
This code is based on the following previous work:
- https://bruteratel.com/research/feature-update/2021/01/30/OBJEXEC/
- https://5pider.net/blog/2024/01/27/modern-shellcode-implant-design/
- https://github.com/wumb0/rust_bof
- https://kerkour.com/rust-position-independent-shellcode
- https://github.com/safedv/Rustic64
Some github and rust-lang issues from the journey, thank you friends!:
- Compiling
no_std
fori686-pc-windows-gnu
ignorespanic=abort
- Inclusion of
-lkernel32
and others when compiling#![no_std]
for i686-pc-windows-gnu i686-w64-mingw32-gcc
and relative data addressing (PIC)
The following targets are supported.
Target | Payload Size |
---|---|
i686-linux |
12397B |
x86_64-linux |
12487B |
i686-windows |
4141B |
x86_64-windows |
4120B |
The size discrepancy is due to my lazy locating of
libc.so
in the linux version.
To build one of these targets use cargo make -p $target build
Following is the current output of cargo make -p x86_64-linux run
:
*** [LOADER] ***
[*] Allocate RW Memory
[*] Copy Shellcode Into RW Memory
[*] Set Memory RX
[*] Allocation Start Address: 0x700000000000
[*] Allocation End Address: 0x7000000030c7
[*] Allocation Size: 12487B
*** [STARDUST x86_64] ***
[*] Hello Stardust!
[*] Stardust Start Address: 0x700000000000
[*] Stardust Length: 12487
[*] Stardust Instance: 0x7f1cccd3b000
[*] Hitting Breakpoint!
Using the alloc::fmt::format!
macro will result in a segementation fault due to absolute addresses to reference the pieces
in Arguments { pieces, fmt: None, args }
.
This results in the if !piece.is_empty()
check failing within the following code
@ https://github.com/rust-lang/rust/blob/master/library/core/src/fmt/mod.rs:
/* core::fmt::write () at core/src/fmt/mod.rs:1179 */
/* 1172 */ match args.fmt {
/* 1173 */ None => {
/* 1174 */ // We can use default formatting parameters for all arguments.
/* 1175 */ for (i, arg) in args.args.iter().enumerate() {
/* 1176 */ // SAFETY: args.args and args.pieces come from the same Arguments,
/* 1177 */ // which guarantees the indexes are always within bounds.
/* 1178 */ let piece = unsafe { args.pieces.get_unchecked(i) };
/* 1179 */ if !piece.is_empty() { // This is the check currently failing
/* 1180 */ formatter.buf.write_str(*piece)?;
/* 1181 */ }
/* 1182 */
/* 1183 */ // SAFETY: There are no formatting parameters and hence no
/* 1184 */ // count arguments.
/* 1185 */ unsafe {
/* 1186 */ arg.fmt(&mut formatter)?;
/* 1187 */ }
/* 1188 */ idx += 1;
/* 1189 */ }
/* 1190 */ }
This leads to a call being made to _gcc_except_table
which has been removed by linux.ld resulting in a segmentation fault.
Note
Patching the GOT appeared to get us a little further along before it crashes. YAY!🥳
Solution: None
A bunch of stuff uses the GOT including calls to functions with the compiler_builtins
crate, e.g. the following functions:
memcpy
memmove
memset
memcmp
bcmp
This resulted in a segmentation fault due to a call
made to a bad/absolute hard-coded memory address stored within the GOT and then referenced by a RIP-relative offset.
Similarly using extern "C"
functions directly may result in the use the GOT.
The following code was used to ensure memcpy
was required by the binary:
let src = alloc::string::String::from("SSECCUS\t\t:ypcmem gnitseT");
let dst: String = src.chars().rev().collect();
info!(&dst);
Patching the hardcoded addresses with GDB results in a successful execution as seen below:
Solution:
- Patch the GOT dynamically during runtime, this appears to allow the use of
compiler_builtins
! - Don't directly call
extern
functions before patching, call them withinasm!
macro
You're best off reading this (or maybe you're not, won't get that time back) i686-w64-mingw32-gcc
and relative data addressing (PIC).
Solution:
- Compile an i686 elf
- Patch the GOT dynamically during runtime
- Specify
stdcall
where required. - "It's all just machine code in the end" - Me while justifying this mess