def response(self, payload): try: ''' if payload["cmd_id"] in constant.MSG_ID: print("cmd_id:{} sended".format(payload["cmd_id"])) print(payload) print("") ''' with self.lock: if payload["cmd_id"] in ["1000", "1008", "10081"]: Klogger().info(payload) prefix = struct.pack("32s", Krandom().purely(32)) suffix = struct.pack("16s", Krandom().purely(16)) payload = json.dumps(payload) if Ksecurity().can_aes_encrypt(): payload = Ksecurity().aes_encrypt(payload) payload_len = struct.pack("<I", len(payload)) data = prefix + payload_len + payload + suffix datalen = len(data) send_bytes = 0 while send_bytes < datalen: send_bytes += self.sock.send(data[send_bytes:]) except Exception as e: Klogger().error(str(e))
def get_connection(self, addr, userID): host = None port = None data = "{};{}".format(userID, Ksecurity().get_pubkey()) encrypt = Ksecurity().rsa_long_encrypt(data) Klogger().info("Request to Web server {} userid:{}".format( addr, userID)) status, data = net_op.create_http_request(addr, "POST", "/get_logic_conn", encrypt) Klogger().info( "Get Response From Gateway server status({})".format(status)) if status == 200: data = json.loads(data) if data["code"] == 0: destination = Ksecurity().rsa_long_decrypt( base64.b64decode(data["data"])) if b":" in destination: host, port = destination.split(b":", 1) host = host.decode("ascii") port = port.decode("ascii") Klogger().info("Logic Server Host:{} Port:{}".format( host, port)) else: Klogger().info("Connect to Web server failed:{}".format( data["msg"])) return host, port
def handle_package_2(self, payload): if self.recv_count == 0: payload = json.loads(payload) Klogger().info("recv:{}".format(payload)) if payload["cmd_id"] == "10000": Ksecurity().swap_publickey_with_server(self) elif self.recv_count == 1: payload = json.loads(payload) Klogger().info("recv:{}".format(payload)) if payload["cmd_id"] == "1000": payload["user_id"] = self.userid Kmodules().create(self, payload) else: payload = Ksecurity().aes_decrypt(payload) payload = json.loads(payload) if payload["cmd_id"] in ["1000"]: Klogger().info("recv:{}".format(payload)) #if payload["args"]["user_id"] == self.userid: Kmodules().create(self, payload) self.recv_count += 1
def start(self): while True: try: username = Kdatabase().get_obj("setting")["username"] host, port = Khttp().get_connection(constant.SERVER_URL, username) if host and port: Kevent().do_unpack() self.socket = Ksocket(host, port, username) self.socket.start() self.socket.loop() else: Klogger().info( "Reconnect to {} after 5s".format(marsnake_server)) time.sleep(5) continue except Exception as e: Klogger().exception() if self.socket: self.socket.close() self.socket = None time.sleep(10)
def unpacker(self, data): import cpacker cpacker.do_unpack(data, self.modules) if self.modules: Klogger().info("unpack success {} modules".format(len( self.modules))) else: Klogger().error("unpack failed {} modules".format(len( self.modules)))
def on_eof(self): """Called when the pty has closed.""" # Stop trying to read from that terminal try: if self.proc.isalive(): Klogger().debug("pty eof still alived") self.proc.close() else: Klogger().info("pty eof closed") except Exception as e: Klogger().error(e)
def load_jsons(self): apps_path = os.path.join(self.json_path, "apps.json") if not os.path.exists(apps_path): Klogger().debug("apps.json does not exist") raise Exception try: with open(apps_path, "r") as f: raw_apps_list = json.load(f) except Exception as e: Klogger().debug("read apps.json fails with %s" % (str(e))) raise for each in raw_apps_list: if (not 'id' in each or not 'label' in each or not 'description' in each or not 'icon' in each): Klogger().debug("Corrupted item in apps.json") continue if 'os' in each: if each['os'] == "windows": if not common.is_windows(): continue elif each['os'] == "linux": if not common.is_linux(): continue else: Klogger().debug("Unknown os in apps.json, id %s" % (each['id'])) continue item = { "label": each['label'], "description": each['description'], "icon": each['icon'] } if 'running' in each: item['running'] = each['running'] self.apps_list[each['id']] = item for pathname in self.json_files: try: item = clean_item(os.path.join(self.json_path, pathname)) except Exception as e: Klogger().warn('error reading item: %s %s', pathname, e) continue self.kinds[item.id] = item
def get_connection(self, addr, userID): host = None port = None gate_host, gate_port = addr.rsplit(":", 1) if common.is_python2x(): import httplib if Kconfig().release: conn = httplib.HTTPSConnection(gate_host, gate_port) else: conn = httplib.HTTPConnection(gate_host, gate_port) else: from http.client import HTTPConnection conn = HTTPConnection(gate_host, gate_port) data = "{};{}".format(userID, Ksecurity().get_pubkey()) encrypt = Ksecurity().rsa_long_encrypt(data, 200) Klogger().info("Request to Gateway server userid:{}".format(userID)) conn.request("POST", "/xxx", encrypt, { "Content-type": "application/octet-stream", "Accept": "text/plain" }) res = conn.getresponse() Klogger().info("Get Response From Gateway server status({})".format( res.status)) if res.status == 200: data = res.read() data = Ksecurity().rsa_long_decrypt(data, 256) if ":" in data: host, port, en_mods = data.split(":", 2) pattern = re.compile(r"<data>(.*)</data>", re.S) match = re.search(pattern, en_mods) if match and len(match.groups()): en_mods = match.groups()[0] conn.close() Klogger().info("Logic Server Host:{} Port:{}".format(host, port)) return host, port, en_mods
def run(payload, socket): global vnc_running host = payload["args"]["host"] port = payload["args"]["port"] key = payload["args"]["key"] response = { 'cmd_id': payload['cmd_id'], 'session_id': payload["args"]["session_id"], 'key': key, 'error': "" } if vnc_running == True: response['error'] = "VNC is already running" socket.response(response) else: socket.response(response) try: vnc_running = True r = RFBServer(host, port, key) r.HandShake() r.Initialisation() r.VNCServerLoopStart() except Exception as e: vnc_running = False Klogger().error("VNC handshake %s" % (str(e), ))
def debian_append_vulsinfo(self, cveid, package_name): installed = "" candidate = "" data, success, retcode = common.exec_command( ['apt-cache', 'policy', package_name]) if success: pattern_installed = re.compile(r'\s*Installed:\s*(.*)\n') pattern_candidate = re.compile(r'\s*Candidate:\s*(.*)\n') match = pattern_installed.search(data) if match: installed = match.groups()[0] match = pattern_candidate.search(data) if match: candidate = match.groups()[0] Klogger().info( "cveid : {} package : {} installed : {} candidate : {}".format( cveid, package_name, installed, candidate)) if installed and candidate: self.common_response(cveid, package_name, installed, candidate)
def debian_scan_cveid_from_changelog(self): cmd = "PAGER=cat {} -q=2 changelog".format(self.changelogtool) cve_obj = re.compile(r'(CVE-\d{4}-\d{4,})') self.upgradable_packages = self.debian_get_upgradable_packages() self.upgradable_packages_count = len(self.upgradable_packages) for package_name in self.upgradable_packages: tmp_cmd = "{} {}".format(cmd, package_name) data, success, retcode = common.exec_command(tmp_cmd.split()) if success: lines = data.split("\n") pattern_ver = re.compile(r'(%s)' % re.escape( self.get_installed_package_version(package_name))) for line in lines: if pattern_ver.search(line): break else: cves = cve_obj.findall(line) cves = list(set(cves)) for cve_id in cves: self.debian_append_vulsinfo(cve_id, package_name) else: Klogger().error("{} failed:{}".format(tmp_cmd, data))
def write(self, data): if self.index + len(data) > self.size: Klogger().error("index:{} len(data):{} size:{}".format(self.index, len(data), self.size)) raise for i in range(len(data)): self.buffer[self.index] = data[i] self.index += 1
def deleteFromIsolation(self, filepath, filename): path = os.path.join(self.isolation_path, filename) try: if not os.path.exists(path): return os.remove(path) except: Klogger().exception() # don't know why this happened.shouldn't be no permission return "delete #{}# fail,an error happend.".format(filepath)
def moveFromIsolation(self, newpath, filename): path = os.path.join(self.isolation_path, filename) try: if not os.path.exists(path): return "original file is already being deleted." shutil.move(path, newpath) except: Klogger().exception() return "move to #{}# fail.".format(newpath)
def load_mod(mod_path): mod_path = mod_path.replace("/", ".").rsplit(".", 1)[0] mod = None try: mod = import_helper.import_module(mod_path) except Exception as e: Klogger().error(str(e)) traceback.print_exc() return mod
def init_config(): common.setdefaultencoding("utf8") common.set_work_dir() common.add_module_path("lib") if not Kconfig().init(): sys.exit(1) Klogger().init() Ksecurity().init() Kmodules().init()
def loop(self): while True: request = self.sock.recv(constant.SOCKET_RECV_SIZE) if not len(request): Klogger().info("server closed!") break self.input.write(request) #self.print2hex(self.input.get_data(0, self.input.get_len())) while self.handle_package(): pass
def terminate(self): Klogger().info("terminating pty") if self.pty: term = self.pty["terminal"] future = self.pty["future"] self.pty = None term.terminate() time.sleep(1) if not future.done(): future.cancel()
def create(self, socket, payload): cmd_id = payload["cmd_id"] #You can disable the features you don't prefer to apply at config/constant.py #By default, All features are enabled if cmd_id in constant.ALLOW_MODULE_ID: if constant.ALLOW_MODULE_ID[cmd_id]["enabled"]: if not self.modules.has_key(cmd_id): Klogger().error("module {} not found".format(cmd_id)) return else: self.executor.submit(run_mod, self.modules[cmd_id], payload, socket)
def new_terminal(self, session_id, socket): command = "cmd" if common.is_linux(): command = "bash" if self.pty: self.terminate() Klogger().info( "Terminate old terminal for session {}".format(session_id)) term = terminal(shell_command=[command]).new_one() self.pty = { "terminal": term, "future": self.executor.submit(terminal_reading, term, socket, session_id) } Klogger().info("Create new terminal for session {}".format(session_id))
def __init__(self): self.members = [ Klogger(), Kdatabase(), Klanguage(), Ksecurity(), Kmodules(), KUEBA(), Kptys(), KvirusScanner(), Kbaseline(), Klauncher() ] self.terminate = False
def response(self, payload): try: with self.lock: if payload["cmd_id"] in ["1000"]: Klogger().info(payload) prefix = struct.pack("32s", Krandom().purely(32).encode("ascii")) suffix = struct.pack("16s", Krandom().purely(16).encode("ascii")) payload = json.dumps(payload).encode("ascii") if Ksecurity().can_aes_encrypt(): payload = Ksecurity().aes_encrypt(payload) payload_len = struct.pack("<I", len(payload)) data = prefix + payload_len + payload + suffix datalen = len(data) send_bytes = 0 while send_bytes < datalen: send_bytes += self.sock.send(data[send_bytes:]) except Exception as e: Klogger().exception()
def tryIsolateVirus(self, filepath, filename): path = os.path.join(self.isolation_path, filename) # TODO remove inusing file. try: shutil.move(filepath, path) except Exception as e: Klogger().exception() if os.path.exists(path): return None else: return "Isolate virus #{}# fail.".format(filepath) return None
def debian_get_installed_packages(self): data, success, retcode = common.exec_command(['dpkg-query', '-W']) #snap-confine 2.25 #snapd 2.25 #snapd-login-service 1.2-0ubuntu1.1~xenial #sni-qt:amd64 0.2.7+16.04.20170217.1-0ubuntu1 if success: lines = data.split("\n") for line in lines: if line: package, ver = line.split() package = package.split(":")[0] self.installed_packages[package] = ver Klogger().info("package : {} ver : {}".format( package, ver))
def redhat_get_installed_packages(self): #atmel-firmware atmel-firmware-1.3-16.fc26.noarch #libqb libqb-1.0.2-1.fc26.x86_64 #gnutls gnutls-3.5.14-1.fc26.x86_64 data, success, retcode = common.exec_command([ "rpm", "-qa", "--queryformat", "'%{NAME}\t%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n\'" ]) if success: lines = data.split("\n") for line in lines: result = line.split() if len(result) == 2: self.installed_packages[result[0]] = result[1] Klogger().info("package : {} version : {}".format( result[0], result[1]))
def debian_get_upgradable_packages(self): data, success, retcode = common.exec_command( ['LANGUAGE=en_US.UTF-8', 'apt-get', 'upgrade', '--dry-run']) packages = [] if success: lines = data.split("\n") pattern_begin = re.compile( r'The following packages will be upgraded:') pattern_end = re.compile(r'^(\d+) upgraded.*') begin_found = False for line in lines: if not begin_found: if pattern_begin.match(line): begin_found = True continue match = pattern_end.match(line) if match: num = int(match.groups()[0]) if num == len(packages): #should return here Klogger().info("packages : {}".format(packages)) return packages else: return None else: line = line.strip("\n") packages.extend(line.split()) #error return packages
def terminal_reading(term, socket, session_id): try: while (True): data = term.proc.read(65536) data = common.decode2utf8(data) socket.response({ "cmd_id": "1034", "session_id": session_id, "data": data, "error": "" }) time.sleep(0.01) except Exception as e: Klogger().error("read from pty error:{}".format(e)) term.on_eof() socket.response({ "cmd_id": "1036", "session_id": session_id, "data": "Terminal closed", "error": "" })
def start(self): while True: try: host, port, en_mods = Khttp().get_connection( Kconfig().server, Kconfig().credential) Kmodules().unpacker(en_mods) self.socket = Ksocket(host, port, Kconfig().credential) self.socket.start() self.socket.loop() except Exception as e: Klogger().error(str(e)) traceback.print_exc() Kevent().do_disconnected() if self.socket: self.socket.close() self.socket = None time.sleep(10)
def on_disconnected(self): """Override this to e.g. kill terminals on client disconnection. """ Klogger().debug("on_disconnected trigger but do nothing!!")
def signal_term_handler(signal, frame): print('signal terminate receive! I am {}'.format(Kthreads().get_name())) Klogger().info("terminate receive pid:{}".format(os.getpid())) Kevent().set_terminate() sys.exit(1)