대부분의 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 의 기본 설정이
인용: |
후자는 configure 시의 가본값이 됩니다. |
와 같이 되어 있으므로 실제로는 "/명령어" 가 실행이 됩니다. 또한 아래와 같은 명령은
인용: |
/usr/bin/cat /etc/passwd; ls -al |
인용: |
/cat /etc/passwd; ls -al |
와 같이 치환이 됩니다. 위와 같이 기본 명령의 path 가 safe_mode_exec_dir 로 명령어의 path 가 변경이 되고, meta charactor 가 존재할 경우에는 escape 처리를 해서 에러가 나게 만드는 것이 safe mode 에서의 system 관련 함수 제어를 합니다.
아래의 nosafe_mode_exec_dir 패치를 적용할 경우에는 다음과 같이 치환이 됩니다.
일단
인용: |
safe_mode_exec_dir = |
과 같이 지정이 되어 있을 경우에는, 패치를 하지 않았을 경우와 완전하게 동일하게 작동을 합니다. (이 경우가 기본값입니다.
인용: |
;safe_mode_exec_dir = |
와 같이 주석 처리가 된다면 ./configure 시의 값이 적용이 됩니다.
인용: |
safe_mode_exec_dir = /var/php/bin |
과 같이 지정이 된다면, 명령어의 모든 경로는 /var/php/bin/명령어 가 됩니다. 처리가 가능한 파서는
인용: |
명령; 명령 명령 $(명령) 명령 `명령` 명령 | 명령 명령 && 명령 명령 || 명령 |
을 parsing 할 수 있습니다. 즉,
인용: |
cat $(echo "/etc/passwd") | /bin/grep root; ls -al or cat `echo "/etc/passwd"` | grep root; ls -al |
와 같이 system 함수를 사용을 하면,
인용: |
/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 에서
인용: |
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 와 같이 옵션으로 명령행을 실행시키는 경우는 파싱이 되지 않으므로 될 수 있으면 사용을 하지 않는 것이 좋습니다. 즉 예를 들어
인용: |
find /tmp -exec ls -al => /var/php/bin/find /tmp -exec ls -al |
과 같이 파싱이 되기 때문에 safe_mode_exec_dir 의 영향을 받지않고 시스템 명령을 실행할 수가 있음을 주의하셔야 합니다. 이 패치에 영향을 받는 함수는 아래와 같습니다.
인용: |
popen exec system passthru proc_open shell_exec |
또는, 내부적으로 php_Exec 함수를 호출하는 함수들은 모두 적용이 됩니다.
* 변경 사항
&& 구문과 || 구문 파싱 가능하도록 변경.
첫 번 째 명령어가 잘리는 현상 수정
shell_exec 함수에서도 작동 하도록 변경
popen 에서도 get_php_shell_cmd call 을 사용하도록 변경