WriteUp

2016 Whitehat Contest Final writeup

침해대응은 2문제가 출제되었고 문제당 서버 1대씩을 각 팀에게 지급했다.

자신의 서버의 취약점을 찾아내 Flag를 인증하면 ssh계정을 주어 패치가 가능하고,
다른팀의 패치가 안된 서버를 공격해 추가득점이 가능하다.

—-
경상북도 문제는 회원가입, 로그인, 사진올리기 기능이 있다.

$uploadfile = $uploaddir . basename($_FILES[‘ufl’][‘name’]);
if (move_uploaded_file($_FILES[‘ufl’][‘tmp_name’], $uploadfile)){

사진을 올릴 때 별도의 필터링이 없으니 webshell upload가 가능하다.
다들 간단히 취약점을 찾아내어 인증하고 패치했다.

그런데 사실 나는 대회장에 30분 지각했고 취약점을 찾아낸 시점에서 1개팀을 제외하고 모든 서버가 패치되어 있었다.
다른 7개팀의 flag를 얻고싶다.
웹사이트가 전반적으로 아주 취약했기에 다른 벡터를 통해 Flag를 얻을 수 있을거라 생각해서 계속 소스코드를 읽어보았다.

if (preg_match(“/[;`|&]/”, $filename)) {
die(“파일명에 유효하지 않은 문자가 포함되어 있습니다.”);
}
system(“rm -rf “.$row[‘filename’]);

사진을 삭제할 때 SQL Injection을 통해 원하는 filename을 리턴시키고 linefeed를 통해 필터를 우회해서 OS Command Injection이 가능하다.

Payload는 del.php?idx=1 union select 1,2,’%0als -al’,4,5– – 이런 모양이 되었다.

다들 1번째 취약점을 찾아낸 시점에서 패치를 끝냈기에 이 추가적인 취약점을 통해서 모든 팀의 Flag를 얻을 수 있었다.

system() 함수를 unlink() 함수로 대체해서 OS Command Injection을 막는 방법으로 패치했다.

—-
전라북도 문제는 평범한 개인 블로그인데 LFI 취약점이 존재한다.

php wrapper를 사용해서 소스코드 leak이 가능하다.
?page=php://filter/convert.base64-encode/resource=index

아래는 핵심이 되는 코드이다.

Remote Code Excution을 해야하는데 include할 때 “.php”가 거슬린다.
zip:// wrapper를 사용해서 우회가 가능하다.

?page=zip:///foo%23bar
이렇게 공격하면 /foo 파일을 압축해제해서 bar.php를 include한다.
이제 원하는 장소에 파일을 올리는게 문제다.

후술될 공격의 원만한 이해를 위해 php의 파일 업로드 방식을 설명하겠다.
서버에서 파일 업로드가 활성화되어있을경우 리퀘스트 파일이 존재하면 php 코드를 실행하기 전에
/tmp/php[0-9a-zA-Z]{6} 라는곳에 임시로 파일을 업로드해주고, php코드의 실행이 끝나면 다시 삭제한다.
그리고 php 코드에서는 $_FILES[‘name’][‘tmp_name’] 라는 변수에 해당 임시 파일의 이름이 담긴다.

그러므로 debug_mode 를 통해서 우리가 Upload한 파일의 tmp_name을 알 수 있고, 해당 파일을 include 할 수 있다.

여기서 문제가 생긴다.
1. Request에 zip 파일을 업로드함과 동시에 해당 파일의 경로가 담긴 페이로드를 보낸다.
2. 서버에서 zip파일을 /tmp/php[0-9a-zA-Z]{6} 에 업로드한다.
3. php 코드가 실행되면서 zip wrapper를 통해 임시파일을 unzip한다.

이 순서로 공격되는데, 파일을 업로드할 때 이 파일이 어느 이름을 가지게 될지 미리 알 수 없다.

이 시점에서 debug_mode 관련 분기문을 다시 한 번 살펴보자.

ob_start();
var_dump(get_defined_vars());
ob_flush();
ob_end_clean();

이런 코드가 있는데 ob_ 시리즈 함수들 php로 웹 irc를 구현해볼 때 본적있다.
php 코드가 실행되는 도중에 값을 출력해주는 역할이였다. ( http://php.net/manual/kr/function.ob-flush.php )
그렇다면 index.php 파일에게 index.php 파일을 include 시켜 무한루프에 빠지게 해서,

3. php 코드가 실행되면서 zip wrapper를 통해 임시파일을 unzip한다.

이 부분에서 시간이 지연되면, php 코드의 실행이 끝난 후 임시파일을 삭제하는 행동 또한 지연되며,
ob_flush() 함수 덕분에 지연되기 이전의 출력값. 즉 /tmp/php[0-9a-zA-Z]{6} 의 이름또한 알 수 있다.
따라서 Race Condition이 가능하다.

그러므로
?page=index&debug_mode=31337 에 zip file을 Upload하면
/tmp/php[0-9a-zA-Z]{6} 의 파일명을 출력해준 후 무한루프에 빠져 delay가 되고
출력된 파일명을 바탕으로 ?page=zip:///tmp/phpabcdef%23bar&cmd=ls -al 에 접속해주면
아직 삭제되지 않은 임시파일을 unzip하고 include해서 Remote Code Excution을 할 수 있다.

include 할 때 변수 앞에 “./” 를 붙여서 wrapper 사용을 막는 방법으로 패치했다.

Leave a Reply

Your email address will not be published. Required fields are marked *