RpcInvestigator/Util/SddlParser.cs

147 lines
4.9 KiB
C#

//
// 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;
}
}