VYPR
Moderate severityNVD Advisory· Published Aug 29, 2024· Updated Aug 30, 2024

CRLF Injection in RestSharp's `RestRequest.AddHeader` method

CVE-2024-45302

Description

RestSharp is a Simple REST and HTTP API Client for .NET. The second argument to RestRequest.AddHeader (the header value) is vulnerable to CRLF injection. The same applies to RestRequest.AddOrUpdateHeader and RestClient.AddDefaultHeader. The way HTTP headers are added to a request is via the HttpHeaders.TryAddWithoutValidation method which does not check for CRLF characters in the header value. This means that any headers from a RestSharp.RequestHeaders object are added to the request in such a way that they are vulnerable to CRLF-injection. In general, CRLF-injection into a HTTP header (when using HTTP/1.1) means that one can inject additional HTTP headers or smuggle whole HTTP requests. If an application using the RestSharp library passes a user-controllable value through to a header, then that application becomes vulnerable to CRLF-injection. This is not necessarily a security issue for a command line application like the one above, but if such code were present in a web application then it becomes vulnerable to request splitting (as shown in the PoC) and thus Server Side Request Forgery. Strictly speaking this is a potential vulnerability in applications using RestSharp, not in RestSharp itself, but I would argue that at the very least there needs to be a warning about this behaviour in the RestSharp documentation. RestSharp has addressed this issue in version 112.0.0. All users are advised to upgrade. There are no known workarounds for this vulnerability.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
RestSharpNuGet
>= 107.0.0-preview.1, < 112.0.0112.0.0

Affected products

1

Patches

1
0fba5e727d24

Don't allow CRLF in headers (#2258)

https://github.com/restsharp/RestSharpAlexey ZimarevAug 29, 2024via ghsa
3 files changed · +65 34
  • src/RestSharp/Parameters/HeaderParameter.cs+55 6 modified
    @@ -13,22 +13,71 @@
     // limitations under the License.
     // 
     
    +using System.Text;
    +using System.Text.RegularExpressions;
    +
     namespace RestSharp;
     
    -public record HeaderParameter : Parameter {
    +public partial record HeaderParameter : Parameter {
         /// <summary>
         /// Instantiates a header parameter
         /// </summary>
    -    /// <param name="name">Parameter name</param>
    -    /// <param name="value">Parameter value</param>
    -    public HeaderParameter(string name, string value)
    +    /// <param name="name">Header name</param>
    +    /// <param name="value">Header value</param>
    +    /// <param name="encode">Set to true to encode header value according to RFC 2047. Default is false.</param>
    +    public HeaderParameter(string name, string value, bool encode = false)
             : base(
    -            Ensure.NotEmptyString(name, nameof(name)),
    -            Ensure.NotNull(value, nameof(value)),
    +            EnsureValidHeaderString(Ensure.NotEmptyString(name, nameof(name)), "name"),
    +            EnsureValidHeaderValue(name, value, encode),
                 ParameterType.HttpHeader,
                 false
             ) { }
     
         public new string Name  => base.Name!;
         public new string Value => (string)base.Value!;
    +
    +    static string EnsureValidHeaderValue(string name, string value, bool encode) {
    +        CheckAndThrowsForInvalidHost(name, value);
    +
    +        return EnsureValidHeaderString(GetValue(Ensure.NotNull(value, nameof(value)), encode), "value");
    +    }
    +
    +    static string EnsureValidHeaderString(string value, string type)
    +        => !IsInvalidHeaderString(value) ? value : throw new ArgumentException($"Invalid character found in header {type}: {value}");
    +
    +    static string GetValue(string value, bool encode) => encode ? GetBase64EncodedHeaderValue(value) : value;
    +
    +    static string GetBase64EncodedHeaderValue(string value) => $"=?UTF-8?B?{Convert.ToBase64String(Encoding.UTF8.GetBytes(value))}?=";
    +
    +    static bool IsInvalidHeaderString(string stringValue) {
    +        // ReSharper disable once ForCanBeConvertedToForeach
    +        for (var i = 0; i < stringValue.Length; i++) {
    +            switch (stringValue[i]) {
    +                case '\t':
    +                case '\r':
    +                case '\n':
    +                    return true;
    +            }
    +        }
    +
    +        return false;
    +    }
    +
    +    static readonly Regex PortSplitRegex = PartSplit();
    +
    +    static void CheckAndThrowsForInvalidHost(string name, string value) {
    +        if (name == KnownHeaders.Host && InvalidHost(value))
    +            throw new ArgumentException("The specified value is not a valid Host header string.", nameof(value));
    +
    +        return;
    +
    +        static bool InvalidHost(string host) => Uri.CheckHostName(PortSplitRegex.Split(host)[0]) == UriHostNameType.Unknown;
    +    }
    +
    +#if NET7_0_OR_GREATER
    +    [GeneratedRegex(@":\d+")]
    +    private static partial Regex PartSplit();
    +#else
    +    static Regex PartSplit() => new(@":\d+");
    +#endif
     }
    \ No newline at end of file
    
  • src/RestSharp/Request/RestRequestExtensions.Headers.cs+4 28 modified
    @@ -12,8 +12,6 @@
     // See the License for the specific language governing permissions and
     // limitations under the License.
     
    -using System.Text.RegularExpressions;
    -
     namespace RestSharp;
     
     public static partial class RestRequestExtensions {
    @@ -39,10 +37,8 @@ public static RestRequest AddHeader(this RestRequest request, string name, strin
         /// <param name="name">Header name</param>
         /// <param name="value">Header value</param>
         /// <returns></returns>
    -    public static RestRequest AddHeader(this RestRequest request, string name, string value) {
    -        CheckAndThrowsForInvalidHost(name, value);
    -        return request.AddParameter(new HeaderParameter(name, value));
    -    }
    +    public static RestRequest AddHeader(this RestRequest request, string name, string value)
    +        => request.AddParameter(new HeaderParameter(name, value));
     
         /// <summary>
         /// Adds a header to the request. RestSharp will try to separate request and content headers when calling the resource.
    @@ -62,10 +58,8 @@ public static RestRequest AddHeader<T>(this RestRequest request, string name, T
         /// <param name="name">Header name</param>
         /// <param name="value">Header value</param>
         /// <returns></returns>
    -    public static RestRequest AddOrUpdateHeader(this RestRequest request, string name, string value) {
    -        CheckAndThrowsForInvalidHost(name, value);
    -        return request.AddOrUpdateParameter(new HeaderParameter(name, value));
    -    }
    +    public static RestRequest AddOrUpdateHeader(this RestRequest request, string name, string value)
    +        => request.AddOrUpdateParameter(new HeaderParameter(name, value));
     
         /// <summary>
         /// Adds or updates the request header. RestSharp will try to separate request and content headers when calling the resource.
    @@ -121,22 +115,4 @@ static void CheckAndThrowsDuplicateKeys(ICollection<KeyValuePair<string, string>
                 throw new ArgumentException($"Duplicate header names exist: {string.Join(", ", duplicateKeys)}");
             }
         }
    -
    -    static readonly Regex PortSplitRegex = PartSplit();
    -
    -    static void CheckAndThrowsForInvalidHost(string name, string value) {
    -        if (name == KnownHeaders.Host && InvalidHost(value))
    -            throw new ArgumentException("The specified value is not a valid Host header string.", nameof(value));
    -
    -        return;
    -
    -        static bool InvalidHost(string host) => Uri.CheckHostName(PortSplitRegex.Split(host)[0]) == UriHostNameType.Unknown;
    -    }
    -
    -#if NET7_0_OR_GREATER
    -    [GeneratedRegex(@":\d+")]
    -    private static partial Regex PartSplit();
    -#else
    -    static Regex PartSplit() => new(@":\d+");
    -#endif
     }
    \ No newline at end of file
    
  • test/RestSharp.Tests/RequestHeaderTests.cs+6 0 modified
    @@ -174,6 +174,12 @@ public void Should_not_allow_empty_header_name() {
             var request = new RestRequest();
             Assert.Throws<ArgumentException>("name", () => request.AddHeader("", "value"));
         }
    +    
    +    [Fact]
    +    public void Should_not_allow_CRLF_in_header_value() {
    +        var request = new RestRequest();
    +        Assert.Throws<ArgumentException>(() => request.AddHeader("name", "test\r\nUser-Agent: injected header!\r\n\r\nGET /smuggled HTTP/1.1\r\nHost: insert.some.site.here"));
    +    }
     
         static Parameter[] GetHeaders(RestRequest request) => request.Parameters.Where(x => x.Type == ParameterType.HttpHeader).ToArray();
     
    

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

News mentions

0

No linked articles in our index yet.