/*
http://beist.org
beist@hanmail.net
wowcode at wowhacker team
*/
2) Open 함수 사용시 주의할점
이번 주제는 Web 프로그래밍 중에서 가장 일어나기 쉬운 버그 중의 하나이다.
Web CGI 에서 File Open 은 많이 쓰이는 알고리즘 중에 하나이다. 하지만
이 알고리즘이 잘못 쓰여졌을 경우에 심각한 Security Hole 을 일으킬 수도
있다.
Web CGI 에서 File Open 이 쓰이는 경우를 보자. 예를 들어 Web Page 의 Menu
를 구현한다고 하자.
menu.html
<html>
<head>
<title>Beist Home 에 오신 것을 환영합니다.</title>
</head>
<body>
<center>
<a href=link.php?file=profile.html>나의소개</a><br>
<a href=link.php?file=board.html>게시판</a><br>
<a href=link.php?file=site.html>추천사이트</a><br>
</center>
</body>
</html>
간단하게 구현해 본 Menu 이다. Menu 는 나의소개, 게시판, 추천사이트로 나뉘
어져 있으며 link.php 라는 File 을 통해 접근을 한다. 각 페이지를 모두 설명할
필요는 없으니 profile.html 파일만 따로 작성하여 설명하겠다.
link.php
1 <?
2
3 $exist = file_exists("./$file");
4 if(!$exist)
5 {
6 echo "$file 파일은 없다.\n";
7 exit;
8 }
9
10 $fp=fopen("./$file", "r");
11
12 while(!feof($fp))
13 {
14 $msg .= fgets($fp,100);
15 }
16
17 $msg=nl2br($msg);
18
19 echo $msg;
20
21 ?>
profile.html
<html>
<head>
<title>yo man.. i'm beist</title>
<body>
전 남자에요. 84 년생이고요. 별명은 스누피.
</body>
</html>
link.php 의 기능은 간단하다. 먼저 fopen 을 시도하기 전에 file_exists 를
이용하여 현재 디렉토리에 그런 파일이 있는지 없는지 확인을 하고 없으면
script 실행을 중지시킨다. 존재한다면 fopen 을 이용하여 현재 디렉토리를 기준으로
read mode 로 file 핸들러를 연다. 그리고 $msg 에 file 의 내용을 담은 후에
사용자에게 뿌려준다. 여기에서는 현재 directory 를 기준으로 한다는 것을
명시하기 위하여 $file 앞에 ./ 를 붙였지만 ./ 를 붙이지 않아도 php 에서는
현재 directory 에서부터 찾으라는 것으로 인식한다.
만약 link.php?file=profile.html 이라고 한다면 file 변수의 값인 profile.html 을 열어서 사용자에게 보여주는 것이다.
[요청]
http://server/link.php?file=profile.html
[결과]
전 남자에요. 84 년생이고요. 별명은 스누피.
하지만 이런 알고리즘은 취약성을 갖는다. link.php 를 요청할때 file 의 변수를
cracker 임의대로 변경하여 요청을 한다면 link.php 에서는 cracker 가 임의로
변경한 file 의 내용을 돌려줄 것이다.
예를 들어
[요청] http://server/link.php?file=../../../../../../../../../etc/passwd
[결과]
root:*:0:0:root:/root:/usr/local/bin/bash
daemon:*:1:1:Owner of many system processes:/root:/sbin/nologin operator:*:2:5:System &:/:/sbin/nologin bin:*:3:7:Binaries Commands and Source,,,:/:/sbin/nologin tty:*:4:65533:Tty Sandbox:/:/sbin/nologin kmem:*:5:65533:KMem Sandbox:/:/sbin/nologin ................................
file 의 값을 상위디렉토리로 이동하기 위하여 ../ 를 여러개를 붙였고 etc
directory 밑에 존재한 passwd file 을 열어서 cracker 는 passwd file 을
볼 수 있었다.
해결책을 알아 보자.
개발자가 이 상황에서 신경써야 할 부분은 link.php 를 호출할때 인자로 딸려오는
변수 $file 이다. cracker 가 $file 의 내용을 변경하더라도 link.php 에서는
올바르게 처리할 수 있어야 한다.
추천하는 처리 방법은 $file 의 내용을 조사하여 .. 같은 문자가 있다면 필터링을
하는 것이다. 다음은 취약성을 개선한 소스이다.
link2.php
1 <?
2 if(eregi("\.\.", $file))
3 {
4 echo "장난하니?.. file 이름에 .. 가 들어있으면 안됩니다.";
5 exit;
6 }
7 $exist = file_exists("./$file");
8 if(!$exist)
9 {
10 echo "$file 파일은 없다.\n";
11 exit;
12 }
13
14 $fp=fopen("./$file", "r");
15
16 while(!feof($fp))
17 {
18 $msg .= fgets($fp,100);
19 }
20
21 $msg=nl2br($msg);
22
23 echo $msg;
24
25 ?>
2 번째 라인에서 eregi 를 이용하여 $file 의 내용에 .. 가 있는지 check 를
하였다. 만약 .. 라는 스트링이 $file 안에 존재한다면 cracking 시도로 간주
하고 script 실행을 중지시키고, 들어있지 않다면 정상적으로 script 를 계속
실행한다.
[요청] http://server/link.php?file=../../../../../../../../../etc/passwd
[결과]
장난하니?.. file 이름에 .. 가 들어있으면 안됩니다.
이런 식으로 해결을 할 수도 있고 eregi_replace 를 이용하여 .. 를 공백으로
만들어 무시할 수도 있을 것이다.
위의 방법에 대하여 어떤 문제점이 발생할 수도 있다. Shell 에서는 (bash
shell 기준) ../ 말고도 상위디렉토리로 접근하는 방법이 있다.
예를 들어
$ cd ./.\./
이렇게 입력하면 상위 디렉토리로 이동한다는 것을 뜻한다. 그래서 만약 해커가
http://server/link.php?file=./.\./.\./.\./.\./.\./etc/passwd
이런식으로 요청을 한다면 eregi 는 체크를 하지 못할 것이다. 하지만 이 것은 걱정
하지 않아도 된다. 왜냐하면 data 가 전송되어질때 encode-decode 과정에서 \ or
' or " 앞에는 \ 문자가 하나 더 붙기 때문이다. 그래서 실제로 저렇게 요청을
한다더라도
./.\\./.\\./.\\./.\\./.\\./etc/passwd
이란 파일을 열려고 시도하게 된다. 하지만 만약 php.ini 같은 설정 파일에서
magic_quote 값을 변경하였을 경우 escape (\) 문자가 앞에 붙지 않게 된다.
그래서 cracker 의 요청대로 passwd 파일을 open 할 것이고 출력하게 된다.
http://beist.org
beist@hanmail.net
wowcode at wowhacker team
*/
2) Open 함수 사용시 주의할점
이번 주제는 Web 프로그래밍 중에서 가장 일어나기 쉬운 버그 중의 하나이다.
Web CGI 에서 File Open 은 많이 쓰이는 알고리즘 중에 하나이다. 하지만
이 알고리즘이 잘못 쓰여졌을 경우에 심각한 Security Hole 을 일으킬 수도
있다.
Web CGI 에서 File Open 이 쓰이는 경우를 보자. 예를 들어 Web Page 의 Menu
를 구현한다고 하자.
menu.html
<html>
<head>
<title>Beist Home 에 오신 것을 환영합니다.</title>
</head>
<body>
<center>
<a href=link.php?file=profile.html>나의소개</a><br>
<a href=link.php?file=board.html>게시판</a><br>
<a href=link.php?file=site.html>추천사이트</a><br>
</center>
</body>
</html>
간단하게 구현해 본 Menu 이다. Menu 는 나의소개, 게시판, 추천사이트로 나뉘
어져 있으며 link.php 라는 File 을 통해 접근을 한다. 각 페이지를 모두 설명할
필요는 없으니 profile.html 파일만 따로 작성하여 설명하겠다.
link.php
1 <?
2
3 $exist = file_exists("./$file");
4 if(!$exist)
5 {
6 echo "$file 파일은 없다.\n";
7 exit;
8 }
9
10 $fp=fopen("./$file", "r");
11
12 while(!feof($fp))
13 {
14 $msg .= fgets($fp,100);
15 }
16
17 $msg=nl2br($msg);
18
19 echo $msg;
20
21 ?>
profile.html
<html>
<head>
<title>yo man.. i'm beist</title>
<body>
전 남자에요. 84 년생이고요. 별명은 스누피.
</body>
</html>
link.php 의 기능은 간단하다. 먼저 fopen 을 시도하기 전에 file_exists 를
이용하여 현재 디렉토리에 그런 파일이 있는지 없는지 확인을 하고 없으면
script 실행을 중지시킨다. 존재한다면 fopen 을 이용하여 현재 디렉토리를 기준으로
read mode 로 file 핸들러를 연다. 그리고 $msg 에 file 의 내용을 담은 후에
사용자에게 뿌려준다. 여기에서는 현재 directory 를 기준으로 한다는 것을
명시하기 위하여 $file 앞에 ./ 를 붙였지만 ./ 를 붙이지 않아도 php 에서는
현재 directory 에서부터 찾으라는 것으로 인식한다.
만약 link.php?file=profile.html 이라고 한다면 file 변수의 값인 profile.html 을 열어서 사용자에게 보여주는 것이다.
[요청]
http://server/link.php?file=profile.html
[결과]
전 남자에요. 84 년생이고요. 별명은 스누피.
하지만 이런 알고리즘은 취약성을 갖는다. link.php 를 요청할때 file 의 변수를
cracker 임의대로 변경하여 요청을 한다면 link.php 에서는 cracker 가 임의로
변경한 file 의 내용을 돌려줄 것이다.
예를 들어
[요청] http://server/link.php?file=../../../../../../../../../etc/passwd
[결과]
root:*:0:0:root:/root:/usr/local/bin/bash
daemon:*:1:1:Owner of many system processes:/root:/sbin/nologin operator:*:2:5:System &:/:/sbin/nologin bin:*:3:7:Binaries Commands and Source,,,:/:/sbin/nologin tty:*:4:65533:Tty Sandbox:/:/sbin/nologin kmem:*:5:65533:KMem Sandbox:/:/sbin/nologin ................................
file 의 값을 상위디렉토리로 이동하기 위하여 ../ 를 여러개를 붙였고 etc
directory 밑에 존재한 passwd file 을 열어서 cracker 는 passwd file 을
볼 수 있었다.
해결책을 알아 보자.
개발자가 이 상황에서 신경써야 할 부분은 link.php 를 호출할때 인자로 딸려오는
변수 $file 이다. cracker 가 $file 의 내용을 변경하더라도 link.php 에서는
올바르게 처리할 수 있어야 한다.
추천하는 처리 방법은 $file 의 내용을 조사하여 .. 같은 문자가 있다면 필터링을
하는 것이다. 다음은 취약성을 개선한 소스이다.
link2.php
1 <?
2 if(eregi("\.\.", $file))
3 {
4 echo "장난하니?.. file 이름에 .. 가 들어있으면 안됩니다.";
5 exit;
6 }
7 $exist = file_exists("./$file");
8 if(!$exist)
9 {
10 echo "$file 파일은 없다.\n";
11 exit;
12 }
13
14 $fp=fopen("./$file", "r");
15
16 while(!feof($fp))
17 {
18 $msg .= fgets($fp,100);
19 }
20
21 $msg=nl2br($msg);
22
23 echo $msg;
24
25 ?>
2 번째 라인에서 eregi 를 이용하여 $file 의 내용에 .. 가 있는지 check 를
하였다. 만약 .. 라는 스트링이 $file 안에 존재한다면 cracking 시도로 간주
하고 script 실행을 중지시키고, 들어있지 않다면 정상적으로 script 를 계속
실행한다.
[요청] http://server/link.php?file=../../../../../../../../../etc/passwd
[결과]
장난하니?.. file 이름에 .. 가 들어있으면 안됩니다.
이런 식으로 해결을 할 수도 있고 eregi_replace 를 이용하여 .. 를 공백으로
만들어 무시할 수도 있을 것이다.
위의 방법에 대하여 어떤 문제점이 발생할 수도 있다. Shell 에서는 (bash
shell 기준) ../ 말고도 상위디렉토리로 접근하는 방법이 있다.
예를 들어
$ cd ./.\./
이렇게 입력하면 상위 디렉토리로 이동한다는 것을 뜻한다. 그래서 만약 해커가
http://server/link.php?file=./.\./.\./.\./.\./.\./etc/passwd
이런식으로 요청을 한다면 eregi 는 체크를 하지 못할 것이다. 하지만 이 것은 걱정
하지 않아도 된다. 왜냐하면 data 가 전송되어질때 encode-decode 과정에서 \ or
' or " 앞에는 \ 문자가 하나 더 붙기 때문이다. 그래서 실제로 저렇게 요청을
한다더라도
./.\\./.\\./.\\./.\\./.\\./etc/passwd
이란 파일을 열려고 시도하게 된다. 하지만 만약 php.ini 같은 설정 파일에서
magic_quote 값을 변경하였을 경우 escape (\) 문자가 앞에 붙지 않게 된다.
그래서 cracker 의 요청대로 passwd 파일을 open 할 것이고 출력하게 된다.