R&D
버그헌팅: 취약점 체이닝의 중요성
Donggyu Kim
Jul 31, 2023

버그헌팅: 취약점 체이닝의 중요성

No impact, No bug

CVSS3.1

버그헌팅에서는 이 취약점으로 어떤 악의적인 행위를 할 수 있는지 증명해야 합니다. 대부분의 기업이나 기관들이 CVSS 점수를 이용해 취약점에 대한 평가를 하기 때문에 취약점을 악용할 수 있는 창의적인 아이디어를 생각해내어 시나리오를 만들어야 합니다.

아무런 생각 없이 단일 취약점을 제보하면, 자칫 많은 시간을 들여 찾은 취약점이 낮은 평가를 받을 수도 있습니다. 그렇다 보니 자연스럽게 버그헌터들은 추후 발견될 취약점과의 연계를 위해 낮은 영향도(impact)의 취약점을 저장해둡니다. 그리고 더 높은 영향력을 가진 시나리오를 만들 수 있게 되면 제보합니다.

이런 방식을 다른 취약점과의 연계, 즉 취약점 체이닝이라고 말합니다.

Vulnerability Chaining

취약점 체이닝이란, 두 개 이상의 취약점을 연결하여 상대적으로 중요하지 않은 보안 이슈들을 결합해 강력한 공격 시나리오를 구축하는 방법

취약점 체이닝은 분석 대상에 대한 깊은 이해를 필요로 합니다. 아무래도 한 가지 취약점이 아닌 여러 취약점을 찾아야 함도 있고, 특히 취약점은 아니지만 서비스 상의 정상적인 기능을 이용해서도 취약점과의 연계가 이루어질 수 있기 때문입니다. 때문에 단순히 취약점 뿐만 아니라 기능에 대한 세부적인 분석도 필요하게 됩니다.

취약점 체이닝 방법론 요약

  1. 시스템 이해 : 시스템이 어떻게 작동하는지 전반적으로 이해하는 것이 중요
  2. 세부적인 분석 : 개별 취약점을 식별하고 이해하는 것이 중요
  3. 시나리오 구축 : 실제 공격 시나리오를 구축하고 시뮬레이션 필요

취약점 체이닝에 대한 좋은 예시로 국내 CMS를 대상으로 한 취약점 하나를 예로 들어보겠습니다. 해당 취약점은 이윰빌더에서 경로 조작을 통한 LFI(Local File Inclusion)가 가능해 발생하는 RCE(Remote Code Execution) 취약점입니다.

