发现有趣的数据库数据
最近我开始阅读Justin Clarke的出色著作《SQL注入攻击与防御》。在早期章节中,他讨论了Asprox僵尸网络,并解释了它如何遍历发现的数据库,寻找可以存储文本类型的列。对于找到的列,它会遍历所有行,在现有数据中添加自己的恶意JavaScript。
阅读源代码后,我认为可以很容易地调整它以寻找“有趣的”列名,而不仅仅是文本字段,并报告发现的内容而不是注入代码,于是我开始尝试。我将“有趣”定义为在审计时如果发现就会停下来查看的那种列名,例如包含以下单词的名称:
- credit
- bank
- account
- password
我最终得出的SQL如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
DECLARE @dbname nvarchar(255), @id int, @sql varchar (4000);
DECLARE table_cursor CURSOR FOR
SELECT name FROM sys.databases OPEN table_cursor FETCH NEXT FROM table_cursor INTO @dbname
WHILE (@@FETCH_STATUS = 0) BEGIN
SET @sql = 'SELECT '
SET @sql = @sql + ' ''' + @dbname + ''' AS ''Database'', '
SET @sql = @sql + 'sys.schemas.name AS ''Schema'', '
SET @sql = @sql + 'sys.objects.name AS ''Table'', '
SET @sql = @sql + 'sys.columns.name AS ''Column'', '
SET @sql = @sql + 'sys.types.name AS ''Column Type'' '
SET @sql = @sql + 'FROM ' + @dbname + '.sys.columns '
SET @sql = @sql + 'INNER JOIN ' + @dbname + '.sys.objects ON sys.objects.object_id = sys.columns.object_id '
SET @sql = @sql + 'INNER JOIN ' + @dbname + '.sys.types ON sys.types.user_type_id = sys.columns.user_type_id '
SET @sql = @sql + 'INNER JOIN ' + @dbname + '.sys.schemas ON sys.schemas.schema_id = sys.objects.schema_id '
SET @sql = @sql + 'WHERE (lower(sys.columns.name) LIKE ''%password%'' OR '
SET @sql = @sql + 'lower(sys.columns.name) LIKE ''%bank%'' OR '
SET @sql = @sql + 'lower(sys.columns.name) LIKE ''%credit%'' OR '
SET @sql = @sql + 'lower(sys.columns.name) LIKE ''%account%'') '
SET @sql = @sql + 'AND sys.objects.type=''U'';'
EXEC (@sql)
FETCH NEXT FROM table_cursor INTO @dbname
END
CLOSE table_cursor
DEALLOCATE table_cursor
|
它遍历每个数据库,然后遍历每个模式和表,并报告找到的列的位置和数据类型。这是在几个示例数据库上运行的截图。
我在MSSQL 2005上测试过,根据我所看到的,它也应该在2008上工作,但由于元列名的变化,它不适用于2000。如果有人非常希望代码在2000上工作,请告诉我,我会看看我能做些什么。我认为修改它并不需要太多努力。
接下来,我想显示表中的一些示例数据,以便审计人员不需要追逐无关的线索。我认为显示第一行、中间行和最后行就足以了解数据。我很快发现MSSQL没有MySQL中的LIMIT命令的概念,所以在一些搜索之后,我想出了这个SQL来提取我想要的数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
declare @count int;
SELECT @count=count(*) FROM AdventureWorks.Person.Contact;
SELECT 'The table has ' + CAST (@count AS nvarchar) + ' rows';
IF (@count > 3)
BEGIN
DECLARE @middle int;
SET @middle = @count / 2;
WITH tmp AS (SELECT *,ROW_NUMBER() OVER (ORDER BY PasswordHash) AS rownumber FROM AdventureWorks.Person.Contact )
SELECT * FROM tmp WHERE rownumber BETWEEN 1 and 1;
WITH tmp AS (SELECT *,ROW_NUMBER() OVER (ORDER BY PasswordHash) AS rownumber FROM AdventureWorks.Person.Contact)
SELECT * FROM tmp WHERE rownumber BETWEEN @middle and @middle;
WITH tmp AS (SELECT *,ROW_NUMBER() OVER (ORDER BY PasswordHash) AS rownumber FROM AdventureWorks.Person.Contact )
SELECT * FROM tmp WHERE rownumber BETWEEN @count and @count ;
END
ELSE
BEGIN
SELECT * FROM AdventureWorks.Person.Contact
END
|
这里选择的表是硬编码的,但将这个查询与上面的查询合并并使用上面收集的数据来生成这里使用的表和列名是相当简单的。我尝试用UNION组合SELECT,而不是返回三个单独的行,但由于某种原因失败了,这就是为什么你有三个WITH子句。这是在上面找到的一个列上运行的示例。
最后,我认为如果所有这些都自动化了就好了,所以我把它全部放到了一个很好的Metasploit模块中,我称之为MSSQL有趣数据查找器。
不幸的是,当前Metasploit中的MSSQL模块有一个错误,所以我无法拉回示例数据,我已经将大部分代码留在模块中,只是注释掉了。HDM知道这个问题,并且在修复列表中,一旦修复,我会把代码放回去并发布新版本。
另一个问题是模块不支持Windows身份验证,所以你只能使用SQL身份验证进行身份验证。据我所知,一旦底层模块支持Windows身份验证,我的模块应该自动支持。同样,这在HDM的非常长的待办事项列表中。
未来
我可以看到这如何轻松扩展到搜索MySQL数据库,虽然我从未使用过它们,但我认为PostgreSQL和Oracle也应该可能。当我有机会时,我会创建MySQL查询,另外两个留给读者作为练习!
结论
我要感谢Justin的出色著作,它激励我在找到SQLi时更仔细地查看,在最初没有找到时更努力地寻找。我只需要找到更多时间坐下来阅读,以便能够超越一半!