
Discovering CVE-2025-22381: Host Header Injection in the Aggie Open-Source Project
CVE-2025-22381: Aggie에서의 Host Header Injection
CVE-2025-22381에 대한 상세 분석 및 개념 증명(PoC) — Aggie 오픈소스 프로젝트에서 발견된 Host Header Injection 취약점입니다.
취약점 개요+
CVE ID: CVE-2025-22381
발표일: 2025년 10월 (MITRE 할당)
공개일: 2026년 2월
제보자: Anas Abderrahman Benbarek
발견일: 2025년 9월 17일
영향받는 프로젝트: TID-Lab/aggie
영향받는 버전: 모든 버전 (2.6.1 및 이전 버전 포함; 2026년 2월 기준 패치 없음)
심각도: 중간~높음 (추정 CVSS ~7.1–7.5)
영향: 피싱 공격을 통해 비밀번호 재설정 토큰 탈취 및 계정 탈취 가능.
배경
저는 GitHub에서 오픈소스 Node.js 프로젝트, 특히 인증 흐름을 처리하는 프로젝트를 리뷰하는 데 상당한 시간을 할애합니다. 2025년 9월, Aggie 저장소를 살펴보던 중 비밀번호 재설정 로직에서 즉시 눈에 띄는 부분을 발견했습니다. 일상적인 코드 리딩으로 시작된 일이 결국 CVE-2025-22381이 되었습니다. 이는 공격자가 비밀번호 재설정 이메일의 도메인을 제어할 수 있는 전형적인 Host Header Injection 취약점입니다.
발견 과정
저장소를 클론하고 lib/api/ 아래의 파일들을 읽기 시작했으며, 인증 및 이메일 생성과 관련된 부분에 집중했습니다.
lib/api/reset-password.js 파일에는 /reset-password 엔드포인트 로직이 포함되어 있습니다. 핵심 부분은 sendEmail 헬퍼 함수 내부에 있습니다.
function sendEmail(user, req, callback) {
var token = encodeToken(user);
mailer.sendFromTemplate({
template: 'forgotPassword',
user: user,
token: token,
host: req.headers.host, // ← 취약
protocol: req.protocol,
acceptLanguage: req.headers['accept-language']
}, callback);
}
host: req.headers.host 줄이 문제입니다. Express에서 req.headers.host는 Host HTTP 헤더에서 직접 가져오며, 이는 전적으로 공격자가 제어할 수 있습니다. 검증, 허용 목록, 또는 설정의 신뢰할 수 있는 도메인으로의 대체(fallback)가 전혀 없습니다.
초기 확인
README 지침(Ubuntu, nvm, npm install, 테스트 SMTP가 포함된 secrets.json)에 따라 로컬 인스턴스를 빠르게 설정하고 서버를 시작한 후 비밀번호 재설정을 트리거했습니다. 생성된 이메일 링크는 예상대로 localhost:3000을 사용했습니다.
그런 다음 조작된 Host 헤더로 요청을 재전송했습니다.
curl -X POST http://localhost:3000/reset-password \
-H "Host: evil-phish.example" \
-d "[email protected]"
MailHog을 통해 캡처된 이메일에는 다음이 포함되었습니다.
http://evil-phish.example/reset-password?token=...
확실한 증거입니다. 애플리케이션은 재설정 링크를 구성할 때 클라이언트가 제공한 Host 헤더를 신뢰합니다.
공격의 실제 작동 방식
공격자는 피해자의 이메일 주소로 비밀번호 재설정 요청을 보내지만, Host 헤더는 자신이 제어하는 도메인(예: evil-phish.example)으로 설정합니다.
Aggie는 합법적인 재설정 토큰(서버 측, 시간 제한, 설정 비밀로 암호화)을 생성합니다.
실제 도메인 대신 공격자의 도메인에 대한 링크가 포함된 이메일이 전송됩니다.
피해자는 이메일을 받고 링크를 클릭합니다(피싱 성공 조건).
피해자는 공격자의 서버에 도달합니다.
공격자의 서버는 다음을 수행할 수 있습니다.
공격자는 나중에 실제 도메인에서 캡처한 토큰을 사용하여 피해자의 비밀번호를 재설정합니다.
핵심 포인트: Host header injection만으로는 공격자가 토큰을 직접 사용할 수 없습니다. 공격자가 토큰을 자신의 인프라에 전달하려면 여전히 피해자가 악성 링크를 방문해야 합니다. 그렇기 때문에 사용자 상호작용 없이 직접 계정을 탈취하는 것이 아니라 피싱을 가능하게 하는 취약점입니다.
기술적 심각도 및 영향
이는 상황에 따라 중간에서 높은 심각도의 문제입니다.
많은 데이터베이스에서 CVSS ~7.1–7.5 범위로 등록합니다. 저는 Aggie가 민감한 모니터링(선거, 위기 상황)에 사용되는 프로덕션 환경에서 심각하다고 생각합니다. 여기서 성공적인 피싱은 전체 계정 탈취로 이어질 수 있기 때문입니다.
개념 증명 (상세 및 재현 가능)
환경
단계별
git clone https://github.com/TID-Lab/aggie.git
cd aggie
nvm install
npm install
cp config/secrets.json.example config/secrets.json
# secrets.json 편집 → adminPassword 설정, 필요한 경우 테스트 SMTP 추가
npm start
웹 UI 또는 MongoDB를 통해 테스트 사용자 생성.
악의적인 재설정 트리거:
curl -i -X POST http://localhost:3000/reset-password \
-H "Host: evil-phish.example" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "[email protected]"
http://evil-phish.example/reset-password?token=...을 가리킵니다.공개 타임라인
2025년 9월 17일: 발견 + 로컬 PoC 2025년 9월 17일: 전체 세부 정보 및 PoC를 [email protected]로 이메일 전송 2025년 9월 17일: MITRE에 제출 (서비스 요청 1926730 / MCID15453119) 2025년 10월 9일: MITRE에서 CVE-2025-22381 할당 2025년 10월~12월: 공개 패치나 응답 없음 2026년 2월: 공개 공개 (이 글)
권장 수정 사항
코드 변경
lib/api/reset-password.js에서 취약한 줄을 신뢰할 수 있는 값으로 대체:
// lib/api/reset-password.js, sendEmail() 내부
const config = require('../../config/secrets').get();
// 옵션 A: 설정 값을 신뢰 (단일 도메인에 권장)
const host = config.appHost || 'localhost:3000';
// 그런 다음 사용:
mailer.sendFromTemplate({
template: 'forgotPassword',
user: user,
token: token,
host: host, // 신뢰할 수 있는 변수 사용
protocol: config.environment === 'production' ? 'https' : req.protocol,
acceptLanguage: req.headers['accept-language']
}, callback);
설정
secrets.json에 추가:
"appHost": "https://your-real-domain.com"
마무리 생각
Host Header Injection은 2025~2026년에도 여전히 놀라울 정도로 흔하며, 특히 몇 년 전에 시작되어 충분히 감사(audit)되지 않은 프로젝트에서 그렇습니다. Aggie는 시빅 테크 및 위기 모니터링에 유용한 도구입니다. 유지보수자가 곧 패치를 적용하기를 바랍니다.
Aggie를 유지보수하거나 사용한다면 배포 환경을 확인하고 공식 릴리스가 나올 때까지 수동으로 패치하시기 바랍니다. 질문이 있거나 다른 프로젝트에서 비슷한 이슈에 대해 논의하고 싶다면 언제든지 연락 주세요.
읽어주셔서 감사합니다. 안전하세요.