리그베다위키발 보안버그 분석 및 대처방법
최근 리그베타위키 발 모니위키 보안 문제가 이슈였습니다. 2003년에 처음 만들어져서 벌써 13년째 장수(?) 자유소프트웨어 프로젝트인 모니위키에서 이정도 심각했던 보안 이슈는 지금까지 없었습니다. 그런데 이번에 발견된 보안문제는 문제가 심각할뿐만 아니라 PHP 관련 보안문제중에 가장 기초적인 부분중에 한가지라서 그 충격이 컸던 것도 사실입니다.
그러면 어찌해서 이런 기초적인 보안오류가 최초 모니위키가 릴리스된 2003년 이후 10년이 지난 시점에서야 발견된 것일까요?
이번에 발견된 보안 문제는 모니위키를 사용하기위해 필수적으로 필요한 RCS 버전관리 프로그램을 popen()으로 호출하기 위해 만들어진 RCS 버전관리 모듈에서 발견되었습니다. 최초로 발견하신 분은 강성훈님이며, 이 이슈는 2013년에 만들어진 모니위키 1.2.0 버전부터 1.2.3 버전까지 영향을 받습니다. 모니위키 1.2.0 버전 이전에는 다음과 같았기때문에 쉘코드 인젝션이 불가합니다.
wiki.php의 savePage() method에서 RCS 모듈 호출 if (strlen($comment)>$this->anonymous_log_maxlen) $comment=''; // restrict comment le $REMOTE_ADDR=$_SERVER['REMOTE_ADDR']; $comment=escapeshellcmd($comment); // 이 부분때문에 문제가 없음 $myid=$user->id; if (!empty($user->info['nick'])) {
그러던 것이 모니위키 1.2.0부터 다음과 같이 바뀌었습니다.
--- a/lib/version.RCS.php +++ b/lib/version.RCS.php @@ -63,7 +63,9 @@ class Version_RCS { _mkdir_p($dir.'/RCS', 2777); umask($om); } - $fp=@popen("ci -l -x,v/ -q -t-\"".$key."\" -m\"".$log."\" ".$key.$this->NULL,"r"); + // $log = escapeshellarg($log); // win32 not work + $log = '"'.preg_replace('/([\\\"])/', "\\\\\\1", $log).'"'; + $fp = @popen("ci -l -x,v/ -q -t-\"".$key."\" -m".$log." ".$key.$this->NULL,"r"); if (is_resource($fp)) pclose($fp); } diff --git a/wiki.php b/wiki.php index 536b2a1..43826be 100644 --- a/wiki.php +++ b/wiki.php @@ -1020,7 +1020,6 @@ class WikiDB { if (strlen($comment)>$this->anonymous_log_maxlen) $comment=''; // restrict comment le $REMOTE_ADDR=$_SERVER['REMOTE_ADDR']; - $comment=escapeshellcmd($comment); $myid=$user->id; if (!empty($user->info['nick'])) {
잘 보시면 escapeshellcmd()가 제거되었으나 RCS모듈에서는 win32 문제때문에 escaheshellarg()를 사용하지 않고, 어찌된 영문인지 쌍따옴표만 escape하는 실수가 보입니다. (커멘트에도 나와있듯이)
그리고 곧 바로 다음과 같이 수정하여 win32에서 문제점을 수정했으나 escapeshellarg()를 다시 적용하지 않는 실수를 저지르고 있습니다.
--- a/lib/version.RCS.php +++ b/lib/version.RCS.php @@ -63,10 +63,27 @@ class Version_RCS { _mkdir_p($dir.'/RCS', 2777); umask($om); } - // $log = escapeshellarg($log); // win32 not work - $log = '"'.preg_replace('/([\\\"])/', "\\\\\\1", $log).'"'; - $fp = @popen("ci -l -x,v/ -q -t-\"".$key."\" -m".$log." ".$key.$this->NULL,"r"); + $mlog = ''; + $plog = ''; + if (getenv('OS') == 'Windows_NT' and isset($log[0])) { + // win32 cmd.exe arguments do not accept UTF-8 charset correctly. + // just use the stdin commit msg method instead of using -m"log" argument. + $logfile = tempnam($this->DB->vartmp_dir, 'COMMIT_LOG'); + $fp = fopen($logfile, 'w'); + if (is_resource($fp)) { + fwrite($fp, $log); + fclose($fp); + $plog = ' < '.$logfile; + } + } + if (empty($plog)) { + // $log = escapeshellarg($log); // win32 does not work correctly + $log = '"'.preg_replace('/([\\\"])/', "\\\\\\1", $log).'"'; // 이미 win32 문제가 해결되었으므로 이 줄을 지우고 윗 줄을 써야 했음 + $mlog = ' -m'.$log; + } + $fp = @popen("ci -l -x,v/ -q -t-\"".$key."\" ".$mlog." ".$key.$plog.$this->NULL,"r"); if (is_resource($fp)) pclose($fp); + if (isset($plog[0])) unlink($logfile); } function rlog($pagename,$rev='',$opt='',$oldopt='') {
이렇게 해서 2013년 버전 1.2.0부터 1.2.3버전까지 보안 이슈가 영향을 받게 된 것이고, 리그베타위키발 보안이슈는 2015년에 비로소 터지게 됩니다.
개인위키 사용자는 안전한가?
그러면 모니위키를 사용하는 대다수 개인위키 사용자들에게 미치는 영향은 어느정도 일까요?
모니위키는 개발 초기부터 개인위키를 지향하였으며 이에 대응하기 위해 개인 사용자들을 위한 보안모듈을 손쉽게 만들 수 있게 하였고, 모니위키를 설치한 개인 사용자들은 대게 자신만이 편집하고 사용할 수 있도록 모니위키를 설정하였습니다. 예를 들어 모니위키를 최초 설치하면 비로그인 사용자를 포함한 모든 사람들이 편집할 수 있도록 설정이 되지만, 개인 사용자들은 다음과 같이 로그인을 한 사용자, 특히 관리자이면서 소유자인 개인만 편집을 하게끔 설정을 바꾸어 사용하는 패턴이었습니다. 게다가 일부 사용자들은 자신만 사용자 등록을 마친 후에 다른 사용자들은 사용자 등록을 하지 못하도록 하는 옵션을 만들어 달라고 제안하기까지 하였습니다. 그래서 만들어진 옵션이 $no_register 옵션인데, 개인위키 사용자들은 다음과 같은 식의 설정으로 모니위키를 운영하고 있습니다.
.... $security_class="needtologin"; // 로그인해야 일반적인 기능을 쓸 수 있도록 설정 $no_register=1; ...
혹은 다음과 같이
.... $security_class="mustlogin"; // 반드시 로그인하도록 설정. $no_register=1; ...
그런데 이러한 설정을 해놓으면 비로그인 사용자는 모니위키의 edit/diff 기능등을 사용할 수 없고, 모니위키의 보안 이슈가 된 RCS모듈은 아무런 영향력을 발휘하지 못하게 되어 위와 같은 식으로 설정된 개인위키는 RCS 보안문제와 상관 없게 됩니다.
이번 보안 이슈가 드러나게된 또다른 원인으로는 리그베타위키는 엔하위키 시절부터 익명사용자의 편집을 장려하는 정책을 펼치고 있었기 때문입니다. 익명사용자가 편집(edit) 및 변경점(diff)을 볼 수 있기때문에 RCS 모듈의 보안버그가 노출되게 되었던 것입니다.
개인위키 사용자의 대처 방법은?
위에서 기술했다시피 이번에 발견된 보안 이슈를 회피하려면 다음의 설정으로 config.php에서 개인위키 설정을 먼저 바꿔주시기 바랍니다. 그리고 최대한 빠른 시일 안에 모니위키를 백업하고 모니위키 최신 1.2.4p2로 업그레이드 하시길 권장해 드립니다.
.... $security_class="mustlogin"; // 반드시 로그인하도록 설정. $no_register=1; ...
다른 위키엔진은 문제 없나?
그렇다면 모니위키 이외의 다른 PHP 위키 엔진들은 이러한 이슈가 많은 편일까요? 개발자들은 실수를 하게 마련이며, 방대한 기능의 미디어위키는 외부프로그램에 의존하는 경우가 많아서 이런 이슈에 더 자주 노출되게 됩니다.
http://www.cvedetails.com/version/129227/Mediawiki-Mediawiki-1.19.0.html 쉘 인젝션 버그 있음.
https://gerrit.wikimedia.org/r/#/c/110069/2/includes/media/Bitmap.php 쉘 인젝션 버그가 있던 파일을 고치는 패치
(이 경우 미디어위키 1.19.0이 2012-05-02에 나왔고 문제 해결은 2014년 1월)
도쿠위키의 경우도 찾아보면 다음과 같이 나옵니다.
http://www.opennet.ru/base/linux/1159548011_6882.txt.html 2006년 쉘 인젝션 버그
https://www.freelists.org/post/dokuwiki/SECURITY-ALERT-problems-in-fetchphp
https://bugs.dokuwiki.org/index.php?do=details&task_id=926 코드 패치
개발자가 완벽한 천재가 아닌이상 그것이 쌍팔년도 보안 취약점이라고 할지라도 보안버그가 아예 없을수만은 없는 것입니다. 모니위키 코드는 지난 10여년간 꽤 지속적으로 관리되었고, 코드가 방대하지 않고 단촐하기때문에 이러한 버그에 대응이 더 쉽습니다.
모니위키 1.2.4p2
이번에 강성훈님이 발견하신 보안버그를 수정한 버전 1.2.4를 내놓았다가, 패키징 실수 및 RCS 보안버그를 추가로 수정한 패치 릴리스 1.2.4p2를 내놓았습니다. 모니위키 개인 위키 사용자분들은 위 내용을 참조하시어 config.php에서 개인위키 전용으로 편집을 할 수 있도록 설정을 바꾼 후에 모니위키를 업그레이드하시기 바랍니다~
http://dev.naver.com/projects/moniwiki/download/note/7020
P.S.
본인은 이번 리그베다위키 사건에 관련된 보안버그 이슈를 과거 토끼군으로 알려졌던 강성훈님이 버그리포팅하신 25일에 처음 알았으며, 버그가 심각한 만큼 이 문제를 해결한 모니위키 1.2.4 버전을 곧바로 내놓았습니다. 또한 이자리를 빌어 본인은 리그베다위키의 운영에 전혀 관여하지 않고 있다는 사실을 다시한번 밝힙니다.
물론 저는 리그베다위키 관리자 청동님과 종종 모니위키 문제점에 대한 이메일을 주고받고 있으며, 이번 보안 패치도 리그베다위키에 개인메일로 가장 먼저 알리고 제공하였습니다. 또한 리그베다위키 미러관리자로 알려진 퍼즐릿정님에게도 모니위키 버그 및 패치에 대해 알려드렸는데, 이는 다름이 아니라 퍼즐릿정님은 과거 국내 최대 위키였던 노스모크 위키의 관리자이기도 하기 때문입니다.
마지막으로 이번 보안버그를 알려주신 강성훈님에게 다시한번 감사를 드리는 바입니다~