Insecure Binary Serialization: 2018 Redux

by Bill Sempf 1. December 2020 00:00

Back in 2018, I wrote about Insecure Binary Deserialization, and I'd like to give an update here for C# Advent. Originally, OWASP had just added Insecure Binary Deserialization to the OWASP Top 10, and while the problem was primarily in Java and older serialization libraries, there were still some .NET libraries that were vulnerable.  Well, no longer. Microsoft fixed the problem by deprecating the library in .NET 5.0, and here we will go over what we found in 2018, how Microsoft made the change, and what you need to do.

What is Serialization?

 At its most straightforward, serialization is the act of taking an in-memory object and encoding it as a string. There is a bit more to it than that, but it doesn't change anything in this article, so we won't get super detailed here. Encoding an in-memory object as a string does have a myriad of uses, though, from saving game state to passing objects from server to server with REST services in JSON.

Let's use game save files as an example It is possible to save a serialized file as serialized binary instead of serialized text, but this is it very difficult to send over the internet, either via service or through a web page. If the game is standalone on PC and doesn't have to report state to a server then binary format is fine - it will appear as hex in Windows and honestly be just as editable.  But, you got to do a lot more trial and error.

If the serialized object is text, then it is just a matter of decoding it, editing it, and saving it. It's pretty straightforward. Different frameworks use different methods, and sometimes different methods within the same framework use different methods.

Let's take a look at a quick example.

If I have a class like this:

public class SerializableClass
    public string StringProperty { get; set; }
    public int IntegerProperty { get; set; }

And I use BinaryFormatter.Serialize() to make a serialized object after setting some values, then save it as a file, I get this:

Pretty slick, eh?

How can Serialization be insecure?

 This file is unsigned, has no Message Authentication Code, and is not encrypted.

