
The Gravity Forms 플러그인(버전 2.9.28까지 테스트됨)은 `gform_get_config` AJAX 액션의 `form_ids` 매개변수를 통해 인증되지 않은 반사형 크로스사이트 스크립팅(XSS) 취약점에 노출되어 있습니다.
gform_get_config form_ids 매개변수를 통한 인증되지 않은 반사형 크로스사이트 스크립팅| 필드 | 값 |
|---|---|
| 영향을 받는 소프트웨어 | Gravity Forms (WordPress 플러그인) |
| 공급업체 | Rocketgenius, Inc. |
| 취약점 유형 | CWE-79: 웹 페이지 생성 중 입력의 부적절한 중화 (반사형 크로스사이트 스크립팅) |
| CWE 체인 | CWE-20 → CWE-116 → CWE-838 → CWE-79 (아래 CWE 분석 참조) |
| 영향을 받는 버전 | 2.9.28에서 확인됨(발견 시점 기준 최신 버전); 이전 버전도 영향을 받을 가능성이 높음 |
| 수정 버전 | 2.9.30.1 (핫픽스) |
| CVSS 3.1 점수 | 6.1 (중간) |
| CVSS 3.1 벡터 | AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N |
| 인증 필요 여부 | 없음 (인증되지 않음) |
| 사용자 상호작용 | 필요 (피해자가 공격자가 제어하는 페이지를 방문하거나 조작된 링크를 클릭해야 함) |
| 발견자 | Anthony Cihan — Obviam |
| 발견 날짜 | 2026-03-04 |
| 공개 날짜 | 2026-03-18 |
| CVE ID | CVE-2026-4406 |
WordPress용 Gravity Forms 플러그인(버전 2.9.28까지 테스트됨)은 gform_get_config AJAX 액션의 form_ids 매개변수를 통해 인증되지 않은 반사형 크로스사이트 스크립팅(XSS)에 취약합니다. 이 취약점은 사용자가 제공한 form_ids 값이 서버의 HTTP 응답에 어떠한 삭제(sanitization), 인코딩 또는 출력 이스케이프 없이 그대로 반사되기 때문에 발생합니다. 응답은 Content-Type: text/html; charset=UTF-8 헤더로 제공되며, 이로 인해 브라우저는 반사된 콘텐츠를 HTML로 파싱하고 렌더링하며, 주입된 스크립트 요소도 포함됩니다.
인증되지 않은 공격자는 이 취약점을 악용하여 대상 WordPress 사이트의 오리진 컨텍스트에서 임의의 JavaScript를 실행할 수 있습니다. gform_get_config 액션은 유효한 config_nonce를 요구하며, 이 nonce는 Gravity Forms 양식을 로드하는 모든 페이지의 HTML 소스에 공개적으로 포함되어 있으므로, 공격자는 악용 요청을 구성하기 전에 대상 사이트의 공개 페이지를 요청하기만 하면 유효한 nonce를 쉽게 얻을 수 있습니다.
성공적인 악용을 통해 공격자는 세션 쿠키를 탈취하고, 인증된 사용자(WordPress 관리자 포함)를 대신하여 작업을 수행하고, 사용자를 악성 사이트로 리디렉션하고, 페이지 콘텐츠를 훼손하거나, 관리자 계정 생성을 통해 지속적인 접근 권한을 확보할 수 있습니다.
POST /wp-admin/admin-ajax.php
### 취약한 액션```
gform_get_config
args POST 파라미터는 form_ids 배열을 포함하는 JSON 객체를 허용합니다. 이 배열의 값은 삭제(샌itization) 없이 JSON 응답 구조의 객체 키로 사용됩니다:```json
{"form_ids":["ATTACKER_CONTROLLED_VALUE"]}
### 응답 동작
서버는 `form_ids` 값을 처리하여 응답 본문 내 JSON 키로 반영합니다. 응답은 HTML 주석 마커로 감싸져 `text/html`로 제공됩니다:```
Content-Type: text/html; charset=UTF-8
<!-- gf:json_start -->{"success":true,"data":{"common":{"form":{"pagination":{"ATTACKER_CONTROLLED_VALUE":null}}}}}<!-- gf:json_end -->
form_ids 값은 정수로 검증되거나, 정화되거나, 필터링되지 않습니다. 서버는 HTML 태그와 이벤트 핸들러를 포함한 임의의 문자열 콘텐츠를 수락합니다.form_ids 값은 HTML 엔터티 인코딩 없이 응답 본문에 반영됩니다. <, >, ", '와 같은 문자는 수정되지 않은 채 통과합니다.Content-Type: text/html; charset=UTF-8로 제공되며, 이는 브라우저가 응답 본문을 HTML로 파싱하도록 지시합니다. 반영된 값 내의 모든 HTML 태그는 브라우저의 HTML 파서에 의해 인스턴스화되고 렌더링됩니다.gform_get_config 액션이 요구하는 config_nonce는 Gravity Forms 양식을 로드하는 모든 페이지의 JavaScript 구성 객체(gform_theme_config)에 포함되어 있습니다. 이 nonce는 모든 페이지에서 동일하며 특정 사용자 세션에 바인딩되지 않으므로, 인증되지 않은 사용자가 쉽게 획득할 수 있습니다.이 취약점은 체인으로 연결되어 악용 가능한 조건을 만들어내는 여러 기여 약점의 결과입니다. CWE-79가 CVE 보고 목적의 주요 분류이지만, 전체 체인은 각 실패가 어떻게 누적되어 악용을 가능하게 하는지 문서화합니다.
CWE-20 CWE-116 CWE-838 CWE-79 Improper Input → Improper Encoding → Inappropriate Encoding → Cross-Site Validation or Escaping of Output for Output Context Scripting (XSS) [EXPLOITABLE] form_ids accepts Reflected values are JSON data served as Browser parses arbitrary strings not HTML-entity text/html instead of injected HTML tags instead of integers encoded in response application/json and executes JS
### CWE-20: 부적절한 입력 검증 (기여 요인)
**역할:** 근본적인 활성화 요인 — 악성 데이터가 처리 파이프라인에 유입되도록 허용합니다.
`args` JSON 객체의 `form_ids` 매개변수는 숫자 형식의 식별자를 포함할 것으로 예상되지만, 어떠한 검증 없이 임의의 문자열 입력을 허용합니다. 유형 검사(`intval()`), 정규식 필터링(`^[0-9]+$`), 알려진 양식 ID에 대한 화이트리스트 비교, 길이 제한이 전혀 적용되지 않습니다.
**증거:** 서버는 HTML 태그, JavaScript 이벤트 핸들러 및 임의의 유니코드를 포함하는 `form_ids` 값을 거부하지 않고 수락하고 처리합니다.```json
{"form_ids":["<svg onload=alert(1)>"]} ← Accepted
{"form_ids":["3"]} ← Expected
역할: 핵심 취약점 — XSS 조건의 직접적인 원인.
서버가 form_ids 값을 포함하는 JSON 응답을 구성할 때 출력에 HTML 엔터티 인코딩을 적용하지 않습니다. HTML에서 특수한 의미를 갖는 문자(<, >, ", ', &)는 수정되지 않은 채 응답 본문으로 전달됩니다. PHP 함수 htmlspecialchars(), esc_html(), JSON_HEX_TAG 옵션을 사용한 wp_json_encode(), 또는 이에 상응하는 출력 인코딩 함수는 form_ids 값이 응답에 기록되기 전에 적용되지 않습니다.
증거: 리터럴 문자열 <svg onload=alert(document.domain)>이 입력과 바이트 단위로 동일하게 응답 본문에 나타나며, <svg onload=alert(document.domain)>로 변환되지 않습니다.
역할: 컨텍스트 확대 — 데이터 반영 문제를 실행 가능한 코드 주입으로 변환합니다.
응답 본문에는 JSON 구조의 데이터가 포함되어 있지만 Content-Type: text/html; charset=UTF-8로 제공됩니다. 이 Content-Type 선언은 브라우저의 HTML 파서가 응답 본문 전체를 HTML 문서로 처리하도록 지시합니다. 응답이 application/json으로 제공되었다면 브라우저는 응답을 일반 텍스트로 렌더링하고 HTML 파싱이 발생하지 않았을 것입니다 — 주입된 태그는 DOM 요소로 인스턴스화되지 않고 리터럴 텍스트로 표시되었을 것입니다.
증거:```http Content-Type: text/html; charset=UTF-8 ← Actual (enables HTML parsing) Content-Type: application/json ← Expected (would prevent exploitation)
`X-Content-Type-Options: nosniff` 헤더는 존재하지만 서버가 명시적으로 `text/html`을 선언하고 있으므로 관련이 없습니다. 방지할 MIME 타입 스니핑이 없기 때문입니다.
### CWE-79: 웹 페이지 생성 중 입력의 부적절한 중화 — 반사형 XSS (결과적)
**역할:** 악용 가능한 결과 — 실현된 취약점.
세 가지 기여 약점의 조합은 반사형 교차 사이트 스크립팅 조건을 생성합니다. 사용자 제공 입력은 HTTP 요청에서 서버 측 처리를 거쳐 HTTP 응답으로 흐르며, 삭제, 인코딩 또는 컨텍스트에 적합한 Content-Type 선언 없이 전달되어 피해자 브라우저에서 임의의 JavaScript 실행을 초래합니다.
**하위 유형:** 반사형(유형 1) — 페이로드가 HTTP 요청에 포함되고 저장 없이 HTTP 응답에 즉시 반영됩니다.
### 요약 표
| CWE ID | 이름 | 역할 | 단독 악용 가능? |
|---|---|---|---|
| CWE-20 | 부적절한 입력 검증 | 기여(근본 활성화 요인) | 아니요 — 잘못된 데이터가 유입되지만 렌더링되지 않음 |
| CWE-116 | 출력의 부적절한 인코딩 또는 이스케이프 | 주요 기술적 실패 | 부분적으로 — HTML 렌더링 컨텍스트 필요 |
| CWE-838 | 출력 컨텍스트에 대한 부적절한 인코딩 | 복합(컨텍스트 확대) | 아니요 — 이스케이프되지 않은 데이터가 존재해야 함 |
| CWE-79 | 교차 사이트 스크립팅(반사형) | **결과적(악용 가능)** | **예 — 이것이 실현된 취약점입니다** |
### 최소 수정 분석
아래 목록의 **단일** 수정 조치만으로도 체인이 끊기고 악용을 방지할 수 있습니다:
| 수정 | 체인 중단 지점 | 단독으로 충분? |
|---|---|---|
| `form_ids`를 정수로 타입 캐스팅 | CWE-20(입력) | ✅ 예 |
| 출력을 HTML 엔티티로 인코딩 | CWE-116(출력) | ✅ 예 |
| 응답을 `application/json`으로 제공 | CWE-838(컨텍스트) | ✅ 예 |
**권장 사항:** 심층 방어 차원에서 세 가지를 모두 구현하십시오. 가장 중요한 수정은 출력 인코딩(CWE-116)입니다. 입력 검증이나 Content-Type 변경과 관계없이 현재 및 향후 주입 벡터로부터 보호하기 때문입니다.
---
## Wordfence 제출 참조
| 필드 | 값 |
|---|---|
| **소프트웨어 유형** | WordPress 플러그인 |
| **소프트웨어 슬러그** | `gravityforms` |
| **Wordfence Intel URL** | https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/gravityforms |
| **영향받는 버전** | <= 2.9.28(최신 확인됨) |
| **취약점 제목** | Gravity Forms <= 2.9.28 — `form_ids` 매개변수를 통한 인증되지 않은 반사형 교차 사이트 스크립팅 |
| **CWE(기본)** | CWE-79 |
| **CVSS 3.1** | 6.1 — `AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N` |
| **필요 권한** | 인증되지 않음 |
| **패치 상태** | 2.9.30.1에서 패치됨 |
---
## 영향받는 구성
### 필수 서버 측 조건
- Gravity Forms 플러그인이 활성화된 WordPress 설치(양식 게시 여부와 무관)
- `gform_get_config` AJAX 작업 등록(플러그인 활성화 시 기본 동작)
### Nonce 접근성
`config_nonce`는 Gravity Forms 양식이 포함된 모든 페이지의 전역 `gform_theme_config` JavaScript 변수에 포함됩니다:```javascript
var gform_theme_config = {
"common": { ... },
"config_nonce": "9308d72c0a" // ← Publicly accessible
};
이 nonce는 다음과 같이 확인되었습니다:
| 헤더 | 값 | 악용 가능성에 미치는 영향 |
|---|---|---|
X-Content-Type-Options | nosniff | 완화 효과 없음 — 서버가 text/html을 명시적으로 선언함 |
X-Frame-Options | SAMEORIGIN | 교차 출처 기반 iframe 악용을 방지하지만, 팝업/리다이렉트 전달은 여전히 작동함 |
Content-Security-Policy | frame-ancestors 'self'; | script-src 지시문이 없음 — 인라인 스크립트 실행이 제한되지 않음 |
Referrer-Policy | strict-origin-when-cross-origin | 악용 가능성에 영향 없음 |