Notepad++ v8.9.6.2 HMAC 무결성 검사 우회 취약점
개요
Notepad++ v8.9.6.2에서 HMAC 무결성 검사를 우회해 악성 커맨드를 실행할 수 있는 취약점을 발견했습니다. 관리자 권한 없이, UAC 없이, 디스크에는 정상 파일이 남아있는 상태에서 공격이 성립합니다.
| 항목 | 내용 |
|---|---|
| 영향 버전 | v8.9.6.2 |
| 패치 버전 | v8.9.6.4 |
| 심각도 | High |
| GHSA | GHSA-qm4c-qg8p-qfcr |
배경: Notepad++의 HMAC 검사
Notepad++는 사용자가 단축키에 외부 커맨드를 등록할 수 있습니다. 이 정보는 shortcuts.xml에 저장되는데, 공격자가 이 파일을 조작해 악성 커맨드를 심을 수 있다는 문제가 있었습니다.
이를 막기 위해 v8.9.6.2에서 HMAC-SHA256 무결성 검사를 도입했습니다.
동작 방식
- 커맨드 실행 시,
shortcuts.xml을 디스크에서 다시 읽음 MachineGUID를 키로 HMAC-SHA256 계산config.xml에 저장된 값과 비교- 일치하면 실행, 불일치하면 차단
좋은 아이디어처럼 보이지만, 여기에 치명적인 결함이 있었습니다.
취약점 분석: TOCTOU란?
**TOCTOU(Time-of-Check, Time-of-Use)**는 “검사한 시점”과 “사용하는 시점”이 달라서 생기는 경쟁 조건(Race Condition) 취약점입니다.
Notepad++의 경우:
| 단계 | 데이터 출처 | 시점 |
|---|---|---|
| HMAC 검사 (CHECK) | 디스크의 shortcuts.xml | 커맨드 실행 시 |
| 커맨드 실행 (USE) | 메모리의 _userCommands 벡터 | 시작 시 로드, 이후 갱신 없음 |
검사하는 파일과 실행에 쓰이는 데이터가 다릅니다.
취약한 코드 (NppCommands.cpp: 4328–4359)
// TIME OF CHECK — 디스크 파일을 읽어서 HMAC 검증std::string currentHMAC = computeHMAC( getMachineGUID(), getFileContent(nppParams.getShortcutsPath().c_str()));
if (currentHMAC != nppGUI._shortcutsXmlHmacInConfig){ return;}
// TIME OF USE — 시작 시 로드된 메모리 벡터에서 실행 (갱신 없음)int i = id - ID_USER_CMD;const vector<UserCommand>& cmds = nppParams.getUserCommandList();UserCommand ucmd = cmds[i];
Command cmd(string2wstring(ucmd.getCmd(), CP_UTF8));cmd.run(_pPublicInterface->getHSelf());공격 시나리오
전제 조건
shortcuts.xml 쓰기 권한
- 포터블 설치:
notepad++.exe와 같은 폴더 - 일반 설치:
%AppData%\Notepad++\
공격 흐름
shortcuts.xml을 악성 버전으로 교체
실행 커맨드:calc.exe- Notepad++ 실행
메모리(_userCommands)에 악성 커맨드 로드 - 즉시 디스크에 정상
shortcuts.xml복원
HMAC 일치 상태 WM_COMMAND(21000)전송 또는Ctrl+Alt+P입력- HMAC 검사: 디스크의 정상 파일 확인 → 통과
- 실행: 메모리의 악성 커맨드 →
calc.exe실행
실행 타임라인
| 시간 | 이벤트 | 상태 |
|---|---|---|
| T+0.0s | 악성 shortcuts.xml 디스크에 배치 | disk=악성 |
| T+0.0s | Notepad++ 실행 | disk=악성 |
| T+0.1s | _userCommands[0]="calc.exe" 메모리 로드 | mem=악성 |
| T+4.2s | Notepad++ 초기화 완료 | mem=악성 |
| T+4.2s | 정상 shortcuts.xml 복원 | disk=정상 / mem=악성 |
| T+4.7s | WM_COMMAND(21000) 전송 | |
| T+4.7s | HMAC 검사: 디스크 정상 파일 → 통과 | CHECK OK |
| T+4.7s | 실행: 메모리 악성 커맨드 → calc.exe | EXPLOIT |
| T+4.7s | calc.exe 실행 (PID 35932) | CONFIRMED |
영향
| 항목 | 내용 |
|---|---|
| 공격 유형 | 공격자 제어 커맨드 실행 |
| 관리자 권한 | 불필요 |
| UAC | 없음 |
| 탐지 난이도 | 높음 — 실행 내내 디스크에는 정상 파일이 존재 |
| CVSS (추정) | AV:L / AC:H / PR:L / UI:N / S:U / C:H / I:H / A:H |
PoC
PoC 스크립트는 GitHub에 공개했습니다:
실행 환경
- Notepad++ v8.9.6.2 포터블 x64
doLocalConf.xml존재 (포터블 모드)- 정상
shortcuts.xml에 대한 유효한 HMAC이config.xml에 존재
검증 결과
calc.exe실행 확인 (PID 35932), 실행 내내 디스크에는 정상 파일 유지
패치 분석
v8.9.6.4 패치 커밋 4f7563c에서 HMAC 검증 시점을 시작 시로 옮겼습니다.
| 항목 | v8.9.6.2 (취약) | v8.9.6.4 (패치) |
|---|---|---|
| HMAC 검사 시점 | 커맨드 실행 시, 디스크 재읽기 | 시작 시, 파싱과 동시 |
| 커맨드 출처 | 시작 시 로드된 메모리 | 시작 시 검증된 메모리 |
| TOCTOU 윈도우 | 시작 ~ 실행 | 제거됨 |
검증과 로드가 동시에 이루어지므로 파일 교체 공격 자체가 불가능해집니다.
공개 타임라인
| 날짜 | 내용 |
|---|---|
| 2026-06-02 15:51 | 개발팀에 최초 제보 |
| 2026-06-04 | v8.9.6.4 릴리즈 (패치 포함) |
| 2026-06-05 | Advisory 공개 (GHSA-qm4c-qg8p-qfcr) |
마무리
이번 취약점은 보안 메커니즘을 설계할 때 검사 대상과 사용 대상이 반드시 동일해야 한다는 원칙을 잘 보여줍니다. HMAC 검사 자체는 올바른 방향이었지만, 검사 시점과 실행 시점 사이의 간격이 공격 벡터가 됐습니다.
개발팀의 빠른 대응에 감사드립니다.