windows: add --remote-shell option to select cmd/powershell

This commit is contained in:
nom3ad 2024-07-16 23:38:36 +05:30 committed by Brian May
parent dff6950c4c
commit bac2a6b0c7
5 changed files with 74 additions and 52 deletions

View File

@ -181,6 +181,11 @@ Options
in a non-standard location or you want to provide extra in a non-standard location or you want to provide extra
options to the ssh command, for example, ``-e 'ssh -v'``. options to the ssh command, for example, ``-e 'ssh -v'``.
.. option:: --remote-shell
For Windows targets, specify configured remote shell program alternative to defacto posix shell.
It would be either ``cmd`` or ``powershell`` unless something like git-bash is in use.
.. option:: --no-cmd-delimiter .. option:: --no-cmd-delimiter
Do not add a double dash (--) delimiter before invoking Python on Do not add a double dash (--) delimiter before invoking Python on

View File

@ -211,8 +211,8 @@ class FirewallClient:
self.auto_nets = [] self.auto_nets = []
argv0 = sys.argv[0] argv0 = sys.argv[0]
# argv0 is either be a normal python file or an executable. # argv0 is either be a normal Python file or an executable.
# After installed as a package, sshuttle command points to an .exe in Windows and python shebang script elsewhere. # After installed as a package, sshuttle command points to an .exe in Windows and Python shebang script elsewhere.
argvbase = (([sys.executable, sys.argv[0]] if argv0.endswith('.py') else [argv0]) + argvbase = (([sys.executable, sys.argv[0]] if argv0.endswith('.py') else [argv0]) +
['-v'] * (helpers.verbose or 0) + ['-v'] * (helpers.verbose or 0) +
['--method', method_name] + ['--method', method_name] +
@ -591,7 +591,7 @@ def ondns(listener, method, mux, handlers):
def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename, def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
python, latency_control, latency_buffer_size, python, latency_control, latency_buffer_size,
dns_listener, seed_hosts, auto_hosts, auto_nets, daemon, dns_listener, seed_hosts, auto_hosts, auto_nets, daemon,
to_nameserver, add_cmd_delimiter): to_nameserver, add_cmd_delimiter, remote_shell):
helpers.logprefix = 'c : ' helpers.logprefix = 'c : '
debug1('Starting client with Python version %s' debug1('Starting client with Python version %s'
@ -607,6 +607,7 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
ssh_cmd, remotename, python, ssh_cmd, remotename, python,
stderr=ssyslog._p and ssyslog._p.stdin, stderr=ssyslog._p and ssyslog._p.stdin,
add_cmd_delimiter=add_cmd_delimiter, add_cmd_delimiter=add_cmd_delimiter,
remote_shell=remote_shell,
options=dict(latency_control=latency_control, options=dict(latency_control=latency_control,
latency_buffer_size=latency_buffer_size, latency_buffer_size=latency_buffer_size,
auto_hosts=auto_hosts, auto_hosts=auto_hosts,
@ -809,7 +810,7 @@ def main(listenip_v6, listenip_v4,
latency_buffer_size, dns, nslist, latency_buffer_size, dns, nslist,
method_name, seed_hosts, auto_hosts, auto_nets, method_name, seed_hosts, auto_hosts, auto_nets,
subnets_include, subnets_exclude, daemon, to_nameserver, pidfile, subnets_include, subnets_exclude, daemon, to_nameserver, pidfile,
user, group, sudo_pythonpath, add_cmd_delimiter, tmark): user, group, sudo_pythonpath, add_cmd_delimiter, remote_shell, tmark):
if not remotename: if not remotename:
raise Fatal("You must use -r/--remote to specify a remote " raise Fatal("You must use -r/--remote to specify a remote "
@ -1158,7 +1159,7 @@ def main(listenip_v6, listenip_v4,
return _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename, return _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
python, latency_control, latency_buffer_size, python, latency_control, latency_buffer_size,
dns_listener, seed_hosts, auto_hosts, auto_nets, dns_listener, seed_hosts, auto_hosts, auto_nets,
daemon, to_nameserver, add_cmd_delimiter) daemon, to_nameserver, add_cmd_delimiter, remote_shell)
finally: finally:
try: try:
if daemon: if daemon:

View File

@ -116,6 +116,7 @@ def main():
opt.group, opt.group,
opt.sudo_pythonpath, opt.sudo_pythonpath,
opt.add_cmd_delimiter, opt.add_cmd_delimiter,
opt.remote_shell,
opt.tmark) opt.tmark)
if return_code == 0: if return_code == 0:

View File

@ -315,6 +315,14 @@ parser.add_argument(
do not add a double dash before the python command do not add a double dash before the python command
""" """
) )
parser.add_argument(
"--remote-shell",
metavar="PROGRAM",
help="""
alternate remote shell program instead of defacto posix shell.
For Windows targets it would be either `cmd` or `powershell` unless something like git-bash is in use.
"""
)
parser.add_argument( parser.add_argument(
"--seed-hosts", "--seed-hosts",
metavar="HOSTNAME[,HOSTNAME]", metavar="HOSTNAME[,HOSTNAME]",

View File

@ -84,7 +84,7 @@ def parse_hostport(rhostport):
return username, password, port, host return username, password, port, host
def connect(ssh_cmd, rhostport, python, stderr, add_cmd_delimiter, options): def connect(ssh_cmd, rhostport, python, stderr, add_cmd_delimiter, remote_shell, options):
username, password, port, host = parse_hostport(rhostport) username, password, port, host = parse_hostport(rhostport)
if username: if username:
rhost = "{}@{}".format(username, host) rhost = "{}@{}".format(username, host)
@ -134,52 +134,59 @@ def connect(ssh_cmd, rhostport, python, stderr, add_cmd_delimiter, options):
portl = ["-p", str(port)] portl = ["-p", str(port)]
else: else:
portl = [] portl = []
if python: if remote_shell == "cmd":
pycmd = '"%s" -c "%s"' % (python, pyscript) pycmd = '"%s" -c "%s"' % (python or 'python', pyscript)
else: elif remote_shell == "powershell":
# By default, we run the following code in a shell. for c in ('\'', ' ', ';', '(', ')', ','):
# However, with restricted shells and other unusual pyscript = pyscript.replace(c, '`' + c)
# situations, there can be trouble. See the RESTRICTED pycmd = '%s -c %s' % (python or 'python', pyscript)
# SHELL section in "man bash" for more information. The else: # posix shell expected
# code makes many assumptions: if python:
# pycmd = '"%s" -c "%s"' % (python, pyscript)
# (1) That /bin/sh exists and that we can call it. else:
# Restricted shells often do *not* allow you to run # By default, we run the following code in a shell.
# programs specified with an absolute path like /bin/sh. # However, with restricted shells and other unusual
# Either way, if there is trouble with this, it should # situations, there can be trouble. See the RESTRICTED
# return error code 127. # SHELL section in "man bash" for more information. The
# # code makes many assumptions:
# (2) python3 or python exists in the PATH and is #
# executable. If they aren't, then exec won't work (see (4) # (1) That /bin/sh exists and that we can call it.
# below). # Restricted shells often do *not* allow you to run
# # programs specified with an absolute path like /bin/sh.
# (3) In /bin/sh, that we can redirect stderr in order to # Either way, if there is trouble with this, it should
# hide the version that "python3 -V" might print (some # return error code 127.
# restricted shells don't allow redirection, see #
# RESTRICTED SHELL section in 'man bash'). However, if we # (2) python3 or python exists in the PATH and is
# are in a restricted shell, we'd likely have trouble with # executable. If they aren't, then exec won't work (see (4)
# assumption (1) above. # below).
# #
# (4) The 'exec' command should work except if we failed # (3) In /bin/sh, that we can redirect stderr in order to
# to exec python because it doesn't exist or isn't # hide the version that "python3 -V" might print (some
# executable OR if exec isn't allowed (some restricted # restricted shells don't allow redirection, see
# shells don't allow exec). If the exec succeeded, it will # RESTRICTED SHELL section in 'man bash'). However, if we
# not return and not get to the "exit 97" command. If exec # are in a restricted shell, we'd likely have trouble with
# does return, we exit with code 97. # assumption (1) above.
# #
# Specifying the exact python program to run with --python # (4) The 'exec' command should work except if we failed
# avoids many of the issues above. However, if # to exec python because it doesn't exist or isn't
# you have a restricted shell on remote, you may only be # executable OR if exec isn't allowed (some restricted
# able to run python if it is in your PATH (and you can't # shells don't allow exec). If the exec succeeded, it will
# run programs specified with an absolute path). In that # not return and not get to the "exit 97" command. If exec
# case, sshuttle might not work at all since it is not # does return, we exit with code 97.
# possible to run python on the remote machine---even if #
# it is present. # Specifying the exact python program to run with --python
devnull = '/dev/null' # avoids many of the issues above. However, if
pycmd = ("P=python3; $P -V 2>%s || P=python; " # you have a restricted shell on remote, you may only be
"exec \"$P\" -c %s; exit 97") % \ # able to run python if it is in your PATH (and you can't
(devnull, quote(pyscript)) # run programs specified with an absolute path). In that
pycmd = ("/bin/sh -c {}".format(quote(pycmd))) # case, sshuttle might not work at all since it is not
# possible to run python on the remote machine---even if
# it is present.
devnull = '/dev/null'
pycmd = ("P=python3; $P -V 2>%s || P=python; "
"exec \"$P\" -c %s; exit 97") % \
(devnull, quote(pyscript))
pycmd = ("/bin/sh -c {}".format(quote(pycmd)))
if password is not None: if password is not None:
os.environ['SSHPASS'] = str(password) os.environ['SSHPASS'] = str(password)