개요

Notepad++ v8.9.6.2에서 HMAC 무결성 검사를 우회해 악성 커맨드를 실행할 수 있는 취약점을 발견했습니다. 관리자 권한 없이, UAC 없이, 디스크에는 정상 파일이 남아있는 상태에서 공격이 성립합니다.


배경: Notepad++의 HMAC 검사

Notepad++는 사용자가 단축키에 외부 커맨드를 등록할 수 있습니다. 이 정보는 shortcuts.xml에 저장되는데, 공격자가 이 파일을 조작해 악성 커맨드를 심을 수 있다는 문제가 있었습니다.

이를 막기 위해 v8.9.6.2에서 HMAC-SHA256 무결성 검사를 도입했습니다.

동작 방식:

  1. 커맨드 실행 시, shortcuts.xml을 디스크에서 다시 읽음
  2. MachineGUID를 키로 HMAC-SHA256 계산
  3. config.xml에 저장된 값과 비교
  4. 일치하면 실행, 불일치하면 차단

좋은 아이디어처럼 보이지만, 여기에 치명적인 결함이 있었습니다.


취약점 분석: 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++\

공격 흐름:

  1. shortcuts.xml을 악성 버전으로 교체 (실행 커맨드: calc.exe)
  2. Notepad++ 실행 → 메모리(_userCommands)에 악성 커맨드 로드
  3. 즉시 디스크에 정상 shortcuts.xml 복원 → HMAC 일치 상태
  4. WM_COMMAND(21000) 전송 또는 Ctrl+Alt+P 입력
  5. HMAC 검사: 디스크의 정상 파일 확인 → 통과
  6. 실행: 메모리의 악성 커맨드 → 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 검사 자체는 올바른 방향이었지만, 검사 시점과 실행 시점 사이의 간격이 공격 벡터가 됐습니다.

개발팀의 빠른 대응에 감사드립니다.

참고:

 

#Description

tarfile module -> tarinfo에서 "../" 와 같은 경로 문자를 검증하지 않아서 발생함.

 

 

exploit

 

#1. 파일에 "../"문자열을 이어붙여 .tar 파일을 만드는 스크립트 (make.py)

 - make.py를 실행하면 "exploit.tar" 이름의 .tar 파일이 생성됨.

  이 때, 안의 hackfile.txt는 "../hackfile.txt" 로 바뀌게 된다.

 

 

#2. .tar 파일을 압축해제하는 스크립트 (unlock.py)

 

 - unlock.py 스크립트를 실행하면 "../hackfile.txt"가 압축해제 되면서 바로위의 경로에 해당 파일이 생기게 된다.

 

 

 

#tarfile module - extractall 함수에서 받아온 인자에 "."나 "/" 기호가 포함되어 있으면

 "warring"경고를 출력하고 작동을 중지하게 모듈을 수정하였다.

 

 - 다시 실행 후

 - 경고 출력과 함께 파일이 생성되지 않았다.

 

 

 

 

참고 : https://www.trellix.com/en-us/about/newsroom/stories/threat-labs/tarfile-exploiting-the-world.html

 

feat. https://shinobu.tistory.com/

https://www.volexity.com/blog/2022/06/02/zero-day-exploitation-of-atlassian-confluence/

 

 

confluence 에서 제로데이 발생.

 

 

 

+ Recent posts