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