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)
|
n = len(data)
|
||||||
|
|
||||||
if n == 0 and not self.blocking:
|
if n == 0 and not self.blocking:
|
||||||
raise BlockingIOError
|
return None
|
||||||
|
|
||||||
obj = bytes(b[:n])
|
obj = bytes(b[:n])
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ import socket
|
|||||||
import functools
|
import functools
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
from pwncat.channel import Channel, ChannelError, ChannelClosed
|
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:
|
if result == b"" and self.stdout_raw.raw.eof:
|
||||||
self._receive_returncode()
|
self._receive_returncode()
|
||||||
return self.returncode
|
return self.returncode
|
||||||
except ValueError:
|
# except ValueError:
|
||||||
self._receive_returncode()
|
# self._receive_returncode()
|
||||||
return self.returncode
|
# return self.returncode
|
||||||
except BlockingIOError:
|
except BlockingIOError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -203,6 +203,12 @@ class PopenLinux(pwncat.subprocess.Popen):
|
|||||||
except BlockingIOError:
|
except BlockingIOError:
|
||||||
time.sleep(0.1)
|
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)
|
return (data, empty)
|
||||||
|
|
||||||
def kill(self):
|
def kill(self):
|
||||||
@ -488,6 +494,7 @@ class Linux(Platform):
|
|||||||
PATH_TYPE = pathlib.PurePosixPath
|
PATH_TYPE = pathlib.PurePosixPath
|
||||||
PROMPTS = {
|
PROMPTS = {
|
||||||
"sh": """'$(command printf "(remote) $(whoami)@$(hostname):$PWD\\$ ")'""",
|
"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} '""",
|
"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\\]\\$ ")'""",
|
"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:
|
else:
|
||||||
self.has_pty = False
|
self.has_pty = False
|
||||||
|
|
||||||
self.shell = self.getenv("SHELL")
|
|
||||||
if self.shell == "" or self.shell is None:
|
if self.shell == "" or self.shell is None:
|
||||||
self.shell = "/bin/sh"
|
self.shell = "/bin/sh"
|
||||||
|
|
||||||
# This doesn't make sense, but happened for some people (see issue #116)
|
# 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.shell = "/bin/sh"
|
||||||
self.channel.sendline(b" export 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
|
# Try to find a better shell
|
||||||
bash = self._do_which("bash")
|
bash = self._do_which("bash")
|
||||||
if bash is not None:
|
if bash is not None:
|
||||||
|
self.session.log(f"upgrading from {self.shell} to {bash}")
|
||||||
self.shell = bash
|
self.shell = bash
|
||||||
self.session.log(f"upgrading from sh to {self.shell}")
|
|
||||||
self.channel.sendline(f"exec {self.shell}".encode("utf-8"))
|
self.channel.sendline(f"exec {self.shell}".encode("utf-8"))
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
@ -1035,6 +1041,8 @@ class Linux(Platform):
|
|||||||
command += f" 2>{stderr}"
|
command += f" 2>{stderr}"
|
||||||
elif stderr == pwncat.subprocess.DEVNULL:
|
elif stderr == pwncat.subprocess.DEVNULL:
|
||||||
command += " 2>/dev/null"
|
command += " 2>/dev/null"
|
||||||
|
elif stderr == pwncat.subprocess.PIPE:
|
||||||
|
command += " 2>&1"
|
||||||
|
|
||||||
if isinstance(stdin, str):
|
if isinstance(stdin, str):
|
||||||
command += f" 0<{stdin}"
|
command += f" 0<{stdin}"
|
||||||
@ -1518,6 +1526,17 @@ class Linux(Platform):
|
|||||||
self.logger.info(command.rstrip("\n"))
|
self.logger.info(command.rstrip("\n"))
|
||||||
self.channel.send(command.encode("utf-8"))
|
self.channel.send(command.encode("utf-8"))
|
||||||
self.channel.drain()
|
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:
|
else:
|
||||||
|
|
||||||
# Going interactive requires a pty
|
# Going interactive requires a pty
|
||||||
@ -1556,7 +1575,7 @@ class Linux(Platform):
|
|||||||
except pwncat.channel.ChannelTimeout:
|
except pwncat.channel.ChannelTimeout:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self._interactive = value
|
self._interactive = True
|
||||||
|
|
||||||
def whoami(self):
|
def whoami(self):
|
||||||
"""Get the name of the current user"""
|
"""Get the name of the current user"""
|
||||||
|
Loading…
Reference in New Issue
Block a user