mirror of
https://github.com/calebstewart/pwncat.git
synced 2024-11-27 19:04:15 +01:00
Identified critical error in RawIOBase implementation
The `ChannelFile` implementation raised a BlockingIOError in certain circumstances which is against the documented implementation for a subclass of `RawIOBase`. This was causing odd behaviour like occasional missing command output (e.g. empty environment variables)
This commit is contained in:
parent
95a6ac98cb
commit
64dcae2f0b
@ -184,7 +184,7 @@ class ChannelFile(RawIOBase):
|
||||
n = len(data)
|
||||
|
||||
if n == 0 and not self.blocking:
|
||||
raise BlockingIOError
|
||||
return None
|
||||
|
||||
obj = bytes(b[:n])
|
||||
|
||||
|
@ -21,7 +21,6 @@ import socket
|
||||
import functools
|
||||
from typing import Optional
|
||||
|
||||
|
||||
from pwncat.channel import Channel, ChannelError, ChannelClosed
|
||||
|
||||
|
||||
|
@ -140,9 +140,9 @@ class PopenLinux(pwncat.subprocess.Popen):
|
||||
if result == b"" and self.stdout_raw.raw.eof:
|
||||
self._receive_returncode()
|
||||
return self.returncode
|
||||
except ValueError:
|
||||
self._receive_returncode()
|
||||
return self.returncode
|
||||
# except ValueError:
|
||||
# self._receive_returncode()
|
||||
# return self.returncode
|
||||
except BlockingIOError:
|
||||
return None
|
||||
|
||||
@ -203,6 +203,12 @@ class PopenLinux(pwncat.subprocess.Popen):
|
||||
except BlockingIOError:
|
||||
time.sleep(0.1)
|
||||
|
||||
# Check if there's any data left buffered
|
||||
if self.stdout:
|
||||
new_data = self.stdout.read()
|
||||
if new_data is not None:
|
||||
data += new_data
|
||||
|
||||
return (data, empty)
|
||||
|
||||
def kill(self):
|
||||
@ -488,6 +494,7 @@ class Linux(Platform):
|
||||
PATH_TYPE = pathlib.PurePosixPath
|
||||
PROMPTS = {
|
||||
"sh": """'$(command printf "(remote) $(whoami)@$(hostname):$PWD\\$ ")'""",
|
||||
"dash": """'$(command printf "(remote) $(whoami)@$(hostname):$PWD\\$ ")'""",
|
||||
"zsh": """'%B%F{red}(remote) %B%F{yellow}%n@%M%B%F{reset}:%B%F{cyan}%(6~.%-1~/…/%4~.%5~)%B%(#.%b%F{white}#.%b%F{white}$)%b%F{reset} '""",
|
||||
"default": """'$(command printf "\\[\\033[01;31m\\](remote)\\[\\033[0m\\] \\[\\033[01;33m\\]$(whoami)@$(hostname)\\[\\033[0m\\]:\\[\\033[1;36m\\]$PWD\\[\\033[0m\\]\\$ ")'""",
|
||||
}
|
||||
@ -546,21 +553,20 @@ class Linux(Platform):
|
||||
else:
|
||||
self.has_pty = False
|
||||
|
||||
self.shell = self.getenv("SHELL")
|
||||
if self.shell == "" or self.shell is None:
|
||||
self.shell = "/bin/sh"
|
||||
|
||||
# This doesn't make sense, but happened for some people (see issue #116)
|
||||
if os.path.basename(self.shell) == "nologin":
|
||||
if os.path.basename(self.shell) in ["nologin", "false", "sync", "git-shell"]:
|
||||
self.shell = "/bin/sh"
|
||||
self.channel.sendline(b" export SHELL=/bin/sh")
|
||||
|
||||
if os.path.basename(self.shell) == "sh":
|
||||
if os.path.basename(self.shell) in ["sh", "dash"]:
|
||||
# Try to find a better shell
|
||||
bash = self._do_which("bash")
|
||||
if bash is not None:
|
||||
self.session.log(f"upgrading from {self.shell} to {bash}")
|
||||
self.shell = bash
|
||||
self.session.log(f"upgrading from sh to {self.shell}")
|
||||
self.channel.sendline(f"exec {self.shell}".encode("utf-8"))
|
||||
time.sleep(0.5)
|
||||
|
||||
@ -1035,6 +1041,8 @@ class Linux(Platform):
|
||||
command += f" 2>{stderr}"
|
||||
elif stderr == pwncat.subprocess.DEVNULL:
|
||||
command += " 2>/dev/null"
|
||||
elif stderr == pwncat.subprocess.PIPE:
|
||||
command += " 2>&1"
|
||||
|
||||
if isinstance(stdin, str):
|
||||
command += f" 0<{stdin}"
|
||||
@ -1518,6 +1526,17 @@ class Linux(Platform):
|
||||
self.logger.info(command.rstrip("\n"))
|
||||
self.channel.send(command.encode("utf-8"))
|
||||
self.channel.drain()
|
||||
self._interactive = False
|
||||
|
||||
# Update self.shell just in case the user changed shells
|
||||
try:
|
||||
# Get the PID of the running shell
|
||||
pid = self.getenv("$")
|
||||
# Grab the path to the executable representing the shell
|
||||
self.shell = self.Path("/proc", pid, "exe").readlink()
|
||||
except (FileNotFoundError, PermissionError):
|
||||
# Fall back to SHELL even though it's not really trustworthy
|
||||
self.shell = self.getenv("SHELL")
|
||||
else:
|
||||
|
||||
# Going interactive requires a pty
|
||||
@ -1556,7 +1575,7 @@ class Linux(Platform):
|
||||
except pwncat.channel.ChannelTimeout:
|
||||
pass
|
||||
|
||||
self._interactive = value
|
||||
self._interactive = True
|
||||
|
||||
def whoami(self):
|
||||
"""Get the name of the current user"""
|
||||
|
Loading…
Reference in New Issue
Block a user