NOW Briefing
WordPress 로그인 화면 XSS 연계 PHP 코드 실행 취약점 (CVE-2026-64638)
■ 개요
WordPress는 웹사이트와 블로그를 만들고 운영할 때 널리 쓰이는 오픈소스 콘텐츠 관리 시스템(CMS)입니다. 관리자 화면에서 글 작성, 테마·플러그인 설치, 사용자 관리 같은 기능을 제공합니다. 이번 취약점은 로그인 실패 오류 메시지에 사용자 입력값이 반영되는 과정에서 발생했습니다.
공격자는 인증 없이 로그인 화면의 아이디 값에 임의 문자열을 넣을 수 있습니다. 이 값이 두 단계의 HTML 정리 과정을 거치며 서로 다르게 해석되면, 오류 메시지 안에 HTML·스크립트가 삽입될 수 있습니다.
XSS 자체는 인증 없이 유발할 수 있지만, 사이트 장악으로 이어지는 공개 공격 체인은 로그인한 단일 사이트 관리자(single-site admin)가 조작된 링크를 여는 상황을 전제로 합니다. 공개 RCE 체인은 애플리케이션 비밀번호와 REST API JSONP가 사용 가능하고, 대시보드에서 파일 변경·플러그인 업로드가 가능한 설정을 요구합니다. 공식 CVSS도 사용자 상호작용(UI)을 필요 조건으로 둡니다. 따라서 대규모 자동 웜보다는 관리자를 겨냥한 피싱형 공격에 더 가까운 시나리오로 봐야 합니다.
WordPress는 2026년 8월 6일 7.0.3 릴리스에서 총 12개 취약점을 수정했고, 이번 취약점 수정은 4.7 브랜치까지 23개 구버전 브랜치에 백포트됐습니다. Patchstack은 이를 주요 항목으로 다뤘고, 8월 7~8일 사이 GitHub에는 공격 체인을 설명하거나 검증하는 공개 저장소가 등장했습니다. 핵심 문제는 특정 플러그인이 아니라 WordPress 코어에 있습니다.
■ 요약
| 항목 | 내용 |
|---|---|
| CVE ID | CVE-2026-64638 (XSS2Shell) |
| CVSS | CVSS v4.0 8.9 / High (HackerOne CNA) |
| 취약점 유형 | CWE-79, 인증 없는 반사형 XSS 관리자 상호작용 이후 PHP 코드 실행 체인으로 연계 가능 |
| 영향 | • 인증 없이 로그인 화면에 스크립트 삽입 가능 • 관리자 세션 권한으로 애플리케이션 비밀번호 탈취 가능 • 악성 플러그인 ZIP 업로드를 통한 서버 측 PHP 코드 실행 가능 • 사이트 전체 장악 및 지속성 확보로 이어질 수 있음 |
| 취약 버전 | NVD·벤더 기준 7.0.3 미만 전체 버전(4.7 브랜치까지 백포트) 연구자 분석에서는 XSS 영향 범위를 WordPress 6.4~7.0.2로 보거나 4.7까지 포함하는 등 차이가 있음 |
| 패치 버전 | 7.0.3 (2026-08-06) 6.9.6 / 6.8.7 / 6.7.6 / 6.6.6 / 6.5.9 / 6.4.9 등 구버전 브랜치 최신 보안 릴리스(4.7.34까지) |
■ 기술 분석
취약점은 wp-includes/user.php의 wp_authenticate_username_password() 흐름에서 발생합니다. 이 함수는 아이디와 비밀번호로 사용자를 인증하는 로그인 처리 함수이며, 비밀번호가 틀렸을 때 입력된 사용자명을 오류 메시지에 넣습니다.
해당 사용자명은 PHP의 strip_tags()와 WordPress의 KSES 정리 과정을 거칩니다. strip_tags()는 HTML·PHP 태그를 제거하는 PHP 함수이고, KSES는 WordPress가 허용된 HTML 태그와 속성만 남기기 위해 사용하는 필터입니다. 두 파서가 태그를 인식하는 방식이 달랐던 점이 문제였습니다.
strip_tags()는 < 바로 뒤에 태그명이 와야 태그로 처리하지만, KSES는 <와 태그명 사이의 공백을 허용합니다. 이 차이 때문에 < area id=...>처럼 공백이 들어간 문자열은 첫 단계에서는 일반 텍스트로 남고, 다음 단계에서는 허용된 요소로 해석될 수 있습니다. 보고자가 사용한 정확한 우회 문자열은 공개되지 않았으며, 여기의 표현은 원리를 설명하기 위한 예시입니다.
후속 공격 체인은 DOM 클로버링을 사용합니다. DOM 클로버링은 페이지에 삽입한 요소의 id나 name 값으로 브라우저의 전역 객체 참조를 덮어쓰는 기법입니다. 공개 분석에서는 이를 이용해 관리자 사용자 프로필 화면의 스크립트인 wp-admin/js/user-profile.js가 참조하는 ajaxurl, color-picker, wp-generate-pw 값을 바꾸는 흐름이 설명됐습니다.
이후에는 REST API JSONP와 SOME(Same Origin Method Execution)을 이용해 애플리케이션 비밀번호 승인 화면인 wp-admin/authorize-application.php 흐름을 조작합니다. 생성된 애플리케이션 비밀번호가 공격자의 success_url로 전달되면, 공격자는 플러그인 업로드 nonce를 얻어 악성 ZIP을 업로드할 수 있습니다. 플러그인이 활성화되지 않아도 업로드된 PHP 파일에 직접 접근하면 코드가 실행될 수 있으므로, 사이트 설정 조건이 맞으면 서버 측 PHP 코드 실행으로 이어질 수 있습니다.
패치는 정리 함수 자체를 바꾸기보다 출력 지점에서 사용자 입력을 HTML 인코딩하는 방식으로 적용됐습니다. 같은 릴리스에서는 잘못된 사용자명·이메일 오류 처리에도 동일한 성격의 보완이 반영됐습니다.
/* [패치 전] */
sprintf(
/* translators: %s: User name. */
__( '<strong>Error:</strong> The password you entered for the username %s is incorrect.' ),
'<strong>' . $username . '</strong>'
)
/* [패치 후] */
sprintf(
/* translators: %s: User name. */
__( '<strong>Error:</strong> The password you entered for the username %s is incorrect.' ),
'<strong>' . esc_html( $username ) . '</strong>'
)
■ PoC
공개 저장소(imbas007/CVE-2026-64638-POC)에는 취약한 WordPress를 실행하는 컨테이너 설정, 버전 확인 스캐너, 공격 과정을 구현한 스크립트가 들어 있습니다. 다만 우회 문자열은 공개하지 않았고, DOM 클로버링과 플러그인 설치 같은 후속 단계도 코드에서 제외했다고 설명합니다. 또한 3~7단계는 로그인한 관리자가 페이로드와 상호작용해야 성립한다고 밝힙니다.
공개된 코드는 주로 입력값의 반사 여부를 확인하고 링크를 만드는 수준입니다. 공격자는 로그인 폼의 파라미터를 조합해 링크 하나를 만들고, 아이디에는 우회 문자열을 넣고 나머지 항목은 로그인이 반드시 실패하도록 채웁니다. 관리자가 이 링크를 열면 로그인 실패 오류 메시지가 화면에 표시되는 순간 삽입된 마크업이 관리자 화면과 같은 출처에서 실행됩니다.
// 방어용으로 단순화한 예시
const params = new URLSearchParams();
params.set('log', payload); // 실패 로그인 오류 문구에 반영되는 아이디 값
params.set('pwd', 'bogus'); // 로그인 실패를 강제
params.set('wp-submit', 'Log+In');
// ... 파서 불일치 우회 문자열 및 후속 단계 생략 ...
const url = base + '?' + params.toString();
⚠️ WARN
이 코드는 실제 익스플로잇이 아니라 공개 PoC의 검증 관점을 방어적으로 단순화한 예시입니다.
반드시 격리된 테스트 사이트에서만 검증해야 합니다. 운영 사이트에 이런 링크를 보내는 행위는 실제 공격으로 간주될 수 있습니다.
■ 탐지 및 점검
• 웹 서버·WAF 로그에서 wp-login.php의 log 파라미터에 꺾쇠 문자(<, URL 인코딩 %3C)가 들어간 요청을 찾습니다. 특히 < area·< div·< button처럼 꺾쇠 뒤에 공백이 붙은 값을 확인합니다.
• 관리자 권한 계정의 프로필 화면에서 발급 이력이 확인되지 않는 애플리케이션 비밀번호가 등록돼 있는지 확인합니다.
• wp-admin/authorize-application.php 요청의 success_url이 외부 도메인을 가리키는지 확인합니다. 이런 요청이 있었다면 직후 수 초 안에 애플리케이션 비밀번호가 만들어진 기록도 함께 찾습니다.
• REST 요청 로그에서 _jsonp= 콜백 값에 점(.)이 포함된 비정상 호출이 있는지 확인합니다.
• POST /wp-admin/update.php?action=upload-plugin 요청 뒤에 업로드한 플러그인 파일로 직접 접근한 기록이 이어지는지 확인합니다. wp-content/plugins 아래에서 최근 바뀐 파일의 무결성도 점검합니다.
✅ CHECK
위 지표 중 하나라도 확인되면 패치 적용만으로 종료하지 말고 침해 대응 절차로 전환해야 합니다.
반사형 XSS는 관리자가 링크를 연 브라우저에서 실행됩니다. 로그에 흔적이 없더라도 관리자가 피싱 메일을 받았는지 함께 확인해야 합니다.
■ 대응 방안
• WordPress를 7.0.3으로 업데이트하거나, 구버전 브랜치를 유지해야 한다면 해당 브랜치의 최신 마이너 버전(예: 6.9.6, 6.8.7, 4.7.34)으로 올립니다.
• 코어 자동 업데이트를 꺼 둔 사이트는 wp-config.php의 설정을 확인하고, 보안 릴리스가 제때 적용되도록 조정합니다.
• 침해 정황이 확인되면 관리자 계정의 애플리케이션 비밀번호를 모두 폐기하고 비밀번호를 재설정해 기존 세션을 무효화합니다.
• wp-config.php에서 DISALLOW_FILE_MODS를 설정해 대시보드를 통한 플러그인·테마 설치와 편집을 차단합니다. DISALLOW_FILE_EDIT는 편집기만 막고 플러그인 업로드 경로는 남기므로, 이 공격 체인의 업로드 단계를 막으려면 DISALLOW_FILE_MODS가 필요합니다.
• 운영에 필요하지 않다면 필터를 사용해 애플리케이션 비밀번호(wp_is_application_passwords_available)와 REST API JSONP(rest_jsonp_enabled) 기능을 끕니다.
• 관리자 권한 계정 수를 최소화하고, 로그인 링크가 담긴 메일을 주의하도록 관리자에게 따로 안내합니다.
💡 TIP
패치 적용이 즉시 어려운 환경에서는 웹 방화벽(WAF)에서 wp-login.php의 log 파라미터에 포함된 꺾쇠 문자를 차단하거나 /wp-admin/update.php?action=upload-plugin 접근을 제한하는 규칙을 임시 대응책으로 적용할 수 있습니다.
우회 변형이 나올 수 있으므로 이 규칙에만 의존해서는 안 됩니다. 임시로 차단한 뒤 반드시 패치를 적용해야 합니다.
[참고 자료]
• PWN.AI - XSS2Shell: WordPress Preauth XSS to RCE Chain (CVE-2026-64638)
• WordPress News - WordPress 7.0.3 Release
• WordPress Documentation - Version 7.0.3
• WordPress/WordPress - 7.0.2...7.0.3 diff
• IONSEC - XSS2Shell: WordPress CVE-2026-64638 Explained
• Patchstack - WordPress 7.0.3 Released: 12 Vulnerabilities Found and Fixed



