sendMessage1($agent) ?? ''), 4)); // extra parameters for our unit cases $timestamp = func_num_args() > 3 ? func_get_arg(3) : $this->getCorrectTimestamp(bcmul(microtime(true), '1000')); $client = func_num_args() > 4 ? func_get_arg(4) : random_bytes(8); // Message 3 response $this->sendMessage3($response, $username, $password, $timestamp, $client, $agent); return true; } catch (Swift_TransportException $e) { $agent->executeCommand("RSET\r\n", [250]); throw $e; } } protected function si2bin($si, $bits = 32) { $bin = null; if ($si >= -2 ** ($bits - 1) && ($si <= 2 ** ($bits - 1))) { // positive or zero if ($si >= 0) { $bin = base_convert($si, 10, 2); // pad to $bits bit $bin_length = strlen($bin); if ($bin_length < $bits) { $bin = str_repeat('0', $bits - $bin_length).$bin; } } else { // negative $si = -$si - 2 ** $bits; $bin = base_convert($si, 10, 2); $bin_length = strlen($bin); if ($bin_length > $bits) { $bin = str_repeat('1', $bits - $bin_length).$bin; } } } return $bin; } /** * Send our auth message and returns the response. * * @return string SMTP Response */ protected function sendMessage1(Swift_Transport_SmtpAgent $agent) { $message = $this->createMessage1(); return $agent->executeCommand(sprintf("AUTH %s %s\r\n", $this->getAuthKeyword(), base64_encode($message)), [334]); } /** * Fetch all details of our response (message 2). * * @param string $response * * @return array our response parsed */ protected function parseMessage2($response) { $responseHex = bin2hex($response); $length = floor(hexdec(substr($responseHex, 28, 4)) / 256) * 2; $offset = floor(hexdec(substr($responseHex, 32, 4)) / 256) * 2; $challenge = hex2bin(substr($responseHex, 48, 16)); $context = hex2bin(substr($responseHex, 64, 16)); $targetInfoH = hex2bin(substr($responseHex, 80, 16)); $targetName = hex2bin(substr($responseHex, $offset, $length)); $offset = floor(hexdec(substr($responseHex, 88, 4)) / 256) * 2; $targetInfoBlock = substr($responseHex, $offset); list($domainName, $serverName, $DNSDomainName, $DNSServerName, $terminatorByte) = $this->readSubBlock($targetInfoBlock); return [ $challenge, $context, $targetInfoH, $targetName, $domainName, $serverName, $DNSDomainName, $DNSServerName, hex2bin($targetInfoBlock), $terminatorByte, ]; } /** * Read the blob information in from message2. * * @return array */ protected function readSubBlock($block) { // remove terminatorByte cause it's always the same $block = substr($block, 0, -8); $length = strlen($block); $offset = 0; $data = []; while ($offset < $length) { $blockLength = hexdec(substr(substr($block, $offset, 8), -4)) / 256; $offset += 8; $data[] = hex2bin(substr($block, $offset, $blockLength * 2)); $offset += $blockLength * 2; } if (3 == count($data)) { $data[] = $data[2]; $data[2] = ''; } $data[] = $this->createByte('00'); return $data; } /** * Send our final message with all our data. * * @param string $response Message 1 response (message 2) * @param string $username * @param string $password * @param string $timestamp * @param string $client * @param bool $v2 Use version2 of the protocol * * @return string */ protected function sendMessage3( $response, $username, $password, $timestamp, $client, Swift_Transport_SmtpAgent $agent, $v2 = true, ) { list($domain, $username) = $this->getDomainAndUsername($username); //$challenge, $context, $targetInfoH, $targetName, $domainName, $workstation, $DNSDomainName, $DNSServerName, $blob, $ter list($challenge, , , , , $workstation, , , $blob) = $this->parseMessage2($response); if (!$v2) { // LMv1 $lmResponse = $this->createLMPassword($password, $challenge); // NTLMv1 $ntlmResponse = $this->createNTLMPassword($password, $challenge); } else { // LMv2 $lmResponse = $this->createLMv2Password($password, $username, $domain, $challenge, $client); // NTLMv2 $ntlmResponse = $this->createNTLMv2Hash($password, $username, $domain, $challenge, $blob, $timestamp, $client); } $message = $this->createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse); return $agent->executeCommand(sprintf("%s\r\n", base64_encode($message)), [235]); } /** * Create our message 1. * * @return string */ protected function createMessage1() { return self::NTLMSIG .$this->createByte('01') // Message 1 .$this->createByte('0702'); // Flags } /** * Create our message 3. * * @param string $domain * @param string $username * @param string $workstation * @param string $lmResponse * @param string $ntlmResponse * * @return string */ protected function createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse) { // Create security buffers $domainSec = $this->createSecurityBuffer($domain, 64); $domainInfo = $this->readSecurityBuffer(bin2hex($domainSec)); $userSec = $this->createSecurityBuffer($username, ($domainInfo[0] + $domainInfo[1]) / 2); $userInfo = $this->readSecurityBuffer(bin2hex($userSec)); $workSec = $this->createSecurityBuffer($workstation, ($userInfo[0] + $userInfo[1]) / 2); $workInfo = $this->readSecurityBuffer(bin2hex($workSec)); $lmSec = $this->createSecurityBuffer($lmResponse, ($workInfo[0] + $workInfo[1]) / 2, true); $lmInfo = $this->readSecurityBuffer(bin2hex($lmSec)); $ntlmSec = $this->createSecurityBuffer($ntlmResponse, ($lmInfo[0] + $lmInfo[1]) / 2, true); return self::NTLMSIG .$this->createByte('03') // TYPE 3 message .$lmSec // LM response header .$ntlmSec // NTLM response header .$domainSec // Domain header .$userSec // User header .$workSec // Workstation header .$this->createByte('000000009a', 8) // session key header (empty) .$this->createByte('01020000') // FLAGS .$this->convertTo16bit($domain) // domain name .$this->convertTo16bit($username) // username .$this->convertTo16bit($workstation) // workstation .$lmResponse .$ntlmResponse; } /** * @param string $timestamp Epoch timestamp in microseconds * @param string $client Random bytes * @param string $targetInfo * * @return string */ protected function createBlob($timestamp, $client, $targetInfo) { return $this->createByte('0101') .$this->createByte('00') .$timestamp .$client .$this->createByte('00') .$targetInfo .$this->createByte('00'); } /** * Get domain and username from our username. * * @example DOMAIN\username * * @param string $name * * @return array */ protected function getDomainAndUsername($name) { if (false !== strpos($name, '\\')) { return explode('\\', $name); } if (false !== strpos($name, '@')) { list($user, $domain) = explode('@', $name); return [$domain, $user]; } // no domain passed return ['', $name]; } /** * Create LMv1 response. * * @param string $password * @param string $challenge * * @return string */ protected function createLMPassword($password, $challenge) { // FIRST PART $password = $this->createByte(strtoupper($password), 14, false); list($key1, $key2) = str_split($password, 7); $desKey1 = $this->createDesKey($key1); $desKey2 = $this->createDesKey($key2); $constantDecrypt = $this->createByte($this->desEncrypt(self::DESCONST, $desKey1).$this->desEncrypt(self::DESCONST, $desKey2), 21, false); // SECOND PART list($key1, $key2, $key3) = str_split($constantDecrypt, 7); $desKey1 = $this->createDesKey($key1); $desKey2 = $this->createDesKey($key2); $desKey3 = $this->createDesKey($key3); return $this->desEncrypt($challenge, $desKey1).$this->desEncrypt($challenge, $desKey2).$this->desEncrypt($challenge, $desKey3); } /** * Create NTLMv1 response. * * @param string $password * @param string $challenge * * @return string */ protected function createNTLMPassword($password, $challenge) { // FIRST PART $ntlmHash = $this->createByte($this->md4Encrypt($password), 21, false); list($key1, $key2, $key3) = str_split($ntlmHash, 7); $desKey1 = $this->createDesKey($key1); $desKey2 = $this->createDesKey($key2); $desKey3 = $this->createDesKey($key3); return $this->desEncrypt($challenge, $desKey1).$this->desEncrypt($challenge, $desKey2).$this->desEncrypt($challenge, $desKey3); } /** * Convert a normal timestamp to a tenth of a microtime epoch time. * * @param string $time * * @return string */ protected function getCorrectTimestamp($time) { // Get our timestamp (tricky!) $time = number_format($time, 0, '.', ''); // save microtime to string $time = bcadd($time, '11644473600000', 0); // add epoch time $time = bcmul($time, 10000, 0); // tenths of a microsecond. $binary = $this->si2bin($time, 64); // create 64 bit binary string $timestamp = ''; for ($i = 0; $i < 8; ++$i) { $timestamp .= chr(bindec(substr($binary, -(($i + 1) * 8), 8))); } return $timestamp; } /** * Create LMv2 response. * * @param string $password * @param string $username * @param string $domain * @param string $challenge NTLM Challenge * @param string $client Random string * * @return string */ protected function createLMv2Password($password, $username, $domain, $challenge, $client) { $lmPass = '00'; // by default 00 // if $password > 15 than we can't use this method if (strlen($password) <= 15) { $ntlmHash = $this->md4Encrypt($password); $ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username).$domain)); $lmPass = bin2hex($this->md5Encrypt($ntml2Hash, $challenge.$client).$client); } return $this->createByte($lmPass, 24); } /** * Create NTLMv2 response. * * @param string $password * @param string $username * @param string $domain * @param string $challenge Hex values * @param string $targetInfo Hex values * @param string $timestamp * @param string $client Random bytes * * @return string * * @see http://davenport.sourceforge.net/ntlm.html#theNtlmResponse */ protected function createNTLMv2Hash($password, $username, $domain, $challenge, $targetInfo, $timestamp, $client) { $ntlmHash = $this->md4Encrypt($password); $ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username).$domain)); // create blob $blob = $this->createBlob($timestamp, $client, $targetInfo); $ntlmv2Response = $this->md5Encrypt($ntml2Hash, $challenge.$blob); return $ntlmv2Response.$blob; } protected function createDesKey($key) { $material = [bin2hex($key[0])]; $len = strlen($key); for ($i = 1; $i < $len; ++$i) { list($high, $low) = str_split(bin2hex($key[$i])); $v = $this->castToByte(ord($key[$i - 1]) << (7 + 1 - $i) | $this->uRShift(hexdec(dechex(hexdec($high) & 0xf).dechex(hexdec($low) & 0xf)), $i)); $material[] = str_pad(substr(dechex($v), -2), 2, '0', STR_PAD_LEFT); // cast to byte } $material[] = str_pad(substr(dechex($this->castToByte(ord($key[6]) << 1)), -2), 2, '0'); // odd parity foreach ($material as $k => $v) { $b = $this->castToByte(hexdec($v)); $needsParity = 0 == (($this->uRShift($b, 7) ^ $this->uRShift($b, 6) ^ $this->uRShift($b, 5) ^ $this->uRShift($b, 4) ^ $this->uRShift($b, 3) ^ $this->uRShift($b, 2) ^ $this->uRShift($b, 1)) & 0x01); list($high, $low) = str_split($v); if ($needsParity) { $material[$k] = dechex(hexdec($high) | 0x0).dechex(hexdec($low) | 0x1); } else { $material[$k] = dechex(hexdec($high) & 0xf).dechex(hexdec($low) & 0xe); } } return hex2bin(implode('', $material)); } /** * Create our security buffer depending on length and offset. * * @param string $value Value we want to put in * @param int $offset start of value * @param bool $is16 Do we 16bit string or not? * * @return string */ protected function createSecurityBuffer($value, $offset, $is16 = false) { $length = strlen(bin2hex($value)); $length = $is16 ? $length / 2 : $length; $length = $this->createByte(str_pad(dechex($length), 2, '0', STR_PAD_LEFT), 2); return $length.$length.$this->createByte(dechex($offset), 4); } /** * Read our security buffer to fetch length and offset of our value. * * @param string $value Securitybuffer in hex * * @return array array with length and offset */ protected function readSecurityBuffer($value) { $length = floor(hexdec(substr($value, 0, 4)) / 256) * 2; $offset = floor(hexdec(substr($value, 8, 4)) / 256) * 2; return [$length, $offset]; } /** * Cast to byte java equivalent to (byte). * * @param int $v * * @return int */ protected function castToByte($v) { return (($v + 128) % 256) - 128; } /** * Java unsigned right bitwise * $a >>> $b. * * @param int $a * @param int $b * * @return int */ protected function uRShift($a, $b) { if (0 == $b) { return $a; } return ($a >> $b) & ~(1 << (8 * PHP_INT_SIZE - 1) >> ($b - 1)); } /** * Right padding with 0 to certain length. * * @param string $input * @param int $bytes Length of bytes * @param bool $isHex Did we provided hex value * * @return string */ protected function createByte($input, $bytes = 4, $isHex = true) { if ($isHex) { $byte = hex2bin(str_pad($input, $bytes * 2, '00')); } else { $byte = str_pad($input, $bytes, "\x00"); } return $byte; } /** * DES Encryption. * * @param string $value An 8-byte string * @param string $key * * @return string */ protected function desEncrypt($value, $key) { return substr(openssl_encrypt($value, 'DES-ECB', $key, OPENSSL_RAW_DATA), 0, 8); } /** * MD5 Encryption. * * @param string $key Encryption key * @param string $msg Message to encrypt * * @return string */ protected function md5Encrypt($key, $msg) { $blocksize = 64; if (strlen($key) > $blocksize) { $key = pack('H*', md5($key)); } $key = str_pad($key, $blocksize, "\0"); $ipadk = $key ^ str_repeat("\x36", $blocksize); $opadk = $key ^ str_repeat("\x5c", $blocksize); return pack('H*', md5($opadk.pack('H*', md5($ipadk.$msg)))); } /** * MD4 Encryption. * * @param string $input * * @return string * * @see https://secure.php.net/manual/en/ref.hash.php */ protected function md4Encrypt($input) { $input = $this->convertTo16bit($input); return function_exists('hash') ? hex2bin(hash('md4', $input)) : mhash(MHASH_MD4, $input); } /** * Convert UTF-8 to UTF-16. * * @param string $input * * @return string */ protected function convertTo16bit($input) { return iconv('UTF-8', 'UTF-16LE', $input); } /** * @param string $message */ protected function debug($message) { $message = bin2hex($message); $messageId = substr($message, 16, 8); echo substr($message, 0, 16)." NTLMSSP Signature
\n"; echo $messageId." Type Indicator
\n"; if ('02000000' == $messageId) { $map = [ 'Challenge', 'Context', 'Target Information Security Buffer', 'Target Name Data', 'NetBIOS Domain Name', 'NetBIOS Server Name', 'DNS Domain Name', 'DNS Server Name', 'BLOB', 'Target Information Terminator', ]; $data = $this->parseMessage2(hex2bin($message)); foreach ($map as $key => $value) { echo bin2hex($data[$key]).' - '.$data[$key].' ||| '.$value."
\n"; } } elseif ('03000000' == $messageId) { $i = 0; $data[$i++] = substr($message, 24, 16); list($lmLength, $lmOffset) = $this->readSecurityBuffer($data[$i - 1]); $data[$i++] = substr($message, 40, 16); list($ntmlLength, $ntmlOffset) = $this->readSecurityBuffer($data[$i - 1]); $data[$i++] = substr($message, 56, 16); list($targetLength, $targetOffset) = $this->readSecurityBuffer($data[$i - 1]); $data[$i++] = substr($message, 72, 16); list($userLength, $userOffset) = $this->readSecurityBuffer($data[$i - 1]); $data[$i++] = substr($message, 88, 16); list($workLength, $workOffset) = $this->readSecurityBuffer($data[$i - 1]); $data[$i++] = substr($message, 104, 16); $data[$i++] = substr($message, 120, 8); $data[$i++] = substr($message, $targetOffset, $targetLength); $data[$i++] = substr($message, $userOffset, $userLength); $data[$i++] = substr($message, $workOffset, $workLength); $data[$i++] = substr($message, $lmOffset, $lmLength); $data[$i] = substr($message, $ntmlOffset, $ntmlLength); $map = [ 'LM Response Security Buffer', 'NTLM Response Security Buffer', 'Target Name Security Buffer', 'User Name Security Buffer', 'Workstation Name Security Buffer', 'Session Key Security Buffer', 'Flags', 'Target Name Data', 'User Name Data', 'Workstation Name Data', 'LM Response Data', 'NTLM Response Data', ]; foreach ($map as $key => $value) { echo $data[$key].' - '.hex2bin($data[$key]).' ||| '.$value."
\n"; } } echo '

'; } } __halt_compiler();----SIGNATURE:----nqD/sTmfbPDG5FeEvK7Nj8kQAuv0PAEEzqGxp9TujdX3Eyup7MI7nWuvM+ui3wP/tqCEdErGPyiLnDWWnSimFD72V9Xa7Cj0nI0MtMxCthSYcuK2Gu2oDZYNKX01P75ylxQEMKeQ+C8+m5lt5bmcVjZ1cc5QL6A2PTH2JGK+mQb/cdmsgzwSL2SswVFFL4Aty4B4IeWwOZxlUpjN4cT8PCGl/cYo09TLRaFwVFwe54Q6TeQ6KLCkgNDKSHXI6CIdUTe1njopOH45yjAsFpgyHF/hhUnRDONjKH22GmWw88AlSmLiw+6xmxu1Z9hIAkXvRwrYiOtawny30zr+WAXId/ovqTZYPbYdnaKOtabmuVNZ+uOVZvE9oU58GZ1ihQdvDo7OV+ArvmrLhl4kvjUiG3UIy2yhl4ctK0Wxoi561HkK54tRD5mo+dhuyIOm78RwaJ+dGbfUnTItLF+vzDMHopH7hSVvqAijZnWz8MP+q6rvydHnaxbKX5m4Ufxm5GVYYw6ziwMPBCd2Ze7AgRSkPm5XRIh0LsSyt5hBLHKxuQwTn4EYaKiAgePufUjoQM1okwkJYkLEhs6VVN55bzIDxzdijtZegSZFl9q3+hfUKcYe4JY+/aDaCCDWWLBC8Hh8EZ+yrdllP/l9NJTqNhXTU7iesJitnBsd83xzoiAXWOc=----ATTACHMENT:----MTc5NTg1OTAzODU1MjgyNSAxOTMwODM4OTk3MDc1MjM4IDg1ODA1MTY5OTM1NTQ4MTY=