pedant
Goto Top

NT-Password (NT Hash) unter Windows generieren

Hallo,

es muss doch irgendwie möglich sein ein NT-Password (einen NT-Hash) unter Windows zu erzeugen!?

Es ist doch ein "Windows-Ding":
NT-Password = Windows NT hashed passwords
Quelle: https://freeradius.org/radiusd/man/rlm_pap.txt

Die einzigen Möglichkeiten des Erzeugens, die ich gefunden habe, sind entweder nur online oder unter Linux.
Online: https://tobtu.com/lmntlm.php
Linux: /usr/bin/smbencrypt "geheim"

Ich benötige es, weil ich einen Radiusserver betreiben möchte, der auf einer LinuxVM läuft, sich aber die Zugangsdaten aus einer MySQL-Datenbank seines Windows-Hosts holen soll.
Ich möchte die Eingabe und Pflege der Zugangsdaten, die in der selbstentworfenen MySQL-Datenbank gespeichert werden, unter Windows (per selbstgeschriebenen Webinterface) durchführen.
Weniger aus Sicherheitsgründen, mehr aus Gründen der Diskretion, möchte ich die Kennwörter nicht im Klartext in der Datenbank ablegen.
(Ich möchte nicht sehen können, wer welches Kennwort nutzt.)

Das kompatibelste Hash-Format ist NT-Password (NT-Hash)
Quelle: http://deployingradius.com/documents/protocols/compatibility.html

Was ich Suche ist ein offline Hash-Generator für Windows.
Das kann gerne eine der folgenden Ansätze sein:
- exe
- PowerShell
- php
- MySQL
- JavaScript
- eventuell Sonstiges

Ich habe mir jetzt einen Workaroud geschrieben, der nicht gerade das Ei des Kolumbus ist (bestenfalls sein Linkes).


Workaround

Auf der Linux-VM namens "linuxvm" habe ich Apache 2.4 und php 7.0 installiert.

Im Webroot der Linux-VM habe ich ein php-Skript abgelegt, das ein per Get übergegenes Kennwort mit exex() an das Tool /usr/bin/smbencrypt übergibt.
Die Ausgabe des php-Skripts ist inhaltlich wie eine php-Datei, die lediglich eine Variable zuweist.

Auf dem Windows-Rechner ist ebenfalls Apache und php installiert.
Zusätzlich ist dort das Tool "WGet" vorhanden.
Auf dem Windowsrechner liegt im Web-Ordner ein php-Skript, das ein per Get übergegenes Kennwort mit exex() an das Tool wget.exe übergibt.
WGet ruft damit das php-Script der Linux-VM auf und speichert dessen Ausgabe in eine temporäre php-Datei, die im Skript per include eingebunden und gleich wieder gelöscht wird.

Ab dann steht dem php-Skript auf dem Windowsrechner das gehashte Kennwort in einer Variablen namens "verschluesselt" zur weiteren Verarbeitung zur Verfügung.


php-Skript "nthash.php" auf der Linux-VM
<?php
$kennwort="";  
$kennwort=$_GET['kennwort'];  
if ($kennwort == "") {  
	die;
};
$modus="";  
$modus=$_GET['modus'];  
if ($modus != "php") {  
	$modus = "show";  
}

$befehl="/usr/bin/smbencrypt \"".$kennwort."\"";  
exec($befehl, $ausgabe, $fehler);
if ($fehler != 0) {
	die;
};

$teile = explode("\t", $ausgabe);  
$verschluesselt=$teile[1];

if ($modus == "show") {  
echo $verschluesselt;
}
if ($modus == "php") {  
echo "<?php \$verschluesselt=\"$verschluesselt\" ?>";  
}
?>
Beim Aufruf per http://linuxvm/nthash.php?kennwort=geheim&modus=php
wird Folgendes generiert:
<?php $verschluesselt="C2AE1FE6E648846352453E816F2AEB93" ?>


