1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-01-08 14:36:22 +01:00

Update ADSI code to support more types

When non-supported types were returned in queries, ADSI just pooped
itself and tore down the Meterpreter session. This a happy tester
Meterpreter does not make!

This code includes more support for ADSI types with attempts to be
semi-sane in stringifying them.

Plus, Meterpreter no longer crashes.
This commit is contained in:
OJ 2014-01-22 10:09:42 +10:00
parent 5b35852e32
commit 3cde9b69c2

View File

@ -13,12 +13,60 @@ extern "C" {
#pragma comment(lib, "Activeds.lib")
#define VALUE_SIZE 512
#define VALUE_SIZE 1024
#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 } };
/*!
* @brief Render a byte array as a GUID string.
* @param bytes Pointer to the GUID bytes.
* @param buffer Pointer to the target buffer.
* @param bufferSize size of the memory available in \c buffer.
* @remark Assumes the caller knows what they're doing, and assumes that there are 16 bytes in the array.
*/
void guid_to_string(LPBYTE bytes, char* buffer, DWORD bufferSize)
{
sprintf_s(buffer, bufferSize, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
bytes[0], bytes[1], bytes[2], bytes[3],
bytes[4], bytes[5], bytes[6], bytes[7],
bytes[8], bytes[9], bytes[10], bytes[11],
bytes[12], bytes[13], bytes[14], bytes[15]);
}
/*!
* @brief Render a byte array as a string.
* @param bytes Pointer to the bytes.
* @param count Number of bytes to write.
* @param buffer Pointer to the target buffer.
* @param bufferSize size of the memory available in \c buffer.
* @param byteFormat optional per-byte format (defaults to \c %02x).
* @param delim optional delimiter to render between bytes (defaults to \c -).
* @remark Assumes the caller knows what they're doing, and assumes that there are 16 bytes in the array.
*/
char* bytes_to_string(LPBYTE bytes, DWORD count, char* buffer, DWORD bufferSize, char* byteFormat = "%02x", char* delim = "-")
{
DWORD sizeLeft = bufferSize;
for (DWORD i = 0; i < count && sizeLeft > 0; ++i)
{
int printed = 0;
if (i != 0)
{
printed = sprintf_s(buffer, sizeLeft, "%s", delim);
sizeLeft -= printed;
buffer += printed;
}
printed = sprintf_s(buffer, sizeLeft, byteFormat, bytes[i]);
sizeLeft -= printed;
buffer += printed;
}
return buffer;
}
/*!
* @brief Perform a domain query via ADSI.
* @param lpwDomain Name of the domain that is to be queried.
@ -117,29 +165,195 @@ DWORD domain_query(LPCWSTR lpwDomain, LPWSTR lpwFilter, LPWSTR* lpwQueryCols,
HRESULT hr = pDirSearch->GetColumn(hSearch, lpwQueryCols[dwIndex], &col);
if (SUCCEEDED(hr))
{
WCHAR* source = NULL;
switch (col.dwADsType)
{
case ADSTYPE_LARGE_INTEGER:
_i64toa_s(col.pADsValues->LargeInteger.QuadPart, 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:
_itoa_s((UINT)col.pADsValues->Integer, valueTarget, VALUE_SIZE, 10);
entries[dwIndex].header.length = lstrlenA(valueTarget) + 1;
dprintf("[ADSI] Adding int value %u", (UINT)col.pADsValues->Integer);
break;
default:
WCHAR* source = col.dwADsType == ADSTYPE_CASE_IGNORE_STRING
? col.pADsValues->CaseIgnoreString
: col.pADsValues->CaseExactString;
case ADSTYPE_LARGE_INTEGER:
{
_i64toa_s(col.pADsValues->LargeInteger.QuadPart, valueTarget, VALUE_SIZE, 10);
dprintf("[ADSI] Adding large int value %ul", (UINT)col.pADsValues->Integer);
break;
}
case ADSTYPE_INTEGER:
{
_itoa_s((UINT)col.pADsValues->Integer, valueTarget, VALUE_SIZE, 10);
dprintf("[ADSI] Adding int value %u", (UINT)col.pADsValues->Integer);
break;
}
case ADSTYPE_DN_STRING:
{
source = col.pADsValues->DNString;
break;
}
case ADSTYPE_PRINTABLE_STRING:
{
source = col.pADsValues->PrintableString;
break;
}
case ADSTYPE_NUMERIC_STRING:
{
source = col.pADsValues->NumericString;
break;
}
case ADSTYPE_CASE_EXACT_STRING:
{
source = col.pADsValues->CaseExactString;
break;
}
case ADSTYPE_CASE_IGNORE_STRING:
{
source = col.pADsValues->CaseIgnoreString;
break;
}
case ADSTYPE_BOOLEAN:
{
source = col.pADsValues->Boolean == 0 ? L"False" : L"True";
break;
}
case ADSTYPE_OCTET_STRING:
{
// We're going to assume that anything that's 16 bytes it's a GUID. This might not be legit, but for the most
// part this is going to be true.
if (col.pADsValues->OctetString.dwLength == 16)
{
guid_to_string(col.pADsValues->OctetString.lpValue, valueTarget, VALUE_SIZE);
}
else
{
bytes_to_string(col.pADsValues->OctetString.lpValue, col.pADsValues->OctetString.dwLength, valueTarget, VALUE_SIZE);
}
break;
}
case ADSTYPE_UTC_TIME:
{
SYSTEMTIME* pt = &col.pADsValues->UTCTime;
sprintf_s(valueTarget, VALUE_SIZE, "%4u-%02u-%02u %02u:%02u:%02u.%03u",
pt->wYear, pt->wMonth, pt->wDay, pt->wHour, pt->wMinute, pt->wSecond, pt->wMilliseconds);
break;
}
case ADSTYPE_PROV_SPECIFIC:
{
bytes_to_string(col.pADsValues->ProviderSpecific.lpValue, col.pADsValues->ProviderSpecific.dwLength, valueTarget, VALUE_SIZE);
break;
}
case ADSTYPE_OBJECT_CLASS:
{
source = col.pADsValues->ClassName;
break;
}
case ADSTYPE_CASEIGNORE_LIST:
{
// list of strings, yay!
char* prefix = "";
PADS_CASEIGNORE_LIST list = col.pADsValues->pCaseIgnoreList;
char* csr = valueTarget;
size_t sizeLeft = VALUE_SIZE;
wcstombs_s(&charsConverted, valueTarget, VALUE_SIZE, source, VALUE_SIZE - 1);
dprintf("[ADSI] Adding string %s", valueTarget);
entries[dwIndex].header.length = lstrlenA(valueTarget) + 1;
break;
while (list != NULL && sizeLeft > 0)
{
int printed = sprintf_s(csr, sizeLeft, "%s%S", prefix, list->String);
sizeLeft -= printed;
csr += printed;
prefix = ", ";
}
break;
}
case ADSTYPE_PATH:
{
PADS_PATH path = col.pADsValues->pPath;
sprintf_s(valueTarget, VALUE_SIZE, "Vol: %S, Path: %S, Type: %u", path->VolumeName, path->Path, path->Type);
break;
}
case ADSTYPE_POSTALADDRESS:
{
PADS_POSTALADDRESS addr = col.pADsValues->pPostalAddress;
char* prefix = "";
PADS_CASEIGNORE_LIST list = col.pADsValues->pCaseIgnoreList;
char* csr = valueTarget;
size_t sizeLeft = VALUE_SIZE;
for (DWORD i = 0; i < sizeof(addr->PostalAddress) / sizeof(addr->PostalAddress[0]) && sizeLeft > 0; ++i)
{
if (!addr->PostalAddress[i] || lstrlenW(addr->PostalAddress[i]) == 0)
{
continue;
}
int printed = sprintf_s(csr, sizeLeft, "%s%S", prefix, addr->PostalAddress[i]);
sizeLeft -= printed;
csr += printed;
prefix = ", ";
}
break;
}
case ADSTYPE_TIMESTAMP:
{
ADS_TIMESTAMP* pts = &col.pADsValues->Timestamp;
sprintf_s(valueTarget, VALUE_SIZE, "Event: %S, Sec: %u", pts->EventID, pts->WholeSeconds);
break;
}
case ADSTYPE_BACKLINK:
{
ADS_BACKLINK* pbl = &col.pADsValues->BackLink;
sprintf_s(valueTarget, VALUE_SIZE, "Name: %S, ID: %u", pbl->ObjectName, pbl->RemoteID);
break;
}
case ADSTYPE_TYPEDNAME:
{
PADS_TYPEDNAME ptn = col.pADsValues->pTypedName;
sprintf_s(valueTarget, VALUE_SIZE, "Interval: %u, Level: %u, Name: %S", ptn->Interval, ptn->Level, ptn->ObjectName);
break;
}
case ADSTYPE_NETADDRESS:
{
PADS_NETADDRESS pna = col.pADsValues->pNetAddress;
bytes_to_string(pna->Address, pna->AddressLength, valueTarget, VALUE_SIZE, "%u", ".");
break;
}
case ADSTYPE_EMAIL:
{
source = col.pADsValues->Email.Address;
break;
}
case ADSTYPE_NT_SECURITY_DESCRIPTOR:
{
ADS_NT_SECURITY_DESCRIPTOR* psd = &col.pADsValues->SecurityDescriptor;
bytes_to_string(psd->lpValue, psd->dwLength, valueTarget, VALUE_SIZE);
break;
}
case ADSTYPE_DN_WITH_BINARY:
{
PADS_DN_WITH_BINARY pdb = col.pADsValues->pDNWithBinary;
sprintf_s(valueTarget, VALUE_SIZE, "DN: %S, Value: %S", pdb->pszDNString);
size_t charsPrinted = lstrlenA(valueTarget);
bytes_to_string(pdb->lpBinaryValue, pdb->dwLength, valueTarget + charsPrinted, VALUE_SIZE - charsPrinted, "%u", ".");
break;
}
case ADSTYPE_DN_WITH_STRING:
{
PADS_DN_WITH_STRING pds = col.pADsValues->pDNWithString;
sprintf_s(valueTarget, VALUE_SIZE, "DN: %S, Value: %S", pds->pszDNString, pds->pszStringValue);
break;
}
case ADSTYPE_FAXNUMBER:
case ADSTYPE_REPLICAPOINTER:
default:
{
// this is a string of some kind
dprintf("[ADSI] Unhandled ADSI type %u (%x), adding unknown", col.dwADsType, col.dwADsType);
sprintf_s(valueTarget, VALUE_SIZE, "(unhandled ADSI type %u)", col.dwADsType);
break;
}
}
// if source is now non-null it means a value was added to it,
// so we can just do a conversion directly into the value target
if (source != NULL)
{
wcstombs_s(&charsConverted, valueTarget, VALUE_SIZE, source, VALUE_SIZE - 1);
}
entries[dwIndex].header.length = lstrlenA(valueTarget) + 1;
pDirSearch->FreeColumn(&col);
}
else