def Main(): cgiEnv = lib_common.CgiEnv(can_process_remote=True) server = cgiEnv.m_entity_id_dict["Domain"] groupName = cgiEnv.m_entity_id_dict["Name"] grph = cgiEnv.GetGraph() # http://www.math.uiuc.edu/~gfrancis/illimath/windows/aszgard_mini/movpy-2.0.0-py2.4.4/movpy/lib/win32/Demos/win32netdemo.py # hostname = "Titi" for example try: lib_win32.WNetAddConnect(server) except: exc = sys.exc_info()[1] lib_common.ErrorMessageHtml("Server=%s Caught:%s" % (server, str(exc))) if not server or lib_util.IsLocalAddress(server): servName_or_None = None # So it is compatible with WMI. servNameNotNone = lib_uris.TruncateHostname(lib_util.currentHostname) # .home serverNode = lib_common.nodeMachine serverBox = lib_common.gUriGen else: servName_or_None = server servNameNotNone = server serverNode = lib_common.gUriGen.HostnameUri(server) serverBox = lib_common.RemoteBox(server) # nodeGroup = serverBox.GroupUri( groupName ) # nodeGroup = survol_Win32_Group.MakeUri( groupName, servName_or_None ) nodeGroup = survol_Win32_Group.MakeUri(groupName, servNameNotNone) try: memberresume = 0 while True: memberData, total, memberResume = win32net.NetLocalGroupGetMembers( servName_or_None, groupName, 2, memberresume) for member in memberData: sidUsage = member['sidusage'] # Converts Sid to username try: memberName, domain, type = win32security.LookupAccountSid( server, member['sid']) except Exception: exc = sys.exc_info()[1] sys.stderr.write("Server=%s Caught:%s\n" % (server, str(exc))) continue sys.stderr.write(" Member: %s:\n" % (str(member))) sys.stderr.write(" Lookup: %s: %s\n" % (memberName, member['domainandname'])) # nodeUser = serverBox.UserUri( userName ) sys.stderr.write("servNameNotNone=%s\n" % servNameNotNone) memberNode = MemberNameToNode(sidUsage, memberName, servNameNotNone) grph.add((memberNode, pc.property_group, nodeGroup)) grph.add((memberNode, lib_common.MakeProp("SID Usage"), lib_common.NodeLiteral(SidUsageToString(sidUsage)))) grph.add( (memberNode, lib_common.MakeProp("Security Identifier"), lib_common.NodeLiteral(member['sid']))) if servName_or_None: nodeMemberRemote = MemberNameToNodeRemote( sidUsage, memberName, servName_or_None, serverBox) # TODO: Instead, both object must have the same universal alias grph.add((memberNode, pc.property_alias, nodeMemberRemote)) if memberResume == 0: break except Exception: exc = sys.exc_info()[1] lib_common.ErrorMessageHtml("win32 local groups:" + str(exc)) cgiEnv.OutCgiRdf("LAYOUT_SPLINE")
def sid2username(sid): """Convert an object sid to a string account name""" account = win32security.LookupAccountSid(None, sid) return account[0]
def get_acl_list_procmon(self, path_dict): try: ''' path_dict = { "proc_name" : proc_name, "orig_cmd" : orig_cmd, "clean_cmd" : clean_cmd, "operation" : operation, "integrity" : integrity } ''' path_dict = dict(path_dict) f_path = path_dict["clean_cmd"] # Working with weird WIndows/Procmon Output... if "|" in f_path: f_path = f_path.split("|")[0] if ("hklm" in f_path.lower() and "c:" in f_path.lower()): f_path = "C:/" + f_path.lower().split("c:")[1] acls = "" gfso = win32security.GetFileSecurity( f_path, win32security.DACL_SECURITY_INFORMATION) dacl = gfso.GetSecurityDescriptorDacl() for n_ace in range(dacl.GetAceCount()): ace = dacl.GetAce(n_ace) (ace_type, ace_flags) = ace[0] mask = 0 # Reset the bitmask for each interation domain = "" # Reset the domain for each interation name = "" # Reset the name for each interation ascii_mask = "" # Reset the ascii permission value for each interation if ace_type in self.__CONVENTIONAL_ACES: mask, sid = ace[1:] else: mask, object_type, inherited_object_type, sid = ace[1:] name, domain, type = win32security.LookupAccountSid(None, sid) # Enumerate windows_security_enums for enum_obj in windows_objects.windows_security_enums: if ctypes.c_uint32(mask).value == enum_obj.value.value: access = self.__CONVENTIONAL_ACES.get( ace_type, "OTHER") ascii_mask = enum_obj.name acls += f"{domain}\\{name} {access} {ascii_mask}\n" # Enumerate nt_security_permissions for enum_obj in windows_objects.nt_security_enums: if ctypes.c_uint32(mask).value == enum_obj.value.value: access = self.__CONVENTIONAL_ACES.get( ace_type, "OTHER") ascii_mask = enum_obj.name acls += f"{domain}\\{name} {access} {ascii_mask}\n" data = f""" Process_Name: {path_dict["proc_name"]} Integrity: {path_dict["integrity"]} Operation: {path_dict["operation"]} Original_Cmd: {path_dict["orig_cmd"]} Path: {f_path} Access: {acls} """ self.__write_acl(data) f_path = "" except Exception as e: error = str(e).lower() if ("find the path specified" in error or "find the file specified" in error or "access is denied" in error or "ace type 9" in error or "nonetype" in error): data = f"\nPath: {f_path}\n{str(e)}\n" self.__write_acl(data) pass elif "no mapping" in error: note = """Possibly VULNERABLE: No mapping between account names and SID's Account used to set GPO may have been removed Account name may be typed incorrectly INFO: https://www.rebeladmin.com/2016/01/how-to-fix-error-no-mapping-between-account-names-and-security-ids-in-active-directory/""" data = f"\nPath: {f_path}\n{note}\n" self.__write_acl(data) pass else: self.__write_error(f_path) self.__print_exception() exit(0)
def GetAccountById(sid, domain = None): return win32security.LookupAccountSid(domain, sid)
def sddl_dacl_parse(self, sddl_string): try: # We already know we have obtained the DACL SDDL string via win32security. Therefore # we do not have to enumerate the SDDL type even though we prove such data in the # windows_objects.SDDL() class. all_permissions = {} sddl_permissions = re.findall(self.re_perms, sddl_string) for dacl in sddl_permissions: # There are some odd dacl windows-isms here that I need to account for: if "WIN://" not in dacl: raw_ace_type = dacl.split(";")[0] raw_ace_flags = dacl.split(";")[1] raw_perms = dacl.split(";")[2] raw_trustee = dacl.split(";")[5] # Obtain the Plaintext ACE Type: access_type = self.ACCESS[raw_ace_type] # Obtain the plaintext ACE Flags: Don't Need These """ flags = "" flags_index = 0 if (len(raw_ace_flags) > 2): flag_split = [raw_ace_flags[i:i+2] for i in range(0, len(raw_ace_flags), 2)] for flag in flag_split: if (flags_index == 0): flags += f"{self.ACCESS[flag]}" flags_index += 1 else: flags += f", {self.ACCESS[flag]}" else: flags += f"{self.ACCESS[raw_ace_flags]}" """ # Obtain the plaintext permissions: acls = "" acl_index = 0 # Check if we have HEX permissions first: if "0x" in raw_perms: raw_perms = self.__access_from_hex(raw_perms) # Plaintext Permission Set: if len(raw_perms) > 2: perm_split = [ raw_perms[i:i + 2] for i in range(0, len(raw_perms), 2) ] for acl in perm_split: if acl_index == 0: acls += f"{self.ACCESS[acl]}" acl_index += 1 else: acls += f", {self.ACCESS[acl]}" else: acls += f"{self.ACCESS[raw_perms]}" # Obtain the Account/User (Trustee) try: # sometimes fails due to undocumented trustees such as services. if len(raw_trustee) <= 2: trustee = self.TRUSTEE[ raw_trustee] # Get the trustee from the windows_objects class else: try: # if the object is a SID, attempt to translate it and obtain the ASCII name trustee = win32security.LookupAccountSid( None, win32security.GetBinarySid(raw_trustee)) trustee = f"{trustee[1]}/{trustee[0]}" except: trustee = None except: trustee = None # Add all the content to the dict object if trustee not in all_permissions.keys( ) and trustee != None: all_permissions[trustee] = acls elif trustee != None: current = f"{str(all_permissions[trustee])} {acls}" all_permissions[trustee] = current current = "" return all_permissions except Exception as e: self.error_index += 1 self.__write_error(sddl_string + "\n" + dacl) self.__print_exception() pass
def dump_token(th): token_type = win32security.GetTokenInformation(th, win32security.TokenType) print('TokenType:', token_type, TOKEN_TYPE.lookup_name(token_type)) if token_type == win32security.TokenImpersonation: imp_lvl = win32security.GetTokenInformation( th, win32security.TokenImpersonationLevel) print('TokenImpersonationLevel:', imp_lvl, SECURITY_IMPERSONATION_LEVEL.lookup_name(imp_lvl)) print('TokenSessionId:', win32security.GetTokenInformation(th, win32security.TokenSessionId)) privs = win32security.GetTokenInformation(th, win32security.TokenPrivileges) print('TokenPrivileges:') for priv_luid, priv_flags in privs: flag_names, unk = TOKEN_PRIVILEGE_ATTRIBUTES.lookup_flags(priv_flags) flag_desc = ' '.join(flag_names) if (unk): flag_desc += '(' + str(unk) + ')' priv_name = win32security.LookupPrivilegeName('', priv_luid) priv_desc = win32security.LookupPrivilegeDisplayName('', priv_name) print('\t', priv_name, priv_desc, priv_flags, flag_desc) print('TokenGroups:') groups = win32security.GetTokenInformation(th, win32security.TokenGroups) for group_sid, group_attr in groups: flag_names, unk = TOKEN_GROUP_ATTRIBUTES.lookup_flags(group_attr) flag_desc = ' '.join(flag_names) if (unk): flag_desc += '(' + str(unk) + ')' if group_attr & TOKEN_GROUP_ATTRIBUTES.SE_GROUP_LOGON_ID: sid_desc = 'Logon sid' else: sid_desc = win32security.LookupAccountSid('', group_sid) print('\t', group_sid, sid_desc, group_attr, flag_desc) ## Vista token information types, will throw (87, 'GetTokenInformation', 'The parameter is incorrect.') on earier OS try: is_elevated = win32security.GetTokenInformation( th, win32security.TokenElevation) print('TokenElevation:', is_elevated) except pywintypes.error as details: if details.winerror != winerror.ERROR_INVALID_PARAMETER: raise return None print( 'TokenHasRestrictions:', win32security.GetTokenInformation(th, win32security.TokenHasRestrictions)) print( 'TokenMandatoryPolicy', win32security.GetTokenInformation(th, win32security.TokenMandatoryPolicy)) print( 'TokenVirtualizationAllowed:', win32security.GetTokenInformation( th, win32security.TokenVirtualizationAllowed)) print( 'TokenVirtualizationEnabled:', win32security.GetTokenInformation( th, win32security.TokenVirtualizationEnabled)) elevation_type = win32security.GetTokenInformation( th, win32security.TokenElevationType) print('TokenElevationType:', elevation_type, TOKEN_ELEVATION_TYPE.lookup_name(elevation_type)) if elevation_type != win32security.TokenElevationTypeDefault: lt = win32security.GetTokenInformation(th, win32security.TokenLinkedToken) print('TokenLinkedToken:', lt) else: lt = None return lt
all_info = win32security.OWNER_SECURITY_INFORMATION | win32security.GROUP_SECURITY_INFORMATION | win32security.DACL_SECURITY_INFORMATION | win32security.SACL_SECURITY_INFORMATION info = win32security.OWNER_SECURITY_INFORMATION | win32security.GROUP_SECURITY_INFORMATION | win32security.DACL_SECURITY_INFORMATION ph = win32process.GetCurrentProcess() th = win32security.OpenProcessToken( ph, win32security.TOKEN_ALL_ACCESS) ##win32con.TOKEN_ADJUST_PRIVILEGES) win32security.AdjustTokenPrivileges(th, 0, new_privs) my_sid = win32security.GetTokenInformation(th, win32security.TokenUser)[0] pwr_sid = win32security.LookupAccountName('', 'Power Users')[0] h = win32process.GetProcessWindowStation() sd = win32security.GetUserObjectSecurity(h, info) dacl = sd.GetSecurityDescriptorDacl() ace_cnt = dacl.GetAceCount() dacl.AddAccessAllowedAce(dacl.GetAclRevision(), win32con.ACCESS_SYSTEM_SECURITY | win32con.WRITE_DAC, my_sid) sd.SetSecurityDescriptorDacl(1, dacl, 0) sd.SetSecurityDescriptorGroup(pwr_sid, 0) sd.SetSecurityDescriptorOwner(pwr_sid, 0) win32security.SetUserObjectSecurity(h, info, sd) new_sd = win32security.GetUserObjectSecurity(h, info) assert new_sd.GetSecurityDescriptorDacl().GetAceCount( ) == ace_cnt + 1, 'Did not add an ace to the Dacl !!!!!!' assert win32security.LookupAccountSid('', new_sd.GetSecurityDescriptorOwner( ))[0] == 'Power Users', 'Owner not successfully set to Power Users !!!!!' assert win32security.LookupAccountSid('', new_sd.GetSecurityDescriptorGroup( ))[0] == 'Power Users', 'Group not successfully set to Power Users !!!!!'
def AddFileOwnerWindows(): try: import win32api import win32con import win32security except ImportError: return from sources_types import Win32_UserAccount from sources_types import Win32_Group def SID_CodeToName(typeSID): mapSIDList = { win32security.SidTypeUser: "******", win32security.SidTypeGroup: "Group SID", win32security.SidTypeDomain: "Domain SID", win32security.SidTypeAlias: "Alias SID", win32security.SidTypeWellKnownGroup: "Well-known group", win32security.SidTypeDeletedAccount: "Deleted account", win32security.SidTypeInvalid: "Invalid SID", win32security.SidTypeUnknown: "Unknown type SID", win32security.SidTypeComputer: "Computer SID", # win32security.SidTypeLabel: "Mandatory integrity label SID" # NOT DEFINED } try: return mapSIDList[typeSID] except: return "Unknown SID" try: sd = win32security.GetFileSecurity( file_name, win32security.OWNER_SECURITY_INFORMATION) except Exception as exc: msg = str(exc) grph.add( (file_node, pc.property_owner, lib_common.NodeLiteral(msg))) return owner_sid = sd.GetSecurityDescriptorOwner() account_name, domain_name, typeCode = win32security.LookupAccountSid( None, owner_sid) typNam = SID_CodeToName(typeCode) DEBUG("Domain=%s Name=%s Type=%s", domain_name, account_name, typNam) if typeCode == win32security.SidTypeUser: account_node = Win32_UserAccount.MakeUri(account_name, domain_name) elif typeCode == win32security.SidTypeGroup: account_node = Win32_Group.MakeUri(account_name, domain_name) elif typeCode == win32security.SidTypeWellKnownGroup: account_node = Win32_Group.MakeUri(account_name, domain_name) else: # What else can we do ? account_node = Win32_UserAccount.MakeUri(account_name, domain_name) # TODO: What can we do with the domain ? grph.add((account_node, lib_common.MakeProp("Domain"), lib_common.NodeLiteral(domain_name))) grph.add((account_node, lib_common.MakeProp("SID"), lib_common.NodeLiteral(typNam))) grph.add((file_node, pc.property_owner, account_node))
def get_file_info(fpath, raw=False): """ Get OS-level info about a file path. Args: fpath (PathLike): a path to a file. raw (bool): if True returns raw counter timestamps, otherwise returns datetime wrapper objects. Returns: Dict: containing created: Time of creation (varies by OS, see os.stat docs) filesize: Size in bytes of the file last_accessed: Time of last access. last_modified: Time of last modification. owner: user that owns the file (None if unable to be determined) Example: >>> import ubelt as ub >>> fpath = ub.__file__ >>> info = get_file_info(fpath) >>> print(ub.repr2(info, nl=1)) # xdoc: +IGNORE_WANT { 'created': datetime.datetime(2018, 10, 25, 12, 30, 36, 806656), 'filesize': 5868, 'last_accessed': datetime.datetime(2018, 10, 25, 12, 30, 38, 886705), 'last_modified': datetime.datetime(2018, 10, 25, 12, 30, 36, 806656), 'owner': 'joncrall', } """ statbuf = os.stat(fpath) try: # Sometimes this fails if sys.platform.startswith('win32'): import win32security sec_desc = win32security.GetFileSecurity( fpath, win32security.OWNER_SECURITY_INFORMATION) owner_sid = sec_desc.GetSecurityDescriptorOwner() owner = win32security.LookupAccountSid(None, owner_sid)[0] else: from pwd import getpwuid owner = getpwuid(statbuf.st_uid).pw_name except Exception: owner = None info = OrderedDict([('created', statbuf.st_ctime), ('filesize', statbuf.st_size), ('last_accessed', statbuf.st_atime), ('last_modified', statbuf.st_mtime), ('owner', owner)]) # permission = [os.access(fpath, os.W_OK), os.access(fpath, os.X_OK)] if not raw: time_keys = [ 'last_accessed', 'last_modified', 'created', ] for key in time_keys: info[key] = datetime.datetime.fromtimestamp(info[key]) return info
def Main(): cgiEnv = lib_common.ScriptEnvironment(can_process_remote=True) server = cgiEnv.GetId() grph = cgiEnv.GetGraph() if lib_util.is_local_address(server): serv_name_or_none = None server_node = lib_common.nodeMachine else: serv_name_or_none = server server_node = lib_uris.gUriGen.HostnameUri(server) try: lib_win32.WNetAddConnect(serv_name_or_none) except Exception as exc: # Maybe the machine is not online. lib_common.ErrorMessageHtml(str(exc)) resume = 0 num_members = 0 try: while True: data, total, resume = win32net.NetLocalGroupEnum( serv_name_or_none, 1, resume) for group in data: logging.debug("Group %(name)s:%(comment)s", group) # TODO: Not sure about the groupname syntax. group_name = group['name'] logging.debug("group_name=%s", group_name) node_group = survol_Win32_Group.MakeUri(group_name, server) grph.add((node_group, pc.property_host, server_node)) group_comment = group['comment'] logging.debug("group_comment=%s", group_comment) if group_comment != "": group_comment_max_width = max(80, len(group_name)) if len(group_comment) > group_comment_max_width: group_comment = group_comment[: group_comment_max_width] + "..." grph.add((node_group, pc.property_information, lib_util.NodeLiteral(group_comment))) memberresume = 0 while True: member_data, total, member_resume = win32net.NetLocalGroupGetMembers( serv_name_or_none, group_name, 2, memberresume) for member in member_data: # Converts Sid to username num_members = num_members + 1 try: user_name, domain, the_type = win32security.LookupAccountSid( server, member['sid']) except Exception as exc: logging.warning( "Server=%s LookupAccountSid Caught:%s", server, str(exc)) continue logging.debug("Member: %s: %s server=%s", user_name, member['domainandname'], server) # node_user = serverBox.UserUri( user_name ) node_user = survol_Win32_UserAccount.MakeUri( user_name, server) # TODO: Not sure about the property. # TODO: Not sure about the username syntax. grph.add((node_user, pc.property_group, node_group)) if memberresume == 0: break if not resume: break except Exception as exc: lib_common.ErrorMessageHtml("win32 local groups:" + str(exc)) cgiEnv.OutCgiRdf("LAYOUT_SPLINE")
def Main(): cgiEnv = lib_common.CgiEnv() grph = cgiEnv.GetGraph() # TODO: Try this on a remote machine. server = None # Run on local machine for the moment. # servName_or_None is for Windows functions where the local host must be None. # servNameNotNone is for our URLs where the hostname must be explicit. if not server or lib_util.IsLocalAddress(server): servName_or_None = None # So it is compatible with WMI. servNameNotNone = lib_uris.TruncateHostname(lib_util.currentHostname) # .home serverNode = lib_common.nodeMachine serverBox = lib_common.gUriGen else: servName_or_None = server servNameNotNone = server serverNode = lib_common.gUriGen.HostnameUri(server) serverBox = lib_common.RemoteBox(server) resume = 0 numMembers = 0 while True: level = 1 data, total, resume = win32net.NetLocalGroupEnum( servName_or_None, level, resume) for group in data: # sys.stderr.write("Group %(name)s:%(comment)s\n" % group) # TODO: Not sure about the groupname syntax. groupName = group['name'] # nodeGroup = lib_common.gUriGen.GroupUri( groupName ) nodeGroup = survol_Win32_Group.MakeUri(groupName, servNameNotNone) grph.add((nodeGroup, pc.property_host, lib_common.nodeMachine)) groupComment = group['comment'] if groupComment != "": groupCommentMaxWidth = max(80, len(groupName)) if len(groupComment) > groupCommentMaxWidth: groupComment = groupComment[:groupCommentMaxWidth] + "..." grph.add((nodeGroup, pc.property_information, lib_common.NodeLiteral(groupComment))) memberresume = 0 while True: levelMember = 2 memberData, total, memberResume = win32net.NetLocalGroupGetMembers( server, group['name'], levelMember, memberresume) for member in memberData: # Converts Sid to username userName, domain, type = win32security.LookupAccountSid( servName_or_None, member['sid']) numMembers = numMembers + 1 # sys.stderr.write(" Member: %s: %s\n" % (userName, member['domainandname'])) # nodeUser = lib_common.gUriGen.UserUri( userName ) nodeUser = survol_Win32_UserAccount.MakeUri( userName, servNameNotNone) # TODO: Not sure about the property. # TODO: Not sure about the username syntax. grph.add((nodeUser, pc.property_group, nodeGroup)) if memberResume == 0: break if not resume: break cgiEnv.OutCgiRdf("LAYOUT_SPLINE")
def sid_to_username(sid): SID_object = win32security.ConvertStringSidToSid(sid) return win32security.LookupAccountSid(None, SID_object) pass
from win32 import win32api import win32con import win32security FILENAME = "./getOwner.py" open(FILENAME, "w").close() print("I am", win32api.GetUserNameEx(win32con.NameSamCompatible)) sd = win32security.GetFileSecurity(FILENAME, win32security.OWNER_SECURITY_INFORMATION) owner_sid = sd.GetSecurityDescriptorOwner() name, domain, type = win32security.LookupAccountSid(None, owner_sid) print("File owned by %s\\%s" % (domain, name)) i = input("Tham re bhau")
def take_screenshot(): ret = False ScreenShot.init_globals() if ScreenShot.DISABLE_SSHOT: p("}}ybSkipping screen shot - disabled by .disable_sshot file}}xx", log_level=2) return # Find the logged in user and run the sshot.exe app cmd = os.path.join(util.BINARIES_FOLDER, "sshot\\sshot.exe") p("}}gnTrying to run " + cmd + "}}xx", log_level=4) user_token = UserAccounts.get_active_user_token() if user_token is None: p("}}ynUnable to get user token - screen locked?}}xx", log_level=2) return ret sidObj, intVal = win32security.GetTokenInformation( user_token, win32security.TokenUser) #source = win32security.GetTokenInformation(tokenh, TokenSource) if sidObj: accountName, domainName, accountTypeInt = \ win32security.LookupAccountSid(".", sidObj) else: p("}}rnUnable to get User Token! }}xx", log_level=1) return None #p("}}gnFound User Token: " + str(user_token) + "}}xx", log_level=5) # If user is in the administrators group, skip taking the sshot if UserAccounts.is_in_admin_group(accountName): p("}}mbUser (" + accountName + ") is in admin group, skipping screen shot...}}xx") return True p("}}gnRunning As: " + accountName + "}}xx", log_level=2) # Put this token in the logged in session #win32security.SetTokenInformation(user_token_copy, win32security.TokenSessionId, session_id) # Use win create process function si = win32process.STARTUPINFO() si.dwFlags = win32process.STARTF_USESHOWWINDOW si.wShowWindow = win32con.SW_NORMAL # si.lpDesktop = "WinSta0\Default" si.lpDesktop = "WinSta0\\Default" # Setup envinroment for the user environment = win32profile.CreateEnvironmentBlock(user_token, False) try: ( hProcess, hThread, dwProcessId, dwThreadId ) = win32process.CreateProcessAsUser( user_token, None, # AppName (really command line, blank if cmd line supplied) "\"" + cmd + "\"", # Command Line (blank if app supplied) None, # Process Attributes None, # Thread Attributes 0, # Inherits Handles win32con. NORMAL_PRIORITY_CLASS, # or win32con.CREATE_NEW_CONSOLE, environment, # Environment os.path.dirname(cmd), # Curr directory si) # Startup info p("Process Started: " + str(dwProcessId), log_level=5) p(hProcess, log_level=5) ret = True except Exception as e: p("}}rnError launching process:}}xx\n" + str(e), log_level=1) # Cleanup user_token.close() # else: # # Not logged in as system user, run as current user # try: # timeout = 10 # 10 seconds? # # Log an error if the process doesn't return 0 # # stdout=PIPE and stderr=STDOUT instead of capture_output=True # p("}}gnRunning as current user " + user_name + "}}xx") # proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,timeout=timeout, check=False) # if (proc.returncode == 0): # p("Command Results: " + cmd + "\n" + proc.stdout.decode()) # ret = True # else: # p("*** Command Failed!: " + cmd + "(" + str(proc.returncode) + ") \n" + proc.stdout.decode()) # except Exception as ex: # p("*** Command Exception! " + cmd + " \n" + \ # str(ex)) if ret is True: p("}}gnSnapped.}}xx", log_level=3) return ret
def get_ownername(filename): sd = win32security.GetFileSecurity( filename, win32security.OWNER_SECURITY_INFORMATION) name, _, _ = win32security.LookupAccountSid( None, sd.GetSecurityDescriptorOwner()) return name
my_sid) sacl.AddAuditAccessAce(sacl.GetAclRevision(), win32con.GENERIC_ALL, my_sid, 1, 1) win32security.SetNamedSecurityInfo(fname, win32security.SE_FILE_OBJECT, all_info, pwr_sid, pwr_sid, dacl, sacl) new_sd = win32security.GetNamedSecurityInfo(fname, win32security.SE_FILE_OBJECT, all_info) ## could do additional checking to make sure added ACE contains expected info if new_sd.GetSecurityDescriptorDacl().GetAceCount() != dacl_ace_cnt + 1: print("New dacl doesn" "t contain extra ace ????") if new_sd.GetSecurityDescriptorSacl().GetAceCount() != sacl_ace_cnt + 1: print("New Sacl doesn" "t contain extra ace ????") if (win32security.LookupAccountSid("", new_sd.GetSecurityDescriptorOwner())[0] != "Power Users"): print("Owner not successfully set to Power Users !!!!!") if (win32security.LookupAccountSid("", new_sd.GetSecurityDescriptorGroup())[0] != "Power Users"): print("Group not successfully set to Power Users !!!!!") win32security.SetNamedSecurityInfo( fname, win32security.SE_FILE_OBJECT, win32security.SACL_SECURITY_INFORMATION, None, None, None, None, )
def set_owner(self): sec_info = win32security.GetFileSecurity( self.path, win32security.OWNER_SECURITY_INFORMATION) sid = sec_info.GetSecurityDescriptorOwner() self.owner = win32security.LookupAccountSid(None, sid)[0]
def token_info(th): token_info = [] token_type = win32security.GetTokenInformation(th, win32security.TokenType) token_info.append( ('TokenType:', token_type, TOKEN_TYPE.lookup_name(token_type))) if token_type == win32security.TokenImpersonation: imp_lvl = win32security.GetTokenInformation( th, win32security.TokenImpersonationLevel) token_info.append(('TokenImpersonationLevel:', imp_lvl, SECURITY_IMPERSONATION_LEVEL.lookup_name(imp_lvl))) token_info.append( token_info.append( ('TokenSessionId:', win32security.GetTokenInformation(th, win32security.TokenSessionId)))) privs = win32security.GetTokenInformation(th, win32security.TokenPrivileges) token_info.append(('TokenPrivileges:', )) for priv_luid, priv_flags in privs: flag_names, unk = TOKEN_PRIVILEGE_ATTRIBUTES.lookup_flags(priv_flags) flag_desc = ' '.join(flag_names) if (unk): flag_desc += '(' + str(unk) + ')' priv_name = win32security.LookupPrivilegeName('', priv_luid) priv_desc = win32security.LookupPrivilegeDisplayName('', priv_name) token_info.append(( '\t', priv_name, priv_desc, priv_flags, flag_desc, )) token_info.append(('TokenGroups:', )) groups = win32security.GetTokenInformation(th, win32security.TokenGroups) for group_sid, group_attr in groups: flag_names, unk = TOKEN_GROUP_ATTRIBUTES.lookup_flags(group_attr) flag_desc = ' '.join(flag_names) if (unk): flag_desc += '(' + str(unk) + ')' if group_attr & TOKEN_GROUP_ATTRIBUTES.SE_GROUP_LOGON_ID: sid_desc = 'Logon sid' else: sid_desc = win32security.LookupAccountSid('', group_sid) token_info.append(( '\t', group_sid, sid_desc, group_attr, flag_desc, )) ## Vista token information types, will throw (87, 'GetTokenInformation', 'The parameter is incorrect.') on earier OS try: is_elevated = win32security.GetTokenInformation( th, win32security.TokenElevation) token_info.append(( 'TokenElevation:', is_elevated, )) except pywintypes.error, details: if details.winerror != winerror.ERROR_INVALID_PARAMETER: raise return None
def crawlFiles(path, DATEEPOCH, DAYSOLD, MINSIZE, EXCLUDED_FILES, LOGGER, threadnum, VERBOSE, DEBUG): """This is the list directory function. It crawls for files using scandir. Ignores files smaller than 'MINSIZE' MB, newer than 'DAYSOLD' old and in 'EXCLUDED_FILES'. Tries to reduce the amount of stat calls to the fs to help speed up crawl times. """ global total_num_files filelist = [] # try to crawl files in directory try: if IS_WIN: path = path.decode('utf-8') startswith = '.' else: startswith = b'.' # Crawl files in the directory for entry in scandir(path): # get file extension extension = os.path.splitext(entry.name)[1][1:].strip().lower() # check EXCLUDED_FILES, if regular file, don't follow symlinks if (entry.name in EXCLUDED_FILES) \ or (entry.name.startswith(startswith) and '.*' in EXCLUDED_FILES) \ or (not extension and 'NULLEXT' in EXCLUDED_FILES) \ or ('*.'+str(extension) in EXCLUDED_FILES) \ or (not entry.is_file(follow_symlinks=False)): continue try: # get absolute path (parent of file) abspath = os.path.abspath(path) # get full path to file filename_fullpath = os.path.join(abspath, entry.name) size = os.path.getsize(filename_fullpath) # Convert bytes to MB size_mb = size / 1024 / 1024 # Skip files smaller than x MB and skip empty files if size_mb >= MINSIZE and size > 0: # Get file modified time mtime_unix = int(os.path.getmtime(filename_fullpath)) mtime_utc = datetime.utcfromtimestamp(mtime_unix).strftime( '%Y-%m-%dT%H:%M:%S') # Convert time in days to seconds time_sec = DAYSOLD * 86400 file_mtime_sec = DATEEPOCH - mtime_unix # Only process files modified at least x days ago if file_mtime_sec >= time_sec: # get access time atime_unix = int(os.path.getatime(filename_fullpath)) atime_utc = datetime.utcfromtimestamp( atime_unix).strftime('%Y-%m-%dT%H:%M:%S') # get change time ctime_unix = int(os.path.getctime(filename_fullpath)) ctime_utc = datetime.utcfromtimestamp( ctime_unix).strftime('%Y-%m-%dT%H:%M:%S') if IS_WIN: sd = win32security.GetFileSecurity( filename_fullpath, win32security.OWNER_SECURITY_INFORMATION) owner_sid = sd.GetSecurityDescriptorOwner() owner, domain, type = win32security.LookupAccountSid( None, owner_sid) # placeholders for windows group = "0" inode = "0" else: # get user id of owner uid = os.stat(filename_fullpath).st_uid # try to get owner user name try: owner = pwd.getpwuid(uid).pw_name.split('\\') # remove domain before owner if len(owner) == 2: owner = owner[1] else: owner = owner[0] # if we can't find the owner's user name, use the uid number except KeyError: owner = uid # get group id gid = os.stat(filename_fullpath).st_gid # try to get group name try: group = grp.getgrgid(gid).gr_name.split('\\') # remove domain before group if len(group) == 2: group = group[1] else: group = group[0] # if we can't find the group name, use the gid number except KeyError: group = gid # get inode number inode = os.stat(filename_fullpath).st_ino # get number of hardlinks hardlinks = os.stat(filename_fullpath).st_nlink if IS_WIN: name = entry.name else: name = entry.name.decode('utf-8') extension = extension.decode('utf-8') abspath = abspath.decode('utf-8') # create md5 hash of file using metadata filestring = unicode(name) + str(size) + str( mtime_unix) filehash = hashlib.md5( filestring.encode('utf-8')).hexdigest() # get time indextime_utc = datetime.utcnow().strftime( "%Y-%m-%dT%H:%M:%S.%f") # create file metadata dictionary filemeta_dict = { "filename": "%s" % name, "extension": "%s" % extension, "path_parent": "%s" % abspath, "filesize": size, "owner": "%s" % owner, "group": "%s" % group, "last_modified": "%s" % mtime_utc, "last_access": "%s" % atime_utc, "last_change": "%s" % ctime_utc, "hardlinks": hardlinks, "inode": inode, "filehash": "%s" % filehash, "tag": "untagged", 'is_dupe': "false", "indexing_date": "%s" % indextime_utc, "indexing_thread": "%s" % threadnum } # add file metadata dictionary to filelist list filelist.append(filemeta_dict) total_num_files += 1 except: if DEBUG or VERBOSE: LOGGER.error('Failed to index file', exc_info=True) pass return filelist except: if DEBUG or VERBOSE: LOGGER.error('Failed to crawl directory', exc_info=True) pass return
#name=r"c:\autoexec.bat" #name= r"g:\!workgrp\lim" name=sys.argv[0] if not os.path.exists(name): print(name, "does not exist!") sys.exit() print("On file " , name, "\n") # get owner SID print("OWNER") try: sd= win32security.GetFileSecurity(name, win32security.OWNER_SECURITY_INFORMATION) sid= sd.GetSecurityDescriptorOwner() print(" ", win32security.LookupAccountSid(None, sid)) except pywintypes.error as exc: # in automation and network shares we see: # pywintypes.error: (1332, 'LookupAccountName', 'No mapping between account names and security IDs was done.') if exc.winerror != winerror.ERROR_NONE_MAPPED: raise print("No owner information is available") # get group SID try: print("GROUP") sd= win32security.GetFileSecurity(name, win32security.GROUP_SECURITY_INFORMATION) sid= sd.GetSecurityDescriptorGroup() print(" ", win32security.LookupAccountSid(None, sid)) except pywintypes.error as exc: if exc.winerror != winerror.ERROR_NONE_MAPPED:
def fill_svn_info(): if os.path.exists(args.FillSVNInfo[0]) is False: print("The CSV file given for the function does not exist!") sys.exit() os.makedirs(output_folder_path) # basepath_to_replace = 'Y:\\ART_FILES\\' csv_fields = [ 'MD5SUM', 'FILESIZE', 'FILENAME', 'FULLPATH', 'DESTINATION', 'ARTDRIVEPATH', 'SVNCOMMENT', 'SVNCOMMITED' ] filepath_output_file = os.path.join(output_folder_path, "FILES_TO_COMMIT.CSV") input_filepath = args.FillSVNInfo[0] # Write column headers in CSV with open(filepath_output_file, 'w', newline='') as output_file: output_file.write( "MD5SUM,FILESIZE,FILENAME,FULLPATH,DESTINATION,ARTDRIVEPATH,SVNCOMMENT,SVNCOMMITED\n" ) with open(input_filepath, 'r') as input_file: input_csv = csv.DictReader(input_file, csv_fields) with open(filepath_output_file, 'a', newline='') as output_file: output_csv = csv.DictWriter(output_file, csv_fields) for index, row in enumerate(input_csv): if index == 0: # Skip first line with column headers continue # Get art file from artists share instead of N:\ Drive (fast NAS) art_file = row['FULLPATH'] # art_file = row['FULLPATH'].replace(basepath_to_replace, row['ARTDRIVEPATH']) if not os.path.exists(art_file): print("Quitting. Can't find the file: ", art_file) assert ( os.path.exists(art_file) ) # Assert if file does not exist as something has gone horribly wrong. # Get last modified time of the file. art_file_modified_time = datetime.datetime.fromtimestamp( os.path.getmtime(art_file)).strftime('%Y-%m-%d %H:%M') # Get the username owner/creator of the file. art_file_security_descriptor = win32security.GetFileSecurity( art_file, win32security.OWNER_SECURITY_INFORMATION) art_file_owner_sid = art_file_security_descriptor.GetSecurityDescriptorOwner( ) art_file_owner_name, domain, type = win32security.LookupAccountSid( None, art_file_owner_sid) # Generate the SVN comment for the file & print it. svn_comment = "- Add art asset \"" + row[ 'FILENAME'] + "\" created by user " + art_file_owner_name + " and last modified " + art_file_modified_time print(svn_comment) # Output the CSV row output_csv.writerow({ \ 'MD5SUM': row['MD5SUM'], \ 'FILESIZE': row['FILESIZE'], \ 'FILENAME': row['FILENAME'], \ 'FULLPATH': art_file, \ 'DESTINATION': row['DESTINATION'], \ 'ARTDRIVEPATH': row['ARTDRIVEPATH'], \ 'SVNCOMMENT': svn_comment }) if (index % 100) == 0: print(colorama.Fore.RED, "Currently Processing index ", index, " out of all files", colorama.Style.RESET_ALL)
def remote_authorize(self, data): err, sec_buffer = self.mServer.authorize(data) if err == 0: # Get the handle for the authenticated user such that we can pass it # to win32security.DuplicateTokenEx() and get back a handle that # allows us to spawn interactive processes as the authenticated user. self.mServer.ctxt.ImpersonateSecurityContext() flags = win32security.TOKEN_DUPLICATE | win32security.TOKEN_QUERY handle = win32security.OpenThreadToken(win32api.GetCurrentThread(), flags, False) self.mServer.ctxt.RevertSecurityContext() # Using handle, reate a primary handle suitable for passing to # CreateProcessAsUser(). sec_attr = None # sec_attr = pywintypes.SECURITY_ATTRIBUTES() # sec_attr.Initialize() # sec_attr.bInheritHandle = True # Try different impersonation levels for DuplicateTokenEx(). These # should be in order of decreasing utility (most useful to least). # See the SECURITY_IMPERSONATION_LEVEL documentation for more # details. levels = [ win32security.SecurityDelegation, win32security.SecurityImpersonation ] primary_handle = None #access = win32security.TOKEN_ALL_ACCESS #access = win32con.MAXIMUM_ALLOWED access = win32security.TOKEN_IMPERSONATE | \ win32security.TOKEN_QUERY | \ win32security.TOKEN_ASSIGN_PRIMARY | \ win32security.TOKEN_DUPLICATE for l in levels: try: primary_handle = \ win32security.DuplicateTokenEx( ExistingToken = handle, DesiredAccess = access, ImpersonationLevel = l, TokenType = ntsecuritycon.TokenPrimary, TokenAttributes = sec_attr ) break except Exception, ex: self.mLogger.error( "Failed to create primary token with impersonation level %s:" % str(l)) self.mLogger.error(str(ex)) # If the above failed to create a primary token, then we throw an # exception. It is important that we do not return None from this # method. if primary_handle is None: msg = 'Failed to create primary token for user!' self.mLogger.error(msg) raise failure.Failure(error.UnauthorizedLogin(msg)) # acct_info[0] has the SID for the user. acct_info = win32security.GetTokenInformation( primary_handle, win32security.TokenUser) user_sid = acct_info[0] # This returns a tuple containing the user name, the domain (if # applicable), and the accouunt type. # NOTE: The returned strings may be Unicode strings. user_info = win32security.LookupAccountSid(None, user_sid) # Close the handle returned by win32security.OpenThreadToken() since # it is a duplicate. handle.Close() # NOTE: We are forcing the addition of user_sid to the window station # and desktop ACLs. This seems to be the only way for the user # authenticated through SSPI to be able to open interactive windows. return self.prepareAvatar( avatar.WindowsAvatar(primary_handle, user_sid, str(user_info[0]), str(user_info[1]), True))
def _get_owner_windows(self, filename): sd = win32security.GetFileSecurity( filename, win32security.OWNER_SECURITY_INFORMATION) owner_sid = sd.GetSecurityDescriptorOwner() name, domain, type = win32security.LookupAccountSid(None, owner_sid) return name
def sid_user(sid): username, domain, typ = win32security.LookupAccountSid(None, sid) return domain, username
import pywintypes, win32security sa = pywintypes.SECURITY_ATTRIBUTES() tmp_sid = win32security.LookupAccountName('', 'tmp')[0] sa.SetSecurityDescriptorOwner(tmp_sid, 0) sid = sa.SECURITY_DESCRIPTOR.GetSecurityDescriptorOwner() print(win32security.LookupAccountSid('', sid))
"%Y-%m-%d %H:%M:%S" ) timeagain_high = time.mktime( time_tuple_high ) time_tuple_low = time.strptime( target_low[0], "%Y-%m-%d %H:%M:%S" ) timeagain_low = time.mktime( time_tuple_low) if seconds > timeagain_low and seconds < timeagain_high: #if seconds < begin_sec-100: sys.exit() usr = ( ev_obj.Sid) username, domain, type = win32security.LookupAccountSid( None, usr) if username == 'SYSTEM' or username == 'sluser2' or username == 'NETWORK SERVICE' or username == 'LOCAL SERVICE' or username == 'ANONYMOUS LOGON': continue else: #print username output1 = open( "temp.txt", "wb") getusername = '******' + username + ')"' username = subprocess.check_output( getusername ) print "This is the username: "******"This is the fixed username: ", fixedusername
def get_acl_list_return(self, f_path): try: with windows_objects.disable_file_system_redirection(): acls = [] gfso = win32security.GetFileSecurity( f_path, win32security.DACL_SECURITY_INFORMATION) dacl = gfso.GetSecurityDescriptorDacl() for n_ace in range(dacl.GetAceCount()): ace = dacl.GetAce(n_ace) (ace_type, ace_flags) = ace[0] mask = 0 # Reset the bitmask for each interation domain = "" # Reset the domain for each interation name = "" # Reset the name for each interation ascii_mask = "" # Reset the ascii permission value for each interation if ace_type in self.__CONVENTIONAL_ACES: mask, sid = ace[1:] else: mask, object_type, inherited_object_type, sid = ace[1:] name, domain, type = win32security.LookupAccountSid( None, sid) # Enumerate windows_security_enums for enum_obj in windows_objects.windows_security_enums: if ctypes.c_uint32(mask).value == enum_obj.value.value: access = self.__CONVENTIONAL_ACES.get( ace_type, "OTHER") ascii_mask = enum_obj.name acls.append( f"{domain}\\{name} {access} {ascii_mask}") # Enumerate nt_security_permissions for enum_obj in windows_objects.nt_security_enums: if ctypes.c_uint32(mask).value == enum_obj.value.value: access = self.__CONVENTIONAL_ACES.get( ace_type, "OTHER") ascii_mask = enum_obj.name acls.append( f"{domain}\\{name} {access} {ascii_mask}") return acls except Exception as e: error = str(e).lower() if ("find the path specified" in error or "find the file specified" in error or "access is denied" in error or "ace type 9" in error or "nonetype" in error): acls.append(str(e)) pass return acls elif "no mapping" in error: note = """Possibly VULNERABLE: No mapping between account names and SID's Account used to set GPO may have been removed Account name may be typed incorrectly INFO: https://www.rebeladmin.com/2016/01/how-to-fix-error-no-mapping-between-account-names-and-security-ids-in-active-directory/""" acls.append(str(e)) pass return acls else: self.__write_error(f_path) self.__print_exception() exit(0)
SID_TYPE_COMPUTER = 9 SID_TYPE_LABEL = 10 def getDomainInfo(): try: token = win32security.OpenThreadToken(win32api.GetCurrentThread(), ntsecuritycon.TOKEN_QUERY, 1) except win32api.error, exc: if exc[0] != winerror.ERROR_NO_TOKEN: raise token = win32security.OpenProcessToken(win32api.GetCurrentProcess(), ntsecuritycon.TOKEN_QUERY) sid, attr = win32security.GetTokenInformation(token, ntsecuritycon.TokenUser) name, domain, type_ = win32security.LookupAccountSid(None, sid) win32api.CloseHandle(token) return name, domain, type_ def SIDTypeToString(type_): TYPE_MAP = { SID_TYPE_USER: "******", SID_TYPE_GROUP: "group", SID_TYPE_DOMAIN: "domain", SID_TYPE_ALIAS: "alias", SID_TYPE_WELL_KNOWN_GROUP: "well-known group", SID_TYPE_DELETED_ACCOUNT: "deleted account", SID_TYPE_INVALID: "invalid", SID_TYPE_UNKNOWN: "unknown", SID_TYPE_COMPUTER: "computer",
my_sid) sacl.AddAuditAccessAce(sacl.GetAclRevision(), win32con.GENERIC_ALL, my_sid, 1, 1) sd.SetSecurityDescriptorDacl(1, dacl, 0) sd.SetSecurityDescriptorSacl(1, sacl, 0) sd.SetSecurityDescriptorGroup(pwr_sid, 0) sd.SetSecurityDescriptorOwner(pwr_sid, 0) win32security.SetKernelObjectSecurity(ph, all_info, sd) new_sd = win32security.GetKernelObjectSecurity(ph, all_info) if new_sd.GetSecurityDescriptorDacl().GetAceCount() != dacl_ace_cnt + 1: print 'New dacl doesn' 't contain extra ace ????' if new_sd.GetSecurityDescriptorSacl().GetAceCount() != sacl_ace_cnt + 1: print 'New Sacl doesn' 't contain extra ace ????' if win32security.LookupAccountSid( '', new_sd.GetSecurityDescriptorOwner())[0] != 'Power Users': print 'Owner not successfully set to Power Users !!!!!' if win32security.LookupAccountSid( '', new_sd.GetSecurityDescriptorGroup())[0] != 'Power Users': print 'Group not successfully set to Power Users !!!!!' sd.SetSecurityDescriptorSacl(0, None, 0) win32security.SetKernelObjectSecurity(ph, win32security.SACL_SECURITY_INFORMATION, sd) new_sd_1 = win32security.GetKernelObjectSecurity( ph, win32security.SACL_SECURITY_INFORMATION) if new_sd_1.GetSecurityDescriptorSacl() is not None: print 'Unable to set Sacl to NULL !!!!!!!!'
def Main(): cgiEnv = lib_common.CgiEnv(can_process_remote=True) server = cgiEnv.GetId() grph = cgiEnv.GetGraph() if lib_util.IsLocalAddress(server): servName_or_None = None serverNode = lib_common.nodeMachine else: servName_or_None = server serverNode = lib_common.gUriGen.HostnameUri(server) # http://www.math.uiuc.edu/~gfrancis/illimath/windows/aszgard_mini/movpy-2.0.0-py2.4.4/movpy/lib/win32/Demos/win32netdemo.py # servName_or_None, imper = lib_win32.MakeImpersonate(server) # hostname = "Titi" for example try: lib_win32.WNetAddConnect(servName_or_None) except: # Maybe the machine is not online. exc = sys.exc_info()[1] lib_common.ErrorMessageHtml(str(exc)) resume = 0 numMembers = 0 try: while True: # data, total, resume = win32net.NetLocalGroupEnum(server, 1, resume) data, total, resume = win32net.NetLocalGroupEnum( servName_or_None, 1, resume) for group in data: DEBUG("Group %(name)s:%(comment)s", group) # TODO: Not sure about the groupname syntax. groupName = group['name'] DEBUG("groupName=%s", groupName) # nodeGroup = serverBox.GroupUri( groupName ) nodeGroup = survol_Win32_Group.MakeUri(groupName, server) grph.add((nodeGroup, pc.property_host, serverNode)) groupComment = group['comment'] DEBUG("groupComment=%s", groupComment) if groupComment != "": groupCommentMaxWidth = max(80, len(groupName)) if len(groupComment) > groupCommentMaxWidth: groupComment = groupComment[:groupCommentMaxWidth] + "..." grph.add((nodeGroup, pc.property_information, lib_common.NodeLiteral(groupComment))) memberresume = 0 while True: # memberData, total, memberResume = win32net.NetLocalGroupGetMembers(server, group['name'], 2, resume) memberData, total, memberResume = win32net.NetLocalGroupGetMembers( servName_or_None, groupName, 2, memberresume) for member in memberData: # Converts Sid to username numMembers = numMembers + 1 try: userName, domain, type = win32security.LookupAccountSid( server, member['sid']) except Exception: exc = sys.exc_info()[1] WARNING("Server=%s Caught:%s", server, str(exc)) continue DEBUG("Member: %s: %s server=%s", userName, member['domainandname'], server) # nodeUser = serverBox.UserUri( userName ) nodeUser = survol_Win32_UserAccount.MakeUri( userName, server) # TODO: Not sure about the property. # TODO: Not sure about the username syntax. grph.add((nodeUser, pc.property_group, nodeGroup)) if memberResume == 0: break if not resume: break except Exception: exc = sys.exc_info()[1] lib_common.ErrorMessageHtml("win32 local groups:" + str(exc)) cgiEnv.OutCgiRdf("LAYOUT_SPLINE")