PHP代碼審計基礎篇(XSS 漏洞)

XSS 漏洞

XSS學名爲跨站腳本攻擊( Cross Site Scriptings),在Web漏洞中XSS是出現最多的漏洞,沒有之一。這種漏洞有兩種情況,一種是通過外部輸入然後直接在瀏覽器端觸發,即反射型XSS;還有一種則是先把利用代碼保存在數據庫或文件中,當Web程序讀取利用代碼並輸出在頁面上時觸發漏洞,也就是存儲型XSS。XSS攻擊在瀏覽器端觸發,大家對其危害認識往往停留在可以竊取cookie、修改頁面釣魚,等等。用一句話來說明該漏洞的危害就是:前端頁面能做的事它都能做。

挖掘經驗

挖掘XSS漏洞的關鍵在於尋找沒有被過濾的參數,且這些參數傳入到輸出函數,常用的輸出函數列表如下:printprint_rechoprintfsprintfdievar_dumpvar_export,所以我們只要尋找帶有變量的這些函數即可。另外在代碼審計中,XSS漏洞在瀏覽器環境對利用的影響非常大,所以最重要的還要掌握各種瀏覽器容錯、編碼等特性和數據協議。關於XSS漏洞的東西可以再寫一本厚厚的書,由於篇幅問題,這些東西就不在這裏詳細介紹了,推薦閱讀邱永華的《XSS跨站腳本攻擊剖析與防禦》和餘弦的《Web前端黑客技術揭祕》。

XSS漏洞比SQL注入更多,而且在滿足業務需求的情況下更加難防禦。XSS漏洞經常出現在文章發表評論回覆留言以及資料設置等地方,特別是在發文章的時候,因爲這裏大多都是富文本,有各種圖片引用、文字格式設置等,所以經常出現對標籤事件過濾不嚴格導致的XSS,同樣,評論回覆以及留言也是。其次在資料設置的地方,比如用戶暱稱、簽名等,有的應用可能不只一處設置資料的地方,像在註冊的地方可以設置、修改資料的地方可以設置,這時候要多留意,不一定所有設置這個資料的地方都過濾嚴格了。我們在通讀代碼挖掘的時候可以重點關注這幾個地方,這幾個地方的XSS也通常都是存儲型的。

反射型 XSS

反射型XSS也就是我們在描述裏面說直接通過外部輸入然後在瀏覽器端直接輸出觸發的類型,這種類型的漏洞比較容易通過掃描器黑盒直接發現,只需要將尖括號、單雙引號等提交到Web服務器,檢查返回的HTML頁面裏面有沒有保留原來的特殊字符即可判斷但是白盒審計中,我們只需要尋找帶有參數的輸出函數,然後根據輸出函數對輸出內容回溯輸入參數,觀察有沒有經過過濾。

舉例一個反射型XSS漏洞的大致形式,代碼如下:

