본문 바로가기
CVE 취약점

CVE-2026-89274 분석: WP Recipe Maker, 댓글이 do_shortcode()를 만났을 때

by WhiteGuidance 2026. 10. 9.
반응형

 

취약점 개요

항목 내용
CVE CVE-2026-89274
대상 WordPress 플러그인 WP Recipe Maker
영향 버전 10.8.1 이하
패치 버전 10.8.2
유형 CWE-94 (Code Injection) — 임의 shortcode 실행
CVSS 3.1 9.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N)
인증 불필요 (단, 댓글이 승인되어야 함)

 

한 줄 요약입니다. 방문자가 남긴 평점 댓글 내용이 do_shortcode()에 그대로 들어가서, 댓글에 적은 [shortcode]가 서버에서 실행됩니다.

주의할 점이 하나 있습니다. 이 취약점은 임의 PHP 실행(RCE)이 아닙니다. 사이트에 이미 등록된 shortcode만 실행할 수 있고, 피해 범위는 그 shortcode가 무엇을 하느냐에 따라 달라집니다.


배경지식: shortcode와 JSON-LD

shortcode란?

WordPress에서 [tag 옵션] 형태로 적는 문법입니다. 글에 이렇게 쓰면

[gallery ids="1,2,3"]

 

WordPress가 do_shortcode()를 호출하는 순간 gallery에 등록된 PHP 함수가 실행되고, 그 반환값(HTML)으로 치환됩니다.

 

핵심은 do_shortcode()가 호출되면 문자열 속 shortcode는 무조건 실행된다는 점입니다. 그래서 do_shortcode()에는 신뢰할 수 있는 문자열만 넣어야 합니다. 글 작성자(author/editor)가 쓴 본문은 신뢰할 수 있지만, 방문자가 쓴 댓글은 신뢰할 수 없습니다.

 

JSON-LD란?

검색엔진에 "이 페이지는 레시피이고, 별점은 이렇고, 리뷰는 이렇다"고 알려주는 구조화 데이터입니다. 페이지 HTML 안에 이렇게 들어갑니다.

 

WP Recipe Maker는 평점이 달린 댓글을 이 review 배열의 reviewBody에 넣습니다. 이 JSON-LD는 모든 방문자와 검색 크롤러에게 공개되는 영역입니다.


취약한 코드 분석

데이터 흐름

 

 

그림 2 설명. 위에서 아래로 읽는 5단계 체인입니다. 맨 위 황토색 카드(①)가 신뢰할 수 없는 입력(Source)이고, 맨 아래 빨간 카드(⑤)가 실행 지점(Sink)입니다. 카드 오른쪽 아래의 작은 글씨는 해당 코드의 줄 번호입니다. 오른쪽의 빨간 점선 괄호는 ①에서 ⑤까지 검증이나 필터링 단계가 하나도 없다는 뜻입니다. 입력이 곧바로 실행으로 이어진다는 점을 이 그림 한 장으로 확인할 수 있습니다.

 

① 어떤 댓글을 가져오는가 (:973, :1016)

 

  • 대상은 레시피가 들어 있는 글의 승인된 평점 댓글입니다.
  • reviewBody에는 comment_content가 검증 없이 그대로 들어갑니다. 여기까지는 문제가 아닙니다. 문제는 다음 단계입니다.

② 취약점의 핵심: sanitize_metadata() (:553)

메타데이터를 출력하기 전에 "정리(sanitize)"하는 함수입니다. 배열이면 안으로 들어가고, 문자열이면 정리합니다.

 

이 한 줄이 취약점의 전부입니다. 안쪽부터 실행되는 순서대로 읽어 보겠습니다.

순서 함수 하는 일
1 do_shortcode( $metadata ) shortcode를 실행하고 결과 문자열로 치환
2 wp_strip_all_tags( ... ) 결과에서 HTML 태그 제거
3 strip_shortcodes( ... ) 남아 있는 shortcode 문법 제거

 

개발자의 의도는 "shortcode와 태그를 다 지워서 깨끗한 텍스트만 JSON-LD에 넣자"였을 것입니다. 그런데 지우기(2, 3번) 전에 실행(1번)이 먼저 일어납니다. 2, 3번은 이미 실행된 결과물을 청소할 뿐이라, 서버 쪽 실행을 막지 못합니다.

 

비유하면 이렇습니다. 택배 상자를 "폭발물 검사 후 배달"해야 하는데, 상자를 먼저 열어 내용물을 작동시키고 나서 포장지를 검사하는 셈입니다.

 

왜 하필 reviewBody인가?

sanitize_metadata()는 배열의 모든 문자열 필드에 do_shortcode()를 적용합니다. 레시피 이름이나 재료 같은 필드는 관리자가 입력한 신뢰 데이터이니 평소엔 문제가 없습니다. 그런데 방문자가 쓴 댓글이 같은 배열에 섞여 들어오는 통로가 reviewBody 하나였습니다. 신뢰 데이터용으로 만든 처리 로직에 비신뢰 데이터가 합류한 것입니다.


