mirror of
https://github.com/calebstewart/pwncat.git
synced 2024-11-27 19:04:15 +01:00
Initial working windows setup
I have opened the Windows C2 repository, and added the ability for pwncat to automatically download the C2 DLLs. If you don't have internet or would rather grab them yourself, you can place them in ~/.local/share/pwncat (or point the `windows_c2_dir` config at the directory where you do place them). If `stageone.dll` and `stagetwo.dll` exist in that directory, pwncat will not attempt to download them from github.
This commit is contained in:
parent
97c4d256ab
commit
830fe7b211
@ -88,6 +88,10 @@ class Config:
|
||||
"cross": {"value": None, "type": str},
|
||||
"psmodules": {"value": ".", "type": local_dir_type},
|
||||
"verbose": {"value": False, "type": bool_type},
|
||||
"windows_c2_dir": {
|
||||
"value": "~/.local/share/pwncat",
|
||||
"type": local_dir_type,
|
||||
},
|
||||
}
|
||||
|
||||
# Locals are set per-used-module
|
||||
|
@ -100,7 +100,7 @@ class Module(BaseModule):
|
||||
with open(output, "w") as filp:
|
||||
template.stream(context).dump(filp)
|
||||
else:
|
||||
markdown = Markdown(template.render(context))
|
||||
markdown = Markdown(template.render(context), hyperlinks=False)
|
||||
console.print(markdown)
|
||||
except jinja2.TemplateError as exc:
|
||||
raise ModuleFailed(str(exc)) from exc
|
||||
|
@ -8,30 +8,26 @@ import time
|
||||
import base64
|
||||
import shutil
|
||||
import pathlib
|
||||
import tarfile
|
||||
import termios
|
||||
import readline
|
||||
import textwrap
|
||||
import subprocess
|
||||
from io import (
|
||||
BytesIO,
|
||||
StringIO,
|
||||
RawIOBase,
|
||||
TextIOWrapper,
|
||||
BufferedIOBase,
|
||||
UnsupportedOperation,
|
||||
)
|
||||
from io import (BytesIO, StringIO, RawIOBase, TextIOWrapper, BufferedIOBase,
|
||||
UnsupportedOperation)
|
||||
from typing import List, Union, BinaryIO
|
||||
from subprocess import TimeoutExpired, CalledProcessError
|
||||
from dataclasses import dataclass
|
||||
|
||||
import pkg_resources
|
||||
|
||||
import pwncat
|
||||
import requests
|
||||
import pwncat.util
|
||||
import pkg_resources
|
||||
import pwncat.subprocess
|
||||
from pwncat.platform import Path, Platform, PlatformError
|
||||
|
||||
INTERACTIVE_END_MARKER = b"\nINTERACTIVE_COMPLETE\r\n"
|
||||
PWNCAT_WINDOWS_C2_RELEASE_URL = "https://github.com/calebstewart/pwncat-windows-c2/releases/download/v0.0.1/pwncat-windows-v0.0.1.tar.gz"
|
||||
|
||||
|
||||
class PowershellError(Exception):
|
||||
@ -122,6 +118,7 @@ class WindowsFile(RawIOBase):
|
||||
return 0
|
||||
|
||||
self.platform.run_method("File", "read")
|
||||
self.platform.channel.sendline(str(self.handle).encode("utf-8"))
|
||||
self.platform.channel.sendline(str(len(b)).encode("utf-8"))
|
||||
count = int(self.platform.channel.recvuntil(b"\n").strip())
|
||||
|
||||
@ -390,8 +387,24 @@ class Windows(Platform):
|
||||
# Tracks paths to modules which have been sideloaded into powershell
|
||||
self.psmodules = []
|
||||
|
||||
# Ensure we have the C2 libraries downloaded
|
||||
self._ensure_libs()
|
||||
|
||||
self._bootstrap_stage_two()
|
||||
|
||||
# This is a dirty hack
|
||||
old_close = self.channel.close
|
||||
|
||||
def new_close():
|
||||
self.run_method("StageTwo", "exit")
|
||||
old_close()
|
||||
|
||||
self.channel.close = new_close
|
||||
|
||||
self.refresh_uid()
|
||||
|
||||
self.setup_prompt()
|
||||
|
||||
# Load requested libraries
|
||||
# for library, methods in self.LIBRARY_IMPORTS.items():
|
||||
# self._load_library(library, methods)
|
||||
@ -401,6 +414,41 @@ class Windows(Platform):
|
||||
|
||||
self.channel.send(f"{typ}\n{method}\n".encode("utf-8"))
|
||||
|
||||
def setup_prompt(self):
|
||||
""" Set a prompt method for powershell to ensure our prompt looks pretty :) """
|
||||
|
||||
self.powershell(
|
||||
"""
|
||||
function prompt {
|
||||
$ESC = [char]27
|
||||
Write-Host "$ESC[31m(remote)$ESC[33m $env:UserName@$env:ComputerName$ESC[0m:$ESC[36m$($executionContext.SessionState.Path.CurrentLocation)$ESC[0m$" -NoNewLine
|
||||
return " "
|
||||
}"""
|
||||
)
|
||||
|
||||
def _ensure_libs(self):
|
||||
"""This method checks that stageone.dll and stagetwo.dll exist within
|
||||
the directory specified by the windows_c2_dir configuration. If they do
|
||||
not, a release copy is downloaded from GitHub."""
|
||||
|
||||
location = pathlib.Path(self.session.config["windows_c2_dir"]).expanduser()
|
||||
location.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if (
|
||||
not (location / "stageone.dll").exists()
|
||||
or not (location / "stagetwo.dll").exists()
|
||||
):
|
||||
self.session.manager.log("Downloading Windows C2 binaries from GitHub...")
|
||||
with requests.get(PWNCAT_WINDOWS_C2_RELEASE_URL, stream=True) as request:
|
||||
data = request.raw.read()
|
||||
with tarfile.open(mode="r:gz", fileobj=BytesIO(data)) as tar:
|
||||
with tar.extractfile("stageone.dll") as stageone:
|
||||
with (location / "stageone.dll").open("wb") as output:
|
||||
shutil.copyfileobj(stageone, output)
|
||||
with tar.extractfile("stagetwo.dll") as stagetwo:
|
||||
with (location / "stagetwo.dll").open("wb") as output:
|
||||
shutil.copyfileobj(stagetwo, output)
|
||||
|
||||
def _bootstrap_stage_two(self):
|
||||
"""This takes the stage one C2 (powershell) and boostraps it for stage
|
||||
two. Stage two is C# code dynamically compiled and executed. We first
|
||||
@ -432,11 +480,17 @@ class Windows(Platform):
|
||||
chunk_sz = 1900
|
||||
|
||||
loader_encoded_name = pwncat.util.random_string()
|
||||
stageone = (
|
||||
pathlib.Path(self.session.config["windows_c2_dir"]).expanduser()
|
||||
/ "stageone.dll"
|
||||
)
|
||||
stagetwo = (
|
||||
pathlib.Path(self.session.config["windows_c2_dir"]).expanduser()
|
||||
/ "stagetwo.dll"
|
||||
)
|
||||
|
||||
# Read the loader
|
||||
with open(
|
||||
pkg_resources.resource_filename("pwncat", "data/loader.dll"), "rb"
|
||||
) as filp:
|
||||
with stageone.open("rb") as filp:
|
||||
loader_dll = base64.b64encode(filp.read())
|
||||
|
||||
# Extract first chunk
|
||||
@ -519,9 +573,7 @@ class Windows(Platform):
|
||||
self.channel.recvuntil(b"\n")
|
||||
|
||||
# Load, Compress and Encode stage two
|
||||
with open(
|
||||
pkg_resources.resource_filename("pwncat", "data/stagetwo.dll"), "rb"
|
||||
) as filp:
|
||||
with stagetwo.open("rb") as filp:
|
||||
stagetwo_dll = filp.read()
|
||||
compressed = BytesIO()
|
||||
with gzip.GzipFile(fileobj=compressed, mode="wb") as gz:
|
||||
@ -774,6 +826,20 @@ class Windows(Platform):
|
||||
except CalledProcessError:
|
||||
return None
|
||||
|
||||
def refresh_uid(self):
|
||||
""" Retrieve the current user ID """
|
||||
|
||||
self.powershell(
|
||||
"Add-Type -AssemblyName System.DirectoryServices.AccountManagement"
|
||||
)
|
||||
self.user_info = self.powershell(
|
||||
"([System.DirectoryServices.AccountManagement.UserPrincipal]::Current).SID.Value"
|
||||
)
|
||||
|
||||
def getuid(self):
|
||||
|
||||
return self.user_info
|
||||
|
||||
def new_item(self, **kwargs):
|
||||
"""Run the `New-Item` commandlet with specified arguments and
|
||||
raise the appropriate local exception if requried."""
|
||||
|
Loading…
Reference in New Issue
Block a user