Skip to content

Instantly share code, notes, and snippets.

@dirkjanm
Created April 13, 2018 10:08
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dirkjanm/a2087c27888a15410b4622009c7f2d41 to your computer and use it in GitHub Desktop.
Save dirkjanm/a2087c27888a15410b4622009c7f2d41 to your computer and use it in GitHub Desktop.
Test Security Descriptor encoding/decoding in impacket
#!/usr/bin/env python
####################
#
# Copyright (c) 2018 Dirk-jan Mollema - Fox-IT
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Test nTSecurityDescriptor encoding/decoding for impacket
#
# Uses impacket and ldap3
# Install impacket from git, and ldap3 via pip
#
####################
import sys
import argparse
import ldapdomaindump
import impacket
import getpass
import binascii
from impacket.ldap.ldaptypes import ACE, ACCESS_ALLOWED_OBJECT_ACE, ACCESS_MASK, LDAP_SID, SR_SECURITY_DESCRIPTOR
from struct import unpack, pack
from ldap3 import NTLM, Server, Connection, ALL, LEVEL, MODIFY_REPLACE
from ldap3.core.exceptions import LDAPProtocolErrorResult
from pyasn1.type.namedtype import NamedTypes, NamedType
from pyasn1.type.univ import Sequence, OctetString, Integer
from ldap3.protocol.controls import build_control
from impacket.uuid import string_to_bin
class SdFlags(Sequence):
# SDFlagsRequestValue ::= SEQUENCE {
# Flags INTEGER
# }
componentType = NamedTypes(NamedType('Flags', Integer())
)
def get_sd_controls(sdflags=0x04):
sdcontrol = SdFlags()
sdcontrol.setComponentByName('Flags', sdflags)
controls = [build_control('1.2.840.113556.1.4.801', True, sdcontrol)]
return controls
def create_object_ace(privguid, sid):
nace = ACE()
nace['AceType'] = ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE
nace['AceFlags'] = 0x00
acedata = ACCESS_ALLOWED_OBJECT_ACE()
acedata['Mask'] = ACCESS_MASK()
acedata['Mask']['Mask'] = ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_CONTROL_ACCESS
acedata['ObjectType'] = string_to_bin(privguid)
acedata['InheritedObjectType'] = ''
acedata['Sid'] = LDAP_SID()
acedata['Sid'].fromCanonical(sid)
acedata['Flags'] = ACCESS_ALLOWED_OBJECT_ACE.ACE_OBJECT_TYPE_PRESENT
nace['Ace'] = acedata
return nace
def print_m(string):
sys.stderr.write('\033[94m[-]\033[0m %s\n' % (string))
def print_o(string):
sys.stderr.write('\033[92m[+]\033[0m %s\n' % (string))
def print_f(string):
sys.stderr.write('\033[91m[!]\033[0m %s\n' % (string))
def main():
parser = argparse.ArgumentParser(description='Test nTSecurityDescriptor encoding/decoding for impacket')
parser._optionals.title = "Main options"
parser._positionals.title = "Required options"
#Main parameters
#maingroup = parser.add_argument_group("Main options")
parser.add_argument("host", type=str,metavar='HOSTNAME',help="Hostname/ip or ldap://host:port connection string to connect to")
parser.add_argument("-u","--user",type=str,metavar='USERNAME',help="DOMAIN\username for authentication, leave empty for anonymous authentication")
parser.add_argument("-p","--password",type=str,metavar='PASSWORD',help="Password or LM:NTLM hash, will prompt if not specified")
args = parser.parse_args()
#Prompt for password if not set
authentication = None
if args.user is not None:
authentication = NTLM
if not '\\' in args.user:
print_f('Username must include a domain, use: DOMAIN\username')
sys.exit(1)
if args.password is None:
args.password = getpass.getpass()
# define the server and the connection
s = Server(args.host, get_info=ALL)
print_m('Connecting to host...')
c = Connection(s, user=args.user, password=args.password, authentication=authentication)
print_m('Binding to host')
# perform the Bind operation
if not c.bind():
print_f('Could not bind with specified credentials')
print_f(c.result)
sys.exit(1)
rootdn = s.info.other['rootDomainNamingContext'][0]
print_o('Bind OK')
perms = lookup_permissions(c, rootdn)
controls = get_sd_controls()
entries = c.extend.standard.paged_search(rootdn, '(objectClass=*)', attributes=['nTSecurityDescriptor'], controls=controls, generator=True)
print_m('Will now enumerate all domain objects. This may take a while depending on the domain size.')
impacket.ldap.ldaptypes.RECALC_ACE_SIZE = False
c_all = 0
c_ok = 0
c_fail = 0
c_no_access = 0
while True:
try:
e = entries.next()
except LDAPProtocolErrorResult:
pass
except StopIteration:
break
if e['type'] != 'searchResEntry':
continue
dn = e['dn']
c_all += 1
if c_all % 100 == 0:
print_m('Processed %d objects' % c_all)
try:
sd = e['raw_attributes']['nTSecurityDescriptor'][0]
except IndexError:
c_no_access += 1
continue
if len(controls) > 1:
controls.pop()
a = SR_SECURITY_DESCRIPTOR()
a.fromString(sd)
b = a.getData()
if b == sd:
c_ok += 1
else:
c_fail += 1
print 'Failed: %d' % c_fail
print 'Ok: %d' % c_ok
print 'No access (or empty SD): %d' % c_no_access
if c_fail == 0:
print_o('No objects failed to encode/decode, yay!')
else:
print_f('Whoops, not all objects were encoded/decoded succesfully')
def lookup_permissions(ldapconnection, rootdn):
ldapconnection.extend.standard.paged_search('CN=Extended-Rights,CN=Configuration,%s' % rootdn, '(rightsGuid=*)', search_scope=LEVEL, attributes=['displayName','rightsGuid'], paged_size=500, generator=False)
ep = {}
for e in ldapconnection.entries:
ep[e['rightsGuid'].values[0].lower()] = e['displayName'].values[0]
ldapconnection.extend.standard.paged_search('CN=Schema,CN=Configuration,%s' % rootdn, '(schemaIdGuid=*)', search_scope=LEVEL, attributes=['name','schemaIdGuid'], paged_size=500, generator=False)
for e in ldapconnection.entries:
ep[e['schemaIdGuid'].values[0].lower()] = e['name'].values[0]
return ep
if __name__ == '__main__':
main()
@aganguly01
Copy link

