김승현

그누보드 취약점 분석 XSS (기본 글쓰기 내용, KVE-2019-1235) 본문

그누보드 : 1-day 분석

그누보드 취약점 분석 XSS (기본 글쓰기 내용, KVE-2019-1235)

kshind 2023. 11. 6. 10:56

다른 분들이 찾은 내용을 거의 따라한 겁니다.

기타 정보

더보기

실습 환경 : 5.3.2.0

취약점 정보 : Cross Site Scripting(XSS)

간단 정리 : /adm/qa_config.php경로 내 기본 글쓰기 내용에 악의적인 스크립트 삽입

취약점 재연에 필요한 경로 adm/qa_config.php, bbs/qawrite.php로 총 두 개이다.

 

일단 분석에 들어가기 전 취약점 재연부터 해보자.

취약점 재연

일단 adm의 qa_config.php부터 확인해보자.

이런  식으로  1:1문의에  대한  설정을  할  수  있다.

내리다보면  이렇게  글쓰기  기본  내용이라는  칸을  볼  수  있다.  여기엔  사이트  이용자가  1:1문의글을  작성할  때  기본적으로  작성되는 내용을  입력하는  칸으로  만약  여기  12345를  작성하면,

이렇게  작성을  할  때마다  12345라는  내용이  기본적으로 입력되어지는 곳이다.

여기  xss를  삽입해보자.

이런 식으로 img를 업로드하고 만약 에러가 발생하면 1이라는 경고창을 띄우도록 작성했다.

이런 식으로 /bbs/qawrite.php에 접속하면 /adm/qa_config.php에 입력했던 스크립트가 자동으로 실행되면서 경고창이 뜨는  볼 수 있다.

 

코드분석

코드분석은  패치된  내용에 대해서만 간단하게 살펴보자

