Our website uses cookies to enhance your browsing experience.
Accept
to the top
close form

Fill out the form in 2 simple steps below:

Your contact information:

Step 1
Congratulations! This is your promo code!

Desired license type:

Step 2
Team license
Enterprise license
** By clicking this button you agree to our Privacy Policy statement
close form
Request our prices
New License
License Renewal
--Select currency--
USD
EUR
* By clicking this button you agree to our Privacy Policy statement

close form
Free PVS‑Studio license for Microsoft MVP specialists
* By clicking this button you agree to our Privacy Policy statement

close form
To get the licence for your open-source project, please fill out this form
* By clicking this button you agree to our Privacy Policy statement

close form
I am interested to try it on the platforms:
* By clicking this button you agree to our Privacy Policy statement

close form
check circle
Message submitted.

Your message has been sent. We will email you at


If you haven't received our response, please do the following:
check your Spam/Junk folder and click the "Not Spam" button for our message.
This way, you won't miss messages from our team in the future.

>
>
>
V5611. OWASP. Potential insecure deseri…
menu mobile close menu
Analyzer diagnostics
General Analysis (C++)
General Analysis (C#)
General Analysis (Java)
Micro-Optimizations (C++)
Diagnosis of 64-bit errors (Viva64, C++)
Customer specific requests (C++)
MISRA errors
AUTOSAR errors
OWASP errors (C#)
Problems related to code analyzer
Additional information
toggle menu Contents

V5611. OWASP. Potential insecure deserialization vulnerability. Potentially tainted data is used to create an object using deserialization.

Jul 07 2021

The analyzer detected data from an external source that may be used to create an object during deserialization. Such code may cause various vulnerabilities.

Insecure deserializations form a separate risk category in OWASP Top 10 Application Security Risks 2017: A 8:2017-Insecure Deserialization.

Consider a synthetic example:

[Serializable]
public class User
{
  ....
  public bool IsAdmin { get; private set; }
  ....
}

private static User GetUserFromFile(string filePath)
{
  User user = null;
  using (var fileStream = new FileStream(filePath, FileMode.Open))
  {
    var soapFormatter = new SoapFormatter();
    user = (User) soapFormatter.Deserialize(fileStream);
  }
  return user;
}

static void Main(string[] args)
{
  Console.WriteLine("Please provide the path to the file.");

  var userInput = Console.ReadLine();
  User user = GetUserFromFile(userInput);

  if (user?.IsAdmin == true)
    // Performs actions with elevated privileges   
  else
    // Performs actions with limited privileges 
}

When running the 'Main' method, the console application will request a path to the file from a user. After you specify this path, the file contents will be deserialized into a 'User' type object. If you deserialized the object from the file successfully and the object's 'IsAdmin' property is 'true', actions with higher privileges will be made. Otherwise, privileges will be limited. Data from file is deserialized by the SOAP serializer into an object of 'User' type. Therefore, you can see the structure of an object in the file:

<SOAP-ENV:Envelope xmlns:xsi=.... 
                   xmlns:xsd=.... 
                   xmlns:SOAP-ENC=.... 
                   xmlns:SOAP-ENV=.... 
                   xmlns:clr=.... 
                   SOAP-ENV:encodingStyle=....>
<SOAP-ENV:Body>
<a1:Program_x002B_User id="ref-1" xmlns:a1=....>
<_x003C_UserId_x003E_k__BackingField>1</_x003C_UserId_x003E_k__BackingField>
<_x003C_UserName_x003E_k__.... id="ref-3">Name</_x003C_UserName_x003E_k__....>
<_x003C_IsAdmin_x003E_k__....>false</_x003C_IsAdmin_x003E_k__....>
</a1:Program_x002B_User>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

With this information, an attacker can change the value of the 'IsAdmin' property with a private setter to 'true' instead of 'false':

<_x003C_IsAdmin_x003E_k__....>true</_x003C_IsAdmin_x003E_k__....>

This way the attacker will be able to get higher privileges for the deserialized object when deserializing the object from file. As a result, the program will perform actions that were initially unavailable to the object from the file. For example, the attacker will be able to steal sensitive data or perform malicious activities that were unavailable before modifying the object from the file.

To get rid of this vulnerability, you must ensure that the attacker will not be able to know the object structure when accessing the file. To do this, use encryption of data that is written to the file. C# has a 'CryptoStream' class to help with this:

private static void SerializeAndEncryptUser(User user, 
                                            string filePath, 
                                            byte[] key, 
                                            byte[] iv)
{
  using (var fileStream = new FileStream(filePath, FileMode.CreateNew))
  {
    using (Rijndael rijndael = Rijndael.Create())
    {
      rijndael.Key = key;
      rijndael.IV = iv;

      var encryptor = rijndael.CreateEncryptor(rijndael.Key, rijndael.IV);
      using (var cryptoStream = new CryptoStream(fileStream, 
                                                 encryptor, 
                                                 CryptoStreamMode.Write))
      {
        var soapFormatter = new SoapFormatter();
        soapFormatter.Serialize(cryptoStream, user);
      }
    }
  }
}

This code encrypts the data obtained when the 'User' object is serialized before writing data to the file. When you process the file contents in the 'GetUserFromFile' method, you will need to decrypt the data before deserialization using 'CryptoStream':

private static User GetUserFromFile(string filePath, byte[] key, byte[] iv)
{
  User user = null;
  using (var fileStream = new FileStream(filePath, FileMode.Open))
  {
    using (Rijndael rijndael = Rijndael.Create())
    {
      rijndael.Key = key;
      rijndael.IV = iv;

      var decryptor = rijndael.CreateDecryptor(rijndael.Key, 
                                               rijndael.IV);
      using (var cryptoStream = new CryptoStream(fileStream, 
                                                 decryptor,
                                                 CryptoStreamMode.Read))
      {
        var soapFormatter = new SoapFormatter();
        user = (User) soapFormatter.Deserialize(cryptoStream);
      }
    }
  }
  return user;
}

This way the attacker will not know the structure and contents of the object from the file. The intruder will not be able to get higher privileges by changing the value of the 'isAdmin' property in the file. This will fix the problem of unsecure deserialization in the described example.

For more robust protection against this type of vulnerabilities, follow a few more rules, listed in the relevant OWASP Top 10 section.

The analyzer also considers methods' parameters available from other assemblies to be tainted data sources. This topic is covered in details in the article: "Why You Should Check Values of Public Methods' Parameters".

Consider an example:

public class DeserializationHelper
{
  public T DesrializeFromStream<T>(Stream stream)
  {
    T deserializedObject = default;
    using(var streamReader = new StreamReader(stream)) 
    {
      deserializedObject = DeserializeXml<T>(streamReader);
    }
    return deserializedObject;
  }

  private T DeserializeXml<T>(StreamReader streamReader)
  {
    return (T) new XmlSerializer(typeof(T)).Deserialize(streamReader);
  }
}

Here the analyzer will issue a low-certainty level warning for the 'DeserializeXml' method call when checking the 'DesrializeFromStream' method. The tool tracked the transfer of tainted data from the 'stream' parameter to the 'StreamReader' constructor. The 'Deserialize' method receives the 'streamReader' object.

You can secure from unsafe deserialization in this code in the same way as in the example above using the 'CryptoStream' class:

public class DeserializationHelper
{
  public T DesrializeFromFile<T>(Stream stream, ICryptoTransform transform)
  {
    T deserializedObject = default;
    using (var cryptoStream = new CryptoStream(stream, 
                                               transform, 
                                               CryptoStreamMode.Read))
    {
      using (var streamReader = new StreamReader(cryptoStream))
      {
        deserializedObject = DeserializeXml<T>(streamReader);
      }
    }
    return deserializedObject;
  }

  private T DeserializeXml<T>(StreamReader streamReader)
  {
    return (T) new XmlSerializer(typeof(T)).Deserialize(streamReader);
  }
}

This diagnostic is classified as:

You can look at examples of errors detected by the V5611 diagnostic.