mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-05-12 19:04:32 +02:00

Exploitation of the System process on Windows XP SP0 resulted in crashes when metepreter was enumerating network interfaces. It appears that the System process isn't able to read from the address that contains prefixes (stored in pPrefix in interfaces.c). In other cases, such as exploitation of svchost.exe or via an msfpayload-generated exe, there was no such crash. This is not an issue on later versions of Windows. This crash happens in the current "production" version of Meterpreter that was deployed with MSF v4.7. The MS08-067 exploit targets the System process, and hence crashes. The MS03-26 exploit targets svchost, and hence does not crash. Checking the protection of the memory in each of those cases showed that the MEM_COMMIT was set when not the System process, and was MEM_RESERVED when it was. Sample runs can be seen here: http://pastebin.com/2WHqJ90A This commit is an attempt to avoid this crash down the track, and it just does a simple check to see if querying the area of memory for the current process shows a state of MEM_COMMIT. If so, it carries on as per normal, if not it uses the other legacy code path to traverse the network interfaces. With this "fix" in place I have not seen a crash at all on Windows XP, Windows 7, Windows 8 and Windows 2012.
397 lines
12 KiB
C
397 lines
12 KiB
C
#include "precomp.h"
|
|
|
|
#ifdef _WIN32
|
|
#include <iptypes.h>
|
|
#include <ws2ipdef.h>
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
DWORD get_interfaces_windows_mib(Remote *remote, Packet *response)
|
|
{
|
|
DWORD result = ERROR_SUCCESS;
|
|
DWORD tlv_cnt;
|
|
|
|
Tlv entries[6];
|
|
PMIB_IPADDRTABLE table = NULL;
|
|
DWORD tableSize = sizeof(MIB_IPADDRROW) * 33;
|
|
DWORD index;
|
|
DWORD mtu_bigendian;
|
|
DWORD interface_index_bigendian;
|
|
MIB_IFROW iface;
|
|
|
|
do
|
|
{
|
|
// Allocate memory for reading addresses into
|
|
if (!(table = (PMIB_IPADDRTABLE)malloc(tableSize)))
|
|
{
|
|
result = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
// Get the IP address table
|
|
if (GetIpAddrTable(table, &tableSize, TRUE) != NO_ERROR)
|
|
{
|
|
result = GetLastError();
|
|
break;
|
|
}
|
|
|
|
// Enumerate the entries
|
|
for (index = 0; index < table->dwNumEntries; index++)
|
|
{
|
|
tlv_cnt = 0;
|
|
|
|
interface_index_bigendian = htonl(table->table[index].dwIndex);
|
|
entries[tlv_cnt].header.length = sizeof(DWORD);
|
|
entries[tlv_cnt].header.type = TLV_TYPE_INTERFACE_INDEX;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&interface_index_bigendian;
|
|
tlv_cnt++;
|
|
|
|
entries[tlv_cnt].header.length = sizeof(DWORD);
|
|
entries[tlv_cnt].header.type = TLV_TYPE_IP;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&table->table[index].dwAddr;
|
|
tlv_cnt++;
|
|
|
|
entries[tlv_cnt].header.length = sizeof(DWORD);
|
|
entries[tlv_cnt].header.type = TLV_TYPE_NETMASK;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&table->table[index].dwMask;
|
|
tlv_cnt++;
|
|
|
|
iface.dwIndex = table->table[index].dwIndex;
|
|
|
|
// If interface information can get gotten, use it.
|
|
if (GetIfEntry(&iface) == NO_ERROR)
|
|
{
|
|
entries[tlv_cnt].header.length = iface.dwPhysAddrLen;
|
|
entries[tlv_cnt].header.type = TLV_TYPE_MAC_ADDR;
|
|
entries[tlv_cnt].buffer = (PUCHAR)iface.bPhysAddr;
|
|
tlv_cnt++;
|
|
|
|
mtu_bigendian = htonl(iface.dwMtu);
|
|
entries[tlv_cnt].header.length = sizeof(DWORD);
|
|
entries[tlv_cnt].header.type = TLV_TYPE_INTERFACE_MTU;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&mtu_bigendian;
|
|
tlv_cnt++;
|
|
|
|
if (iface.bDescr)
|
|
{
|
|
entries[tlv_cnt].header.length = iface.dwDescrLen + 1;
|
|
entries[tlv_cnt].header.type = TLV_TYPE_MAC_NAME;
|
|
entries[tlv_cnt].buffer = (PUCHAR)iface.bDescr;
|
|
tlv_cnt++;
|
|
}
|
|
}
|
|
|
|
// Add the interface group
|
|
packet_add_tlv_group(response, TLV_TYPE_NETWORK_INTERFACE,
|
|
entries, tlv_cnt);
|
|
}
|
|
|
|
} while (0);
|
|
|
|
if (table)
|
|
free(table);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
DWORD get_interfaces_windows(Remote *remote, Packet *response) {
|
|
DWORD result = ERROR_SUCCESS;
|
|
DWORD tlv_cnt;
|
|
// Most of the time we'll need:
|
|
// index, name (description), MAC addr, mtu, flags, IP addr, netmask, maybe scope id
|
|
// In some cases, the interface will have multiple addresses, so we'll realloc
|
|
// this when necessary, but this will cover the common case.
|
|
DWORD allocd_entries = 20;
|
|
Tlv *entries = (Tlv *)malloc(sizeof(Tlv) * 20);
|
|
int prefixes[30];
|
|
int prefixes_cnt = 0;
|
|
|
|
DWORD mtu_bigendian;
|
|
DWORD interface_index_bigendian;
|
|
|
|
ULONG flags = GAA_FLAG_INCLUDE_PREFIX
|
|
| GAA_FLAG_SKIP_DNS_SERVER
|
|
| GAA_FLAG_SKIP_MULTICAST
|
|
| GAA_FLAG_SKIP_ANYCAST;
|
|
|
|
LPSOCKADDR sockaddr;
|
|
|
|
ULONG family = AF_UNSPEC;
|
|
IP_ADAPTER_ADDRESSES *pAdapters = NULL;
|
|
IP_ADAPTER_ADDRESSES *pCurr = NULL;
|
|
ULONG outBufLen = 0;
|
|
DWORD (WINAPI *gaa)(DWORD, DWORD, void *, void *, void *);
|
|
|
|
// Use the newer version so we're guaranteed to have a large enough struct.
|
|
// Unfortunately, using these probably means it won't compile on older
|
|
// versions of Visual Studio. =(
|
|
IP_ADAPTER_UNICAST_ADDRESS_LH *pAddr = NULL;
|
|
IP_ADAPTER_UNICAST_ADDRESS_LH *pPref = NULL;
|
|
// IP_ADAPTER_PREFIX is only defined if NTDDI_VERSION > NTDDI_WINXP
|
|
// Since we request older versions of things, we have to be explicit
|
|
// when using newer structs.
|
|
IP_ADAPTER_PREFIX_XP *pPrefix = NULL;
|
|
|
|
MEMORY_BASIC_INFORMATION memInfo;
|
|
|
|
do
|
|
{
|
|
gaa = (DWORD (WINAPI *)(DWORD,DWORD,void*,void*,void*))GetProcAddress(
|
|
GetModuleHandle("iphlpapi"), "GetAdaptersAddresses"
|
|
);
|
|
if (!gaa) {
|
|
result = get_interfaces_windows_mib(remote, response);
|
|
break;
|
|
}
|
|
|
|
gaa(family, flags, NULL, pAdapters, &outBufLen);
|
|
if (!(pAdapters = (IP_ADAPTER_ADDRESSES *)malloc(outBufLen)))
|
|
{
|
|
result = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
if (gaa(family, flags, NULL, pAdapters, &outBufLen))
|
|
{
|
|
result = GetLastError();
|
|
break;
|
|
}
|
|
|
|
// Enumerate the entries
|
|
for (pCurr = pAdapters; pCurr; pCurr = pCurr->Next)
|
|
{
|
|
tlv_cnt = 0;
|
|
|
|
interface_index_bigendian = htonl(pCurr->IfIndex);
|
|
entries[tlv_cnt].header.length = sizeof(DWORD);
|
|
entries[tlv_cnt].header.type = TLV_TYPE_INTERFACE_INDEX;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&interface_index_bigendian;
|
|
tlv_cnt++;
|
|
|
|
entries[tlv_cnt].header.length = pCurr->PhysicalAddressLength;
|
|
entries[tlv_cnt].header.type = TLV_TYPE_MAC_ADDR;
|
|
entries[tlv_cnt].buffer = (PUCHAR)pCurr->PhysicalAddress;
|
|
tlv_cnt++;
|
|
|
|
entries[tlv_cnt].header.length = (DWORD)wcslen(pCurr->Description)*2 + 1;
|
|
entries[tlv_cnt].header.type = TLV_TYPE_MAC_NAME;
|
|
entries[tlv_cnt].buffer = (PUCHAR)pCurr->Description;
|
|
tlv_cnt++;
|
|
|
|
mtu_bigendian = htonl(pCurr->Mtu);
|
|
entries[tlv_cnt].header.length = sizeof(DWORD);
|
|
entries[tlv_cnt].header.type = TLV_TYPE_INTERFACE_MTU;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&mtu_bigendian;
|
|
tlv_cnt++;
|
|
|
|
if (pCurr->Length > 68) {
|
|
// Then this is a Longhorn struct version and it contains the
|
|
// FirstPrefix member, save it for later in case we don't have
|
|
// an OnLinkPrefixLength
|
|
pPrefix = pCurr->FirstPrefix;
|
|
}
|
|
|
|
for (pAddr = (void*)pCurr->FirstUnicastAddress; pAddr; pAddr = (void*)pAddr->Next)
|
|
{
|
|
sockaddr = pAddr->Address.lpSockaddr;
|
|
if (AF_INET != sockaddr->sa_family && AF_INET6 != sockaddr->sa_family) {
|
|
// Skip interfaces that aren't IP
|
|
continue;
|
|
}
|
|
|
|
// This loop can add up to three Tlv's - one for address, one
|
|
// for scope_id, one for netmask. Go ahead and allocate enough
|
|
// room for all of them.
|
|
if (allocd_entries < tlv_cnt+3) {
|
|
entries = realloc(entries, sizeof(Tlv) * (tlv_cnt+3));
|
|
allocd_entries += 3;
|
|
}
|
|
|
|
if (pAddr->Length > 44) {
|
|
// Then this is Vista+ and the OnLinkPrefixLength member
|
|
// will be populated
|
|
prefixes[prefixes_cnt] = htonl(pAddr->OnLinkPrefixLength);
|
|
}
|
|
|
|
// We'll only attempt to read the prefix information if the current process
|
|
// has a MEM_COMMIT state. Without this, XP SP0 (home) results in a crash
|
|
// in cases where exploitation of the Service process is underway
|
|
ZeroMemory(&memInfo, sizeof(memInfo));
|
|
VirtualQueryEx(GetCurrentProcess(), pPrefix, &memInfo, sizeof(memInfo));
|
|
|
|
if (pPrefix && 0 == prefixes[prefixes_cnt] && memInfo.State == MEM_COMMIT) {
|
|
// Otherwise, we have to walk the FirstPrefix linked list
|
|
prefixes[prefixes_cnt] = htonl(pPrefix->PrefixLength);
|
|
pPrefix = pPrefix->Next;
|
|
} else {
|
|
// This is XP SP0 and as far as I can tell, we have no way
|
|
// of determining the netmask short of bailing on
|
|
// this method and falling back to MIB, which doesn't
|
|
// return IPv6 addresses. Older versions (e.g. NT4, 2k)
|
|
// don't have GetAdapterAddresses, so they will have fallen
|
|
// through earlier to the MIB implementation.
|
|
free(entries);
|
|
free(pAdapters);
|
|
return get_interfaces_windows_mib(remote, response);
|
|
}
|
|
|
|
if (prefixes[prefixes_cnt]) {
|
|
entries[tlv_cnt].header.length = 4;
|
|
entries[tlv_cnt].header.type = TLV_TYPE_IP_PREFIX;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&prefixes[prefixes_cnt];
|
|
tlv_cnt++;
|
|
prefixes_cnt++;
|
|
}
|
|
|
|
if (sockaddr->sa_family == AF_INET) {
|
|
entries[tlv_cnt].header.length = 4;
|
|
entries[tlv_cnt].header.type = TLV_TYPE_IP;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&(((struct sockaddr_in *)sockaddr)->sin_addr);
|
|
tlv_cnt++;
|
|
|
|
} else {
|
|
entries[tlv_cnt].header.length = 16;
|
|
entries[tlv_cnt].header.type = TLV_TYPE_IP;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&(((struct sockaddr_in6 *)sockaddr)->sin6_addr);
|
|
tlv_cnt++;
|
|
|
|
entries[tlv_cnt].header.length = sizeof(DWORD);
|
|
entries[tlv_cnt].header.type = TLV_TYPE_IP6_SCOPE;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&(((struct sockaddr_in6 *)sockaddr)->sin6_scope_id);
|
|
tlv_cnt++;
|
|
}
|
|
|
|
}
|
|
// Add the interface group
|
|
packet_add_tlv_group(response, TLV_TYPE_NETWORK_INTERFACE,
|
|
entries, tlv_cnt);
|
|
}
|
|
} while (0);
|
|
|
|
if (entries)
|
|
free(entries);
|
|
if (pAdapters)
|
|
free(pAdapters);
|
|
|
|
return result;
|
|
}
|
|
|
|
#else /* _WIN32 */
|
|
int get_interfaces_linux(Remote *remote, Packet *response) {
|
|
struct ifaces_list *ifaces = NULL;
|
|
int i;
|
|
int result;
|
|
uint32_t interface_index_bigendian, mtu_bigendian;
|
|
DWORD allocd_entries = 10;
|
|
Tlv *entries = (Tlv *)malloc(sizeof(Tlv) * 10);
|
|
|
|
dprintf("Grabbing interfaces");
|
|
result = netlink_get_interfaces(&ifaces);
|
|
dprintf("Got 'em");
|
|
|
|
if (!result) {
|
|
for (i = 0; i < ifaces->entries; i++) {
|
|
int tlv_cnt = 0;
|
|
int j = 0;
|
|
dprintf("Building TLV for iface %d", i);
|
|
|
|
entries[tlv_cnt].header.length = strlen(ifaces->ifaces[i].name)+1;
|
|
entries[tlv_cnt].header.type = TLV_TYPE_MAC_NAME;
|
|
entries[tlv_cnt].buffer = (PUCHAR)ifaces->ifaces[i].name;
|
|
tlv_cnt++;
|
|
|
|
entries[tlv_cnt].header.length = 6;
|
|
entries[tlv_cnt].header.type = TLV_TYPE_MAC_ADDR;
|
|
entries[tlv_cnt].buffer = (PUCHAR)ifaces->ifaces[i].hwaddr;
|
|
tlv_cnt++;
|
|
|
|
mtu_bigendian = htonl(ifaces->ifaces[i].mtu);
|
|
entries[tlv_cnt].header.length = sizeof(uint32_t);
|
|
entries[tlv_cnt].header.type = TLV_TYPE_INTERFACE_MTU;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&mtu_bigendian;
|
|
tlv_cnt++;
|
|
|
|
entries[tlv_cnt].header.length = strlen(ifaces->ifaces[i].flags)+1;
|
|
entries[tlv_cnt].header.type = TLV_TYPE_INTERFACE_FLAGS;
|
|
entries[tlv_cnt].buffer = (PUCHAR)ifaces->ifaces[i].flags;
|
|
tlv_cnt++;
|
|
|
|
interface_index_bigendian = htonl(ifaces->ifaces[i].index);
|
|
entries[tlv_cnt].header.length = sizeof(uint32_t);
|
|
entries[tlv_cnt].header.type = TLV_TYPE_INTERFACE_INDEX;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&interface_index_bigendian;
|
|
tlv_cnt++;
|
|
|
|
for (j = 0; j < ifaces->ifaces[i].addr_count; j++) {
|
|
if (allocd_entries < tlv_cnt+2) {
|
|
entries = realloc(entries, sizeof(Tlv) * (tlv_cnt+2));
|
|
allocd_entries += 2;
|
|
}
|
|
if (ifaces->ifaces[i].addr_list[j].family == AF_INET) {
|
|
dprintf("ip addr for %s", ifaces->ifaces[i].name);
|
|
entries[tlv_cnt].header.length = sizeof(__u32);
|
|
entries[tlv_cnt].header.type = TLV_TYPE_IP;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&ifaces->ifaces[i].addr_list[j].ip.addr;
|
|
tlv_cnt++;
|
|
|
|
//dprintf("netmask for %s", ifaces->ifaces[i].name);
|
|
entries[tlv_cnt].header.length = sizeof(__u32);
|
|
entries[tlv_cnt].header.type = TLV_TYPE_NETMASK;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&ifaces->ifaces[i].addr_list[j].nm.netmask;
|
|
tlv_cnt++;
|
|
} else {
|
|
dprintf("-- ip six addr for %s", ifaces->ifaces[i].name);
|
|
entries[tlv_cnt].header.length = sizeof(__u128);
|
|
entries[tlv_cnt].header.type = TLV_TYPE_IP;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&ifaces->ifaces[i].addr_list[j].ip.addr6;
|
|
tlv_cnt++;
|
|
|
|
//dprintf("netmask6 for %s", ifaces->ifaces[i].name);
|
|
entries[tlv_cnt].header.length = sizeof(__u128);
|
|
entries[tlv_cnt].header.type = TLV_TYPE_NETMASK;
|
|
entries[tlv_cnt].buffer = (PUCHAR)&ifaces->ifaces[i].addr_list[j].nm.netmask6;
|
|
tlv_cnt++;
|
|
}
|
|
}
|
|
|
|
dprintf("Adding TLV to group");
|
|
packet_add_tlv_group(response, TLV_TYPE_NETWORK_INTERFACE, entries, tlv_cnt);
|
|
dprintf("done Adding TLV to group");
|
|
}
|
|
}
|
|
|
|
if (ifaces)
|
|
free(ifaces);
|
|
if (entries)
|
|
free(entries);
|
|
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Returns zero or more local interfaces to the requestor
|
|
*/
|
|
DWORD request_net_config_get_interfaces(Remote *remote, Packet *packet)
|
|
{
|
|
Packet *response = packet_create_response(packet);
|
|
DWORD result = ERROR_SUCCESS;
|
|
|
|
#ifdef _WIN32
|
|
result = get_interfaces_windows(remote, response);
|
|
#else
|
|
result = get_interfaces_linux(remote, response);
|
|
#endif
|
|
|
|
// Transmit the response if valid
|
|
packet_transmit_response(result, remote, response);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|