Microsoft ASP.NET Forms Authentication Bypass

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: www.microsoft.com/net

found: 2011-10-02

by: K. Gudinavicius / SEC Consult Vulnerability Lab

m. / SEC Consult Vulnerability Lab

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: 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:

 

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:

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:

technet.microsoft.com/en-us/security/bulletin/ms11-100

 

 

Vendor contact timeline:

------------------------

2011-10-07: Contacted vendor through secure@microsoft.com

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:

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:

-------------

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

www.sec-consult.com

 

EOF K. Gudinavicius, J. Greil / @2012