eclipse-ee4j / jersey

Eclipse Jersey Project - Read our Wiki:
https://github.com/eclipse-ee4j/jersey/wiki
Other
691 stars 356 forks source link

Process dump caused by malicious response headers #5678

Open reganxt opened 5 months ago

reganxt commented 5 months ago

We've observer an issue with Jersey 2.40+。When the Jersey client is used to access the server, if the server returns a malicious request header (containing an oversized packet), the client process will be dumped, causing a DoS attack. A malicious server adds a 1 GB packet to the response header. The payload is as follows:

urls = (
    '/v1/uniagent/baseInfo/ping1','baseInfo_ping',
    '/v1/uniagent/baseInfo/ping','baseInfo_ping1'
    )

class baseInfo_ping:
    def GET(self):
        web.header('Set-Cookie', 'SESSION=ZTgwODZkYzMtOGMxYi00ODFmLTk0ZjItM2VjZmJlNDFmOGRi; 
              Path=/; Secure; HttpOnly; SameSite=None')
    text = open('data.json', 'rb').read()
    web.header('s',text)
    return '{"resultCode":"0"}'

When the client uses Jersey for HTTP connection, the response header is loaded to the memory. As a result, a dump occurs. The stack is as follows:

Thread Stack

https-jsse-nio-127.0.0.1-25920-exec-8
    at java.net.SocketInputStream.socketRead0(Ljava/io/FileDescriptor;[BIII)I (Native Method)
    at java.net.SocketInputStream.socketRead(Ljava/io/FileDescriptor;[BIII)I (SocketInputStream.java:116)
    at java.net.SocketInputStream.read([BIII)I (SocketInputStream.java:171)
    at java.net.SocketInputStream.read([BII)I (SocketInputStream.java:141)
    at sun.security.ssl.SSLSocketInputRecord.read(Ljava/io/InputStream;[BII)I (SSLSocketInputRecord.java:475)
    at sun.security.ssl.SSLSocketInputRecord.readHeader()I (SSLSocketInputRecord.java:469)
    at sun.security.ssl.SSLSocketInputRecord.bytesInCompletePacket()I (SSLSocketInputRecord.java:69)
    at sun.security.ssl.SSLSocketImpl.readApplicationRecord(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer; (SSLSocketImpl.java:1352)
    at sun.security.ssl.SSLSocketImpl.access$300(Lsun/security/ssl/SSLSocketImpl;Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer; 
          (SSLSocketImpl.java:73)
    at sun.security.ssl.SSLSocketImpl$AppInputStream.read([BII)I (SSLSocketImpl.java:969)
    at java.io.BufferedInputStream.fill()V (BufferedInputStream.java:246)
    at java.io.BufferedInputStream.read1([BII)I (BufferedInputStream.java:286)
    at java.io.BufferedInputStream.read([BII)I (BufferedInputStream.java:345)
    at sun.net.www.http.ChunkedInputStream.fastRead([BII)I (ChunkedInputStream.java:244)
    at sun.net.www.http.ChunkedInputStream.read([BII)I (ChunkedInputStream.java:689)
    at java.io.FilterInputStream.read([BII)I (FilterInputStream.java:133)
    at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read([BII)I (HttpURLConnection.java:3460)
    at org.glassfish.jersey.client.internal.HttpUrlConnector$2.read([BII)I (HttpUrlConnector.java:216)
    at org.glassfish.jersey.message.internal.EntityInputStream.read([BII)I (EntityInputStream.java:79)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream.read([BII)I 
        (ReaderInterceptorExecutor.java:273)
    at org.glassfish.jersey.message.internal.ReaderWriter.readAllBytes(Ljava/io/InputStream;)[B (ReaderWriter.java:183)
    at org.glassfish.jersey.message.internal.ReaderWriter.readFromAsString
        (Ljava/io/InputStream;Ljavax/ws/rs/core/MediaType;)Ljava/lang/String; (ReaderWriter.java:140)
    at org.glassfish.jersey.message.internal.AbstractMessageReaderWriterProvider.readFromAsString
        (Ljava/io/InputStream;Ljavax/ws/rs/core/MediaType;)Ljava/lang/String; (AbstractMessageReaderWriterProvider.java:94)
    at org.glassfish.jersey.message.internal.StringMessageProvider.readFrom
        (Ljava/lang/Class;Ljava/lang/reflect/Type; [Ljava/lang/annotation/Annotation;Ljavax/ws/rs/core/MediaType;
        Ljavax/ws/rs/core/MultivaluedMap;Ljava/io/InputStream;)Ljava/lang/String; (StringMessageProvider.java:54)
    at org.glassfish.jersey.message.internal.StringMessageProvider.readFrom(Ljava/lang/Class;Ljava/lang/reflect/Type; 
        [Ljava/lang/annotation/Annotation;Ljavax/ws/rs/core/MediaType;
        Ljavax/ws/rs/core/MultivaluedMap;Ljava/io/InputStream;)Ljava/lang/Object; (StringMessageProvider.java:36)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom
        (Ljavax/ws/rs/ext/ReaderInterceptorContext;Ljavax/ws/rs/ext/MessageBodyReader;
        Lorg/glassfish/jersey/message/internal/EntityInputStream;)Ljava/lang/Object; (ReaderInterceptorExecutor.java:233)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom
        (Ljavax/ws/rs/ext/ReaderInterceptorContext;)Ljava/lang/Object; (ReaderInterceptorExecutor.java:212)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed()Ljava/lang/Object; 
        (ReaderInterceptorExecutor.java:132)
    at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom
        (Ljava/lang/Class;Ljava/lang/reflect/Type;[Ljava/lang/annotation/Annotation;Ljavax/ws/rs/core/MediaType;
        Ljavax/ws/rs/core/MultivaluedMap;Lorg/glassfish/jersey/internal/PropertiesDelegate;
        Ljava/io/InputStream;Ljava/lang/Iterable;Z)Ljava/lang/Object; (MessageBodyFactory.java:1072)
    at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(Ljava/lang/Class;Ljava/lang/reflect/Type; 
        [Ljava/lang/annotation/Annotation;Lorg/glassfish/jersey/internal/PropertiesDelegate;)Ljava/lang/Object; 
        (InboundMessageContext.java:919)
    at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity
        (Ljava/lang/Class;Lorg/glassfish/jersey/internal/PropertiesDelegate;)Ljava/lang/Object; (InboundMessageContext.java:853)
    at org.glassfish.jersey.client.ClientResponse.readEntity(Ljava/lang/Class;)Ljava/lang/Object; (ClientResponse.java:298)
    at org.glassfish.jersey.client.InboundJaxrsResponse$1.call()Ljava/lang/Object; (InboundJaxrsResponse.java:93)
    at org.glassfish.jersey.internal.Errors.process(Ljava/util/concurrent/Callable;Z)Ljava/lang/Object; (Errors.java:292)
    at org.glassfish.jersey.internal.Errors.process(Lorg/glassfish/jersey/internal/util/Producer;Z)Ljava/lang/Object; (Errors.java:274)
    at org.glassfish.jersey.internal.Errors.process(Lorg/glassfish/jersey/internal/util/Producer;)Ljava/lang/Object; (Errors.java:205)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope
        (Lorg/glassfish/jersey/process/internal/RequestContext;Lorg/glassfish/jersey/internal/util/Producer;)
        Ljava/lang/Object; (RequestScope.java:365)
   at org.glassfish.jersey.client.InboundJaxrsResponse.runInScopeIfPossible
        (Lorg/glassfish/jersey/internal/util/Producer;)Ljava/lang/Object; (InboundJaxrsResponse.java:244)
   at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(Ljava/lang/Class;)Ljava/lang/Object; (InboundJaxrsResponse.java:90)
jansupol commented 5 months ago

Yes, Jersey tries to read the whole String when you call Response.readEntity(String.class).

It might be possible to add a property to optionally limit the size of the entity.

However, this stacktrace does not seem to show the actual issue with a long header, rather the result of full memory when the entity is read. (SSLSocketInputRecord.readHeader() actually reads 5 bytes). I am not sure we can prevent reading the headers by the JDK as the headers are parsed by HttpUrlConnection#getHeaderFields()

fantasy1713 commented 4 months ago

@jansupol which property should be used to limit the reponse header size?

jansupol commented 4 months ago

HttpUrlConnection does not seem to allow to limit the response header size.