security patch

This commit is contained in:
Amberstone 2024-09-19 20:57:55 +09:00
parent 121c3b4224
commit d149ccdae2
Signed by: amber
GPG key ID: 094B0E55F98D8BF1
9 changed files with 422 additions and 161 deletions

View file

@ -34,6 +34,45 @@ $_POST['mb_nick'] = $_POST['mb_name'];
$mb_zip1 = substr($_POST['mb_zip'], 0, 3);
$mb_zip2 = substr($_POST['mb_zip'], 3);
// 관리자가 자동등록방지를 사용해야 할 경우 ( 회원의 비밀번호 변경시 캡챠를 체크한다 )
if ($mb_password && function_exists('get_admin_captcha_by') && get_admin_captcha_by()) {
include_once(G5_CAPTCHA_PATH . '/captcha.lib.php');
if (!chk_captcha()) {
alert('자동등록방지 숫자가 틀렸습니다.');
}
}
$check_keys = array(
'mb_name',
'mb_homepage',
'mb_tel',
'mb_addr1',
'mb_addr2',
'mb_addr3',
'mb_addr_jibeon',
'mb_signature',
'mb_leave_date',
'mb_intercept_date',
'mb_mailling',
'mb_sms',
'mb_open',
'mb_profile',
'mb_level'
);
for ($i = 1; $i <= 10; $i++) {
$check_keys[] = 'mb_' . $i;
}
foreach ($check_keys as $key) {
if (in_array($key, array('mb_signature', 'mb_profile'))) {
$posts[$key] = isset($_POST[$key]) ? clean_xss_tags($_POST[$key], 1, 1, 0, 0) : '';
} else {
$posts[$key] = isset($_POST[$key]) ? clean_xss_tags($_POST[$key], 1, 1) : '';
}
}
$sql_common = " mb_name = '{$_POST['mb_name']}',
mb_nick = '{$_POST['mb_nick']}',
mb_email = '{$_POST['mb_email']}',
@ -158,5 +197,4 @@ if ($w == '') {
sql_query($sql);
} else
alert('제대로 된 값이 넘어오지 않았습니다.');
goto_url('./member_form.php?' . $qstr . '&amp;w=u&amp;mb_id=' . $mb_id);

View file

