CVE-2025-60859: Reflected XSS In Gnuboard

· omacs's blog


Table of Contents

Introduction #

본 취약점은 그누보드 (Gnuboard) 5.6.15 버전의 댓글 조회 기능에서 발생한, CSS injection을 통한 reflected XSS가 가능한 취약점이다[1, 2].

Root-cause Analylsis #

그누보드 게시판에서 댓글을 조회할 때 c_id를 GET 요청의 파라미터로 전달한다. 이 파라미터는 다음과 같이 XSS 필터링을 거친다 (bbs/view_comment.php에서 발췌)[5].

$c_id= isset($_GET['c_id']) ? clean_xss_tags($_GET['c_id'], 1, 1) : '';

그리고 clean_xss_tags 함수는 다음과 같이 구현되어 있다 (lib/common.lib.php에서 발췌)[5].

// XSS 관련 태그 제거
function clean_xss_tags($str, $check_entities=0, $is_remove_tags=0, $cur_str_len=0, $is_trim_both=1)
{
    if( $is_trim_both ) {
        // tab('\t'), formfeed('\f'), vertical tab('\v'), newline('\n'), carriage return('\r') 를 제거한다.
        $str = preg_replace("#[\t\f\v\n\r]#", '', $str);
    }

    if( $is_remove_tags ){
        $str = strip_tags($str);
    }

    if( $cur_str_len ){
        $str = utf8_strcut($str, $cur_str_len, '');
    }

    $str_len = strlen($str);

    $i = 0;
    while($i <= $str_len){
        $result = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $str);

        if( $check_entities ){
            $result = str_replace(array('&colon;', '&lpar;', '&rpar;', '&NewLine;', '&Tab;'), '', $result);
        }

        $result = preg_replace('#([^\p{L}]|^)(?:javascript|jar|applescript|vbscript|vbs|wscript|jscript|behavior|mocha|livescript|view-source)\s*:(?:.*?([/\\\;()\'">]|$))#ius',
                '$1$2', $result);

        if((string)$result === (string)$str) break;

        $str = $result;
        $i++;
    }

    return $str;
}

위 구현들을 보면 따옴표나 CSS와 이벤트 관련 속성에 대한 필터링이 충분하지 않음을 알 수 있다.

이렇게 처리된 c_id는 다음과 같이 사용자 브라우저에 표시된다 (skin/board/basic/view_comment_skin.php에서 발췌)[5].

<!-- 댓글 쓰기 시작 { -->
<aside id="bo_vc_w" class="bo_vc_w">
  <h2>댓글쓰기</h2>
  <form name="fviewcomment" id="fviewcomment" action="<?php echo $comment_action_url; ?>" onsubmit="return fviewcomment_submit(this);" method="post" autocomplete="off">
    <!-- ... -->
    <input type="hidden" name="comment_id" value="<?php echo $c_id ?>" id="comment_id">

이것으로 CSS injection을 통한 reflected XSS를 할 수 있다.

Proof-of-Concept #

kinugawamasato님은 input 태그의 type 속성이 hidden일 때 CSS injection으로 XSS를 하는 방법을 소개했다. 이는 여기에 그대로 적용 가능하다. 그 페이로드는 다음과 같다[4].

http://localhost:12345/bbs/board.php?bo_table=free&wr_id=1&c_id=2\"oncontentvisibilityautostatechange=alert(1) style=content-visibility:auto type=hidden

Patch #

패치는 따옴표, CSS나 이벤트 핸들러 속성에 대한 검증을 추가하는 방식으로 진행되었다[3].

From 002e43e5fb84b465357b445772c881e196e100d3 Mon Sep 17 00:00:00 2001
From: thisgun <thisgun@naver.com>
Date: Thu, 28 Aug 2025 13:35:14 +0900
Subject: [PATCH] =?UTF-8?q?XSS=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=EC=88=98?=
 =?UTF-8?q?=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 bbs/view_comment.php | 2 +-
 lib/common.lib.php   | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/bbs/view_comment.php b/bbs/view_comment.php
index f7cb5d0872..8c83090d9d 100644
--- a/bbs/view_comment.php
+++ b/bbs/view_comment.php
@@ -7,7 +7,7 @@
     $captcha_html = captcha_html('_comment');
 }

-$c_id = isset($_GET['c_id']) ? clean_xss_tags($_GET['c_id'], 1, 1) : '';
+$c_id = isset($_GET['c_id']) ? preg_replace('/[\'",]/', '', clean_xss_tags($_GET['c_id'], 1, 1)) : '';
 $c_wr_content = '';

 @include_once($board_skin_path.'/view_comment.head.skin.php');
diff --git a/lib/common.lib.php b/lib/common.lib.php
index a7ab4197c0..ddb8bbace0 100644
--- a/lib/common.lib.php
+++ b/lib/common.lib.php
@@ -3429,6 +3429,12 @@ function clean_xss_tags($str, $check_entities=0, $is_remove_tags=0, $cur_str_len
         $result = preg_replace('#([^\p{L}]|^)(?:javascript|jar|applescript|vbscript|vbs|wscript|jscript|behavior|mocha|livescript|view-source)\s*:(?:.*?([/\\\;()\'">]|$))#ius',
                 '$1$2', $result);

+        // 이벤트 핸들러 속성 제거 (예: onclick=, onerror= 등)
+        $result = preg_replace('/on\w+\s*=\s*(".*?"|\'.*?\'|[^\s>]+)/i', '', $result);
+        
+        // 속성 제거 (CSS 기반 인젝션 차단)
+        $result = preg_replace('/\s*style\s*=\s*(".*?"|\'.*?\'|[^\s>]+)/i', '', $result);
+        
         if((string)$result === (string)$str) break;

         $str = $result;

References #

  1. "CVE-2025-60859 Detail." nvd.nist.gov, Accessed: Mar. 30, 2026. [Online]. Available: https://nvd.nist.gov/vuln/detail/CVE-2025-60859
  2. creeperkirby, "Gnboard5 5.6.15 reflected XSS." creeperkirby.notion.site, Accessed: Mar. 30, 2026. [Online]. Available: https://creeperkirby.notion.site/Gnboard5-5-6-15-reflected-XSS-25c4fe7db8cf80efa20fc2ebefcfe61e
  3. thisgun, "XSS 취약점 수정." github.com, Accessed: Mar. 30, 2026. [Online]. Available: https://github.com/gnuboard/gnuboard5/commit/002e43e5fb84b465357b445772c881e196e100d3
  4. Masato Kinugawa [@kinugawamasato], "ooh, this works on Chrome Canary :D". X/Twitter. https://x.com/kinugawamasato/status/1816234368714871185. (Accessed: Mar. 30, 2026)
  5. kagla, "gnuboard5", (Version 5.6.15) [Source Code]. https://github.com/gnuboard/gnuboard5
last updated: