/* * Receive a new packet */ DWORD packet_receive(Remote *remote, Packet **packet) { DWORD headerBytes = 0, payloadBytesLeft = 0, res; CryptoContext *crypto = NULL; Packet *localPacket = NULL; TlvHeader header; LONG bytesRead; BOOL inHeader = TRUE; PUCHAR payload = NULL; ULONG payloadLength; #ifdef _UNIX int local_error = -1; #endif do { // Read the packet length while (inHeader) { if ((bytesRead = SSL_read(remote->ssl, ((PUCHAR)&header + headerBytes), sizeof(TlvHeader) - headerBytes)) <= 0) { if (!bytesRead) SetLastError(ERROR_NOT_FOUND); if(bytesRead < 0) { dprintf("receive header failed with error code %d\n", bytesRead); SetLastError(ERROR_NOT_FOUND); } break; } headerBytes += bytesRead; if (headerBytes != sizeof(TlvHeader)) continue; else inHeader = FALSE; } if (bytesRead != sizeof(TlvHeader)) break; // Initialize the header header.length = header.length; header.type = header.type; payloadLength = ntohl(header.length) - sizeof(TlvHeader); payloadBytesLeft = payloadLength; // Allocate the payload if (!(payload = (PUCHAR)malloc(payloadLength))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; } // Read the payload while (payloadBytesLeft > 0) { if ((bytesRead = SSL_read(remote->ssl, payload + payloadLength - payloadBytesLeft, payloadBytesLeft)) <= 0) { if (GetLastError() == WSAEWOULDBLOCK) continue; if (!bytesRead) SetLastError(ERROR_NOT_FOUND); if(bytesRead < 0) { dprintf("receive payload of length %d failed with error code %d\n", payloadLength, bytesRead); SetLastError(ERROR_NOT_FOUND); } break; } payloadBytesLeft -= bytesRead; } // Didn't finish? if (payloadBytesLeft) break; // Allocate a packet structure if (!(localPacket = (Packet *)malloc(sizeof(Packet)))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; } // If the connection has an established cipher and this packet is not // plaintext, decrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = payloadLength; PUCHAR origPayload = payload; // Decrypt if ((res = crypto->handlers.decrypt(crypto, payload, payloadLength, &payload, &payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // We no longer need the encrypted payload free(origPayload); } localPacket->header.length = header.length; localPacket->header.type = header.type; localPacket->payload = payload; localPacket->payloadLength = payloadLength; *packet = localPacket; SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Cleanup on failure if (res != ERROR_SUCCESS) { if (payload) free(payload); if (localPacket) free(localPacket); } return res; }
/* * Transmit and destroy a packet */ DWORD packet_transmit(Remote *remote, Packet *packet, PacketRequestCompletion *completion) { CryptoContext *crypto; Tlv requestId; DWORD res; DWORD idx; #ifdef _UNIX int local_error = -1; #endif // If the packet does not already have a request identifier, create // one for it if (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) != ERROR_SUCCESS) { DWORD index; CHAR rid[32]; rid[sizeof(rid) - 1] = 0; for (index = 0; index < sizeof(rid) - 1; index++) rid[index] = (rand() % 0x5e) + 0x21; packet_add_tlv_string(packet, TLV_TYPE_REQUEST_ID, rid); } do { // If a completion routine was supplied and the packet has a request // identifier, insert the completion routine into the list if ((completion) && (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) == ERROR_SUCCESS)) packet_add_completion_handler((LPCSTR)requestId.buffer, completion); // If the endpoint has a cipher established and this is not a plaintext // packet, we encrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = packet->payloadLength; PUCHAR origPayload = packet->payload; // Encrypt if ((res = crypto->handlers.encrypt(crypto, packet->payload, packet->payloadLength, &packet->payload, &packet->payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // Destroy the original payload as we no longer need it free(origPayload); // Update the header length packet->header.length = htonl(packet->payloadLength + sizeof(TlvHeader)); } idx = 0; while( idx < sizeof(packet->header)) { // Transmit the packet's header (length, type) res = SSL_write( remote->ssl, (LPCSTR)(&packet->header) + idx, sizeof(packet->header) - idx ); if(res <= 0) { dprintf("transmit header failed with return %d at index %d\n", res, idx); break; } idx += res; } if(res < 0) break; idx = 0; while( idx < packet->payloadLength) { // Transmit the packet's payload (length, type) res = SSL_write( remote->ssl, packet->payload + idx, packet->payloadLength - idx ); if(res < 0) break; idx += res; } if(res < 0) { dprintf("transmit header failed with return %d at index %d\n", res, idx); break; } SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Destroy the packet packet_destroy(packet); return res; }
/*! * @brief Receive a new packet on the given remote endpoint. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to a pointer that will receive the \c Packet data. * @return An indication of the result of processing the transmission request. */ static DWORD packet_receive_via_ssl(Remote *remote, Packet **packet) { DWORD headerBytes = 0, payloadBytesLeft = 0, res; CryptoContext *crypto = NULL; Packet *localPacket = NULL; PacketHeader header; LONG bytesRead; BOOL inHeader = TRUE; PUCHAR payload = NULL; ULONG payloadLength; TcpTransportContext* ctx = (TcpTransportContext*)remote->transport->ctx; lock_acquire(remote->lock); do { // Read the packet length while (inHeader) { if ((bytesRead = SSL_read(ctx->ssl, ((PUCHAR)&header + headerBytes), sizeof(PacketHeader)-headerBytes)) <= 0) { if (!bytesRead) { SetLastError(ERROR_NOT_FOUND); } if (bytesRead < 0) { dprintf("[PACKET] receive header failed with error code %d. SSLerror=%d, WSALastError=%d\n", bytesRead, SSL_get_error(ctx->ssl, bytesRead), WSAGetLastError()); SetLastError(ERROR_NOT_FOUND); } break; } headerBytes += bytesRead; if (headerBytes != sizeof(PacketHeader)) { continue; } inHeader = FALSE; } if (headerBytes != sizeof(PacketHeader)) { break; } header.xor_key = ntohl(header.xor_key); // xor the header data xor_bytes(header.xor_key, &header.length, 8); // Initialize the header header.length = ntohl(header.length); // use TlvHeader size here, because the length doesn't include the xor byte payloadLength = header.length - sizeof(TlvHeader); payloadBytesLeft = payloadLength; // Allocate the payload if (!(payload = (PUCHAR)malloc(payloadLength))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; } // Read the payload while (payloadBytesLeft > 0) { if ((bytesRead = SSL_read(ctx->ssl, payload + payloadLength - payloadBytesLeft, payloadBytesLeft)) <= 0) { if (GetLastError() == WSAEWOULDBLOCK) { continue; } if (!bytesRead) { SetLastError(ERROR_NOT_FOUND); } if (bytesRead < 0) { dprintf("[PACKET] receive payload of length %d failed with error code %d. SSLerror=%d\n", payloadLength, bytesRead, SSL_get_error(ctx->ssl, bytesRead)); SetLastError(ERROR_NOT_FOUND); } break; } payloadBytesLeft -= bytesRead; } // Didn't finish? if (payloadBytesLeft) { break; } xor_bytes(header.xor_key, payload, payloadLength); // Allocate a packet structure if (!(localPacket = (Packet *)malloc(sizeof(Packet)))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; } memset(localPacket, 0, sizeof(Packet)); // If the connection has an established cipher and this packet is not // plaintext, decrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = payloadLength; PUCHAR origPayload = payload; // Decrypt if ((res = crypto->handlers.decrypt(crypto, payload, payloadLength, &payload, &payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // We no longer need the encrypted payload free(origPayload); } localPacket->header.length = header.length; localPacket->header.type = header.type; localPacket->payload = payload; localPacket->payloadLength = payloadLength; *packet = localPacket; SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Cleanup on failure if (res != ERROR_SUCCESS) { if (payload) { free(payload); } if (localPacket) { free(localPacket); } } lock_release(remote->lock); return res; }
/*! * @brief Transmit a packet via SSL _and_ destroy it. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to the \c Packet that is to be sent. * @param completion Pointer to the completion routines to process. * @return An indication of the result of processing the transmission request. * @remark This uses an SSL-encrypted TCP channel, and does not imply the use of HTTPS. */ DWORD packet_transmit_via_ssl(Remote* remote, Packet* packet, PacketRequestCompletion* completion) { CryptoContext* crypto; Tlv requestId; DWORD res; DWORD idx; TcpTransportContext* ctx = (TcpTransportContext*)remote->transport->ctx; lock_acquire(remote->lock); // If the packet does not already have a request identifier, create one for it if (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) != ERROR_SUCCESS) { DWORD index; CHAR rid[32]; rid[sizeof(rid)-1] = 0; for (index = 0; index < sizeof(rid)-1; index++) { rid[index] = (rand() % 0x5e) + 0x21; } packet_add_tlv_string(packet, TLV_TYPE_REQUEST_ID, rid); } do { // If a completion routine was supplied and the packet has a request // identifier, insert the completion routine into the list if ((completion) && (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) == ERROR_SUCCESS)) { packet_add_completion_handler((LPCSTR)requestId.buffer, completion); } // If the endpoint has a cipher established and this is not a plaintext // packet, we encrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = packet->payloadLength; PUCHAR origPayload = packet->payload; // Encrypt if ((res = crypto->handlers.encrypt(crypto, packet->payload, packet->payloadLength, &packet->payload, &packet->payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // Destroy the original payload as we no longer need it free(origPayload); // Update the header length packet->header.length = htonl(packet->payloadLength + sizeof(TlvHeader)); } dprintf("[PACKET] New xor key for sending"); packet->header.xor_key = rand_xor_key(); // before transmission, xor the whole lot, starting with the body xor_bytes(packet->header.xor_key, (LPBYTE)packet->payload, packet->payloadLength); // then the header xor_bytes(packet->header.xor_key, (LPBYTE)&packet->header.length, 8); // be sure to switch the xor header before writing packet->header.xor_key = htonl(packet->header.xor_key); idx = 0; while (idx < sizeof(packet->header)) { // Transmit the packet's header (length, type) res = SSL_write( ctx->ssl, (LPCSTR)(&packet->header) + idx, sizeof(packet->header) - idx ); if (res <= 0) { dprintf("[PACKET] transmit header failed with return %d at index %d\n", res, idx); break; } idx += res; } if (res < 0) { break; } idx = 0; while (idx < packet->payloadLength) { // Transmit the packet's payload (length, type) res = SSL_write( ctx->ssl, packet->payload + idx, packet->payloadLength - idx ); if (res < 0) { break; } idx += res; } if (res < 0) { dprintf("[PACKET] transmit header failed with return %d at index %d\n", res, idx); break; } SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Destroy the packet packet_destroy(packet); lock_release(remote->lock); return res; }
/* * Receive a new packet over HTTP using WinInet */ DWORD packet_receive_http_via_wininet(Remote *remote, Packet **packet) { DWORD headerBytes = 0, payloadBytesLeft = 0, res; CryptoContext *crypto = NULL; Packet *localPacket = NULL; TlvHeader header; LONG bytesRead; BOOL inHeader = TRUE; PUCHAR payload = NULL; ULONG payloadLength; DWORD flags; DWORD flen; HINTERNET hReq; HINTERNET hRes; DWORD retries = 5; lock_acquire( remote->lock ); do { flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_AUTO_REDIRECT | INTERNET_FLAG_NO_UI; if (remote->transport == METERPRETER_TRANSPORT_HTTPS) { flags |= INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID; } dprintf("[PACKET RECEIVE] HttpOpenRequest"); hReq = HttpOpenRequest(remote->hConnection, "POST", remote->uri, NULL, NULL, NULL, flags, 0); if (hReq == NULL) { dprintf("[PACKET RECEIVE] Failed HttpOpenRequest: %d", GetLastError()); SetLastError(ERROR_NOT_FOUND); break; } if (remote->transport == METERPRETER_TRANSPORT_HTTPS) { InternetQueryOption( hReq, INTERNET_OPTION_SECURITY_FLAGS, &flags, &flen); flags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_UNKNOWN_CA; InternetSetOption(hReq, INTERNET_OPTION_SECURITY_FLAGS, &flags, flen); } hRes = HttpSendRequest(hReq, NULL, 0, "RECV", 4 ); if (! hRes) { dprintf("[PACKET RECEIVE] Failed HttpSendRequest: %d", GetLastError()); SetLastError(ERROR_NOT_FOUND); break; } // Read the packet length retries = 3; while (inHeader && retries > 0) { retries--; if (! InternetReadFile(hReq, ((PUCHAR)&header + headerBytes), sizeof(TlvHeader) - headerBytes, &bytesRead)) { dprintf("[PACKET RECEIVE] Failed HEADER InternetReadFile: %d", GetLastError()); SetLastError(ERROR_NOT_FOUND); break; } // If the response contains no data, this is fine, it just means the // remote side had nothing to tell us. Indicate this through a // ERROR_EMPTY response code so we can update the timestamp. if (bytesRead == 0) { SetLastError(ERROR_EMPTY); break; } headerBytes += bytesRead; if (headerBytes != sizeof(TlvHeader)) { continue; } else { inHeader = FALSE; } } if (GetLastError() == ERROR_EMPTY) break; if (headerBytes != sizeof(TlvHeader)) { SetLastError(ERROR_NOT_FOUND); break; } // Initialize the header header.length = header.length; header.type = header.type; payloadLength = ntohl(header.length) - sizeof(TlvHeader); payloadBytesLeft = payloadLength; // Allocate the payload if (!(payload = (PUCHAR)malloc(payloadLength))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; } // Read the payload retries = payloadBytesLeft; while (payloadBytesLeft > 0 && retries > 0 ) { retries--; if (! InternetReadFile(hReq, payload + payloadLength - payloadBytesLeft, payloadBytesLeft, &bytesRead)) { dprintf("[PACKET RECEIVE] Failed BODY InternetReadFile: %d", GetLastError()); SetLastError(ERROR_NOT_FOUND); break; } if (!bytesRead) { SetLastError(ERROR_NOT_FOUND); break; } payloadBytesLeft -= bytesRead; } // Didn't finish? if (payloadBytesLeft) break; // Allocate a packet structure if (!(localPacket = (Packet *)malloc(sizeof(Packet)))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; } memset( localPacket, 0, sizeof(Packet) ); // If the connection has an established cipher and this packet is not // plaintext, decrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = payloadLength; PUCHAR origPayload = payload; // Decrypt if ((res = crypto->handlers.decrypt(crypto, payload, payloadLength,&payload, &payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // We no longer need the encrypted payload free(origPayload); } localPacket->header.length = header.length; localPacket->header.type = header.type; localPacket->payload = payload; localPacket->payloadLength = payloadLength; *packet = localPacket; SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Cleanup on failure if (res != ERROR_SUCCESS) { if (payload) free(payload); if (localPacket) free(localPacket); } if (hReq) InternetCloseHandle(hReq); lock_release( remote->lock ); return res; }
/* * Transmit and destroy a packet over HTTP(S) */ DWORD packet_transmit_via_http(Remote *remote, Packet *packet, PacketRequestCompletion *completion) { CryptoContext *crypto; Tlv requestId; DWORD res; #ifdef _UNIX int local_error = -1; #endif lock_acquire( remote->lock ); // If the packet does not already have a request identifier, create one for it if (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID,&requestId) != ERROR_SUCCESS) { DWORD index; CHAR rid[32]; rid[sizeof(rid) - 1] = 0; for (index = 0; index < sizeof(rid) - 1; index++) rid[index] = (rand() % 0x5e) + 0x21; packet_add_tlv_string(packet, TLV_TYPE_REQUEST_ID, rid); } do { // If a completion routine was supplied and the packet has a request // identifier, insert the completion routine into the list if ((completion) && (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) == ERROR_SUCCESS)) packet_add_completion_handler((LPCSTR)requestId.buffer, completion); // If the endpoint has a cipher established and this is not a plaintext // packet, we encrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = packet->payloadLength; PUCHAR origPayload = packet->payload; // Encrypt if ((res = crypto->handlers.encrypt(crypto, packet->payload, packet->payloadLength, &packet->payload, &packet->payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // Destroy the original payload as we no longer need it free(origPayload); // Update the header length packet->header.length = htonl(packet->payloadLength + sizeof(TlvHeader)); } #ifdef _WIN32 dprintf("Transmitting packet of length %d to remote", packet->payloadLength); res = packet_transmit_via_http_wininet(remote, packet, completion); #else // XXX: Implement non-windows HTTP delivery #endif if(res < 0) { dprintf("[PACKET] transmit failed with return %d\n", res); break; } SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Destroy the packet packet_destroy(packet); lock_release( remote->lock ); return res; }