Category Archives: php-ntlm

PHP NTLM integration with Samba

I’ve been sitting on this for ages, but finally I’m using the Christmas break to complete this article about integration between Samba and my PHP NTLM script. The basic idea is that pdbedit -w someuser will emit the NT hash (MD4) of someuser. Using this knowledge we will be able to create a helper utility that verifies NTLMv2 hashes by calling on pdbedit. This helper utility I named verifyntlm. A key requirement for this to work is that the Apache/PHP process be in the same machine the samba user database is hosted on, which also means this is strictly for samba and not for Windows servers.

There is one issue though, which is that pdbedit requires root privileges to access the hash.  It unreasonable to think that the apache/PHP could be run in root. The answer to this is to setuid verifyntlm to root such that verifyntlm will get elevated to root every time it gets executed by any user. It also why verifyntlm is written in C as setuid doesn’t not work for scripts. For this to be secure, verifyntlm must be watertight or else it can potentially be used for a root privilege escalation exploit. Maximum care was taken when coding with this but as with everything, I can’t guarantee absolutely security. The source code is for all to see though, so feel free to comb through it.

You would only use verifyntlm in conjunction with ntlm.php as expects parameters such as the user, challenge, hash etc which only ntlm.php provides. The program is designed to divulge as little information as possible, only 1 if successful, 0 for failure. This way in the event that an attacker gains access to the binary, they can gain no new information that’s not already gained through brute forcing a login prompt.

Prerequisites

verifyntlm.c requires:

  • gcc – To compile the C file
  • openssl (library & headers) – If using debian/ubuntu, run apt-get install libssl-dev
  • Samba – Obviously, so we can execute pdbedit

Installation

You may need to modify PDBEDIT_PATH in verifyntlm.c to point to where pdbedit is if it’s not at /usr/bin/pdbedit

Login as root (or add sudo in front) to compile and set the sticky bit:


# gcc verifyntlm.c -lssl -o verifyntlm
# chown root verifyntlm
# chmod u=rwxs,g=x,o=x verifyntlm

Move the binary to a location such as /sbin/


# mv verifyntlm /sbin

If you put the binary somewhere else, please modify $ntlm_verifyntlmpath in ntlm.php.

Usage

After compiling the binary, jump out of root into a normal user and try running it without parameters. If it prints usage information, then you should be set.

On PHP side, here’s how to use it in your own PHP scripts:

[php]
session_start();
$auth = ntlm_prompt("testwebsite", "testdomain", "mycomputer", "testdomain.local", "mycomputer.local", null, "ntlm_verify_hash_smb");

if ($auth[‘authenticated’]) {
print "You are authenticated as $auth[username] from $auth[domain]/$auth[workstation]";
}
[/php]

As always, get the source from my php-ntlm github.

Unfortunately, if you’re looking to integrate PHP NTLM with Active Directory, this is not the article you’re looking for, you may be able to achieve it through winbind, another samba component, but that’s for another time.

PHP NTLM now working with lighttpd/FastCGI

Previously, the PHP NTLM library relied on apache_request_headers which required mod_php on apache. On many setups including lighttpd, php fastcgi is used instead to execute php scripts. This meant that the ntlm script can’t be used – until now. I’ve modified the script to use the HTTP_AUTHORIZATION server variable available to CGI scripts and fallback to apache_request_headers. I’ve also did some minor fixes include fallback to use the hash() function if mhash is unavailable.

The latest version of the library is available from the php_ntlm github.

NTLM authentication in PHP – Now with NTLMv2 hash checking

A few years ago, I investigated NTLM and PHP and managed to write a simple PHP script that can retrieve the current windows username. However, it was only partly finished as it did not authenticate the user. Inspired by a recent comment, I’ve decided to revisit this problem and solve the authentication issue. For the better part of the afternoon, I wrestled with the NTLMv2 hash checking as detailed here.

It turns out that NT passwords are stored as a MD4 hash of the UTF16LE password string. So once we have this hash, we can begin verifying passwords. Obtaining this hash for a user is relatively easy with Samba, but seems to pose a challenge on Windows. In a later blog post I will detail how to integrate PHP authentication with Samba. For now, the code will assume you have already obtained a database of your users MD4 hashed passwords.

The crux of the NTLMv2 authentication involves using HMAC-MD5 on challenges and nonces using the MD4 hashed password as the key. The result is a 150 line source code that perform authentication on clients supporting NTLMv2. On the support NTLMv2, Internet Explorer supports it fine. Firefox on the other hand only has limited support for NTLMv2. In Firefox on Windows, if you have whitelisted your server with network.automatic-ntlm-auth.trusted-uris, Firefox will attempt to use Windows’ SSPI support (sys-ntlm) to perform single sign on. The SSPI module supports NTLMv2 fine. However, if you are using Firefox’s own cross platform NTLM module, you’re out of luck, it only supports the legacy NTLM and LM hashes. Perhaps it will support NTLMv2 in the future.

For Internet Explorer 8, intranet settings are now off by default, which means single sign on won’t automatically activate. To fix this, you should see a yellow bar prompting you whether to apply intranet settings.

