A while back I was looking for a simple way of decoding an objectSid value from Active Directory into the readable string name you typically see it in. There are some examples of doing this out there, but many are incomplete or don't account for edge-cases for all the different type of SIDs. By using PHP's pack and unpack functions the process of decoding and encoding the binary objectSid value becomes much easier:

    /**
     * Decode the binary SID into its readable form.
     *
     * @param string $value
     * @return string
     */
    function decodeSID($value)
    {
        $sid = @unpack('C1rev/C1count/x2/N1id', $value);
        $subAuthorities = [];

        if (!isset($sid['id']) || !isset($sid['rev'])) {
            throw new \UnexpectedValueException(
                'The revision level or identifier authority was not found when decoding the SID.'
            );
        }

        $revisionLevel = $sid['rev'];
        $identifierAuthority = $sid['id'];
        $subs = isset($sid['count']) ? $sid['count'] : 0;

        // The sub-authorities depend on the count, so only get as many as the count, regardless of data beyond it
        for ($i = 0; $i < $subs; $i++) {
            $subAuthorities[] = unpack('V1sub', hex2bin(substr(bin2hex($value), 16 + ($i * 8), 8)))['sub'];
        }

        return 'S-'.$revisionLevel.'-'.$identifierAuthority.implode(
            preg_filter('/^/', '-', $subAuthorities)
        );
    }

    /**
     * Encode a readable string SID into its binary form.
     *
     * @param string $value
     * @return string
     */
    function encodeSID($value)
    {
        $sid = explode('-', ltrim($value, 'S-'));

        $revisionLevel = (int) array_shift($sid);
        $identifierAuthority = (int) array_shift($sid);
        $subAuthorities = array_map('intval', $sid);

        return pack(
            'C2xxNV*',
            $revisionLevel,
            count($subAuthorities),
            $identifierAuthority,
            ...$subAuthorities
        );
    }

Strictly speaking, the encodeSID($value) method is not needed. With Active Directory you can query against the objectSid using the string readable name. However, you can also pass the binary value from the encode method above to PHPs bin2hex() method then escape each pair of hex values and use that as the filter value when searching against the objectSid.

However, it's still useful to be able to fully encode a SID to its binary form as there are other data formats in Active Directory (And Windows in general) that store SIDs using their binary representation. Most notable of those would be the ntSecurityDescriptor.

Next Post