That's the quick answer.  The slow answer is that this file can be altered if you know what you are doing.  I covered that pretty well in my 2018 article, so I'll let it pass here and refer you there. That said, this means that any "binary" object (which looks like a bunch of gobbligook to an inexperienced developer) it editable by an attacker. For instance, I changed a word (I'll let you guess which one) and changed the data in the program on file load:

This is just one example in C# - there are a number of examples of serializers that can become a security concern, for exactly the same reason.  The thing is, there are a number of better classes that do the work properly. We just need to use them. So that leads to - what changed.

What changed?

 TLDR: They started deprecating the classes that provide insecure serialization.

The Deets:

In .NET 5.0, Microsoft has begun deprecating the classes that provide insecure serialization.  They started with the BinaryFormatter, and the pattern will gradually be used for all serialization methods. It's easy to say "Hey don't use these methods" but it is harder to follow up.  So, as part of the Long Term Obsoletion Plan, use of the BinaryFormatter will cause a warning in your build.  Eventually, over some time, that will become an error, and then in the fullness of time (h/t Don Box) the appropriate classes and methods will be removed. The goal, of course, is to simply have developers use more secure versions of the serialization classes.

So, for instance as seen at the early pre-release documentation at, the recommendation is to use JsonSerializer instead of BinaryFormatter.  If you use BinaryFormatter, you will get warnings today, errors tomorrow, and it will eventually break your build. Over time the other insecure methods and classes will be given the same treatment.

In ASP.NET 5.0, BinaryFormatter is disabled by default.  I recommend you do not override this, especially if you have a big project that uses serialization.  Junior programmers and contractors will use BinaryFormatter because it is the tool they know, and it can sneak into your project.  Blazor also disables BinaryFormatter by default.  This is good news, because web applications are the weakest link much of the time, storing serialized objects in cookies, headers, and hidden form fields to simplify communication. An attacker can deserialize those blobs, then steal secrets or change values. You must be on the watch, and default disable is a great way to help with that.

BinaryFormatter was the first but will hardly be the last serialization feature to be deprecated.  For instance, certain features of SecureString use serialization in an insecure way, and they are on their way out too.

What you need to do.

This is a fluid situation, so the best thing you can do is keep informed.  Subscribe to the Binary Formatter Security Guide.  As time passes, the updates will show there.  You can also get involved!  Two of the key folks involved in this are beer-drinking level friends of mine, and they assure me that the commitment to community involvement is as important if not more important to the security folks as it is to the rest of the developer community.  Write some stuff!

Upgrade to .NET 5.0 as soon as you can.  Then watch your warnings.  If your code base is clean enough, break the build on warnings.  Depending on your CICD situation, you might be able to only break on certain parts of code, which is awesome.  That way, you can take advantage of the changes they are making to the framework as it happens.

Search your codebase for BinaryFormatter and replace instances with format specific instances. For instance, use BinaryReader and BinaryWriter for XML and JSON. Also, if you are working with data objects, take a look at DataContractSerializer.  All of those classes validate inputs properly.

Avoid the SoapFormatter, LosFormatter, and ObjectStateFormatter completely.  Those use the old underlying structure, and are not secure.

Finally, consider if you need to use a serialized object at all. There are a lot of better ways to solve that problem. For instance, make an indirect object reference, save the information server side and give the client the reference ID.  That's better on all accounts.

Assume always that input is untrustworthy.  Consider your risk model and then ask yourself "if someone sent in malicious input here, what could happen?"  Dig deep.  You might be surprised what you learn about your code.


AppSec | C#

Insecure Binary Deserialization

by Bill Sempf 3. December 2018 00:01

The OWASP Top 10 was updated last year, and there are a couple of new items.  One of them is Insecure Binary Deserialization.  Many of us use serialization in our applications, weather we know it or not, and through it sounds obscure it is a significant vulnerability. I talked about the bug on the Application Security Podcast earlier this year.   In 2017, Paypal was hit by a serialization bug in their JBoss middleware because of an unpatched system.  Several .NET projects were found to use the insecure BinarySerializer class in 2016 as well.  It's a very real problem.

Let's take a quick tour of serialization, look at the ways it can be attacked, and what you should do about it, here as part of the C# Advent.

What the heck is serialization anyway?

 Serialization is just saving the state of an object to a storable format.  Encoding and character sets aside, it is usually the conversion of an object in a system to text, so that it can be written to disk in one way or another.  Consider the ASP.NET Session object.  To the developer, it is just a Collection of items, but once stored and sent, it becomes part of the ViewState hidden field.

Back in the day, if we canted to move an object over the wire we had to use a binary transfer protocol, like DCOM or CORBA.  They stunk - they were hard to use and suffered from vendor lock-in.  XML, and later JSON, were eventually made the standard for data serialization, but it still didn't give us a way to move good old objects around.  

Serialization of objects had been around for a while (that's what CORBA does under the covers) but it wasn't really part of the languages we were using back in the 90s, but frameworks like the JDK and .NET changed all of that.  Serialization became commonplace for transfer and storage of the state of an application, identity information, even just a generic object we wanted to persist for some reason.

In .NET, there are a number of serialization options.  After marking an object [Serializable] one can use a member of the System.Runtime.Serialization namespace to render the object to a stable format.

For instance, here is a bit of demo code that serialized a simple class with two properties, and saves it to disk.  Keep in mind, this is vulnerable code and should not be used in production!

SerializableClass sc = new SerializableClass();
sc.StringProperty = "Hello World!";
sc.IntegerProperty = 42;
BinaryFormatter fmt = new BinaryFormatter();
using (FileStream stm = File.OpenWrite(@"c:\temp\output.stm"))
    fmt.Serialize(stm, sc);

If we take a look at output.stm in a text editor, we will see that it is, well, binary, as one would imagine.

However, this is just an encoding issue.  If we load it up in UTF-8 we see that things get a lot more interesting.

How can something like that be insecure?

It's an injection vector, just like a querystring variable or a INPUT in a web form.  An attacker can modify those values and submit them to the server.

"Wait," you are probably thinking, "doesn't the framework make sure it hasn't changed?"

Well, no.  It doesn't, and therein lies the rub.  The issue, in the case of C# and the .NET Framework, with the BinaryFormatter.  When one deserializes the object, no checks at all take place - it is left in the hands of the developer.  Since most folks don't know there is a problem, most folks don't confirm that the object is as they expect.

If the developer is depending on data in that object, then we have a real consideration.  Bypassing some controls, an attacker might be able to discover SQL Injection, Directory Injection, or even Remote Code Execution commands on the operating system.  But that's just where it starts.  With no signature confirming that the object is as we left it, nothing is stopping an attacker from reverse engineering the object, writing their own application to deserialize it, and even change its functionality.

How does one fix this?

There are two main solutions to insecure deserialization.  The first is to know your framework.  BinarySerializer, the class I used in the example, is the only serialization class that remains vulnerable to this kinds of thing.  As such, if you have to use it, don't store anything that is important in the class being serialized.  Just like a cookie, right? You know the cookie is plain text on the user's computer, so you don't put anything sensitive in it.  Right?

And I know this is a C# article, but other languages have this problem as well.In Java, the ObjectInputScream is vulnerable and it is the most commonly used serializer in the language, so watch for that.  PHP doesn't have a secure serialization route at all - the unserialize method is the only way (so far as I know) to process a serialized object.  The Ruby Marshal.load() method is designed insecure, and they even warn you of it in the documentation.  This is not unlike the BinarySerializer - it lets you run with scissors so that you can solve certain hard-to-solve problems.

So what do you do if you have to use a vulnerable serializer?  Panic.  Not not really.  Start to think in terms of a positive security model.  If you are allowing something that has been outside the boundary of your system back into your system, you should only allow expected values and reject all others.  This starts with type checking, of course, but can extend further than that.  If you are expecting a ZipCode parameter to contain five digits, then anything other than that should set off alarm bells. Input validation is your friend here. 

Alright, what if I ignore this whole thing and hope it goes away?

 In C#land, the BinarySerializer can only be used to store properties, not methods.  Therefore, actual code injection is practically impossible.  If the data is such that you don't care if the user edits it (say, a local only game state file) then ignoring the problem might not cause you any really large scale problems.

However, if you are storing anything that is used in the running of the program, especially in network connected or web applications, then you have to be very careful of possible side effects.  Just like any other input, if you allow the user to change it outside the bounds of your user interface to are begging for problems.  Say you store the contents of the shopping cart in a serialized object.  Can the user change the quantity ordered to a -1?  Would that give them a discount?

At the very least testing of any serialized objects should be part of your vulnerability assessment plan.  Any time data crosses the boundary of your application, check it!


AppSec | C#

Some neat events I'll be participating in this spring

by Bill Sempf 9. April 2018 09:35

There are some neat developer and security events this spring that I'll be speaking at or otherwise participating in, and I'd love to see all of you there!

On the morning of the 18th, I'll be talking about updated OWASP Projects at the Columbus ISSA meeting.  I know daytime meetings are weird, but come by if you can. There is a small charge for non-members.

April 26th, there is an OWASP meetup where we will be following up on Jason Kent's Docker seminar and building some cool python code. This event is at the Idea Foundry, and is a lab environment - bring your machine. I'll get everyone started, but this is mostly team coding.

In May, on the 4th, I'll be speaking about the changes to the OWASP Top 10 at Stir Trek, one of the most awesome developer events in the midwest. The content is awesome, the venue is a movie theater, and you get to watch the new Avengers movie the night before the rest of the world! NO SPOILERS.

May 14th, I have the honor of speaking at one of the most awesome security events in the midwest (I told you this was quite a spring!) It's the Central Ohio InfoSec Summit, and the content is also awesome, although there isn't a movie at the end. If you can go, do so.  It's really a great conference.

Gonna be a bust few months, but some really great events.  Make sure you catch what you can - attending these events is one of the best ways to stretch your mind and see what skills you should be working on right now.


AppSec | C#

Day 6 of C# Advent - Coding for an encrypted service

by Bill Sempf 6. December 2017 00:16

Welcome to the 6th day of the C# Advent! Let's encrypt some malware.

That sounds horrible, but in security testing, sometimes you have to use the tools of the bad guy to make sure you aren't likely to be susceptible to any attacks. There are many such tools, but a new one - one that takes instructions from an HTTP web server - was developed by Dave Kennedy of TrustedSec. Called TrevorC2, it is a Python Command and Control server with a variety of clients.  The attacker would install the client on the target workstation, and have a server delivering commands.  In this case, it is over HTTP.  And one of the clients is in C#.  And encrypted in AES.

Wait, what?  AES?  Really?  

Yes, really.  Companies look for certain strings that are common in command and control servers moving across their networks.  So, the attackers encrypt things! How do we find it then?  Well, that's not our problem at the moment - we just need to figure out how to replicate what the bad guys are doing.  So, that's the task for this day of the C# Advent - implement AES in C# that will talk nice to a Python command an control server over HTTP.  

Fortunately, most of it I have written.  The communication bits in C# are pretty straightforward, but the encryption piece is not at all.  When I started, I wanted to use System.Security.Cryptography.Aes, but guess what?  It ain't that easy.  The Python client encryption method looks like this:

    def encrypt(self, raw):
        raw = self._pad(AESCipher.str_to_bytes(raw))
        iv =
        cipher =, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw)).decode('utf-8')