The php ntlm authentication library is available here: PHP NTLM Github

To use it just put

[php]
include(‘ntlm.php’);

function get_ntlm_user_hash($user) {
$userdb = array(‘loune’=>’test’, ‘you’=>’gg’, ‘a’=> ‘a’);

if (!isset($userdb[strtolower($user)]))
return false;
return mhash(MHASH_MD4, ntlm_utf8_to_utf16le($userdb[strtolower($user)]));
}

session_start();
$auth = ntlm_prompt("testwebsite", "testdomain", "mycomputer", "testdomain.local", "mycomputer.local", "get_ntlm_user_hash");

if ($auth[‘authenticated’]) {
print "You are authenticated as $auth[username] from $auth[domain]/$auth[workstation]";
}
[/php]

You need to provide your own implementation of the callback function get_ntlm_user_hash($user) which should return the MD4/Unicode hashed password of the requested $user. You can get that by doing mhash(MHASH_MD4, ntlm_utf8_to_utf16le("password")). You also need session_start() as the script needs to persist challenge information across http requests.

Next time, I will blog about the best way to integrate it with Samba on Linux.

Simple lightweight NTLM in PHP

Many months ago I made a PHP script that could read NTLM authentication information from your browser. What’s NTLM? Basically, if you’re using Microsoft Windows, your browser can automatically send your windows login information to a website (if you agree to it). This means that without needing to enter additional username or passwords, you can be authenticated at the website you’re visiting. This is quite convenient especially for company intranets. NTLM should work with all major browsers (Internet Explorer, Firefox and Opera).

The PHP code I wrote is simple and can be inserted into the top of any PHP script. The key output is $user $domain $workstation, which is the information advertised by the user. Be warned though, the script does NOT authenticate the user and merely assumes that the user is who they say they are. This is akin to a user entering only a username with no password required. I plan to add password/hash verification possibly in conjuction with samba in the future.

A limitation is that the PHP script relies on apache_request_headers() which is only available if you run PHP as a apache module. (Update 2010, newer code doesn’t have this issue)

[php]
<?php

// loune 25/3/2006, updated 22/08/2009
// For more information see:
// http://siphon9.net/loune/2007/10/simple-lightweight-ntlm-in-php/
//
// This script is obsolete, you should see
// http://siphon9.net/loune/2009/09/ntlm-authentication-in-php-now-with-ntlmv2-hash-checking/
//

// NTLM specs http://davenport.sourceforge.net/ntlm.html

$headers = apache_request_headers();

if (!isset($headers[‘Authorization’])){
header(‘HTTP/1.1 401 Unauthorized’);
header(‘WWW-Authenticate: NTLM’);
exit;
}

$auth = $headers[‘Authorization’];

if (substr($auth,0,5) == ‘NTLM ‘) {
$msg = base64_decode(substr($auth, 5));
if (substr($msg, 0, 8) != "NTLMSSP\x00")
die(‘error header not recognised’);

if ($msg[8] == "\x01") {
$msg2 = "NTLMSSP\x00\x02\x00\x00\x00".
"\x00\x00\x00\x00". // target name len/alloc
"\x00\x00\x00\x00". // target name offset
"\x01\x02\x81\x00". // flags
"\x00\x00\x00\x00\x00\x00\x00\x00". // challenge
"\x00\x00\x00\x00\x00\x00\x00\x00". // context
"\x00\x00\x00\x00\x00\x00\x00\x00"; // target info len/alloc/offset

header(‘HTTP/1.1 401 Unauthorized’);
header(‘WWW-Authenticate: NTLM ‘.trim(base64_encode($msg2)));
exit;
}
else if ($msg[8] == "\x03") {
function get_msg_str($msg, $start, $unicode = true) {
$len = (ord($msg[$start+1]) * 256) + ord($msg[$start]);
$off = (ord($msg[$start+5]) * 256) + ord($msg[$start+4]);
if ($unicode)
return str_replace("\0", ”, substr($msg, $off, $len));
else
return substr($msg, $off, $len);
}
$user = get_msg_str($msg, 36);
$domain = get_msg_str($msg, 28);
$workstation = get_msg_str($msg, 44);

print "You are $user from $domain/$workstation";
}
}

?>
[/php]

If you try the script in Firefox (on windows), you will notice that you get prompted for a username and password when encountering an NTLM challenge. This is because sending your windows credentials to any unscrupulous website poses a real security risk. To make it automatically use your windows credentials for sites you trust, you can add the website to a whitelist.

The whitelist is located at Firefox’s about:config (type that into the address bar), which allows the editing of all of the browser’s preferences. Find the preference entry network.automatic-ntlm-auth.trusted-uris, double click on it and type the hostname of the site (ie http://www.abc.com) that you want in your whitelist. Multiple entries are seperated by commas. After doing that, Firefox should send your windows creds automatically.

Update 20/09/2009. The above script is outdated, anyone wishing to use NTLM should see the new post: Part 2 – Now with hash checking