VYPR
Moderate severityNVD Advisory· Published Feb 12, 2024· Updated Nov 11, 2025

Undertow: directory traversal vulnerability

CVE-2024-1459

Description

A path traversal vulnerability was found in Undertow. This issue may allow a remote attacker to append a specially-crafted sequence to an HTTP request for an application deployed to JBoss EAP, which may permit access to privileged or restricted files and directories.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A path traversal vulnerability in Undertow allows remote attackers to access restricted files on JBoss EAP by sending a crafted HTTP request.

Vulnerability

Overview

A path traversal vulnerability was identified in Undertow, the web server component used in Red Hat JBoss Enterprise Application Platform (EAP). The flaw allows a remote attacker to append a specially-crafted sequence to an HTTP request targeting an application deployed on JBoss EAP, which may bypass intended access controls and expose privileged or restricted files and directories [1].

Attack

Vector

The vulnerability is exploitable remotely without authentication. An attacker only needs to send a maliciously crafted HTTP request to an application deployed on JBoss EAP. The attack leverages a sequence that traverses the file system path, enabling the attacker to read files that should be protected. No special network position is required [1].

Impact

If successfully exploited, the attacker could gain unauthorized read access to sensitive files and directories on the server. This could include configuration files, application source code, or other data containing credentials or business secrets. The impact is primarily confidentiality breach, with potential for further escalation if exposed files contain privileged information [1].

Mitigation

Red Hat has released security updates to address this vulnerability. The fix is included in RHSA-2024:1674 (for JBoss EAP 7 on RHEL 7), RHSA-2024:1675 (for RHEL 8), and RHSA-2024:1676 (for RHEL 9) [2][3][4]. Users are strongly advised to apply these updates to their JBoss EAP installations.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
io.undertow:undertow-coreMaven
< 2.2.31.Final2.2.31.Final
io.undertow:undertow-coreMaven
>= 2.3.0.Alpha1, < 2.3.12.Final2.3.12.Final

Affected products

2

Patches

2
54f3e4325425

Merge pull request #1556 from fl4via/UNDERTOW-2339

https://github.com/undertow-io/undertowFlavia RainoneFeb 21, 2024via ghsa
3 files changed · +63 0
  • core/src/main/java/io/undertow/server/protocol/http/HttpRequestParser.java+23 0 modified
    @@ -388,6 +388,10 @@ private void sanitazeListTypeHeaders(final HttpServerExchange builder) {
         private static final int IN_PATH = 4;
         private static final int HOST_DONE = 5;
     
    +    private static final int PATH_SEGMENT_START = 0;
    +    private static final int PATH_DOT_SEGMENT = 1;
    +    private static final int PATH_NON_DOT_SEGMENT = 2;
    +
         /**
          * Parses a path value
          *
    @@ -403,6 +407,8 @@ final void handlePath(ByteBuffer buffer, ParseState state, HttpServerExchange ex
             int canonicalPathStart = state.pos;
             boolean urlDecodeRequired = state.urlDecodeRequired;
     
    +        int pathSubState = 0;
    +
             while (buffer.hasRemaining()) {
                 char next = (char) (buffer.get() & 0xFF);
                 if(!allowUnescapedCharactersInUrl && !ALLOWED_TARGET_CHARACTER[next]) {
    @@ -426,6 +432,11 @@ final void handlePath(ByteBuffer buffer, ParseState state, HttpServerExchange ex
                     state.urlDecodeRequired = urlDecodeRequired;
                     // store at canonical path the partial path parsed up until here
                     state.canonicalPath.append(stringBuilder.substring(canonicalPathStart));
    +                if (parseState == IN_PATH && pathSubState == PATH_DOT_SEGMENT) {
    +                    // Inside a dot-segment (".", ".."), we don't want to allow removal of the ';' character from
    +                    // the path. This is to avoid path traversal issues - "/..;" should not be treated as "/..".
    +                    state.canonicalPath.append(";");
    +                }
                     state.stringBuilder.append(";");
                     // set position to end of path (possibly start of parameter name)
                     state.pos = state.stringBuilder.length();
    @@ -459,6 +470,18 @@ final void handlePath(ByteBuffer buffer, ParseState state, HttpServerExchange ex
                     } else if (next == '/' && parseState != HOST_DONE) {
                         parseState = IN_PATH;
                     }
    +
    +                // This is helper state that tracks if the parser is currently in a path dot-segment (".", "..") or not.
    +                if (parseState == IN_PATH) {
    +                    if (next == '/') {
    +                        pathSubState = PATH_SEGMENT_START;
    +                    } else if (next == '.' && (pathSubState == PATH_SEGMENT_START || pathSubState == PATH_DOT_SEGMENT)) {
    +                        pathSubState = PATH_DOT_SEGMENT;
    +                    } else {
    +                        pathSubState = PATH_NON_DOT_SEGMENT;
    +                    }
    +                }
    +
                     stringBuilder.append(next);
                 }
     
    
  • core/src/main/java/io/undertow/server/protocol/http/ParseState.java+1 0 modified
    @@ -142,6 +142,7 @@ public void reset() {
             this.leftOver = 0;
             this.urlDecodeRequired = false;
             this.stringBuilder.setLength(0);
    +        this.canonicalPath.setLength(0);
             this.nextHeader = null;
             this.nextQueryParam = null;
             this.mapCount = 0;
    
  • core/src/test/java/io/undertow/server/protocol/http/SimpleParserTestCase.java+39 0 modified
    @@ -676,6 +676,45 @@ public void testNonEncodedAsciiCharactersExplicitlyAllowed() throws UnsupportedE
             Assert.assertEquals("/bår", result.getRequestURI()); //not decoded
         }
     
    +    @Test
    +    public void testDirectoryTraversal() throws Exception {
    +        byte[] in = "GET /path/..;/ HTTP/1.1\r\n\r\n".getBytes();
    +        ParseState context = new ParseState(10);
    +        HttpServerExchange result = new HttpServerExchange(null);
    +        HttpRequestParser.instance(OptionMap.EMPTY).handle(ByteBuffer.wrap(in), context, result);
    +        Assert.assertEquals("/path/..;/", result.getRequestURI());
    +        Assert.assertEquals("/path/..;/", result.getRequestPath());
    +        Assert.assertEquals("/path/..;/", result.getRelativePath());
    +        Assert.assertEquals("", result.getQueryString());
    +
    +        in = "GET /path/../ HTTP/1.1\r\n\r\n".getBytes();
    +        context = new ParseState(10);
    +        result = new HttpServerExchange(null);
    +        HttpRequestParser.instance(OptionMap.EMPTY).handle(ByteBuffer.wrap(in), context, result);
    +        Assert.assertEquals("/path/../", result.getRequestURI());
    +        Assert.assertEquals("/path/../", result.getRequestPath());
    +        Assert.assertEquals("/path/../", result.getRelativePath());
    +        Assert.assertEquals("", result.getQueryString());
    +
    +        in = "GET /path/..?/ HTTP/1.1\r\n\r\n".getBytes();
    +        context = new ParseState(10);
    +        result = new HttpServerExchange(null);
    +        HttpRequestParser.instance(OptionMap.EMPTY).handle(ByteBuffer.wrap(in), context, result);
    +        Assert.assertEquals("/path/..", result.getRequestURI());
    +        Assert.assertEquals("/path/..", result.getRequestPath());
    +        Assert.assertEquals("/path/..", result.getRelativePath());
    +        Assert.assertEquals("/", result.getQueryString());
    +
    +        in = "GET /path/..~/ HTTP/1.1\r\n\r\n".getBytes();
    +        context = new ParseState(10);
    +        result = new HttpServerExchange(null);
    +        HttpRequestParser.instance(OptionMap.EMPTY).handle(ByteBuffer.wrap(in), context, result);
    +        Assert.assertEquals("/path/..~/", result.getRequestURI());
    +        Assert.assertEquals("/path/..~/", result.getRequestPath());
    +        Assert.assertEquals("/path/..~/", result.getRelativePath());
    +        Assert.assertEquals("", result.getQueryString());
    +    }
    +
     
         private void runTest(final byte[] in) throws BadRequestException {
             runTest(in, "some value");
    
40bb3314f013

[UNDERTOW-2339] CVE-2024-1459 Path segment "/..;" should not be treated as "/.."

https://github.com/undertow-io/undertowTomas HofmanJan 31, 2024via ghsa
3 files changed · +63 0
  • core/src/main/java/io/undertow/server/protocol/http/HttpRequestParser.java+23 0 modified
    @@ -372,6 +372,10 @@ private void handleStateful(ByteBuffer buffer, ParseState currentState, HttpServ
         private static final int IN_PATH = 4;
         private static final int HOST_DONE = 5;
     
    +    private static final int PATH_SEGMENT_START = 0;
    +    private static final int PATH_DOT_SEGMENT = 1;
    +    private static final int PATH_NON_DOT_SEGMENT = 2;
    +
         /**
          * Parses a path value
          *
    @@ -387,6 +391,8 @@ final void handlePath(ByteBuffer buffer, ParseState state, HttpServerExchange ex
             int canonicalPathStart = state.pos;
             boolean urlDecodeRequired = state.urlDecodeRequired;
     
    +        int pathSubState = 0;
    +
             while (buffer.hasRemaining()) {
                 char next = (char) (buffer.get() & 0xFF);
                 if(!allowUnescapedCharactersInUrl && !ALLOWED_TARGET_CHARACTER[next]) {
    @@ -410,6 +416,11 @@ final void handlePath(ByteBuffer buffer, ParseState state, HttpServerExchange ex
                     state.urlDecodeRequired = urlDecodeRequired;
                     // store at canonical path the partial path parsed up until here
                     state.canonicalPath.append(stringBuilder.substring(canonicalPathStart));
    +                if (parseState == IN_PATH && pathSubState == PATH_DOT_SEGMENT) {
    +                    // Inside a dot-segment (".", ".."), we don't want to allow removal of the ';' character from
    +                    // the path. This is to avoid path traversal issues - "/..;" should not be treated as "/..".
    +                    state.canonicalPath.append(";");
    +                }
                     state.stringBuilder.append(";");
                     // set position to end of path (possibly start of parameter name)
                     state.pos = state.stringBuilder.length();
    @@ -443,6 +454,18 @@ final void handlePath(ByteBuffer buffer, ParseState state, HttpServerExchange ex
                     } else if (next == '/' && parseState != HOST_DONE) {
                         parseState = IN_PATH;
                     }
    +
    +                // This is helper state that tracks if the parser is currently in a path dot-segment (".", "..") or not.
    +                if (parseState == IN_PATH) {
    +                    if (next == '/') {
    +                        pathSubState = PATH_SEGMENT_START;
    +                    } else if (next == '.' && (pathSubState == PATH_SEGMENT_START || pathSubState == PATH_DOT_SEGMENT)) {
    +                        pathSubState = PATH_DOT_SEGMENT;
    +                    } else {
    +                        pathSubState = PATH_NON_DOT_SEGMENT;
    +                    }
    +                }
    +
                     stringBuilder.append(next);
                 }
     
    
  • core/src/main/java/io/undertow/server/protocol/http/ParseState.java+1 0 modified
    @@ -142,6 +142,7 @@ public void reset() {
             this.leftOver = 0;
             this.urlDecodeRequired = false;
             this.stringBuilder.setLength(0);
    +        this.canonicalPath.setLength(0);
             this.nextHeader = null;
             this.nextQueryParam = null;
             this.mapCount = 0;
    
  • core/src/test/java/io/undertow/server/protocol/http/SimpleParserTestCase.java+39 0 modified
    @@ -676,6 +676,45 @@ public void testNonEncodedAsciiCharactersExplicitlyAllowed() throws UnsupportedE
             Assert.assertEquals("/bår", result.getRequestURI()); //not decoded
         }
     
    +    @Test
    +    public void testDirectoryTraversal() throws Exception {
    +        byte[] in = "GET /path/..;/ HTTP/1.1\r\n\r\n".getBytes();
    +        ParseState context = new ParseState(10);
    +        HttpServerExchange result = new HttpServerExchange(null);
    +        HttpRequestParser.instance(OptionMap.EMPTY).handle(ByteBuffer.wrap(in), context, result);
    +        Assert.assertEquals("/path/..;/", result.getRequestURI());
    +        Assert.assertEquals("/path/..;/", result.getRequestPath());
    +        Assert.assertEquals("/path/..;/", result.getRelativePath());
    +        Assert.assertEquals("", result.getQueryString());
    +
    +        in = "GET /path/../ HTTP/1.1\r\n\r\n".getBytes();
    +        context = new ParseState(10);
    +        result = new HttpServerExchange(null);
    +        HttpRequestParser.instance(OptionMap.EMPTY).handle(ByteBuffer.wrap(in), context, result);
    +        Assert.assertEquals("/path/../", result.getRequestURI());
    +        Assert.assertEquals("/path/../", result.getRequestPath());
    +        Assert.assertEquals("/path/../", result.getRelativePath());
    +        Assert.assertEquals("", result.getQueryString());
    +
    +        in = "GET /path/..?/ HTTP/1.1\r\n\r\n".getBytes();
    +        context = new ParseState(10);
    +        result = new HttpServerExchange(null);
    +        HttpRequestParser.instance(OptionMap.EMPTY).handle(ByteBuffer.wrap(in), context, result);
    +        Assert.assertEquals("/path/..", result.getRequestURI());
    +        Assert.assertEquals("/path/..", result.getRequestPath());
    +        Assert.assertEquals("/path/..", result.getRelativePath());
    +        Assert.assertEquals("/", result.getQueryString());
    +
    +        in = "GET /path/..~/ HTTP/1.1\r\n\r\n".getBytes();
    +        context = new ParseState(10);
    +        result = new HttpServerExchange(null);
    +        HttpRequestParser.instance(OptionMap.EMPTY).handle(ByteBuffer.wrap(in), context, result);
    +        Assert.assertEquals("/path/..~/", result.getRequestURI());
    +        Assert.assertEquals("/path/..~/", result.getRequestPath());
    +        Assert.assertEquals("/path/..~/", result.getRelativePath());
    +        Assert.assertEquals("", result.getQueryString());
    +    }
    +
     
         private void runTest(final byte[] in) throws BadRequestException {
             runTest(in, "some value");
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

15

News mentions

0

No linked articles in our index yet.