1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-03-12 12:14:29 +01:00

Hack to ignore metsrv.dll stage when connecting to staged listener

The last issue we had in removing the OpenSSL library from Windows
meterp is making it so that reconnects would behave. With a staged
listener, the first thing that gets sent down the wire is metsrv.dll. As
a result, when a fully staged connect comes in (whether it be from
a stageless payload, from a transport switch or from a sleeping session
waking up), Meterpreter needs to handle the case that the data coming
down the wire is no actually a TLV packet, and hence ignore it.

This "hack" abuses the properties of the XOR key for the packet,
relying on the fact that the XOR key will never contain NULl bytes and
that the first 4 bytes from a staged listener starts with the length of
the metsrv DLL, which is small enough to result in a NULL byte in the
MSB position.

If we see a NULL byte in that position, we assume it's the metsrv header
coming in, and we just ignore it and move on. If the XOR key looks
legit, we assume it's a valid TLV packet.

Dirty, but it's quick and it works!
This commit is contained in:
OJ 2017-06-16 13:34:46 +10:00
parent 3554aff9de
commit 9e3aef62bc
No known key found for this signature in database
GPG Key ID: D5DC61FB93260597

View File

@ -394,92 +394,138 @@ static DWORD packet_receive(Remote *remote, Packet **packet)
break;
}
header.xor_key = ntohl(header.xor_key);
dprintf("[TCP] the XOR key is: %08x", header.xor_key);
// xor the header data
xor_bytes(header.xor_key, (LPBYTE)&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)))
// At this point, we might have read in a valid TLV packet, or we might have read in the first chunk of data
// from a staged listener after a reconnect. We can figure this out rather lazily by assuming the following:
// XOR keys are always 4 bytes that are non-zero. If the higher order byte of the xor key is zero, then it
// isn't an XOR Key, instead it's the 4-byte length of the metsrv binary (because metsrv isn't THAT big).
if ((header.xor_key >> 24) == 0)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
break;
}
// looks like we have a metsrv instance, time to ignore it.
int length = (int)header.xor_key;
dprintf("[TCP] discovered a length header, assuming it's metsrv of length %d", length);
// Read the payload
while (payloadBytesLeft > 0)
{
if ((bytesRead = recv(ctx->fd, payload + payloadLength - payloadBytesLeft, payloadBytesLeft, 0)) <= 0)
int bytesToRead = length - sizeof(PacketHeader) + sizeof(DWORD);
char buffer[65535];
while (bytesToRead > 0)
{
if (GetLastError() == WSAEWOULDBLOCK)
{
continue;
}
int bytesRead = recv(ctx->fd, buffer, min(sizeof(buffer), bytesToRead), 0);
if (bytesRead < 0)
{
if (GetLastError() == WSAEWOULDBLOCK)
{
continue;
}
SetLastError(ERROR_NOT_FOUND);
break;
}
break;
bytesToRead -= bytesRead;
}
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)
// did something go wrong.
if (bytesToRead > 0)
{
SetLastError(res);
break;
}
// We no longer need the encrypted payload
free(origPayload);
// indicate success, but don't return a packet for processing
SetLastError(ERROR_SUCCESS);
*packet = NULL;
}
else
{
dprintf("[TCP] XOR key looks fine, moving on");
header.xor_key = ntohl(header.xor_key);
localPacket->header.length = header.length;
localPacket->header.type = header.type;
localPacket->payload = payload;
localPacket->payloadLength = payloadLength;
// xor the header data
xor_bytes(header.xor_key, (LPBYTE)&header.length, 8);
*packet = localPacket;
// Initialize the header
header.length = ntohl(header.length);
SetLastError(ERROR_SUCCESS);
// 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 = recv(ctx->fd, payload + payloadLength - payloadBytesLeft, payloadBytesLeft, 0)) <= 0)
{
if (GetLastError() == WSAEWOULDBLOCK)
{
continue;
}
if (bytesRead < 0)
{
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);
@ -538,8 +584,15 @@ static DWORD server_dispatch_tcp(Remote* remote, THREAD* dispatchThread)
break;
}
running = command_handle(remote, packet);
dprintf("[DISPATCH] command_process result: %s", (running ? "continue" : "stop"));
if (packet == NULL)
{
dprintf("[DISPATCH] No packet received, probably just metsrv being ignored");
}
else
{
running = command_handle(remote, packet);
dprintf("[DISPATCH] command_process result: %s", (running ? "continue" : "stop"));
}
// packet received, reset the timer
lastPacket = current_unix_timestamp();