파일 업로드(File Upload) 취약점 - WordPress Estatik 플러그인

by Toff
  개요
이번 포스팅에서는 Estatik 플러그인을 사용해 파일 업로드(File Upload) 취약점에 대해서 알아보자. 파일 업로드 취약점에 대한 설명은 다음 포스팅을 참고하도록 하자.
> http://dntsecurity.blogspot.kr/2017/01/file-upload.html

 해당 플러그인에서는 업로드 함수에서 파일 확장자 체크를 하는 로직이 존재하지 않아서 취약점이 발생한다. 또한 워드프레스에서 제공하는 에이잭스의 잘못된 사용으로 발생한다. 해당 취약점에서는 파일 확장자 체크를 하지 않아도 워드프레스 에이잭스 기능을 잘 사용했다면 취약점이 발생하지 않았을 것이다. 이번 포스팅처럼 파일 업로드 우회 방안을 쓰지 않더라도 취약점이 발생할 수도 있다는 것을 보여주기 위해 해당 플러그인 취약점을 선택했다. 자세한건 아래 실습에서 살펴보도록 하자.


  환경 구성
환경 구성은 다음 그림 1-1 과 같다.
그림 1-1 환경 구성

Estatik 플러그인 설치 주소는 다음과 같다.
- 취약점 발생 버전 : https://github.com/wp-plugins/estatik/
- 취약점 패치 버전 : https://downloads.wordpress.org/plugin/estatik.zip

  실습 순서
아래의 포스팅 순서대로 실습을 진행해도 되지만 더욱 빠른 이해를 위해 아래의 영상으로 실습해도 된다. 해당 영상은 "실습 과정"만을 영상으로 만들었기에 포스팅과 같이 본다면 더욱 편하다.

먼저 그림 1-2 와 같이 Estatik 플러그인을 활성화 한다. 플러그인을 활성화만 시켜도 취약점이 발생하기에 다른 부가적인 설정은 필요 없다.

그림 1-2 Estatik 플러그인 활성화

다음으로 표와 같이 코드를 작성한 후에 upload.html 이름으로 저장한다. action 에서 워드프레스 주소만 실습 환경에 맞춰 변경하면 된다. 해당 코드는 워드프레스의 ajax(에이잭스) 기능을 사용해 파일을 업로드하는 간단한 폼이다. 해당 폼을 admin-ajax.php 에 전송한다는 것과 input 값으로 es_prop_media_images 를 사용한다는 것만 알아두도록 하자. 이에 대한 자세한 분석은 결과 분석에서 살펴보겠다.

<html>
<body>
<form action="http://192.168.100.9/wordpress/wp-admin/admin-ajax.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="action" value="es_prop_media_images" />
<input type="file" name="es_media_images[]" />
<input type="submit" name="submit" value="submit" />
</form>
</body>
</html>
표 1-1 업로드 폼 작성

upload.html 파일을 실행시켜 그림 1-3 와 같이 php 확장자로 된 웹쉘 파일을 선택하고 submit 버튼을 클릭한다.

그림 1-3 웹쉘 업로드

간헐적으로 그림 1-4 와 같이 오류가 발생할 수도 있지만 정상적으로 업로드는 되기에 크게 신경쓰지 말고 넘어가도록 한다.

그림 1-4 업로드 성공

워드프레스에서 에이잭스를 이용해 파일 업로드 할 경우에는 default 업로드 디렉터리는 /wp-content/uploads 이다. 해당 디렉터리에 접근해 업로드 한 날짜 디렉터리(2016/11)에 접근하면 그림 1-5 와 같이 업로드 된 파일 목록이 출력된다. 이전에 업로드 했던 b374k.php 웹쉘 파일이 정상적으로 업로드 됬음을 알 수 있다.
그림 1-5 업로드 된 웹쉘 확인


해당 웹쉘을 클릭해 실행하고 b374k 를 입력하면 그림 1-6 과 같이 웹쉘이 실행된다. 공격자는 웹쉘을 사용해 피해자 PC에 다양한 공격을 할 수 있다.

그림 1-6 웹쉘 실행


  결과 분석
워드프레스는 자체적으로 에이잭스 기능을 사용하고 있다. 위의 실습에서 업로드 폼으로 워드프레스 서버에 파일을 업로드 할 때 에이잭스 기능이 사용 된다. 클라이언트(공격자PC)에서 서버(워드프레스)로 파일을 업로드 할 때 서버의 에이잭스 파일(/wp-admin/admin-ajax.php)에서 요청을 받아 처리하는 구조이며 여기서 파일 업로드 취약점이 발생했다. 해당 파일 업로드 기능이 에이잭스로 구현되는 로직을 도식화 하면 다음 그림 1-7 과 같다.
그림 1-7 파일 업로드 진행 로직

과정마다 설명을 붙이자면, 먼저 1번 과정은 입력 폼(upload.html)에서 에이잭스로 데이터를 전송하고 Estatik 플러그인의 action 함수인 es_prop_media_images 를 사용한다. 해당 전송 값은 위의 표 1-1을 다시 살펴보면 알 수 있다.

es_prop_media_images action 함수는 표 1-2 와 같으며 파일을 업로드 하는 함수이다. 즉 워드프레스의 에이잭스를 사용하는데 파일 업로드 함수는 Estatik 에 정의되어 있는 표 1-2 코드를 사용하는 것이다. 파일 업로드 취약점은 해당 함수에서 발생하였다. 코드에서 볼 수 있듯이 업로드 시에 파일 확장자를 검사하는 로직이 존재하지 않는다. 해당 함수는 Estatik/admin_template/es_property/es_property_functions.php 에 정의되어 있다.
function es_prop_media_images(){

 $es_prop_id = sanitize_text_field($_GET['es_prop_id']);

 $uploadedfile = $_FILES['es_media_images'];

 $upload_dir = wp_upload_dir(); 

 $save_image_array = array();

 $es_settings = es_front_settings();

        .... 중략 ....

}

add_action('wp_ajax_es_prop_media_images', 'es_prop_media_images'); 

add_action('wp_ajax_nopriv_es_prop_media_images', 'es_prop_media_images'); 
표 1-2 es_prop_media_images() action 함수

2번 과정으로, 표 1-2 의 하단과 같이 에이잭스에서 action 함수를 사용하려면 add_action('wp_ajax_action 함수 이름', 'action 함수 이름') 과 같이 선언이 되어 있어야 해당 action 함수를 참조할 수 있다. 즉 플러그인의 함수를 참조하는 과정이다.

3번 과정으로, add_action('wp_ajax_~~) 는 해당 action 함수를 로그인한 사용자만 사용할 수 있고 아래의 add_action('wp_ajax_nopriv_~~) 는 로그인이 되어 있지 않은 사용자도 사용할 수 있음을 뜻한다. 즉 두가지 모두 정의되어 있기에 모든 사용자가 해당 업로드 함수를 사용할 수 있음을 말한다. 해당 코드는 wordpress/wp-admin/admin-ajax.php 에 정의되어 있다. 첫 줄에서 로그인이 되어 있다면 wp_ajax_~ 를 실행하고 else 에서 로그인 되어 있지 않다면 wp_ajax_nopriv_~ 로 실행하라고 정의되어 있다.

if ( is_user_logged_in() ) {
        /**
         * Fires authenticated Ajax actions for logged-in users.
         *
         * The dynamic portion of the hook name, `$_REQUEST['action']`,
         * refers to the name of the Ajax action callback being fired.
         *
         * @since 2.1.0
         */
        do_action( 'wp_ajax_' . $_REQUEST['action'] );
} else {
        /**
         * Fires non-authenticated Ajax actions for logged-out users.
         *
         * The dynamic portion of the hook name, `$_REQUEST['action']`,
         * refers to the name of the Ajax action callback being fired.
         *
         * @since 2.8.0
         */
        do_action( 'wp_ajax_nopriv_' . $_REQUEST['action'] );
}
표 1-3 admin-ajax.php 코드

마지막 4번 과정으로, 파일 업로드를 실행한다.

위의 업로드 폼인 표 1-1 을 다시 살펴보며 정리하자면
1. upload.html 폼을 사용해서 admin-ajax.php(에이잭스)에 데이터를 전송한다.
2. 해당 데이터를 전송할 때 Estatik 플러그인의 파일 업로드 action 함수인 es_prop_media_images를 사용 한다.
3. 정상적으로 파일 업로드가 된다.

해당 플러그인에서 파일 업로드 취약점이 발생한 이유는 크게 두가지이다. 
첫번째, es_prop_media_images 함수명만 봐도 알 수 있듯이 이미지 파일을 업로드를 위해 구현된 함수임을 알 수 있다. 하지만 확장자 명을 검사하는 로직이 존재하지 않는다.

두번째, 검증되지 않은 모든 사용자가 해당 함수를 사용할 수 있게끔 설정되어 있다. 확장자 명은 검사하는 로직이 없더라도 관리자만 업로드가 가능하게끔 권한을 설정해두었으면 문제가 되지 않았을 것이다.



  대응 방안
해당 플러그인의 취약점 대응 방안은 바로 위에서 설명한 발생 원인을 조치하면 된다. 플러그인의 제작자는 해당 취약점의 대응 방안을 세워 2.4.0 버전을 내놓았다. 취약점이 발생한 소스 코드 부분을 패치 이전 버전과 패치 이후 버전을 비교하면 더욱 명확히 알 수 있다.

각각 표 1-3 과 표 1-4 와 같다.
취약점 패치 버전에서는 기존의 표 1-3 마지막 줄을 제거하면서 로그인하지 않은 사용자는 함수를 사용하지 못하게 설정하였다.
또한 표 1-4 의 두번째 줄을 추가하면서 current_user_can('manage_options') 을 사용해 매니저 권한을 가지고 있는 사용자만 업로드를 할 수 있게끔 권한을 더욱 강화하였고 워드프레스 함수인 check_ajax_referer 함수를 추가하여 파일 확장자를 검사하는 로직을 추가해 두가지의 대응 방안을 세웠다.

function es_prop_media_images(){  // 취약점 발생 버전

 $es_prop_id = sanitize_text_field($_GET['es_prop_id']);

 $uploadedfile = $_FILES['es_media_images'];

 $upload_dir = wp_upload_dir(); 

 $save_image_array = array();

 $es_settings = es_front_settings();

        .... 중략 ....

}

add_action('wp_ajax_es_prop_media_images', 'es_prop_media_images'); 

add_action('wp_ajax_nopriv_es_prop_media_images', 'es_prop_media_images');  
표 1-3 es_prop_media_images 함수 취약점 발생 버전


function es_prop_media_images(){  // 취약점 패치 버전

    if (check_ajax_referer( 'image-validation', 'security', false ) && current_user_can('manage_options')) {
    
 $prop_id = sanitize_text_field($_GET['es_prop_id']);

 $uploadedfile = $_FILES['es_media_images'];

 $upload_dir = wp_upload_dir();

 $save_image_array = array();

 $es_settings = es_front_settings();

        .... 중략 ....

}

add_action('wp_ajax_es_prop_media_images', 'es_prop_media_images');
표 1-4 es_prop_media_images 함수 취약점 패치 버전