/ POSTS

web-0x02

LFI to RCE on PHPMyAdmin 4.8.0 ~ 4.8.1

안녕하세요 hackyu입니다.

오늘은 CVE-2018-12613 PHPMyAdmin LFI to RCE 취약점에 대한 포스팅입니다.

해당 취약점은 PMA(PHP My Admin) 버전 4.8.0 ~ 4.8.1에서 발생하는 취약점이며, index.php 요청 시 사용되는 target 파라미터의 값이 조건을 만족하게되는 경우 include()를 통해 발생되는 LFI 취약점을 이용한 RCE 공격입니다.

LFI / RCE?

LFI와 RCE 취약점에 대해 간략하게 설명드리겠습니다.

LFI(Local File Inclusion) 취약점이란 클라이언트에 의한 서버로의 요청 시 반환하는 응답의 결과 내에 서버 내 존재하는 파일이 포함되어 노출되는 취약점을 말합니다.

RCE(Remote Command Execution) 취약점이란 말그대로 원격 명령 실행으로 공격자가 삽입하는 또는 전달하는 값으로 서버 측의 환경에서 시스템 명령어를 실행할 수 있는 취약점을 말합니다.

해당 취약점을 이해하기 위해서 세션에 대해서 간략하게 말씀드리자면, 서버가 클라이언트를 식별하는데 사용하는 일종의 별명이라고 생각하시면 될 것 같습니다. 또한, 일반적으로 세션은 서버 측에서 파일로 관리하게 됩니다.

세션이 파일로 관리되는 부분에 대해서는 본 포스팅의 뒷부분 취약점 분석 부분에서 간략히 확인하실 수 있으실 것 같습니다. 더불어 해당 취약점은 관리자로의 접근 이후 단계에서의 진행되는 것으로 생각하시면 될 것 같습니다.

취약점 분석
  • 환경구성
    • Apache 2.4.18
    • PHP 7.0.30
    • PHPMyadmin 4.8.1
- LFI to RCE Vulnerability Flow -


다음은 취약점이 발생한 PHPMyAdmin 4.8.1 소스코드 부분입니다.

// phpmyadmin 4.8.1 index.php line 53 ~ 62

// If we have a valid target, let's load that script instead 
if (! empty($_REQUEST['target'])                            // Request 시 target 파라미터 값 empty 야부
    && is_string($_REQUEST['target'])                       // Request 요청 파라미터 target 값의 타입 문자열 여부
    && ! preg_match('/^index/', $_REQUEST['target'])        //Request 요청 파라미터 target값에 'index' 문자열 매칭 여부
    && ! in_array($_REQUEST['target'], $target_blacklist)   // equest 요청 파라미터 target의 값이 blacklist에 속해 있는지 여부
    && Core::checkPageValidity($_REQUEST['target'])         // Core 클래스의 checkPageValidity 함수의 인자로 호출 결과 여부
) {
    include $_REQUEST['target'];                            // 위의 조건이 만족하는 경우 include
    exit;
}
- PHPMyAdmin 4.8.1 index.php line 53 to 62 -


// whitelist of /phpmyadmin/libraries/Core.php

public static $goto_whitelist = array(
    'db_datadict.php',
    'db_sql.php',
    'db_events.php',
    'db_export.php',        
    'db_importdocsql.php',
    'db_multi_table_query.php',
    'db_structure.php',
    'db_import.php',
    'db_operations.php',
    'db_search.php',
    'db_routines.php',
    'export.php',
    'import.php',
    'index.php',
    'pdf_pages.php',
    'pdf_schema.php',
    'server_binlog.php',
    'server_collations.php',
    'server_databases.php',
    'server_engines.php',
    'server_export.php',
    'server_import.php',
    'server_privileges.php',
    'server_sql.php',
    'server_status.php',
    'server_status_advisor.php',
    'server_status_monitor.php',
    'server_status_queries.php',
    'server_status_variables.php',
    'server_variables.php',
    'sql.php',
    'tbl_addfield.php',
    'tbl_change.php',
    'tbl_create.php',
    'tbl_import.php',
    'tbl_indexes.php',
    'tbl_sql.php',
    'tbl_export.php',
    'tbl_operations.php',
    'tbl_structure.php',
    'tbl_relation.php',
    'tbl_replace.php',
    'tbl_row_action.php',
    'tbl_select.php',
    'tbl_zoom_select.php',
    'transformation_overview.php',
    'transformation_wrapper.php',
    'user_password.php',
);
- PHPMyAdmin 4.8.1 whitelist of phpmyadmin/libraries/Core.php -


// checkPageValidity of /phpmyadmin/libraries/Core.php

public static function checkPageValidity(&$page, array $whitelist = [])
{
   if (empty($whitelist)) {
       $whitelist = self::$goto_whitelist;
   }
   if (! isset($page) || !is_string($page)) {
       return false;
   }

   if (in_array($page, $whitelist)) {
       return true;
   }

   $_page = mb_substr(
       $page,
       0,
       mb_strpos($page . '?', '?')
   );
   if (in_array($_page, $whitelist)) {
       return true;
   }

   $_page = urldecode($page);   // url decoding 
   $_page = mb_substr(
       $_page,
       0,
       mb_strpos($_page . '?', '?')
   );
   if (in_array($_page, $whitelist)) {
       return true;
   }

   return false;
}
- PHPMyAdmin 4.8.1 checkPageValidity of phpmyadmin/libraries/Core.php -


