SQL注入第一步:数据库类型判断
在网络安全领域,SQL注入(SQL Injection,简称SQLi)是一种常见的Web应用攻击技术。它通过在用户输入中插入恶意SQL代码,干扰应用程序对数据库的查询,从而实现数据窃取、篡改或破坏等目的。然而,要成功实施SQL注入,首先需要了解目标Web应用后端的数据库类型。因为不同的数据库管理系统(DBMS)在语法、函数支持和错误处理上存在差异,只有“对症下药”,才能更有效地构建注入payload,提高攻击成功率。
判断数据库类型的过程称为“数据库指纹识别”(Database Fingerprinting)。它主要依赖于数据库的特有特性,如独有函数、字符串连接方式、注释符号等。通过注入特定SQL片段并观察响应(如页面正常返回、报错或延迟),可以逐步排除不可能的数据库类型,最终锁定目标。
常见数据库类型及其关联
主流数据库管理系统包括Oracle、MySQL、SQL Server(MSSQL)、Access、PostgreSQL和MongoDB等。这些数据库在企业级应用中广泛使用,各有侧重。例如,Oracle常用于大型企业系统,MySQL则流行于开源Web应用如WordPress;SQL Server多见于Microsoft生态,Access适用于小型桌面应用,PostgreSQL以其扩展性和标准符合性著称,而MongoDB作为NoSQL数据库,处理非结构化数据更灵活。
判断数据库类型时,可以从前端技术栈入手作为初步推测。这是因为某些编程语言或框架偏好特定数据库:
- ASP(Active Server Pages):常搭配SQL Server或Access,因为这些是Microsoft的产品,集成性强。
- .NET框架:通常使用SQL Server,作为Windows服务器的默认选择。
- PHP:多与PostgreSQL或MySQL结合,尤其是LAMP(Linux+Apache+MySQL+PHP)栈。
- Java:偏好Oracle或MySQL,特别是在企业级应用如J2EE环境中。
这种关联并非绝对,但可以作为起点。例如,如果目标网站URL以“.asp”或“.aspx”结尾,很可能后端是SQL Server或Access。通过查看HTTP响应头、页面源代码或使用工具如WhatWeb,也可以获取框架信息,进一步缩小范围。
此外,数据库的默认端口也可作为辅助判断:MySQL为3306,SQL Server为1433,Oracle为1521,PostgreSQL为5432。如果能通过网络扫描(如Nmap)探测端口,虽不直接依赖SQL注入,但可提供线索。不过,在实际注入中,更依赖于注入点的响应。
通过标志性信息判断数据库类型
最直接的方法是查询数据库的版本信息。不同数据库有专属的系统变量或函数,用于输出版本详情。通过联合查询(UNION-based)或错误注入(Error-based),注入这些函数,如果页面返回版本字符串,即可确认类型。
- SQL Server:使用
SELECT @@VERSION。例如,在注入点后添加' UNION SELECT @@VERSION --,如果返回如“Microsoft SQL Server 2019”,则确认。 - Oracle:
SELECT BANNER FROM V$VERSION或SELECT * FROM V$VERSION。返回如“Oracle Database 19c Enterprise Edition”。 - MySQL:
SELECT @@VERSION或VERSION()。如返回“8.0.30-MySQL”,确认MySQL。 - PostgreSQL:
SELECT VERSION()。返回如“PostgreSQL 13.4 on x86_64-pc-linux-gnu”。 - Access:无直接版本函数,但可通过系统表判断(如后文所述)。
- MongoDB:作为NoSQL,注入方式不同,常通过JavaScript函数如
db.version(),但需确认是否支持SQL-like查询。
如果注入点是盲注(Blind SQLi),无法直接看到输出,可使用条件语句如AND (SELECT @@VERSION) LIKE '%Microsoft%'观察页面是否正常返回(真则正常,假则异常)。
通过特有函数快速排除数据库
每个数据库都有独有的内置函数,这些函数在其他系统中不存在或行为不同。通过注入这些函数并观察响应,可以快速指纹识别。
- SQL Server特有:
@@PACK_RECEIVED(网络包计数)、@@ROWCOUNT(影响行数)。注入如AND @@PACK_RECEIVED > 0,如果正常,返回SQL Server。 - MySQL特有:
CONNECTION_ID()(连接ID)、LAST_INSERT_ID()(最后插入ID)、ROW_COUNT()。如AND CONNECTION_ID() > 0正常,则MySQL。 - Oracle特有:
BITAND(1,1)(位运算)、UTL_INADDR.GET_HOST_NAME。注入AND BITAND(1,1)=1,正常则Oracle。 - PostgreSQL特有:
EXTRACT(DOW FROM NOW())(提取星期)、PG_SLEEP(1)(延迟)。如AND EXTRACT(DOW FROM NOW())=0。 - Access:无复杂函数,但可测试
IIF(1=1,1,0)。 - MongoDB:函数如
ObjectId(),但需在支持的上下文中测试。
在盲注场景下,可结合时间延迟:如MySQL的SLEEP(5)、BENCHMARK(1000000,MD5(1))引起延迟,确认类型。如果函数不存在,其他数据库会报错或无响应。
通过字符串处理方式判断
字符串连接和类型转换是数据库间差异明显的特性。注入时,测试不同连接符:
- SQL Server:使用
+连接字符串,如AND 'a'+'b'='ab'正常。 - MySQL:支持
+(但视作数字加法,若字符串则为0),更常用CONCAT('a','b')。注入AND 'a'+'b'=0正常(字符串转数字)。 - Oracle:使用
||或CONCAT('a','b'),+无效。注入AND 'a'||'b'='ab'正常。 - PostgreSQL:类似Oracle,使用
||或CONCAT。 - Access:支持
&或+,如'a' & 'b'='ab'。
通过这些差异,可以构建条件注入:如果AND 'a'+'b'='ab'正常,可能是SQL Server;如果失败但AND 'a'||'b'='ab'正常,则Oracle或PostgreSQL。
此外,类型转换测试:MySQL中length(user)>0正常(user是系统函数),而其他数据库可能不同。
通过特殊符号和注释判断
注释符号是另一个关键指纹。不同数据库支持的注释不同,通过注入注释观察是否被忽略(页面正常)或报错。
判断顺序建议:
- 测试
NULL和%00:Access支持作为注释。如果id=1%00正常,则可能Access。 - 测试
#:MySQL特有注释。如id=1 #,正常则MySQL(注意MySQL也支持--和/* */)。 - 测试
--(注意空格)和/* */:SQL Server、Oracle、PostgreSQL支持。如果id=1 --正常,则可能是这些。 - 测试
;:分号用于多语句查询。Oracle不支持多行查询,如果id=1; SELECT 1报错,则Oracle。
结合这些,一步步排除:如果#正常,MySQL;否则测试--,若正常则SQL Server/Oracle/PostgreSQL;再用;区分Oracle(报错)。MongoDB不支持SQL注释,但可测试JSON注入。
针对MSSQL和Access的特定判断
MSSQL和Access常用于Windows环境,可通过系统表判断:
- 测试
' AND EXISTS (SELECT COUNT(*) FROM sysobjects) >0:如果正常,返回MSSQL(sysobjects是系统表)。 - 测试
' AND EXISTS (SELECT COUNT(*) FROM msysobjects) >0:如果正常,返回Access(msysobjects是Access系统表);若两者均异常,则非此二者。
这些表是独有的,注入后若存在则确认。
