sql注入准备
环境
ubuntu+niginx+mysql+php sqli-labs
你也可以使用phpstudy打开nginx和mysql直接访问
##工具
burpsuit抓包
vscode编码
sql注入开始
//1 union select 1,2,3 第一件事 逃脱出单引号的控制 ------- 闭合单引号(加单引号)
//但是单双引号要成对出现,python,java.....
//2个办法 要么将多出来的单引号闭合 要么注释掉单引号
//mysql注释符 单行 -- # 多行----- /** */ */
//需要知道联合查询的列数 要么试 要么查
//求当前表的列
//用order by 检测列数
//让第一个表为空,然后面连接的1,2,3显示,给个无效id就行 库名:security
//注入最终目的 注入管理员账号密码
//要知道管理员账号密码 首先要知道管理员表表名
//列名
一些指令函数
select substr(‘aaa’,1,1);从第一位开始截取截取一位
查看当前所处的数据库名
查看当前用户权限
查看当前登录数据的版本
length,截取函数
内置函数。。。。。
get传参
可以去mysql官网查看一些指令
链接: https://www.php.net/
连接数据库的
isset 检测变量是否已声明并且其值不为 null
一般get传参通过url
post传参一般是通过form表单
因为没有传参所以到这里
那就get传参数呗,
看代码,打开的result文件其实就是记录的日志文件的内容
后面就开始查询用where 条件id是谁就查谁
limit 0,1这里就是如果查询有多行就从第0行开始取1个,这里的意思就是最开始哪行
多用断点调试,查看具体的流程
sql
sql注入是合理合法的mysql执行语句,因为没有对用户的输入进行合理的过滤,导致意外的查询语句进到程序中,查询到本不应该查询的数据
字符型注入
有单双引号闭合需要闭合单双引号
那么这里是(用户输入)可控的,那是不是可以联表查询
本来id传的是1,那我现在传 select * from users where id=1 union select 1,2,3;
但是查出来没有反应,是因为数据库认为你的id是1 union select 1,2,3这么长,数据库的纠错机制就将union select 1,2,3给删了,还是查询的1,所以要逃脱单引号的控制,输入合理合法
改了之后查询还是报错?
因为在url进行地址栏传参的时候有编码规范,它会将#编码成%23,咋来的?
URLcode编码规范,先取assii再转成hex(16),再去掉前面的0x,加上%
成了!!!
把后面的 ’ 注释掉了,也将后面的单引号也注释掉了,正常回显了
好了!!!开始扩展思路了
但是你不知道select 后面对面数据库的表有几列
select * from goods order by gid desc;
//order by 用来排序,可以用户列数排序,desc表示倒序。默认正序asc
select * from goods order by gname desc;
//每一列都可以排序,基于ascii排序
但是不知道列名啊,诶嘿,mysql的列名可以用数字替换,用1可以代表第一列…
那么现在用order by 检测列数,但是不知道列数,反正代码里面有显示报错,比如20可以显示,30报错,那列数就在20 30 之间
注释 --空格 就是%20
--%20 也行
--+ 也可以
让第一个表为空,然后面连接的1,2,3显示,给个无效id就行
select 1,2,可以展示那么 将这些换成mysql自带的函数可以不
那么当前的用户,权限,数据库名都ok了
库名:security
注入最终目的 注入管理员账号密码
要知道管理员账号密码 首先要知道管理员表表名
列名
mariadb默认三张表,mysql多了sys
mysql :mysql数据库的权限,用户对数据库的权限
performance_schema:性能,查表的快慢
information_schema:数据库表的信息
information_schema里面有tables 数据库里面所有的表名
colums:列名
schemata:所有数据库的库名
查到了库名,那看看information_schema里面的table
这个可疑,一个是数据表所处数据库的名称,一是数据表本身的表名
所有数据库名字
查到了所有数据库的所有表
但是我们要的是刚刚查到的数据表
那就查到了当前数据库下的所有表
users可疑
表名ok了,查列名
ok数据库名,表名,列名都查到了
上手,不and就把数据库下面的所有列名都查出来了
都有了直接查
数字型注入
直接显示数字都不需要闭合
直接查数据库和版本看看
看到了数据名是 security
那就查它
拉满
括号注入
看到了数据名那就查!!!
根据爆出的字段去查
双引号加括号
尝试注入,提示了双引号加括号
那就根据")去注入,记得后面加–+注释了
报错注入
?id=1’会报错,不加没显示,可以试试报错注入
用substr 最大截取32位,默认截取前32位
?id=1' and updatexml(1,concat(0x7e,substr((select group_concat(username,0x3a,password)from users),1,32),0x7e),1) --+
?id=1' and updatexml(1,concat(0x7e,substr((select group_concat(username,0x3a,password)from users),32,64),0x7e),1) --+
updatexml(xml_target, xpath_expr, new_xml)
xml_target:xml文档对象的名称,是一个string类型。
xpath_expr:使用xpath语法格式的路径。
new_xml:需要更新的内容。
第二个参数就是xml文件的路径,但是路径里不能用波浪线~ ,那就0x7e就是~的16进制表现的
SQL报错注入的应用:当使用updatexml(xml_target, xpath_expr, new_xml)函数时,若xpath_expr参数不符合xpath格式,就会报错。
而~符号(ascii编码值:0x7e)是不存在xpath格式中的, 所以一旦在xpath_expr参数中使用~符号,就会产生xpath syntax error (xpath语法错误),通过使用这个方法就可以达到报错注入的目的。
select group_concat(username,0x3a,password)from users;
select substr((select group_concat(username,0x3a,password)from users),32,64);
GROUP_CONCAT() 是聚合函数,将属于一组的相关行的数据项进行合并并以字符串的形式返回
0x3a就是 : 冒号,用来分隔其他字符串
一直截取,截完就好了
6:
这里闭合成了双引号而已
and updatexml(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema='security'),0x7e),1) --+
查完表就查表的字段
and updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_schema='security' and table_name='users'),0x7e),1) --+
在username paasword查就好了…
7
1:mysql用户权限必须为root,(现在很少给网站的权限为root,一般就是普通用户)
2:必须知道网站的物理路径(要导出webshell要导在网站里面,要访问到)
3:在my.ini里面有个参数必须为空 secure_file_priv
(my.cnf) 这要进到服务器才能改
三个参数
1:null 不让导出,默认配置就是null
2:写一个具体目录,只能往这个目录下导
3:啥都没有,啥目录都可以
有语法错误无所谓看文件生成没有
布尔盲注
错误不显示,页面展示不出报错和联合查询的效果,从侧面指标,页面回显正确与否
页面特征,加单引号为假,不加单引号为真
?id=1'and ascii(substr(database(),1,1))=115--+
substr截取当前数据库当前第一个字符s,转成ascii,不是115就是假,就能判断第一个字段是啥,
?id=1'and ascii(substr(database(),1,1))>115--+
取范围也ok
可以用sqlmap工具
可以使用二分法解决
出来了后面就按步骤走吧
时间盲注
这里的输入显示的解结果都是一样的 You are in…
状态没变,没法用布尔,那就用if 和sleep判断,真就沉睡,假就不沉睡
所有报错注入可能没戏了,所有可以试试时间注入
?id=1' and if(ascii(substr(database(),1,1)) > 100,sleep(3),0)--+
当注入的数据为真时,沉睡三秒
那就可以通过这样去查,转的久就是真,快就是假,用sqlmap或者脚本就行
post传参
联合查询
这里post传参不涉及url编码,直接#就可以
密码随便写,因为#将后面的闭合掉了
username随便写,让前面的查不到,才会让后面的回显
注入页面的回显是两个字段name和password
那就老样子用
a' union select 1,group_concat(table_name) from information_schema.tables where table_schema = 'security' #
12
就是") 注入
a") union select 1,group_concat(table_name) from information_schema.tables where table_schema = 'security' #
出来了就直接去查
报错注入
13
报错不会在页面展示了就报错注入,联合查询展示不出来
asas') and updatexml(1,concat(0x7e,user(),0x7e),1) #
a') and updatexml(1,concat(0x7e,substr((select group_concat(username,0x7e,password)from users),1,32),0x7e),1) #
布尔盲注
只能通过图显示正确,那就考虑布尔盲注
request登录记得这个参数
admin' and ascii(substr(database(),1,1)) > 110 #
还是看报错显示
脚本暴力破解
16
和15一样只是闭合方式不一样
能显示,ok,用暴力破解或者sqlmap都行
17 过滤了username
说明在这里报错注入
update
还有种方法
看好了
分组功能 group by 和count或者 having配合
随机数种子是0或者中子数固定的话那么值就是固定的,向下取整都是0,*2就不是了
那么分组不就成了1和0两组,我天,那用count统计分别多少个0建立虚表,,试试
主键重复?
为啥,ok来看看,下面是我老师写的一段笔记
这个整合然后计数的过程中,中间发生了什么我们是必须要明白的。
首先mysql遇到该语句时会建立一个虚拟表。该虚拟表有两个字段,一个是分组的key,一个是计数值count0。也就对应于实验中的user nome fПcountO。
然后在查询数据的时候,首先查看该虚拟表中是否存在该分组,如果存在那么计数值加1,不存在则新建该分组
然后mysq!官方有给过提示,就是查询的时候如果使用rand()的话,该值会被计算多次,那这个"被计算多次"到底是什么意思,就是在使用group by的时候fioor(rand(0)2)会被执行一次,如果虚表不存在记录,插入虚表的时候会再被执行一次,我们来看下oor(rand(0)2)报错的过程就知道了,从上面的的数使用中可以看到在一次多记录的查询过程中介oor(rand(0)2)的值是定性的,为011011(这个顺序很重要),报错实际上就是/loor(rand(0)2)被计算多次导致的,
select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)as y;
那这样比如第一次插入没有,主键插入的是root@localhost,到下次有了的话那就会报错,爆出主键重复,但是查询三次,数据至少要三条,再用group by将数据整合
18
username 和passwd都被过滤了
一个浏览器版本一个客户端ip
看代码注入点,注册登录就行了
burpsuit抓包改就行
发现
看那个能注入
发现这里x-forwarded-for不行
那就user-agnt试试
诶嘿可以,两种方法要么闭合要注释,但是注释的话后面的参数需要跟不要丢,不然小心报错
闭合
注释
19
改这里就可以了
20
这里没有过滤
代码原理就是先设置cookie,将cookie取出来,再select查询,由于存的时候和取的时候都没过滤,那就出问题了
21
base64编码
多了编解码的过程
说明注入语句要编码,因为它要解码
22
跟21的区别就是闭合不一样
23
不让注释,那直接闭合
二次注入
数据库往外取的时候没有过滤
也就是存的时候没办法注入,但是取的时候可以注入
二次注入的特点就是可以修改任意用户的密码
看代码
那就只需要更新现在写的密码
后面闭合就好了
那就注册用户 admin’#,单引号一闭合,后面的被注释,admin密码就被修改了
admin’# 这样当然可以注册,但是’#就把后面注释掉了