最新公告
  • 欢迎您光临悠哉网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入我们
  • APACHE OFBIZ XMLRPC远程代码执行漏洞分析

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

     漏洞分析
    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数据时,便会触发该漏洞。

    1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
    2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
    3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
    4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
    5. 如有链接无法下载、失效或广告,请联系管理员处理!
    6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
    7. 如遇到加密压缩包,默认解压密码为"www.yoozai.net",如遇到无法解压的请联系管理员!
    悠哉网 » APACHE OFBIZ XMLRPC远程代码执行漏洞分析

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    悠哉网 WWW.YOOZAI.NET
    悠哉网,用户消费首选的网站,喜欢你就悠哉一下。

    发表评论

    • 664会员总数(位)
    • 5161资源总数(个)
    • 177本周发布(个)
    • 20 今日发布(个)
    • 206稳定运行(天)

    提供最优质的资源集合

    立即查看 了解详情