Skip to content

Instantly share code, notes, and snippets.

@Ksnk
Created July 6, 2016 09:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ksnk/cea7c5dea4491952ea9de358d025bd75 to your computer and use it in GitHub Desktop.
Save Ksnk/cea7c5dea4491952ea9de358d025bd75 to your computer and use it in GitHub Desktop.
NTLM page authorization
<?php
// this user allow to pass
$config=array(
'domain'=>'WEB-server', // Вставлять в окно ввода : WEB-server\Юзер Пароль
'user'=> 'Юзер', // name of user
'password' => 'Пароль', // password of registered user
'forceNTLM' => false , // обязательно ли требовать авторизацию?
);
session_start();
ob_start();
$ntlm=new NTLM_service($config);
header('Content-type: text/html; charset=utf-8');
if(false===$ntlm->checkAuth()){
$ntlm->sendHeaders();
ob_end_flush();
exit;
}
if($_SERVER['REQUEST_METHOD']=='POST'){
if(isset($_POST['logout']) && $ntlm->checkAuth()){
$ntlm->sendHeaders(true);
exit;
}
if(isset($_POST['login']) && !$ntlm->checkAuth()){
$ntlm->sendHeaders(true);
exit;
}
}
if(!function_exists('hash_hmac')){
echo 'hash_hmac function not found. So you can\'t use NTLM here. :(<br>';
}
if(!$ntlm->checkAuth()){
// выводим незащищенный контент
?>
Unprotected page<br>
<form method="POST" action="">
<input type="submit" name="login" value="login">
</form>
<?php
} else {// выводим защищенный контент
?>
Hello <?=$ntlm->config['user']?>(<?=$ntlm->config['workstation']?>)
<form method="POST" action="">
<input type="submit" name="logout" value="logout">
</form>
<?php
}
/**
* Отладочная функция - дамп бинарной строки со строковым дополнением
* @param $str
* @param string $label
* @return string
*/
function hex_dump($str,$label='')
{
return
(empty($label)?'':$label.'('.strlen($str).'): ').
chunk_split(bin2hex($str),2," ").
' | '.iconv('cp1251','utf-8//ignore',preg_replace('#[\x00-\x20,\xff]#', '.', $str));
}
class NTLM_service {
var $_config;
private
$retCode=false;
const
NTLM_401_UNAUTHORIZED = 1,
NTLM_NEXT_STEP = 2,
NTLM_SUCCESS = 3;
/**
* get all headers.
*/
function header(){
if(function_exists('apache_request_headers'))
return apache_request_headers();
$headers = array();
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) == "HTTP_") {
$key = str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", substr($key, 5)))));
$headers[$key] = $value;
} else {
$headers[$key] = $value;
}
}
return $headers;
}
/**
* start or continue authorisation
* @param string $code
*/
function sendHeaders($code=''){
if(!empty($code)) $code=self::NTLM_401_UNAUTHORIZED;
else $code=$this->retCode;
switch($code){
case self::NTLM_401_UNAUTHORIZED:
header('HTTP/1.1 401 Unauthorized'); // требуем от клиента авторизации
header('WWW-Authenticate: NTLM'); // тип требуемой авторизации - NTLM
break;
case self::NTLM_NEXT_STEP:
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: NTLM ' . base64_encode($this->Type2())); // отправляем сообщение type-2
break;
}
}
function __construct($config){
if(isset($_SESSION['ntlm']) && is_array($_SESSION['ntlm'])){
$config=array_merge($config,$_SESSION['ntlm']);
};
$this->config=$config;
}
function store(){
$_SESSION['ntlm']=$this->config;
}
function random8(){
$result='';
for($i=0;$i<8;$i++) $result.=chr(rand(0,255));
return $result;
}
function Type2(){
$this->config['server_challenge']=$this->random8();
$this->store();
return "NTLMSSP\x00" . // протокол // 0-
"\x02" /* номер этапа */ . "\x00\x00\x00\x00\x00\x00\x00" . //8-
"\x28\x00" /* общая длина сообщения */ . "\x00\x00" . //16
"\x01\x82" /* признак */ . "\x00\x00" . // 20
$this->config['server_challenge'] . // 24-8 -challenge
"\x00\x00\x00\x00\x00\x00\x00\x00";
}
function checkAuth(){
if(empty($this->retCode)){
$headers=$this->header();
$this->retCode=self::NTLM_401_UNAUTHORIZED;
if (isset($headers['Authorization']) && substr($headers['Authorization'], 0, 5) == 'NTLM ') {
// проверяем, что это NTLM-аутентификация
$chain = base64_decode(substr($headers['Authorization'], 5)); // получаем декодированное значение
switch (ord($chain{8})) {
case 1:
if (ord($chain{13}) == 0x82 || ord($chain{13}) == 0xB2) { // проверяем признак NTLM 0x82 по смещению 13 в сообщении type-1:
$this->retCode=self::NTLM_NEXT_STEP;
}
break;
case 3:
$respond=array('LM_resp'=>false, 'NT_resp'=>false, 'domain'=>true, 'user'=>true, 'workstaion'=>true);
$k=0;
$offset=0; $length=0;
foreach ($respond as $v => $vv) {
extract(unpack('vlength/voffset', substr($chain, $k * 8 + 14, 4)));
$respond[$v] = substr($chain, $offset, $length);
if($vv)
$respond[$v]=iconv('UTF-16LE','UTF-8',$respond[$v]);
$k++;
}
$blob=substr($respond['NT_resp'],16);
//echo 'domain:'. $respond['domain']."<br>\n";
//echo 'user:'. $respond['user']."<br>\n";
//echo 'workstaion:'. $respond['workstaion']."<br>\n";
//echo hex_dump($respond['NT_resp'],'NT_resp')."<br>\n";
$start_hash=hash_hmac('md5',$this->config['server_challenge'].$blob,
hash_hmac('md5',iconv('UTF-8', 'UTF-16LE',mb_strtoupper($this->config['user'],'UTF-8').$this->config['domain']),
hash('md4',iconv('UTF-8', 'UTF-16LE',$this->config['password']),true),
true),
true);
//echo hex_dump($start_hash,'hash')."<br>\n";
$this->config['workstation']=$respond['workstaion'];
if($start_hash==substr($respond['NT_resp'],0,16)){
$this->retCode=self::NTLM_SUCCESS;
}
break;
}
}
}
return
$this->retCode==self::NTLM_401_UNAUTHORIZED?0:
($this->retCode==self::NTLM_NEXT_STEP?false:true);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment