1. Reminder

Before going any further, I recommend you to look at the parameter passage accessible here: Part-3: NASM Anatomy / Syscall / Passing Argument.

2. Tracing system call

Under Linux, there is a program to analyze system calls made by other programs.
By analyzing the program, we directly see the parameters that are passed to the function and how they are passed.
Let us take the following example:

#include <stdlib.h>
int main()
    return 0;
$ strace -f ./system
[pid 21726] execve("/bin/sh", ["sh", "-c", "/bin/ls"], 0x7fffffffdd78 /* 78 vars */) = 0
[pid 21726] brk(NULL)                   = 0x555555773000
[pid 21726] access("/etc/ld.so.preload", R_OK) = -1 ENOENT (Aucun fichier ou dossier de ce type)

We can conclude that the "system" function is a wrapper of the "execve" function.

3. What is the system call "execve"?

Let's look at the description provided in the manual.

       execve - execute program

       #include <unistd.h>

       int execve(const char *filename, char *const argv[],
                  char *const envp[]);

       execve() executes the program pointed to by filename.  This causes the
       program that is currently being run by the calling process to be
       replaced with a new program, with newly initialized stack, heap, and
       (initialized and uninitialized) data segments.

Concretely, execve will replace the current process with a new process. To achieve this he needs:

4. Using execve in C

Before developing our shellcode, it is useful/interesting to develop a small program in C to check the operation of the function we want to call.

#include <unistd.h>

int main()
    char *filename="/bin/ls";
    char *const argv[] = {filename,NULL};
    char *const envp[] = {NULL};

    execve(filename, argv, envp);

5. Creating the shellcode

We can now create our shellcode.

global _start


jmp call_shellcode

    ; get filename
    pop rdi

    xor rax, rax
    ; filename=/bin/sh\0
    mov [rdi+7], byte ah

    mov [rdi+8], rdi

    ; *argv[] = { &filename, 0}
    lea rsi, [rdi+8]
    ; *envp[] = {0}
    lea rdx, [rax]

    ; execve syscall 
    mov al, 59

    call shellcode
    filename: db "/bin/sh"

6. Shellcode compilation and testing

nasm execve.s -f elf64 && ld execve.o -o execve

We get the opcodes to test our shellcode:

$ objdumptoshellcode execve

To test our shellcode, we will use a program developed in C:


unsigned char code[] = \

int main()
    printf("Shellcode Length:  %d\n", (int)strlen(code));
    int (*ret)() = (int(*)())code;

In order to compile our shellcode, we must not forget to remove the following security options:

Which gives:

$ gcc -z execstack -fno-stack-protector shellcode.c -o shellcode

We try:

Shellcode Length:  36
$ whoami

And voilĂ .

7. Going further

To go further, you can redevelop this shellcode using the other techniques.
You can also reduce the number of opcode of the shellcode. Shellcode example on ExploitBD