static uint8_t *decode_html(uint8_t *data, uint64_t data_length, uint64_t *out_length) { buffer_t *b = buffer_create(BO_HOST); uint64_t i = 0; while(i < data_length) { /* If the character is a '%' and we aren't at the end of the string, decode * the hex character and add it to the string. * * The typecasts to 'int' here are to fix warnings from cygwin. */ if(data[i] == '%' && (i + 2) < data_length && isxdigit((int)data[i + 1]) && isxdigit((int)data[i + 2])) { /* Add the new character to the string as a uint8_t. */ buffer_add_int8(b, hex_to_int(&data[i] + 1)); /* We consumed three digits here. */ i += 3; } else if(data[i] == '+') { /* In html encoding, a '+' is a space. */ buffer_add_int8(b, ' '); i++; } else { /* If it's not %NN or +, it's just a raw number.k */ buffer_add_int8(b, data[i]); i++; } } return buffer_create_string_and_destroy(b, out_length); }
static uint8_t *decode_hex(uint8_t *data, uint64_t data_length, uint64_t *out_length) { buffer_t *b = buffer_create(BO_HOST); uint64_t i = 0; /* If we wind up with an odd number of characters, the final character is * ignored. */ while(i + 1 < data_length) { /* Skip over and ignore non-hex digits. */ if(!isxdigit(data[i]) || !isxdigit(data[i+1])) { i++; continue; } /* Add the new character to the string as a uint8_t. */ buffer_add_int8(b, hex_to_int(&data[i])); /* We consumed three digits here. */ i += 2; } return buffer_create_string_and_destroy(b, out_length); }
static uint8_t *encode_html(uint8_t *data, uint64_t data_length, uint64_t *out_length) { int i; buffer_t *b = buffer_create(BO_HOST); char tmp[16]; for(i = 0; i < data_length; i++) { if(isalnum(data[i])) { /* If the character is alphanumeric, add it as-is. */ buffer_add_int8(b, data[i]); } else if(data[i] == ' ') { /* If the character is a space, add a '+'. */ buffer_add_int8(b, '+'); } else { /* Otherwise, encode it as a % followed by a hex code. */ sprintf(tmp, "%%%02x", data[i]); buffer_add_string(b, tmp); } } return buffer_create_string_and_destroy(b, out_length); }
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); }
static uint8_t *buffer_decode_hex(uint8_t *str, size_t *length) { size_t i = 0; buffer_t *out = buffer_create(BO_BIG_ENDIAN); /*printf("Decoding: %s (%zu bytes)...\n", str, *length);*/ while(i < *length) { uint8_t c1 = 0; uint8_t c2 = 0; /* Read the first character, ignoring periods */ do { c1 = toupper(str[i++]); } while(c1 == '.' && i < *length); /* Make sure we aren't at the end of the buffer. */ if(i >= *length) { LOG_ERROR("Couldn't hex-decode the name (name was an odd length): %s", str); return NULL; } /* Make sure we got a hex digit */ if(!isxdigit(c1)) { LOG_ERROR("Couldn't hex-decode the name (contains non-hex characters): %s", str); return NULL; } /* Read the second character. */ do { c2 = toupper(str[i++]); } while(c2 == '.' && i < *length); /* Make sure we got a hex digit */ if(!isxdigit(c2)) { LOG_ERROR("Couldn't hex-decode the name (contains non-hex characters): %s", str); return NULL; } c1 = ((c1 < 'A') ? (c1 - '0') : (c1 - 'A' + 10)); c2 = ((c2 < 'A') ? (c2 - '0') : (c2 - 'A' + 10)); buffer_add_int8(out, (c1 << 4) | c2); } return buffer_create_string_and_destroy(out, length); }
/* Note: This function isn't tested, so be careful about messing around! */ static uint8_t *encode_fancy(uint8_t *data, uint64_t data_length, uint64_t *out_length) { uint64_t i, j; buffer_t *b = buffer_create(BO_HOST); char tmp[64]; for(i = 0; i < data_length; i++) { if((i % 16) == 0) /* if i is a multiple of 16... */ { if(i > 0) { sprintf(tmp, " "); buffer_add_string(b, tmp); for(j = 16; j > 0; j--) buffer_add_int8(b, get_character_from_byte(data[i - j])); } sprintf(tmp, "\n%04X: ", (uint16_t)i); buffer_add_string(b, tmp); } sprintf(tmp, "%02X ", data[i]); buffer_add_string(b, tmp); } if((i % 16) == 0) { sprintf(tmp, " "); buffer_add_string(b, tmp); for(j = 16; j > 0; j--) buffer_add_int8(b, get_character_from_byte(data[i - j])); } else { /* Add padding spaces. */ for(i = data_length % 16; i < 17; i++) buffer_add_string(b, " "); for(i = data_length - (data_length % 16); i < data_length; i++) buffer_add_int8(b, get_character_from_byte(data[i])); } sprintf(tmp, "\nLength: 0x%"PRIX64" (%"PRId64")\n", data_length, data_length); buffer_add_string(b, tmp); /* Null terminate the buffer. */ buffer_add_int8(b, 0); return buffer_create_string_and_destroy(b, out_length); }
static uint8_t *encode_cstr_pure(uint8_t *data, uint64_t data_length, uint64_t *out_length) { int i; buffer_t *b = buffer_create(BO_HOST); char tmp[16]; /* Encode every character as \xNN. */ for(i = 0; i < data_length; i++) { sprintf(tmp, "\\x%02x", data[i]); buffer_add_string(b, tmp); } return buffer_create_string_and_destroy(b, out_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); }
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); } }
/* Read data from the session that will be sent out across the network. This * will return up to read_length bytes (and will return the number of bytes * read in read_length as well). The memory returned has to be freed. * * If the session isn't found, NULL is returned. If no data is waiting, the * empty string is returned and read_length is set to 0. */ uint8_t *session_read(sessions_t *sessions, char *session_name, uint32_t *read_length) { uint32_t i = 0; buffer_t *incoming; buffer_t *response; session_t *session; session = session_get(sessions, session_name); if(!session) return NULL; if(session_is_closed(sessions, session_name)) return NULL; if(buffer_can_read_int8(sessions->buffer_data)) { incoming = sessions->buffer_data; } else { if(!session) return NULL; incoming = session->buffer; } response = buffer_create(BO_NETWORK); while(buffer_can_read_int8(incoming) && i < *read_length) { if(sessions->log) fputc(buffer_peek_next_int8(incoming), sessions->log); buffer_add_int8(response, buffer_read_next_int8(incoming)); i++; } /* Clear whichever buffer we're using if we're at the end. */ if(!buffer_can_read_int8(incoming)) buffer_clear(incoming); return buffer_create_string_and_destroy(response, read_length); }
static uint8_t *encode_cstr(uint8_t *data, uint64_t data_length, uint64_t *out_length) { int i; buffer_t *b = buffer_create(BO_HOST); char tmp[16]; for(i = 0; i < data_length; i++) { if(isalnum(data[i])) { /* Add letters/numbers as-is. */ buffer_add_int8(b, data[i]); } else { /* Encode all other characters as "\xNN". */ sprintf(tmp, "\\x%02x", data[i]); buffer_add_string(b, tmp); } } return buffer_create_string_and_destroy(b, out_length); }
/* 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 uint8_t *decode_cstr(uint8_t *data, uint64_t data_length, uint64_t *out_length) { buffer_t *b = buffer_create(BO_HOST); uint64_t i = 0; uint64_t in_length = data_length; while(i < in_length) { if(data[i] == '\\') { /* Consume the slash. */ i++; /* Check for the various format specifiers - \a, \b, \t, \n, \r, etc) */ if(i < in_length && data[i] == '\\') { buffer_add_int8(b, '\\'); i++; } else if(i < in_length && data[i] == 'a') { buffer_add_int8(b, 0x07); i++; } else if(i < in_length && data[i] == 'b') { buffer_add_int8(b, 0x08); i++; } else if(i < in_length && data[i] == 't') { buffer_add_int8(b, 0x09); i++; } else if(i < in_length && data[i] == 'n') { buffer_add_int8(b, 0x0a); i++; } else if(i < in_length && data[i] == 'v') { buffer_add_int8(b, 0x0b); i++; } else if(i < in_length && data[i] == 'f') { buffer_add_int8(b, 0x0c); i++; } else if(i < in_length && data[i] == 'r') { buffer_add_int8(b, 0x0d); i++; } else if(i < in_length && data[i] == 'e') { buffer_add_int8(b, 0x1b); i++; } /* Ensure the data is sane. */ else if(i + 2 < in_length && data[i] == 'x' && isxdigit((int)data[i + 1]) && isxdigit((int)data[i + 2])) { /* Add the new character to the string as a uint8_t. */ buffer_add_int8(b, hex_to_int(&data[i] + 1)); /* We consumed three digits here. */ i += 3; } else { buffer_add_int8(b, '\\'); } } else { buffer_add_int8(b, data[i]); i++; } } return buffer_create_string_and_destroy(b, out_length); }
/* 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); }