
CVE-2018-4343: macOS 및 iOS의 GSSCred 데몬에서 발생하는 use-after-free 취약점에 대한 개념 증명(PoC)입니다.
com.apple.GSSCred XPC 서비스는 macOS 및 iOS에서 root로 실행되며, "move" 명령을 제대로 구현하지 않아 do_Move 함수에서 use-after-free(사용 후 해제) 조건이 발생합니다. GSSCred 서비스는 iOS의 기본 애플리케이션 샌드박스 내에서 접근할 수 있습니다.
이 프로그램은 이 취약점을 악용하여 GSSCred 서비스를 충돌시킵니다. GSSCred에서 코드 실행을 달성하려면 경합 창(race window) 동안 해제된 메모리를 제어된 데이터로 덮어써야 합니다. macOS High Sierra 10.13.2 Beta 17C79a에서 테스트되었습니다.
다음은 Heimdal-520의 do_Move 관련 부분으로, 중요하지 않은 오류 검사는 생략했습니다:
//
// 1. from and to are fully controlled UUID objects deserialized from the XPC request.
//
CFUUIDRef from = HeimCredMessageCopyAttributes(request, "from", CFUUIDGetTypeID());
CFUUIDRef to = HeimCredMessageCopyAttributes(request, "to", CFUUIDGetTypeID());
...
//
// 2. credfrom and credto are HeimCredRef objects looked up by the from and to UUIDs.
// CFDictionaryGetValue() returns the objects without adding a reference. Note that if
// the from and to UUIDs are the same, then credfrom and credto will both reference the
// same object.
//
HeimCredRef credfrom = (HeimCredRef)CFDictionaryGetValue(peer->session->items, from);
HeimCredRef credto = (HeimCredRef)CFDictionaryGetValue(peer->session->items, to);
...
//
// 3. credfrom is removed from the dictionary. Since there was only one reference
// outstanding, this causes credfrom to be freed.
//
CFMutableDictionaryRef newattrs = CFDictionaryCreateMutableCopy(NULL, 0, credfrom->attributes);
CFDictionaryRemoveValue(peer->session->items, from);
credfrom = NULL;
...
//
// 4. At this point we check credto. If credfrom and credto refer to the same object, then
// credto is a non-NULL pointer to the freed HeimCredRef object.
//
if (credto == NULL) {
...
} else {
//
// 5. Now we dereference credto, passing a value read from freed memory as a
// CFDictionaryRef object to CFDictionaryGetValue().
//
CFUUIDRef parentUUID = CFDictionaryGetValue(credto->attributes, kHEIMAttrParentCredential);
...
}
이 코드는 다음을 수행합니다:
from과 to라는 두 UUID를 역직렬화합니다. 요청은 완전히 제어할 수 있으므로 이 UUID들의 값을 임의로 설정할 수 있습니다. 이 두 UUID가 동일한지에 대한 검사는 없습니다.from과 to에 해당하는 HeimCredRef 객체 credfrom과 credto를 조회합니다. peer->session->items 사전은 현재 연결된 클라이언트 프로그램을 대신하여 GSSCred가 관리하는 모든 자격 증명을 저장합니다. CFDictionaryGetValue 함수는 HeimCredRef 객체에 대한 참조를 반환하지만 참조 횟수를 증가시키지는 않는다는 점에 유의하세요. 특히 from과 to가 같은 UUID라면 credfrom과 credto는 모두 참조 횟수가 1(포함하는 CFDictionary가 보유)인 동일한 HeimCredRef를 가리키게 됩니다.credfrom이 peer->session->items 사전에서 제거됩니다. 과 가 다른 UUID일 때는 객체가 해제된 후 다시 참조되지 않으므로 일반적으로 안전합니다. 그러나 과 가 같으면 가 나중에 참조되므로 문제가 발생합니다.이 프로그램은 이 경합 창을 이용하려 시도하지 않습니다. 대신 HeimCredRef 소멸자가 attributes 필드를 0으로 만들게 하여 CFDictionaryGetValue에서 NULL 포인터 역참조를 유발합니다.
빌드하려면 make를 실행하세요. 다양한 빌드 옵션은 Makefile 상단을 참조하세요.
exploit을 실행하면 GSSCred와 교환된 XPC 메시지 시퀀스가 표시됩니다:
$ ./GSSCred-move-uaf
create: <dictionary: 0x7ff359e07740> { count = 1, transaction: 0, voucher = 0x0, contents =
"attributes" => <dictionary: 0x7ff359e06b60> { count = 5, transaction: 0, voucher = 0x0, contents =
"kHEIMObjectType" => <string: 0x7ff359e06a00> { length = 19, contents = "kHEIMObjectKerberos" }
"kHEIMAttrBundleIdentifierACL" => <array: 0x7ff359e06a70> { count = 1, capacity = 1, contents =
0: <string: 0x7ff359e06aa0> { length = 1, contents = "*" }
}
"kHEIMAttrUUID" => <uuid: 0x7ff359e06b20> AB000000-0000-0000-0000-000000000000
"kHEIMAttrStoreTime" => <date: 0x7ff359e06c60> Sat Dec 09 15:09:56 2017 PST (approx)
"kHEIMAttrType" => <string: 0x7ff359e06ce0> { length = 17, contents = "kHEIMTypeKerberos" }
}
}
Event: <error: 0x7fff9959cc60> { count = 1, transaction: 0, voucher = 0x0, contents =
"XPCErrorDescription" => <string: 0x7fff9959cfd0> { length = 22, contents = "Connection interrupted" }
}
move: <error: 0x7fff9959cc60> { count = 1, transaction: 0, voucher = 0x0, contents =
"XPCErrorDescription" => <string: 0x7fff9959cfd0> { length = 22, contents = "Connection interrupted" }
}
"Connection interrupted" XPC 이벤트는 XPC 연결이 중단되었음을 나타내며, 이는 GSSCred가 종료되었기 때문일 가능성이 높습니다.
GSSCred-move-uaf 코드는 퍼블릭 도메인으로 공개됩니다. 이 코드를 참조하거나 사용할 때는 저를 출처로 표시해 주시기를 정중히 부탁드립니다.
fromtocredfromfromtocredtocredto가 NULL인지 확인합니다. credfrom과 credto가 같고 credfrom이 NULL이 아니었으므로 else 분기로 들어갑니다.credto를 역참조하여 attributes 필드를 읽고, 이를 CFDictionaryGetValue의 첫 번째 매개변수로 전달합니다. 만약 그 사이에 credto가 가리키는 해제된 메모리가 재할당되어 attributes 필드의 위치가 특수하게 제작된 가짜 CFDictionary 객체를 가리키도록 변경되었다면, 이 단계에서 코드 실행을 달성하는 것이 가능할 것입니다.