Skip to content

Instantly share code, notes, and snippets.

@itc-lab
Last active November 18, 2021 13:48
Show Gist options
  • Save itc-lab/030844f88869eb3fbd3712363e766035 to your computer and use it in GitHub Desktop.
Save itc-lab/030844f88869eb3fbd3712363e766035 to your computer and use it in GitHub Desktop.
ldap_search from AD(Active Directory)
<?php
// Copyright (c) 2021 ITC Lab.
// Released under the MIT license
// https://opensource.org/licenses/mit-license.php
$adServer = "ldap://ad.contoso.com";
$ldap = ldap_connect($adServer);
$username = "Administrator";
$password = "password";
$ldaprdn = 'ad' . "\\" . $username;
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
$bind = ldap_bind($ldap, $ldaprdn, $password);
if ($bind) {
$filter = "objectclass=*";
$rt = ldap_search($ldap, "dc=ad,dc=contoso,dc=com", $filter);
$results = array();
$entry_id = @ldap_first_entry($ldap, $rt);
while ($entry_id != false) {
$result = array();
$vals = @ldap_get_attributes($ldap, $entry_id);
$attrnum = intval($vals["count"]);
for ($n = 0; $n < $attrnum; $n ++) {
$attr = $vals[$n];
$key = strtolower($attr);
$num = intval($vals[$attr]["count"]);
if ($num != 0) {
$result[$key] = array();
for ($no = 0; $no < $num; $no ++) {
$value = $vals[$attr][$no];
$result[$key][$no] = $value;
}
if (isset($result[$key]) && count($result[$key]) == 1) {
$result[$key] = $result[$key][0];
}
}
}
$result["0dn"] = @ldap_get_dn($ldap, $entry_id);
Bin2String($result);
$results[] = $result;
$entry_id = @ldap_next_entry($ldap, $entry_id);
}
@ldap_close($ldap);
foreach ($results as $result) {
ksort($result);
foreach ($result as $key => $value) {
$attr = strcmp($key, "0dn") !== 0 ? $key : "dn";
if (is_array($value)) {
echo $attr.": ".implode("; ", $value).";\n";
} else {
echo $attr.": ".$value.";\n";
}
}
echo "\n";
}
} else {
$msg = "Invalid bind dn / password";
echo $msg;
}
function Bin2String(&$result)
{
$conv_func_attrs["SIDtoString"] = array("objectsid");
$conv_func_attrs["GUIDtoString"] = array("objectguid", "msdfsr-replicationgroupguid", "msdfsr-contentsetguid");
$conv_func_attrs["bin2hex"] = array("auditingpolicy", "dsasignature", "dnsrecord", "ipsecdata", "samdomainupdates", "msds-generationid", "logonhours");
$conv_func_attrs["userParameters"] = array("userparameters");
foreach ($conv_func_attrs as $func => $attrs) {
foreach ($attrs as $attr) {
if (isset($result[$attr]) && !empty($result[$attr])) {
$result[$attr] = @$func($result[$attr]);
}
}
}
}
function SIDtoString($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;
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)
);
}
function GUIDtoString($ADguid)
{
$guidinhex = str_split(bin2hex($ADguid), 2);
$guid = "";
$first = array_reverse(array_slice($guidinhex, 0, 4));
foreach ($first as $value) {
$guid .= $value;
}
$guid .= "-";
$second = array_reverse(array_slice($guidinhex, 4, 2, true), true);
foreach ($second as $value) {
$guid .= $value;
}
$guid .= "-";
$third = array_reverse(array_slice($guidinhex, 6, 2, true), true);
foreach ($third as $value) {
$guid .= $value;
}
$guid .= "-";
$fourth = array_slice($guidinhex, 8, 2, true);
foreach ($fourth as $value) {
$guid .= $value;
}
$guid .= "-";
$last = array_slice($guidinhex, 10, 16, true);
foreach ($last as $value) {
$guid .= $value;
}
return $guid;
}
function userParameters($userParameters)
{
// userParameters data structure described at: http://msdn.microsoft.com/en-us/library/ff635189.aspx
// TSProperty data structure described at: http://msdn.microsoft.com/en-us/library/ff635169.aspx
// Input: userParameters blob returned from ldap_search
// Output: associative array of user parameters
$parameters = array();
$userParameters = bin2hex($userParameters);
$userParameters = substr($userParameters, 96);
$Signature = chr(hexdec(substr($userParameters, 0, 2)));
$userParameters = substr($userParameters, 2);
if ($Signature != 'P') {
return false;
}
$TSPropertyCount = hexdec(substr($userParameters, 0, 2));
$userParameters = substr($userParameters, 2);
for ($i = 0; $i < $TSPropertyCount; $i++) {
$NameLength = hexdec(substr($userParameters, 0, 2));
$userParameters = substr($userParameters, 2);
$ValueLength = hexdec(substr($userParameters, 0, 2)) * 3;
if ($ValueLength == 0xc2 * 3) {
$userParameters = substr($userParameters, 2);
$ValueLength = hexdec(substr($userParameters, 0, 2)) * 3;
}
$userParameters = substr($userParameters, 2);
$Type = substr($userParameters, 0, 2);
$userParameters = substr($userParameters, 2);
$PropName = substr($userParameters, 0, $NameLength);
$PropName = hex2str($PropName);
$userParameters = substr($userParameters, $NameLength);
$PropValue = substr($userParameters, 0, $ValueLength);
$userParameters = substr($userParameters, $ValueLength);
switch ($PropName) {
case 'CtxWFHomeDir':
case 'CtxWFHomeDirW':
case 'CtxWFHomeDirDrive':
case 'CtxInitialProgram':
case 'CtxInitialProgramW':
case 'CtxWFProfilePath':
case 'CtxWFProfilePathW':
case 'CtxWorkDirectory':
case 'CtxWorkDirectoryW':
case 'CtxCallbackNumber':
$parameters[$PropName] = decode_PropValue($PropValue, true);
break;
case 'CtxCfgFlags1':
$parameters[$PropName] = parse_CtxCfgFlags1(decode_PropValue($PropValue));
break;
case 'CtxShadow':
$parameters[$PropName] = parse_CtxShadow(decode_PropValue($PropValue));
break;
default:
$parameters[$PropName] = decode_PropValue($PropValue);
}
}
//return $parameters;
array_walk_recursive($parameters, 'remove_null_string');
return json_encode($parameters, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
}
function remove_null_string(&$item, $key)
{
$item = str_replace("\0", "", $item);
}
function hex2str($hex)
{
$str = '';
for ($i = 0; $i < strlen($hex); $i += 2) {
$str .= chr(hexdec(substr($hex, $i, 2)));
}
return $str;
}
function decode_PropValue($hex, $ascii=false)
{
// Encoding described at: http://daduke.org/linux/userparameters.html
// for each character you want to encode, do:
// - split the character's byte into nibbles xxxx and yyyy
// - have a look at xxxx. If it's <= 9, control x (XXXXXX) equals 001011, otherwise it's 011010
// - have a look at yyyy. Here the bit patterns for control y (YYYYYY) are 001110 (yyyy <= 9), 011010 otherwise
// - if xxxx > 9: xxxx -= 9
// - if yyyy > 9: yyyy -= 9
// - take the prefix (1110), control y, yyyy, control x and xxxx and glue them all together to yield a 24 bit string
// - convert this bit stream to three bytes: 1110 YYYY YYyy yyXX XXXX xxxx
$decode_PropValue = '';
$blobs = str_split($hex, 6);
foreach ($blobs as $blob) {
$bin = decbin(hexdec($blob));
$control_y = substr($bin, 4, 6);
$nibble_y = substr($bin, 10, 4);
$control_x = substr($bin, 14, 6);
$nibble_x = substr($bin, 20, 4);
$byte = nibble_control($nibble_x, $control_x).nibble_control($nibble_y, $control_y);
if ($ascii) {
$decode_PropValue .= chr(bindec($byte));
} else {
$decode_PropValue = str_pad(dechex(bindec($byte)), 2, '0', STR_PAD_LEFT).$decode_PropValue;
}
}
return $decode_PropValue;
}
function nibble_control($nibble, $control)
{
if ($control == '011010') {
$dec = bindec($nibble);
$dec += 9;
return str_pad(decbin($dec), 4, '0', STR_PAD_LEFT);
}
return $nibble;
}
function parse_CtxCfgFlags1($CtxCfgFlags1)
{
// Flag bit mask values from: http://msdn.microsoft.com/en-us/library/ff635169.aspx
$parse_CtxCfgFlags1 = array();
$CtxCfgFlags1 = hexdec($CtxCfgFlags1);
$flags = array(
'F1MSK_INHERITINITIALPROGRAM' => 268435456,
'F1MSK_INHERITCALLBACK' => 134217728,
'F1MSK_INHERITCALLBACKNUMBER' => 67108864,
'F1MSK_INHERITSHADOW' => 33554432,
'F1MSK_INHERITMAXSESSIONTIME' => 16777216,
'F1MSK_INHERITMAXDISCONNECTIONTIME' => 8388608,
'F1MSK_INHERITMAXIDLETIME' => 4194304,
'F1MSK_INHERITAUTOCLIENT' => 2097152,
'F1MSK_INHERITSECURITY' => 1048576,
'F1MSK_PROMPTFORPASSWORD' => 524288,
'F1MSK_RESETBROKEN' => 262144,
'F1MSK_RECONNECTSAME' => 131072,
'F1MSK_LOGONDISABLED' => 65536,
'F1MSK_AUTOCLIENTDRIVES' => 32768,
'F1MSK_AUTOCLIENTLPTS' => 16384,
'F1MSK_FORCECLIENTLPTDEF' => 8192,
'F1MSK_DISABLEENCRYPTION' => 4096,
'F1MSK_HOMEDIRECTORYMAPROOT' => 2048,
'F1MSK_USEDEFAULTGINA' => 1024,
'F1MSK_DISABLECPM' => 512,
'F1MSK_DISABLECDM' => 256,
'F1MSK_DISABLECCM' => 128,
'F1MSK_DISABLELPT' => 64,
'F1MSK_DISABLECLIP' => 32,
'F1MSK_DISABLEEXE' => 16,
'F1MSK_WALLPAPERDISABLED' => 8,
'F1MSK_DISABLECAM' => 4
);
foreach ($flags as $flag => $bit) {
if ($CtxCfgFlags1 & $bit) {
$parse_CtxCfgFlags1[] = $flag;
}
}
return($parse_CtxCfgFlags1);
}
function parse_CtxShadow($CtxShadow)
{
// Flag values from: http://msdn.microsoft.com/en-us/library/ff635169.aspx
$CtxShadow = hexdec($CtxShadow);
$flags = array('Disable','EnableInputNotify','EnableInputNoNotify','EnableNoInputNotify','EnableNoInputNoNotify');
if ($CtxShadow < 0 || $CtxShadow > 4) {
return false;
}
return $flags[$CtxShadow];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment