mirror of
https://github.com/calebstewart/pwncat.git
synced 2024-11-30 20:34:15 +01:00
Updated windows platform for new C2 comms
This commit is contained in:
parent
00c6e13c39
commit
04587bffb1
@ -2,14 +2,15 @@
|
|||||||
|
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
import pwncat
|
|
||||||
import rich.markup
|
import rich.markup
|
||||||
|
|
||||||
|
import pwncat
|
||||||
from pwncat import util
|
from pwncat import util
|
||||||
from pwncat.db import Fact
|
from pwncat.db import Fact
|
||||||
from pwncat.modules import ModuleFailed
|
from pwncat.modules import ModuleFailed
|
||||||
from pwncat.modules.enumerate import EnumerateModule, Schedule
|
|
||||||
from pwncat.platform import PlatformError
|
from pwncat.platform import PlatformError
|
||||||
from pwncat.platform.windows import PowershellError, Windows
|
from pwncat.platform.windows import Windows, PowershellError
|
||||||
|
from pwncat.modules.enumerate import Schedule, EnumerateModule
|
||||||
|
|
||||||
|
|
||||||
class LSAProtectionData(Fact):
|
class LSAProtectionData(Fact):
|
||||||
@ -18,17 +19,19 @@ class LSAProtectionData(Fact):
|
|||||||
|
|
||||||
self.active: bool = active
|
self.active: bool = active
|
||||||
|
|
||||||
|
|
||||||
def title(self, session):
|
def title(self, session):
|
||||||
out = "LSA Protection is "
|
out = "LSA Protection is "
|
||||||
out += "[bold red]active[/bold red]" if self.active else "[bold green]inactive[/bold green]"
|
out += (
|
||||||
|
"[bold red]active[/bold red]"
|
||||||
|
if self.active
|
||||||
|
else "[bold green]inactive[/bold green]"
|
||||||
|
)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def description(self, session):
|
def description(self, session):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Module(EnumerateModule):
|
class Module(EnumerateModule):
|
||||||
"""Enumerate the current Windows Defender settings on the target"""
|
"""Enumerate the current Windows Defender settings on the target"""
|
||||||
|
|
||||||
@ -37,11 +40,9 @@ class Module(EnumerateModule):
|
|||||||
|
|
||||||
def enumerate(self, session):
|
def enumerate(self, session):
|
||||||
|
|
||||||
|
|
||||||
registry_value = "RunAsPPL"
|
registry_value = "RunAsPPL"
|
||||||
registry_key = "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\LSA"
|
registry_key = "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\LSA"
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = session.platform.powershell(
|
result = session.platform.powershell(
|
||||||
f"Get-ItemPropertyValue {registry_key} -Name {registry_value}"
|
f"Get-ItemPropertyValue {registry_key} -Name {registry_value}"
|
||||||
@ -55,7 +56,7 @@ class Module(EnumerateModule):
|
|||||||
status = bool(result[0])
|
status = bool(result[0])
|
||||||
|
|
||||||
except PowershellError as exc:
|
except PowershellError as exc:
|
||||||
if "does not exist" in exc.errors[0]["Message"]:
|
if "does not exist" in exc.message:
|
||||||
status = bool(0) # default
|
status = bool(0) # default
|
||||||
else:
|
else:
|
||||||
raise ModuleFailed(
|
raise ModuleFailed(
|
||||||
|
@ -2,14 +2,15 @@
|
|||||||
|
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
import pwncat
|
|
||||||
import rich.markup
|
import rich.markup
|
||||||
|
|
||||||
|
import pwncat
|
||||||
from pwncat import util
|
from pwncat import util
|
||||||
from pwncat.db import Fact
|
from pwncat.db import Fact
|
||||||
from pwncat.modules import ModuleFailed
|
from pwncat.modules import ModuleFailed
|
||||||
from pwncat.modules.enumerate import EnumerateModule, Schedule
|
|
||||||
from pwncat.platform import PlatformError
|
from pwncat.platform import PlatformError
|
||||||
from pwncat.platform.windows import PowershellError, Windows
|
from pwncat.platform.windows import Windows, PowershellError
|
||||||
|
from pwncat.modules.enumerate import Schedule, EnumerateModule
|
||||||
|
|
||||||
|
|
||||||
class UACData(Fact):
|
class UACData(Fact):
|
||||||
@ -117,7 +118,7 @@ class Module(EnumerateModule):
|
|||||||
registry_values[registry_value] = registry_type(result[0])
|
registry_values[registry_value] = registry_type(result[0])
|
||||||
|
|
||||||
except PowershellError as exc:
|
except PowershellError as exc:
|
||||||
if "does not exist" in exc.errors[0]["Message"]:
|
if "does not exist" in exc.message:
|
||||||
registry_values[registry_value] = registry_type(0)
|
registry_values[registry_value] = registry_type(0)
|
||||||
else:
|
else:
|
||||||
raise ModuleFailed(
|
raise ModuleFailed(
|
||||||
|
@ -2,14 +2,15 @@
|
|||||||
|
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
import pwncat
|
|
||||||
import rich.markup
|
import rich.markup
|
||||||
|
|
||||||
|
import pwncat
|
||||||
from pwncat import util
|
from pwncat import util
|
||||||
from pwncat.db import Fact
|
from pwncat.db import Fact
|
||||||
from pwncat.modules import ModuleFailed
|
from pwncat.modules import ModuleFailed
|
||||||
from pwncat.modules.enumerate import EnumerateModule, Schedule
|
|
||||||
from pwncat.platform import PlatformError
|
from pwncat.platform import PlatformError
|
||||||
from pwncat.platform.windows import PowershellError, Windows
|
from pwncat.platform.windows import Windows, PowershellError
|
||||||
|
from pwncat.modules.enumerate import Schedule, EnumerateModule
|
||||||
|
|
||||||
|
|
||||||
class AlwaysInstallElevatedData(Fact):
|
class AlwaysInstallElevatedData(Fact):
|
||||||
@ -19,9 +20,12 @@ class AlwaysInstallElevatedData(Fact):
|
|||||||
self.enabled: bool = enabled
|
self.enabled: bool = enabled
|
||||||
self.context: str = context
|
self.context: str = context
|
||||||
|
|
||||||
|
|
||||||
def title(self, session):
|
def title(self, session):
|
||||||
out = "AlwaysInstallElevated is " + "[bold green]enabled[/bold green]" if self.enabled else "[red]disabled[/red]"
|
out = (
|
||||||
|
"AlwaysInstallElevated is " + "[bold green]enabled[/bold green]"
|
||||||
|
if self.enabled
|
||||||
|
else "[red]disabled[/red]"
|
||||||
|
)
|
||||||
out += f" for this {self.context}"
|
out += f" for this {self.context}"
|
||||||
return out
|
return out
|
||||||
|
|
||||||
@ -34,14 +38,12 @@ class Module(EnumerateModule):
|
|||||||
|
|
||||||
def enumerate(self, session):
|
def enumerate(self, session):
|
||||||
|
|
||||||
|
|
||||||
registry_value = "AlwaysInstallElevated"
|
registry_value = "AlwaysInstallElevated"
|
||||||
registry_keys = [
|
registry_keys = [
|
||||||
"HKCU:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Installer\\",
|
"HKCU:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Installer\\",
|
||||||
"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Installer\\"
|
"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Installer\\",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
for registry_key in registry_keys:
|
for registry_key in registry_keys:
|
||||||
try:
|
try:
|
||||||
result = session.platform.powershell(
|
result = session.platform.powershell(
|
||||||
@ -56,14 +58,14 @@ class Module(EnumerateModule):
|
|||||||
status = bool(result[0])
|
status = bool(result[0])
|
||||||
|
|
||||||
except PowershellError as exc:
|
except PowershellError as exc:
|
||||||
if "does not exist" in exc.errors[0]["Message"]:
|
if "does not exist" in exc.message:
|
||||||
status = bool(0) # default
|
status = bool(0) # default
|
||||||
else:
|
else:
|
||||||
raise ModuleFailed(
|
raise ModuleFailed(
|
||||||
f"could not retrieve registry value {registry_value}: {exc}"
|
f"could not retrieve registry value {registry_value}: {exc}"
|
||||||
) from exc
|
) from exc
|
||||||
|
|
||||||
if registry_key.startswith('HKCU'):
|
if registry_key.startswith("HKCU"):
|
||||||
yield AlwaysInstallElevatedData(self.name, status, "current user")
|
yield AlwaysInstallElevatedData(self.name, status, "current user")
|
||||||
else:
|
else:
|
||||||
yield AlwaysInstallElevatedData(self.name, status, "local machine")
|
yield AlwaysInstallElevatedData(self.name, status, "local machine")
|
||||||
|
@ -49,16 +49,25 @@ import pwncat.subprocess
|
|||||||
from pwncat.platform import Path, Platform, PlatformError
|
from pwncat.platform import Path, Platform, PlatformError
|
||||||
|
|
||||||
INTERACTIVE_END_MARKER = b"INTERACTIVE_COMPLETE\r\n"
|
INTERACTIVE_END_MARKER = b"INTERACTIVE_COMPLETE\r\n"
|
||||||
PWNCAT_WINDOWS_C2_VERSION = "v0.1.1"
|
PWNCAT_WINDOWS_C2_VERSION = "v0.2.0"
|
||||||
PWNCAT_WINDOWS_C2_RELEASE_URL = "https://github.com/calebstewart/pwncat-windows-c2/releases/download/{version}/pwncat-windows-{version}.tar.gz"
|
PWNCAT_WINDOWS_C2_RELEASE_URL = "https://github.com/calebstewart/pwncat-windows-c2/releases/download/{version}/pwncat-windows-{version}.tar.gz"
|
||||||
|
|
||||||
|
|
||||||
class PowershellError(Exception):
|
class PowershellError(Exception):
|
||||||
"""Executing a powershell script caused an error"""
|
"""Executing a powershell script caused an error"""
|
||||||
|
|
||||||
def __init__(self, errors):
|
def __init__(self, msg):
|
||||||
self.errors = json.loads(errors)
|
super().__init__(msg)
|
||||||
super().__init__(self.errors[0]["Message"])
|
|
||||||
|
self.message = msg
|
||||||
|
|
||||||
|
|
||||||
|
class ProtocolError(Exception):
|
||||||
|
def __init__(self, code: int, message: str):
|
||||||
|
self.code = code
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
super().__init__(self.message)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -116,8 +125,7 @@ class WindowsFile(RawIOBase):
|
|||||||
if not self.is_open:
|
if not self.is_open:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.platform.run_method("File", "close")
|
self.platform.run_method("File", "close", self.handle)
|
||||||
self.platform.channel.sendline(str(self.handle).encode("utf-8"))
|
|
||||||
self.is_open = False
|
self.is_open = False
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -140,25 +148,24 @@ class WindowsFile(RawIOBase):
|
|||||||
if self.eof:
|
if self.eof:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
self.platform.run_method("File", "read")
|
try:
|
||||||
self.platform.channel.sendline(str(self.handle).encode("utf-8"))
|
result = self.platform.run_method("File", "read", self.handle, len(b))
|
||||||
self.platform.channel.sendline(str(len(b)).encode("utf-8"))
|
except ProtocolError as exc:
|
||||||
count = int(self.platform.channel.recvuntil(b"\n").strip())
|
|
||||||
|
|
||||||
if count == 0:
|
# ERROR_BROKEN_PIPE
|
||||||
|
if exc.code == 0x6D:
|
||||||
self.eof = True
|
self.eof = True
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
n = 0
|
raise IOError(exc.message) from exc
|
||||||
while n < count:
|
|
||||||
try:
|
|
||||||
n += self.platform.channel.recvinto(b[n:])
|
|
||||||
except NotImplementedError:
|
|
||||||
data = self.platform.channel.recv(count - n)
|
|
||||||
b[n : n + len(data)] = data
|
|
||||||
n += len(data)
|
|
||||||
|
|
||||||
return count
|
data = base64.b64decode(result["data"])
|
||||||
|
b[: len(data)] = data
|
||||||
|
|
||||||
|
if len(data) == 0:
|
||||||
|
self.eof = True
|
||||||
|
|
||||||
|
return len(data)
|
||||||
|
|
||||||
def write(self, data: bytes):
|
def write(self, data: bytes):
|
||||||
"""Write data to this file"""
|
"""Write data to this file"""
|
||||||
@ -170,16 +177,18 @@ class WindowsFile(RawIOBase):
|
|||||||
while nwritten < len(data):
|
while nwritten < len(data):
|
||||||
chunk = data[nwritten:]
|
chunk = data[nwritten:]
|
||||||
|
|
||||||
payload = BytesIO()
|
try:
|
||||||
with gzip.GzipFile(fileobj=payload, mode="wb") as gz:
|
result = self.platform.run_method(
|
||||||
gz.write(chunk)
|
"File", "write", self.handle, base64.b64encode(data)
|
||||||
|
|
||||||
self.platform.run_method("File", "write")
|
|
||||||
self.platform.channel.sendline(str(self.handle).encode("utf-8"))
|
|
||||||
self.platform.channel.sendline(base64.b64encode(payload.getbuffer()))
|
|
||||||
nwritten += int(
|
|
||||||
self.platform.channel.recvuntil(b"\n").strip().decode("utf-8")
|
|
||||||
)
|
)
|
||||||
|
except ProtocolError as exc:
|
||||||
|
# ERROR_BROKEN_PIPE
|
||||||
|
if exc.code == 0x6D:
|
||||||
|
self.eof = True
|
||||||
|
break
|
||||||
|
raise IOError(exc.message) from exc
|
||||||
|
|
||||||
|
nwritten += result["count"]
|
||||||
|
|
||||||
return nwritten
|
return nwritten
|
||||||
|
|
||||||
@ -200,19 +209,18 @@ class PopenWindows(pwncat.subprocess.Popen):
|
|||||||
encoding,
|
encoding,
|
||||||
errors,
|
errors,
|
||||||
bufsize,
|
bufsize,
|
||||||
handle,
|
result,
|
||||||
stdio,
|
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.platform = platform
|
self.platform = platform
|
||||||
self.handle = handle
|
self.handle = result["handle"]
|
||||||
self.stdio = stdio
|
self.stdio = [result["stdin"], result["stdout"], result["stderr"]]
|
||||||
self.returncode = None
|
self.returncode = None
|
||||||
|
|
||||||
self.stdin = WindowsFile(platform, "w", stdio[0])
|
self.stdin = WindowsFile(platform, "w", result["stdin"])
|
||||||
self.stdout = WindowsFile(platform, "r", stdio[1])
|
self.stdout = WindowsFile(platform, "r", result["stdout"])
|
||||||
self.stderr = WindowsFile(platform, "r", stdio[2])
|
self.stderr = WindowsFile(platform, "r", result["stderr"])
|
||||||
|
|
||||||
if stdout != subprocess.PIPE:
|
if stdout != subprocess.PIPE:
|
||||||
self.stdout.close()
|
self.stdout.close()
|
||||||
@ -266,9 +274,7 @@ class PopenWindows(pwncat.subprocess.Popen):
|
|||||||
if self.returncode is not None:
|
if self.returncode is not None:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.platform.run_method("Process", "kill")
|
self.platform.run_method("Process", "kill", self.handle, 0)
|
||||||
self.platform.channel.sendline(str(self.handle).encode("utf-8"))
|
|
||||||
self.platform.channel.sendline(b"0")
|
|
||||||
self.returncode = -1
|
self.returncode = -1
|
||||||
|
|
||||||
def poll(self):
|
def poll(self):
|
||||||
@ -277,15 +283,13 @@ class PopenWindows(pwncat.subprocess.Popen):
|
|||||||
if self.returncode is not None:
|
if self.returncode is not None:
|
||||||
return self.returncode
|
return self.returncode
|
||||||
|
|
||||||
self.platform.run_method("Process", "poll")
|
try:
|
||||||
self.platform.channel.sendline(str(self.handle).encode("utf-8"))
|
result = self.platform.run_method("Process", "poll", self.handle)
|
||||||
result = self.platform.channel.recvuntil(b"\n").strip().decode("utf-8")
|
except ProtocolError as exc:
|
||||||
|
raise RuntimeError(exc.message)
|
||||||
|
|
||||||
if result == "E":
|
if result["stopped"]:
|
||||||
raise RuntimeError(f"process {self.handle}: failed to get exit status")
|
self.returncode = result["code"] or 0
|
||||||
|
|
||||||
if result != "R":
|
|
||||||
self.returncode = int(result)
|
|
||||||
return self.returncode
|
return self.returncode
|
||||||
|
|
||||||
def wait(self, timeout: float = None):
|
def wait(self, timeout: float = None):
|
||||||
@ -430,19 +434,54 @@ class Windows(Platform):
|
|||||||
|
|
||||||
self.run_method("StageTwo", "exit")
|
self.run_method("StageTwo", "exit")
|
||||||
|
|
||||||
def run_method(self, typ: str, method: str):
|
def parse_response(self, data: bytes):
|
||||||
"""Run a method reflectively from the loaded StageTwo assembly. This
|
""" Parse a line of data from the C2 """
|
||||||
can technically run any .Net method, but doesn't implement a way to
|
|
||||||
abstractly pass arguments. Instead, all the StageTwo methods take
|
with gzip.GzipFile(
|
||||||
arguments through stdin.
|
fileobj=BytesIO(base64.b64decode(data.decode("utf-8").strip())),
|
||||||
|
mode="rb",
|
||||||
|
) as gz:
|
||||||
|
result = json.loads(gz.read().decode("utf-8"))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def run_method(self, typ: str, method: str, *args, wait: bool = True):
|
||||||
|
"""
|
||||||
|
Execute a method within the pwncat-windows-c2. You must specify the type
|
||||||
|
and method arguments. Arguments are passed via json encoding so any valid
|
||||||
|
JSON types should be passed correctly onto the C2. Named arguments are not
|
||||||
|
supported. Results are returned as a dictionary. In the case of an error,
|
||||||
|
a ProtocolError is raised with the error code and message.
|
||||||
|
|
||||||
:param typ: The type name where the method you'd like to execute resides
|
:param typ: The type name where the method you'd like to execute resides
|
||||||
:type typ: str
|
:type typ: str
|
||||||
:param method: The name of the method you'd like to execute
|
:param method: The name of the method you'd like to execute
|
||||||
:type method: str
|
:type method: str
|
||||||
|
:param \*args: the positional arguments for the method you are calling
|
||||||
|
:type \*args: correct type for given method
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.channel.send(f"{typ}\n{method}\n".encode("utf-8"))
|
command = [typ, method, *args]
|
||||||
|
payload = BytesIO()
|
||||||
|
|
||||||
|
# compress command arguments
|
||||||
|
with gzip.GzipFile(fileobj=payload, mode="wb") as gz:
|
||||||
|
gz.write(json.dumps(command).encode("utf-8"))
|
||||||
|
|
||||||
|
# Send the command
|
||||||
|
thing = base64.b64encode(payload.getbuffer())
|
||||||
|
self.channel.sendline(thing)
|
||||||
|
|
||||||
|
if wait:
|
||||||
|
|
||||||
|
# Receive the response
|
||||||
|
result = self.parse_response(self.channel.recvline())
|
||||||
|
|
||||||
|
# Raise an appropriate error if needed
|
||||||
|
if result["error"] != 0:
|
||||||
|
raise ProtocolError(result["error"], result.get("message", ""))
|
||||||
|
|
||||||
|
return result["result"]
|
||||||
|
|
||||||
def setup_prompt(self):
|
def setup_prompt(self):
|
||||||
"""Set a prompt method for powershell to ensure our prompt looks pretty :)"""
|
"""Set a prompt method for powershell to ensure our prompt looks pretty :)"""
|
||||||
@ -679,24 +718,13 @@ function prompt {
|
|||||||
elif not isinstance(args, str):
|
elif not isinstance(args, str):
|
||||||
raise ValueError("expected command string or list of arguments")
|
raise ValueError("expected command string or list of arguments")
|
||||||
|
|
||||||
self.run_method("Process", "start")
|
try:
|
||||||
self.channel.sendline(args.encode("utf-8"))
|
result = self.run_method("Process", "start", args)
|
||||||
|
except ProtocolError as exc:
|
||||||
hProcess = self.channel.recvuntil(b"\n").strip().decode("utf-8")
|
if "pipe" in exc.message:
|
||||||
if hProcess == "E:IN":
|
raise OSError(exc.message)
|
||||||
raise RuntimeError("failed to open stdin pipe")
|
else:
|
||||||
if hProcess == "E:OUT":
|
raise FileNotFoundError(exc.message)
|
||||||
raise RuntimeError("failed to open stdout pipe")
|
|
||||||
if hProcess == "E:ERR":
|
|
||||||
raise RuntimeError("failed to open stderr pipe")
|
|
||||||
if hProcess == "E:PROC":
|
|
||||||
raise FileNotFoundError("executable or command not found")
|
|
||||||
|
|
||||||
# Collect process properties
|
|
||||||
hProcess = int(hProcess)
|
|
||||||
stdio = []
|
|
||||||
for i in range(3):
|
|
||||||
stdio.append(int(self.channel.recvuntil(b"\n").strip().decode("utf-8")))
|
|
||||||
|
|
||||||
return PopenWindows(
|
return PopenWindows(
|
||||||
self,
|
self,
|
||||||
@ -708,8 +736,7 @@ function prompt {
|
|||||||
encoding,
|
encoding,
|
||||||
errors,
|
errors,
|
||||||
bufsize,
|
bufsize,
|
||||||
hProcess,
|
result,
|
||||||
stdio,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_host_hash(self):
|
def get_host_hash(self):
|
||||||
@ -762,15 +789,28 @@ function prompt {
|
|||||||
# Reset the tracker
|
# Reset the tracker
|
||||||
|
|
||||||
if value:
|
if value:
|
||||||
self.run_method("PowerShell", "start")
|
try:
|
||||||
|
self.run_method("PowerShell", "start", wait=False)
|
||||||
|
except ProtocolError as exc:
|
||||||
|
raise PlatformError(exc.message)
|
||||||
|
|
||||||
|
# Wait for the powershell runspace to be up and running
|
||||||
output = self.channel.recvline()
|
output = self.channel.recvline()
|
||||||
if not output.strip().startswith(b"INTERACTIVE_START"):
|
if not output.strip().startswith(b"INTERACTIVE_START"):
|
||||||
self.interactive_tracker = len(INTERACTIVE_END_MARKER)
|
self.interactive_tracker = len(INTERACTIVE_END_MARKER)
|
||||||
raise PlatformError(f"no interactive start message: {output}")
|
result = self.parse_response(output)
|
||||||
|
raise PlatformError(result["message"])
|
||||||
|
|
||||||
self._interactive = True
|
self._interactive = True
|
||||||
self.interactive_tracker = 0
|
self.interactive_tracker = 0
|
||||||
return
|
return
|
||||||
if not value:
|
if not value:
|
||||||
|
|
||||||
|
# Receive the method response
|
||||||
|
data = self.parse_response(self.channel.recvline())
|
||||||
|
if data["error"] != 0:
|
||||||
|
self.session.log(data["message"])
|
||||||
|
|
||||||
self._interactive = False
|
self._interactive = False
|
||||||
self.refresh_uid()
|
self.refresh_uid()
|
||||||
|
|
||||||
@ -837,17 +877,12 @@ function prompt {
|
|||||||
if "b" not in mode:
|
if "b" not in mode:
|
||||||
buffering = -1
|
buffering = -1
|
||||||
|
|
||||||
self.run_method("File", "open")
|
|
||||||
self.channel.sendline(str(path).encode("utf-8"))
|
|
||||||
self.channel.sendline(mode.encode("utf-8"))
|
|
||||||
result = self.channel.recvuntil(b"\n").strip()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
handle = int(result)
|
result = self.run_method("File", "open", path, mode)
|
||||||
except ValueError:
|
except ProtocolError as exc:
|
||||||
raise FileNotFoundError(f"{str(path)}: {result}")
|
raise FileNotFoundError(f"{path}: {exc.message}")
|
||||||
|
|
||||||
stream = WindowsFile(self, mode, handle, name=path)
|
stream = WindowsFile(self, mode, result["handle"], name=path)
|
||||||
|
|
||||||
if "b" not in mode:
|
if "b" not in mode:
|
||||||
stream = TextIOWrapper(
|
stream = TextIOWrapper(
|
||||||
@ -1291,37 +1326,14 @@ function prompt {
|
|||||||
:type depth: int
|
:type depth: int
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(script, str):
|
if not isinstance(script, str):
|
||||||
script = BytesIO(script.encode("utf-8"))
|
script = script.read()
|
||||||
|
if isinstance(script, bytes):
|
||||||
payload = BytesIO()
|
script = script.decode("utf-8")
|
||||||
|
|
||||||
with gzip.GzipFile(fileobj=payload, mode="wb") as gz:
|
|
||||||
shutil.copyfileobj(script, gz)
|
|
||||||
|
|
||||||
self.run_method("PowerShell", "run")
|
|
||||||
self.channel.sendline(base64.b64encode(payload.getbuffer()))
|
|
||||||
self.channel.sendline(str(depth).encode("utf-8"))
|
|
||||||
|
|
||||||
results = []
|
|
||||||
result = self.channel.recvline().strip()
|
|
||||||
|
|
||||||
if result.startswith(b"E:S2:EXCEPTION:"):
|
|
||||||
raise PlatformError(result.split(b"E:S2:EXCEPTION:")[1].decode("utf-8"))
|
|
||||||
|
|
||||||
# Wait for the command to complete
|
|
||||||
while result != b"DONE":
|
|
||||||
result = self.channel.recvline().strip()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Receive results
|
result = self.run_method("PowerShell", "run", script, depth)
|
||||||
result = self.channel.recvline().strip()
|
except ProtocolError as exc:
|
||||||
if result.startswith(b"E:PWSH:"):
|
raise PowershellError(exc.message)
|
||||||
raise PowershellError(result.split(b"E:PWSH:")[1].decode("utf-8"))
|
|
||||||
while result != b"END":
|
|
||||||
results.append(json.loads(result))
|
|
||||||
result = self.channel.recvline().strip()
|
|
||||||
except json.JSONDecodeError as exc:
|
|
||||||
raise PlatformError(result)
|
|
||||||
|
|
||||||
return results
|
return [json.loads(x) for x in result["output"]]
|
||||||
|
Loading…
Reference in New Issue
Block a user