漏洞学习--SQL注入篇

  • 前言
  • 前期准备
  • Burp简单使用
  • sql注入概念
  • Pikachu SQL注入练习
        • 数字型注入
        • 字符型注入
        • 搜索型注入
        • insert/update注入
        • delete注入
        • http header注入
        • 盲注(base on boolian)
        • 盲注(base on time)
        • 宽字节注入

前言

趁着假期还没结束,写博客记录自己网络安全学习经历,也当记笔记方便以后复习。卷起来兄弟们!

前期准备

1.安装抓包工具Burp
Burp中文版安装教程

2.下载并配置phpstudy
Pikachu安装教程

3.下载本地靶场Pikachu
Pikachu安装教程

4.下载并配置火狐浏览器Firefox
火狐浏览器配置代理

Burp简单使用

如图所示,开始对火狐浏览器数据包进行拦截,点击放包,浏览器才能正常访问页面。在代理-选项中可以进行设置,设置中注意允许拦截本地数据包。截取到了数据包后可以发送到重发器修改后发送,查看响应。

sql注入概念

SQL实例可以参考下面这篇博客
sql注入详解
1.什么是sql注入?
SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至篡改数据库。
例如,从网页输入的账号密码信息未经过滤,直接带入数据库中。

2.sql注入步骤?
1、首先需要测试是否有注入点,可以使用漏洞扫描工具,也可以手动尝试构造测试语句x’ payload。

2、然后通过注入点获取信息

环境信息:数据库类型,数据库版本,操作系统版本,用户信息等。
数据库信息:数据库名称,数据库表,表字段,字段内容(加密内容破解)

注:需要知道操作系统和数据库类型,是因为操作系统类型会影响大小写,例如Windows系统在命令行中不区分大小写,而Linux系统是区分的。不同数据库(Mysql,Oracle)的语言也有差别。

3、最后,通过之前的payload得到了足够信息后,就可以进行SQL注入,获取系统权限。

3.如何防范SQL注入?
1、检查变量数据类型和格式

如果你的SQL语句是类似where id={$id}这种形式,数据库里所有的id都是数字,那么就应该在SQL被执行前,检查确保变量id是int类型;如果是接受邮箱,那就应该检查并严格确保变量一定是邮箱的格式,其他的类型比如日期、时间等也是一个道理。总结起来:只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。

2、过滤特殊符号

对于无法确定固定格式的变量,一定要进行特殊符号过滤或转义处理。

3、绑定变量,使用预编译语句

MySQL的mysqli驱动提供了预编译语句的支持,不同的程序语言,都分别有使用预编译语句的方法。实际上,绑定变量使用预编译语句是预防SQL注入的最佳方式,使用预编译的SQL语句语义不会发生改变,在SQL语句中,变量用问号?表示,黑客即使本事再大,也无法改变SQL语句的结构。

Pikachu SQL注入练习

这是我当时参考学习的博客
Pikachu通关教程
Pikachu通关教程
前面几个题简单介绍一下思路

数字型注入

提交方式为post,数据放置在HTML HEADER内提交。
根据返回结果,判断语句为

select 字段1,字段2 from 表名 where id = 1

利用Burp进行抓包,修改id为 id or 1=1,将会遍历返回所有用户名和邮箱信息

字符型注入

提交方式为get,通过URL提交数据。
根据返回结果,判断语句为

select 字段1,字段2 from 表名 where username = 'x';

我们要构成一个合法闭合。字符串需要加单引号才合法。我们需要把前后两个单引号注释掉。输入x’ or 1=1#第一个单引号会将前面的单引号闭合,后面的#号会注释掉后面的单引号。

搜索型注入

也是想办法形成合法闭合,这里我直接查看代码

这里得到sql语句

select from 表名 where username like ' %k% ';

这是修改后的sql语句,多了一个%

select from 表名 where username like ' %x%'or 1=1 #% ';

xx型注入:查看后端代码,输入xx’) or 1=1 #

基于报错信息的获取

基于报错的信息获取------三个常用的用来报错的函数
updatexml() :函数是MYSQL对XML文档数据进行查询和修改的XPATH函数。
extractvalue():函数也是MYSQL对XML文档数据进行查询的XPATH函数。
floor(): MYSQL中用来取整的函数。

还是利用字符型输入那道题,构造输入语句,返回数据库版本号

一般为方便阅读,加上十六进制的分隔符~,其十六进制为0x7e

kobe' and updatexml(1,concat(0x7e,version()),0)#

再尝试构造语句逐一获取表名,limit 0,1表示从第0条记录位置开始,获取1条记录,可以用limit x,1 逐一获取表名

kobe' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu' limit 0,1)),0)#


类似的,可以用以下语句获取用户名

kobe' and updatexml(1,concat(0x7e,(select username from users limit 0,1)),0)#

知道了一个用户名admin,接下来就可以获取密码,由于updatexml返回值有32位长度限制,所以得去掉~,将0x7e改成0。

kobe' and updatexml(1,concat(0x7e,(select password from users where username='admin' limit 0,1)),0)#


MD5解密后获取密码 MD5在线解密工具

insert/update注入

和刚刚的逻辑类似,点击注册,在用户处填写下面语句,密码随意

wake' or updatexml(1,concat(0x7e,database()),0) or '

delete注入

先输几条留言试试,这里需要用到Burp抓包了,开启拦截,删除留言。

显然,驻入点是id,将数据转发重发器,并修改
id=56 or updatexml(1,concat(0x7e,database()),0)
这里需要进行URL特殊字符转码,发送,返回数据库名,搞定

