Skip to content

Instantly share code, notes, and snippets.

@amanda-mitchell
Last active September 12, 2022 11:48
Show Gist options
  • Save amanda-mitchell/5350992 to your computer and use it in GitHub Desktop.
Save amanda-mitchell/5350992 to your computer and use it in GitHub Desktop.
Demonstrates how to decode an .ASPXAUTH cookie as generated by the current version of Microsoft's implementation of .NET. You should be able to drop this into the `Global.asax.cs` file of an ASP.NET project and be able to share cookies with code running on Mono. A few notes: (1) This assumes a machine key section that uses SHA1 validation and AE…
public void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs args)
{
var machineKeySection = (MachineKeySection) WebConfigurationManager.GetWebApplicationSection("system.web/machineKey");
var cookie = args.Context.Request.Cookies[".ASPXAUTH"];
if (cookie == null || !Regex.IsMatch(cookie.Value, "^([a-fA-F0-9]{2})+$"))
return;
var cookieBytes = ByteUtility.ToBytes(cookie.Value);
int signatureLength;
bool isValid = true;
using (var validationAlgorithm = new HMACSHA1(ByteUtility.ToBytes(machineKeySection.ValidationKey)))
{
signatureLength = validationAlgorithm.HashSize >> 3;
if (cookieBytes.Length - 1 < signatureLength)
return;
var signature = validationAlgorithm.ComputeHash(cookieBytes, 0, cookieBytes.Length - signatureLength);
for (int signatureIndex = 0; signatureIndex < signature.Length; signatureIndex++)
{
// If we break early, we'll be more vulnerable to timing attacks.
if (signature[signatureIndex] != cookieBytes[cookieBytes.Length - signatureLength + signatureIndex])
isValid = false;
}
}
byte[] decryptedBytes;
int initializationVectorLength;
using (var decryptionAlgorithm = Rijndael.Create())
{
initializationVectorLength = decryptionAlgorithm.IV.Length;
decryptionAlgorithm.Key = ByteUtility.ToBytes(machineKeySection.DecryptionKey);
using (var decryptor = decryptionAlgorithm.CreateDecryptor())
decryptedBytes = decryptor.TransformFinalBlock(cookieBytes, 0, cookieBytes.Length - signatureLength);
if (!isValid)
decryptedBytes = null;
}
if (decryptedBytes == null || decryptedBytes.Length < 51 + initializationVectorLength)
return;
FormsAuthenticationTicket ticket;
using (var stream = new MemoryStream(decryptedBytes, 8 + initializationVectorLength, decryptedBytes.Length - 28 - initializationVectorLength, false))
using (var reader = new BinaryReader(stream, Encoding.Unicode))
{
if (reader.ReadByte() != 0x01)
return;
int version = (int) reader.ReadByte();
var issueDate = new DateTime(reader.ReadInt64(), DateTimeKind.Utc);
if (reader.ReadByte() != 0xFE)
return;
var expirationDate = new DateTime(reader.ReadInt64(), DateTimeKind.Utc);
bool isPersistent = reader.ReadBoolean();
string name = ReadFormsAuthenticationTicketString(reader);
string userData = ReadFormsAuthenticationTicketString(reader);
string path = ReadFormsAuthenticationTicketString(reader);
if (reader.ReadByte() != 0xFF)
return;
ticket = new FormsAuthenticationTicket(version, name, issueDate, expirationDate, isPersistent, userData, path);
}
args.User = new GenericPrincipal(new FormsIdentity(ticket), new string[] { });
}
private static string ReadFormsAuthenticationTicketString(BinaryReader reader)
{
int stringLength = 0;
byte lengthByte;
int iterations = 0;
do
{
lengthByte = reader.ReadByte();
stringLength |= (lengthByte & 0x7F) << (iterations * 7);
iterations++;
} while ((lengthByte & (byte) 0x80) != 0);
return new string(reader.ReadChars(stringLength));
}
@memark
Copy link

memark commented Feb 14, 2018

This ByteUtility, where is it coming from?

@gautambjain
Copy link

Yes, same question. ByteUtility?

@gautambjain
Copy link

gautambjain commented Oct 10, 2018

Ok. Thanks a lot for fantastic code. It is important to mention here that ByteUtility.ToBytes converts hexadecimal string to byte array. After spending a lot of time, I discovered this.

I replaced ByteUtility.ToBytes with following method and the above code is working:


  private static byte[] ConvertHexStringToByteArray(string hexString)
        {
            if (hexString.Length % 2 != 0)
            {
                throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The binary key cannot have an odd number of digits: {0}", hexString));
            }

            byte[] data = new byte[hexString.Length / 2];
            for (int index = 0; index < data.Length; index++)
            {
                string byteValue = hexString.Substring(index * 2, 2);
                data[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
            }

            return data;
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment