<?php
  define
('_DEBUG_',1);  // zapisuj przesyłane nagłówki HTTP do pliku "debug.txt"
  
define('_DEBUG_FILE_',dirname(__FILE__).DIRECTORY_SEPARATOR.'debug.txt');  // plik "debug.txt" z przesyłanymi nagłówkami
  
require_once('auth.php');
  
session_start();
  unset(
$_SESSION['NTLM.USER_NAME']);  // wymuś procedurę autentykacji
  
checkLogin();
  
session_destroy();
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="pl">
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="description" content="programistrz.pl">
  <title>Analiza działania procesu uwierzytelniania NTLM</title>
<style>
body      { font-size: 85%; }
th        { font-size: 115%; background-color: #d0d0d0; }
tr.odd    { background-color: #f0f0f0; }
td.lp     { text-align: center; }
td.direct { text-align: center; }
b.message { color: #008000; font-size: 115%; }
</style>
</head>
<body>
<h2>Analiza działania procesu uwierzytelniania NTLM</h2>
<table border="1" cellspacing="0" cellpadding="2">
<tr><th>L.p.</th><th>Czas</th><th>Przesłane nagłówki HTTP</th><th>Zdekodowane dane uwierzytelniające</th><th>Kierunek</th></tr>
<?php
  $startTime
=null;
  
$headersArray=@ntlm_file_get_contents(_DEBUG_FILE_);
  
$headersArray=explode("|",$headersArray);
  for (
$r=0;$r<count($headersArray)-1;$r++)
  {
    
$headersArray[$r]=explode("\t",$headersArray[$r],2);
    
$headersArray[$r][2]='';
    
$rawHeaders=$headersArray[$r][1];
    
$headers=explode("\n",$rawHeaders);
    for (
$i=0;$i<count($headers);$i++)
    {
      
$headers[$i]=preg_replace('/^([a-zA-Z0-9_+=-]+):\s/e',"'<b>\\1</b>: '",$headers[$i]);
      
$headers[$i]=chunk_split($headers[$i],60,'<br>');
    }
    
$headersArray[$r][1]=preg_replace('/(<br>)+$/sim','',implode('',$headers));
    if (
preg_match('/^(?:Authorization|WWW-Authenticate):\s(?:Negotiate|NTLM)\s([a-zA-Z0-9+=\/]+)$/sim',$rawHeaders,$matches))
    {
      
$matches=base64_decode($matches[1]);
      
$decoded='';
      for (
$i=0;$i<strlen($matches);$i++)
      {
        
$ord=ord($matches[$i]);
        if (
$ord<=32 || $ord>127)
          
$decoded.=' 0x'.($ord<16?'0':''). strtoupper(dechex($ord)).' ';
        else
          
$decoded.=$matches[$i];
      }
      
$decoded=htmlspecialchars(str_replace('  ',' ',$decoded));
      
$i=strpos($matches,"NTLMSSP\0");
      if (
$i!==false)
      {
        if (
$i$matches=substr($matches,$i);
        
$type=getByte($matches,8);
        
$decoded.='<hr><b class="message">Message type '.$type.'</b><br>';
        if (
$type==1)
        {
          list(
$flags,$osVersion,$domain,$host)=decodeMessageType1($matches);
          
$decoded.='<br>Flags: <b>0x'.dechex($flags).'</b><br><i>&nbsp; &nbsp; '.implode('<br>&nbsp; &nbsp; ',showMessageFlags($flags)).'</i>';
          
$decoded.='<br>OS Version: <b>'.$osVersion.'</b>';
          
$decoded.='<br>Domain: <b>'.$domain.'</b>';
          
$decoded.='<br>Host: <b>'.$host.'</b>';
        }
        elseif (
$type==2)
        {
          list(
$flags,$osVersion,$targetName,$challenge,$context,$targetInformation)=decodeMessageType2($matches);
          
$decoded.='<br>Flags: <b>0x'.dechex($flags).'</b><br><i>&nbsp; &nbsp; '.implode('<br>&nbsp; &nbsp; ',showMessageFlags($flags)).'</i>';
          
$decoded.='<br>OS Version: <b>'.$osVersion.'</b>';
          
$decoded.='<br>Target Name: <b>'.$targetName.'</b>';
          
$decoded.='<br>Challenge: <b>'.$challenge.'</b>';
          
$decoded.='<br>Context: <b>'.$context.'</b>';
          for (
$i=0;$i<count($targetInformation);)
          {
            switch (
$targetInformation[$i])
            {
              case 
1:  $name='Server';     break;
              case 
2:  $name='Domain';     break;
              case 
3:  $name='DNS Server'; break;
              case 
4:  $name='DNS Domain'; break;
              default: 
$name='Unknown';
            }
            
$decoded.='<br>Target Information '.$name.' (type: <b>'.$targetInformation[$i++].'</b>): <b>'.$targetInformation[$i++].'</b>';
          }
        }
        elseif (
$type==3)
        {
          list(
$flags,$osVersion,$targetName,$userName,$workstationName,$sessionKey,$LMresponse,$NTLMresponse)=decodeMessageType3($matches);
          
$decoded.='<br>Flags: <b>0x'.dechex($flags).'</b><br><i>&nbsp; &nbsp; '.implode('<br>&nbsp; &nbsp; ',showMessageFlags($flags)).'</i>';
          
$decoded.='<br>OS Version: <b>'.$osVersion.'</b>';
          
$decoded.='<br>Target Name: <b>'.$targetName.'</b>';
          
$decoded.='<br>User Name: <b>'.$userName.'</b>';
          
$decoded.='<br>Workstation Name: <b>'.$workstationName.'</b>';
          
$decoded.='<br>Session Key: <b>'.$sessionKey.'</b>';
          
$decoded.='<br>LM Response: <b>'.$LMresponse.'</b>';
          
$decoded.='<br>NTLM Response: <b>'.$NTLMresponse.'</b>';
        }
      }
      
$headersArray[$r][2]=$decoded;
    }
    if (
$startTime===null$startTime=$headersArray[$r][0];
    
$headersArray[$r][0]=round($headersArray[$r][0]-$startTime,4);
    echo 
'<tr'.($r&1?' class="odd"':'').'><td class="lp">'.($r+1).'</td>';
    for (
$c=0;$c<3;$c++)
      echo 
'<td>'.$headersArray[$r][$c].'</td>';
    echo 
'<td class="direct">'.($r&1?'serwer<br>=><br>przeglądarka':'przeglądarka<br>=><br>serwer').'</td></tr>';
  }

function 
decodeMessageType1($data)
{
  
$flags=getWord($data,0x0c)+(getWord($data,0x0e)<<16);
  
$osVersion=getByte($data,0x20,1).'.'.getByte($data,0x21,1).' (build '.getWord($data,0x22).')';
  
$domain=substr($data,getWord($data,0x14),getWord($data,0x10));
  
$host  =substr($data,getWord($data,0x1c),getWord($data,0x18));
  return array(
$flags,$osVersion,$domain,$host);
}

function 
decodeMessageType2($data)
{
  
$targetNameLength=getWord($data,0x0c);
  
$targetNameSpace =getWord($data,0x0e);
  
$targetNameOffset=getWord($data,0x10);
  
$flags=getWord($data,0x14)+(getWord($data,0x16)<<16);
  
$challenge=getHexString($data,0x18,8);
  
$context=getHexString($data,0x20,8);
  
$targetInformationLength=getWord($data,0x28);
  
$targetInformationSpace =getWord($data,0x2a);
  
$targetInformationOffset=getWord($data,0x2c);
  
$osVersion=getByte($data,0x30,1).'.'.getByte($data,0x31,1).' (build '.getWord($data,0x32).')';
  
$targetNameDomain=ntlm_utf16le2utf8(substr($data,$targetNameOffset,$targetNameLength));
  
$targetInformation=array();
  
$offset=$targetInformationOffset;
  while (
true)
  {
    
$type=getWord($data,$offset);
    if (!
$type) break;
    
$length=getWord($data,$offset+2);
    if (
$type<7)
      
$domain=ntlm_utf16le2utf8(substr($data,$offset+4,$length));
    else
      
$domain=getHexString($data,$offset+4,$length);
    
$targetInformation[]=$type;
    
$targetInformation[]=$domain;
    
$offset+=$length+4;
  } 

  return array(
$flags,$osVersion,$targetNameDomain,$challenge,$context,$targetInformation);
}

function 
decodeMessageType3($data)
{
  
$LMresponseOffset  =getWord($data,0x10);
  
$NTLMresponseOffset=getWord($data,0x18);
  
$targetName        =ntlm_utf16le2utf8(substr($data,getWord($data,0x20),getWord($data,0x1c)));
  
$userName          =ntlm_utf16le2utf8(substr($data,getWord($data,0x28),getWord($data,0x24)));
  
$workstationName   =ntlm_utf16le2utf8(substr($data,getWord($data,0x30),getWord($data,0x2c)));
  
$sessionKey=getHexString($data,0x38,4);
  
$flags=getWord($data,0x3c)+(getWord($data,0x3e)<<16);
  
$osVersion=getByte($data,0x40,1).'.'.getByte($data,0x41,1).' (build '.getWord($data,0x42).')';
  
$LMresponse=getHexString($data,$LMresponseOffset,24);
  
$NTLMresponse=getHexString($data,$NTLMresponseOffset,24);
  return array(
$flags,$osVersion,$targetName,$userName,$workstationName,$sessionKey,$LMresponse,$NTLMresponse);
}

function 
showMessageFlags($flags)
{
  
$names=array();
  
$bit31=($flags>0x7fffffff);
  
$flags&=0x7fffffff;
  
$bit=0;
  while (
$flags)
  {
    if (
$flags&1$names[]=messageFlagName($bit);
    
$flags>>=1;
    
$bit++;
  }
  if (
$bit31$names[]=messageFlagName(31);
  return 
$names;
}

function 
messageFlagName($bit)
{
  switch (
$bit)
  {
    case  
0: return 'NEGOTIATE_UNICODE';
    case  
1: return 'NEGOTIATE_OEM';
    case  
2: return 'REQUEST_SERVER_AUTH_REALM';
    case  
4: return 'NEGOTIATE_SIGN';
    case  
5: return 'NEGOTIATE_SEAL';
    case  
6: return 'NEGOTIATE_DATAGRAM_STYLE';
    case  
7: return 'NEGOTIATE_LAN_MANAGER_KEY';
    case  
9: return 'NEGOTIATE_NTLM';
    case 
11: return 'NEGOTIATE_ANONYMOUS';
    case 
12: return 'NEGOTIATE_DOMAIN_SUPPLIED';
    case 
13: return 'NEGOTIATE_WORKSTATION_SUPPLIED';
    case 
14: return 'NEGOTIATE_LOCAL_CALL';
    case 
15: return 'NEGOTIATE_ALWAYS_SIGN';
    case 
16: return 'TARGET_TYPE_DOMAIN';
    case 
17: return 'TARGET_TYPE_SERVER';
    case 
18: return 'TARGET_TYPE_SHARE';
    case 
19: return 'NEGOTIATE_NTLM2';
    case 
23: return 'NEGOTIATE_TARGET_INFO';
    case 
29: return 'NEGOTIATE_128_BIT_ENCRYPTION';
    case 
30: return 'NEGOTIATE_KEY_EXCHANGE';
    case 
31: return 'NEGOTIATE_56_BIT_ENCRYPTION';
    default: return 
'bit #'.$bit;
  }
}

function 
getByte($data,$offset)
{
  return 
ord(substr($data,$offset,1));
}

function 
getWord($data,$offset)
{
  return 
getByte($data,$offset)|getByte($data,$offset+1)<<8;
}

function 
getHexString($data,$offset,$length)
{
  for (
$str='0x';$length--;$offset++)
  {
    
$ord=ord(substr($data,$offset,1));
    
$str.=($ord<16?'0':'').dechex($ord);
  }
  return 
$str;
}
?>
</table>
</body>
</html>