php-Skript "nthash.php" auf dem Windows-Host
<?php
$kennwort="";  
$kennwort=$_GET['kennwort'];  
if ($kennwort == "") {  
die;
}

// WGet mit Parametern vorbereiten
$url="http://linuxvm/nthash.php?kennwort=".$kennwort."^&modus=php";  
$nthashtmp="D:\www\webroot\Test\\nthashtmp.php";  
$befehl="\"C:\Program Files (x86)\Tools\WGET\WGET.EXE\" ".$url." -O ".$nthashtmp;  
// Achtung:
//  Enthält ein Pfad "\t" oder "\n" so ist entweder "\\t" bzw. "\\n" anzugeben  
//  oder Großbuchstaben zu verwenden: "\T" bzw. "\N", damit nicht Tab oder NewLine daraus wird.  

//Ausgabe zur Überprüfung
//echo "nthashtmp: ".$nthashtmp."<br>\n";  
//echo "Befehl   : ".$befehl."<br>\n";  

// php-Datei des Linux-Rechners mit WGet aufrufen
exec($befehl, $ausgabe, $fehler);

// Erzeugte Datei einbinden und dann löschen
if ($fehler == 0) {
	include $nthashtmp;
	unlink($nthashtmp);
}
else {
	$verschluesselt="Fehler";  
}

// Ergebnis ausgeben (alternativ mit der Variablen andere Dinge machen)
echo $verschluesselt."\n";  
?>

Beim Aufruf per http://windowshost/nthash.php?kennwort=geheim
steht im Skript die Variable "verschluesselt" mit dem NT-Hash als Inhalt, zur Verfügung.
Resultat:
$verschluesselt="C2AE1FE6E648846352453E816F2AEB93"
(Kennwort "geheim" wir zu "C2AE1FE6E648846352453E816F2AEB93")

...und schon ist die Aufgabe erledigt.
(Die Eingangs erwähnte Diskretion sollte mich noch dazu veranlassen die Übergaben des Kennworts per Post und nicht per Get auszuführen.)

Mit der Behauptung "Das muss auch einfacher gehen" stehe ich hoffentlich nicht alleine da.

Gruß Frank

Content-Key: 364398

Url: https://administrator.de/contentid/364398

Printed on: June 23, 2024 at 21:06 o'clock

Member: wiesi200
Solution wiesi200 Feb 10, 2018 updated at 14:03:25 (UTC)
Goto Top
Hallo,

hab das jetzt vor ein Paar wochen auch gemacht, für ein Mitarbeiter W-LAN. Nur Liegt das Webfrontend (PHP) auf nem Linux Server.
Ich such's kurz raus und Schick es dir.

function NTLMHash($Input) {
  // Convert the password from UTF8 to UTF16 (little endian)
  $Input=iconv('UTF-8','UTF-16LE',$Input);  

  // Encrypt it with the MD4 hash
  $MD4Hash=bin2hex(mhash(MHASH_MD4,$Input));

  // You could use this instead, but mhash works on PHP 4 and 5 or above
  // The hash function only works on 5 or above
  //$MD4Hash=hash('md4',$Input);  

  // Make it uppercase, not necessary, but it's common to do so with NTLM hashes  
  $NTLMHash=strtoupper($MD4Hash);

  // Return the result
  return($NTLMHash);
}

Wo ich das gefunden hab, kann ich leider nicht mehr sagen.
Mitglied: 135333
135333 Feb 10, 2018 updated at 14:33:53 (UTC)
Goto Top
Powershell:
Get-MD4Hash

Gruß snap
Member: Pedant
Pedant Feb 10, 2018 at 15:53:26 (UTC)
Goto Top
Hallo wiesi200,

danke für den Code.

Ich habe ihn hier eingesetzt und ausprobiert.
nthash.php
<?php
$kennwort="";  
$kennwort=$_GET['kennwort'];  
if ($kennwort == "") {  
die;
}

