You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bookkeeper.apache.org by yo...@apache.org on 2023/06/19 07:42:53 UTC
[bookkeeper] 29/31: Fix arbitrary file upload vulnerability with httpServerEnabled (#3982)
This is an automated email from the ASF dual-hosted git repository.
yong pushed a commit to branch branch-4.16
in repository https://gitbox.apache.org/repos/asf/bookkeeper.git
commit 07d3af61e5d8cb62a34adb416433064f3791114b
Author: 萧易客 <km...@live.com>
AuthorDate: Mon Jun 19 10:19:15 2023 +0800
Fix arbitrary file upload vulnerability with httpServerEnabled (#3982)
### Motivation
There is a potential arbitrary file upload vulnerability with `httpServerEnabled=true`, it's caused by `BodyHandler.create()` which returns a BodyHandler that automatically processes file upload requests.
https://github.com/apache/bookkeeper/blob/7f64246ad38981126cc8dd929ff448805a738b8f/bookkeeper-http/vertx-http-server/src/main/java/org/apache/bookkeeper/http/vertx/VertxHttpServer.java#L82
This simple command will upload a file into the `file-uploads` directory under the bookkeeper server process `CWD`.
```shell
$ curl -i --request POST \
--url http://localhost:8000/api/v1/bookie/info \
--header 'Content-Type: multipart/form-data' \
--form file=@<a-path-of-the-file>
$ ls
LICENSE NOTICE README.md bin conf deps file-uploads lib logs scripts
$ ls file-uploads
758801ba-ea1e-49e3-85d6-e510f539ea0d
```
### Changes
Create the `BodyHandler` with handleFileUploads disabled (`BodyHandler.create(false)`).
(cherry picked from commit 1809e69c6d4eeced49e76b76e979e9da96942231)
---
.../bookkeeper/http/vertx/VertxHttpServer.java | 2 +-
.../bookkeeper/http/vertx/TestVertxHttpServer.java | 76 ++++++++++++++++++++++
2 files changed, 77 insertions(+), 1 deletion(-)
diff --git a/bookkeeper-http/vertx-http-server/src/main/java/org/apache/bookkeeper/http/vertx/VertxHttpServer.java b/bookkeeper-http/vertx-http-server/src/main/java/org/apache/bookkeeper/http/vertx/VertxHttpServer.java
index ceaaf7d02d..331ed6566b 100644
--- a/bookkeeper-http/vertx-http-server/src/main/java/org/apache/bookkeeper/http/vertx/VertxHttpServer.java
+++ b/bookkeeper-http/vertx-http-server/src/main/java/org/apache/bookkeeper/http/vertx/VertxHttpServer.java
@@ -79,7 +79,7 @@ public class VertxHttpServer implements HttpServer {
CompletableFuture<AsyncResult<io.vertx.core.http.HttpServer>> future = new CompletableFuture<>();
VertxHttpHandlerFactory handlerFactory = new VertxHttpHandlerFactory(httpServiceProvider);
Router router = Router.router(vertx);
- router.route().handler(BodyHandler.create());
+ router.route().handler(BodyHandler.create(false));
HttpRouter<VertxAbstractHandler> requestRouter = new HttpRouter<VertxAbstractHandler>(handlerFactory) {
@Override
public void bindHandler(String endpoint, VertxAbstractHandler handler) {
diff --git a/bookkeeper-http/vertx-http-server/src/test/java/org/apache/bookkeeper/http/vertx/TestVertxHttpServer.java b/bookkeeper-http/vertx-http-server/src/test/java/org/apache/bookkeeper/http/vertx/TestVertxHttpServer.java
index 24a4bf9185..a0c6c4ae69 100644
--- a/bookkeeper-http/vertx-http-server/src/test/java/org/apache/bookkeeper/http/vertx/TestVertxHttpServer.java
+++ b/bookkeeper-http/vertx-http-server/src/test/java/org/apache/bookkeeper/http/vertx/TestVertxHttpServer.java
@@ -20,13 +20,20 @@
*/
package org.apache.bookkeeper.http.vertx;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import com.google.common.io.Files;
+import io.vertx.ext.web.handler.BodyHandler;
import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
@@ -109,6 +116,22 @@ public class TestVertxHttpServer {
httpServer.stopServer();
}
+ @Test
+ public void testArbitraryFileUpload() throws IOException {
+ VertxHttpServer httpServer = new VertxHttpServer();
+ HttpServiceProvider httpServiceProvider = NullHttpServiceProvider.getInstance();
+ httpServer.initialize(httpServiceProvider);
+ assertTrue(httpServer.startServer(0));
+ int port = httpServer.getListeningPort();
+ File tempFile = File.createTempFile("test-" + System.currentTimeMillis(), null);
+ Files.asCharSink(tempFile, StandardCharsets.UTF_8).write(TestVertxHttpServer.class.getName());
+ String[] filenamesBeforeUploadRequest = listFiles(BodyHandler.DEFAULT_UPLOADS_DIRECTORY);
+ HttpResponse httpResponse = sendFile(getUrl(port, HttpRouter.BOOKIE_INFO), tempFile);
+ assertEquals(HttpServer.StatusCode.OK.getValue(), httpResponse.responseCode);
+ assertArrayEquals(filenamesBeforeUploadRequest, listFiles(BodyHandler.DEFAULT_UPLOADS_DIRECTORY));
+ httpServer.stopServer();
+ }
+
private HttpResponse send(String url, HttpServer.Method method) throws IOException {
return send(url, method, "");
}
@@ -143,6 +166,59 @@ public class TestVertxHttpServer {
return new HttpResponse(responseCode, response.toString());
}
+ private HttpResponse sendFile(String url, File file) throws IOException {
+ URL obj = new URL(url);
+ HttpURLConnection con = (HttpURLConnection) obj.openConnection();
+ String boundary = "---------------------------" + System.currentTimeMillis();
+ // optional, default is GET
+ con.setRequestMethod("POST");
+ con.setDoOutput(true);
+ con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
+ try (
+ OutputStream outputStream = con.getOutputStream();
+ PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8),
+ true);
+ FileInputStream fileInputStream = new FileInputStream(file);
+ ) {
+ writer.append("--" + boundary).append("\r\n");
+ writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"file.txt\"").append("\r\n");
+ writer.append("Content-Type: text/plain").append("\r\n");
+ writer.append("\r\n");
+
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = fileInputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, bytesRead);
+ }
+
+ writer.append("\r\n");
+ writer.append("--" + boundary + "--").append("\r\n");
+ }
+ int responseCode = con.getResponseCode();
+ StringBuilder response = new StringBuilder();
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+ String inputLine;
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+ } finally {
+ if (in != null) {
+ in.close();
+ }
+ }
+ return new HttpResponse(responseCode, response.toString());
+ }
+
+ private String[] listFiles(String directory) {
+ File directoryFile = new File(directory);
+ if (!directoryFile.exists() || !directoryFile.isDirectory()) {
+ return new String[0];
+ }
+ return directoryFile.list();
+ }
+
private String getUrl(int port, String path) {
return "http://localhost:" + port + path;
}