My initial take was something like this for an encrypt method:

        static string Encrypt(string target)
            byte[] result = null;
                if (target == null || target.Length <= 0)
                    throw new ArgumentNullException("plainText");

                using (Aes aesAlg = Aes.Create())
                    aesAlg.Key = Cipher;
                    ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
                    using (MemoryStream msEncrypt = new MemoryStream())
                        using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                            using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                            result = msEncrypt.ToArray();
            catch(Exception e)
            return Encoding.ASCII.GetString(result);

That didn't work.  So, I did what any good senior developer would do.  I buckled down, focused, and looked for a library written by someone smarter than me. Fortunately, Adam Caudill is out there with, a C# implementation of the well known NaCl library. There's the solution I needed. is a living breathing example of how complicated encryption can be.  NaCl is a very well known implementation of the main encryption protocols ... in C.  Yeah, C.  You can use it in other languages but C is what NaCl is for.

The chem majors among us will recognize that NaCl is table salt.  Thus, Sodium.  Libsodium is a higher level library for C++ and ilk, and then Adam's implementation uses the .NET native libraries to perform the same tasks.  It's a reasonable legacy, and you should consider it if you have to write encryption code, as, well, I do right now.

Anyway, the interface is SUPER easy to use.  The SecretBox holds everything you need, including the abilityto generate the nonce required for the AES encryption, and to do the actual work.