아래는 /adm/qa_config.php의 코드이다.

        <tr>
            <th scope="row"><label for="qa_insert_content">글쓰기 기본 내용</label></th>
            <td>
                <textarea id="qa_insert_content" name="qa_insert_content" rows="5"><?php echo $qaconfig['qa_insert_content'] ?></textarea> # 제거된 부분
                <textarea id="qa_insert_content" name="qa_insert_content" rows="5"><?php echo html_purifier($qaconfig['qa_insert_content']); ?></textarea> # 새롭게 추가된 부분
            </td>
        </tr>
        <?php for ($i=1; $i<=5; $i++) { ?>
        <tr>
            <th scope="row">여분필드<?php echo $i ?></th>
            <td class="td_extra">
                <label for="qa_<?php echo $i ?>_subj">여분필드 <?php echo $i ?> 제목</label>
                <input type="text" name="qa_<?php echo $i ?>_subj" id="qa_<?php echo $i ?>_subj" value="<?php echo get_text($qaconfig['qa_'.$i.'_subj']) ?>" class="frm_input">
                <label for="qa_<?php echo $i ?>">여분필드 <?php echo $i ?> 값</label>
                <input type="text" name="qa_<?php echo $i ?>" value="<?php echo get_text($qaconfig['qa_'.$i]) ?>" id="qa_<?php echo $i ?>" class="frm_input">
            </td>
        </tr>

코드에 대해 간단하게 설명하자면,

td 태그 내 textarea 중 첫 textarea는 기존의 입력값을 받는 코드, 두 번째는 새롭게 추가된 입력값을 받는 코드이다.

위의 이미지는 두 개의 차이점에 대해 보여주는 사진이며 기존과 다르게 새로운 코드는 html_purifier라는 함수를 통해 입력값을 echo함수를 통해 출력하는 걸 알 수 있다. html_purifier이라는 함수에 대해 확인해보자.

function html_purifier($html)
{
    $f = file(G5_PLUGIN_PATH.'/htmlpurifier/safeiframe.txt');
    $domains = array();
    foreach($f as $domain){
        // 첫행이 # 이면 주석 처리
        if (!preg_match("/^#/", $domain)) {
            $domain = trim($domain);
            if ($domain)
                array_push($domains, $domain);
        }
    }
    // 내 도메인도 추가
    array_push($domains, $_SERVER['HTTP_HOST'].'/');
    $safeiframe = implode('|', $domains);

    include_once(G5_PLUGIN_PATH.'/htmlpurifier/HTMLPurifier.standalone.php');
    include_once(G5_PLUGIN_PATH.'/htmlpurifier/extend.video.php');
    $config = HTMLPurifier_Config::createDefault();
    // data/cache 디렉토리에 CSS, HTML, URI 디렉토리 등을 만든다.
    $config->set('Cache.SerializerPath', G5_DATA_PATH.'/cache');
    $config->set('HTML.SafeEmbed', false);
    $config->set('HTML.SafeObject', false);
    $config->set('Output.FlashCompat', false);
    $config->set('HTML.SafeIframe', true);
    if( (function_exists('check_html_link_nofollow') && check_html_link_nofollow('html_purifier')) ){
        $config->set('HTML.Nofollow', true);    // rel=nofollow 으로 스팸유입을 줄임
    }
    $config->set('URI.SafeIframeRegexp','%^(https?:)?//('.$safeiframe.')%');
    $config->set('Attr.AllowedFrameTargets', array('_blank'));
    //유튜브, 비메오 전체화면 가능하게 하기
    $config->set('Filter.Custom', array(new HTMLPurifier_Filter_Iframevideo()));
    $purifier = new HTMLPurifier($config);
    return $purifier->purify($html);
}

간단하게 설명하면,

$f에서 /htmlpurifier/safeiframe.txt라는 파일을 열어 안전한 프레임의 목록을 확인한다. 그리고 그 외 다른 것들을 통해 html을 더 안전하도록 만들어주는 그런 함수인데 업데이트를 하면서 우리가 입력한 값을 확인하고 제거하는 함수를 추가함으로써 xss 공격을 차단한  것  같다.  원래  다른  곳들엔  이미  적용이  되어있었으나  adm경로기  때문에  제대로  적용시키지  않은  것  같다.

bbs/qawrite.php코드를  확인해보자.

    if($w == 'r')
        $write['qa_subject'] = '';

    $content = '';
    if ($w == '') {
        $content = $qaconfig['qa_insert_content'];#기존의코드
        $content = html_purifier($qaconfig['qa_insert_content']);#새롭게작성된코드
    } else if($w == 'r') {
        if($is_dhtml_editor)
            $content = '<div><br><br><br>====== 이전 답변내용 =======<br></div>';
        else
            $content = "\n\n\n\n====== 이전 답변내용 =======\n";
        $content .= get_text($write['qa_content'], 0);
    } else {
        //$content = get_text($write['qa_content'], 0);
        
        // KISA 취약점 권고사항 Stored XSS
        $content = get_text(html_purifier($write['qa_content']), 0);
    }

여기도  동일하게  html_purifier라는 함수가 적용된 것을 있다.

 

입력한 값과 처리  과정에  대해서  간단하게  보자.

$content = $qaconfig['qa_insert_content'];는 qawrite.php의 내용이다.

 

<textarea id="qa_insert_content" name="qa_insert_content" rows="5"><?php echo $qaconfig['qa_insert_content'] ?></textarea>

위는 qa_config.php의 내용이다.  

우리가  qa_config.php에  입력한  내용은  qaconfig변수의 qa_insert_content에  저장이  되고

qawrite.php에  해당  변수가  전달이 된다.

이후 글의 내용이 저장되는 content변수에  저장된 내용이 전달이 된다.

전달이 되어 별다른 검증 없이 해당 페이지에 출력이 되어 xss가 실행된다.

그냥 문의를 작성하는 부분에는 이미  동일한  xss에 대한 조치가 취해진 것을 볼 수 있다.