diff --git a/README.md b/README.md index cfd927e..1ff8e06 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,35 @@ yay -S arsenal ./run -t -e # just like the -t mode but with direct execution in the other pane without quitting arsenal ``` +## Launch new tmux mode + +Previous mode was working only when only one session was running. Since libtmux does not provide a way to identify tmux session the code is running in. + + +This new mode will need a `pane_path` which pattern is `::[]` + +Regarding `pane_path` : +- session identified by `session_name` must exist. +- if windows identified by `window_name`: + - does not exist: it will be created then pane number is ignored if specified + - exist and `pane_id`: + - is not specified: command will be sent to all panes in the window + - does not exist: a new pane will be created (similar to previous mode) + - exist: guess what + +Note: within tmux session `prefix-q` willdisplay panes number + +``` +# will send command to all pane in arsenal-windows (windows creation needed) +./run --tmux-new tmux-pentest:arsenal-windows: + +# will send command to a new pane +./run --tmux-new tmux-pentest:arsenal-windows:99 + +# will send command to pane 3 +./run --tmux-new tmux-pentest:arsenal-windows:3 +``` + ## Add external cheatsheets You could add your own cheatsheets insode the my_cheats folder or in the ~/.cheats folder. diff --git a/arsenal/app.py b/arsenal/app.py index 65a37c3..f6a0c90 100644 --- a/arsenal/app.py +++ b/arsenal/app.py @@ -15,7 +15,16 @@ from .modules import gui as arsenal_gui +class PanePathAction(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + if not re.fullmatch("^[^:]+:[^:]+:[0-9]*", values): + raise ValueError("Not a PANE_PATH") + setattr(namespace, self.dest, values) + + class App: + tmux_server = None + tmux_session = None def __init__(self): pass @@ -47,6 +56,8 @@ def get_args(self): group_out.add_argument('-x', '--copy', action='store_true', help='Output to clipboard') group_out.add_argument('-e', '--exec', action='store_true', help='Execute cmd') group_out.add_argument('-t', '--tmux', action='store_true', help='Send command to tmux panel') + group_out.add_argument("-z", "--tmux-new", action=PanePathAction, metavar="PANE_PATH", + help="Send command to tmux pane", dest="tmux_new") group_out.add_argument('-c', '--check', action='store_true', help='Check the existing commands') group_out.add_argument('-f', '--prefix', action='store_true', help='command prefix') parser.add_argument('-V', '--version', action='version', version='%(prog)s (version {})'.format(__version__)) @@ -55,6 +66,8 @@ def get_args(self): def run(self): args = self.get_args() + if args.tmux_new: + self.check_tmux(args) # load cheatsheets cheatsheets = cheat.Cheats().read_files(config.CHEATS_PATHS, config.FORMATS, @@ -158,6 +171,9 @@ def start(self, args, cheatsheets): except ImportError: self.prefil_shell_cmd(cmd) break + elif args.tmux_new: + self.process_tmux(args, cmd.cmdline) + break # DEFAULT: Prefill Shell CMD else: self.prefil_shell_cmd(cmd) @@ -192,6 +208,50 @@ def prefil_shell_cmd(self, cmd): # restore TTY attribute for stdin termios.tcsetattr(stdin, termios.TCSADRAIN, oldattr) + def check_tmux(self, args): + try: + import libtmux + except ImportError: + raise RuntimeError("Could not load libtmux") from None + pane_path = args.tmux_new.split(":") + try: + self.tmux_server = libtmux.Server() + self.tmux_session = self.tmux_server.sessions.get(session_name=pane_path[0]) + except libtmux._internal.query_list.ObjectDoesNotExist: + raise RuntimeError(f"Could not find session {pane_path[0]}") from None + + def process_tmux(self, args, cmdline): + pane_path = args.tmux_new.split(":") + new_window = False + import libtmux + try: + window = self.tmux_session.select_window(pane_path[1]) + except libtmux.exc.LibTmuxException: + window = self.tmux_session.new_window(attach=False, window_name=pane_path[1]) + new_window = True + if new_window: + pane = window.panes[0] + elif pane_path[2] == "": # all panes + pane = None + elif int(pane_path[2]) > len(window.panes): + pane = window.split_window(attach=False) + time.sleep(0.3) + else: + pane = window.panes[int(pane_path[2])] + if pane: + if args.exec: + pane.send_keys(cmdline) + else: + pane.send_keys(cmdline, enter=False) + pane.select_pane() + else: + for pane in window.panes: + if args.exec: + pane.send_keys(cmdline) + else: + pane.send_keys(cmdline, enter=False) + pane.select_pane() + def main(): try: diff --git a/arsenal/modules/config.py b/arsenal/modules/config.py index 94dfcf8..b40f90e 100644 --- a/arsenal/modules/config.py +++ b/arsenal/modules/config.py @@ -24,4 +24,4 @@ savevarfile = join(HOMEPATH, ".arsenal.json") -PREFIX_GLOBALVAR_NAME = "arsenal_prefix_cmd" \ No newline at end of file +PREFIX_GLOBALVAR_NAME = "arsenal_prefix_cmd"