cross site scripting 취약점 문의
아래와 같은 취약점을 해결해 주시면 고마움의 뜻으로 스타벅스 커피 쿠폰을 보내드릴께요.^^
1. 문제점 : 홈페이지에 그누보드를 사용하고 있으며 외부기관 의뢰 홈페이지 취약점 점검 결과
Cross Site Scripting 취약점이 발견되어 개선을 요구함
2. 취약점 #1 내용
○ 취약점 경로 : http://홈페이지주소/board/bbs/download.php?bo_table=resume&wr_id=2213320&no=1
○ 취약점 내용 : 헤더에 악성스크립트를 삽입하여 악성코드 유포 및 피싱사이트 유도 공격 시도 가능
○ 대응방안 : 모든 사용자 입력 값에 불필요한 특수문자(',",<,>)를 서버단에서 치환해야 함.
(필수적으로 모든 페이지의 입력 값 파라미터에 일괄 적용해야 함)
3. 취약점 #2 내용
○ 취약점 경로 : http://홈페이지주소/board/bbs/board.php?bo_table=resume&wr_id=2213327
○ 취약점 내용 : 게시판에 악성스크립트를 삽입하여 악성코드 유포 및 피싱사이트 유도 공격 시도 가능
○ 대응방안 : 모든 사용자 입력 값에 불필요한 특수문자(',",<,>)를 서버단에서 치환해야 함.
(필수적으로 모든 페이지의 입력 값 파라미터에 일괄 적용해야 함)
4. 취약점 #3 내용
○ 취약점 경로 : http://홈페이지주소/board/bbs/login.php?url=/%27%20a=b%27%3E%3Cscript%3Ealert
(1)%3C/script%3E%3Cb%3E%3C%27
○ 취약점 내용 : Reflected XSS 공격을 통해 악성코드 유포 및 피싱사이트 유도 공격 시도 가능
○ 대응방안 : 모든 사용자 입력 값에 불필요한 특수문자(',",<,>)를 서버단에서 치환해야 함.
(필수적으로 모든 페이지의 입력 값 파라미터에 일괄 적용해야 함)
많은 관심부탁드립니다. 감사합니다.
답변 5개
댓글을 작성하려면 로그인이 필요합니다.
그누보드 최신 버전을 받아 common.php파일과 common.lib.php파일을 대체해 봤더니 홈페이지가 열리지 않네요. 현재 원상복구 시켰더니 홈페이지는 정상으로 운영됩니다.
혹시나 아래에 common.php 파일과 common.lib.php파일의 소스를 올렸는데 수정 가능한지요?
부탁드립니다.
common.php파일
// 보안설정이나 프레임이 달라도 쿠키가 통하도록 설정 header('P3P: CP="ALL CURa ADMa DEVa TAIa OUR BUS IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC OTC"');
if (!defined('G5_SET_TIME_LIMIT')) define('G5_SET_TIME_LIMIT', 0); https://sir.kr/main/member/?mb_id=naver_rpfvau67" target="_blank">@set_time_limit(G5_SET_TIME_LIMIT);
//========================================================================================================================== // extract($_GET); 명령으로 인해 page.php?_POST[var1]=data1&_POST[var2]=data2 와 같은 코드가 _POST 변수로 사용되는 것을 막음 // 081029 : letsgolee 님께서 도움 주셨습니다. //-------------------------------------------------------------------------------------------------------------------------- $ext_arr = array ('PHP_SELF', '_ENV', '_GET', '_POST', '_FILES', '_SERVER', '_COOKIE', '_SESSION', '_REQUEST', 'HTTP_ENV_VARS', 'HTTP_GET_VARS', 'HTTP_POST_VARS', 'HTTP_POST_FILES', 'HTTP_SERVER_VARS', 'HTTP_COOKIE_VARS', 'HTTP_SESSION_VARS', 'GLOBALS'); $ext_cnt = count($ext_arr); for ($i=0; $i<$ext_cnt; $i++) { // POST, GET 으로 선언된 전역변수가 있다면 unset() 시킴 if (isset($_GET[$ext_arr[$i]])) unset($_GET[$ext_arr[$i]]); if (isset($_POST[$ext_arr[$i]])) unset($_POST[$ext_arr[$i]]); } //==========================================================================================================================
function g5_path() { $result['path'] = str_replace('\\', '/', dirname(__FILE__)); // $result['path'] = str_replace('\\', '/', dirname(__FILE__)); $tilde_remove = preg_replace('/^\/\~[^\/]+(.*)$/', '$1', $_SERVER['SCRIPT_NAME']); $document_root = str_replace($tilde_remove, '', $_SERVER['SCRIPT_FILENAME']); $root = str_replace($document_root, '', $result['path']); $port = $_SERVER['SERVER_PORT'] != 80 ? ':'.$_SERVER['SERVER_PORT'] : ''; $http = 'http' . ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']=='on') ? 's' : '') . '://'; $user = str_replace(str_replace($document_root, '', $_SERVER['SCRIPT_FILENAME']), '', $_SERVER['SCRIPT_NAME']); $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME']; if(isset($_SERVER['HTTP_HOST']) && preg_match('/:[0-9]+$/', $host)) $host = preg_replace('/:[0-9]+$/', '', $host); //$result['url'] = $http.$host.$port.$user.$root; $result['url'] = $http.$host.$port.$user; //echo $result['url'];exit; //$result['url'] = $http.$host."/board";
return $result; }
$g5_path = g5_path(); //echo $g5_path['url'];exit;
include_once($g5_path['path'].'/config.php'); // 설정 파일
unset($g5_path);
// multi-dimensional array에 사용자지정 함수적용 function array_map_deep($fn, $array) { if(is_array($array)) { foreach($array as $key => $value) { if(is_array($value)) { $array[$key] = array_map_deep($fn, $value); } else { $array[$key] = call_user_func($fn, $value); } } } else { $array = call_user_func($fn, $array); }
return $array; }
// SQL Injection 대응 문자열 필터링 function sql_escape_string($str) { if(defined('G5_ESCAPE_PATTERN') && defined('G5_ESCAPE_REPLACE')) { $pattern = G5_ESCAPE_PATTERN; $replace = G5_ESCAPE_REPLACE;
if($pattern) $str = preg_replace($pattern, $replace, $str); }
$str = call_user_func('addslashes', $str);
return $str; }
//============================================================================== // SQL Injection 등으로 부터 보호를 위해 sql_escape_string() 적용 //------------------------------------------------------------------------------ // magic_quotes_gpc 에 의한 backslashes 제거 if (get_magic_quotes_gpc()) { $_POST = array_map_deep('stripslashes', $_POST); $_GET = array_map_deep('stripslashes', $_GET); $_COOKIE = array_map_deep('stripslashes', $_COOKIE); $_REQUEST = array_map_deep('stripslashes', $_REQUEST); }
// sql_escape_string 적용 $_POST = array_map_deep(G5_ESCAPE_FUNCTION, $_POST); $_GET = array_map_deep(G5_ESCAPE_FUNCTION, $_GET); $_COOKIE = array_map_deep(G5_ESCAPE_FUNCTION, $_COOKIE); $_REQUEST = array_map_deep(G5_ESCAPE_FUNCTION, $_REQUEST); //==============================================================================
// PHP 4.1.0 부터 지원됨 // php.ini 의 register_globals=off 일 경우 @extract($_GET); @extract($_POST); @extract($_SERVER);
// 완두콩님이 알려주신 보안관련 오류 수정 // $member 에 값을 직접 넘길 수 있음 $config = array(); $member = array(); $board = array(); $group = array(); $g5 = array();
//============================================================================== // 공통 //------------------------------------------------------------------------------ $dbconfig_file = G5_DATA_PATH.'/'.G5_DBCONFIG_FILE; if (file_exists($dbconfig_file)) { include_once($dbconfig_file); include_once(G5_LIB_PATH.'/common.lib.php'); // 공통 라이브러리
$connect_db = sql_connect(G5_MYSQL_HOST, G5_MYSQL_USER, G5_MYSQL_PASSWORD) or die('MySQL Connect Error!!!'); $select_db = sql_select_db(G5_MYSQL_DB, $connect_db) or die('MySQL DB Error!!!');
// mysql connect resource $g5 배열에 저장 - 명랑폐인님 제안 $g5['connect_db'] = $connect_db;
sql_query(" set names utf8 "); if(defined('G5_MYSQL_SET_MODE') && G5_MYSQL_SET_MODE) sql_query("SET SESSION sql_mode = ''"); if (defined(G5_TIMEZONE)) sql_query(" set time_zone = '".G5_TIMEZONE."'"); } else { ?>
그누보드5를 먼저 설치해주십시오.
GPL! OPEN SOURCE GNUBOARD
common.lib.php파일
/************************************************************************* ** ** 일반 함수 모음 ** *************************************************************************/
// 마이크로 타임을 얻어 계산 형식으로 만듦 function get_microtime() { list($usec, $sec) = explode(" ",microtime()); return ((float)$usec + (float)$sec); }
// 한페이지에 보여줄 행, 현재페이지, 총페이지수, URL function get_paging($write_pages, $cur_page, $total_page, $url, $add="") { //$url = preg_replace('#&page=[0-9]*(&page=)$#', '$1', $url); $url = preg_replace('#&page=[0-9]*#', '', $url) . '&page=';
$str = ''; if ($cur_page > 1) { $str .= '처음'.PHP_EOL; }
$start_page = ( ( (int)( ($cur_page - 1 ) / $write_pages ) ) * $write_pages ) + 1; $end_page = $start_page + $write_pages - 1;
if ($end_page >= $total_page) $end_page = $total_page;
if ($start_page > 1) $str .= '이전'.PHP_EOL;
if ($total_page > 1) { for ($k=$start_page;$k<=$end_page;$k++) { if ($cur_page != $k) $str .= ''.$k.'페이지'.PHP_EOL; else $str .= '열린'.$k.'페이지'.PHP_EOL; } }
if ($total_page > $end_page) $str .= '다음'.PHP_EOL;
if ($cur_page < $total_page) { $str .= '맨끝'.PHP_EOL; }
if ($str) return ""; else return ""; }
// 페이징 코드의
return preg_replace("/^(
// 페이징 코드의 태그 이전에 코드를 삽입
function page_insertafter($paging_html, $insert_html)
{
if(!$paging_html)
$paging_html = '
if(preg_match("#".PHP_EOL."#", $paging_html)) $php_eol = ''; else $php_eol = PHP_EOL;
return preg_replace("#()$#", $php_eol.$insert_html.'$1', $paging_html); }
// 변수 또는 배열의 이름과 값을 얻어냄. print_r() 함수의 변형 function print_r2($var) { ob_start(); print_r($var); $str = ob_get_contents(); ob_end_clean(); $str = str_replace(" ", " ", $str); echo nl2br("$str"); }
// 메타태그를 이용한 URL 이동 // header("location:URL") 을 대체 function goto_url($url) { $url = str_replace("&", "&", $url); //echo "";
if (!headers_sent()) header('Location: '.$url); else { echo ''; echo ''; } exit; }
// 세션변수 생성 function set_session($session_name, $value) { if (PHP_VERSION < '5.3.0') session_register($session_name); // PHP 버전별 차이를 없애기 위한 방법 $$session_name = $_SESSION[$session_name] = $value; }
// 세션변수값 얻음 function get_session($session_name) { return isset($_SESSION[$session_name]) ? $_SESSION[$session_name] : ''; }
// 쿠키변수 생성 function set_cookie($cookie_name, $value, $expire) { global $g5;
setcookie(md5($cookie_name), base64_encode($value), G5_SERVER_TIME + $expire, '/', G5_COOKIE_DOMAIN); }
// 쿠키변수값 얻음 function get_cookie($cookie_name) { $cookie = md5($cookie_name); if (array_key_exists($cookie, $_COOKIE)) return base64_decode($_COOKIE[md5($cookie_name)]); else return ""; }
// 경고메세지를 경고창으로 function alert($msg='', $url='', $error=true, $post=false) { global $g5, $config, $member; global $is_admin;
if (!$msg) $msg = '올바른 방법으로 이용해 주십시오.';
$header = ''; if (isset($g5['title'])) { $header = $g5['title']; } include_once(G5_BBS_PATH.'/alert.php'); exit; }
// 경고메세지 출력후 창을 닫음 function alert_close($msg, $error=true) { global $g5;
$header = ''; if (isset($g5['title'])) { $header = $g5['title']; } include_once(G5_BBS_PATH.'/alert_close.php'); exit; }
// confirm 창 function confirm($msg, $url1='', $url2='', $url3='') { global $g5;
if (!$msg) { $msg = '올바른 방법으로 이용해 주십시오.'; alert($msg); }
if(!trim($url1) || !trim($url2)) { $msg = '$url1 과 $url2 를 지정해 주세요.'; alert($msg); }
if (!$url3) $url3 = $_SERVER['HTTP_REFERER'];
$msg = str_replace("\\n", " ", $msg);
$header = ''; if (isset($g5['title'])) { $header = $g5['title']; } include_once(G5_BBS_PATH.'/confirm.php'); exit; }
// way.co.kr 의 wayboard 참고 function url_auto_link($str) { global $g5; global $config;
// 140326 유창화님 제안코드로 수정 // http://sir.co.kr/bbs/board.php?bo_table=pg_lecture&wr_id=461 // http://sir.co.kr/bbs/board.php?bo_table=pg_lecture&wr_id=463 $str = str_replace(array("<", ">", "&", """, " "), array("\t_lt_\t", "\t_gt_\t", "&", "\"", "\t_nbsp_\t"), $str); $str = preg_replace("`(?:(?:(?:href|src)\s*=\s*(?:\"|'|)){0})((http|https|ftp|telnet|news|mms)://[^\"'\s()]+)`", "\\1", $str); $str = preg_replace("/(^|[\"'\s(])(www\.[^\"'\s()]+)/i", "\\1http://\\2\" TARGET='{$config['cf_link_target']}'>\\2", $str); $str = preg_replace("/[0-9a-z_-]+@[a-z0-9._-]{4,}/i", "\\0", $str); $str = str_replace(array("\t_nbsp_\t", "\t_lt_\t", "\t_gt_\t"), array(" ", "<", ">"), $str);
/* // 속도 향상 031011 $str = preg_replace("/</", "\t_lt_\t", $str); $str = preg_replace("/>/", "\t_gt_\t", $str); $str = preg_replace("/&/", "&", $str); $str = preg_replace("/"/", "\"", $str); $str = preg_replace("/ /", "\t_nbsp_\t", $str); $str = preg_replace("/([^(http:\/\/)]|\(|^)(www\.[^[:space:]]+)/i", "\\1http://\\2\" TARGET='{$config['cf_link_target']}'>\\2", $str); //$str = preg_replace("/([^(HREF=\"?'?)|(SRC=\"?'?)]|\(|^)((http|https|ftp|telnet|news|mms):\/\/[a-zA-Z0-9\.-]+\.[\xA1-\xFEa-zA-Z0-9\.:=_\?\/~\+%@;\-\|\,]+)/i", "\\1\\2", $str); // 100825 : () 추가 // 120315 : CHARSET 에 따라 링크시 글자 잘림 현상이 있어 수정 $str = preg_replace("/([^(HREF=\"?'?)|(SRC=\"?'?)]|\(|^)((http|https|ftp|telnet|news|mms):\/\/[a-zA-Z0-9\.-]+\.[가-힣\xA1-\xFEa-zA-Z0-9\.:=_\?\/~\+%@;\-\|\,\(\)]+)/i", "\\1\\2", $str);
// 이메일 정규표현식 수정 061004 //$str = preg_replace("/(([a-z0-9_]|\-|\.)+@([^[:space:]]*)([[:alnum:]-]))/i", "\\1", $str); $str = preg_replace("/([0-9a-z]([-_\.]?[0-9a-z])*@[0-9a-z]([-_\.]?[0-9a-z])*\.[a-z]{2,4})/i", "\\1", $str); $str = preg_replace("/\t_nbsp_\t/", " " , $str); $str = preg_replace("/\t_lt_\t/", "<", $str); $str = preg_replace("/\t_gt_\t/", ">", $str); */
return $str; }
// url에 http:// 를 붙인다 function set_http($url) { if (!trim($url)) return;
if (!preg_match("/^(http|https|ftp|telnet|news|mms)\:\/\//i", $url)) $url = "http://" . $url;
return $url; }
// 파일의 용량을 구한다. //function get_filesize($file) function get_filesize($size) { //$size = @filesize(addslashes($file)); if ($size >= 1048576) { $size = number_format($size/1048576, 1) . "M"; } else if ($size >= 1024) { $size = number_format($size/1024, 1) . "K"; } else { $size = number_format($size, 0) . "byte"; } return $size; }
// 게시글에 첨부된 파일을 얻는다. (배열로 반환) function get_file($bo_table, $wr_id) { global $g5, $qstr;
$file['count'] = 0; $sql = " select * from {$g5['board_file_table']} where bo_table = '$bo_table' and wr_id = '$wr_id' order by bf_no "; $result = sql_query($sql); while ($row = sql_fetch_array($result)) { $no = $row['bf_no']; $file[$no]['href'] = G5_BBS_URL."/download.php?bo_table=$bo_table&wr_id=$wr_id&no=$no" . $qstr; $file[$no]['download'] = $row['bf_download']; // 4.00.11 - 파일 path 추가 $file[$no]['path'] = G5_DATA_URL.'/file/'.$bo_table; $file[$no]['size'] = get_filesize($row['bf_filesize']); $file[$no]['datetime'] = $row['bf_datetime']; $file[$no]['source'] = addslashes($row['bf_source']); $file[$no]['bf_content'] = $row['bf_content']; $file[$no]['content'] = get_text($row['bf_content']); //$file[$no]['view'] = view_file_link($row['bf_file'], $file[$no]['content']); $file[$no]['view'] = view_file_link($row['bf_file'], $row['bf_width'], $row['bf_height'], $file[$no]['content']); $file[$no]['file'] = $row['bf_file']; $file[$no]['image_width'] = $row['bf_width'] ? $row['bf_width'] : 640; $file[$no]['image_height'] = $row['bf_height'] ? $row['bf_height'] : 480; $file[$no]['image_type'] = $row['bf_type']; $file['count']++; }
return $file; }
// 폴더의 용량 ($dir는 / 없이 넘기세요) function get_dirsize($dir) { $size = 0; $d = dir($dir); while ($entry = $d->read()) { if ($entry != '.' && $entry != '..') { $size += filesize($dir.'/'.$entry); } } $d->close(); return $size; }
/************************************************************************* ** ** 그누보드 관련 함수 모음 ** *************************************************************************/
// 게시물 정보($write_row)를 출력하기 위하여 $list로 가공된 정보를 복사 및 가공 function get_list($write_row, $board, $skin_url, $subject_len=40) { global $g5, $config; global $qstr, $page;
//$t = get_microtime();
// 배열전체를 복사 $list = $write_row; unset($write_row);
$board_notice = array_map('trim', explode(',', $board['bo_notice'])); $list['is_notice'] = in_array($list['wr_id'], $board_notice);
if ($subject_len) $list['subject'] = conv_subject($list['wr_subject'], $subject_len, '…'); else $list['subject'] = conv_subject($list['wr_subject'], $board['bo_subject_len'], '…');
// 목록에서 내용 미리보기 사용한 게시판만 내용을 변환함 (속도 향상) : kkal3(커피)님께서 알려주셨습니다. if ($board['bo_use_list_content']) { $html = 0; if (strstr($list['wr_option'], 'html1')) $html = 1; else if (strstr($list['wr_option'], 'html2')) $html = 2;
$list['content'] = conv_content($list['wr_content'], $html); }
$list['comment_cnt'] = ''; if ($list['wr_comment']) $list['comment_cnt'] = "".$list['wr_comment']."";
// 당일인 경우 시간으로 표시함 $list['datetime'] = substr($list['wr_datetime'],0,10); $list['datetime2'] = $list['wr_datetime']; if ($list['datetime'] == G5_TIME_YMD) $list['datetime2'] = substr($list['datetime2'],11,5); else $list['datetime2'] = substr($list['datetime2'],5,5); // 4.1 $list['last'] = substr($list['wr_last'],0,10); $list['last2'] = $list['wr_last']; if ($list['last'] == G5_TIME_YMD) $list['last2'] = substr($list['last2'],11,5); else $list['last2'] = substr($list['last2'],5,5);
$list['wr_homepage'] = get_text(addslashes($list['wr_homepage']));
$tmp_name = get_text(cut_str($list['wr_name'], $config['cf_cut_name'])); // 설정된 자리수 만큼만 이름 출력 if ($board['bo_use_sideview']) $list['name'] = get_sideview($list['mb_id'], $tmp_name, $list['wr_email'], $list['wr_homepage']); else $list['name'] = ''.$tmp_name.'';
$reply = $list['wr_reply'];
$list['reply'] = strlen($reply)*10;
$list['icon_reply'] = '';
if ($list['reply'])
$list['icon_reply'] = '
';
$list['icon_link'] = '';
if ($list['wr_link1'] || $list['wr_link2'])
$list['icon_link'] = '
';
// 분류명 링크 $list['ca_name_href'] = G5_BBS_URL.'/board.php?bo_table='.$board['bo_table'].'&sca='.urlencode($list['ca_name']);
$list['href'] = G5_BBS_URL.'/board.php?bo_table='.$board['bo_table'].'&wr_id='.$list['wr_id'].$qstr; $list['comment_href'] = $list['href'];
$list['icon_new'] = '';
if ($board['bo_new'] && $list['wr_datetime'] >= date("Y-m-d H:i:s", G5_SERVER_TIME - ($board['bo_new'] * 3600)))
$list['icon_new'] = '
';
$list['icon_hot'] = '';
if ($board['bo_hot'] && $list['wr_hit'] >= $board['bo_hot'])
$list['icon_hot'] = '
';
$list['icon_secret'] = '';
if (strstr($list['wr_option'], 'secret'))
$list['icon_secret'] = '
';
// 링크 for ($i=1; $i<=G5_LINK_COUNT; $i++) { $list['link'][$i] = set_http(get_text($list["wr_link{$i}"])); $list['link_href'][$i] = G5_BBS_URL.'/link.php?bo_table='.$board['bo_table'].'&wr_id='.$list['wr_id'].'&no='.$i.$qstr; $list['link_hit'][$i] = (int)$list["wr_link{$i}_hit"]; }
// 가변 파일 if ($board['bo_use_list_file'] || ($list['wr_file'] && $subject_len == 255) /* view 인 경우 */) { $list['file'] = get_file($board['bo_table'], $list['wr_id']); } else { $list['file']['count'] = $list['wr_file']; }
if ($list['file']['count'])
$list['icon_file'] = '
';
return $list; }
// get_list 의 alias function get_view($write_row, $board, $skin_url) { return get_list($write_row, $board, $skin_url, 255); }
// set_search_font(), get_search_font() 함수를 search_font() 함수로 대체 function search_font($stx, $str) { global $config;
// 문자앞에 \ 를 붙입니다. $src = array('/', '|'); $dst = array('\/', '\|');
if (!trim($stx)) return $str;
// 검색어 전체를 공란으로 나눈다 $s = explode(' ', $stx);
// "/(검색1|검색2)/i" 와 같은 패턴을 만듬
$pattern = '';
$bar = '';
for ($m=0; $m
// 지정된 검색 폰트의 색상, 배경색상으로 대체 $replace = "\\1";
return preg_replace("/($pattern)/i", $replace, $str); }
// 제목을 변환 function conv_subject($subject, $len, $suffix='') { return cut_str(get_text($subject), $len, $suffix); }
// 내용을 변환 function conv_content($content, $html, $filter=true) { global $config, $board;
if ($html) { $source = array(); $target = array();
$source[] = "//"; $target[] = "";
if ($html == 2) { // 자동 줄바꿈 $source[] = "/\n/"; $target[] = " "; }
// 테이블 태그의 개수를 세어 테이블이 깨지지 않도록 한다. $table_begin_count = substr_count(strtolower($content), "