개요
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에 공개했습니다: v3s9er/notepad-plus-plus-security-
실행 환경:
- 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 검사 자체는 올바른 방향이었지만, 검사 시점과 실행 시점 사이의 간격이 공격 벡터가 됐습니다.
개발팀의 빠른 대응에 감사드립니다.
참고:





