From 6571119db6893e91c06cb6cd97dbe3d14960cbef Mon Sep 17 00:00:00 2001 From: thisgun Date: Mon, 10 Aug 2026 00:58:49 +0000 Subject: [PATCH] =?UTF-8?q?[KVE-2026-1806]=20=EC=BD=98=ED=85=90=EC=B8=A0?= =?UTF-8?q?=20include=20=EA=B2=BD=EB=A1=9C=20=EB=B0=8F=20=EB=AF=B8?= =?UTF-8?q?=EC=99=84=EB=A3=8C=EC=A3=BC=EB=AC=B8=20=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 콘텐츠 상단/하단 파일 경로를 저장 전에 먼저 정규화한 뒤 확장자와 위치를 검증하도록 순서를 조정한다. 정규화 과정에서 확장자가 사라져 검증을 우회하던 경로를 차단한다. 실행 직전 bbs/content.php 에서도 승인 확장자와 data 디렉터리 귀결 여부를 다시 확인하는 is_content_include_allowed() 를 적용하고, 콘텐츠 상단/하단 이미지 업로드는 이미지 파일만 허용하도록 검사한다. 미완료주문 상세의 결제방법 출력에 get_text() 인코딩을 적용하고, 임시주문 저장 시 정제한 결제수단 값을 원본에도 반영한다. 개인결제 주문 세션은 결제정보 검증을 통과한 뒤에만 설정한다. Co-Authored-By: Claude Opus 5 (1M context) (cherry picked from commit 196255d76912531d7e13861821bd59ca15c967d1) --- adm/contentformupdate.php | 33 ++++++++++++++++++++++++++++----- adm/shop_admin/inorderform.php | 2 +- bbs/content.php | 6 ++++-- lib/common.lib.php | 27 +++++++++++++++++++++++++++ shop/ajax.orderdatasave.php | 2 ++ shop/personalpayformupdate.php | 5 +++-- 6 files changed, 65 insertions(+), 10 deletions(-) diff --git a/adm/contentformupdate.php b/adm/contentformupdate.php index 612e95c12..4fcb083bb 100644 --- a/adm/contentformupdate.php +++ b/adm/contentformupdate.php @@ -40,6 +40,12 @@ if ($is_admin !== 'super') { $co_include_tail = ''; } } +// 저장·검증 전에 경로를 먼저 정규화하여, 검증 이후 경로가 달라지지 않도록 한다. +if (function_exists('filter_input_include_path')) { + $co_include_head = filter_input_include_path($co_include_head); + $co_include_tail = filter_input_include_path($co_include_tail); +} + $co_tag_filter_use = isset($_POST['co_tag_filter_use']) ? (int) $_POST['co_tag_filter_use'] : 1; $co_himg_del = (isset($_POST['co_himg_del']) && $_POST['co_himg_del']) ? 1 : 0; $co_timg_del = (isset($_POST['co_timg_del']) && $_POST['co_timg_del']) ? 1 : 0; @@ -86,6 +92,14 @@ if ($co_include_tail) { } } +if ($co_include_head && function_exists('is_content_include_allowed') && !is_content_include_allowed($co_include_head)) { + alert('상단 파일 경로로 사용할 수 없는 위치입니다.'); +} + +if ($co_include_tail && function_exists('is_content_include_allowed') && !is_content_include_allowed($co_include_tail)) { + alert('하단 파일 경로로 사용할 수 없는 위치입니다.'); +} + if ($co_include_head && !is_include_path_check($co_include_head, 1)) { $co_include_head = ''; $error_msg = '/data/file/ 또는 /data/editor/ 포함된 문자를 상단 파일 경로에 포함시킬수 없습니다.'; @@ -96,11 +110,6 @@ if ($co_include_tail && !is_include_path_check($co_include_tail, 1)) { $error_msg = '/data/file/ 또는 /data/editor/ 포함된 문자를 하단 파일 경로에 포함시킬수 없습니다.'; } -if (function_exists('filter_input_include_path')) { - $co_include_head = filter_input_include_path($co_include_head); - $co_include_tail = filter_input_include_path($co_include_tail); -} - $co_seo_title = exist_seo_title_recursive('content', generate_seo_title($co_subject), $g5['content_table'], $co_id); $sql_common = " co_include_head = '$co_include_head', @@ -147,6 +156,20 @@ if (function_exists('get_admin_captcha_by')) { g5_delete_cache_by_prefix('content-' . $co_id . '-'); if ($w == "" || $w == "u") { + foreach (array('co_himg', 'co_timg') as $content_img_field) { + if (empty($_FILES[$content_img_field]['name']) || empty($_FILES[$content_img_field]['tmp_name'])) + continue; + + // 상단/하단 이미지 슬롯에는 이미지만 허용하고, 실행 가능한 태그가 담긴 파일은 거부한다. + $tmp_file = $_FILES[$content_img_field]['tmp_name']; + $is_valid_image = @getimagesize($tmp_file); + $head_bytes = @file_get_contents($tmp_file, false, null, 0, 8192); + + if ($is_valid_image === false || ($head_bytes !== false && preg_match('/<\?php|<\?=|<\?|