[PHP 보안] safe_mode_exec_dir 패치

김정균의 이미지

자료실이나 강좌란이 마땅할 것 같지만,, 요즘 php 의 injection 보안버그와 관련이 많다고 생각되어 정보로 분류를 해도 될 것 같습니다. 당분간은 새소식란에 있는 것이 더 좋을 것 같습니다.

버전 history
20050107 최초 등록
20050107 1차 업데이트
     - && 구문과  || 구문 파싱 가능하도록 변경.
     - 첫 번 째 명령어가 잘리는 현상 수정
     - shell_exec 함수에서도 작동 하도록 변경
     - popen 에서도 get_php_shell_cmd call 을 사용하도록 변경
20050130 2차 업데이트
     - "" 또는 '' 블럭 내부에서 meta charactor 보존 하도록 수정
     - ;; 가 ;'path; 로 확장 되는 문제 해결
     - $변수 에서 변수 이름이 1자일 경우 확장되는 버그 수정

요즘 php injection 보안버그 때문에 말이 많습니다. phpbb의 경우에는 worm 같이 그 외에 많이 사용되는 zeroboard, korweblog, jsboard, gnuboard 등등..

대부분의 injection 보안버그는 대부분 system 관련 함수를 사용하여 spam 을 보내거나 index page 를 변고 하거나, 대부분의 관리자들이 간과하는 local expolit 을 실행해서 root shell 을 얻는데 많이 이용됩니다. 이 경우 가장 많이 사용되는 것이 system 함수이며, 그 외에 exec, passthru, popen 등의 함수가 있습니다. php.ini 에서 이 함수들을 막아놓는 것도 한방법이지만 그렇다고 막으면 원성이 자자합니다.

그러던 중에, safe mode 에서 사용되는 safe_mode_exec_dir 을 safe mode 가 아닐 경우에도 사용을 할 수 있으면, system 관련 함수를 제어하는데 아주 좋을 것 같아서, php 4.3.10 을 기준으로 safe mode 가 아닐때도 사용이 가능하도록 패치를 해 보았습니다.

대략 safe mode 일 경우, php 는 meta charactor 를 escape 시키는데 반해서 safe mode 가 아닐 경우에는 이를 정상적으로 이용할 수 있도록 패치가 되었습니다.

보통 safe mode 에서 system 함수를 사용하면, php 를 빌드 할 때, prefix 를 /usr/local 로 하면 safe_mode_exec_dir 의 기본값은 /usr/local/bin 이 되며, /usr 로 했을 경우에는 /usr/bin 이 됩니다. 하지만 php.ini 의 기본 설정이

Quote:

safe_mode_exec_dir =

참고로
safe_mode_exec_dir =

;safe_mode_exec_dir =
는 완전히 다른 설정값을 가지게 됩니다. 전자는 safe mode 에서는 / 가 되며, 후자는 configure 시의 가본값이 됩니다.

와 같이 되어 있으므로 실제로는 "/명령어" 가 실행이 됩니다. 또한 아래와 같은 명령은

Quote:

/usr/bin/cat /etc/passwd; ls -al

Quote:

/cat /etc/passwd\; ls -al

와 같이 치환이 됩니다. 위와 같이 기본 명령의 path 가 safe_mode_exec_dir 로 명령어의 path 가 변경이 되고, meta charactor 가 존재할 경우에는 escape 처리를 해서 에러가 나게 만드는 것이 safe mode 에서의 system 관련 함수 제어를 합니다.

아래의 nosafe_mode_exec_dir 패치를 적용할 경우에는 다음과 같이 치환이 됩니다.
일단

Quote:

safe_mode_exec_dir =

과 같이 지정이 되어 있을 경우에는, 패치를 하지 않았을 경우와 완전하게 동일하게 작동을 합니다. (이 경우가 기본값입니다.

Quote:

;safe_mode_exec_dir =

와 같이 주석 처리가 된다면 ./configure 시의 값이 적용이 됩니다.

Quote:

safe_mode_exec_dir = /var/php/bin

과 같이 지정이 된다면, 명령어의 모든 경로는 /var/php/bin/명령어 가 됩니다. 처리가 가능한 파서는

Quote:

명령; 명령
명령 $(명령)
명령 $(명령 $(명령))
명령 $(명령 `명령`)
명령 `명령`
명령 | 명령
명령 && 명령
명령 || 명령

을 parsing 할 수 있습니다. 즉,

Quote:

cat $(echo "/etc/passwd") | /bin/grep root; ls -al
or
cat `echo "/etc/passwd"` | grep root; ls -al

와 같이 system 함수를 사용을 하면,

Quote:

/var/php/bin/cat $(/var/php/bin/echo "/etc/passwd") | /var/php/bin/grep root; /var/php/bin/ls -al
or
/var/php/bin/cat `/var/php/bin/echo "/etc/passwd"` | /var/php/bin/grep root; /var/php/bin/ls -al

와 같이 쉘에서 사용할 수 있는 명령어 형식의 명령들의 path 는 safe_mode_exec_dir 에 지정한 경로로 변경이 되게 됩니다. 즉, system 함수를 막지 않고서도, 꼭 사용해야만 하는 명령들 (예를 들어 moniwiki 의 경우 rcs package 의 명령들을 사용하죠) 을 /var/php/bin 에 soft link 만 시켜 놓으면 된다는 얘기입니다. 즉 system 함수를 풀어 주고서도, cracker 들이 노리는 xterm 이나 wget 같은 것을 만질 수 없게 함으로서 cracker 들의 제약을 할 수가 있는 것입니다.

이와 더불어 각 가상 호스트 별로 open_basedir 을 document root 로 지정을 하고, php.ini 에서 upload tmp 와 session save_path 를 다른 곳으로 이동을 시키면 계정간의 보안도 어느정도 처리를 할 수가 있게 됩니다.

예를 들어 php.ini 에서

Quote:

safe_mode_exec_dir = /var/php/bin
upload_tmp_dir = /var/php/tmp
session.safe_path = /var/php/session

chown apache.apache /var/php/{tmp,session}

<VirtualHost IP-ADDR>
DocumentRoot /path/domain
ServerName Some-Domain

<IfModule mod_php4.c>
php_admin_value open_basedir /path/domain:/var/php
</IfModule>
</VritualHost>

와 같이 지정을 하고, 각 계정의 권한을 711 과 같이 지정을 한다면, php 를 이용해서 남의 계정을 탐색하는 것 자체를 원천 봉쇄를 할 수 있습니다. 이는, nosafe_mode_exec_dir 패치가 되어 있어야 되며, open_basedir 은 system 함수를 이용해서 shell 명령을 실행하는 것과는 무관하기 때문입니다.

또한, safe_mode_exec_dir 을 사용할 경우 주의해야 할 것은 find 와 같이 옵션으로 명령행을 실행시키는 경우는 파싱이 되지 않으므로 될 수 있으면 사용을 하지 않는 것이 좋습니다. 즉 예를 들어

Quote:

find /tmp -exec ls -al
=> /var/php/bin/find /tmp -exec ls -al

과 같이 파싱이 되기 때문에 safe_mode_exec_dir 의 영향을 받지않고 시스템 명령을 실행할 수가 있음을 주의하셔야 합니다. 이 패치에 영향을 받는 함수는 아래와 같습니다.

Quote:

popen
exec
system
passthru
proc_open
shell_exec

또는, 내부적으로 php_Exec 함수를 호출하는 함수들은 모두 적용이 됩니다.

P.S
20050130 버전 이 후 부터는 " block 과 ' block 에서 메터 캐릭터가 보호 됩니다. 예를 들어

echo "$(ls -l | grep abc); right"

path/echo "$(path/ls -l | path/grep abc); right"

로 파싱이 됩니다. single quote 로 사용할 경우에는 파싱 자체를 하지 않습니다. (할 필요가 없으니까요)

echo '$(ls -l | grep abc); right'

path/echo '$(ls -l | grep abc); right'

이 점을 체크 하시면 쉘 명령을 이용하기가 좀 더 용이해 질 겁니다.

File attachments: 
첨부파일 크기
파일 php-nosafe-exec-dir.diff7.53 KB
파일 php-nosafe-exec-dir.diff10.97 KB
ibin의 이미지

일단 타사용자간의 파일접근문제를 open_basedir 로 일부 해결하더라도
시스템함수에서 ls, cat 정도만 사용이 되도 무용지물이더군요.

그래서 시스템함수를 모두 막으려고하니 간혹 쓰이는곳이 있고
안전모드로 돌리려고하니 더 많은 제약이 따라서 난감하더군요.

거기에 반해 안전모드가 아니더라도 safe_mode_exec_dir 를 쓸 수 있다는 점은 기존 서버에 보다 손쉽게 적용이 가능할 것 같습니다.
아직 실제로 사용해보진 못했지만... PHP 에서도 공식적으로 지원했으면 하는 기능이군요.

환골탈태의 이미지

open_basedir 과 disable_functions로만 근근히 버텼는데..
좋네요..
감사합니다.

__________________________________________________
모두 다 Hardy로 업그레이드 하고 있습니다.

김정균의 이미지

본문을 업데이트 하고, 새로운 패치를 첨부 했으니 참조 하십시오.
기능이 조금 늘었으며, 파싱 버그를 수정했습니다.

mudori의 이미지

반영하기가 쉽지 않은것 같습니다.

이 소스를 4.3.11에서 반영해주면 좋겠는데요.

된건가?

김정균의 이미지

mudori wrote:
반영하기가 쉽지 않은것 같습니다.

이 소스를 4.3.11에서 반영해주면 좋겠는데요.

된건가?

4.3.11 이 나왔나요? snapshot 말씀하시는 것 같기는 한데.. ^^;