fredy688
fredy688

Reputation: 41

Linux 64-bit shellcode

I'm trying to write my first "Hello world" shellcode on my Ubuntu 64-bit, but it doesn't work.

I have file hello.asm:

; 64-bit "Hello World!" in Linux NASM

global _start            ; global entry point export for ld

section .text
_start:

    ; sys_write(stdout, message, length)

    mov    rax, 1        ; sys_write
    mov    rdi, 1        ; stdout
    mov    rsi, message    ; message address
    mov    rdx, length    ; message string length
    syscall

    ; sys_exit(return_code)

    mov    rax, 60        ; sys_exit
    mov    rdi, 0        ; return 0 (success)
    syscall

section .data
    message: db 'Hello, world!',0x0A    ; message and newline
    length:    equ    $-message        ; NASM definition pseudo-instruction

I used this commands:

nasm -felf64 hello.asm -o hello.o
ld -o hello hello.o
objdump -d hello

I put shellcode from objdump into my C program:

char code[] = "\xb8\x01\x00\x00\x00\xbf\x01\x00\x00\x00\x48\xbe\xd8\x00\x60\x00\x00\x00\x00\x00\xba\x0e\x00\x00\x00\x0f\x05\xb8\x3c\x00\x00\x00\xbf\x00\x00\x00\x00\x0f\x05";

int main(int argc, char **argv)
{
    int (*func)();
    func = (int (*)()) code;
    (int)(*func)();
    return 0;
}

and compile it in gcc, but after run I've got "Segmentation fault (core dumped)".

I have no idea what I'm doing wrong. Assembler code seems to work, because when i run ./hello it prints "Hello world".

Upvotes: 2

Views: 9774

Answers (4)

David C. Rankin
David C. Rankin

Reputation: 84642

You can simply, compile with nasm -felf64 and link with ld. There is no need for gcc. I believe the issue you stumbled on was the x86 style equ $-message calculation in your code. For x86_64, that is not valid. Here is a quick example:

section .data
    string1 db  0xa, "  Hello StackOverflow!!!", 0xa, 0xa, 0

section .text
    global _start

    _start:
        ; calculate the length of string
        mov     rdi, string1        ; string1 to destination index
        xor     rcx, rcx            ; zero rcx
        not     rcx                 ; set rcx = -1
        xor     al,al               ; zero the al register (initialize to NUL)
        cld                         ; clear the direction flag
        repnz   scasb               ; get the string length (dec rcx through NUL)
        not     rcx                 ; rev all bits of negative results in absolute value
        dec     rcx                 ; -1 to skip the null-terminator, ecx contains length
        mov     rdx, rcx            ; put length in rdx
        ; write string to stdout
        mov     rsi, string1        ; string1 to source index
        mov     rax, 1              ; set write to command
        mov     rdi,rax             ; set destination index to rax (stdout)
        syscall                     ; call kernel

        ; exit
        xor     rdi,rdi             ; zero rdi (rdi hold return value)
        mov     rax, 0x3c           ; set syscall number to 60 (0x3c hex)
        syscall                     ; call kernel

Compile/Output

$ nasm -felf64 -o hello-stack_64.o hello-stack_64.asm
$ ld -o hello-stack_64 hello-stack_64.o
$ ./hello-stack_64

  Hello StackOverflow!!!

Upvotes: 1

zwol
zwol

Reputation: 140856

There's two layers of problem here.

1) char data[] = ... will be put in the data segment, which is not executable. You need const char data[] = ... so that it is put in the text segment instead. (Technically, it will be in the read-only data segment, which could be marked non-executable, but currently no one does that as far as I know.)

2) This is what I get when I disassemble the contents of data:

   0:   b8 01 00 00 00          mov    eax, 0x1
   5:   bf 01 00 00 00          mov    edi, 0x1
   a:   48 be d8 00 60 00 00    movabs rsi, 0x6000d8
  11:   00 00 00 
  14:   ba 0e 00 00 00          mov    edx, 0xe
  19:   0f 05                   syscall 
  1b:   b8 3c 00 00 00          mov    eax, 0x3c
  20:   bf 00 00 00 00          mov    edi, 0x0
  25:   0f 05                   syscall 

and this is what I get when I try to run it:

$ strace ./a.out
execve("./a.out", ["./a.out"], [/* 38 vars */]) = 0
write(1, 0x6000d8, 14)                  = -1 EFAULT (Bad address)
_exit(0)                                = ?
+++ exited with 0 +++

You can see that this is not that far off from what you wanted, but it clearly does not work, there being nothing at absolute address 0x6000d8. The problem is that you only put the code into data, not the string. You need to put the string immediately after the code and use PC-relative addressing to get at it. nighter's answer shows how that's done (under the additional constraint that no \x00 bytes may appear in data, which would be necessary if you were actually exploiting a typical buffer overflow here instead of just goofing around with machine language).

Upvotes: 0

nighter
nighter

Reputation: 31

It's good practice to remove nullbytes in case you want to inject it into a buffer, but main issue is probably that you have the string in the data segment?

I rewrote it like this and got it to work.

    global _start
    _start:
    jmp short string

    code:
    pop rsi
    xor rax, rax
    mov al, 1
    mov rdi, rax
    mov rdx, rdi
    add rdx, 14
    syscall

    xor rax, rax
    add rax, 60
    xor rdi, rdi
    syscall

    string:
    call code
    db  'Hello, world!',0x0A

$ nasm -felf64 hello.asm -o hello.o
$ ld -s -o hello hello.o
$ for i in $(objdump -d hello |grep "^ " |cut -f2); do echo -n '\x'$i; done; echo
\xeb\x1e\x5e\x48\x31\xc0\xb0\x01\x48\x89\xc7\x48\x89\xfa\x48\x83\xc2\x0e\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xdd\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21\x0a

char code[] = "\xeb\x1e\x5e\x48\x31\xc0\xb0\x01\x48\x89\xc7\x4\x89\xfa  \x48\x83\xc2\x0e\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xdd\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21\x0a";

int main(int argc, char **argv)
{
    int (*func)();
    func = (int (*)()) code;
    (int)(*func)();
     return 0;
}

$ gcc -fno-stack-protector -z execstack -o code code.c    
$./code
Hello, world!

Upvotes: 3

Mohammad Reza  Ramezani
Mohammad Reza Ramezani

Reputation: 768

Hope is not too late to answer to this question :)

You must use this code to compile your C code (To disable stack protection and make it executable):

gcc -fno-stack-protector -z execstack -o hello hello.c

Good luck

Upvotes: 0

Related Questions