So, my NEW method, after adding, looks like this:

        static string Encrypt(string target)
                var nonce = SecretBox.GenerateNonce();
                var result = SecretBox.Create(target, nonce, Cipher);
            catch (Exception e)
            return Encoding.ASCII.GetString(result);

Tune back in next week, after I get my Trevor server up and running, and we'll get everything configured and working. (Remember, play with malware on a network that is not your employer's network!) 



AppSec | C#

The OWASP .NET project

by Bill Sempf 31. March 2014 05:15

I'm helping OWASP with the awesome but recently neglected .NET project. There is a lot of great .NET security stuff out there <cough> troyhunt </cough> and I am helping them organize and broaden it.

There is a roadmap started and I would like the community's feedback. There is a lot of work to do and we are going to need a lot of help doing it. 

Feel free to email me, use the contact form, contact OWASP, sign up for the .NET Project email list, tweet me, or do what ever makes the most sense. We need your input.


AppSec | C#

Direct Object References

by Bill Sempf 9. February 2012 05:34

I have to use the Open Graph API from Facebook ton my current project, and I found a real life example of the Direct Object Reference flaw I discuss in my Pentesting ASP.NET talk.

The Direct Object Reference is one of the OWASP Top 10, and is one of the most common security flaws in REST or SOAP APIs. When you use a knowable value as a unique identifier in your response, you are exposing an inportant part of your architecture to a potential attacker, or anyone else who deems your information interesting.

