XSS(Cross Site Scripting) 취약점 - WordPress CM-AD-Changer 플러그인 (3)

by Dreamer

   개요
앞선 포스팅에서 CM-AD-Changer 플러그인의 취약점 소개와 CSRF를 활용한 시나리오를 설명하였다. 이번 포스팅에서는 XSS 취약점을 해결 할 수 있는 대응 방안에 대해 살펴보자.

광고 배너 정보를 저장할 때 처리되는 페이지는 admin_campaigns.php와 cmac_data.php 이다. admin_campaigns.php는 [그림 1-1]과 같이 배너 정보를 입력하는 Form 태그와 input 태그 정보가 있는 페이지고, cmac_data.php는 입력 된 배너 정보를 데이터베이스에 저장 할 때 처리되는 페이지다. 대응 방안으로 직접 시큐어코딩을 적용해보고 패치된 버전은 어떻게 보안했는 지 확인한다. 그리고 시큐어코딩 이외에 보안가능한 방법이 있는 지도 살펴보자.
그림 1-1 광고 배너 정보 입력 창

포스팅 링크
1. XSS(Cross Site Scripting) 취약점 - CM-AD-Changer 플러그인 (1)
2. XSS(Cross Site Scripting) 취약점 - CM-AD-Changer 플러그인 (2)


   시큐어코딩
입력 페이지(admin_campagins.php)에서 가능한 시큐어 코딩은 취약점이 발생한 위치의 입력 길이를 제한하거나 입력 후 처리 페이지(cmac_data.php)로 전송되기 전 유효성을 체크하는 방법이 있다. 입력 값 유효성 체크는 자바스크립트를 통해 적용 할 수 있는 데 보통은 프록시를 이용하면 쉽게 우회 가능하다. 하지만, 해당 플러그인은 관리자만 조작할 수 있는 특징 때문에 해커가 관리자로 로그인 하지 않는 이상 프록시로 응답 메시지를 트랩 할 수 없어 적용 된 유효성을 우회할 수 없다.

입력 값 길이 제한
해당 취약점은 이미지 이름(Name)의 입력 창에서 발생한다. 이름이 입력되는 INPUT 태그의 정보를 보면, [그림 2-1]의 좌측과 같이 최대 길이 옵션(maxlength)이 150자로 설정되어 있다. 이미지 이름을 입력 받는 데 150자는 불필요하다. 최대 길이를 15~20자로 설정하면 XSS 공격 스크립트를 작성 할 수 없기 때문에 XSS 취약점을 보안 할 수 있다.
그림 2-1 Input 태그 (좌)1.7.7 버전 (우) 시큐어 코딩 코드

최대 길이 옵션을 15로 설정하고 이미지 이름을 입력한 결과 [그림 2‑2]처럼 15자만 입력 할 수 있다.
그림 2-2 최대 길이 옵션 적용


자바 스크립트 유효성 체크
자바스크립트로 유효성을 검사하는 함수를 만들기 이전에 Form 태그의 Name을 지정하고, submit 태그에 Onclick 옵션을 사용한다. [그림 2-3]와 같이 Form 태그의 Name은 cam으로 Onclick을 통해 동작할 스크립트 함수 명은 save(); 로 하였다.
그림 2-3 1.7.7 버전 (좌), 시큐어코딩 적용 (우)

Form 태그의 name 값을 지정한 이유는 document.cam[].value를 통하여 원하는 INPUT 태그의 값을 불러오기 위함이다. 그리고 < > 문자를 검사하는 스크립트 함수로 search()를 사용한다. Search 함수는 해당 문자가 있을 시 문자가 있는 위치를 반환하고 없다면 -1을 반환한다. 그래서 if 조건에 +1을 적용하여 문자가 없을 시 0이 되도록 한다.
<script type=”text/javascript”>
function save2(){
  var string = document.cam["banner_title[]"].value;
//cam이름을 가진 폼의 banner_title[] 값을 string 변수에 대입
  if(string.search('<')+1||string.search('>')+1){
//search함수를 통해 문자열 찾기
//해당되는 문자가 없으면 -1을 반환하기 때문에 +1을 더하여 0으로 만듬
   alert('이름을 다시 입력하시오');
   history.back(-1); return;
  }
 }
</script>
표 2-1유효성 체크 함수

스크립트를 적용 후 [그림 2-4]와 같이 <를 입력하면, [그림 2-5]처럼 알림 창이 뜬 뒤 이전 페이지로 이동한다.
그림 2-4 유효성 함수 작동 확인

그림 2-5 유효성 함수 정상 작동


   플러그인 업데이트
CM-AD-Changer 플러그인은 1.7.7에서 발생한 XSS 취약점을 패치한 1.7.8 버전을 제공한다. 해당 플러그인을 설치 후 소스코드를 비교하여 어떻게 시큐어코딩을 적용했는 지 살펴보자. 1.7.8 버전은 여기에서 다운로드 받는다.

패치 버전과 소스 코드 비교 분석
1.7.8 버전의 경우는 입력 페이지는 변경하지 않고, 처리 페이지 (cmac-data.php)의 코드 중 데이터베이스로 처리되기 전의 값을 sanitize_text_field 함수를 이용하여 예외 처리한다. Sanitize_text_field 함수는 워드프레스에서 제공하는 함수로, 입력 받은 값이나 데이터베이스로부터 값 중에서 특정 문자(값)을 필터링한다. 필터링 대상은 모든 태그, 줄 바꿈, 탭, 여분의 공백이며 해당 대상을 제거한 뒤 잘못된 UTF-8을 검사한다.
그림 3-1 1.7.7 버전 (좌), 1.7.8 버전 (우)

[그림 6‑8]은 sanitize_text_field 함수가 적용되지 않은 1.7.7 버전에서 저장 된 이미지 이름 정보이다. </script><script> …스크립트가 데이터베이스에 저장된 것을 볼 수 있다. 응답 메시지로 출력할 때도 해당 문자열 그대로 가져오기 때문에 스크립트가 동작한다.
그림 3-2 1.7.7 버전 데이터베이스 저장 결과

하지만, sanitize_text_field 함수가 적용된 1.7.8 버전의 경우에는 </script><script>…..를 입력하더라도 데이터베이스에 저장되지 않음을 알 수 있다. 태그를 모두 제거했기 때문에 입력 받은 값이 없다.
그림 3-3 1.7.8 버전 데이터베이스 저장 결과

태그나 공백 등을 알아서 제거해주는 sanitize_text_field 함수는 /wp-include/formatting.php 경로에 위치한다. 앞서 설명한 내용과 같이 태그를 치환하거나, 공백 엔터, 탭, + 를 공백으로 치환하는 코드가 적용되어 있다.
function sanitize_text_field( $str ) {
 $filtered = wp_check_invalid_utf8( $str );
// wp_check_invalid_utf8 함수를 통해 적절하지 않은 UTF-8을 필터링 함.
 if ( strpos($filtered, '<') !== false ) {
  $filtered = wp_pre_kses_less_than( $filtered );
  $filtered = wp_strip_all_tags( $filtered, true );
 } else {
  $filtered = trim( preg_replace('/[\r\n\t ]+/', ' ', $filtered) );
//공백, 엔터, 탭을 공백으로 대체함.
 }

 $found = false;
 while ( preg_match('/%[a-f0-9]{2}/i', $filtered, $match) ) {
  $filtered = str_replace($match[0], '', $filtered);
  $found = true;
 }

 if ( $found ) {
  $filtered = trim( preg_replace('/ +/', ' ', $filtered) );
//+문자를 공백으로 대체함.
 }

 return apply_filters( 'sanitize_text_field', $filtered, $str );
}
표 3-1 sanitize_text_field 함수


   관리자 디렉터리 명 변경
시나리오를 활용한 CSRF 공격이 처리되는 URL은 “http://localhost/wordpress/wp-admin/admin.php?page=cmac_campaigns &action=edit&campaign_id=1”이다. 이때 /wordpress/wp-admin/ 경로는 워드프레스 설치 후 기본 설정 된 관리자 디렉토리 경로이다. wp-admin 디렉토리는 기본 값이므로 관리자가 아닌 공격자도 워드프레스를 잘 안다면 쉽게 관리자 페이지 경로를 알 수 있기 때문에 취약하다. 관리자 디렉토리를 공격자가 모르게 감출 수 있다면 CSRF 취약점이 발생될 가능성이 낮아진다. 직접적으로 wp-admin디렉토리 명을 수정하고 워드프레스 내부 코드를 수정하여 관리자 디렉토리를 숨길 수도 있지만, 워드프레스의 장점인 플러그인을 사용하여 관리자 디렉토리를 숨길 수 있다.
플러그인
다운로드 경로
Stealth Login Plugin
Protect Your Admin
Hide My WP
표 4‑1 다운로드 경로

[표 4‑1]은 관리자 로그인 페이지 및 관리자 페이지 경로를 /wp-admin/에서 다른 명칭으로 바꿀 수 있는 대표적인 플러그인이다. 각 플러그인 마다 특징을 깊게 다루진 않겠지만 어렵지 않게 사용 가능하다. 워드프레스의 플러그인을 이용하여 보안하는 방법은 보안에 대해 무지한 관리자 입장에서는 손쉬운 효과적인 방법이라 판단된다.