CVSS Metrics 알아보기
01. Background
취약점 리포트를 받으면 가장 먼저 보는 것은 CVSS 점수입니다. 이 점수에 따라 대응 순서가 정해지고, 스캐너 리포트도 자산 관리 대시보드도 대부분 이 숫자 하나로 정렬됩니다.
그런데 그 점수 옆에는 항상 아래와 같은 문자열이 붙어 있습니다.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N
이 문자열은 점수를 구성하는 한 축을 담당하는 부분입니다. 이전까지는 그냥 넘겼는데, 최근 관련 연구를 진행하면서 의미를 알게 됐고 유용하다 생각하여 이 글의 주제로 잡게 됐습니다.
본 글은 CVSS 3.1 버전을 대상으로 작성했습니다. 최신 버전은 4.0이지만 제가 많이 다루는 게 3.1이다 보니 3.1로 진행하게 됐습니다. 4.0 사양은 아래 링크에서 확인할 수 있습니다.
https://www.first.org/cvss/v4.0/
02. Why?
CVSS Metrics를 이해하면 뭐가 좋을까요?
CVSS Score는 취약점의 유형과 영향 등을 고려해 계산한 하나의 숫자입니다. 여러 정보를 하나로 압축하다 보니 그 이면에 보이지 않는 내용들이 있습니다.
1. 같은 점수가 같은 취약점을 뜻하지 않습니다.
아래 두 취약점은 모두 High에 해당하는 취약점이고 Score는 0.5밖에 차이 나지 않습니다.
CVE-2014-0160 Heartbleed 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
CVE-2016-5195 Dirty COW 7.0 CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
그런데 성격은 완전히 다릅니다. 하나는 인터넷에서 인증 없이 메모리를 읽는 취약점이고, 다른 하나는 이미 서버에 들어와 있어야 쓸 수 있는 로컬 권한상승 취약점입니다.
외부 노출 자산에 Heartbleed가 있다면 당장 처리해야 하지만, 접근이 통제된 내부 서버의 Dirty COW는 상대적으로 여유가 있을 수 있습니다. 점수 순서와 실제 대응 순서가 일치하지 않는 경우는 흔하기 때문에 점수만으로는 판단하기 어렵습니다.
2. CVE 설명을 다 읽지 않아도 윤곽이 잡힙니다
CVE 하나를 제대로 파악하려면 설명, 벤더 공지, 패치 노트까지 확인해야 합니다. 목록에 수십 건이 쌓여 있으면 현실적으로 다 살펴보기는 어렵습니다.
하지만 벡터를 기준으로 본다면 해당 취약점이 어떤 경로로 트리거되는지, 어떤 영향을 주는지 정도는 빠르게 파악할 수 있습니다. 자세히 볼 것과 나중에 볼 것을 나누는 기준으로 쓰기에 유용합니다.
3. 자동화할 수 있습니다.
제가 CVSS Metrics를 살펴보게 된 결정적인 계기입니다.
벡터는 정형화된 문자열이라 기계적으로 다루기 좋습니다. NVD API는 벡터 조건으로 직접 질의하는 파라미터를 제공하고, 여기에 CWE나 Impact 조건을 얹으면 목적에 맞는 목록을 정밀하게 뽑을 수 있습니다.
저 같은 경우 업무 특성상 RCE와 LPE 취약점을 주로 탐색하는데, 벡터 값을 통해 조건에 맞는 취약점만 걸러서 볼 수 있습니다.
CTI 관점에서도 마찬가지입니다. 최신 CVE를 모니터링하면서 파급력이 높은 것만 추려 보고서를 만드는 데 활용할 수 있습니다.
03. CVSS Metrics?
소프트웨어 및 하드웨어 보안 취약점의 심각도를 정량적으로 평가하기 위해 표준화된 지표 집합입니다.
CVSS v3.1 기준으로 Metrics는 크게 3가지 그룹으로 나뉩니다.
- 기본 지표 (Base Metric Group): 시간이나 환경 변화와 무관한 취약점 고유의 특성
- 시간적 지표 (Temporal Metric Group): 익스플로잇 공개 여부, 패치 존재 여부 등 시간에 따라 변하는 특성
- 환경적 지표 (Environmental Metric Group): 대상 자산의 중요도 등 특정 조직/환경에 국한되는 특성
이 세 지표를 종합하여 산출한 값이 CVSS Score이며, 각 세부 항목의 설정값을 표준화된 문자열 형태로 표현한 것이 Vector String입니다.
참고로 NVD나 벤더 공지에 표기되는 CVSS 점수는 보통 Base Metrics만 반영되어 있습니다. 규격은 Temporal과 Environmental을 선택 항목으로 두고 소비자가 직접 채우도록 권고하고 있지만, 실제로 그렇게 쓰는 경우는 많지 않은 것 같습니다.
이 글에서는 계산 방식은 다루지 않고 이 Vector 문자열을 중점으로 다뤄보겠습니다.
04. Vector 문자열의 구조
앞서 언급한 세 그룹이 그대로 벡터 문자열의 구조가 됩니다.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
└─┬─┘└─────────┬─────────┘
버전 Base
// Temporal과 Environmental은 선택 항목이라 보통 생략됩니다.
위 벡터는 유명한 취약점인 CVE-2021-44228, Log4Shell 취약점의 벡터 값입니다. 해당 취약점의 Base Score는 10.0(Critical)으로, 산출 가능한 최댓값에 해당합니다.
v3.1 기준 Base는 세 부분으로 나눠집니다.
AV:N/AC:L/PR:N/UI:N / S:C / C:H/I:H/A:H
└────┬────┘ └┬┘ └──┬──┘
Exploitability Scope Impact
- Exploitability (AV, AC, PR, UI) — 공격이 성립하기까지 필요한 조건
- Scope (S) — 피해가 원래 컴포넌트를 넘어가는지 여부
- Impact (C, I, A) — 공격에 성공했을 때 발생하는 결과
쉽게 앞부분(Exploitability)은 "이 취약점을 쓰려면 뭐가 필요한가", 뒷부분(Impact)은 "성공하면 뭘 얻는가"에 해당하고, Scope는 그 결과가 어디까지 번지는지로 볼 수 있습니다.
아래에서 각 항목을 설명하면서 이 벡터 값을 하나씩 해석해보겠습니다.
Exploitability
AV (Attack Vector)
공격자가 취약점에 도달하는 경로를 의미하는 필드입니다.
| 값 |
의미 |
N Network |
인터넷 등 원격에서 패킷 전송만으로 공격 가능 (ex - RCE 취약점) |
A Adjacent |
동일한 물리적 네트워크(Wi-Fi, 블루투스) 접근 필요 |
L Local |
로컬 쉘이나 SSH 접근 필요 (ex - LPE 취약점) |
P Physical |
물리적 조작(USB 등) 필요 |
Log4Shell 취약점의 경우 HTTP 요청 등 원격에서 payload만 전송하면 취약점을 트리거할 수 있기 때문에 Network 값을 가집니다. 반면에 DirtyCow 같은 LPE 취약점은 대상 로컬 쉘에서 트리거가 가능하기 때문에 Local 값을 가지게 됩니다.
로컬보다는 원격에서 공격 가능한 취약점이 잠재적 공격자 수가 더 많기 때문에 AV가 N일 경우 높은 CVSS 점수를 받게 됩니다.
AC (Attack Complexity)
공격 성공을 위해 통제 불가능한 변수(운, 환경)가 필요한지 여부를 나타내는 필드입니다.
| 값 |
의미 |
L Low |
특수 조건 불필요. 언제든 재현 가능 |
H High |
공격자 통제 밖의 조건에 성공 여부가 좌우됨 |
여기서 통제 밖의 조건이란 Heap spray나 레이스 컨디션처럼 실패 확률이 있는 경우를 말합니다. 대표적으로는 한 번에 공격이 성공하지 않고 여러 번 시도해야 하는 취약점이 이에 해당합니다.
Log4Shell의 경우 payload만 전달되면 바로 트리거되기 때문에 Low 값을 가집니다.
PR (Privileges Required)
공격 실행 전에 이미 갖고 있어야 하는 권한 수준을 나타내는 필드입니다.
| 값 |
의미 |
N None |
인증이나 권한이 전혀 필요 없음 |
L Low |
일반 사용자 계정과 같이 낮은 수준의 권한이 필요 |
H High |
관리자 계정과 같이 높은 수준의 권한이 필요 |
Unauthenticated 취약점이 바로 Privileges Required 값이 N인 취약점입니다.
Authenticated 취약점은 필요한 권한에 따라 L, H로 분류됩니다. 예를 들어 WordPress RCE 취약점인데 이를 트리거하기 위해 일반 사용자 계정이 필요하다면 L 값을 가집니다. 관리자 계정이 필요하다면 H인 식입니다.
Log4Shell의 경우 인증 여부와 무관하게 로그로 기록되기만 하면 트리거되기 때문에 None 값을 가집니다.
UI (User Interaction)
공격 과정에서 피해자(사용자)의 개입이 필요한지 여부를 나타내는 필드입니다.
| 값 |
의미 |
N None |
피해자 개입 불필요 |
R Required |
링크 클릭, 파일 열기 등 피해자의 능동적 행위 필요 |
피해자의 개입이 무엇인지는 XSS 취약점을 생각하면 이해가 쉽습니다. XSS는 payload를 보낸다고 해서 바로 터지는 게 아니라, 링크나 payload가 들어간 내용을 피해자가 읽어야 작동합니다. 따라서 XSS 같은 취약점들은 R 값을 가지게 됩니다.
Log4Shell 취약점은 사용자 개입 없이 바로 트리거 되기 때문에 None 값을 가집니다.
여기까지 오면 Log4Shell이 Exploitability 네 항목 모두 최상값이라는 걸 알 수 있습니다.
AV:N/AC:L/PR:N/UI:N
Scope (S)
취약점의 영향이 원래 취약한 컴포넌트를 넘어 다른 관리 영역까지 미치는지를 나타내는 필드입니다.
| 값 |
의미 |
U Unchanged |
영향이 취약한 컴포넌트 안에서 끝남 |
C Changed |
영향이 다른 관리 영역까지 미침 |
사실 이 필드는 아직 잘 이해하지 못했습니다. 사양 문서에서도 보안 권한(security authority)이라는 개념을 기준으로 설명하는데, 다른 필드들에 비해 추상적이라 값을 판단하는 기준이 명확하게 잡히지 않았습니다.
대략적으로는 취약한 대상과 실제 피해를 입는 대상이 다른 경우 C 값을 가진다고 이해하고 있습니다. 참고로 이 모호함 때문인지 v4.0에서는 Scope 필드가 제거되고, 영향받는 대상을 나눠서 표기하는 방식으로 바뀌었습니다.
Log4Shell의 경우 취약한 건 로깅 라이브러리인데 그 결과로 호스트 전체에서 코드가 실행되기 때문에 Changed 값을 가진다고 해석했습니다.
Impact (C / I / A)
공격에 성공했을 때 어떤 피해가 발생하는지를 나타내는 필드입니다. 정보보안의 3요소인 기밀성, 무결성, 가용성 각각에 대해 별도의 값을 가집니다.
| 필드 |
의미 |
C Confidentiality |
공격자가 봐서는 안 될 정보를 얼마나 볼 수 있게 되는지 |
I Integrity |
공격자가 데이터나 시스템을 얼마나 변조할 수 있게 되는지 |
A Availability |
대상 서비스의 가용성이 얼마나 손상되는지 |
세 필드 모두 아래 세 가지 값 중 하나를 가집니다.
| 값 |
의미 |
H High |
완전한 손실. 또는 일부여도 결정적인 손실 |
L Low |
손실은 있으나 공격자가 범위나 내용을 통제할 수 없음 |
N None |
해당 영역에는 영향 없음 |
세 값을 조합해서 보면 취약점의 유형이 대략 드러납니다.
| 조합 |
유형 |
C:H/I:H/A:H |
완전 장악 (ex - RCE) |
C:H/I:N/A:N |
정보 유출 (ex - Heartbleed, LFI) |
C:N/I:H/A:N |
변조 |
C:N/I:N/A:H |
서비스 거부 (ex - DoS) |
Log4Shell의 경우 원격 코드 실행이 가능하기 때문에 세 필드 모두 High 값을 가집니다.
Temporal / Environmental
지금까지 다룬 Base 외에 두 그룹이 더 있습니다. 벡터 문자열에는 거의 등장하지 않지만, 어떤 항목인지 간단하게 살펴보겠습니다.
Temporal은 시간에 따라 변하는 값입니다.
| 필드 |
의미 |
E Exploit Code Maturity |
익스플로잇이 얼마나 성숙했는지 (PoC 수준인지, 안정적으로 도는지) |
RL Remediation Level |
패치나 우회책이 나왔는지 |
RC Report Confidence |
보고 내용이 얼마나 확실한지 |
세 필드 모두 1.0 이하의 계수라 Base를 낮추는 방향으로만 작용합니다. 공식 패치가 나왔다면 그만큼 위험도가 내려간다고 보는 것입니다.
Environmental은 우리 환경 기준으로 값을 다시 매기는 그룹입니다. CR/IR/AR로 해당 자산에서 기밀성·무결성·가용성이 각각 얼마나 중요한지를 반영하고, MAV/MAC/MPR 같은 필드로 Base 값을 덮어쓸 수 있습니다.
05. Conclusion
CVSS Metrics를 읽을 수 있다면 해당 CVE의 대략적인 성격을 판단할 수 있습니다. 어떤 경로로 도달하는지, 어떤 전제 조건이 필요한지, 성공하면 무엇이 손상되는지가 여덟 글자에 들어 있습니다.
앞서 살펴본 Log4Shell의 벡터를 다시 보면 이렇게 읽힙니다.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H 10.0 CRITICAL
원격에서 도달 가능하고(AV:N), 특수한 조건 없이 재현되며(AC:L), 사전 권한도(PR:N) 사용자 개입도(UI:N) 필요 없습니다. 게다가 피해는 원래 컴포넌트를 넘어가고(S:C), 기밀성·무결성·가용성 모두 완전히 손상됩니다(C:H/I:H/A:H). 전제 조건은 하나도 없는데 피해는 전부인 셈입니다.
가장 유용한 건 역시 필터링이라고 생각합니다. Metrics의 각 항목으로 노이즈를 걸러내고 원하는 유형의 취약점만 모아, 다른 작업으로 연계해 사용할 수 있습니다.
실제로 NVD API는 벡터 조건으로 직접 질의할 수 있는 파라미터를 제공합니다.
// 원격에서 전제 조건 없이 트리거 가능한 취약점만
https://services.nvd.nist.gov/rest/json/cves/2.0?cvssV3Metrics=AV:N/AC:L/PR:N/UI:N
Telegram이나 여러 포럼에서 심각한 취약점만 골라 보여주는 CTI 서비스들도 이런 방식을 쓰지 않았을까 싶습니다. 목적에 맞는 조건을 걸어두면 최신 CVE를 모니터링하면서 필요한 것만 추려낼 수 있을 것입니다.
참고