文章总结: 本文分析了用友U8COA系统的SQL注入漏洞,发现PaperQueryAction.save方法未过滤参数导致注入。通过逆向路由与配置文件定位接口,利用特定appcode及isEncrypt=N参数绕过鉴权,成功构造了/u8cloud/openapi/ce.paper.query攻击路径并给出POC,建议厂商及时修复。 综合评分: 91 文章分类: 代码审计,漏洞分析,漏洞POC,WEB安全,漏洞预警
0day代码审计:某知名OA SQL注入
原创
团队内部成员供稿 团队内部成员供稿
凌曦安全
2026年2月6日 10:01 上海
本推文提供的信息、技术和方法仅用于教育目的。文中讨论的所有案例和技术均旨在帮助读者更好地理解相关安全问题,并采取适当的防护措施来保护自身系统免受攻击。
严禁将本文中的任何信息用于非法目的或对任何未经许可的系统进行测试。未经授权尝试访问计算机系统或数据是违法行为,可能会导致法律后果。
作者不对因阅读本文后采取的任何行动所造成的任何形式的损害负责,包括但不限于直接、间接、特殊、附带或后果性的损害。用户应自行承担使用这些信息的风险。我们鼓励所有读者遵守法律法规,负责任地使用技术知识,共同维护网络空间的安全与和谐。
本文来自凌曦安全团队成员-juzi供稿
一期课程&&新版课程介绍(链接直达):Zer0 sec 正式更名为 凌曦安全!新的一年,新的课程,元旦特惠来啦~
声明:
厂商已公布补丁:https://security.yonyou.com/#/noticeInfo?id=756
1.漏洞方法确认
思路
U8C是典型MVC所以我们先去找controller层,从controller层定位source点然后source一路跟到sink点即可。
source点
PaperQueryAction.save
这里sava方法接受一个PaperExecuteVO类型数组的参数传入到PaperBP().queryResult里面,根据这个方法的名字其实已经能猜到就很可能是有问题的。
PaperBP.queryResult
这边会传入到PaperResultDMO().query
PaperResultDMO().query
此处接受两个传参pk_paper和pk_questiongroup然后直接拼接传到queryVOByWhere根据分析发现没有什么过滤,很明确的存在sql注入。
2.路由分析
漏洞方法确定了那么就要知道怎么调用,完整的路由怎么访问。
因为漏洞方法是PaperQueryAction,那么就要去找doAction。
它本身没有声明的话就去看父类
一直跟就能跟到AbstractBatchSaveAction
AbstractBatchSaveAction
public abstract class AbstractBatchSaveAction<E>implements IAction,IActionExcel,IChgToJson<E> {
这里直接去看调用方法最方便
看到这个ExtSystemInvokerServlet就会很熟悉(因为它在web.xml有出现过)
是api openapi yls这三个baseurl的servlet
到这里就对路由构造就有把握了。
然后我们跟着调用一点点来看即可。
2.JSONInvokeBP读取配置文件
JSONInvokeBP.invoke
注意这边接受的数据是json格式的
invoke方法的主要作用:
主要是queryConfigVO这边
BillConfigFileParse.queryConfigVO
这边会先把传入的servername分割成多段然后path这边先是获取你本地的api路径然后读取前面分成多段的第一个加上.config例如你传入ce.paper.query 那么就是变成ce.config然后用 ServerConfigHandler 解析其中定义的 <service> 项,再通过匹配完整的 serviceName 返回对应的 APIConfigVO 配置对象
此时就可以明确必须得根据config里面的传入
<billconfig name = "ce.paper.query"> <action>u8c.bs.ce.action.PaperQueryAction</action>
</billconfig>
即这边要用ce.paper.query
然后继续根据调用继续分析
3.分析鉴权并绕过
APIOpenController.forWard
这个方法有两个鉴权的地方。
1.appcode
它会先解析基础请求信息而后进行一个合法校验是否传入U8 cloud颁发的appcode
只要我们能满足传入的路由里面带有appcode并且是如下四种就可以通过
huo esn lbsj ubz
2.isEncrypt
还会根据isEncrypt这个参数判断是否加密
String decryptData = this.decrypt(inPutData.getIndata(), inPutData.isEncrypt(), secretKey);
此时保证isEncrypt=N即可。
3.upm读取
这里还有一点
NC开发中会先写个upm文件它类似映射表
https://blog.csdn.net/mr_accompany/article/details/124608265
当服务启动之后底层的Component Container会:
-
遍历所有 jar 包和目录下的
META-INF/*.upm。 -
解析 XML 里的
interface和implementation。 -
把这些信息存入内存中的一个 Registry(注册表)。
当代码运行到图内的IInvokeWithJSon invokeFrame = ()NCLocator.getInstance().lookup(IInvokeWithJSon.class);就会去内存里面查询。
所以我们就需要去找IInvokeWithJSon对应的upm文件(该upm下面就会用到)
可以指定文件后缀节省点时间。
4.构造完整url
ExtSystemInvokerServlet.doAction
doAction 的主要流程
从请求 URI 解析对应服务名。根据服务名获取服务对象。启动线程监控并记录日志。
判断对象类型:
Servlet → 调用 service()
IHttpServletAdaptor → 调用 doAction()
普通对象 → 使用反射调用 doAction(request, response)
前置处理:preRemoteProcess()
后置处理:postRemoteProcess()
异常处理和日志记录:postErrorRemoteProcess()
根据我们前面发现的
那么我们如何确认是哪个baseurl呢?以及是用doaction还是serivice呢?
这个时候就要回头看我们唯一没有分析的调用了APIOpenServletForJSON
APIOpenServletForJSON
这里第二个问题就有回答了,implements IHttpServletAdaptor 所以是doaction
再根据前面分析的upm文件
<component name="u8cloud_openapi" remote="false" singleton="false" tx="NONE"> <implementation>u8c.server.APIOpenServletForJSON</implementation>
</component>
以及
会去根据我们传入的找upm里面对应的name 而我们的调用链中有APIOpenServletForJSON
所以对应的baseurl就是/u8cloud/openapi
所以完整的路由就是
/u8cloud/openapi/ce.paper.query?appcode=huo&isEncrypt=N
3.Poc构造
根据上面的以及
PaperExecuteVO
得到如下的Poc
由于群人数超过了200,只能邀请拉群,可以关注公众号,后台回复“加群”,获取助手绿泡泡,联系小助手进交流群
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:凌曦安全 团队内部成员供稿 团队内部成员供稿《0day代码审计:某知名OA SQL注入》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。








评论