위 코드들이 해당 취약점에 발생되는 부분이며 간략하게 바로 드러나는 부분에 대해서는 주석처리로 설명하였습니다.

사용자가 입력한 값에 대한 검증에 미흡한 검증 절차가 있으며, checkPageValidity()에서 whitelist에 존재하는 값을 이용하여 db_sql.php?와 같이 bypass하기에 include() 함수를 이용하여 LFI가 가능한 것을 확인할 수 있습니다.

취약점 공격 테스트

먼저 다음과 같이 현재 php 관련 정보를 확인할 수 있는 것을 알 수 있습니다. 또한, 이번 취약점을 분석하는데 있어 중요하게 봐야할 부분 중 하나인 기본 세션 저장 경로(session_save_path)를 확인할 수 있습니다.

현재 테스트 경로는 기본적으로 /var/lib/php/sessions라는 경로로 생성된 세션 파일이 저장되는 것으로 볼 수 있습니다.

- phpinfo() 결과 -


phpmyadmin으로 로그인 한 이후, SQL문을 실행할 수 있는 부분에서 다음과 같이 입력 한 후 실행하겠습니다.

- SQL문 실행 시도 -


정상적으로 SQL문이 실행이 되고, 로그인 또는 페이지 접근 시 생성된 쿠키 정보 중 phpMyAdmin 세션 값을 확인할 수 있습니다.

- SQL문 실행 결과 확인 및 쿠키, 세션정보 확인 -


위에서 봤던 것과 같이 해당 경로에 이전단계에서 확인한 세션값 앞에 ‘sess_‘이라는 문자열이 연결된 세션 파일이 생성되고, 존재하는 것을 확인할 수 있습니다.

- 세션 파일 확인 -


해당 세션 파일의 내용을 확인해보면 앞서 실행하였던 SQL 쿼리문이 세션 파일 내용으로 포함된 것을 확인할 수 있습니다.

- 세션 파일 내용 확인 -


다음 그림과 같이 화이트리스트에 존재하는 목록(db_sql.php) 이용한 index.php의 파라미터 target으로 사용하며, 추가적으로 파일다운로드 취약점을 이용한 공격 시 많이 알려지고 사용되는 상대경로 ex) ../../../ 를 이용하여 세션저장경로의 세션파일 연결하여 파라미터 값으로 요청한 결과입니다.

본 그림에서 보이는 것과 같이 앞서 실행한 SQL문이 포함되어 phpinfo()가 정상적으로 동작된 결과가 LFI를 통해 확인할 수 있습니다.

- LFI 시도 및 결과 -


지금까지 입력한 SQL문이 세션 파일 내 포함이 되며 정상적으로 실행된 결과를 LFI를 통해 확인할 수 있는 부분까지 진행하였습니다.

본격적인 RCE를 위해 다음과 같이 SQL문을 실행하여 세션파일 내 포함하도록 합니다. 본 SQL문에 포함되는 php구문에 시스템 명렁어를 실행하는 함수 중 system() 함수를 이용하였습니다.

- 세션 파일 내용 확인 -


다음 그림으로 정상적으로 RCE가 가능한 것을 확인할 수 있습니다. 앞에서 수행했던 방식과 조금의 차이점은 파라미터를 포함하여 요청 하였습니다. cmd라는 파라미터로 입력한 시스템 명령어가 정상적으로 실행되는 것을 확인할 수 있습니다.

- RCE 시도 및 결과 (id) -


- RCE 시도 및 결과 (cat /etc/passwd) -


취약점 분석한 결과를 요약하면 다음과 같습니다.

사용자가 실행한 SQL문이 세션 파일 내 포함됨
Whitelist 값을 이용한 index.php의 target 파라미터로 요청 시 LFI 취약점 확인
LFI를 이용한 원활한 RCE 사용 가능

  • 패치 및 대응방안
    https://www.phpmyadmin.net/files/4.8.2/ 를 통해 본 취약점과 타 취약점들에 대해서 패치가 된 버전인 4.8.2로 업데이트 된 것을 확인할 수 있습니다.

    ./phpmyadmin/libraries/classes/Core.php의 checkPageValidity()와 index.php의 checkPageValidity 호출 시 사용되는 파라미터 부분이 변경된 것을 확인하였습니다.

  • References
    https://blog.vulnspy.com/2018/06/21/phpMyAdmin-4-8-x-Authorited-CLI-to-RCE/
    https://www.vulnspy.com/phpmyadmin-4.8.1/
    https://medium.com/@happyholic1203/phpmyadmin-4-8-0-4-8-1-remote-code-execution-257bcc146f8e
    https://www.youtube.com/watch?v=bT-00ZUTq0o