SQL注入(二)
5.限制輸入長度??
如果在Web頁面上使用文本框收集用戶輸入的數據,使用文本框的MaxLength屬性來限制用戶輸入過長的字符也是一個很好的方法,因為用戶的輸入不夠長,也就減少了貼入大量腳本的可能性。程序員可以針對需要收集的數據類型作出一個相應的限制策略。
6.URL重寫技術
????????我們利用URL重寫技術過濾一些SQL注入字符,從而達到防御SQL注入。因為許多SQL注入是從URL輸入發生的。
7.傳遞參數盡量不是字符
假設我們顯示一篇新聞的頁面,從URL傳遞參數中獲得newid我們可能會隨手寫下下面的代碼:???
string?newsid?=?Request.QueryString["newsid"];
string?newssql?=?"select?*?from?news?where?newsid="?+?newsid;
?
?
如果傳遞過來的參數是數字字符就沒有問題。但是如果傳遞過來的newsid是“1 delete from table ”的話,那么sql的值就變成了“select * from table where newsid=1 delete from news”。發生注入成功。但是這里改為
??????? int newsid=int.Parse(Request.QueryString["newsid"].ToString());
????????string?newssql?=?"select?*?from?news?where?newsid="?+?newsid.Tostring();
????????這里如果還是上面"1 delete from table "會發生錯誤,因為在轉換時候出現了錯誤
????????從上面的一個小例子,我們得出在傳遞參數時候盡量不要用字符,免得被注入。?
?
最后是我想擴展下利用URL重寫技術來過濾一些SQL注入字符,首先這里有一篇關于URL重寫的文章,我的基本思想是可以利用它,屏蔽一些危險的SQL注入字符串,這些字符串我們可以人為的設定,畢竟我們還是根據特定的環境設定我們防御措施。首先我們在ModuleRewriter類中的Rewrite函數得到絕對的URL判斷其中是否有危險字符,如果有我們就將它鏈接到一個提示用戶您輸入危險的URL地址。如果不是我們繼續判斷是否觸發了其他的URL重寫的規則,觸發了就重寫。這樣就大致上能在URL上防御危險字符
代碼?
上面是要在web.config配置文件加上的內容,這里我加上了兩個重寫規則,第一個規則是專門針對滿足這個正則表達式的頁面URL查看是否有危險字符,有危險字符就會發送到Default_sql_error.aspx頁面,來示警。這里我假定會發生危險字符注入的頁面時以"d"字符開頭并加上數字的頁面(這里我們可以根據實際情況自己定義,看哪些頁面URL容易發生我們就制定這些頁面的正則表達式),第二個是一般URL重寫。因為這里我采用的是HTTP模塊執行URL重寫,所以加上<httpModules></httpModules>這一塊。
?????????第二步就是要在重寫Rewrite函數了
?
protected override void Rewrite(string requestedPath, System.Web.HttpApplication app){
// 獲得配置規則
RewriterRuleCollection rules = RewriterConfiguration.GetConfig().Rules;
Uri url = app.Request.Url;
// 判斷url 中是否含有SQL 注入攻擊敏感的字符或字符串,如果存在,sqlatFlag = 1 ;
string urlstr = url.AbsoluteUri;
int sqlatFlag = 0;
string words = "exec ,xp ,sp ,declare ,cmd ,Union ,--";
// 如果還有其他敏感的字符或者符號,可以加入上面這行字符串中
string[] split = words.Split(',');
foreach (string s in split)
{
if (urlstr.IndexOf(s.ToUpper()) > 0)
{
sqlatFlag = 1;
break;
}
}
if (sqlatFlag == 1)
{
// 創建regex
Regex re = new Regex(rules[0].SendTo, RegexOptions.IgnoreCase);
// 找到匹配的規則,進行必要的替換
string sendToUrl = RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath, re.ToString());
// 重寫URL
RewriterUtils.RewriteUrl(app.Context, sendToUrl);
//RewriterUtils.RewriteUrl(app.Context, rules[0].SendTo);
}
else
{
// 遍歷除rules[0 ]以外的其他URL 重寫規則
for (int i = 1; i < rules.Count; i++)
{
// 獲得要查找的模式,并且解析URL (轉換為相應的目錄)
string lookFor = "^" + RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath, rules[i].LookFor) + "$";
// 創建regex
Regex re = new Regex(lookFor, RegexOptions.IgnoreCase);
// 查看是否找到了匹配的規則
if (re.IsMatch(requestedPath))
{
// 找到了匹配的規則, 進行必要的替換
string sendToUrl = RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath, re.Replace(requestedPath, rules[i].SendTo));
// 重寫URL
RewriterUtils.RewriteUrl(app.Context, sendToUrl);
break;
// 退出For 循環
}
}
}
}
?
?
那么下一步就是檢驗例子了
首先我們輸入http://localhost:4563/web/Default.aspx?id=1;--?
這樣http://localhost:4563/web/Default.aspx?id=1;--?沒有改變,就會顯示Default_sql_error.aspx里內容“您輸入了危險字符”。?
再輸入http://localhost:4563/web/D11.aspx就會顯示?Default2.aspx內容,因為這里觸發了第二個重寫規則
?試驗成功。
當然用URL防SQL只是我想到一種怪癖思路,大家如果有什么特別方法可以共同考論一下,還是引用前輩們話“見招拆招”遇到實際的問題我們就根據實際情況解決,選用那個最實用的方法。
?
?
?
一個客戶對我們請求說,請我們來檢查一下他的內部網絡,這個網絡被公司的職員以及客戶們來使用。這是一個較大的安全評估的一部分,而且,雖然我們以前從沒 有真正的使用過SQL注入來破解一個網絡,但是我們對于其一般的概念相當的熟悉。在此次“戰斗”中,我們是完全成功的,而且想要通過把這個過程的每一個步驟重新記錄下來,并作為一個“生動的例子”。
“SQL注入”是特定的一種未被確認或未明確身份的用戶輸入漏洞的一個子集(“緩沖溢出”是一個不同的子集),而這個想法的目標是,讓應用程序確信從而去 運行SQL代碼,而這些代碼并不在其目的之內。如果一個應用程序是在本地通過即時的方式來創建一個SQL字符串,結果很直接,會造成一些真正的出人意料的結果。
我們要明確說明的是,這是一個有些曲折的過程,并且其中會有多次的錯誤的轉折,而其余的更有經驗的人當然會有著不同的-甚至更好的-方法。而事實上,我們成功的實現了建議,并沒有被完全的誤導。
還有一些不同的論文討論SQL注入問題,包括一些更加詳細的文章,不過此文所展示的,與破解的過程同樣份量的是發現了SQL注入的原因。
目標內網
這顯然是一個完全自主開發的應用程序,而我們對于它沒有預先的了解,或者訪問源代碼的權限:這已是一個“blind”攻擊。若干次偵測之后,我們了解到服 務器運行的是微軟的IIS6,并使用ASP.NET框架,從這其中得到的,似乎可以假定數據庫是微軟的SQL服務器:我們相信這些技術可以應用于幾乎任何 一種web應用,而此應用可能為任何一種SQL服務器所支持。
登陸頁面是一個傳統的用戶名-密碼表單,帶有一個用電子郵件給我傳送密碼的鏈接;而后者被證實是整個系統的敗筆。
當輸入一個電子郵件地址的時候,系統會假定次郵件存在的方式,再用戶數據庫里面尋找這個電子郵件地址,并且會郵寄一些內容到這個地址。由于我的電子郵件地址沒有被找到,所以它不會給我發送任何內容。
所以,第一個測試,對于任何SQL化的表單而言,是輸入一個帶有單引號構成的數據:這樣做的目標是查看是否他們在構建SQL字符串的時候根本沒有使用數據 的清理機制。當為此表單提交了一個有單引號的電子郵件之后,我們得到了一個500錯誤(服務器失敗),這就是說,這個“被破壞了的”的輸入實際上被真實的 分析過。中!
我們猜測底層的SQL代碼可能類似于如此:
這里,$EMAIL是由用戶通過表單提交的電子郵件地址,而這段較長的查詢提供的應用符號,是為了使得這個$EMAIL成為一個真正的字符串。我們不知道 這個數據域的確切的名字或者是于此相關的數據表的名字,但是我們卻了解他們的特性,而此后我們將會得到一些很好的猜測結果。
當我們輸入steve@unixwiz.net' - 留意那個結尾的引號 - 這將產生出一個如下構建的SQL:
當這個SQL被執行的時候,SQL分析器發現了多余的引號,從而中止了工作,并給出一個語法錯誤。而這個錯誤如何表述給用戶,取決于應用程序內部錯誤修復 的過程,但是這通常不同于“電子郵件地址不存在”的錯誤提示。這個錯誤的響應是致命的第一通道,既用戶的輸入沒有被正確的清理,而因此應用程序成為了破解 的美食。
由于我們輸入的數據顯然位于WHERE子語句中,讓我們用合法的SQL方式改變一下這個子句的本貌并看看會發生什么。通過輸入任何一種‘OR ’x‘=x語句,結果SQL成為:
因為應用程序沒有真正的對這樣的查詢有所考慮,而僅僅是構建一個字符串,以致于我們使用的引號使得一個單元素的WHERE子句,變成了一個雙元素的子句, 而且’x'=x字句是確定為真的,無論第一個字句是什么。(有一種更好的方式來確保“始終為真”,這部分我們后面會談及)
不過與“真實”的查詢不同,本應當一次返回一個單獨的項,這個版本必然會返回成員數據庫里面的每一個項。唯一的可以發現應用程序在這種情況下會做什么的方式,就是嘗試。不斷嘗試,我們留意到以下結果:
你的登錄信息已經發送到 random.person@example.com.
我們一般都會把查詢返回的第一行作為作為猜測的主要入口。這哥們確實從E-mail里拿回了他的密碼,同樣這個郵件可能會讓他感到吃驚并且會引起懷疑。
現在我們知道怎么在本地玩這條查詢了,雖然目前我們還不知道我們看不到的那部分SQL結構是怎么拼起來的。但是我們通過逆向工程看到了三個不同的查詢結果:
- 您的登陸信息已經以Email形式發送給您
- 我們無法識別您的Email地址
- 服務端錯誤
頭兩個響應是有效的查詢的結果,最后一個是無效SQL造成的。 類似這樣的響應結果會幫助我們更好的逆推服務端用來查詢的SQL語句結構。
預設字段映射
我們要干的第一步就是猜字段名,首先我們合理的推測查詢帶了“email address” 和 “password”,所以可能的字段名選擇會有“US Mail Address”? 或者 “userid” 亦或者“phone number”? 。當然最好能執行 show table,但是我們又不知道表名,貌似目前沒什么明顯的方法能讓我們拿到表名。
那就分步走吧。在每個例子里,我們會用我們已知的SQL加上我們自己的特殊“段”。我們已知的這條SQL的結尾是個Email地址的比對,那就猜下email是這個字段名吧
如果服務器響應是報錯,那基本上可以說明我們的SQL拼錯了。但是如果我們得到任何正常的返回,例如“未知的郵件地址” 或 “密碼已發送”? ,說明我們的字段名蒙對了。
要注意的是,我們的“And” 關鍵字而沒用“OR” 關鍵字,這么做是有目地滴。在上一步中,我們不關心到底是哪一個Email,而且我們不想因為蒙中某人的Email然后給他發了重置密碼的郵件。這么搞那 哥們兒一定會懷疑有人對他的帳號搞三搞四。所以用“And”關鍵字拼上一個不合法的Email地址,這樣服務端總是返回空結果集,也就不會給任何人發郵 件。
提交上面的SQL代碼段確實返回了“未知的郵件地址” 這么一個響應。現在我們確認了email地址的字段名是email。如果不是這么個響應,那我就再蒙“email_adress”或者“mail”亦或者 其他類似的。這個環節總是靠蒙的,但是蒙也得講技巧和方法方式。
這段SQL的用意是我們假設預設的SQL查詢中的字段名是 email ,跑一下看看是不是有效。我不會管你到底有沒有匹配的Email,所以用了個偽名“x” , “--” 這個標示是表示SQL的起始。這樣SQL解析到這就會把它直接當成一條命令,而“--”后面的會是一個新的命令,這樣就屏蔽掉了后面的那些不知道的玩意 兒。
下一步,我們來猜下其他比較明顯的字段名: "password","userid","name" 和類似的。每次我們只蒙一個名字,當返回結果不是“服務端錯誤” , 那就說明我們蒙對了。
通過這一步,我們蒙出來了下面幾個字段名:
- passwd
- login_id
- full_name
肯定還有更多其他的(把HTML頁面表單的 <Input?? name="XXXXX"> 拿來做參考是個非常不錯的選擇)后來我又挖了一下但是沒挖出來更多的字段名。 到目前為止,我們還是不知道這些字段所屬的表的表名--咋個弄呢?
搜尋表名
應用內建的query已經把表名放在語句中,但我們不知道表的名字。有幾種方法可以找到這些插入在語句中表名(以及其他的表名)。我們用的是一種依賴于subselect的方法。
下面這個單獨的query
返回表中記錄的數量,當然,若表名是無效的,則查詢失敗。我們可以把這個查詢放入我們的查詢語句中來探查表名。
我們實際上并不關心表中有多少記錄,我們關心的是表名是否有效。通過試探不同的猜測,我們最終確定members是數據庫中的有效表名。但是,這是這個查 詢中所用的表名嗎?為此,我們需要另一個使用table.field的查詢:這只在表名的確是查詢中的表名時才工作,而不僅僅當表存在時工作。
當這個語句返回 "Email unknown"時,可以確認我們的SQL正確執行了,并且我們成功的猜出了表名。這對后面的工作很重要,但我們先臨時使用一下另一種方法。
弄幾個帳號先
目前我們搞到了members表結構的部分信息,但是我們只知道一個用戶名,就是之前我們蒙中的那個發了郵件通知的那個用戶名。當時我們只得到了郵件地址,但是拿不到郵件內容。所以我們得再弄幾個有效得用戶名,高端洋氣上檔次的最好。
我們的從公司的網站開始人肉,找到那些人物介紹的頁面,一般都是介紹公司內部人員的。這些介紹里大多都有這些人的Email地址和名字。就算沒有這些信息也沒啥,咱兜里還有貨。
思路是這樣的,提交一個帶有“Like” 關鍵字的SQL,這樣我們可以對Email地址或者用戶名做些模糊匹配,每次提交如果返回“我們已發送您的密碼至郵箱”那也就是說我們的模糊查詢有效了,而且郵件也真的發了!!!。這么干雖然我們能拿到郵件地址,也意味著對方會收到郵件并引起警覺,所以? 慎用!!
我們能做email、full_name(或者其他字段)的查詢,每次放入%這個通配符執行如下的查詢:
暴力破解密碼
我們肯定可以在登陸頁面嘗試暴力破解密碼,但是大多的應用都做了相應的防護手段。可能的防護會有操作日志,帳號鎖定或者其他能大大降低我們效率的手段或設備,但是因為輸入沒有被過濾所以給我們繞過這些防護多了一些可能。
我們將把密碼和郵件名稱的代碼段加到我們已知的SQL里。在這個例子里我們會用一個倒霉催的哥們的郵箱,bob@example.com 然后試試我們準備的一些密碼。
這條SQL是完整有效的,所以服務器鐵定不能夠報錯,所以我們知道當服務器響應是“您的密碼已經發送至您的郵箱” 這么個結果是我們就知道剛提交的那個密碼就是我們要的密碼。雖然倒霉催的Bob也收到了郵件并且一定會警覺,但是我們在他警覺之前就干完我們想干的了。
這個過程可以在Perl下用腳本自動化完成,所以我們就去搞了搞Perl的腳本,結果寫腳本的時候發現了另外一種方法來干這事。
數據庫不是只讀的
迄今為止,我們對數據庫除了進行查詢外,沒做其他事。盡管SELECT是只讀的,但不意味SQL就只能這樣。SQL使用分號來中斷一個語句, 如果對輸入沒有做正確的處理, 它就不能阻止我們在查詢語句后面添加不相關的字符串。
最恰當的一個例子就是這樣:
第一部分提供了一個假的郵件地址 --?'x'?-- 我們并不關系這個查詢的返回,我們僅僅是給出了一個我們能夠使用無關SQL指令的方式,一個嘗試刪除整個members表而真的是無任何關系的操作.
這表明我們不僅僅可以切分SQL指令,我們還可以修改數據庫,這完全是被允許的。
加入一個新成員
由上所得,我們獲知了members表的部分的結構,嘗試添加一個新的記錄到這個表里面似乎是一個可拊掌稱慶的方法:如果這能成功,我們就能夠通過我們新插入的帳戶來直接登陸了。
這里,毫不驚奇的是,這只需要稍微加些SQL,我們把它放在不同的行從而讓我們的展示更易理解,不過從頭到尾這一部分其實仍然是一個字符串:
SELECT?email,?passwd,?login_id,?full_name?
??FROM?members?
?WHERE?email?=?'x';?INSERT?INTO?members?('email','passwd','login_id','full_name')??VALUES?('steve@unixwiz.net','hello','steve','Steve?Friedl');--';?
即便是我們真的確定了表的名字以及使用的字段都是正確的,在成功的實施攻擊之前,仍然有一些障礙:
對于手頭的案例而言,我們遇到了#4或者是#5上面的障礙 - 我們無法真正確定是哪一個 - 因為在主登陸界面上,輸入上面的用戶名+密碼的時候,返回了一個服務器上的錯誤。這就是說,那些我們沒有用到的字段可能是必要的字段,而盡管如此,它們仍 然沒有被正確的處理。
這里,一個可行的方法,就是猜測別的字段,但是這可以保證是一個長時間而且耗費勞力的過程:雖然你可能可以猜測出來那些“顯而易見”的字段,但是卻很難構建出整個應用程序的組織圖像。
所以,最后我們選擇走另一條不同的路。
轉載于:https://www.cnblogs.com/barrywxx/p/4483621.html
總結
- 上一篇: SQL Server表分区【转】
- 下一篇: 也记一次性能优化:LINQ to SQL