@ -23,7 +23,8 @@ for ($i = 0; $i < $count; $i++) {
$code = $_POST['code'][$i];
$me_name = $_POST['me_name'][$i];
$me_link = $_POST['me_link'][$i];
// kve-2021-0755 gnuboard 3a3434104c
$me_link = html_purifier($_POST['me_link'][$i]);
if (!$code || !$me_name)
continue;

View file

@ -14,7 +14,15 @@ else
$g5['title'] = '회원 비밀번호 확인';
include_once('./_head.sub.php');
$url = clean_xss_tags($_GET['url']);
// gnuboard xss patch - 2457055514
$url = isset($_GET['url']) ? clean_xss_tags($_GET['url']) : '';
while (1) {
$tmp = preg_replace('/&#[^;]+;/', '', $url);
if ($tmp == $url)
break;
$url = $tmp;
}
// url 체크
check_url_host($url);

View file

@ -14,8 +14,11 @@ if ($kind == 'recv')
$unkind = 'send';
else if ($kind == 'send')
$unkind = 'recv';
else
else {
// xss patch - gnuboard 2e81619ea8
$kind = clean_xss_tags(trim($kind));
alert('' . $kind . '값을 넘겨주세요.');
}
$sql = " select count(*) as cnt from {$g5['memo_table']} where me_{$kind}_mb_id = '{$member['mb_id']}' ";
$row = sql_fetch($sql);

View file

@ -74,9 +74,10 @@ if (is_file($skin_file)) {
else
$content = "\n\n\n\n====== 이전 답변내용 =======\n";
$content .= get_text($write['qa_content'], 0);
// gnuboard xss patch 210624 - f631b3dca7
$content .= get_text(html_purifier($write['qa_content']), 0);
} else {
$content = get_text($write['qa_content'], 0);
$content = get_text(html_purifier($write['qa_content']), 0);
}
$editor_html = editor_html('qa_content', $content, $is_dhtml_editor);

View file

@ -452,7 +452,7 @@ if (!$group['gr_use_access'] && $board['bo_read_level'] < 2 && !$secret) {
// 파일개수 체크
$file_count = 0;
$upload_count = count($_FILES['bf_file']['name']);
$upload_count = !empty($_FILES['bf_file']['name']) ? count($_FILES['bf_file']['name']) : 0;
for ($i = 0; $i < $upload_count; $i++) {
if ($_FILES['bf_file']['name'][$i] && is_uploaded_file($_FILES['bf_file']['tmp_name'][$i]))
@ -477,7 +477,9 @@ $chars_array = array_merge(range(0, 9), range('a', 'z'), range('A', 'Z'));
// 가변 파일 업로드
$file_upload_msg = '';
$upload = array();
for ($i = 0; $i < count($_FILES['bf_file']['name']); $i++) {
if ($upload_count != 0) {
for ($i = 0; $i < count($_FILES['bf_file']['name']); $i++) {
$upload[$i]['file'] = '';
$upload[$i]['source'] = '';
$upload[$i]['filesize'] = 0;
@ -573,6 +575,7 @@ for ($i = 0; $i < count($_FILES['bf_file']['name']); $i++) {
// 올라간 파일의 퍼미션을 변경합니다.
chmod($dest_file, G5_FILE_PERMISSION);
}
}
}
// 나중에 테이블에 저장하는 이유는 $wr_id 값을 저장해야 하기 때문입니다.

View file

@ -67,7 +67,6 @@ if (defined('_INDEX_')) {
<?php } ?>
<title><?php echo $g5_head_title; ?></title>
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Two+Tone|Material+Icons+Round|Material+Icons+Sharp"
rel="stylesheet">
@ -81,23 +80,18 @@ if (defined('_INDEX_')) {
echo '<link rel="stylesheet" href="' . G5_DATA_URL . '/css/_design.config.css?v=' . $config['cf_css_version'] . '" type="text/css" />';
}
?>
<?php if ($config['cf_favicon']) { ?>
<link rel="shortcut icon" href="<?= $config['cf_favicon'] ?>" type="image/x-icon">
<link rel="icon" href="<?= $config['cf_favicon'] ?>" type="image/x-icon">
<?php } ?>
<!--[if lte IE 8]>
<script src="<?php echo G5_JS_URL ?>/html5.js"></script>
<![endif]-->
<script>
<?php if ($config['cf_use_http']) { ?>
if (window.location.protocol == "https:")
location.href = location.href.replace(/^https:/, 'http:')
<?php } ?>
// 자바스크립트에서 사용하는 전역변수 선언
var g5_url = "<?php echo G5_URL ?>";
var g5_bbs_url = "<?php echo G5_BBS_URL ?>";
@ -112,18 +106,15 @@ if (defined('_INDEX_')) {
var g5_admin_url = "<?php echo G5_ADMIN_URL; ?>";
<?php } ?>
</script>
<?php if (defined('G5_IS_ADMIN')) { ?>
<script src="<?php echo G5_JS_URL ?>/jquery-1.8.3.min.js"></script>
<?php } else { ?>
<script src="<?php echo G5_JS_URL ?>/jquery-1.12.3.min.js"></script>
<?php } ?>
<script src="<?php echo G5_JS_URL ?>/jquery.cookie.js"></script>
<script src="<?php echo G5_JS_URL ?>/jquery.rwdImageMaps.js"></script>
<script src="<?php echo G5_JS_URL ?>/common.js"></script>
<script src="<?php echo G5_JS_URL ?>/wrest.js?ver=<?php echo G5_JS_VER; ?>"></script>
<?php
if (G5_IS_MOBILE) {
echo '<script src="' . G5_JS_URL . '/modernizr.custom.70111.js"></script>' . PHP_EOL; // overflow scroll 감지
@ -131,11 +122,9 @@ if (defined('_INDEX_')) {
if (!defined('G5_IS_ADMIN'))
echo $config['cf_add_script'];
?>
<script>
if (!parent || parent == this) $('html').addClass('single');
</script>
<?php if ($config['cf_cursor']) { ?>
<style>
* {
@ -144,5 +133,4 @@ if (defined('_INDEX_')) {
</style>
<?php } ?>
</head>
<body>

View file

@ -3,6 +3,10 @@ if (!defined('_GNUBOARD_'))
exit;
include_once(dirname(__FILE__) . '/pbkdf2.compat.php');
include_once __DIR__ . "/hook.lib.php";
include_once __DIR__ . '/Hook/hook.class.php';
include_once __DIR__ . '/Hook/hook.extends.class.php';
/*************************************************************************
**
** 일반 함수 모음
@ -1438,7 +1442,8 @@ function sql_connect($host, $user, $pass, $db = G5_MYSQL_DB)
global $g5;
if (function_exists('mysqli_connect') && G5_MYSQLI_USE) {
$link = mysqli_connect($host, $user, $pass, $db);
mysqli_report(MYSQLI_REPORT_OFF);
$link = @mysqli_connect($host, $user, $pass, $db) or die('MySQL Host, User, Password, DB 정보에 오류가 있습니다.');
// 연결 오류 발생 시 스크립트 종료
if (mysqli_connect_errno()) {
@ -1477,12 +1482,27 @@ function sql_set_charset($charset, $link = null)
mysql_query(" set names {$charset} ", $link);
}
function sql_data_seek($result, $offset = 0)
{
if (!$result)
return;
if (function_exists('mysqli_set_charset') && G5_MYSQLI_USE)
mysqli_data_seek($result, $offset);
else
mysql_data_seek($result, $offset);
}
function _callback_sql_show_tables($m)
{
return "show tables like '" . str_replace("`", "", $m[1]) . "'";
}
// mysqli_query 와 mysqli_error 를 한꺼번에 처리
// mysql connect resource 지정 - 명랑폐인님 제안
function sql_query($sql, $error = G5_DISPLAY_SQL_ERROR, $link = null)
{
global $g5;
global $g5, $g5_debug;
if (!$link)
$link = $g5['connect_db'];
@ -1495,11 +1515,19 @@ function sql_query($sql, $error = G5_DISPLAY_SQL_ERROR, $link = null)
// `information_schema` DB로의 접근을 허락하지 않습니다.
$sql = preg_replace("#^select.*from.*where.*`?information_schema`?.*#i", "select 1", $sql);
if (preg_match("#^desc(?:ribe)?\s+(.*)#i", $sql)) {
$sql = preg_replace_callback("#^desc(?:ribe)?\s+(.*)#i", '_callback_sql_show_tables', trim($sql));
}
if (function_exists('mysqli_query') && G5_MYSQLI_USE) {
if ($error) {
$result = @mysqli_query($link, $sql) or die("<p>$sql<p>" . mysqli_errno($link) . " : " . mysqli_error($link) . "<p>error file : {$_SERVER['SCRIPT_NAME']}");
} else {
try {
$result = @mysqli_query($link, $sql);
} catch (Exception $e) {
$result = null;
}
}
} else {
if ($error) {
@ -1509,6 +1537,8 @@ function sql_query($sql, $error = G5_DISPLAY_SQL_ERROR, $link = null)
}
}
run_event('sql_query_after', $result, $sql);
return $result;
}
@ -1531,9 +1561,15 @@ function sql_fetch($sql, $error = G5_DISPLAY_SQL_ERROR, $link = null)
// 결과값에서 한행 연관배열(이름으로)로 얻는다.
function sql_fetch_array($result)
{
if (!$result)
return array();
if (function_exists('mysqli_fetch_assoc') && G5_MYSQLI_USE)
try {
$row = @mysqli_fetch_assoc($result);
else
} catch (Exception $e) {
$row = null;
} else
$row = @mysql_fetch_assoc($result);
return $row;
@ -1545,6 +1581,9 @@ function sql_fetch_array($result)
// 단, 결과 값은 스크립트(script) 실행부가 종료되면서 메모리에서 자동적으로 지워진다.
function sql_free_result($result)
{
if (!is_resource($result))
return;
if (function_exists('mysqli_free_result') && G5_MYSQLI_USE)
return mysqli_free_result($result);
else
@ -1629,7 +1668,6 @@ function sql_error_info($link = null)
}
}
// PHPMyAdmin 참고
function get_table_define($table, $crlf = "\n")
{
@ -2864,9 +2902,44 @@ function get_search_string($stx)
}
// XSS 관련 태그 제거
function clean_xss_tags($str)
// gnuboard - 258f94e597, d4f554e653
function clean_xss_tags($str, $check_entities = 0, $is_remove_tags = 0, $cur_str_len = 0, $is_trim_both = 1)
{
$str = 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 ($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;
}
@ -3033,24 +3106,71 @@ function login_password_check($mb, $pass, $hash)
return check_password($pass, $hash);
}
// 동일한 host url 인지
function check_url_host($url, $msg = '', $return_url = G5_URL)
function check_url_host($url, $msg = '', $return_url = G5_URL, $is_redirect = false)
{
if (!$msg)
$msg = 'url에 타 도메인을 지정할 수 없습니다.';
$p = @parse_url($url);
$host = preg_replace('/:[0-9]+$/', '', $_SERVER['HTTP_HOST']);
if (stripos($url, 'http:') !== false) {
if (!isset($p['scheme']) || !$p['scheme'] || !isset($p['host']) || !$p['host'])
alert('url 정보가 올바르지 않습니다.', $return_url);
if (run_replace('check_url_host_before', '', $url, $msg, $return_url, $is_redirect) === 'is_checked') {
return;
}
if ((isset($p['scheme']) && $p['scheme']) || (isset($p['host']) && $p['host'])) {
// KVE-2021-1277 Open Redirect 취약점 해결
if (preg_match('#\\\0#', $url)) {
alert('url 에 올바르지 않은 값이 포함되어 있습니다.');
}
while (($replace_url = preg_replace(array('/\/{2,}/', '/\\@/'), array('//', ''), urldecode($url))) != $url) {
$url = $replace_url;
}
$p = @parse_url(trim($url));
$host = preg_replace('/:[0-9]+$/', '', $_SERVER['HTTP_HOST']);
$is_host_check = false;
// url을 urlencode 를 2번이상하면 parse_url 에서 scheme와 host 값을 가져올수 없는 취약점이 존재함
if ($is_redirect && !isset($p['host']) && urldecode($url) != $url) {
$i = 0;
while ($i <= 3) {
$url = urldecode($url);
if (urldecode($url) == $url)
break;
$i++;
}
if (urldecode($url) == $url) {
$p = @parse_url($url);
} else {
$is_host_check = true;
}
}
// if(stripos($url, 'http:') !== false) {
// if(!isset($p['scheme']) || !$p['scheme'] || !isset($p['host']) || !$p['host'])
// alert('url 정보가 올바르지 않습니다.', $return_url);
// }
//php 5.6.29 이하 버전에서는 parse_url 버그가 존재함
//php 7.0.1 ~ 7.0.5 버전에서는 parse_url 버그가 존재함
if ($is_redirect && (isset($p['host']) && $p['host'])) {
$bool_ch = false;
foreach (array('user', 'host') as $key) {
if (isset($p[$key]) && strpbrk($p[$key], ':/?#@')) {
$bool_ch = true;
}
}
if ($bool_ch) {
$regex = '/https?\:\/\/' . $host . '/i';
if (!preg_match($regex, $url)) {
$is_host_check = true;
}
}
}
if ((isset($p['scheme']) && $p['scheme']) || (isset($p['host']) && $p['host']) || $is_host_check) {
//if ($p['host'].(isset($p['port']) ? ':'.$p['port'] : '') != $_SERVER['HTTP_HOST']) {
if ($p['host'] != $host) {
if (run_replace('check_same_url_host', (($p['host'] != $host) || $is_host_check), $p, $host, $is_host_check, $return_url, $is_redirect)) {
echo '<script>' . PHP_EOL;
echo 'alert("url에 타 도메인을 지정할 수 없습니다.");' . PHP_EOL;
echo 'document.location.href = "' . $return_url . '";' . PHP_EOL;
@ -3313,47 +3433,3 @@ function is_include_path_check($path = '')
}
return true;
}
include_once dirname(__FILE__) . '/Hook/hook.class.php';
include_once dirname(__FILE__) . '/Hook/hook.extends.class.php';
function run_event($tag, $arg = '')
{
if ($hook = get_hook_class()) {
$args = array();
if (
is_array($arg)
&&
isset($arg[0])
&&
is_object($arg[0])
&&
1 == count($arg)
) {
$args[] =& $arg[0];
} else {
$args[] = $arg;
}
$numArgs = func_num_args();
for ($a = 2; $a < $numArgs; $a++) {
$args[] = func_get_arg($a);
}
$hook->doAction($tag, $args, false);
}
}
function get_hook_class()
{
if (class_exists('GML_Hook')) {
return GML_Hook::getInstance();
}
return null;
}

View file

@ -0,0 +1,143 @@
<?php
if (!defined('_GNUBOARD_'))
exit;
define('G5_HOOK_DEFAULT_PRIORITY', 8);
if (!function_exists('get_called_class')) {
function get_called_class()
{
$bt = debug_backtrace();
$lines = file($bt[1]['file']);
preg_match(
'/([a-zA-Z0-9\_]+)::' . $bt[1]['function'] . '/',
$lines[$bt[1]['line'] - 1],
$matches
);
return $matches[1];
}
}
include_once(dirname(__FILE__) . '/Hook/hook.class.php');
include_once(dirname(__FILE__) . '/Hook/hook.extends.class.php');
function get_hook_class()
{
if (class_exists('GML_Hook')) {
return GML_Hook::getInstance();
}
return null;
}
function add_event($tag, $func, $priority = G5_HOOK_DEFAULT_PRIORITY, $args = 0)
{
if ($hook = get_hook_class()) {
$hook->addAction($tag, $func, $priority, $args);
}
}
function run_event($tag, $arg = '')
{
if ($hook = get_hook_class()) {
$args = array();
if (
is_array($arg)
&&
isset($arg[0])
&&
is_object($arg[0])
&&
1 == count($arg)
) {
$args[] =& $arg[0];
} else {
$args[] = $arg;
}
$numArgs = func_num_args();
for ($a = 2; $a < $numArgs; $a++) {
$args[] = func_get_arg($a);
}
$hook->doAction($tag, $args, false);
}
}
function add_replace($tag, $func, $priority = G5_HOOK_DEFAULT_PRIORITY, $args = 1)
{
if ($hook = get_hook_class()) {
return $hook->addFilter($tag, $func, $priority, $args);
}
return null;
}
function run_replace($tag, $arg = '')
{
if ($hook = get_hook_class()) {
$args = array();
if (
is_array($arg)
&&
isset($arg[0])
&&
is_object($arg[0])
&&
1 == count($arg)
) {
$args[] =& $arg[0];
} else {
$args[] = $arg;
}
$numArgs = func_num_args();
for ($a = 2; $a < $numArgs; $a++) {
$args[] = func_get_arg($a);
}
return $hook->apply_filters($tag, $args, false);
}
return null;
}
function delete_event($tag, $func, $priority = G5_HOOK_DEFAULT_PRIORITY)
{
if ($hook = get_hook_class()) {
return $hook->remove_action($tag, $func, $priority);
}
return null;
}
function delete_replace($tag, $func, $priority = G5_HOOK_DEFAULT_PRIORITY)
{
if ($hook = get_hook_class()) {
return $hook->remove_filter($tag, $func, $priority);
}
return null;
}
function get_hook_datas($type = '', $is_callback = '')
{
if ($hook = get_hook_class()) {
return $hook->get_properties($type, $is_callback);
}
return null;
}