I was suprised to discover that the user identifier for Facebook is an integer. The OpenGraph request for a user looks like this:

That's me, obviously.


   "id": "1138975844",
   "name": "Bill Sempf",
   "first_name": "Bill",
   "last_name": "Sempf",
   "username": "billsempf",
   "gender": "male",
   "locale": "en_US"


By the way, I have every privacy control turned on, and yet anyone can view this basic information. I'm not terribly happy about that.

But who is next in line?

That could get interesting.

   "id": "1138975845",
   "name": "Mary Loaiza",
   "first_name": "Mary",
   "last_name": "Loaiza",
   "link": "",
   "gender": "female",
   "locale": "es_LA"


I wonder how Mary feels about me knowing that she is a facebook user.

Anyway, that's a direct object reference. Keep an eye out for it in your code. It's very simple for an attacker to write a script that checks every number in a range, and get a lot of your database. The easy remiadiation is to use a GUID as the ID, or use the ESAPI AccessRequestMap








AppSec | Biz | C# | Javascript

Referencing a C# class library in HTML5 Metro UI

by Bill Sempf 14. December 2011 15:39

I am sure that you, like me, are hoping that you can use your C# code as the backend to your HTML5 Metro applications. For instance, I need to use the Meetup API that I am developing in a metro application that I am planning. I don't want to rewrite all of that in JavaScript.

In the solution that needs the C# class, right click on the solution in Solution Explorer and select Add New Project. I used new rather than trying to import one becasue the import facility is a little buggy in Visual Studio 2011. Click on Visual C# and then select Class Library as shown in Figure 1.


Figure 1- Add a class library

Once the project is added , you need to make two changed before you can reference it in your HTML5 application. First, the output type of the assembly needs to be set to WinMD File, as shown in Figure 2.  You can change this in the Properties.


Second, you need to seal the class. You can do that in the code for the class, using the sealed declaration, like this:

    public sealed class WebService
        public string BaseUri { get; set; }


Note that implementation inheritance isn't alloweed in Metro applications, so you need to head back to the 90s to get your polymorphism working, sorry!



Biz | C# | HTML5 | Windows8

CodeMash v2.0.1.1

by Bill Sempf 16. January 2011 06:05


Another CodeMash is in the books, and all kinds of new stuff was in the offing for me this year.  But first I would be remiss if I didn’t thank Jason Gilmore, Brian Prince and especially Jim Holmes (along with the rest of the board) for uncompromising management of simply the best conference on this topic.  Period. Not for the money, not for the constraints of space.  It is simply the best code-centric conference on the planet.

I owe a lot of people a lot of links and information on a lot of topics.

imageFirst and foremost, I was delighted to be asked to speak again, and was pleased to have Matthew Groves join me for a discussion on Monodroid.  We had 100 people join us for a look at how Monodroid came to be and what the future holds.

Then Matt took us for a tour of his excellent Stock Tracker application (shown left), converted from Windows Mobile.  There were a number of good points made all around, and generally a good time was had by all.

The Monodroid documentation contains nearly everything that you need to know to get programming.  The tutorials are the best starting point, and provide the templates for all of the major use cases. Matt’s application is on GitHub – please feel free to get it an mess around.  It’s a good app.  I’ll have BabyTrak up here in a couple of months.

