// // Copyright (c) 2022-present, Trail of Bits, Inc. // All rights reserved. // // This source code is licensed in accordance with the terms specified in // the LICENSE file found in the root directory of this source tree. // using NtApiDotNet; using System; using System.Text; using System.Security.AccessControl; using System.Security.Principal; using static NtApiDotNet.NtSecurity; using AceType = NtApiDotNet.AceType; using AceFlags = NtApiDotNet.AceFlags; using System.Runtime.InteropServices; using System.Diagnostics; using RpcInvestigator.Windows; namespace RpcInvestigator.Util { using static TraceLogger; public static class SddlParser { public static string SidToString(SecurityIdentifier SidValue) { try { return SidValue.ToString() + " (" + SidValue.Translate(typeof(NTAccount)).Value + ")"; } catch { return null; } } public static Ace GetAce(GenericAce ace) { var aceData = new byte[ace.BinaryLength]; IntPtr acePointer = Marshal.AllocHGlobal(ace.BinaryLength); IntPtr currentPointer = acePointer; try { ace.GetBinaryForm(aceData, 0); Marshal.Copy(aceData, 0, currentPointer, ace.BinaryLength); var header = (ACE_HEADER)Marshal.PtrToStructure( currentPointer, typeof(ACE_HEADER)); // // What follows the header depends on the ACE type, but the // access mask, which is the last part we need, is always // directly after the header. // currentPointer = IntPtr.Add( currentPointer, Marshal.SizeOf(typeof(ACE_HEADER))); var accessMask = Marshal.ReadInt32(currentPointer); currentPointer = IntPtr.Add(currentPointer, 4); var type = (AceType)header.AceType; if (IsObjectAceType(type)) { // // Skip 32 bytes (object type and inherited object type) // currentPointer = IntPtr.Add(currentPointer, 32); } var sid = new Sid(currentPointer); return new Ace((AceType)header.AceType, (AceFlags)header.AceFlags, accessMask, sid); } catch (Exception ex) { Trace(TraceLoggerType.SddlParser, TraceEventType.Error, "Exception parsing SDDL string: " + ex.Message); } finally { Marshal.FreeHGlobal(acePointer); } return null; } private static string AclToString(RawAcl Acl) { StringBuilder result = new StringBuilder(); if (Acl == null) { return " {none}"; } foreach (var ace in Acl) { var ntAce = GetAce(ace); if (ntAce != null) { if (ntAce.Type != AceType.Allowed) { result.Append("Type: " + ntAce.Type.ToString() + ", "); } result.Append("Sid: " + ntAce.Sid.ToString() + " (" + ntAce.Sid.Name + ")" + ", Mask: " + String.Format("0x{0:X}", ntAce.Mask)); result.AppendLine(); } } return result.ToString(); } public static string Parse(string SddlString) { StringBuilder result = new StringBuilder(); RawSecurityDescriptor descriptor; try { descriptor = new RawSecurityDescriptor(SddlString); } catch (Exception ex) { throw new Exception("Unable to create RawSecurityDescriptor from " + "the provided SDDL string '" + SddlString + "': " + ex.Message); } result.AppendLine("Owner: " + SidToString(descriptor.Owner)); result.AppendLine("Group: " + SidToString(descriptor.Group)); result.Append("Discretionary ACL: "); result.AppendLine(); result.Append(AclToString(descriptor.DiscretionaryAcl)); result.AppendLine(); result.Append("System ACL: "); result.Append(AclToString(descriptor.SystemAcl)); result.AppendLine(); return result.ToString(); } } [StructLayout(LayoutKind.Sequential)] public struct ACE_HEADER { public byte AceType; public byte AceFlags; public ushort AceSize; } }