CVE-2017-12061, CVE-2017-12062
MantisBT는 오픈소스 이슈 트래킹 도구 중 하나이며 간편한 사용법으로 많은 사용자를 보유하고 있다. 최근 Cross Site Scripting 취약점 (CVE-2017-12061, CVE-2017-12062)이 발생하였고 조치가 완료되었다. 수정된 소스코드를 통해서 해당 취약점을 살펴보자.
CVE-2017-12061
취약점이 발생된 곳은 /admin/install.php
소스이다.
- 영향 버전: 1.3.11 and older, 2.5.1 and older
- 조치 버전: 1.3.12, 2.5.2, 2.6.0 (not yet released*)
POC
http://mantis.server/admin/install.php?install=3&database_name=%3Ch1%3EXSS&admin_username=%3Ch1%3EXSS
http://mantis.server//admin/install.php?install=3&database_name=%3Cscript%3Ealert(%27XSS%27)%3C/script%3E&admin_username=%3Cscript%3Ealert(%27XSS%27)%3C/script%3E
Fix
# the check only works on mysql if the database is open
$t_version_info = @$g_db->ServerInfo();
} else {
- print_test_result( BAD, true, 'Does administrative user have access to the database? ( ' . db_error_msg() . ' )' );
+ print_test_result(
+ BAD,
+ true,
+ 'Does administrative user have access to the database? ( ' . string_attribute( db_error_msg() ) . ' )'
+ );
$t_version_info = null;
}
?>
@@ -469,7 +473,11 @@ function print_test( $p_test_description, $p_result, $p_hard_fail = true, $p_mes
print_test_result( GOOD );
}
} else {
- print_test_result( BAD, false, 'Database user doesn\'t have access to the database ( ' . db_error_msg() . ' )' );
+ print_test_result(
+ BAD,
+ false,
+ 'Database user doesn\'t have access to the database ( ' . string_attribute( db_error_msg() ) . ' )'
+ );
}
?>
</tr>
@@ -791,7 +799,11 @@ function print_test( $p_test_description, $p_result, $p_hard_fail = true, $p_mes
if( !$t_rs ) {
$t_result = false;
- print_test_result( BAD, true, 'Does administrative user have access to create the database? ( ' . db_error_msg() . ' )' );
+ print_test_result(
+ BAD,
+ true,
+ 'Does administrative user have access to create the database? ( ' . string_attribute( db_error_msg() ) . ' )'
+ );
$t_install_state--; # db creation failed, allow user to re-enter user/password info
} else {
print_test_result( GOOD );
@@ -814,9 +826,18 @@ function print_test( $p_test_description, $p_result, $p_hard_fail = true, $p_mes
}
if( $t_db_exists ) {
- print_test_result( BAD, false, 'Database already exists? ( ' . db_error_msg() . ' )' );
- } else {
- print_test_result( BAD, true, 'Does administrative user have access to create the database? ( ' . db_error_msg() . ' )' );
+ print_test_result(
+ BAD,
+ false,
+ 'Database already exists? ( ' . string_attribute( db_error_msg() ) . ' )'
+ );
+ }
+ else {
+ print_test_result(
+ BAD,
+ true,
+ 'Does administrative user have access to create the database? ( ' . string_attribute( db_error_msg() ) . ' )'
+ );
$t_install_state--; # db creation failed, allow user to re-enter user/password info
}
}
@@ -847,7 +868,11 @@ function print_test( $p_test_description, $p_result, $p_hard_fail = true, $p_mes
if( $t_result == true ) {
print_test_result( GOOD );
} else {
- print_test_result( BAD, false, 'Database user doesn\'t have access to the database ( ' . db_error_msg() . ' )' );
+ print_test_result(
+ BAD,
+ false,
+ 'Database user doesn\'t have access to the database ( ' . string_attribute( db_error_msg() ) . ' )'
+ );
}
$g_db->Close();
?>
@@ -1242,7 +1267,11 @@ function print_test( $p_test_description, $p_result, $p_hard_fail = true, $p_mes
if( $t_result == true ) {
print_test_result( GOOD );
} else {
- print_test_result( BAD, false, 'Database user does not have access to the database ( ' . db_error_msg() . ' )' );
+ print_test_result(
+ BAD,
+ false,
+ 'Database user does not have access to the database ( ' . string_attribute( db_error_msg() ) . ' )'
+ );
}
if( $f_db_type == 'db2' ) {
@@ -1264,7 +1293,11 @@ function print_test( $p_test_description, $p_result, $p_hard_fail = true, $p_mes
if( $t_result != false ) {
print_test_result( GOOD );
} else {
- print_test_result( BAD, true, 'Database user does not have SELECT access to the database ( ' . db_error_msg() . ' )' );
+ print_test_result(
+ BAD,
+ true,
+ 'Database user does not have SELECT access to the database ( ' . string_attribute( db_error_msg() ) . ' )'
+ );
}
?>
</tr>
@@ -1279,7 +1312,11 @@ function print_test( $p_test_description, $p_result, $p_hard_fail = true, $p_mes
if( $t_result != false ) {
print_test_result( GOOD );
} else {
- print_test_result( BAD, true, 'Database user does not have INSERT access to the database ( ' . db_error_msg() . ' )' );
+ print_test_result(
+ BAD,
+ true,
+ 'Database user does not have INSERT access to the database ( ' . string_attribute( db_error_msg() ) . ' )'
+ );
}
?>
</tr>
@@ -1294,7 +1331,11 @@ function print_test( $p_test_description, $p_result, $p_hard_fail = true, $p_mes
if( $t_result != false ) {
print_test_result( GOOD );
} else {
- print_test_result( BAD, true, 'Database user does not have UPDATE access to the database ( ' . db_error_msg() . ' )' );
+ print_test_result(
+ BAD,
+ true,
+ 'Database user does not have UPDATE access to the database ( ' . string_attribute( db_error_msg() ) . ' )'
+ );
}
?>
</tr>
@@ -1309,7 +1350,11 @@ function print_test( $p_test_description, $p_result, $p_hard_fail = true, $p_mes
if( $t_result != false ) {
print_test_result( GOOD );
} else {
- print_test_result( BAD, true, 'Database user does not have DELETE access to the database ( ' . db_error_msg() . ' )' );
+ print_test_result(
+ BAD,
+ true,
+ 'Database user does not have DELETE access to the database ( ' . string_attribute( db_error_msg() ) . ' )'
+ );
}
?>
</tr>
소스코드에서 확인할 수 있듯이 db_error_msg()
함수를 string_attribute()
함수로 감싸는 형식으로 조치한 것을 볼 수 있다. POC Code에서 스크립트 구문 삽입 시 사용된 database_name
, admin_username
파라미터의 값을 db_error_msg()
함수가 그대로 사용자에게 반환하여 판단되며 스크립트 관련 특수 문자를 걸러내기 위해서 string_attribute()
함수를 적용시킨 것으로 보인다.
이제 string_attribute()
함수를 살펴보자. string_attribute()
함수는 mantisbt/core/string_api.php
231번 줄에 위치하고 있다.
function string_attribute( $p_string ) {
return string_html_specialchars( $p_string );
}
string_html_specialchars()
함수를 호출하는 것 외에는 다른 내용이 없다. string_html_specialchars()
함수는 동일한 파일의 908번 줄에 위치하고 있다.
function string_html_specialchars( $p_string ) {
# Remove any invalid character from the string per XML 1.0 specification
# http://www.w3.org/TR/2008/REC-xml-20081126/#NT-Char
$p_string = preg_replace( '/[^\x9\xA\xD\x20-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]+/u', '', $p_string );
# achumakov: @ added to avoid warning output in unsupported codepages
# e.g. 8859-2, windows-1257, Korean, which are treated as 8859-1.
# This is VERY important for Eastern European, Baltic and Korean languages
return preg_replace( '/&(#[0-9]+|[a-z]+);/i', '&$1;', @htmlspecialchars( $p_string, ENT_COMPAT, 'utf-8' ) );
}
Cross Site Scripting에 관련된 부분은 @htmlspecialchars( $p_string, ENT_COMPAT, 'utf-8' )
이 부분으로 $p_string
변수의 문자열 값 중 특수문자를 HTML 엔터티 값으로 변환하는 데, ENT_COMPAT
을 설정하였으므로 따옴표 중 겹따옴표만 변환하며 문자셋은 utf-8
을 사용하게 된다.
- 치환 특수문자
- ’&’ :
&
- ‘”’ :
"
- ”’ :
'
- ‘<’ :
<
- ‘>’ :
>
- ’&’ :
CVE-2017-12062
취약점이 발생된 곳은 manage_user_page.php
소스이다.
영향 버전: 2.1.0 through 2.5.1
조치 버전: 2.5.2, 2.6.0 (not yet released*)
POC
http://localhost/mantisbt/manage_user_page.php
?sort=username
&dir=desc
&save=1
&hideinactive=0
&showdisabled=0
&filter=ALL"><SVG ONLOAD=a&<a href="/bugs/view.php?id=108" title="[종료된 이슈] When adding a bugnote can we have at some other detail on the page." class="resolved">0000108</a>e&<a href="/bugs/view.php?id=114" title="[종료된 이슈] Make date format displayed configurable." class="resolved">0000114</a>t(1)><IMG SRC="
<svg onload=alert(1)>
Fix
<input type="hidden" name="sort" value="<?php echo $c_sort ?>" />
<input type="hidden" name="dir" value="<?php echo $c_dir ?>" />
<input type="hidden" name="save" value="1" />
- <input type="hidden" name="filter" value="<?php echo $f_filter ?>" />
+ <input type="hidden" name="filter" value="<?php echo string_attribute( $f_filter ); ?>" />
<label class="inline">
<input type="checkbox" class="ace" name="hideinactive" value="<?php echo ON ?>" <?php check_checked( (int)$c_hide_inactive, ON ); ?> />
<span class="lbl"> <?php echo lang_get( 'hide_inactive' ) ?></span>
조치된 방법은 CVE-2017-12061 취약점과 동일하다.