imageThe Locksport openspace was a rousing success.  About 40 people were taught to pick, and about that many more stopped me in the halls and told me that they would like to have been there.  I was frankly astonished by the turnout, and would have brought 5 times as many picks if I would have known about the interest – all 15 of the sets I brought were sold.

For those looking for more information:

The Locksport International site has a lot of good links to publications and whatnot.  Deviant Ollem’s book, Practical Lock Picking, is excellent – he is the guy who wrote the presentation that I gave (twice). The best community is online at Lockpicking101, and they have an IRC channel too.  If you need to order picks, look at LockPickShop – Red does an awesome job.  The 14 piece set is on sale right now and is a great learners set!

imageFinally, if you are in the Columbus area please join us at the Columbus branch of Locksport International.  We have a Meetup group – just go sign up and you’ll get the locations for each meeting.  You can attend for free, but if you want a membership card and to participate in competitions, it’s $20 a year.

And last but not least, I got a ton of comments on the jam band.  Lots of questions too.  Yes, I was a professional musician for many years.  I taught at a lot of area band camps, like Upper Arlington and Teays Valley.  I played in a Dixieland band in London Ohio called the Lower London Street Dixieland Jazz Band and Chamber Music Society for nearly ten years. I haven’t played in quite a while, and I have to say it was a lot of fun.  Hope to do it again next year.

All in all, an awesome conference.  Again, I was a net producer of content rather than a consumer of content, and that’s OK.  I still learned a ton just by chatting with friends old and new, and picked up information about the hip new technologies that the cool kids are using by osmosis.

Hope to see everyone at DevLink!


Biz | Personal | C# | Enterprise Architecture | VB

I'll be speaking at DogFoodCon

by Bill Sempf 12. October 2010 17:33

I'll be speaking at the 2010 DogFood Conference that Microsoft puts on here in Columbus.  Danilo Castilo runs it (largely) and it is pretty cool - a neat community event on a budget.

It's a cool collection of talks about the newest Microsoft products and how people are using them.  Thus the name: 'DogFood' from the phrase 'eating your own dog food.'

I'll be speaking with Mario Fulan about using AFDS 2.0 to cross domain borders.  If you don't already know Mario, he is a beast - one of like ten Certified Sharepoint Masters in the whole freakin universe or something.  He has forgotten more about SharePoint than I will ever learn.  I do know Windows Identity Foundation a little bit though, so that's what I'll be covering.

The conference is at and is selling out really fast.  If you are interested in the hot new stuff, check it out and get registered while you can.  It's next month - November 4 and 5.


Biz | C# | Enterprise Architecture

Being Object Stingy

by Bill Sempf 14. September 2010 05:33


This is clipped content from my C# Book. 

You can’t construct an object without using a constructor of some sort. If you define your own constructor, C# takes its constructor away. You can combine these two actions to create a class that can only be instantiated locally.

For example, only methods that are defined within the same assembly as BankAccount can create a BankAccount object with the constructor declared internal, as in the bold text in this chunk of code:

// BankAccount -- Simulate a simple bank account.
public class BankAccount
    // Bank accounts start at 1000 and increase sequentially.
    private static int _nextAccountNumber = 1000;
    // Maintain the account number and balance.
    private int _accountNumber;
    double _balance;
    internal BankAccount() // Here’s the internal, not public, constructor.
        _accountNumber = ++_nextAccountNumber; 
        _balance = 0;

    public string GetString()
        return String.Format("#{0} = {1:N}", _accountNumber, _balance);


Biz | C#

Husband. Father. Pentester. Secure software composer. Brewer. Lockpicker. Ninja. Insurrectionist. Lumberjack. All words that have been used to describe me recently. I help people write more secure software.

Find me on Mastodon

profile for Bill Sempf on Stack Exchange, a network of free, community-driven Q&A sites