공격 흐름

  1. 공격자가 레시피가 들어 있는 글에 평점과 함께 댓글을 작성합니다. 본문에 shortcode 문법을 넣습니다. (wprm-comment-rating 값이 있어야 리뷰로 취급됩니다.)
  2. 댓글이 사이트의 승인 기준을 통과합니다. 자동 승인이거나 관리자가 승인한 경우입니다.
  3. 누군가 그 레시피 페이지를 열면 플러그인이 메타데이터를 만듭니다. 승인된 평점 댓글이 review에 포함됩니다.
  4. sanitize_metadata()가 댓글 본문에 do_shortcode()를 실행하고, shortcode가 서버에서 실행됩니다.
  5. 실행 결과가 reviewBody에 들어가 JSON-LD로 출력되고, 모든 방문자와 크롤러에게 노출됩니다.

 

그림 1 설명. 가로 세 개의 레인(공격자 / 사이트 / 방문자·크롤러)을 번호 ①~⑥ 순서대로 따라가며 읽습니다. 공격자는 ① 한 번만 등장하고, 이후는 모두 사이트와 방문자 레인에서 일어납니다. 곡선 화살표가 레인 사이를 오가는 부분은 "댓글 승인 → 누군가의 페이지 요청 → 메타데이터 생성"의 흐름입니다. ⑤번 카드만 빨간 강조선이 있는데, 서버에서 shortcode가 실행되는 지점이라는 뜻입니다. ⑤에서 ⑥으로 내려가는 빨간 화살표는 실행 결과가 방문자에게 그대로 전달된다는 표시입니다. 공격자가 아닌 방문자 레인에 결과가 도착한다는 점이 이 그림의 핵심입니다.

참고로 WordPress 기본 설정에는 "이전에 승인된 댓글이 있는 작성자만 자동 승인"이 켜져 있습니다. 그래서 첫 댓글은 대기 상태로 걸리는 경우가 많고, 이 점이 공격 난이도를 어느 정도 낮춥니다. 반대로 승인 정책이 느슨한 사이트는 바로 노출됩니다.

 

 

 

 

영향

  • 실행 결과가 페이지에 박히기 때문에, 출력을 내놓는 shortcode가 정보 유출 통로가 됩니다. 어드바이저리가 예로 든 것은 첨부파일 캡션, 비공개 글의 필드, 설치된 플러그인이 노출하는 데이터입니다.
  • 부작용이 있는 shortcode가 설치되어 있다면, 페이지가 렌더링될 때마다 반복 실행될 수 있습니다.
  • 페이지에 서버 측 코드 실행 능력이 직접 생기는 것은 아니므로 RCE로 볼 수는 없습니다.

패치 분석 (10.8.2)

바뀐 곳은 do_shortcode() → WPRM_Instacart::do_shortcode_safe() 하나입니다.

  • do_shortcode_safe()는 shortcode를 실행하지 않습니다. shortcode 태그는 걷어내고 안쪽 내용만 남깁니다. (class-wprm-instacart.php:501)
  • 같은 입력이라도 이제 shortcode 출력이 reviewBody에 찍히지 않고, 문법만 제거되거나 아무것도 실행되지 않습니다.
  • 공식 변경 로그에도 "recipe metadata 안의 댓글 텍스트에서 shortcode가 실행되지 않도록 수정"이라는 항목이 있습니다.

 

그림 3 설명. 위 줄이 10.8.1, 아래 줄이 10.8.2의 처리 순서입니다. 세 칸 중 첫 칸만 색이 있고 나머지는 회색입니다. 패치에서 바뀐 곳이 첫 단계 한 줄뿐이라는 점을 색으로 보여주려는 것입니다. 위 줄 아래의 빨간 괄호는 2, 3번 방어 코드가 실행(1번)보다 뒤에 있어서 이미 늦었다는 뜻이고, 아래 줄의 청록 괄호는 첫 단계에서 실행 자체를 차단한다는 뜻입니다.

 

 

그림 4 설명. 위의 같은 댓글 한 개가 좌우 두 갈래로 나뉘어 각각 10.8.1과 10.8.2에서 처리됩니다. 왼쪽(10.8.1)은 reviewBody 값에 shortcode의 실행 결과가 들어가 모든 방문자에게 공개되고, 오른쪽(10.8.2)은 shortcode가 제거되어 실행되지 않습니다. 강조된 줄만 보면 두 버전의 차이를 바로 알 수 있습니다. 오른쪽의 빈 값은 shortcode만 있는 댓글을 기준으로 한 예이며, 안쪽에 글이 있는 shortcode라면 그 글은 남을 수 있습니다.


대응 방법

  • WP Recipe Maker를 10.8.2 이상, 가능하면 최신 버전으로 업데이트합니다.
  • 업데이트 전 임시 조치로 평점 댓글 승인 정책을 수동 승인으로 바꾸고, 대기 중이거나 승인된 댓글 중 [ ] 문법이 들어간 것을 점검합니다.
  • 사이트에 등록된 shortcode 중 민감한 데이터를 출력하는 것이 없는지 함께 점검합니다.

 

반응형