// Convert the password from UTF8 to UTF16 (little endian)
$Input=iconv('UTF-8','UTF-16LE',$Input);  
// Encrypt it with the MD4 hash
//$MD4Hash=bin2hex(mhash(MHASH_MD4,$Input));
// You could use this instead, but mhash works on PHP 4 and 5 or above
// The hash function only works on 5 or above
$MD4Hash=hash('md4',$Input);  
// Make it uppercase, not necessary, but it's common to do so with NTLM hashes 
$verschluesselt=strtoupper($MD4Hash);
// Return the result

// Ergebnis ausgeben (alternativ mit der Variablen andere Dinge machen)
echo $verschluesselt."\n";  
?>
Aufruf: http://windowshost/nthash.php?kennwort=geheim

Hier erhalte ich für "geheim"
31D6CFE0D16AE931B73C59D7E0C089C0
und nicht
C2AE1FE6E648846352453E816F2AEB93

Verwende ich mhash() statt hash() erhalte ich:
Fatal error: Call to undefined function mhash() in D:\www\webroot\test\nthash2.php on line 11
Installiert ist hier: PHP Version 5.2.10


Hallo snap,

danke für den PowerShell-Link.

In Bezug auf PS bin ich noch sehr unbedarft.
Ich habe den dortigen Code in eine Datei kopiert und diese Get-MD4Hash.ps genannt.
Rufe ich sie dann auf:
PS D:\Temp> ./Get-MD4Hash -DataToHash $([Text.Encoding]::Unicode.GetBytes("geheim"))
erhalte ich:
Fehler beim Ausführen des Programms "Get-MD4Hash.ps": Der angegebenen Datei ist keine Anwendung zugeordnetIn Zeile:1
Zeichen:1
+ ./Get-MD4Hash.ps -DataToHash $([Text.Encoding]::Unicode.GetBytes("geh ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.
In Zeile:1 Zeichen:32
+ ... MD4Hash.ps -DataToHash $([Text.Encoding]::Unicode.GetBytes("geheim"))
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (:) , ApplicationFailedException
    + FullyQualifiedErrorId : NativeCommandFailed

Eine Funktion ist also noch kein Skript.
"Schade" dachte sich da der Ochs' vorm Berg.

Gruß Frank
Mitglied: 135333
Solution 135333 Feb 10, 2018 updated at 16:14:17 (UTC)
Goto Top
Zitat von @Pedant:
In Bezug auf PS bin ich noch sehr unbedarft.
Ich habe den dortigen Code in eine Datei kopiert und diese Get-MD4Hash.ps genannt.
Rufe ich sie dann auf:
PS D:\Temp> ./Get-MD4Hash -DataToHash $([Text.Encoding]::Unicode.GetBytes("geheim"))
erhalte ich:
Das ist falsch. Die PS1 muss ja erst mal so aufgerufen werden damit die Funktion der aktuellen Powershell-Konsole bekannt ist.

Mach es folgendermaßen, kopiere den u.s. Code in eine PS1 und dann rufst du diese in der Powershell so auf
.\script.ps1 'Geheim'

param(
    [parameter(mandatory=$true)][string]$password
)

Function Get-MD4Hash
{
<#
.SYNOPSIS
    This cmdlet returns the MD4 hash of the data that is input.
    WARNING: MD4 is not secure, so it should NEVER be used to
    protect sensitive data. This cmdlet is for research purposes only!
 
.DESCRIPTION
    This cmdlet returns the MD4 hash of the data that is input.
    WARNING: MD4 is not secure, so it should NEVER be used to
    protect sensitive data. This cmdlet is for research purposes only!
    This cmdlet uses Microsoft's implementation of MD4, exported  
    from bcrypt.dll. The implementation is fully compliant with
    RFC 1320. This cmdlet takes a byte array as input, not a string.
    So if you wanted to hash a string (such as a password,) you
    need to convert it to a byte array first.
 
.EXAMPLE
    Get-MD4Hash -DataToHash $([Text.Encoding]::Unicode.GetBytes("YourPassword1!"))  
 
.PARAMETER DataToHash
    A byte array that represents the data that you want to hash.
 
.INPUTS
    A byte array containing the data you wish to hash.
 
.OUTPUTS
    A 128-bit hexadecimal string - the MD4 hash of your data.
 
.NOTES
    Author: Ryan Ries, 2014, ryan@myotherpcisacloud.com
 
.LINK
    https://myotherpcisacloud.com
#>
    [CmdletBinding()]
    Param ([Parameter(Mandatory=$True, ValueFromPipeline=$False)]          
           [Byte[]]$DataToHash)
    END
    {       
        Set-StrictMode -Version Latest
        Add-Type -TypeDefinition @'  
        using System;
        using System.Text;
        using System.Runtime.InteropServices;
        public class BCrypt
        {
            [DllImport("bcrypt.dll", CharSet = CharSet.Auto)]  
            public static extern NTStatus BCryptOpenAlgorithmProvider(
                [Out] out IntPtr phAlgorithm,
                [In] string pszAlgId,
                [In, Optional] string pszImplementation,
                [In] UInt32 dwFlags);
 
            [DllImport("bcrypt.dll")]  
            public static extern NTStatus BCryptCloseAlgorithmProvider(
                [In, Out] IntPtr hAlgorithm,
                [In] UInt32 dwFlags);
 
            [DllImport("bcrypt.dll", CharSet = CharSet.Auto)]  
            public static extern NTStatus BCryptCreateHash(
                [In, Out] IntPtr hAlgorithm,
                [Out] out IntPtr phHash,
                [Out] IntPtr pbHashObject,
                [In, Optional] UInt32 cbHashObject,
                [In, Optional] IntPtr pbSecret,
                [In] UInt32 cbSecret,
                [In] UInt32 dwFlags);
 
            [DllImport("bcrypt.dll")]  
            public static extern NTStatus BCryptDestroyHash(
                [In, Out] IntPtr hHash);
 
            [DllImport("bcrypt.dll")]  
            public static extern NTStatus BCryptHashData(
                [In, Out] IntPtr hHash,
                [In, MarshalAs(UnmanagedType.LPArray)] byte pbInput,
                [In] int cbInput,
                [In] UInt32 dwFlags);
 
            [DllImport("bcrypt.dll")]  
            public static extern NTStatus BCryptFinishHash(
                [In, Out] IntPtr hHash,
                [Out, MarshalAs(UnmanagedType.LPArray)] byte pbInput,
                [In] int cbInput,
                [In] UInt32 dwFlags);
 
            [Flags]
            public enum AlgOpsFlags : uint
            {           
                BCRYPT_PROV_DISPATCH = 0x00000001,
                BCRYPT_ALG_HANDLE_HMAC_FLAG = 0x00000008,
                BCRYPT_HASH_REUSABLE_FLAG = 0x00000020
            }
 
            // This is a gigantic enum and I don't want to copy all of it into this Powershell script. 
            // Basically anything other than zero means something went wrong.
            public enum NTStatus : uint
            {
                STATUS_SUCCESS = 0x00000000
            }
        }
'@  
 
        [Byte[]]$HashBytes   = New-Object Byte 16
        [IntPtr]$PHAlgorithm = [IntPtr]::Zero
        [IntPtr]$PHHash      = [IntPtr]::Zero
        $NTStatus = [BCrypt]::BCryptOpenAlgorithmProvider([Ref] $PHAlgorithm, 'MD4', $Null, 0)  
        If ($NTStatus -NE 0)
        {
            Write-Error "BCryptOpenAlgorithmProvider failed with NTSTATUS $NTStatus"  
            If ($PHAlgorithm -NE [IntPtr]::Zero)
            {
                $NTStatus = [BCrypt]::BCryptCloseAlgorithmProvider($PHAlgorithm, 0)
            }
            Return
        }
        $NTStatus = [BCrypt]::BCryptCreateHash($PHAlgorithm, [Ref] $PHHash, [IntPtr]::Zero, 0, [IntPtr]::Zero, 0, 0)
        If ($NTStatus -NE 0)
        {
            Write-Error "BCryptCreateHash failed with NTSTATUS $NTStatus"  
            If ($PHHash -NE [IntPtr]::Zero)
            {
                $NTStatus = [BCrypt]::BCryptDestroyHash($PHHash)               
            }
            If ($PHAlgorithm -NE [IntPtr]::Zero)
            {
                $NTStatus = [BCrypt]::BCryptCloseAlgorithmProvider($PHAlgorithm, 0)
            }
            Return
        }
 
        $NTStatus = [BCrypt]::BCryptHashData($PHHash, $DataToHash, $DataToHash.Length, 0)
        $NTStatus = [BCrypt]::BCryptFinishHash($PHHash, $HashBytes, $HashBytes.Length, 0)
 
        If ($PHHash -NE [IntPtr]::Zero)
        {
            $NTStatus = [BCrypt]::BCryptDestroyHash($PHHash)
        }
        If ($PHAlgorithm -NE [IntPtr]::Zero)
        {
            $NTStatus = [BCrypt]::BCryptCloseAlgorithmProvider($PHAlgorithm, 0)
        }
         
        $HashString = New-Object System.Text.StringBuilder
        Foreach ($Byte In $HashBytes)
        {
            [Void]$HashString.Append($Byte.ToString("X2"))  
        }
        Return $HashString.ToString()
    }
}
Get-MD4Hash -DataToHash ([System.Text.Encoding]::Unicode.getBytes($password))
p.s. mit den Daten aus der Funktion lässt sich auch schnell eine EXE z.B. mit c# schreiben wenn man möchte.
Member: wiesi200
wiesi200 Feb 10, 2018 at 16:16:20 (UTC)
Goto Top
Hallo,

ich hab's grad getestet.
Bei mir kommt
C2AE1FE6E648846352453E816F2AEB93 

Unter Linux mit PHP 7.0.23
Und auch unter Windows PHP 5.6.28
Mitglied: 135333
135333 Feb 10, 2018 updated at 16:33:59 (UTC)
Goto Top
Ich schätze mal das kommt vom unterschiedlichen PHP-Encoding seiner PHP-Umgebung. Er sollte prüfen ob alles auf UTF-8 kodiert vorliegt/eingestellt ist. Oder eben das Input-Encoding mit mb_http_input auslesen und an iconv übergeben.
Member: Pedant
Pedant Feb 10, 2018 at 16:47:06 (UTC)
Goto Top
Hallo snap,

prima, danke.
So funktioniert's.
PS D:\Temp> ./ntcrypt.ps1 geheim
C2AE1FE6E648846352453E816F2AEB93


Hallo wiesi200,

bei mir funktioniert es jetzt auch.
Es war lediglich Schlamperei meinerseits.
Ich hatte den Get-Parameter in $kennwort gespeichert, aber dann unverändert mit $Input gearbeitet.
$Input war dann aber unbelegt und damit wurde dann "Nichts" gehasht.

Hier der korrigierte Code:
nthash.php
<?php
$kennwort="";  
$kennwort=$_GET['kennwort'];  
if ($kennwort == "") {  
die;
}

// Convert the password from UTF8 to UTF16 (little endian)
$kennwort=iconv('UTF-8','UTF-16LE',$kennwort);  
// Encrypt it with the MD4 hash
//$MD4Hash=bin2hex(mhash(MHASH_MD4,$kennwort));
// You could use this instead, but mhash works on PHP 4 and 5 or above
// The hash function only works on 5 or above
$MD4Hash=hash('md4',$kennwort);  
// Make it uppercase, not necessary, but it's common to do so with NTLM hashes 
$verschluesselt=strtoupper($MD4Hash);
// Return the result

// Ergebnis ausgeben (alternativ mit der Variablen andere Dinge machen)
echo $verschluesselt."\n";  
?>

Aufruf per http://windowshost/nthash.php?kennwort=geheim
C2AE1FE6E648846352453E816F2AEB93


Euch Beiden recht herzlichen Dank
Gruß Frank