Linux Shellcode "Hello, World!"

35,393

As BSH mentioned, your shellcode does not contain the message bytes. Jumping to the MESSAGE label and calling the GOBACK routine just before defining the msg byte was a good move as the address of msg would be on the top of the stack as return address which could be popped to ecx, where the address of msg is stored.

But both yours and BSH's code has a slight limitation. It contains NULL bytes ( \x00 ) which would be considered as end of string when dereferenced by the function pointer.

There is a smart way around this. The values you store into eax, ebx and edx are small enough to be directly written into the lower nibbles of the respective registers in one go by accessing al, bl and dl respectively. The upper nibble may contain junk value so it can be xored.

b8 04 00 00 00 ------ mov $0x4,%eax


becomes

b0 04          ------ mov $0x4,%al
31 c0          ------ xor    %eax,%eax


Unlike the prior instruction set, the new instruction set does not contain any NULL byte.

So, the final program looks like this :

global _start

section .text

_start:
jmp message

proc:
    xor eax, eax
    mov al, 0x04
    xor ebx, ebx
    mov bl, 0x01
    pop ecx
    xor edx, edx
    mov dl, 0x16
    int 0x80

    xor eax, eax
    mov al, 0x01
    xor ebx, ebx
    mov bl, 0x01   ; return 1
    int 0x80

message:
    call proc
    msg db " y0u sp34k 1337 ? "

section .data

Assembling and linking :

$ nasm -f elf hello.asm -o hello.o
$ ld -s -m elf_i386 hello.o -o hello
$ ./hello
 y0u sp34k 1337 ? $ 

Now extract the shellcode from the hello binary :

$ for i in `objdump -d hello | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\\x$i" ; done

output:

\xeb\x19\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x59\x31\xd2\xb2\x12\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xb3\x01\xcd\x80\xe8\xe2\xff\xff\xff\x20\x79\x30\x75\x20\x73\x70\x33\x34\x6b\x20\x31\x33\x33\x37\x20\x3f\x20

Now we can have our driver program to launch the shellcode.

#include <stdio.h>

char shellcode[] = "\xeb\x19\x31\xc0\xb0\x04\x31\xdb"
                   "\xb3\x01\x59\x31\xd2\xb2\x12\xcd"
                   "\x80\x31\xc0\xb0\x01\x31\xdb\xb3"
                   "\x01\xcd\x80\xe8\xe2\xff\xff\xff"
                   "\x20\x79\x30\x75\x20\x73\x70\x33"
                   "\x34\x6b\x20\x31\x33\x33\x37\x20"
                   "\x3f\x20";


int main(int argc, char **argv) {
    (*(void(*)())shellcode)();
    return 0;
}

There are certain security features in modern compilers like NX protection which prevents execution of code in data segment or stack. So we should explicitly specify the compiler to disable these.

$ gcc -g -Wall -fno-stack-protector -z execstack launcher.c -o launcher

Now the launcher can be invoked to launch the shellcode.

$ ./launcher
 y0u sp34k 1337 ? $ 

For more complex shellcodes, there would be another hurdle. Modern Linux kernels have ASLR or Address Space Layout Randomization You may need to disable this before your inject the shellcode, especially when it is through buffer overflows.

root@localhost:~# echo 0 > /proc/sys/kernel/randomize_va_space 
Share:
35,393
user1408643
Author by

user1408643

Updated on July 28, 2020

Comments

  • user1408643
    user1408643 almost 4 years

    I have the following working NASM code:

    global _start
    
    section .text
    
    _start:
        mov eax, 0x4
        mov ebx, 0x1
        mov ecx, message
        mov edx, 0xF
        int 0x80
    
        mov eax, 0x1
        mov ebx, 0x0
        int 0x80
    
    section .data
        message: db "Hello, World!", 0dh, 0ah
    

    which prints "Hello, World!\n" to the screen. I also have the following C wrapper which contains the previous NASM object code:

    char code[] =
    "\xb8\x04\x00\x00\x00"
    "\xbb\x01\x00\x00\x00"
    "\xb9\x00\x00\x00\x00"
    "\xba\x0f\x00\x00\x00"
    "\xcd\x80\xb8\x01\x00"
    "\x00\x00\xbb\x00\x00"
    "\x00\x00\xcd\x80";
    
    int main(void)
    {
        (*(void(*)())code)();
    }
    

    However when I run the code, it seems like the assembler code isn't executed, but the program exits fine. Any ideas?

    Thanks