[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f7b3KYieMsNwTSRfD9MVQb22epRVyQcSN5YalepcYA6w":3},{"id":4,"question":5,"answer":6,"answerHtml":7,"slug":8,"keywords":9,"article":10,"status":34,"aiModel":39,"aiConfidence":39,"updatedAt":52,"createdAt":53,"_status":51},1132,"What additional capabilities does the Python implementation pyKerbrute have compared to the original kerbrute?","PyKerbrute adds support for TCP protocol in addition to UDP, and it allows validation of NTLM hashes instead of only plaintext passwords. These enhancements provide more flexibility in network environments and attack scenarios. The password verification function uses the NTLM hash directly in the padata encryption, similar to how plaintext passwords are processed. For a deeper understanding of password brute-forcing via other protocols, see [Penetration Basics - Brute-Forcing Domain User Passwords via LDAP Protocol](\u002Fnews\u002Fpenetration-basics-brute-forcing-domain-user-paswords-via-ldap-protocol).","\u003Cp>PyKerbrute adds support for TCP protocol in addition to UDP, and it allows validation of NTLM hashes instead of only plaintext passwords. These enhancements provide more flexibility in network environments and attack scenarios. The password verification function uses the NTLM hash directly in the padata encryption, similar to how plaintext passwords are processed. For a deeper understanding of password brute-forcing via other protocols, see [Penetration Basics - Brute-Forcing Domain User Passwords via LDAP Protocol](\u002Fnews\u002Fpenetration-basics-brute-forcing-domain-user-paswords-via-ldap-protocol).\u003C\u002Fp>\u003Cp>\u003Ca href=\"\u002Fnews\u002Fpenetration-techniques-user-enumeration-and-password-brute-forcing-via-kerberos-pre-authentication\">Read the related One Day Sec article\u003C\u002Fa>\u003C\u002Fp>","what-additional-capabilities-does-the-python-implementation-pykerbrute-have-comp-1777480350249","pyKerbrute, TCP support, NTLM hash, plaintext password, padata",{"id":11,"title":12,"slug":13,"description":14,"content":15,"contentHtml":30,"cover":31,"author":32,"views":19,"readingTime":33,"status":34,"publishedAt":35,"seo":36,"tags":41,"qaPairs":42,"meta":48,"updatedAt":49,"createdAt":50,"_status":51},275,"Penetration Techniques - User Enumeration and Password Brute-forcing via Kerberos Pre-Authentication","penetration-techniques-user-enumeration-and-password-brute-forcing-via-kerberos-pre-authentication","Learn how to use Kerberos pre-authentication for stealthy user enumeration and password brute-forcing without generating 4625 logs, with Python implementation tips.",{"root":16},{"type":17,"format":18,"indent":19,"version":20,"children":21,"direction":29},"root","",0,1,[22],{"type":23,"format":18,"indent":19,"version":20,"children":24,"direction":29},"paragraph",[25],{"mode":26,"text":27,"type":28,"style":18,"detail":19,"format":19,"version":20},"normal","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>In the previous article 'Penetration Basics - Brute-forcing Domain User Passwords via LDAP Protocol', methods for brute-forcing domain user passwords through the LDAP protocol were introduced, with the key characteristic being that it generates logs (4625 - An account failed to log on).\u003C\u002Fp>\u003Cp>However, when using kerbrute for brute-forcing via Kerberos pre-authentication, no logs (4625 - An account failed to log on) are generated. Therefore, I conducted further research on kerbrute, implemented the same functionality using Python, and added support for TCP protocol and NTLM hash verification. This article documents my research process and learning insights.\u003C\u002Fp>\u003Ch2>0x01 Overview\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cul>\u003Cli>Introduction to kerbrute\u003C\u002Fli>\u003Cli>Principles of kerbrute\u003C\u002Fli>\u003Cli>Details of implementing kerbrute in Python\u003C\u002Fli>\u003Cli>Open-source code pyKerbrute\u003C\u002Fli>\u003Cli>Detection of Kerberos pre-authentication brute-forcing\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Applicable Scenarios for kerbrute\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Applicable scenarios: User enumeration and password brute-forcing against domain users from outside the domain\u003C\u002Fp>\u003Cp>Since there is no domain user password, it is not possible to enumerate all domain users via the LDAP protocol, and using the LDAP protocol for brute-force attacks will generate logs (4625 - An account failed to log on).\u003C\u002Fp>\u003Cp>Using kerbrute has the following advantages:\u003C\u002Fp>\u003Cul>\u003Cli>Kerberos pre-auth bruteforcing is faster.\u003C\u002Fli>\u003Cli>It does not generate logs (4625 - An account failed to log on).\u003C\u002Fli>\u003C\u002Ful>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>The default port for Kerberos pre-auth is 88.\u003C\u002Fp>\u003Ch2>0x03 kerbrute testing\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The test environment is shown in the figure below.\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016723063_0_df77a6ea57.jpeg\">\u003C\u002Fp>\u003Cp>kerbrute is developed in Go, and GitHub provides compiled files at the following address:\u003C\u002Fp>\u003Cp>https:\u002F\u002Fgithub.com\u002Fropnop\u002Fkerbrute\u002Freleases\u003C\u002Fp>\u003Cp>kerbrute mainly includes the following two functions:\u003C\u002Fp>\u003Ch3>1. User enumeration\u003C\u002Fh3>\u003Cp>Used to verify whether a user exists, with the command as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>kerbrute_windows_amd64.exe userenum --dc 192.168.1.1 -d test.com user.txt\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016730023_1_1c4c85e5c3.jpeg\">\u003C\u002Fp>\u003Cp>Applicable scenario:\u003C\u002Fp>\u003Cp>Without knowing the domain user passwords, it is impossible to enumerate all domain users via the LDAP protocol. This method can be used to verify whether a user exists.\u003C\u002Fp>\u003Ch3>2. Password verification\u003C\u002Fh3>\u003Cp>After confirming the existence of a user, this function can be used to verify if the password is correct. The command is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>kerbrute_windows_amd64.exe passwordspray -d test.com user.txt DomainUser123!\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016733945_2_8e6f38681b.jpeg\">\u003C\u002Fp>\u003Cp>If login is successful, a log will be generated (4768 - A Kerberos authentication ticket (TGT) was requested), as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016737753_3_ba234ab04e.jpeg\">\u003C\u002Fp>\u003Ch2>0x04 Details of implementing kerbrute using Python\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>My idea is to implement the two main functions of kerbrute: user enumeration and password verification\u003C\u002Fp>\u003Cp>I referenced pykek for the part implementing the Kerberos protocol via Python.\u003C\u002Fp>\u003Cp>Next, I captured the packet content of kerbrute by packet sniffing, then constructed identical packets using Python.\u003C\u002Fp>\u003Cp>kerbrute uses the UDP protocol to implement Kerberos pre-authentication for validating plaintext passwords.\u003C\u002Fp>\u003Cp>During my research, I discovered that the same functionality can be achieved via the TCP protocol, and it can also validate NTLM hashes.\u003C\u002Fp>\u003Ch3>1. Implementing user enumeration with Python\u003C\u002Fh3>\u003Cp>Using Wireshark to capture packets generated by kerbrute's user enumeration feature.\u003C\u002Fp>\u003Cp>Using the UDP protocol, the content of packets sent during user enumeration is as shown in the figure below.\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016740679_4_604f9a4bd4.jpeg\">\u003C\u002Fp>\u003Cp>If the user exists, the returned packet content is as shown in the figure below.\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016744351_5_078921d114.jpeg\">\u003C\u002Fp>\u003Cp>Determination flag: error-code: eRR-PREAUTH-REQUIRED (25)\u003C\u002Fp>\u003Cp>If the user does not exist, the returned packet content is as shown in the figure.\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016746874_6_4eb839b739.jpeg\">\u003C\u002Fp>\u003Cp>Determination flag: error-code: eRR-C-PRINCIPAL-UNKNOWN (6)\u003C\u002Fp>\u003Cp>Next, I used Python to send UDP data, with the content identical to the packets during kerbrute user enumeration; receiving the response and determining user existence via the flag bits.\u003C\u002Fp>\u003Cp>The same functionality can also be achieved through the TCP protocol, only the packet format is different\u003C\u002Fp>\u003Cp>A string pack('&gt;I', len(data)) needs to be added in front of the TCP packet\u003C\u002Fp>\u003Cp>The specific code is as follows:\u003C\u002Fp>\u003Cp>TCP:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def send_req_tcp(req, kdc, port=88):\u003Cbr>    data = encode(req)\u003Cbr>    data = pack('&gt;I', len(data)) + data\u003Cbr>    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\u003Cbr>    sock.connect((kdc, port))\u003Cbr>    sock.send(data)\u003Cbr>    return sock\u003Cbr>\u003Cbr>def recv_rep_tcp(sock):\u003Cbr>    data = ''\u003Cbr>    datalen = None\u003Cbr>    while True:\u003Cbr>        rep = sock.recv(8192)\u003Cbr>        if not rep:\u003Cbr>            sock.close()\u003Cbr>            raise IOError('Connection error')\u003Cbr>        data += rep\u003Cbr>        if len(rep) &gt;= 4:\u003Cbr>            if datalen is None:\u003Cbr>                datalen = unpack('&gt;I', rep[:4])[0]\u003Cbr>            if len(data) &gt;= 4 + datalen:\u003Cbr>                sock.close()\u003Cbr>                return data[4:4 + datalen]\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>UDP:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def send_req_udp(req, kdc, port=88):\u003Cbr>    data = encode(req)\u003Cbr>    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\u003Cbr>    sock.connect((kdc, port))\u003Cbr>    sock.send(data)\u003Cbr>    return sock\u003Cbr>\u003Cbr>def recv_rep_udp(sock):\u003Cbr>    data = ''\u003Cbr>    datalen = None\u003Cbr>    while True:\u003Cbr>        rep = sock.recv(8192)\u003Cbr>        if not rep:\u003Cbr>            sock.close()\u003Cbr>            raise IOError('Connection error')\u003Cbr>        data += rep\u003Cbr>        if len(rep) &gt;= 4:\u003Cbr>            sock.close()\u003Cbr>            return data\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>2. Implement password verification using Python\u003C\u002Fh3>\u003Cp>Capture packets generated by kerbrute password verification function using Wireshark\u003C\u002Fp>\u003Cp>Using UDP protocol, the content of packets sent during password verification is shown in the figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016748712_7_607e827baa.jpeg\">\u003C\u002Fp>\u003Cp>Compared to user enumeration, password verification includes additional content (padata)\u003C\u002Fp>\u003Cp>The specific differences are as follows:\u003C\u002Fp>\u003Cp>The packet format sent during user enumeration is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016750167_8_8f9a66ae9b.jpeg\">\u003C\u002Fp>\u003Cp>The packet format sent during password verification is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016751192_9_79de3da93f.jpeg\">\u003C\u002Fp>\u003Cp>Therefore, the padata section content needs to be added in implementation\u003C\u002Fp>\u003Cp>If the password is correct, the returned packet content is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016752169_10_6da9f27bf0.jpeg\">\u003C\u002Fp>\u003Cp>If the password is incorrect, the returned packet content is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016752988_11_c4df4efada.jpeg\">\u003C\u002Fp>\u003Cp>For the specific packet structure, please refer to the RFC document at the following address:\u003C\u002Fp>\u003Cp>https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc1510#page-50\u003C\u002Fp>\u003Cp>To compute the padata-value, first convert the plaintext password into an NTLM hash before calculation.\u003C\u002Fp>\u003Cp>Therefore, this position can use not only plaintext passwords but also NTLM hashes.\u003C\u002Fp>\u003Cp>Part of the encrypted Python code is as follows:\u003C\u002Fp>\u003Cp>Using plaintext password:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>clearpassword = DomainUser123!\u003Cbr>user_key = (RC4_HMAC, ntlm_hash(clearpassword).digest())\u003Cbr>pa_ts = build_pa_enc_timestamp(current_time, user_key)\u003Cbr>as_req['padata'][0]['padata-value'] = encode(pa_ts)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Using NTLM hash:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>ntlmhash = e00045bd566a1b74386f5c1e3612921b\u003Cbr>user_key = (RC4_HMAC, ntlmhash.decode('hex'))\u003Cbr>pa_ts = build_pa_enc_timestamp(current_time, user_key)\u003Cbr>as_req['padata'][0]['padata-value'] = encode(pa_ts)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch2>0x05 Open-source code pyKerbrute\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The complete implementation code has been uploaded to GitHub, with the address as follows:\u003C\u002Fp>\u003Cp>An open-source project\u003C\u002Fp>\u003Cp>pyKerbrute is a Python implementation of kerbrute, offering the following two additional features compared to kerbrute:\u003C\u002Fp>\u003Cul>\u003Cli>Added support for TCP protocol\u003C\u002Fli>\u003Cli>Added verification for NTLM hash\u003C\u002Fli>\u003C\u002Ful>\u003Cp>pyKerbrute is divided into two functions: user enumeration and password verification\u003C\u002Fp>\u003Ch3>1. EnumADUser.py\u003C\u002Fh3>\u003Cp>User enumeration function, supporting both TCP and UDP protocols\u003C\u002Fp>\u003Cp>Command example:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>EnumADUser.py 192.168.1.1 test.com user.txt tcp\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The output result is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016753872_12_ef95d529dc.jpeg\">\u003C\u002Fp>\u003Ch3>2. ADPwdSpray.py\u003C\u002Fh3>\u003Cp>Password verification function, supports TCP and UDP protocols, supports plaintext passwords and NTLM hash\u003C\u002Fp>\u003Cp>Command example 1:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>ADPwdSpray.py 192.168.1.1 test.com user.txt clearpassword DomainUser123! tcp\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Result output as shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016754370_13_0f753da522.jpeg\">\u003C\u002Fp>\u003Cp>Command example 2:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>ADPwdSpray.py 192.168.1.1 test.com user.txt ntlmhash e00045bd566a1b74386f5c1e3612921b udp\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Result output as shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016754720_14_801f2769e5.jpeg\">\u003C\u002Fp>\u003Ch2>0x06 Detection of Kerberos pre-auth bruteforcing\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Kerbrute uses the Kerberos pre-auth protocol and does not generate logs (4625 - An account failed to log on)\u003C\u002Fp>\u003Cp>However, it generates the following logs:\u003C\u002Fp>\u003Cul>\u003Cli>Log generated when password verification is successful (4768 - A Kerberos authentication ticket (TGT) was requested)\u003C\u002Fli>\u003Cli>Log generated when password verification fails (4771 - Kerberos pre-authentication failed)\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x07 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article conducts testing and analysis of kerbrute, implements identical functionality using Python with added support for TCP protocol and NTLM hash verification, releases the source code, details script development specifics, and provides detection methods for Kerberos pre-authentication brute-forcing.\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>","text","ltr","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>In the previous article 'Penetration Basics - Brute-forcing Domain User Passwords via LDAP Protocol', methods for brute-forcing domain user passwords through the LDAP protocol were introduced, with the key characteristic being that it generates logs (4625 - An account failed to log on).\u003C\u002Fp>\u003Cp>However, when using kerbrute for brute-forcing via Kerberos pre-authentication, no logs (4625 - An account failed to log on) are generated. Therefore, I conducted further research on kerbrute, implemented the same functionality using Python, and added support for TCP protocol and NTLM hash verification. This article documents my research process and learning insights.\u003C\u002Fp>\u003Ch2>0x01 Overview\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cul>\u003Cli>Introduction to kerbrute\u003C\u002Fli>\u003Cli>Principles of kerbrute\u003C\u002Fli>\u003Cli>Details of implementing kerbrute in Python\u003C\u002Fli>\u003Cli>Open-source code pyKerbrute\u003C\u002Fli>\u003Cli>Detection of Kerberos pre-authentication brute-forcing\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Applicable Scenarios for kerbrute\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Applicable scenarios: User enumeration and password brute-forcing against domain users from outside the domain\u003C\u002Fp>\u003Cp>Since there is no domain user password, it is not possible to enumerate all domain users via the LDAP protocol, and using the LDAP protocol for brute-force attacks will generate logs (4625 - An account failed to log on).\u003C\u002Fp>\u003Cp>Using kerbrute has the following advantages:\u003C\u002Fp>\u003Cul>\u003Cli>Kerberos pre-auth bruteforcing is faster.\u003C\u002Fli>\u003Cli>It does not generate logs (4625 - An account failed to log on).\u003C\u002Fli>\u003C\u002Ful>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>The default port for Kerberos pre-auth is 88.\u003C\u002Fp>\u003Ch2>0x03 kerbrute testing\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The test environment is shown in the figure below.\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016723063_0_df77a6ea57-1.jpeg\">\u003C\u002Fp>\u003Cp>kerbrute is developed in Go, and GitHub provides compiled files at the following address:\u003C\u002Fp>\u003Cp>https:\u002F\u002Fgithub.com\u002Fropnop\u002Fkerbrute\u002Freleases\u003C\u002Fp>\u003Cp>kerbrute mainly includes the following two functions:\u003C\u002Fp>\u003Ch3>1. User enumeration\u003C\u002Fh3>\u003Cp>Used to verify whether a user exists, with the command as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>kerbrute_windows_amd64.exe userenum --dc 192.168.1.1 -d test.com user.txt\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016730023_1_1c4c85e5c3-1.jpeg\">\u003C\u002Fp>\u003Cp>Applicable scenario:\u003C\u002Fp>\u003Cp>Without knowing the domain user passwords, it is impossible to enumerate all domain users via the LDAP protocol. This method can be used to verify whether a user exists.\u003C\u002Fp>\u003Ch3>2. Password verification\u003C\u002Fh3>\u003Cp>After confirming the existence of a user, this function can be used to verify if the password is correct. The command is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>kerbrute_windows_amd64.exe passwordspray -d test.com user.txt DomainUser123!\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016733945_2_8e6f38681b-1.jpeg\">\u003C\u002Fp>\u003Cp>If login is successful, a log will be generated (4768 - A Kerberos authentication ticket (TGT) was requested), as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016737753_3_ba234ab04e-1.jpeg\">\u003C\u002Fp>\u003Ch2>0x04 Details of implementing kerbrute using Python\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>My idea is to implement the two main functions of kerbrute: user enumeration and password verification\u003C\u002Fp>\u003Cp>I referenced pykek for the part implementing the Kerberos protocol via Python.\u003C\u002Fp>\u003Cp>Next, I captured the packet content of kerbrute by packet sniffing, then constructed identical packets using Python.\u003C\u002Fp>\u003Cp>kerbrute uses the UDP protocol to implement Kerberos pre-authentication for validating plaintext passwords.\u003C\u002Fp>\u003Cp>During my research, I discovered that the same functionality can be achieved via the TCP protocol, and it can also validate NTLM hashes.\u003C\u002Fp>\u003Ch3>1. Implementing user enumeration with Python\u003C\u002Fh3>\u003Cp>Using Wireshark to capture packets generated by kerbrute's user enumeration feature.\u003C\u002Fp>\u003Cp>Using the UDP protocol, the content of packets sent during user enumeration is as shown in the figure below.\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016740679_4_604f9a4bd4-1.jpeg\">\u003C\u002Fp>\u003Cp>If the user exists, the returned packet content is as shown in the figure below.\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016744351_5_078921d114-1.jpeg\">\u003C\u002Fp>\u003Cp>Determination flag: error-code: eRR-PREAUTH-REQUIRED (25)\u003C\u002Fp>\u003Cp>If the user does not exist, the returned packet content is as shown in the figure.\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016746874_6_4eb839b739-1.jpeg\">\u003C\u002Fp>\u003Cp>Determination flag: error-code: eRR-C-PRINCIPAL-UNKNOWN (6)\u003C\u002Fp>\u003Cp>Next, I used Python to send UDP data, with the content identical to the packets during kerbrute user enumeration; receiving the response and determining user existence via the flag bits.\u003C\u002Fp>\u003Cp>The same functionality can also be achieved through the TCP protocol, only the packet format is different\u003C\u002Fp>\u003Cp>A string pack('&gt;I', len(data)) needs to be added in front of the TCP packet\u003C\u002Fp>\u003Cp>The specific code is as follows:\u003C\u002Fp>\u003Cp>TCP:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def send_req_tcp(req, kdc, port=88):\u003Cbr>    data = encode(req)\u003Cbr>    data = pack('&gt;I', len(data)) + data\u003Cbr>    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\u003Cbr>    sock.connect((kdc, port))\u003Cbr>    sock.send(data)\u003Cbr>    return sock\u003Cbr>\u003Cbr>def recv_rep_tcp(sock):\u003Cbr>    data = ''\u003Cbr>    datalen = None\u003Cbr>    while True:\u003Cbr>        rep = sock.recv(8192)\u003Cbr>        if not rep:\u003Cbr>            sock.close()\u003Cbr>            raise IOError('Connection error')\u003Cbr>        data += rep\u003Cbr>        if len(rep) &gt;= 4:\u003Cbr>            if datalen is None:\u003Cbr>                datalen = unpack('&gt;I', rep[:4])[0]\u003Cbr>            if len(data) &gt;= 4 + datalen:\u003Cbr>                sock.close()\u003Cbr>                return data[4:4 + datalen]\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>UDP:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def send_req_udp(req, kdc, port=88):\u003Cbr>    data = encode(req)\u003Cbr>    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\u003Cbr>    sock.connect((kdc, port))\u003Cbr>    sock.send(data)\u003Cbr>    return sock\u003Cbr>\u003Cbr>def recv_rep_udp(sock):\u003Cbr>    data = ''\u003Cbr>    datalen = None\u003Cbr>    while True:\u003Cbr>        rep = sock.recv(8192)\u003Cbr>        if not rep:\u003Cbr>            sock.close()\u003Cbr>            raise IOError('Connection error')\u003Cbr>        data += rep\u003Cbr>        if len(rep) &gt;= 4:\u003Cbr>            sock.close()\u003Cbr>            return data\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>2. Implement password verification using Python\u003C\u002Fh3>\u003Cp>Capture packets generated by kerbrute password verification function using Wireshark\u003C\u002Fp>\u003Cp>Using UDP protocol, the content of packets sent during password verification is shown in the figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016748712_7_607e827baa-1.jpeg\">\u003C\u002Fp>\u003Cp>Compared to user enumeration, password verification includes additional content (padata)\u003C\u002Fp>\u003Cp>The specific differences are as follows:\u003C\u002Fp>\u003Cp>The packet format sent during user enumeration is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016750167_8_8f9a66ae9b-1.jpeg\">\u003C\u002Fp>\u003Cp>The packet format sent during password verification is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016751192_9_79de3da93f-1.jpeg\">\u003C\u002Fp>\u003Cp>Therefore, the padata section content needs to be added in implementation\u003C\u002Fp>\u003Cp>If the password is correct, the returned packet content is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016752169_10_6da9f27bf0-1.jpeg\">\u003C\u002Fp>\u003Cp>If the password is incorrect, the returned packet content is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016752988_11_c4df4efada-1.jpeg\">\u003C\u002Fp>\u003Cp>For the specific packet structure, please refer to the RFC document at the following address:\u003C\u002Fp>\u003Cp>https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc1510#page-50\u003C\u002Fp>\u003Cp>To compute the padata-value, first convert the plaintext password into an NTLM hash before calculation.\u003C\u002Fp>\u003Cp>Therefore, this position can use not only plaintext passwords but also NTLM hashes.\u003C\u002Fp>\u003Cp>Part of the encrypted Python code is as follows:\u003C\u002Fp>\u003Cp>Using plaintext password:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>clearpassword = DomainUser123!\u003Cbr>user_key = (RC4_HMAC, ntlm_hash(clearpassword).digest())\u003Cbr>pa_ts = build_pa_enc_timestamp(current_time, user_key)\u003Cbr>as_req['padata'][0]['padata-value'] = encode(pa_ts)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Using NTLM hash:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>ntlmhash = e00045bd566a1b74386f5c1e3612921b\u003Cbr>user_key = (RC4_HMAC, ntlmhash.decode('hex'))\u003Cbr>pa_ts = build_pa_enc_timestamp(current_time, user_key)\u003Cbr>as_req['padata'][0]['padata-value'] = encode(pa_ts)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch2>0x05 Open-source code pyKerbrute\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The complete implementation code has been uploaded to GitHub, with the address as follows:\u003C\u002Fp>\u003Cp>An open-source project\u003C\u002Fp>\u003Cp>pyKerbrute is a Python implementation of kerbrute, offering the following two additional features compared to kerbrute:\u003C\u002Fp>\u003Cul>\u003Cli>Added support for TCP protocol\u003C\u002Fli>\u003Cli>Added verification for NTLM hash\u003C\u002Fli>\u003C\u002Ful>\u003Cp>pyKerbrute is divided into two functions: user enumeration and password verification\u003C\u002Fp>\u003Ch3>1. EnumADUser.py\u003C\u002Fh3>\u003Cp>User enumeration function, supporting both TCP and UDP protocols\u003C\u002Fp>\u003Cp>Command example:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>EnumADUser.py 192.168.1.1 test.com user.txt tcp\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The output result is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016753872_12_ef95d529dc-1.jpeg\">\u003C\u002Fp>\u003Ch3>2. ADPwdSpray.py\u003C\u002Fh3>\u003Cp>Password verification function, supports TCP and UDP protocols, supports plaintext passwords and NTLM hash\u003C\u002Fp>\u003Cp>Command example 1:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>ADPwdSpray.py 192.168.1.1 test.com user.txt clearpassword DomainUser123! tcp\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Result output as shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016754370_13_0f753da522-1.jpeg\">\u003C\u002Fp>\u003Cp>Command example 2:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>ADPwdSpray.py 192.168.1.1 test.com user.txt ntlmhash e00045bd566a1b74386f5c1e3612921b udp\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Result output as shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016754720_14_801f2769e5-1.jpeg\">\u003C\u002Fp>\u003Ch2>0x06 Detection of Kerberos pre-auth bruteforcing\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Kerbrute uses the Kerberos pre-auth protocol and does not generate logs (4625 - An account failed to log on)\u003C\u002Fp>\u003Cp>However, it generates the following logs:\u003C\u002Fp>\u003Cul>\u003Cli>Log generated when password verification is successful (4768 - A Kerberos authentication ticket (TGT) was requested)\u003C\u002Fli>\u003Cli>Log generated when password verification fails (4771 - Kerberos pre-authentication failed)\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x07 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article conducts testing and analysis of kerbrute, implements identical functionality using Python with added support for TCP protocol and NTLM hash verification, releases the source code, details script development specifics, and provides detection methods for Kerberos pre-authentication brute-forcing.\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>",116,"Onedaysec",6,"published","2026-02-02T07:25:19.687Z",{"title":37,"description":14,"keywords":38,"ogImage":39,"canonicalUrl":39,"noIndex":40},"Kerberos Pre-Auth Brute-Forcing: User Enumeration & Password Testing","kerbrute, Kerberos pre-authentication, user enumeration, password brute-forcing, penetration testing, pyKerbrute, domain security, no logs 4625",null,false,[],{"docs":43,"hasNextPage":40},[44,45,4,46,47],1134,1133,1131,1130,{"title":39,"description":39,"image":39},"2026-07-24T15:37:09.360Z","2026-07-23T16:02:34.645Z","draft","2026-07-23T16:16:54.150Z","2026-07-23T16:16:54.149Z"]