http header注入

请求头中常见信息的有:

Accept:浏览器可接受的MIME类型
Cookie:这是最重要的请求头信息之一
Host:初始URL中的主机和端口
Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面
User-agent:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用

如何判断注入点呢?

有些时候,后台开发人员为了验证客户端头信息(比如常用的cookie验证)
或者通过http header头信息获取客户端的一些资料,比如useragent、accept字段等。
会对客户端的http header信息进行获取并使用SQL进行处理

那么我们这里主要对Cookie以及User-agent这两个地方进行检测

根据提示的账号密码登录,发现返回请求头中信息。用Burp抓包试试,
将User-agent的值改成 ’ 返回了数据库报错语句,说明这里可以注入。


接下来对User-agent故技重施

Payload:firefox ' or updatexml(1,concat(0x7e,database()),0) or '


再试试cookie注入,在admin后面加 ’ 试试

	You have an error in your SQL syntax;

看到这句报错就没问题了,用上payload试试:

admin' or updatexml(1,concat(0x7e,database()),0) or '

盲注(base on boolian)

盲注的概念:
开发人员把数据库报错信息进行处理,使得信息不会返回前端页面,这样就使我们想要通过union注入或报错注入的攻击方式难以实现。
当不显示报错信息的时候,我们还可以通过盲注的方式来对数据库进行注入攻击。
盲注,就是在页面没有提供明显信息的情况执行的注入方式。
盲注又分为两种,布尔型盲注和时间型盲注。

我们这一题是基于布尔型的盲注,主要表现为

--没有报错信息
--不管是输入正确的还是错误的,都只显示两种情况(0或1)
--在正确的输入下,输入and 1=1/and 1=2发现可以判断

输入正确username看看返回结果

再试试这个,and结果前后都为真,返回正确结果,说明可以注入。

那么该怎么构造注入语句呢?需要先介绍一下需要用到的方法

Substr()截取字符串函数,string表示目标字符串
格式一:substr(string string int a, int b);a表示起始位置,b表示要截取的长度
格式二:substr(string string, int a);截取a后面所有字符

asii(string) 函数 就是返回指定字符的ascii码
我们只要记住65~90为26个大写英文字母,97~122号为26个小写英文字母

尝试构造payload为: vince ’ and asii(substr(database(),1,1))=112 #
当然这里是因为已经知道数据库名Pikachu,p对应112,如果我们不知道则可以用二分法判断:

首先取第一个字符的Ascii值同(65,122)的中间值相比较,如果正好相等,则输出
字符,如果Ascii的值大于中间值,则取中间值到最大值这一范围的中间值继续同
字符的Ascii值比较,重复上述操作直到两个值相等为止,最终输出整个字符串。

语句如下:

vince’ and asii(substr(database(),1,1))>94 #

返回正确,继续用(94,122)中间值108尝试

vince’ and asii(substr(database(),1,1))>108 #

最终找到首字母asii值为112,首字母为p,修改substr(str,a,b)中a的值,重复以上过程,得出数据库名。

盲注(base on time)

基于时间的盲注和基于布尔的盲注差别不大
学习基于时间的盲注之前先让我们学习一下,if函数、sleep函数

If(a, b, c)
a:条件语句
b:条件语句为真返回b
c:条件语句不为真返回c

sleep(int $seconds)
表示延时seconds秒执行

发现是否输入正确的用户名vince,返回结果都一样

所以之前的方法是不行了,所以用加载时间作为判断依据,构造下面payload,发现确实加载了5秒

vince ' and if(substr(database(),1,1)='p',sleep(5),null) #

不知道数据库名的情况下和基于布尔的盲注一样使用二分法得出数据库名

宽字节注入

这个需要理解,还是先了解一下有关知识

在MySQL中,有着addslashes,mysql_real_escape_string等转义函数
这些函数会将用户输入的 ‘ 转换为 \’
字符集:
utf-8(3字节)
gbk(2字节)
ascii(1字节)
PHP中编码为GBK,函数执行添加的是ASCII编码,MYSQL默认字符集是GBK等宽字节字符集。

注入原理:

首先我们的注入语句存在 ‘ 为了闭合引号,但是addslashes等函数会将 ‘ 转换为 \’
在ascii编码中就是 %27 转换为 %5c%27
%5c 就是\,%27就是 ‘
当我们输入的是 %df%27 时,addslashes函数看到 ‘会转换为 \’
就成为了 %df%5c%27,由于我们的数据库是执行gbk的编码,gbk的编码是2字节
 %df%5c 这两个字节就会被编码成 運 字,剩下 %27这个时候 ‘ 就逃逸出来了

我们这里的宽字节注入是利用的MySQL的一个特性,MySQL的在使用GBK编码的时候,会认为两个字符是一个汉字(前一个ASCII码要大于128,才到汉字的范围)。这就是MySQL的的特性,因为GBK是多字节编码,他认为两个字节代表一个汉字,所以%DF和后面的\也就是%5c中变成了一个汉字“运”,而“逃逸了出来。

尝试无果,看看源码,可以看到这里的配置是 gbk的编码

尝试payload=kobe %df ’ and 1=1 # ,还是不行,Burp抓包

浏览器对%进行了编码,成为了%25,那么我们在数据包这里改一下

name=kobe %df' or 1=1#


接下来就可以用union查询其他信息了。

更多推荐

漏洞学习--SQL注入篇