1--- 2-- This module takes care of the authentication used in SMB (LM, NTLM, LMv2, NTLMv2). 3-- 4-- There is a lot to this functionality, so if you're interested in how it works, read 5-- on. 6-- In SMB authentication, there are two distinct concepts. Each will be dealt with 7-- separately. There are: 8-- * Stored hashes 9-- * Authentication 10-- 11-- What's confusing is that the same names are used for each of those. 12-- 13-- Stored Hashes: 14-- Windows stores two types of hashes: Lanman and NT Lanman (or NTLM). Vista and later 15-- store NTLM only. Lanman passwords are divided into two 7-character passwords and 16-- used as a key in DES, while NTLM is converted to unicode and MD4ed. 17-- 18-- The stored hashes can be dumped in a variety of ways (pwdump6, fgdump, Metasploit's 19-- <code>priv</code> module, <code>smb-psexec.nse</code>, etc). Generally, two hashes are dumped together 20-- (generally, Lanman:NTLM). Sometimes, Lanman is empty and only NTLM is given. Lanman 21-- is never required. 22-- 23-- The password hashes can be given instead of passwords when supplying credentials; 24-- this is done by using the <code>smbhash</code> argument. Either a pair of hashes 25-- can be passed, in the form of Lanman:NTLM, or a single hash, which is assumed to 26-- be NTLM. 27-- 28-- Authentication: 29-- There are four types of authentication. Confusingly, these have the same names as 30-- stored hashes, but only slight relationships. The four types are Lanmanv1, NTLMv1, 31-- Lanmanv2, and NTLMv2. By default, Lanmanv1 and NTLMv1 are used together in most 32-- applications. These Nmap scripts default to NTLMv1 alone, except in special cases, 33-- but it can be overridden by the user. 34-- 35-- Lanmanv1 and NTLMv1 both use DES for their response. The DES mixes a server challenge 36-- with the hash (Lanman hash for Lanmanv1 response and NTLMv1 hash for NTLM response). 37-- The way the challenge is DESed with the hashes is identical for Lanmanv1 and NTLMv1, 38-- the only difference is the starting hash (Lanman vs NTLM). 39-- 40-- Lanmanv2 and NTLMv2 both use HMAC-MD5 for their response. The HMAC-MD5 mixes a 41-- server challenge and a client challenge with the NTLM hash, in both cases. The 42-- difference between Lanmanv2 and NTLMv2 is the length of the client challenge; 43-- Lanmanv2 has a maximum client challenge of 8 bytes, whereas NTLMv2 doesn't limit 44-- the length of the client challenge. 45-- 46-- The primary advantage to the 'v2' protocols is the client challenge -- by 47-- incorporating a client challenge, a malicious server can't use a precomputation 48-- attack. 49-- 50-- In addition to hashing the passwords, messages are also signed, by default, if a 51-- v1 protocol is being used (I (Ron Bowes) couldn't get signatures to work on v2 52-- protocols; if anybody knows how I'd love to implement it). 53-- 54--@args smbusername The SMB username to log in with. The forms "DOMAIN\username" and "username@DOMAIN" 55-- are not understood. To set a domain, use the <code>smbdomain</code> argument. 56--@args smbdomain The domain to log in with. If you aren't in a domain environment, then anything 57-- will (should?) be accepted by the server. 58--@args smbpassword The password to connect with. Be cautious with this, since some servers will lock 59-- accounts if the incorrect password is given. Although it's rare that the 60-- Administrator account can be locked out, in the off chance that it can, you could 61-- get yourself in trouble. To use a blank password, leave this parameter off 62-- altogether. 63--@args smbhash A password hash to use when logging in. This is given as a single hex string (32 64-- characters) or a pair of hex strings (both 32 characters, optionally separated by a 65-- single character). These hashes are the LanMan or NTLM hash of the user's password, 66-- and are stored on disk or in memory. They can be retrieved from memory 67-- using the fgdump or pwdump tools. 68--@args smbtype The type of SMB authentication to use. These are the possible options: 69-- * <code>v1</code>: Sends LMv1 and NTLMv1. 70-- * <code>LMv1</code>: Sends LMv1 only. 71-- * <code>NTLMv1</code>: Sends NTLMv1 only (default). 72-- * <code>v2</code>: Sends LMv2 and NTLMv2. 73-- * <code>LMv2</code>: Sends LMv2 only. 74-- * <code>NTLMv2</code>: Doesn't exist; the protocol doesn't support NTLMv2 alone. 75-- The default, <code>NTLMv1</code>, is a pretty decent compromise between security and 76-- compatibility. If you are paranoid, you might want to use <code>v2</code> or 77-- <code>lmv2</code> for this. (Actually, if you're paranoid, you should be avoiding this 78-- protocol altogether!). If you're using an extremely old system, you might need to set 79-- this to <code>v1</code> or <code>lm</code>, which are less secure but more compatible. 80-- For information, see <code>smbauth.lua</code>. 81--@args smbnoguest Use to disable usage of the 'guest' account. 82 83local nmap = require "nmap" 84local stdnse = require "stdnse" 85local string = require "string" 86local table = require "table" 87local unicode = require "unicode" 88local unittest = require "unittest" 89_ENV = stdnse.module("smbauth", stdnse.seeall) 90 91local have_ssl, openssl = pcall(require, "openssl") 92 93-- Constants 94local NTLMSSP_NEGOTIATE = 0x00000001 95local NTLMSSP_CHALLENGE = 0x00000002 96local NTLMSSP_AUTH = 0x00000003 97 98local session_key = string.rep("\0", 16) 99 100-- Types of accounts (ordered by how useful they are 101local ACCOUNT_TYPES = { 102 ANONYMOUS = 0, 103 GUEST = 1, 104 USER = 2, 105 ADMIN = 3 106} 107 108local function account_exists(host, username, domain) 109 if(host.registry['smbaccounts'] == nil) then 110 return false 111 end 112 113 for i, j in pairs(host.registry['smbaccounts']) do 114 if(j['username'] == username and j['domain'] == domain) then 115 return true 116 end 117 end 118 119 return false 120end 121 122--- Try the next stored account for this host 123-- @param host The host table 124-- @param num If nil, the next account is chosen. If a number, the account at 125-- that index is chosen 126function next_account(host, num) 127 if(num == nil) then 128 if(host.registry['smbindex'] == nil) then 129 host.registry['smbindex'] = 1 130 else 131 host.registry['smbindex'] = host.registry['smbindex'] + 1 132 end 133 else 134 host.registry['smbindex'] = num 135 end 136end 137 138---Writes the given account to the registry. 139-- 140-- There are several places where accounts are stored: 141-- * registry['usernames'][username] => true 142-- * registry['smbaccounts'][username] => password 143-- * registry[ip]['smbaccounts'] => array of table containing 'username', 'password', and 'is_admin' 144-- 145-- The final place, 'smbaccount', is reserved for the "best" account. This is 146-- an administrator account, if one's found; otherwise, it's the first account 147-- discovered that isn't <code>guest</code>. 148-- 149-- This has to be called while no SMB connections are made, since it 150-- potentially makes its own connection. 151-- 152--@param host The host object. 153--@param username The username to add. 154--@param domain The domain to add. 155--@param password The password to add. 156--@param password_hash The password hash to add. 157--@param hash_type The hash type to use. 158--@param is_admin [optional] Set to 'true' the account is known to be an administrator. 159function add_account(host, username, domain, password, password_hash, hash_type, is_admin) 160 -- Save the username in a global list -- TODO: restore this 161 -- if(nmap.registry.usernames == nil) then 162 -- nmap.registry.usernames = {} 163 -- end 164 -- nmap.registry.usernames[username] = true 165 -- 166 -- -- Save the username/password pair in a global list 167 -- if(nmap.registry.smbaccounts == nil) then 168 -- nmap.registry.smbaccounts = {} 169 -- end 170 -- nmap.registry.smbaccounts[username] = password 171 172 -- Check if we've already recorded this account 173 if(account_exists(host, username, domain)) then 174 return 175 end 176 177 if(host.registry['smbaccounts'] == nil) then 178 host.registry['smbaccounts'] = {} 179 end 180 181 -- Determine the type of account, if it wasn't given 182 local account_type = nil 183 if(is_admin) then 184 account_type = ACCOUNT_TYPES.ADMIN 185 else 186 if(username == '') then 187 -- Anonymous account 188 account_type = ACCOUNT_TYPES.ANONYMOUS 189 elseif(string.lower(username) == 'guest') then 190 -- Guest account 191 account_type = ACCOUNT_TYPES.GUEST 192 else 193 -- We have to assume it's a user-level account (we just can't call any SMB functions from inside here) 194 account_type = ACCOUNT_TYPES.USER 195 end 196 end 197 198 -- Set some defaults 199 if(hash_type == nil) then 200 hash_type = 'ntlm' 201 end 202 203 -- Save the new account if this is our first one, or our other account isn't an admin 204 local new_entry = {} 205 new_entry['username'] = username 206 new_entry['domain'] = domain 207 new_entry['password'] = password 208 new_entry['password_hash'] = password_hash 209 new_entry['hash_type'] = string.lower(hash_type) 210 new_entry['account_type'] = account_type 211 212 -- Insert the new entry into the table 213 table.insert(host.registry['smbaccounts'], new_entry) 214 215 -- Sort the table based on the account type (we want anonymous at the end, administrator at the front) 216 table.sort(host.registry['smbaccounts'], function(a,b) return a['account_type'] > b['account_type'] end) 217 218 -- Print a debug message 219 stdnse.debug1("SMB: Added account '%s' to account list", username) 220 221 -- Reset the credentials 222 next_account(host, 1) 223 224 -- io.write("\n\n" .. nsedebug.tostr(host.registry['smbaccounts']) .. "\n\n") 225end 226 227---Retrieve the current set of credentials set in the registry. 228-- 229-- If these fail, <code>next_account</code> should be called. 230-- 231--@param host The host object. 232--@return status true or false. If false, the next return value is an error 233-- message and no other values are returned. 234--@return username 235--@return domain 236--@return password 237--@return password_hash 238--@return hash_type 239--@see next_account 240function get_account(host) 241 if(host.registry['smbindex'] == nil) then 242 host.registry['smbindex'] = 1 243 end 244 245 local index = host.registry['smbindex'] 246 local account = host.registry['smbaccounts'][index] 247 248 if(account == nil) then 249 return false, "No accounts left to try" 250 end 251 252 return true, account['username'], account['domain'], account['password'], account['password_hash'], account['hash_type'] 253end 254 255---Initialize the host's account table. 256-- 257-- Create the account table with the anonymous and guest users, as well as the 258-- user given in the script's arguments, if there is one. 259-- 260--@param host The host object. 261function init_account(host) 262 -- Don't run this more than once for each host 263 if(host.registry['smbaccounts'] ~= nil) then 264 return 265 end 266 267 -- Create the list 268 host.registry['smbaccounts'] = {} 269 270 -- Add the anonymous/guest accounts 271 add_account(host, '', '', '', nil, 'none') 272 273 if(not stdnse.get_script_args( "smbnoguest" )) then 274 add_account(host, 'guest', '', '', nil, 'ntlm') 275 end 276 277 -- Add the account given on the commandline (TODO: allow more than one?) 278 local args = nmap.registry.args 279 local username = nil 280 local domain = '' 281 local password = nil 282 local password_hash = nil 283 local hash_type = 'ntlm' 284 285 -- Do the username first 286 if(args.smbusername ~= nil) then 287 username = args.smbusername 288 elseif(args.smbuser ~= nil) then 289 username = args.smbuser 290 end 291 292 -- If the username exists, do everything else 293 if(username ~= nil) then 294 -- Domain 295 if(args.smbdomain ~= nil) then 296 domain = args.smbdomain 297 end 298 299 -- Type 300 if(args.smbtype ~= nil) then 301 hash_type = args.smbtype 302 end 303 304 -- Do the password 305 if(args.smbpassword ~= nil) then 306 password = args.smbpassword 307 elseif(args.smbpass ~= nil) then 308 password = args.smbpass 309 end 310 311 -- Only use the hash if there's no password 312 if(password == nil) then 313 password_hash = args.smbhash 314 end 315 316 -- Add the account, if we got a password 317 if(password == nil and password_hash == nil) then 318 stdnse.debug1("SMB: Either smbpass, smbpassword, or smbhash have to be passed as script arguments to use an account") 319 else 320 add_account(host, username, domain, password, password_hash, hash_type) 321 end 322 end 323end 324 325---Generate the Lanman v1 hash (LMv1). 326-- 327-- The generated hash is incredibly easy to reverse, because the input is 328-- padded or truncated to 14 characters, then split into two 7-character 329-- strings. Each of these strings are used as a key to encrypt the string, 330-- "KGS!@#$%" in DES. Because the keys are no longer than 7-characters long, 331-- it's pretty trivial to bruteforce them. 332-- 333--@param password the password to hash 334--@return true on success, or false on error 335--@return The LMv1 hash 336local function lm_create_hash(password) 337 if(have_ssl ~= true) then 338 return false, "SMB: OpenSSL not present" 339 end 340 341 local str1, str2 342 local key1, key2 343 local result 344 345 -- Convert the password to uppercase 346 password = string.upper(password) 347 348 -- Encode the password in OEM code page 349 -- Supporting all the OEM code pages would be burdensome, so we try to 350 -- convert to CP437, the default for US-English Windows, which is 351 -- used for Alt+NumPad "unicode" entry in all versions of Windows. 352 -- https://en.wikipedia.org/wiki/Code_page_437 353 do 354 local buf = {} 355 for i, cp in ipairs(unicode.decode(password, unicode.utf8_dec)) do 356 local ch = unicode.cp437_enc(cp) 357 if ch == nil then 358 return false, "Couldn't encode password in CP437" 359 end 360 buf[i] = ch 361 end 362 password = table.concat(buf) 363 end 364 365 -- If password is under 14 characters, pad it to 14 366 password = password .. string.rep('\0', 14 - #password) 367 368 -- Take the first and second half of the password (note that if it's longer than 14 characters, it's truncated) 369 str1 = string.sub(password, 1, 7) 370 str2 = string.sub(password, 8, 14) 371 372 -- Generate the keys 373 key1 = openssl.DES_string_to_key(str1) 374 key2 = openssl.DES_string_to_key(str2) 375 376 -- Encrypt the string "KGS!@#$%" with each half, and concatenate it 377 result = openssl.encrypt("DES", key1, nil, "KGS!@#$%") .. openssl.encrypt("DES", key2, nil, "KGS!@#$%") 378 379 return true, result 380end 381 382---Generate the NTLMv1 hash. 383-- 384-- This hash is quite a bit better than LMv1, and is far easier to generate. 385-- Basically, it's the MD4 of the Unicode password. 386-- 387--@param password the password to hash 388--@return true on success, or false on error 389--@return The NTLMv1 hash 390function ntlm_create_hash(password) 391 if(have_ssl ~= true) then 392 return false, "SMB: OpenSSL not present" 393 end 394 395 return true, openssl.md4(unicode.utf8to16(password)) 396end 397 398---Create the Lanman response to send back to the server. 399-- 400-- To do this, the Lanman password is padded to 21 characters and split into 401-- three 7-character strings. Each of those strings is used as a key to encrypt 402-- the server challenge. The three encrypted strings are concatenated and 403-- returned. 404-- 405--@param lanman The LMv1 hash 406--@param challenge The server's challenge. 407--@return true on success, or false on error 408--@return The client challenge response, or an error message 409function lm_create_response(lanman, challenge) 410 if(have_ssl ~= true) then 411 return false, "SMB: OpenSSL not present" 412 end 413 414 local str1, str2, str3 415 local key1, key2, key3 416 local result 417 418 -- Pad the hash to 21 characters 419 lanman = lanman .. string.rep('\0', 21 - #lanman) 420 421 -- Take the first and second half of the password (note that if it's longer than 14 characters, it's truncated) 422 str1 = string.sub(lanman, 1, 7) 423 str2 = string.sub(lanman, 8, 14) 424 str3 = string.sub(lanman, 15, 21) 425 426 -- Generate the keys 427 key1 = openssl.DES_string_to_key(str1) 428 key2 = openssl.DES_string_to_key(str2) 429 key3 = openssl.DES_string_to_key(str3) 430 431 -- Print a warning message if a blank challenge is received, and create a phony challenge. A blank challenge is 432 -- invalid in the protocol, and causes some versions of OpenSSL to abort with no possible error handling. 433 if(challenge == "") then 434 stdnse.debug1("SMB: ERROR: Server returned invalid (blank) challenge value (should be 8 bytes); failing login to avoid OpenSSL crash.") 435 challenge = "AAAAAAAA" 436 end 437 438 -- Encrypt the challenge with each key 439 result = openssl.encrypt("DES", key1, nil, challenge) .. openssl.encrypt("DES", key2, nil, challenge) .. openssl.encrypt("DES", key3, nil, challenge) 440 441 return true, result 442end 443 444---Create the NTLM response to send back to the server. 445-- 446-- This is actually done the exact same way as the Lanman hash, 447-- so I call the <code>Lanman</code> function. 448-- 449--@param ntlm The NTLMv1 hash 450--@param challenge The server's challenge. 451--@return true on success, or false on error 452--@return The client challenge response, or an error message 453function ntlm_create_response(ntlm, challenge) 454 if(have_ssl ~= true) then 455 return false, "SMB: OpenSSL not present" 456 end 457 458 return lm_create_response(ntlm, challenge) 459end 460 461---Create the NTLM mac key, which is used for message signing. 462-- 463-- For basic authentication, this is the md4 of the NTLM hash, concatenated 464-- with the response hash; for extended authentication, this is just the md4 of 465-- the NTLM hash. 466-- 467--@param ntlm_hash The NTLM hash. 468--@param ntlm_response The NTLM response. 469--@param is_extended Should be set if extended security negotiations are being used. 470--@return The NTLM mac key 471function ntlm_create_mac_key(ntlm_hash, ntlm_response, is_extended) 472 if(have_ssl ~= true) then 473 return false, "SMB: OpenSSL not present" 474 end 475 if(is_extended) then 476 return openssl.md4(ntlm_hash) 477 else 478 return openssl.md4(ntlm_hash) .. ntlm_response 479 end 480end 481 482---Create the LM mac key, which is used for message signing. 483-- 484-- For basic authentication, it's the first 8 bytes of the lanman hash, 485-- followed by 8 null bytes, followed by the lanman response; for extended 486-- authentication, this is just the first 8 bytes of the lanman hash followed 487-- by 8 null bytes. 488-- 489--@param lm_hash The LM hash. 490--@param lm_response The LM response. 491--@param is_extended Should be set if extended security negotiations are being used. 492--@return The LM mac key 493function lm_create_mac_key(lm_hash, lm_response, is_extended) 494 if(have_ssl ~= true) then 495 return false, "SMB: OpenSSL not present" 496 end 497 498 if(is_extended) then 499 return string.sub(lm_hash, 1, 8) .. string.rep('\0', 8) 500 else 501 return string.sub(lm_hash, 1, 8) .. string.rep('\0', 8) .. lm_response 502 end 503end 504 505---Create the NTLMv2 hash. 506-- 507-- The NTLMv2 hash is based on the NTLMv1 hash (for easy upgrading), the 508-- username, and the domain. Essentially, the NTLM hash is used as a HMAC-MD5 509-- key, which is used to hash the unicode domain concatenated with the unicode 510-- username. 511-- 512--@param ntlm The NTLMv1 hash. 513--@param username The username we're using. 514--@param domain The domain. 515--@return true on success, or false on error 516--@return The NTLMv2 hash or an error message 517function ntlmv2_create_hash(ntlm, username, domain) 518 if(have_ssl ~= true) then 519 return false, "SMB: OpenSSL not present" 520 end 521 522 username = unicode.utf8to16(string.upper(username)) 523 domain = unicode.utf8to16(string.upper(domain)) 524 525 return true, openssl.hmac("MD5", ntlm, username .. domain) 526end 527 528---Create the LMv2 response, which can be sent back to the server. 529-- 530-- This is identical to the <code>NTLMv2</code> function, 531-- except that it uses an 8-byte client challenge. 532-- 533-- The reason for LMv2 is a long and twisted story. Well, not really. The 534-- reason is basically that the v1 hashes are always 24-bytes, and some servers 535-- expect 24 bytes, but the NTLMv2 hash is more than 24 bytes. So, the only way 536-- to keep pass-through compatibility was to have a v2-hash that was guaranteed 537-- to be 24 bytes. So LMv2 was born -- it has a 16-byte hash followed by the 538-- 8-byte client challenge, for a total of 24 bytes. And now you've learned 539-- something 540-- 541--@param ntlm The NVLMv1 hash. 542--@param username The username we're using. 543--@param domain The domain. 544--@param challenge The server challenge. 545--@return true on success, or false on error 546--@return The LMv2 response, or an error message 547function lmv2_create_response(ntlm, username, domain, challenge) 548 if(have_ssl ~= true) then 549 return false, "SMB: OpenSSL not present" 550 end 551 552 return ntlmv2_create_response(ntlm, username, domain, challenge, 8) 553end 554 555---Create the NTLMv2 response, which can be sent back to the server. 556-- 557-- This is done by using the HMAC-MD5 algorithm with the NTLMv2 hash as a key, 558-- and the server challenge concatenated with the client challenge for the 559-- data. The resulting hash is concatenated with the client challenge and 560-- returned. 561-- 562-- The "proper" implementation for this uses a certain structure for the client 563-- challenge, involving the time and computer name and stuff (if you don't do 564-- this, Wireshark tells you it's a malformed packet). In my tests, however, I 565-- couldn't get Vista to recognize a client challenge longer than 24 bytes, and 566-- this structure was guaranteed to be much longer than 24 bytes. So, I just 567-- use a random string generated by OpenSSL. I've tested it on every Windows 568-- system from Windows 2000 to Windows Vista, and it has always worked. 569-- 570--@param ntlm The NVLMv1 hash. 571--@param username The username we're using. 572--@param domain The domain. 573--@param challenge The server challenge. 574--@param client_challenge_length number of random bytes of client challenge to use 575--@return true on success, or false on error 576--@return The NTLMv2 response, or an error message 577function ntlmv2_create_response(ntlm, username, domain, challenge, client_challenge_length) 578 if(have_ssl ~= true) then 579 return false, "SMB: OpenSSL not present" 580 end 581 582 local client_challenge = openssl.rand_bytes(client_challenge_length) 583 584 local status, ntlmv2_hash = ntlmv2_create_hash(ntlm, username, domain) 585 586 return true, openssl.hmac("MD5", ntlmv2_hash, challenge .. client_challenge) .. client_challenge 587end 588 589 590--- Generates the ntlmv2 session response. 591-- It starts by generatng an 8 byte random client nonce, it is padded to 24 bytes. 592-- The padded value is the lanman response. A session nonce is made by 593-- concatenating the server challenge and the client nonce. The ntlm session hash 594-- is first 8 bytes of the md5 hash of the session nonce. 595-- The ntlm response is the lm response with session hash as challenge. 596-- @param ntlm_passsword_hash The md4 hash of the utf-16 password. 597-- @param challenge The challenge sent by the server. 598function ntlmv2_session_response(ntlm_password_hash, challenge) 599 local client_nonce = openssl.rand_bytes(8) 600 601 local lm_response = client_nonce .. string.rep('\0', 24 - #client_nonce) 602 local session_nonce = challenge .. client_nonce 603 local ntlm_session_hash = openssl.md5(session_nonce):sub(1,8) 604 605 local status, ntlm_response = lm_create_response(ntlm_password_hash, ntlm_session_hash) 606 607 return status, lm_response, ntlm_response 608end 609---Generate the Lanman and NTLM password hashes. 610-- 611-- The password itself is taken from the function parameters, the script 612-- arguments, and the registry (in that order). If no password is set, then the 613-- password hash is used (which is read from all the usual places). If neither 614-- is set, then a blank password is used. 615-- 616-- The output passwords are hashed based on the hash type. 617-- 618--@param ip The ip address of the host, used for registry lookups. 619--@param username The username, which is used for v2 passwords. 620--@param domain The username, which is used for v2 passwords. 621--@param password [optional] The overriding password. 622--@param password_hash [optional] The overriding password hash. Shouldn't be 623-- set if password is set. 624--@param challenge The server challenge. 625--@param hash_type The way in which to hash the password. 626--@param is_extended Set to 'true' if extended security negotiations are being 627-- used (this has to be known for the message-signing key to 628-- be generated properly). 629--@return lm_response, to be send directly back to the server 630--@return ntlm_response, to be send directly back to the server 631--@reutrn mac_key used for message signing. 632function get_password_response(ip, username, domain, password, password_hash, hash_type, challenge, is_extended) 633 local status 634 local lm_hash = nil 635 local ntlm_hash = nil 636 local mac_key = nil 637 local lm_response, ntlm_response 638 639 -- Check for a blank password 640 if(password == nil and password_hash == nil) then 641 stdnse.debug2("SMB: Couldn't find password or hash to use (assuming blank)") 642 password = "" 643 end 644 645 -- The anonymous user requires a single 0-byte instead of a LANMAN hash (don't ask me why, but it doesn't work without) 646 if(hash_type == 'none') then 647 return '\0', '', nil 648 end 649 650 -- If we got a password, hash it 651 if(password ~= nil) then 652 status, lm_hash = lm_create_hash(password) 653 status, ntlm_hash = ntlm_create_hash(password) 654 else 655 if(password_hash ~= nil) then 656 if(string.find(password_hash, "^" .. string.rep("%x%x", 16) .. "$")) then 657 stdnse.debug2("SMB: Found a 16-byte hex string") 658 lm_hash = stdnse.fromhex(password_hash:sub(1, 32)) 659 ntlm_hash = stdnse.fromhex(password_hash:sub(1, 32)) 660 elseif(string.find(password_hash, "^" .. string.rep("%x%x", 32) .. "$")) then 661 stdnse.debug2("SMB: Found a 32-byte hex string") 662 lm_hash = stdnse.fromhex(password_hash:sub(1, 32)) 663 ntlm_hash = stdnse.fromhex(password_hash:sub(33, 64)) 664 elseif(string.find(password_hash, "^" .. string.rep("%x%x", 16) .. "." .. string.rep("%x%x", 16) .. "$")) then 665 stdnse.debug2("SMB: Found two 16-byte hex strings") 666 lm_hash = stdnse.fromhex(password_hash:sub(1, 32)) 667 ntlm_hash = stdnse.fromhex(password_hash:sub(34, 65)) 668 else 669 stdnse.debug1("SMB: ERROR: Hash(es) provided in an invalid format (should be 32, 64, or 65 hex characters)") 670 lm_hash = nil 671 ntlm_hash = nil 672 end 673 end 674 end 675 676 -- At this point, we should have a good lm_hash and ntlm_hash if we're getting one 677 if(lm_hash == nil or ntlm_hash == nil) then 678 stdnse.debug2("SMB: Couldn't determine which password to use, using a blank one") 679 return "", "" 680 end 681 682 -- Output what we've got so far 683 stdnse.debug2("SMB: Lanman hash: %s", stdnse.tohex(lm_hash)) 684 stdnse.debug2("SMB: NTLM hash: %s", stdnse.tohex(ntlm_hash)) 685 686 -- Hash the password the way the user wants 687 if(hash_type == "v1") then 688 -- LM and NTLM are hashed with their respective algorithms 689 stdnse.debug2("SMB: Creating v1 response") 690 status, lm_response = lm_create_response(lm_hash, challenge) 691 status, ntlm_response = ntlm_create_response(ntlm_hash, challenge) 692 693 mac_key = ntlm_create_mac_key(ntlm_hash, ntlm_response, is_extended) 694 695 elseif(hash_type == "lm") then 696 -- LM is hashed with its algorithm, NTLM is blank 697 stdnse.debug2("SMB: Creating LMv1 response") 698 status, lm_response = lm_create_response(lm_hash, challenge) 699 ntlm_response = "" 700 701 mac_key = lm_create_mac_key(lm_hash, lm_response, is_extended) 702 703 elseif(hash_type == "ntlm") then 704 -- LM and NTLM both use the NTLM algorithm 705 stdnse.debug2("SMB: Creating NTLMv1 response") 706 status, lm_response = ntlm_create_response(ntlm_hash, challenge) 707 status, ntlm_response = ntlm_create_response(ntlm_hash, challenge) 708 709 mac_key = ntlm_create_mac_key(ntlm_hash, ntlm_response, is_extended) 710 711 elseif(hash_type == "v2") then 712 -- LM and NTLM are hashed with their respective v2 algorithms 713 stdnse.debug2("SMB: Creating v2 response") 714 status, lm_response = lmv2_create_response(ntlm_hash, username, domain, challenge) 715 status, ntlm_response = ntlmv2_create_response(ntlm_hash, username, domain, challenge, 24) 716 717 elseif(hash_type == "lmv2") then 718 -- LM is hashed with its v2 algorithm, NTLM is blank 719 stdnse.debug2("SMB: Creating LMv2 response") 720 status, lm_response = lmv2_create_response(ntlm_hash, username, domain, challenge) 721 ntlm_response = "" 722 723 elseif(hash_type == "ntlmv2_session") then 724 stdnse.debug2("SMB: Creating nltmv2 session response") 725 status, lm_response, ntlm_response = ntlmv2_session_response(ntlm_hash, challenge) 726 else 727 -- Default to NTLMv1 728 if(hash_type ~= nil) then 729 stdnse.debug1("SMB: Invalid login type specified ('%s'), using default (NTLM)", hash_type) 730 else 731 stdnse.debug1("SMB: No login type specified, using default (NTLM)") 732 end 733 734 status, lm_response = ntlm_create_response(ntlm_hash, challenge) 735 status, ntlm_response = ntlm_create_response(ntlm_hash, challenge) 736 737 end 738 739 stdnse.debug2("SMB: Lanman response: %s", stdnse.tohex(lm_response)) 740 stdnse.debug2("SMB: NTLM response: %s", stdnse.tohex(ntlm_response)) 741 742 return lm_response, ntlm_response, mac_key 743end 744 745---Generate an NTLMSSP security blob. 746--@param security_blob The server's security blob, or nil if this is the first 747-- message 748--@param ip The ip address of the host, used for registry lookups. 749--@param username The username, which is used for v2 passwords. 750--@param domain The username, which is used for v2 passwords. 751--@param password [optional] The overriding password. 752--@param password_hash [optional] The overriding password hash. Shouldn't be 753-- set if password is set. 754--@param hash_type The way in which to hash the password. 755--@param flags The NTLM flags as a number 756function get_security_blob(security_blob, ip, username, domain, password, password_hash, hash_type, flags) 757 local pos = 1 758 local new_blob 759 local flags = flags or 0x00008215 -- (NEGOTIATE_SIGN_ALWAYS | NEGOTIATE_NTLM | NEGOTIATE_SIGN | REQUEST_TARGET | NEGOTIATE_UNICODE) 760 761 if(security_blob == nil) then 762 -- If security_blob is nil, this is the initial packet 763 new_blob = string.pack("<zI4I4I8I8", 764 "NTLMSSP", -- Identifier 765 NTLMSSP_NEGOTIATE, -- Type 766 flags, -- Flags 767 0, -- Calling workstation domain 768 0 -- Calling workstation name 769 ) 770 771 return true, new_blob, "", "" 772 else 773 -- Parse the old security blob 774 local identifier, message_type, domain_length, domain_max, domain_offset, server_flags, challenge, reserved = string.unpack("<I8I4I2I2I4I4c8c8", security_blob) 775 local lanman, ntlm, mac_key = get_password_response(ip, username, domain, password, password_hash, hash_type, challenge, true) 776 777 -- Convert the username and domain to unicode (TODO: Disable the unicode flag, evaluate if that'll work) 778 local hostname = unicode.utf8to16("nmap") 779 username = unicode.utf8to16(username) 780 domain = (#username > 0 ) and unicode.utf8to16(domain) or "" 781 ntlm = (#username > 0 ) and ntlm or "" 782 lanman = (#username > 0 ) and lanman or '\0' 783 784 local domain_offset = 0x40 785 local username_offset = domain_offset + #domain 786 local hostname_offset = username_offset + #username 787 local lanman_offset = hostname_offset + #hostname 788 local ntlm_offset = lanman_offset + #lanman 789 local sessionkey_offset = ntlm_offset + #ntlm 790 791 new_blob = string.pack("<zI4 I2I2I4 I2I2I4 I2I2I4 I2I2I4 I2I2I4 I2I2I4 I4", 792 "NTLMSSP", 793 NTLMSSP_AUTH, 794 #lanman, 795 #lanman, 796 lanman_offset, 797 ( #ntlm > 0 and #ntlm - 16 or 0 ), 798 ( #ntlm > 0 and #ntlm - 16 or 0 ), 799 ntlm_offset, 800 #domain, 801 #domain, 802 domain_offset, 803 #username, 804 #username, 805 username_offset, 806 #hostname, 807 #hostname, 808 hostname_offset, 809 #session_key, 810 #session_key, 811 sessionkey_offset, 812 flags) 813 .. domain 814 .. username 815 .. hostname 816 .. lanman 817 .. ntlm 818 .. session_key 819 820 return true, new_blob, mac_key 821 end 822 823end 824 825--- 826-- Host information for NTLM security 827-- @class table 828-- @name host_info 829-- @field target_realm Target Name Data 830-- @field netbios_computer_name Server name 831-- @field netbios_domain_name Domain name 832-- @field fqdn DNS server name 833-- @field dns_domain_name DNS domain name 834-- @field dns_forest_name DNS tree name 835-- @field timestamp Timestamp 836 837--- 838-- Gets host info from a security blob 839-- @param security_blob The NTLM security blob 840-- @return A host_info table containing the data in the blob. 841-- @see host_info 842function get_host_info_from_security_blob(security_blob) 843 local identifier, message_type, domain_length, domain_max, domain_offset, server_flags, challenge, hpos = string.unpack("<c8I4 I2I2I4 I4I8", security_blob) 844 845 -- Do some validation on the NTLMSSP message 846 if ( identifier ~= "NTLMSSP\0" ) then 847 stdnse.debug1("SMB: Invalid NTLM challenge message: unexpected signature." ) 848 return false, "Invalid NTLM challenge message" 849 -- Per MS-NLMP, this field must be 2 for an NTLM challenge message 850 elseif ( message_type ~= 0x2 ) then 851 stdnse.debug1("SMB: Invalid NTLM challenge message: unexpected message type: %d.", message_type ) 852 return false, "Invalid message type in NTLM challenge message" 853 end 854 855 local ntlm_challenge = {} 856 857 -- Parse the TargetName data (i.e. the server authentication realm) 858 if ( domain_length > 0 ) then 859 local length = domain_length 860 local pos = domain_offset + 1 -- +1 to convert to Lua's 1-based indexes 861 local target_realm 862 target_realm = string.unpack("c" .. length, security_blob, pos ) 863 ntlm_challenge[ "target_realm" ] = unicode.utf16to8( target_realm ) 864 end 865 866 if hpos + domain_length > #security_blob then 867 -- Context, Target Information, and OS Version structure are all omitted 868 -- Probably Win9x 869 return ntlm_challenge 870 end 871 872 local context, target_info_length, target_info_max, target_info_offset, hpos = string.unpack("<I8 I2I2I4", security_blob, hpos) 873 874 -- OS info is in the intervening 8 bytes, subtract 1 for lua 1-index 875 if target_info_offset >= hpos + 7 and domain_offset >= hpos + 7 then 876 local major, minor, build, reserved = string.unpack("<BBI2c4", security_blob, hpos) 877 if reserved == "\0\0\0\x0f" then 878 ntlm_challenge.os_major_version = major 879 ntlm_challenge.os_minor_version = minor 880 ntlm_challenge.os_build = build 881 else 882 stdnse.debug2("smbauth: Unknown OS info structure in NTLM handshake") 883 end 884 end 885 886 -- Parse the TargetInfo data (Wireshark calls this the "Address List") 887 if ( target_info_length > 0 ) then 888 889 -- Definition of AvId values (IDs for AV_PAIR (attribute-value pair) structures), 890 -- as defined by the NTLM Authentication Protocol specification [MS-NLMP]. 891 local NTLM_AV_ID_VALUES = { 892 MsvAvEOL = 0x0, 893 MsvAvNbComputerName = 0x1, 894 MsvAvNbDomainName = 0x2, 895 MsvAvDnsComputerName = 0x3, 896 MsvAvDnsDomainName = 0x4, 897 MsvAvDnsTreeName = 0x5, 898 MsvAvFlags = 0x6, 899 MsvAvTimestamp = 0x7, 900 MsvAvRestrictions = 0x8, 901 MsvAvTargetName = 0x9, 902 MsvAvChannelBindings = 0xA, 903 } 904 -- Friendlier names for AvId values, to be used as keys in the results table 905 -- e.g. ntlm_challenge[ "dns_computer_name" ] -> "host.test.local" 906 local NTLM_AV_ID_NAMES = { 907 [NTLM_AV_ID_VALUES.MsvAvNbComputerName] = "netbios_computer_name", 908 [NTLM_AV_ID_VALUES.MsvAvNbDomainName] = "netbios_domain_name", 909 [NTLM_AV_ID_VALUES.MsvAvDnsComputerName] = "fqdn", 910 [NTLM_AV_ID_VALUES.MsvAvDnsDomainName] = "dns_domain_name", 911 [NTLM_AV_ID_VALUES.MsvAvDnsTreeName] = "dns_forest_name", 912 [NTLM_AV_ID_VALUES.MsvAvTimestamp] = "timestamp", 913 } 914 915 916 local length = target_info_length 917 local pos = target_info_offset + 1 -- +1 to convert to Lua's 1-based indexes 918 local target_info 919 target_info = string.unpack("c" .. length, security_blob, pos) 920 921 pos = 1 -- reset pos to 1, since we'll be working out of just the target_info 922 repeat 923 local value, av_id 924 av_id, value, pos = string.unpack( "<I2s2", target_info, pos ) 925 local friendly_name = NTLM_AV_ID_NAMES[ av_id ] 926 927 if ( av_id == NTLM_AV_ID_VALUES.MsvAvEOL ) then 928 break 929 elseif ( av_id == NTLM_AV_ID_VALUES.MsvAvTimestamp ) then 930 -- this is a FILETIME value (see [MS-DTYP]), representing the time in 100-ns increments since 1/1/1601 931 ntlm_challenge[ friendly_name ] = string.unpack( "<I8", value ) 932 elseif ( friendly_name ) then 933 ntlm_challenge[ friendly_name ] = unicode.utf16to8( value ) 934 end 935 until ( pos >= #target_info ) 936 end 937 938 return ntlm_challenge 939end 940 941---Create an 8-byte message signature that's sent with all SMB packets. 942-- 943--@param mac_key The key used for authentication. It's the concatenation of the 944-- session key and the response hash. 945--@param data The packet to generate the signature for. This should be the 946-- packet that's about to be sent, except with the signature slot 947-- replaced with the sequence number. 948--@return The 8-byte signature. The signature is equal to the first eight bytes 949-- of md5(mac_key .. smb_data) 950function calculate_signature(mac_key, data) 951 if(have_ssl) then 952 return string.sub(openssl.md5(mac_key .. data), 1, 8) 953 else 954 return string.rep('\0', 8) 955 end 956end 957 958if not unittest.testing() then 959 return _ENV 960end 961 962test_suite = unittest.TestSuite:new() 963-- OpenSSL-dependent crypto tests. 964if have_ssl then 965 test_suite:add_test(unittest.equal( 966 stdnse.tohex(select(-1, lm_create_hash("passphrase"))), 967 "855c3697d9979e78ac404c4ba2c66533" 968 ), 969 "lm_create_hash" 970 ) 971 test_suite:add_test(unittest.equal( 972 stdnse.tohex(select(-1, ntlm_create_hash("passphrase"))), 973 "7f8fe03093cc84b267b109625f6bbf4b" 974 ), 975 "ntlm_create_hash" 976 ) 977 test_suite:add_test(unittest.equal( 978 stdnse.tohex(select(-1, lm_create_hash("ÅÇÅÇ"))), 979 "1830f5732b438091aad3b435b51404ee" 980 ), 981 "lm_create_hash" 982 ) 983 test_suite:add_test(unittest.equal( 984 stdnse.tohex(select(-1, ntlm_create_hash("öäü"))), 985 "4848bcb81cf018c3b70ea1479bd1374d" 986 ), 987 "ntlm_create_hash" 988 ) 989end 990 991return _ENV; 992