Lucene search

K
packetstormK. GudinaviciusPACKETSTORM:111277
HistoryMar 29, 2012 - 12:00 a.m.

Microsoft ASP.NET Forms Authentication Bypass

2012-03-2900:00:00
K. Gudinavicius
packetstormsecurity.com
983

0.968 High

EPSS

Percentile

99.6%

`SEC Consult Vulnerability Lab Security Advisory < 20120328-1 >  
=======================================================================  
title: Microsoft ASP.NET Forms Authentication Bypass  
product: Microsoft .NET Framework  
vulnerable version: Microsoft .NET Framework Version:4.0.30319;   
ASP.NET Version:4.0.30319.237 and below  
fixed version: MS11-100  
CVE: CVE-2011-3416  
impact: critical  
homepage: http://www.microsoft.com/net  
found: 2011-10-02  
by: K. Gudinavicius / SEC Consult Vulnerability Lab   
m. / SEC Consult Vulnerability Lab  
https://www.sec-consult.com   
=======================================================================  
  
Vendor description:  
-------------------  
".NET is an integral part of many applications running on Windows and  
provides common functionality for those applications to run. This  
download is for people who need .NET to run an application on their  
computer. For developers, the .NET Framework provides a comprehensive  
and consistent programming model for building applications that have  
visually stunning user experiences and seamless and secure  
communication."  
  
Source: http://www.microsoft.com/net  
  
  
  
Vulnerability overview/description:  
-----------------------------------  
This advisory is an update to SEC Consult SA-20111230-0 with a detailed  
PoC section.  
  
Furthermore, SEC Consult created a PoC video which can be found here:  
  
https://www.sec-consult.com/files/20120328-1_asp.net_authentication_bypass_MS11_100.mp4  
  
  
  
The null byte termination vulnerability exists in the  
CopyStringToUnAlingnedBuffer() function of the webengine4.dll library  
used by the .NET framework. The unicode string length is determined  
using the lstrlenW function. The lstrlenW function returns the length  
of the string, in characters not including the terminating null  
character. If the unicode string containing a null byte is passed, its  
length is incorrectly calculated, so only characters before the null  
byte are copied into the buffer.  
  
This vulnerability can be leveraged into an authentication bypass  
vulnerability. Microsoft ASP.NET membership system depends on the  
FormsAuthentication.SetAuthCookie(username, false) method for certain  
functionality. By exploiting this vulnerability an attacker is able to  
log on as a different existing user with all the privileges of the  
targeted user (e.g. admin).  
  
  
  
Proof of concept:  
-----------------  
  
If developers are programming the "Microsoft way" then they will use the  
standard built-in controls for the membership management, for example,  
"CreateUserWizard" and "Login". The interesting one is  
"CreateUserWizard", which calls the CreateUser() function of the  
System.Web.Security.MembershipProvider class (Assembly: System.Web (in  
System.Web.dll)) with the parameters that the user has submitted to the  
form.  
  
The only validation (besides ASP.NET request validation) of the username  
parameter is done by the ValidateParameter() function, which basically  
checks the username length and if the username contains commas.  
  
Source code excerpt:  
  
public override MembershipUser CreateUser(string username, string  
password, string email, string passwordQuestion, string  
passwordAnswer, bool isApproved, object providerUserKey, out  
MembershipCreateStatus status) { <...> if  
(!SecUtility.ValidateParameter(ref username, true, true, true,  
0x100)) { status = MembershipCreateStatus.InvalidUserName;  
return null;  
}  
<...>  
  
Source code excerpt:   
  
internal static bool ValidateParameter(ref string param, bool  
checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize) {  
if (param == null)  
{  
return !checkForNull;  
}  
param = param.Trim();  
return (((!checkIfEmpty || (param.Length >= 1)) && ((maxSize <=  
0) || (param.Length <= maxSize))) && (!checkForCommas  
|| !param.Contains(","))); }  
  
  
The new user info is stored in the database and if everything went  
successfully (there is no duplicate username) function CreateUser()  
returns a MembershipUser object which contains basic user information.  
  
Source code excerpt:  
  
providerUserKey = new  
Guid(command.Parameters["@UserId"].Value.ToString()); time =  
time.ToLocalTime(); user = new MembershipUser(this.Name,  
username, providerUserKey, email, passwordQuestion, null,  
isApproved, false, time, time, time, time, new DateTime(0x6da,  
1, 1)); Later on, the FormsAuthentication class (Assembly:  
System.Web (in System.Web.dll)) is used, its methods  
SetAuthCookie and GetAuthCookie get called with the username  
taken from the MembershipUser object. The purpose of these  
functions is to create a FormsAuthentication ticket and set the  
cookie which will be used by the ASP.NET form authentication  
mechanism. The cookie is signed and encrypted using the machine  
key.  
  
The encryption function Encrypt() of the FormsAuthentication class  
calls the MakeTicketIntoBinaryBlob() function, which converts  
FormsAuthentication ticket to the binary data.  
  
Related stack trace:  
  
> System.Web.dll!System.Web.Security.FormsAuthentication.MakeTicketIntoBinaryBlob(System.Web.Security.FormsAuthenticationTicket  
> ticket = {System.Web.Security.FormsAuthenticationTicket}) Line  
> 534 C#  
System.Web.dll!System.Web.Security.FormsAuthentication.Encrypt(System.Web.Security.FormsAuthenticationTicket  
ticket = {System.Web.Security.FormsAuthenticationTicket}, bool  
hexEncodedTicket = true) Line 253 + 0x9 bytes C#  
System.Web.dll!System.Web.Security.FormsAuthentication.GetAuthCookie(string  
userName = "admin\0AAAAA", bool createPersistentCookie = false, string  
strCookiePath = "/", bool hexEncodedTicket = true) Line 309 + 0xd  
bytes C#  
System.Web.dll!System.Web.Security.FormsAuthentication.SetAuthCookie(string  
userName = "admin\0AAAAA", bool createPersistentCookie = false, string  
strCookiePath = "/") Line 810 + 0x62 bytes C#  
System.Web.dll!System.Web.Security.FormsAuthentication.SetAuthCookie(string  
userName = "admin\0AAAAA", bool createPersistentCookie = false) Line  
799 C#  
  
  
If the parameter's "TicketCompatibilityMode" value is set to  
"Framework20" (which is set by default:  
http://msdn.microsoft.com/en-us/library/system.web.configuration.ticketcompatibilitymode.aspx)  
the native method CookieAuthConstructTicket() from the external library  
webengine4.dll is called. The username is passed as the ticket.Name  
parameter, the result is returned in the dst buffer.  
  
Source code excerpt:  
  
private static byte[]  
MakeTicketIntoBinaryBlob(FormsAuthenticationTicket ticket) {  
<...>  
if (TicketCompatibilityMode ==  
System.Web.Configuration.TicketCompatibilityMode.Framework20) {  
num =  
System.Web.UnsafeNativeMethods.CookieAuthConstructTicket(dst,  
dst.Length, ticket.Name, ticket.UserData, ticket.CookiePath,  
pBytes, pDates); } <...>  
  
Source code excerpt:  
  
[DllImport("webengine4.dll", CharSet=CharSet.Unicode)]  
internal static extern int CookieAuthConstructTicket(byte[] pData,  
int iDataLen, string szName, string szData, string szPath, byte[]  
pBytes, long[] pDates);   
  
The disassembly of the CookieAuthConstructTicket() function  
(webengine4.dll) shows that the CopyStringToUnAlignedBuffer() function  
is used to copy unicode string (Src) into the array (a1).  
  
Pseudocode:  
  
int __stdcall CookieAuthConstructTicket(int a1, int a2, LPCWSTR Src,  
const WCHAR *a4, const WCHAR *a5, int a6, int a7) {  
int v7; // eax@8  
int result; // eax@9  
int v9; // ecx@10  
int v10; // eax@11  
int v11; // ecx@12  
int v12; // edi@13  
int v13; // eax@13  
int v14; // edi@14  
int v15; // eax@14  
  
if ( a1 && a2 >= 18 && Src && a4 && a5 && a6 && a7 )  
{  
*(_BYTE *)(a1 + 8) = *(_BYTE *)a6;  
v7 = CopyStringToUnAlingnedBuffer(Src, (void *)(a1 + 9), a2 - 9);  
if ( v7 < 2  
|| (v9 = v7 + 17, v7 + 17 > a2)  
|| (*(_DWORD *)(v7 + a1 + 9) = *(_DWORD *)a7,  
*(_DWORD *)(v7 + a1 + 13) = *(_DWORD *)(a7 + 4),  
v10 = v7 + 18,  
v9 + 1 > a2)  
|| (*(_BYTE *)(v9 + a1) = *(_BYTE *)(a6 + 1), v11 = v9 + 9, v10 +  
8 > a2) || (*(_DWORD *)(v10 + a1) = *(_DWORD *)(a7 + 8),  
*(_DWORD *)(v10 + a1 + 4) = *(_DWORD *)(a7 + 12),  
v12 = v10 + 8,  
v13 = CopyStringToUnAlingnedBuffer(a4, (void *)(a1 + v11), a2  
- v11), v13 < 2)  
|| (v14 = v13 + v12, v15 = CopyStringToUnAlingnedBuffer(a5, (void  
*)(a1 + v14), a2 - v14), v15 < 2) ) result = -2147418113;  
else  
result = v15 + v14;  
}  
else  
{  
result = -2147024809;  
}  
return result;  
}  
  
  
The analysis of the CopyStringToUnAlignedBuffer() function reveals that  
the unicode string length is determined using the lstrlenW function.  
The function returns the length of the string, in characters not  
including the terminating null character. This is the reason why the  
authentication bypass occurs. If the unicode string (in our case  
username) containing a null byte is passed, its length is incorrectly  
calculated, so only characters before the null byte are copied into the  
buffer. For example, the string "admin\0AAAAAAA" becomes "admin".  
  
Pseudocode:  
  
signed int __stdcall CopyStringToUnAlingnedBuffer(LPCWSTR Src, void  
*Dst, signed int a3) {  
int v3; // eax@4  
int v4; // esi@4  
signed int result; // eax@5  
  
if ( Src && Dst && a3 >= 2 )  
{  
v3 = lstrlenW(Src);  
v4 = 2 * v3 + 2;  
if ( v4 <= a3 )  
{  
memcpy(Dst, Src, 2 * v3 + 2);  
result = v4;  
}  
else  
{  
result = -1;  
}  
}  
else  
{  
result = 0;  
}  
return result;  
}  
  
The data returned by the CookieAuthConstructTicket() function is then  
signed and encrypted and set in the FormsAuthentication cookie, which  
is issued to the client.  
  
  
  
  
Vulnerable / tested versions:  
-----------------------------  
The vulnerability has been verified to exist in Microsoft .NET Framework  
Version:4.0.30319; ASP.NET Version:4.0.30319.237, which was the most  
recent version at the time of discovery.  
  
More information regarding affected versions is available within the  
advisory of Microsoft:  
http://technet.microsoft.com/en-us/security/bulletin/ms11-100  
  
  
Vendor contact timeline:  
------------------------  
2011-10-07: Contacted vendor through [email protected]  
2011-10-07: Vendor response, MSRC 11838  
2011-10-14: Contacted MSRC asking for status  
2011-10-15: Answer from case manager: the vulnerability will be  
addressed through a security bulletin, a timeframe is  
unknown.  
2011-11-23: Contacted MSRC asking for status  
2011-11-23: Answer from case manager: a release date of update is  
unknown, best guess would be a month before or after the  
March (2012) update cycle  
2011-12-29: Microsoft publishes out-of-band security patch MS11-100  
which also addresses this vulnerability  
2011-12-30: SEC Consult releases redacted version of advisory due to  
criticality of this issue  
2012-03-28: SEC Consult releases detailed advisory incl. PoC video in  
coordination with Microsoft  
  
  
  
Solution:  
---------  
Immediately apply the MS11-100 patch:  
http://technet.microsoft.com/en-us/security/bulletin/ms11-100  
  
  
Workaround:  
-----------  
In .NET 4.0 the vulnerability can be mitigated by setting the  
ticketCompatibilityMode attribute in the application or global  
web.config file like this:  
  
<system.web>  
<authentication mode="Forms">  
<forms ticketCompatibilityMode="Framework40" />  
</authentication>  
</system.web>  
  
  
  
Advisory URL:  
-------------  
https://www.sec-consult.com/en/advisories.html  
  
  
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
SEC Consult Unternehmensberatung GmbH  
  
Office Vienna  
Mooslackengasse 17  
A-1190 Vienna  
Austria  
  
Tel.: +43 / 1 / 890 30 43 - 0  
Fax.: +43 / 1 / 890 30 43 - 25  
Mail: research at sec-consult dot com  
https://www.sec-consult.com  
  
EOF K. Gudinavicius, J. Greil / @2012  
  
`

0.968 High

EPSS

Percentile

99.6%