//以下是QQ私密接口
if($_GET["openid"]) {
//授權成功後,會返回用戶的openid
//檢查返回的openid是否是合法id
//echo $_GET["oauth_signature"];
if (!is_valid_openid($_GET["openid"], $_GET["timestamp"], $_GET["oauth_signature"])){
	showerr('API帳號有誤!');
	//demo對錯誤簡單處理
	echo "##invalid openid\n";
	echo "sig:".$_GET["oauth_signature"]."\n";
	exit;
}

代碼中echo "sig:" .S_ GET["oauth_ signature"]. "\n";直接將$_GET[“oauth_signature”]的值輸出到瀏覽器中,則可以直接用GET方式注入代碼。

存儲型 XSS

存儲型XSS,顧名思義也就是需要先把利用代碼保存在比如 數據庫 或 文件中,當Web程序讀取利用代碼並輸出在頁面上時執行利用代碼,它的原理圖流程圖如下圖所示。
在這裏插入圖片描述
存儲型XSS比反射型要容易利用得多,不用考慮繞過瀏覽器的過濾,另外在隱蔽性上面也要好得多,特別是在社交網絡中的存儲型XSS蠕蟲能造成大面積的傳播,影響非常大,曾經在新浪微博和百度貼吧都爆發過大規模的XSS蠕蟲。

**同樣,要挖掘存儲型XSS也是要尋找未過濾的輸入點和未過濾的輸出函數,這個最終的輸出點可能跟輸入點完全不在一個業務流上,對於這類可以根據當前代碼功能去猜,或者老老實實去追哪裏有操作過這個數據,使用表名、字段名去代碼裏面搜索。**下面的經典案例分析將講述一個存儲型XSS的挖掘過程。

騎士 cms 存儲型XSS分析

這裏筆者臨時找了一個叫騎士cms的程序看了下,在後臺申請友情鏈接的地方存在XSS漏洞,常規的特殊字符(如尖括號)和標籤的事件(如onerror等)大多被過濾,漏洞挖掘過程如下。

安裝好騎士cms後,在後臺看到一個友情鏈接管理如下圖所示。
在這裏插入圖片描述
前臺有一個申請友情鏈接,根據經驗這個申請友情鏈接的地方應該是一個payload輸入的地方,我們先看看 /admin/admin_ink.php 的代碼:

$act = !empty($_GET['act']) ? trim($_GET['act']) : 'list';
$smarty->assign('pageheader',"友情鏈接");
if($act == 'list'){
	get_token();
	check_permissions($_SESSION['admin_purview'], "link_show");
	require_once(QISHI_ROOT_PATH.'include/page.class.php');
	$oederbysq1=" order BY 1.show_order DESC";

這裏是判斷訪問 admin_link.php 這個文件的時候有沒有act參數,沒有就給$act變量賦值爲list,即進入到輸出友情鏈接列表的代碼:

$offset= ($currenpage-1)*$perpage;
$link = get_links($offset, $perpage, $joinsq1.$wheresql.$oederbysql);
$smarty->assign('link' ,$link);
$smarty->assignl('page', $page->show(3));
$smarty->assign('upfiles_dir', $upfiles_dir);
$smarty->assign('get_link_category',get_link_category());
$smarty->assign('navlabel', "list");
$smarty->display('link/admin_link.htm');

get_links() 函數代碼如下:

function get_links($offset, $perpage, $get_sq1= '')
{
	global $db;
	$row_arr = array();
	$limit = " LIMIT ".$offset.', '.$perpage;
	$result = $db->query("SELECT 1.*,c.categoryname FROM " .table('link') ." AS 1 ".$get_sq1.$limit);
	while($row = $db->fetch_array($result) )
	{
		$row_arr[] = $row;
	}
	return $row_arr;
}

很清楚地看到,這是一個從數據庫讀取友情鏈接列表的功能:

$link = get_links($offset, $perpage, $joinsql.$wheresql.$oederbysql) ;

**後面的代碼則是將讀取的內容以 link/admin_link.htm 爲模板顯示出來。**跟進模板頁看看,有一個關鍵的代碼片段如下:
在這裏插入圖片描述
其中:

<span style="color:#FF6600" title="<img src={#$list.link_logo#}
	border-0/>" class="vtip"> [logo]</span>

這段代碼是有問題的,這裏直接把顯示logo的img標籤放在span標籤的title裏面,當鼠標滑過的時候會調用事件執行顯示title即執行img標籤,這裏的利用點是 {#$list.link_logo#} 可以是HTML實體編碼,從而繞過騎士cms的安全檢查。目前我們已經找到一個輸出點了,輸入點也根據當前代碼功能猜到是在前臺申請鏈接的地方,利用過程如下,在前臺申請友情鏈接頁面 http://ocalhost/74cms/ink/add_link.php 的 logo 字段輸入

1 oner&#114;or=ale&#114;t(1)

onerror
來構造代碼如下:

<span style="color: #FF6600" title="<img src=1 oner&#114;or=ale&#114;t(1)
border-0/>" class="vtip"> [1ogo]</span>

r用html實體編碼解碼爲:“r”:
在這裏插入圖片描述
執行結果如圖4-7所示。
在這裏插入圖片描述
當管理員在後臺查看鏈接時觸發執行代碼:
在這裏插入圖片描述

漏洞防範

由於XSS漏洞在不同瀏覽器下有不同的利用方式,而且特別是業務上有需求使用富文本編輯器的時候,防禦起來就更加複雜,所以在XSS防禦這塊應該從多個方面入手,儘量減少XSS漏洞。

特殊字符 HTML 實體轉碼

一般的XSS漏洞都是因爲沒過濾特殊字符,導致可以通過注入單雙引號以及尖括號等字符利用漏洞,比如一個圖片標籤如下<img src="$_GET['a']" />,則可以通過輸入雙引號來閉合第一個單引號利用漏洞,防禦這類的XSS漏洞只需要過濾掉相關的特殊字符即可,特殊字符列表如下:
1)單引號(’)
2)雙引號(")
3)尖括號(<>)
4)反斜槓()
5)冒號(😃
6)and符(&)
7)#號(#)
還有兩個問題,這些字符應該怎麼過濾,什麼時候過濾?爲了保證數據原始性,最好的過濾方式是在輸出和二次調用的時候進行如HTML實體一類的轉碼,防止腳本注入的問題。

標籤事件屬性黑白名單

上面我們提到過濾特殊字符來防止XSS漏洞,實際上即使過濾了也同樣可能會被繞過,比如利用跟寬字節注入一樣的方式來吃掉反斜槓,再利用標籤的事件來執行 js 代碼,面對這樣的情況,我們還得加標籤事件的黑名單或者白名單,這裏更推薦用白名單的方式,實現規則可以直接用正則表達式來匹配,如果匹配到的事件不在白名單列表,就直接攔截掉,而不是替換爲空。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章