Skip to content

Commit fecf9f6

Browse files
committed
Port SSH remote part to python3
1 parent f0f0c36 commit fecf9f6

File tree

2 files changed

+25
-21
lines changed

2 files changed

+25
-21
lines changed

pwnlib/context/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,8 @@ def _decode(self, b):
839839
return b.decode('utf-8')
840840
except UnicodeDecodeError:
841841
return b.decode('latin1')
842+
except AttributeError:
843+
return b
842844
return b.decode(self.encoding)
843845

844846
@_validator

pwnlib/tubes/ssh.py

+23-21
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,7 @@ def process(self, argv=None, executable=None, tty=True, cwd=None, env=None, time
828828
>>> print(s.process('false', preexec_fn=uses_globals).recvall().strip().decode()) # doctest: +ELLIPSIS
829829
Traceback (most recent call last):
830830
...
831-
NameError: global name 'bar' is not defined
831+
NameError: name 'bar' is not defined
832832
833833
>>> s.process('echo hello', shell=True).recvall()
834834
b'hello\n'
@@ -865,10 +865,14 @@ def process(self, argv=None, executable=None, tty=True, cwd=None, env=None, time
865865
arg = oarg
866866
if b'\x00' in arg[:-1]:
867867
self.error('Inappropriate nulls in argv[%i]: %r' % (i, oarg))
868-
argv[i] = arg.rstrip(b'\x00')
868+
argv[i] = bytearray(arg.rstrip(b'\x00'))
869869

870+
if env is not None and not isinstance(env, dict) and env != os.environ:
871+
self.error("env must be a dict: %r" % env)
872+
873+
# Converts the environment variables to a list of tuples to retain order.
874+
env2 = []
870875
# Python also doesn't like when envp contains '\x00'
871-
env2 = {}
872876
if env and hasattr(env, 'items'):
873877
for k, v in env.items():
874878
if isinstance(k, six.text_type):
@@ -879,17 +883,16 @@ def process(self, argv=None, executable=None, tty=True, cwd=None, env=None, time
879883
self.error('Inappropriate nulls in environment key %r' % k)
880884
if b'\x00' in v[:-1]:
881885
self.error('Inappropriate nulls in environment value %r=%r' % (k, v))
882-
env2[k.rstrip(b'\x00')] = v.rstrip(b'\x00')
886+
env2.append((bytearray(k.rstrip(b'\x00')), bytearray(v.rstrip(b'\x00'))))
883887
env = env2 or env
884888

885889
executable = executable or argv[0]
886890
cwd = cwd or self.cwd
887891

888892
# Validate, since failures on the remote side will suck.
889-
if not isinstance(executable, (six.text_type, six.binary_type)):
893+
if not isinstance(executable, (six.text_type, six.binary_type, bytearray)):
890894
self.error("executable / argv[0] must be a string: %r" % executable)
891-
if env is not None and not isinstance(env, dict) and env != os.environ:
892-
self.error("env must be a dict: %r" % env)
895+
executable = context._decode(executable)
893896

894897
# Allow passing in sys.stdin/stdout/stderr objects
895898
handles = {sys.stdin: 0, sys.stdout:1, sys.stderr:2}
@@ -912,22 +915,20 @@ def func(): pass
912915
func_src = inspect.getsource(func).strip()
913916
setuid = True if setuid is None else bool(setuid)
914917

915-
# Converts the environment variables to a list of tuples to remain order.
916-
env = list(env.items())
917-
918918
script = r"""
919-
#!/usr/bin/env python2
919+
#!/usr/bin/env python
920920
import os, sys, ctypes, resource, platform, stat
921921
from collections import OrderedDict
922922
exe = %(executable)r
923-
argv = %(argv)r
924-
env = OrderedDict(%(env)r)
923+
argv = [bytes(a) for a in %(argv)r]
924+
env = %(env)r
925925
926926
os.chdir(%(cwd)r)
927927
928928
if env is not None:
929+
env = OrderedDict((bytes(k), bytes(v)) for k,v in env)
929930
os.environ.clear()
930-
os.environ.update(env)
931+
getattr(os, 'environb', os.environ).update(env)
931932
else:
932933
env = os.environ
933934
@@ -987,10 +988,11 @@ def is_exe(path):
987988
988989
for fd, newfd in {0: %(stdin)r, 1: %(stdout)r, 2:%(stderr)r}.items():
989990
if newfd is None:
990-
close(fd)
991-
elif isinstance(newfd, str):
992991
os.close(fd)
993-
os.open(newfd, os.O_RDONLY if fd == 0 else (os.O_RDWR|os.O_CREAT))
992+
elif isinstance(newfd, (str, bytes)):
993+
newfd = os.open(newfd, os.O_RDONLY if fd == 0 else (os.O_RDWR|os.O_CREAT))
994+
os.dup2(newfd, fd)
995+
os.close(newfd)
994996
elif isinstance(newfd, int) and newfd != fd:
995997
os.dup2(fd, newfd)
996998
@@ -1015,7 +1017,7 @@ def is_exe(path):
10151017
pass
10161018
10171019
%(func_src)s
1018-
apply(%(func_name)s, %(func_args)r)
1020+
%(func_name)s(*%(func_args)r)
10191021
10201022
os.execve(exe, argv, env)
10211023
""" % locals()
@@ -1049,15 +1051,15 @@ def is_exe(path):
10491051

10501052
with self.progress(msg) as h:
10511053

1052-
script = 'for py in python2.7 python2 python; do test -x "$(which $py 2>&1)" && exec $py -c %s check; done; echo 2' % sh_string(script)
1054+
script = 'for py in python3 python; do test -x "$(which $py 2>&1)" && exec $py -c %s check; done; echo 2' % sh_string(script)
10531055
with context.local(log_level='error'):
10541056
python = ssh_process(self, script, tty=True, raw=True, level=self.level, timeout=self.timeout)
10551057

10561058
try:
10571059
result = safeeval.const(python.recvline())
1058-
except Exception:
1060+
except (EOFError, ValueError):
10591061
h.failure("Process creation failed")
1060-
self.warn_once('Could not find a Python2 interpreter on %s\n' % self.host \
1062+
self.warn_once('Could not find a Python interpreter on %s\n' % self.host \
10611063
+ "Use ssh.run() instead of ssh.process()")
10621064
return None
10631065

0 commit comments

Comments
 (0)