Skip to content

Commit 92cd969

Browse files
goreilArusekkpeace-maker
authored
Allow empty argv in ssh.process() (Gallopsled#2217) (Gallopsled#2234)
* Fix bug at ssh.py:process() - empty argv[0] Error Before this, process.py relied on `os.execve` which disallows an empty argv or argv[0] == "". This commit replaces `os.execve` with `ctypes.CDLL(None).execve` to call the C-Library function directly which allows an empty argv. * Add Gallopsled#2225 to stable changelog * Better ctypes syntax * Add error message if cytpes.execve fails. * Updata CHANGELOG.md * ssh.py: python2 compatibility for os.environb * Add check that "=" not in misc.normalize_argv_env This check checks prevents the use of "=" in the key of an environment variable, which is generally impossible. * ssh.process: Seperate cases for empty argv[0] This commit seperates the cases for an empty argv[0] from normal cases. * ssh.py delete leftover comment --------- Co-authored-by: Youheng Lü <[email protected]> Co-authored-by: Arusekk <[email protected]> Co-authored-by: peace-maker <[email protected]>
1 parent 8b86cc3 commit 92cd969

File tree

3 files changed

+55
-5
lines changed

3 files changed

+55
-5
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,15 @@ The table below shows which release corresponds to each branch, and what date th
7373
- [#2219][2219] Fix passing arguments on the stack in shellcraft syscall template
7474
- [#2212][2212] Add `--libc libc.so` argument to `pwn template` command
7575
- [#2257][2257] Allow creation of custom templates for `pwn template` command
76+
- [#2225][2225] Allow empty argv in ssh.process()
7677

7778
[2202]: https://github.com/Gallopsled/pwntools/pull/2202
7879
[2117]: https://github.com/Gallopsled/pwntools/pull/2117
7980
[2221]: https://github.com/Gallopsled/pwntools/pull/2221
8081
[2219]: https://github.com/Gallopsled/pwntools/pull/2219
8182
[2212]: https://github.com/Gallopsled/pwntools/pull/2212
8283
[2257]: https://github.com/Gallopsled/pwntools/pull/2257
84+
[2225]: https://github.com/Gallopsled/pwntools/pull/2225
8385

8486
## 4.11.0 (`beta`)
8587

@@ -95,7 +97,7 @@ The table below shows which release corresponds to each branch, and what date th
9597
[2186]: https://github.com/Gallopsled/pwntools/pull/2186
9698
[2129]: https://github.com/Gallopsled/pwntools/pull/2129
9799

98-
## 4.10.1 (`stable`)
100+
## 4.10.1 (`stable`)
99101

100102
- [#2214][2214] Fix bug at ssh.py:`download` and `download_file` with relative paths
101103
- [#2241][2241] Fix ssh.process not setting ssh_process.cwd attribute

pwnlib/tubes/ssh.py

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,23 @@ def process(self, argv=None, executable=None, tty=True, cwd=None, env=None, time
882882
>>> io = s.process(['cat'], timeout=5)
883883
>>> io.recvline()
884884
b''
885+
886+
>>> # Testing that empty argv works
887+
>>> io = s.process([], executable='sh')
888+
>>> io.sendline(b'echo $0')
889+
>>> io.recvline()
890+
b'$ \n'
891+
>>> # Make sure that we have a shell
892+
>>> io.sendline(b'echo hello')
893+
>>> io.recvline()
894+
b'$ hello\n'
895+
896+
>>> # Testing that empty argv[0] works
897+
>>> io = s.process([''], executable='sh')
898+
>>> io.sendline(b'echo $0')
899+
>>> io.recvline()
900+
b'$ \n'
901+
885902
"""
886903
if not argv and not executable:
887904
self.error("Must specify argv or executable")
@@ -945,7 +962,7 @@ def func(): pass
945962
os.environ.clear()
946963
environ.update(env)
947964
else:
948-
env = os.environ
965+
env = environ
949966
950967
def is_exe(path):
951968
return os.path.isfile(path) and os.access(path, os.X_OK)
@@ -1034,8 +1051,35 @@ def is_exe(path):
10341051
%(func_src)s
10351052
%(func_name)s(*%(func_args)r)
10361053
1037-
os.execve(exe, argv, env)
1038-
""" % locals() # """
1054+
""" % locals()
1055+
1056+
if len(argv) > 0 and len(argv[0]) > 0:
1057+
script += r"os.execve(exe, argv, env) "
1058+
1059+
# os.execve does not allow us to pass empty argv[0]
1060+
# Therefore we use ctypes to call execve directly
1061+
else:
1062+
script += r"""
1063+
# Transform envp from dict to list
1064+
env_list = [key + b"=" + value for key, value in env.items()]
1065+
1066+
# ctypes helper to convert a python list to a NULL-terminated C array
1067+
def to_carray(py_list):
1068+
py_list += [None] # NULL-terminated
1069+
return (ctypes.c_char_p * len(py_list))(*py_list)
1070+
1071+
c_argv = to_carray(argv)
1072+
c_env = to_carray(env_list)
1073+
1074+
# Call execve
1075+
libc = ctypes.CDLL('libc.so.6')
1076+
libc.execve(exe, c_argv, c_env)
1077+
1078+
# We should never get here, since we sanitized argv and env,
1079+
# but just in case, indicate that something went wrong.
1080+
libc.perror(b"execve")
1081+
raise OSError("execve failed")
1082+
""" % locals()
10391083

10401084
script = script.strip()
10411085

@@ -1054,7 +1098,7 @@ def is_exe(path):
10541098
execve_repr = "execve(%r, %s, %s)" % (executable,
10551099
argv,
10561100
'os.environ'
1057-
if (env in (None, os.environ))
1101+
if (env in (None, getattr(os, "environb", os.environ)))
10581102
else env)
10591103
# Avoid spamming the screen
10601104
if self.isEnabledFor(logging.DEBUG) and len(execve_repr) > 512:

pwnlib/util/misc.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ def normalize_argv_env(argv, env, log, level=2):
229229
for k,v in env_items:
230230
if not isinstance(k, (bytes, six.text_type)):
231231
log.error('Environment keys must be strings: %r' % k)
232+
# Check if = is in the key, Required check since we sometimes call ctypes.execve directly
233+
# https://github.com/python/cpython/blob/025995feadaeebeef5d808f2564f0fd65b704ea5/Modules/posixmodule.c#L6476
234+
if b'=' in packing._encode(k):
235+
log.error('Environment keys may not contain "=": %r' % (k))
232236
if not isinstance(v, (bytes, six.text_type)):
233237
log.error('Environment values must be strings: %r=%r' % (k,v))
234238
k = packing._need_bytes(k, level, 0x80) # ASCII text is okay

0 commit comments

Comments
 (0)