Unable to decode this kind of ntsecuritydescriptor
b'0100049c000000000000000000000000140000000400d4000500000005003800300100000100000068c9100efb78d21190d400c04f79dc550105000000000005150000009328446371b3986185a90c5c0002000005003800300100000100000068c9100efb78d21190d400c04f79dc550105000000000005150000009328446371b3986185a90c5c0702000000002400ff000f000105000000000005150000009328446371b3986185a90c5c0002000000002400ff000f000105000000000005150000009328446371b3986185a90c5c07020000000014009400020001010000000000050b00' on python3

on sd.fromString(secDesc)

Getting exception as

struct.error: ('unpack requires a buffer of 1 bytes', "When unpacking field 'Revision | <B | b''[:1]'")
Same data works perfectly when I use C# RawSecurityDescriptor. Am I doing something silly ?

@iHaagcom
Copy link

Unable to decode this kind of ntsecuritydescriptor b'0100049c000000000000000000000000140000000400d4000500000005003800300100000100000068c9100efb78d21190d400c04f79dc550105000000000005150000009328446371b3986185a90c5c0002000005003800300100000100000068c9100efb78d21190d400c04f79dc550105000000000005150000009328446371b3986185a90c5c0702000000002400ff000f000105000000000005150000009328446371b3986185a90c5c0002000000002400ff000f000105000000000005150000009328446371b3986185a90c5c07020000000014009400020001010000000000050b00' on python3

on sd.fromString(secDesc)

Getting exception as

struct.error: ('unpack requires a buffer of 1 bytes', "When unpacking field 'Revision | <B | b''[:1]'") Same data works perfectly when I use C# RawSecurityDescriptor. Am I doing something silly ?

Did you solve this im having a similar issue

@aganguly01
Copy link

if secDesc[0:2] == '0x':
secDesc = secDesc[2:]
sd.fromString(binascii.unhexlify(secDesc)) #worked for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment