Last active
September 12, 2022 11:48
-
-
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…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} |
Yes, same question. ByteUtility?
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
This
ByteUtility
, where is it coming from?