APACHE OFBIZ XMLRPC远程代码执行漏洞分析

                                     [[344961]]

 漏洞分析
Apache OFBiz是一个开源的企业资源规划(ERP)系统,它提供了一系列企业应用程序来帮助企业自动化实现很多业务流程。它包含了一个能提供常见数据模型和业务进程的框架,企业内所有的应用程序都需要采用这个框架来使用常见数据、逻辑和业务处理组件。除了框架本身之外,Apache OFBiz还提供了包括会计(合同协议、票据、供应商管理、总账)、资产维护、项目分类、产品管理、设备管理、仓库管理系统(WMS)、制造执行/制造运营管理(MES/MOM)和订单处理等功能,除此之外,还实现了库存管理、自动库存补充、内容管理系统(CMS)、人力资源(HR)、人员和团队管理、项目管理、销售人员自动化、工作量管理、电子销售点(ePOS)、电子商务(电子商务)和scrum(开发)等多种功能。

Apache OFBiz使用了一系列开源技术和标准,比如Java、JavaEE、XML和SOAP。

超文本传输协议是一种请求/响应协议,该协议在 RFC 7230-7237中有详细描述。请求由客户端设备发送至服务器,服务器接收并处理请求后,会将响应发送回客户端。一个HTTP请求由请求内容、各种Header、空行和可选消息体组成:

  1. Request = Request-Line headers CRLF [message-body] 
  2.  
  3. Request-Line = Method SP Request-URI SP HTTP-Version CRLF 
  4.  
  5. Headers = *[Header] 
  6.  
  7. Header = Field-Name “:” Field-Value CRLF 

CRLF代表新的行序列回车符(CR),后跟换行符(LF),SP表示空格字符。参数将以键值对的形式通过Request- URI或message-body由客户端传递给服务器,具体将取决于Method和Content-Type头中定义的参数。比如说在下面的HTTP请求样本中,有一个名为“param”的参数,其值为“1”,使用的是POST方法:

  1. POST /my_webapp/mypage.htm HTTP/1.1 
  2.  
  3. Host: www.myhost.com 
  4.  
  5. Content-Type: application/x-www-form-urlencoded 
  6.  
  7. Content-Length: 7 
  8.  
  9.   
  10.  
  11. param=1 

Java序列化
Java支持对对象进行序列化操作,使它们额能够被表示为紧凑和可移植的字节流,然后可以通过网络传输这个字节流,并将其反序列化以供接收的servlet或applet使用。下面的示例演示了如何将一个类进行序列化并在随后提取数据:

  1. public static void main(String args[]) throws Exception{ 
  2.  
  3.    //This is the object we're going to serialize. 
  4.  
  5.    MyObject1 myObj = new MyObject1(); 
  6.  
  7.    MyObject2 myObj2 = new MyObject2(); 
  8.  
  9.    myObj2.name = "calc"
  10.  
  11.    myObj.test = myObj2; 
  12.  
  13.   
  14.  
  15.    //We'll write the serialized data to a file "object.ser" 
  16.  
  17.    FileOutputStream fos = new FileOutputStream("object.ser"); 
  18.  
  19.    ObjectOutputStream os = new ObjectOutputStream(fos); 
  20.  
  21.    os.writeObject(myObj); 
  22.  
  23.    os.close(); 
  24.  
  25.   
  26.  
  27.    //Read the serialized data back in from the file "object.ser" 
  28.  
  29.    FileInputStream fis = new FileInputStream("object.ser"); 
  30.  
  31.    ObjectInputStream ois = new ObjectInputStream(fis); 
  32.  
  33.   
  34.  
  35.    //Read the object from the data stream, and convert it back to a String 
  36.  
  37.    MyObject1 objectFromDisk = (MyObject1)ois.readObject(); 
  38.  
  39.    ois.close(); 
  40.  

所有的Java对象都需要通过Serializable或Externalizable接口来进行序列化,这个接口实现了writeObject()/writeExternal()和readObject()/readExternal()方法,它们会在对象序列化或反序列化时被调用。这些方法能够在序列化和反序列化过程中通过修改代码来实现自定义行为。

XML-RPC
XML-RPC是一个远程过程调用(RPC)协议,它使用XML对其调用进行编码,并使用HTTP作为传输机制。它是一种标准规范,并提供了现成的实现方式,允许运行在不同的操作系统和环境中。在在XML-RPC中,客户机通过向实现XML-RPC并接收HTTP响应的服务器发送HTTP请求来执行RPC。

每个XML-RPC请求都以XML元素“<methodCall></methodCall>”开头。此元素包含一个子元素“<methodName>something</methodName>”。元素“<methodName>”包含子元素“<params>”,该子元素可以包含一个或多个“<param>”元素。param XML元素可以包含许多数据类型。

如下样例所示,常见的数据类型可以被转换成对应的XML类型:

  1. <array> 
  2.  
  3.   <data> 
  4.  
  5.     <value><i4>1404</i4></value> 
  6.  
  7.     <value><string>Something here</string></value> 
  8.  
  9.     <value><i4>1</i4></value> 
  10.  
  11.   </data> 
  12.  
  13. </array> 

各种原语的编码示例如下:

  1. <boolean>1</boolean> 
  2.  
  3. <double>-12.53</double
  4.  
  5. <int>42</int

字符串的编码示例如下:

  1. <string>Hello world!</string> 

对结构体的编码示例如下:

  1. <struct> 
  2.  
  3.   <member> 
  4.  
  5.     <name>foo</name
  6.  
  7.     <value><i4>1</i4></value> 
  8.  
  9.   </member> 
  10.  
  11.   <member> 
  12.  
  13.     <name>bar</name
  14.  
  15.     <value><i4>2</i4></value> 
  16.  
  17.   </member> 
  18.  
  19. </struct> 

序列化数据由””和””XML元素包裹来表示,在Apache OFBiz中,序列化代码在org.apache.xmlrpc.parser.SerializableParser这个Java类中实现。

但是,Apache OFBiz中存在一个不安全的反序列化漏洞,这个漏洞是由于OFBiz被配置为在发送到“/webtools/control/xmlrpc”URL时使用XML-RPC拦截和转换HTTP主体中的XML数据所导致的。发送到此端点的请求最初由org.apache.ofbiz.webapp.control.RequestHandler这个Java类来处理,它确定的URL的映射方式。接下来,org.apache.ofbiz.webapp.event.XmlRpcEventHandler类将调用execute()方法,XML解析首先需要通过XMLReader类来调用parse()方法,而这个方法需要在org.apache.ofbiz.webapp.event.XmlRpcEventHandler类的getRequest()方法中调用。

XML-RPC请求中的元素将会在下列类中被解析:

  1. org.apache.xmlrpc.parser.XmlRpcRequestParser 
  2.  
  3. org.apache.xmlrpc.parser.RecursiveTypeParserImpl 
  4.  
  5. org.apache.xmlrpc.parser.MapParser 

不安全的序列化问题存在于org.apache.xmlrpc.parser.SerializableParser类的getResult()方法之中。一个未经身份验证的远程攻击者可以利用该漏洞来发送包含了定制XML Payload的恶意HTTP请求。由于OFBiz使用了存在漏洞的Apache Commons BeanUtils库和Apache ROME库,攻击者将能够使用ysoserial工具以XML格式来构建恶意Payload。该漏洞的成功利用将导致攻击者在目标应用程序中实现任意代码执行。

源代码分析
下列代码段取自Apache OFBiz v17.12.03版本,并添加了相应的注释。

org.apache.ofbiz.webapp.control.RequestHandler:

  1. public void doRequest(HttpServletRequest request, HttpServletResponse response, String chain, 
  2.  
  3. GenericValue userLogin, Delegator delegator) throws RequestHandlerException, 
  4.  
  5. RequestHandlerExceptionAllowExternalRequests { 
  6.  
  7.     ConfigXMLReader.RequestResponse eventReturnBasedRequestResponse; 
  8.  
  9.     if (!this.hostHeadersAllowed.contains(request.getServerName())) { 
  10.  
  11.       Debug.logError("Domain " + request.getServerName() + " not accepted to prevent host header injection ", module); 
  12.  
  13.       throw new RequestHandlerException("Domain " + request.getServerName() + " not accepted to prevent host header injection "); 
  14.  
  15.     }   
  16.  
  17.     boolean throwRequestHandlerExceptionOnMissingLocalRequest = EntityUtilProperties.propertyValueEqualsIgnoreCase("requestHandler""throwRequestHandlerExceptionOnMissingLocalRequest""Y", delegator); 
  18.  
  19.     long startTime = System.currentTimeMillis(); 
  20.  
  21.     HttpSession session = request.getSession(); 
  22.  
  23.     ConfigXMLReader.ControllerConfig controllerConfig = getControllerConfig(); 
  24.  
  25.     Map<String, ConfigXMLReader.RequestMap> requestMapMap = null
  26.  
  27.     String statusCodeString = null
  28.  
  29.     try { 
  30.  
  31.       requestMapMap = controllerConfig.getRequestMapMap(); 
  32.  
  33.       statusCodeString = controllerConfig.getStatusCode(); 
  34.  
  35.     } catch (WebAppConfigurationException e) { 
  36.  
  37.       Debug.logError((Throwable)e, "Exception thrown while parsing controller.xml file: ", module); 
  38.  
  39.       throw new RequestHandlerException(e); 
  40.  
  41.     } 
  42.  
  43.     if (UtilValidate.isEmpty(statusCodeString)) 
  44.  
  45.       statusCodeString = this.defaultStatusCodeString; 
  46.  
  47.     String cname = UtilHttp.getApplicationName(request); 
  48.  
  49.     String defaultRequestUri = getRequestUri(request.getPathInfo()); 
  50.  
  51.     if (request.getAttribute("targetRequestUri") == null
  52.  
  53.       if (request.getSession().getAttribute("_PREVIOUS_REQUEST_") != null) { 
  54.  
  55.         request.setAttribute("targetRequestUri", request.getSession().getAttribute("_PREVIOUS_REQUEST_")); 

org.apache.ofbiz.webapp.event.XmlRpcEventHandler:

  1. public void execute(XmlRpcStreamRequestConfig pConfig, ServerStreamConnection pConnection) throws XmlRpcException { 
  2.  
  3.     try { 
  4.  
  5.         ByteArrayOutputStream baos; 
  6.  
  7.         OutputStream initialStream; 
  8.  
  9.         Object result = null
  10.  
  11.         boolean foundError = false
  12.  
  13.         try (InputStream istream = getInputStream(pConfig, pConnection)) { 
  14.  
  15.             XmlRpcRequest request = getRequest(pConfig, istream); 
  16.  
  17.             result = execute(request); 
  18.  
  19.         } catch (Exception e) { 
  20.  
  21.             Debug.logError(e, module); 
  22.  
  23.             foundError = true
  24.  
  25.         } 
  26.  
  27.         if (isContentLengthRequired(pConfig)) { 
  28.  
  29.             baos = new ByteArrayOutputStream(); 
  30.  
  31.             initialStream = baos; 
  32.  
  33.         } else { 
  34.  
  35.             baos = null
  36.  
  37.             initialStream = pConnection.newOutputStream(); 
  38.  
  39.         }   
  40.  
  41.         try (OutputStream ostream = getOutputStream(pConnection, pConfig, initialStream)) { 
  42.  
  43.             if (!foundError) { 
  44.  
  45.                 writeResponse(pConfig, ostream, result); 
  46.  
  47.             } else { 
  48.  
  49.                 writeError(pConfig, ostream, new Exception("Failed to read XML-RPC request. Please check logs for more information")); 
  50.  
  51.             } 
  52.  
  53.         } 
  54.  
  55.         if (baos != null
  56.  
  57.         try (OutputStream dest = getOutputStream(pConfig, pConnection, baos.size())) { 
  58.  
  59.             baos.writeTo(dest); 
  60.  
  61.         } 
  62.  
  63.         pConnection.close(); 
  64.  
  65.         pConnection = null
  66.  
  67.     } catch (IOException e) { 
  68.  
  69.         throw new XmlRpcException("I/O error while processing request: " + e.getMessage(), e); 
  70.  
  71.     } finally { 
  72.  
  73.         if (pConnection != null
  74.  
  75.         try { 
  76.  
  77.             pConnection.close(); 
  78.  
  79.         } catch (IOException e) { 
  80.  
  81.             Debug.logError(e, "Unable to close stream connection"); 
  82.  
  83.         } 
  84.  
  85.     } 
  86.  
  87.  
  88.   
  89.  
  90. protected XmlRpcRequest getRequest(final XmlRpcStreamRequestConfig pConfig, InputStream pStream) throws XmlRpcException { 
  91.  
  92.     final XmlRpcRequestParser parser = 
  93.  
  94.     new XmlRpcRequestParser((XmlRpcStreamConfig)pConfig, getTypeFactory()); 
  95.  
  96.     XMLReader xr = SAXParsers.newXMLReader(); 
  97.  
  98.     xr.setContentHandler((ContentHandler)parser); 
  99.  
  100.     try { 
  101.  
  102.         xr.setFeature("http://apache.org/xml/features/disallow-doctype-decl"true); 
  103.  
  104.         xr.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd"false); 
  105.  
  106.         xr.setFeature("http://xml.org/sax/features/external-general-entities"false); 
  107.  
  108.         xr.setFeature("http://xml.org/sax/features/external-parameter-entities"false); 
  109.  
  110.         //the parsing of XML in the HTTP body starts in this function 
  111.  
  112.         xr.parse(new InputSource(pStream)); 
  113.  
  114.         //truncated 
  115.  
  116.     } 
  117.  

org.apache.xmlrpc.parser.XmlRpcRequestParser:

  1. public void endElement(String pURI, String pLocalName, String pQName) throws SAXException { 
  2.  
  3.     //XML-RPC parsing happens here 
  4.  
  5.     switch(--level) { 
  6.  
  7.         case 0: 
  8.  
  9.             break; 
  10.  
  11.         case 1: 
  12.  
  13.             if (inMethodName) { 
  14.  
  15.                 if ("".equals(pURI) && "methodName".equals(pLocalName)) { 
  16.  
  17.                     if (methodName == null) { 
  18.  
  19.                         methodName = ""
  20.  
  21.                         } 
  22.  
  23.                     } else { 
  24.  
  25.                         throw new SAXParseException("Expected /methodName, got " + new QName(pURI, pLocalName), getDocumentLocator()); 
  26.  
  27.                         }   
  28.  
  29.                     inMethodName = false
  30.  
  31.                 } else if (!"".equals(pURI) || !"params".equals(pLocalName)) { 
  32.  
  33.                     throw new SAXParseException("Expected /params, got " + new QName(pURI, pLocalName), getDocumentLocator()); 
  34.  
  35.                 } 
  36.  
  37.                 break; 
  38.  
  39.             case 2: 
  40.  
  41.                 if (!"".equals(pURI) || !"param".equals(pLocalName)) { 
  42.  
  43.                     throw new SAXParseException("Expected /param, got " + new QName(pURI, pLocalName), getDocumentLocator()); 
  44.  
  45.                 } 
  46.  
  47.                 break; 
  48.  
  49.             case 3: 
  50.  
  51.                 if (!"".equals(pURI) || !"value".equals(pLocalName)) { 
  52.  
  53.                     throw new SAXParseException("Expected /value, got " + new QName(pURI, pLocalName), getDocumentLocator()); 
  54.  
  55.                 } 
  56.  
  57.                 endValueTag(); 
  58.  
  59.                 break; 
  60.  
  61.             default
  62.  
  63.                 super.endElement(pURI, pLocalName, pQName); 
  64.  
  65.                 break; 
  66.  
  67.          }   
  68.  

org.apache.xmlrpc.parser.SerializableParser:

  1. public class SerializableParser extends ByteArrayParser { 
  2.  
  3.     public Object getResult() throws XmlRpcException { 
  4.  
  5.         try { 
  6.  
  7.             byte[] res = (byte[]) super.getResult(); 
  8.  
  9.             ByteArrayInputStream bais = new ByteArrayInputStream(res); 
  10.  
  11.             ObjectInputStream ois = new ObjectInputStream(bais); 
  12.  
  13.      
  14.  
  15.             //insecure deserialization happens here 
  16.  
  17.             return ois.readObject(); 
  18.  
  19.         } catch (IOException e) { 
  20.  
  21.             throw new XmlRpcException("Failed to read result object: " + e.getMessage(), e); 
  22.  
  23.         } catch (ClassNotFoundException e) { 
  24.  
  25.             throw new XmlRpcException("Failed to load class for result object: " + e.getMessage(), e); 
  26.  
  27.         } 
  28.  
  29.     }   
  30.  

为了触发该漏洞,攻击者需要以XML格式在HTTP请求中携带定制的序列化对象,并发送给存在漏洞的目标应用程序,当服务器端在序列化XML数据时,便会触发该漏洞。

 

文章来源网络,作者:运维,如若转载,请注明出处:https://shuyeidc.com/wp/149657.html<

(0)
运维的头像运维
上一篇2025-03-12 17:27
下一篇 2025-03-12 17:29

相关推荐

  • 个人主题怎么制作?

    制作个人主题是一个将个人风格、兴趣或专业领域转化为视觉化或结构化内容的过程,无论是用于个人博客、作品集、社交媒体账号还是品牌形象,核心都是围绕“个人特色”展开,以下从定位、内容规划、视觉设计、技术实现四个维度,详细拆解制作个人主题的完整流程,明确主题定位:找到个人特色的核心主题定位是所有工作的起点,需要先回答……

    2025-11-20
    0
  • 社群营销管理关键是什么?

    社群营销的核心在于通过建立有温度、有价值、有归属感的社群,实现用户留存、转化和品牌传播,其管理需贯穿“目标定位-内容运营-用户互动-数据驱动-风险控制”全流程,以下从五个维度展开详细说明:明确社群定位与目标社群管理的首要任务是精准定位,需明确社群的核心价值(如行业交流、产品使用指导、兴趣分享等)、目标用户画像……

    2025-11-20
    0
  • 香港公司网站备案需要什么材料?

    香港公司进行网站备案是一个涉及多部门协调、流程相对严谨的过程,尤其需兼顾中国内地与香港两地的监管要求,由于香港公司注册地与中国内地不同,其网站若主要服务内地用户或使用内地服务器,需根据服务器位置、网站内容性质等,选择对应的备案路径(如工信部ICP备案或公安备案),以下从备案主体资格、流程步骤、材料准备、注意事项……

    2025-11-20
    0
  • 如何企业上云推广

    企业上云已成为数字化转型的核心战略,但推广过程中需结合行业特性、企业痛点与市场需求,构建系统性、多维度的推广体系,以下从市场定位、策略设计、执行落地及效果优化四个维度,详细拆解企业上云推广的实践路径,精准定位:明确目标企业与核心价值企业上云并非“一刀切”的方案,需先锁定目标客户群体,提炼差异化价值主张,客户分层……

    2025-11-20
    0
  • PS设计搜索框的实用技巧有哪些?

    在PS中设计一个美观且功能性的搜索框需要结合创意构思、视觉设计和用户体验考量,以下从设计思路、制作步骤、细节优化及交互预览等方面详细说明,帮助打造符合需求的搜索框,设计前的规划明确使用场景:根据网站或APP的整体风格确定搜索框的调性,例如极简风适合细线条和纯色,科技感适合渐变和发光效果,电商类则可能需要突出搜索……

    2025-11-20
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注