The second assignment of the SLAE64 exam states:
- Create a Shell_Reverse_TCP shellcode:
- Reverse connects to configure IP and port
- Needs a "passcode"
- If passcode is correct then execute a shell
0x00from the Reverse TCP shellcode discussed in the course
Reverse TCP shellcode
This is quite a lot simpler than the previous exercise in that we don't have to bind to the socket before listening to it and accepting incoming connections. Instead we simple create a socket and initiate a connection to the remote host.
So for this exercise the flow be defined as:
- Allocate a file description through
- Set up the structure defining the address family, address and port to connect to
- Initiate the connection to the remote host
- Print a password prompt and require the correct password to be entered
- If the password was correct a shell is spawn; otherwise it exits
Now, my shellcode is not too dissimilar to what I wrote the first assignment, in fact, I have in re-used large parts. Two sections I did rewrite; one was to use a different method of setting up the stack for
connect(2) and the other was to demonstrate a loop in invoking
Let's have a look at both.
The pre-configured address of the "remote" host we connect to is 127.0.0.1, or
0x0100007f in a format we can push on the stack (reverse hex). However there are two NULL bytes in there! I figured I could simply workaround this and use a different address, but then I remembered the XOR encoder video and figured I could use the XOR operation here.
The truth table for the XOR operation is:
and also note that:
( A xor B ) xor B == A.
So substituting A with our address (containing NULLs) and B with a bitmask of all ones, we can safely use the result (
0x1011116e) in our code. Then we XOR it again with the bitmask before pushing the result (the original address) onto the stack!
mov r13d, 0x1011116e xor r13d, 0x11111111 mov dword [rsp-4], r13d
In the code for the second part of this assignment below I've taken the naive and straightforward approach in removing NULLS in the code to
dup2 the file descriptors:
xor rax, rax mov al, 33 xor rsi, rsi ; %RSI is 0 (STDIN) syscall xor rax, rax mov al, 33 inc rsi ; %RSI is 1 (STDOUT) syscall xor rax, rax mov al, 33 inc rsi ; %RSI is 2 (STDERR) syscall
However for this exercise I used a different method that is actually smaller by 8 bytes for a total of 22 bytes for this code:
xor rsi, rsi xor rcx, rcx mov cl, 0x2 ; upper limit for our loop corresponding to STDERR (2) dup: push rcx xor rax, rax mov al, 33 syscall inc rsi pop rcx loop dup
It's still very straightforward; we zero out both RSI and RCX, then we set RCX to the highest file descriptor number (2) and increment RSI with each iteration of the loop until
loop detects that RCX contains zero and program flow continues.
Removing 0x00 from the discussed shellcode
This was a fairly straightforward removal of NULLs using techniques similar to those in the previous assignment as well as re-applying the bitmask technique described above.
In the end my code was 142 bytes in size compared to 138 of the original.
I have uploaded my code to jasperla/slae64 on GitHub:
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification.
Student ID: SLAE64-1614