제로보드 preg_replace 보안 취약점

강 성훈, 2005년 1월 23일1) (수정 #2)

2010년 5월 8일 덧붙임: 이 취약점의 CVE 식별자는 CVE-2005-1820입니다. 이 문서는 역사적인 이유로 보존되고 있으나 주소는 새 도메인으로 바뀌었습니다. (이전 주소 자체는 무기한 보존되지만 새 주소로 넘겨줍니다.) 어느 경우든, 제로보드 4.x 계열의 사용은 어떤 이유로도 권장되지 않습니다.

일러두기

배경

php4/php5에서 지원하는 펄 정규표현식 라이브러리(PCRE)는 정규표현식에 널 문자(\0)가 들어 있을 경우 그 문자까지를 정규표현식이라 인식하도록 되어 있습니다. 여기에 대한 설명은 PHP 레퍼런스(Difference From Perl 절의 4항 참고)에 있으며, 따라서 정규표현식에 사용자가 제어할 수 있는 변수가 별도의 처리 없이 들어 갈 경우 상황에 따라서는 위험할 수도 있습니다.

설명

이 취약점은 include/list_check.php의 다음과 같은 코드(105/106행)에 의해 발생합니다.

$keyword_pattern = "/([^<]*)$keyword([^>]*)/i";
$memo = preg_replace($keyword_pattern, "\\1<font color=FF001E style=background-color:FFF000;>$keyword</font>\\2", $memo);

(참고로 위의 코드는 HTML 태그 부분 허용 이상의 옵션으로 글을 썼을 때 적용됩니다. 이 옵션이 켜져 있지 않을 경우 모든 <, >는 &lt;, &gt;로 변환되어 정규표현식의 적용을 받지 않습니다.)

위에서 $keyword가 "string/e\0"라고 가정하면, $keyword_pattern은 "/([^<]*)string/e\0([^>]*)/i"이 됩니다. 이 정규표현식이 preg_replace에서 처리될 때 실제로 인식되는 정규표현식은 "/([^<]*)string/e"가 되어, $memo에서 꺽쇠 괄호(<)가 아닌 문자부터 string이라는 문자열이 나올 때까지의string이라는 문자열이 나올 때까지의 꺽쇠 괄호(<)가 아닌 문자로 이루어진 문자열이 php 코드로 인식되어 실행되게 됩니다.

실제로는 뒤의 HTML 코드를 무시하기 위해서 string 앞에 //나 #와 같은 주석문을 추가하는 것이 일반적입니다. 이 때 e modifier가 설정된 상태에서는 치환될 문자열(\\1 부분에 들어 갈 문자열)에 자동으로 addslashes 함수가 적용되므로 \나 ", '와 같은 문자는 사용할 수 없습니다만, 이 정도로도 php 코드를 실행하기에는 충분합니다.

이 취약점이 magic_quotes_gpc 설정과 무관하게 적용되는 것은, _head.php에서 $keyword 변수에 다음과 같은 처리를 하기 때문입니다. 왼쪽의 숫자는 줄 번호를 의미하며 직접적으로 관련되는 부분에는 뒤에 주석을 달았습니다.

131: $keyword=stripslashes($keyword); // (1)
132: $keyword=str_replace("`","",$keyword);
133: $keyword=str_replace("\"","",$keyword);
134: $keyword=str_replace("'","",$keyword);
...
142: if(!isblank($keyword)) {
143:     $keyword=addslashes($keyword); // (2)
...
155:     $keyword=stripslashes($keyword); // (3)
156: }

위 코드가 실행되었을 때 $keyword는 (1)에서 stripslashes가 적용되어, magic_quotes_gpc 설정으로 인해 처리된 '\0'이라는 길이 2인 문자열이 널 문자로 다시 변환됩니다. (2)와 (3)은 서로 반대 역할을 하기 때문에 $keyword의 값에 영향을 주지 않습니다. 이렇게 해서 널 문자가 포함된 $keyword는 list_check 함수에 전달되어 정규표현식에 삽입됩니다.

해결책

이 글을 쓰는 시점에서 공식적인 패치는 이루어져 있지 않습니다.1월 23일에 발표된 4.1 pl6은 이 문제가 수정되었습니다. 수동으로 수정하려면 다음과 같이 합니다:

include/list_check.php의 105번째 줄을 다음에서,

$keyword_pattern = "/([^<]*)$keyword([^>]*)/i";

다음으로 변경합니다.

$keyword_pattern = "/([^<]*)".str_replace("\0","\\0",preg_quote($keyword,"/"))."([^>]*)/i";

php 4.3.5 및 php 5.0.0 이상의 환경에서는 preg_quote가 널 문자도 함께 처리하기 때문에, 다음과 같이 하여도 충분합니다.

$keyword_pattern = "/([^<]*)".preg_quote($keyword,"/")."([^>]*)/i";

비공식 패치2)여기에서 받을 수 있습니다.3) 압축을 풀어서 들어 있는 파일들을 해당하는 위치에 ftp로 올리면 됩니다.

제로보드 공식 홈페이지에서 제공하는 4.1 pl6 패치에는 이 보안 버그 패치가 반영되어 있으므로 설명에 따라서 업데이트하면 되겠습니다.

바뀐 점들

이 글은 2005년 1월 23일 자정에 처음 발표되었습니다. 23일 12시 반에 수정된 내용은 다음과 같습니다:

23일 22시 반에 수정된 내용은 다음과 같습니다:


1) 이 취약점은 2003년 10월 말에 발견되었습니다.
2) 이 패치는 제로보드 저작권 정책과 어긋나지만 이미 패치되지 않은 버그들에 대한 비공식 패치가 배포되고 있는 상황이기 때문에 무시하고 배포하기로 하였습니다.
3) 2005년 1월 23일 12시 30분에 고쳐서 재배포합니다. 옛 파일을 받으신 분들께서는 불편이 없으시다면 패치하지 않으셔도 상관이 없습니다. 검색어 하이라이팅 부분에서 종종 발생하는 오류 메시지를 모두 수정하였습니다.

Copyright © 2003-2005, Kang Seonghoon (Tokigun). All Rights Reserved.