x-up-devcap-post-charset Header in ASP.NET to Bypass WAFs Again!

In the past, I showed how the request encoding technique can be abused to bypass web application firewalls (WAFs). The generic WAF solution to stop this technique has been implemented by only allowing whitelisted charset via the Content-Type header or by blocking certain encoding charsets. Although WAF protection mechanisms can normally be bypassed by changing the headers slightly, I have also found a new header in ASP.NET that can hold the charset value which should bypass any existing protection mechanism using the Content-Type header.

Let me introduce to you, the one and only, the x-up-devcap-post-charset header that can be used like this:

POST /test/a.aspx?%C8%85%93%93%96%E6%96%99%93%84= HTTP/1.1
Host: target
User-Agent: UP foobar
Content-Type: application/x-www-form-urlencoded
x-up-devcap-post-charset: ibm500
Content-Length: 40
 
%89%95%97%A4%A3%F1=%A7%A7%A7%A7%A7%A7%A7

As it is shown above, the Content-Type header does not have the charset directive and the x-up-devcap-post-charset header holds the encoding’s charset instead. In order to tell ASP.NET to use this new header, the User-Agent header should start with UP!

The parameters in the above request were create by the Burp Suite HTTP Smuggler, and this request is equal to:

POST /testme87/a.aspx?HelloWorld= HTTP/1.1
Host: target
User-Agent: UP foobar
Content-Type: application/x-www-form-urlencoded
Content-Length: 14

input1=xxxxxxx

I found this header whilst I was looking for something else inside the ASP.NET Framework. Here is how ASP.NET reads the content encoding before it looks at the charset directive in the Content-Type header:

https://github.com/Microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/System/net/System/Net/HttpListenerRequest.cs#L362

if (UserAgent!=null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP")) {
	string postDataCharset = Headers["x-up-devcap-post-charset"];
	if (postDataCharset!=null && postDataCharset.Length>0) {
		try {
			return Encoding.GetEncoding(postDataCharset);

Or

https://github.com/Microsoft/referencesource/blob/08b84d13e81cfdbd769a557b368539aac6a9cb30/System.Web/HttpRequest.cs#L905

if (UserAgent != null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP")) {
	String postDataCharset = Headers["x-up-devcap-post-charset"];
	if (!String.IsNullOrEmpty(postDataCharset)) {
		try {
			return Encoding.GetEncoding(postDataCharset);

I should admit that the original technique still works on most of the WAFs out there as they have not taken the request encoding bypass technique seriously ;) However, the OWASP ModSecurity Core Rule Set (CRS) quickly created a simple rule for it at the time which they are going to improve in the future. Therefore, I disclosed this new header to Christian Folini (@ChrFolini) from CRS to create another useful rule before releasing this blog post. The pull request for the new rule is pending at https://github.com/SpiderLabs/owasp-modsecurity-crs/pull/1392.

References:
https://soroush.me/downloadable/request-encoding-to-bypass-web-application-firewalls.pdf
https://www.slideshare.net/SoroushDalili/waf-bypass-techniques-using-http-standard-and-web-servers-behaviour
https://soroush.secproject.com/blog/2018/08/waf-bypass-techniques-using-http-standard-and-web-servers-behaviour/
https://soroush.me/downloadable/Rare_ASP.NET_Request_Validation_Bypass_Using_Request_Encoding.pdf
https://github.com/nccgroup/BurpSuiteHTTPSmuggler/