Partial path traversal vulnerability in Support Bundle feature of Graylog
Description
Graylog is a free and open log management platform. A partial path traversal vulnerability exists in Graylog's Support Bundle feature. The vulnerability is caused by incorrect user input validation in an HTTP API resource. Graylog's Support Bundle feature allows an attacker with valid Admin role credentials to download or delete files in sibling directories of the support bundle directory. The default data_dir in operating system packages (DEB, RPM) is set to /var/lib/graylog-server. The data directory for the Support Bundle feature is always <data_dir>/support-bundle. Due to the partial path traversal vulnerability, an attacker with valid Admin role credentials can read or delete files in directories that start with a /var/lib/graylog-server/support-bundle directory name. The vulnerability would allow the download or deletion of files in the following example directories: /var/lib/graylog-server/support-bundle-test and /var/lib/graylog-server/support-bundlesdirectory. For the Graylog Docker images, the data_dir is set to /usr/share/graylog/data by default. This vulnerability is fixed in Graylog version 5.1.3 and later. Users are advised to upgrade. Users unable to upgrade should block all HTTP requests to the following HTTP API endpoints by using a reverse proxy server in front of Graylog. GET /api/system/debug/support/bundle/download/{filename} and DELETE /api/system/debug/support/bundle/{filename}.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.graylog2:graylog2-serverMaven | >= 5.1.0, < 5.1.3 | 5.1.3 |
Affected products
1- Range: >= 5.1.0, < 5.1.3
Patches
102b8792e6f4bMerge pull request from GHSA-2q4p-f6gf-mqr5
3 files changed · +40 −4
changelog/unreleased/ghsa-2q4p-f6gf-mqr5.toml+2 −0 added@@ -0,0 +1,2 @@ +type = "security" +message = "Fix partial path traversal vulnerability in Support Bundle feature. [GHSA-2q4p-f6gf-mqr5](https://github.com/Graylog2/graylog2-server/security/advisories/GHSA-2q4p-f6gf-mqr5)"
graylog2-server/src/main/java/org/graylog2/rest/resources/system/debug/bundle/SupportBundleService.java+5 −4 modified@@ -500,7 +500,7 @@ public List<BundleFile> listBundles() { } public void downloadBundle(String filename, OutputStream outputStream) throws IOException { - ensureFileWithinBundleDir(filename); + ensureFileWithinBundleDir(bundleDir, filename); try { final Path filePath = bundleDir.resolve(filename); @@ -512,14 +512,15 @@ public void downloadBundle(String filename, OutputStream outputStream) throws IO } } - private void ensureFileWithinBundleDir(String filename) throws IOException { - if (!bundleDir.resolve(filename).toFile().getCanonicalPath().startsWith(bundleDir.toFile().getCanonicalPath())) { + @VisibleForTesting + void ensureFileWithinBundleDir(Path bundleDir, String filename) { + if (!bundleDir.resolve(filename).toAbsolutePath().normalize().startsWith(bundleDir.toAbsolutePath().normalize())) { throw new NotFoundException(); } } public void deleteBundle(String filename) throws IOException { - ensureFileWithinBundleDir(filename); + ensureFileWithinBundleDir(bundleDir, filename); final Path filePath = bundleDir.resolve(filename); Files.delete(filePath); }
graylog2-server/src/test/java/org/graylog2/rest/resources/system/debug/bundle/SupportBundleServiceTest.java+33 −0 modified@@ -21,17 +21,22 @@ import org.graylog2.shared.bindings.providers.ObjectMapperProvider; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import javax.ws.rs.NotFoundException; import java.nio.file.Path; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.List; import java.util.concurrent.ExecutorService; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; @ExtendWith(MockitoExtension.class) public class SupportBundleServiceTest { @@ -95,4 +100,32 @@ public void testLogSizeLimiterWithSpaceForOneZippedFile() { assertThat(shrinkedList).hasSize(3); assertThat(shrinkedList).extracting(LogFile::id).contains("memory", "0", "1"); } + + @ParameterizedTest + @ValueSource(strings = {"/tmp/safe_dir", "safe_dir", "../safe_dir"}) + void ensureWithinBundleDir(String bundleDirString) throws Exception { + final var bundleDir = Path.of(bundleDirString); + + assertThatCode(() -> supportBundleService.ensureFileWithinBundleDir(bundleDir, "file.zip")) + .doesNotThrowAnyException(); + assertThatCode(() -> supportBundleService.ensureFileWithinBundleDir(bundleDir, "hello/file.zip")) + .doesNotThrowAnyException(); + assertThatCode(() -> supportBundleService.ensureFileWithinBundleDir(bundleDir, "hello/world/file.zip")) + .doesNotThrowAnyException(); + assertThatCode(() -> supportBundleService.ensureFileWithinBundleDir(bundleDir, "..file.zip")) + .doesNotThrowAnyException(); + + assertThatThrownBy(() -> supportBundleService.ensureFileWithinBundleDir(bundleDir, "/etc/file.zip")) + .isInstanceOf(NotFoundException.class); + assertThatThrownBy(() -> supportBundleService.ensureFileWithinBundleDir(bundleDir, "/etc/hello/../world/../file.zip")) + .isInstanceOf(NotFoundException.class); + assertThatThrownBy(() -> supportBundleService.ensureFileWithinBundleDir(bundleDir, "../file.zip")) + .isInstanceOf(NotFoundException.class); + assertThatThrownBy(() -> supportBundleService.ensureFileWithinBundleDir(bundleDir, "../../file.zip")) + .isInstanceOf(NotFoundException.class); + assertThatThrownBy(() -> supportBundleService.ensureFileWithinBundleDir(bundleDir, "../safe_dir_insecure/file.zip")) + .isInstanceOf(NotFoundException.class); + assertThatThrownBy(() -> supportBundleService.ensureFileWithinBundleDir(bundleDir, "/safe_dir_insecure/file.zip")) + .isInstanceOf(NotFoundException.class); + } }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-2q4p-f6gf-mqr5ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-41044ghsaADVISORY
- github.com/Graylog2/graylog2-server/commit/02b8792e6f4b829f0c1d87fcbf2d58b73458b938ghsax_refsource_MISCWEB
- github.com/Graylog2/graylog2-server/security/advisories/GHSA-2q4p-f6gf-mqr5ghsax_refsource_CONFIRMWEB
- go2docs.graylog.org/5-1/making_sense_of_your_log_data/cluster_support_bundle.htmghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.