자세한 정보는 KISA의 보안 취약점 정보 포털의 국내 취약점 정보 페이지에 방문하시면 확인하실 수 있습니다. (https://knvd.krcert.or.kr/detailDos.do?IDX=5789)

CVE-2022-41158 | 이윰빌더 원격 코드 실행 취약점

취약점 종류 영향 심각도 CVSS 점수 제품 영향 받는 버전
LFI (Local File Inclusion) 원격 코드 실행 High 7.2 이윰빌더 4.5.3 및 이전 버전

취약점 설명 : 이윰빌더가 쿠키 값을 파일의 경로에 사용하는 것을 이용하여 원격 코드 실행이 가능한 취약점

이윰빌더 개요

이윰빌더는 그누보드5(영카트5)를 프레임워크로 사용한 회원가입, 게시판, 쇼핑몰 결제 등의 기능을 제공하는 국내 CMS 중 하나입니다.

[https://eyoom.net/page/eb4_introduction](https://eyoom.net/page/eb4_introduction)

LFI Patch Analysis

  • 이윰빌더 변경 파일 소스 보기(Github)

[https://github.com/eyoom/eyoom_builder_4/commit/4fa8436cf2691dae5064d7c187ffa90327d41042](https://github.com/eyoom/eyoom_builder_4/commit/4fa8436cf2691dae5064d7c187ffa90327d41042) https://github.com/eyoom/eyoom_builder_4/commit/4fa8436cf2691dae5064d7c187ffa90327d41042

4.5.4 버전 이상에서는 위와 같이 $unique_theme_id 변수 값에 숫자만 입력 값으로 들어갈 수 있도록 정규식 필터링이 걸렸습니다.

다시 말해 4.5.3 이하 버전에서는 $unique_theme_id 변수의 값에 ../ 와 같은 파일의 경로를 조작할 수 있는 문자열이 들어갈 수 있다는 말과도 같습니다. 어떻게 그것이 가능한지 확인해보겠습니다.

아래 코드는 git commit에서 보았던 eyoom/class/theme.class.php 파일 내용 중 취약한 코드입니다.

/**
 * 유니크 아이디 쿠키 생성
 */
if (get_cookie('unique_theme_id')) {
    $unique_theme_id = get_cookie('unique_theme_id');
} else {
    $unique_theme_id = date('YmdHis', time()) . str_pad((int)(microtime()*100), 2, "0", STR_PAD_LEFT);
    set_cookie('unique_theme_id',$unique_theme_id,3600);
}

$file = $this->tmp_path . '/' . $_SERVER['REMOTE_ADDR'] . '.' . $unique_theme_id . '.php';
if (file_exists($file)) {
    include_once($file);
    if ($is_shop_theme) {
        $arr['theme']       = $user_config['theme'];
    } else {
        $arr['shop_theme']  = $user_config['shop_theme'];
    }
}
$_config = $arr;

/**
 * 파일 생성 및 갱신
 */
parent::save_file('user_config', $file, $_config);

쿠키 값으로부터 $unique_theme_id 값을 받아오고, 다시 그 값은 $file 변수에 $unique_theme_id.php 라는 확장자가 더해져 특정 파일의 경로가 삽입됩니다.

그리고 바로 아래 줄에서 만약 해당 경로에 파일이 존재한다면, include_once($file) 에서 파일이 include 됩니다.

만약 공격자가 특정 경로에 php 파일을 생성하거나 이미 존재하는 PHP 파일에 임의 명령어를 삽입할 수 있다면, 이 LFI 취약점을 이용해 그 PHP 파일을 불러와 임의 명령어를 실행할 수 있는 RCE 취약점으로 연계할 수 있을 것입니다.

하지만 실제로는 file_exists 함수에서는 존재하지 않은 디렉터리의 상위경로에 접근할 수 없습니다. ex) not_exists_directory/../../index.php (X)

만약 이윰빌더에 대한 이해가 부족하다면, 위 취약점에서 어떻게 LFI로 연계하고 RCE 취약점까지 트러기할 수 있는지 발견해내기 쉽지 않을 것입니다.

$file 변수의 값이 결국 하단의 save_file 함수에 들어가게 되는데, save_file 함수의 내용을 보면 PHP 파일의 내용으로 들어가게 되는 값들은 사용자가 직접 조작할 수 없어 보입니다.

다만, $unique_theme_id 값에 ../와 같은 문자열이 들어왔을 때 fopen 함수에서 상위 경로에 파일이 쓰여질 수 있다는 사실을 알 수 있습니다.

/**
 * 배열을 지정한 파일로 저장 - 폴더에 웹서버의 쓰기 권한이 있어야 함
 */
public function save_file($outvar, $filename, $info=array(), $int=false) {
    $fp = @fopen($filename, 'w');
    $contents  = "<?php\n";
    $contents .= "if (!defined('_EYOOM_')) exit;\n";
    $contents .= "\$" . $outvar . " = array(\n";
    if ($info != NULL) {
        foreach ($info as $key => $value) {
            if (!is_array($value)) {
                // 키값으로 정수를 허용하지 않는다면
                if (!$int) {
                    if (!is_int($key)) {
                        $contents .= "\t\"" . $key . "\" => \"" . addslashes($value) . "\",\n";
                    }
                } else $contents .= "\t\"" . $key . "\" => \"" . addslashes($value) . "\",\n";
            } else {
                $arr = '';
                foreach ($value as $k => $v) {
                    if (!$int) {
                        if (!is_int($key)) {
                            $arr .= "\"" . $k . "\" => \"" . addslashes($v) . "\",";
                        }
                    } else $arr .= "\"" . $k . "\" => \"" . addslashes($v) . "\",";
                }
                if ($arr) {
                    $arr = substr($arr,0,-1);
                    $contents .= "\t\"" . $key . "\" => array(" . $arr . "),\n";
                }
            }
        }
    }

    $contents .= ");\n";
    @fwrite($fp, $contents);
    @fclose($fp);
    @chmod($filename, 0644);
}

또 코드를 보시면 $value 값은 모두 addslashes() 함수로 필터링 되고 있습니다. 그리고 $key 값은 save_file() 함수 호출 전의 값을 확인해보시면 알 수 있는 것으로, 이윰빌더의 설정 값($arr['theme'])에 해당합니다. 이 값은 사용자 측에서 조작할 수 없는 값입니다.

때문에 위에서 본 코드 만으로는 임의 파일 작성 취약점을 LFI와 RCE 취약점으로 연계할 수 없다는 결론이 나옵니다.

만약 여러분이더라도 여기서 포기하기에는 너무 아쉽겠죠. 사실상 위 취약점만으로는 악성 시나리오를 제작하기에는 파급력이 많이 낮습니다. 어떤 곳에서는 단순히 가능성만 제시하여도 Potential 취약점으로 취급해주기도 합니다. 하지만 실질적인 시나리오를 제시하는 것이 제보자 뿐만 아니라 제보 받는 사람 입장에서도 취약점에 대한 심각도를 확실히 이해하고 문제의 시급함을 느낄 수 있습니다.

Finding Arbitrary Code Injection

저희는 save_file() 함수에서 php 파일을 작성하고 있음을 알 수 있었습니다. 위에서 보았던 코드에서는 이윰빌더의 설정 값을 받아서 작성하고 있다고 하지만, 다른 곳에서는 사용자로부터 입력 값을 받아서 작성하는 곳이 있지 않을까요?

그 질문에 대한 답을 찾는 쉬운 방법이 있습니다. 바로 Visual Studio Code에서 Go to References 기능을 이용할 수 있습니다.

VSCode

보시다시피 우측의 수많은 파일에서 save_file() 함수를 사용하고 있습니다. 이 파일들 중에서 하나쯤은 사용자로부터 받은 입력(user input, cookies, 등)으로부터 값을 받아 파일을 작성하고 있지 않을까요? 어떤 파일에서 그렇게 처리하고 있는지는 스포(?)하지 않겠습니다. (개인 사유로 취약점에 대한 자세한 설명은 내년에 자세히 하게 될 것 같습니다.)

위에서 살펴본 save_file() 함수를 다시 면밀히 살펴보면, $value 값은 addslashes() 함수로 필터링하고 있지만, $key 값은 필터링하고 있지 않습니다. 이 점 또한 중요한 포인트입니다. 만약 사용자가 $key 값을 조작할 수 있다면, 원하는 내용의 스크립트를 php 파일에 삽입할 수 있게 됩니다.

Conclusion

지금까지 버그헌팅에서의 취약점 체이닝의 중요성에 대해 설명해보았습니다. 그리고 그 예시로 국내 오픈소스 CMS 중 하나인 이윰빌더에 대해서도 말씀드려보았습니다. 비록 취약점에 대한 자세한 설명은 드리지 않았지만, 그 이후의 과정보다는 취약점을 체이닝해나가는 그 과정 자체가 더욱 중요한 것 같습니다.

버그헌팅 뿐만 아니라 모의해킹 업무를 하면서도 많이 느끼곤 합니다. 고객사에 단일 취약점을 설명하는 것보다는 하나 또는 두 개 이상의 시나리오와 연계하여 취약점에 대한 파급력을 설명하는 경우가 당사자 간의 취약점의 위험성과 조치에 대한 시급함을 명확하게 설명할 수 있는 것 같습니다.

다만 취약점 체이닝은 보통 시스템에 대한 깊은 이해가 필요하므로 많은 시간을 투자해야 하곤 합니다. 이 때문에 제한된 시간 내에 취약점을 찾아야 하는 모의해킹보다는 비교적 시간 제한이 없는 편인 버그헌팅에 많이 활용되는 것 같기도 합니다.

저는 최근 파스텔플래닛(pastelplanet)에서 만든 zerowhale이라는 플랫폼에서 버그헌팅을 하곤 합니다. 이 플랫폼은 비교적 외부에 잘 알려져 있지는 않지만, 버그헌터들 사이에서는 프라이빗 버그바운티를 위주로 진행되곤 합니다. 프라이빗은 말그대로 신뢰 가능한 몇몇 버그헌터들만 초대하여 비밀 유지 서약을 맺고 특정 제품 또는 웹사이트 대상으로 버그헌팅을 수행하는 프로그램입니다.

다른 플랫폼들도 많지만 개인적으로 저는 버그헌팅 기간이 길고 충분한 분석을 요구하고 무엇보다 보상이 좋은 곳을 선택하게 되는 것 같습니다.

이상으로 글을 마무리하겠습니다!

Specially thanks to 이진성 님.

Donggyu Kim
RECENT POST
Minjoong Kim
Android 1day Exploit Analysis (CVE-2019-2215)
Android 1day Exploit Analysis by Newbie
이주협, 이주영
뉴비들의 하드웨어 해킹 입문기
뉴비들의 하드웨어 해킹 입문기