Search

Helping developers and enterprises secure their code is what we do. Got a project, an RFP, or just some questions? Let us know!

info(at)matasano.com
1-888-677-0666 x0

Playbook is our product. It does firewall sync. To learn more about Playbook, check out the site, or get in touch with us via the web, e-mail, or phone.

playbook(at)matasano.com
1-888-677-0666 x7529 (PLAY)

Tuesday
20Oct2009

Ninja Threat Modeling

Conquer your fear

Like it or not, developing an attack plan for a penetration test requires standing up a rudimentary threat model. Not surprisingly, threat modeling often produces discomfort in the stomach and uncertainty in the heart of many testers, but you’ve got to cowboy up and do it.

You need to determine where you’re going to spend your limited testing time, which tools you want to pull out of your arsenal, and how to prioritize your findings after the test is complete. Testing an application while blind to the context of how it could be abused in a real world environment is a sure-fire recipe for disaster and embarrassment.

Start with an application overview sitting peacefully on your desk. A sudden bang, a quick diversion, a cloud of smoke - and a test plan appears in its place… Fear no more, for the guide to ninja threat modeling is here.



SDL: This isn’t the threat model you’re looking for

You’ve probably heard of threat models before, especially in traditional security development lifecycle circles. In that context, the threat models are generated in the requirements/design phase, which is much earlier in the process. It has spawned not just one, but two, Visio-driven toolsets from Microsoft and countless data-flow diagrams, attack trees, consulting engagements, and perplexed developers. When performed by a skilled and experienced team member, the model can be used to identify architectural weaknesses, guide default application behavior, and outline functional requirements for the product.

However, by the time you’re in the verification phase of the lifecycle, generating a formal threat model starts to yield diminishing returns. We recommend taking a short-cut with a set of assumptions that have served us well over the years.

Software would be great if it wasn’t for the users:
Client-side code assumptions

  • The attacker will have unfettered access to the client and will instrument all of the functionality (this includes the “secret” admin and debug functions), and all of the client-side controls will be removed.
  • If it accesses the network, there’s a man-in-the-middle.
  • All input will be malformed and unexpected - even from the trusted end user… who will happily introduce input from untrusted sources.
  • The source code is completely exposed - including any secrets stored in the code.
  • If the application runs at a privilege level that the attacker desires, he will attempt to abuse it for nefarious purposes.
  • Don’t assume the code you’re testing is the attacker’s final destination in his path of exploitation. If the code weakens the system’s security posture in any way, the attacker will abuse it.

“I’m sorry, Dave. I’m afraid I can’t do that”:
Server-side code assumptions

  • All of the assumptions for client-side code hold for server-side code as well. This includes the source code disclosure assumption if you’re shipping product. If you’re running SaaS, it’s more of a question of when, not if.
  • The attacker is going to sit on the same network segment as the application. There’s no firewall or filters. There’s a special place in hell reserved for products that require firewalls or filtering to protect themselves against attack.
  • The naming service that the product relies upon will be compromised.
  • The switching and routing fabrics will be compromised.
  • If you have more than one defined user, one user will want to do things as the other user.
  • If you have more than one defined role, a user in one role will want to perform functions in the other role.
  • An attacker may want to cover their tracks. If he can do it with subtlety, he will take that path if it is easy to do.
  • If it is exposed to the Internet, some yahoo will want to make it crash with an asymmetric attack (think packet of death or attack amplification).
  • If the application runs on a multi-user system, other users on the system will attempt to subvert the application through any and all resources they can access (such as the file system, shared frameworks, IPC, and others).
  • If the application uses URIs (HTTP/FTP/SMTP/ITMS, whatever), an attacker has compromised the innocent client and has access to the session command channel.

A return to olde SDL land?

With these assumptions in place, you could go ahead and do some basic data flow analysis. You should be able identify key entry points in the application where data and functionality cross trust boundaries. If you want to stay in step with Microsoft, feel free to pull out the STRIDE* model and apply the threat types to each entry point, keeping in mind the assumptions mentioned above. With the threat landscape outlined, you’ll find the STRIDE process will go faster than simply working from a blank data flow diagram without context.

However, ninja threat modeling uses data flow analysis as a clean-up task, rather than the opening salvo. We’re impatient and want to know the answer to the question,”What entry points should we really care about?” right away.

* As required by the SDL powers-that-be, I am obligated to mention STRIDE threat types (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, and Elevation of Privilege) in all communications relating to threat modeling at least once.

Where there’s smoke, there’s fire

A heat-seeking approach to drive your test plan is just what you need. When we blather on about entry points, trust boundaries, and threat vectors, what we’re really thinking is “Where can I do the most damage in the shortest period of time?”

The ninja threat model relies on two techniques: incident and vulnerability history and the commission of deadly sins.

Those who cannot learn from history are doomed to repeat it

The first technique examines how the product or similar products have been abused in the past. Don’t just look up old advisories or bug reports - look for incident data. Did a product have a vulnerability that allowed malicious code to spread? Was the vulnerability discovered and abused by a 16-year-old to make lots of friends on the social networking platform of the day?

Examine these abuse cases and make sure you can recreate them against the application you’re testing. At the root of each one, there should be a vulnerability, a threat actor, and an impacted asset. If you’re stuck with dry vulnerability advisories, sometimes the researcher will have a good grasp on the impact of the finding and will document it well. In other cases, you have to watch out for advisories with overreaching statements or extreme speculation.

What’s nice about these types of derived threats is a good amount of the test planning is already done for you and can be lifted wholesale. Watch out for tunnel vision, though - time, platform specifics, or other dependencies may have limited the avenues available to the researcher or intruder. Make sure you’re looking for the pattern that indicates the flaw is present, not just performing a pattern match on yesterday’s exploit.

Where DIY should be DDIY - Don’t Do it Yourself

The second technique focuses on what developers get wrong most of the time - otherwise known as the “Deadly Sins”. The vulnerabilities introduced by these common flaws are mentioned for a reason: they get abused by threat actors on a regular basis.

Every security outfit has a list of their deadly sins, including Microsoft, SANS, OWASP, and Matasano, because lists are just that cool. We think ours is better for one major reason - our Deadly Sins are features rather than defects. As a result, our work fits into ninja threat modeling much cleaner than a laundry list of vulnerabilities.

It’s an issue of perspective and language. Developers don’t roll out of bed in the morning and say, “I’m going to code up a few SQL Injection vulnerabilities today!” Instead, they say, “I’m going to write the Advanced Search module for the Report Engine today!” When you hear this, your utter lack of faith in your development brethren should kick into overdrive and you should be busily entering “Test for SQL Injection here, here, and here” in your test plan.

We’ve appended our list of Deadly Sins for Web Applications for easy reference. Parts of it also apply to other sorts of applications. When you see these features, get out your red pen, add another page to your test plan, and start outlining how many different ways developers get these things wrong and how to test for it.

Time for the test plan

After wrapping up the heat-seeking exercises, it’s time to sanity check your work with data flow analysis including the selective application of STRIDE mentioned earlier. Hopefully, the overlap should be extensive. If it isn’t, you may be dealing with something that hasn’t been tested extensively and may bear some interesting fruit. On the other hand, you may have been assigned the most boring application known to mankind. Congratulations!

After following this approach, you should have a good definition of the application’s attack surface and the threat landscape which you will be attempting to recreate. As a result, you should be able to produce a meaningful test plan with a threat model that is defensible and sufficient to guide your engagement. You might even get away with doing one without a single Visio diagram.

 

Deadly Features for Web Applications


1. Security

  •     Encryption
  •     Hierarchical role/privilege management
  •     Password storage
  •     Password reset


2. E-mail functionality

3. Thick Clients: Web Applications In Name Only (WINOs)

4. File Upload/Download

5. Templating and Content-Controlled Code

6. Advanced Search


More Deadly Features for Applications


7. Persistent network sockets that accept arbitrary connections

8. Installers

9. Use of plug-ins or third party software to write directly to the DOM

10. Single Sign-On / Authentication Hand-offs

Thursday
15Oct2009

A C++ Challenge - The Conclusion

The contest is over! We got some good submissions and as expected a few security researchers found the bug rather quickly. The sample code had numerous defects, however not all were exploitable. Here are our top 3 correct submissions in order they were received:

1st Drew Yao: With the quickest submission and first exploit. He exploited the bug on OSX Snow Leopard
2nd Kevin Easton: Sent in code that produces an exploit file
3rd Evin Robertson: With a great fuzzing technique for initially finding the bug (valgrind a.out /dev/urandom)

Other correct entries:
Joe Damato - Joe did a great write up on the challenge here
Rachel Blum
Anonymous 1
Anonymous 2

In addition to our contest submissions, we promised we would provide an answer to our vulnerability challenge, and here it is. The example code has a lot of issues. Everything from missing NULL pointer checks to a missing free. The bug we were looking for is the size overflow in the argument to our new operator. Our program opens up a binary file and reads some values out of it using a _stream structure. From those first few values we get an integer ‘s->num_of_streams’, which of course we sanity check before using it as an argument to a memory allocator. Unfortunately our sanity check is broken in the following if statement:

    if(s->num_of_streams >= INT_MAX / (int)sizeof(int)) {
        safe_count = MAX_STREAMS;
    } else {
        safe_count = s->num_of_streams;
    }

The problem with this check is that our Object class is not sizeof(int). We assign safe_count the value of ‘s->num_of_streams’ and go on our way. Following this bad check we call the C++ new operator to allocate an array of class instances representing each stream in our file. Unfortunately g++/libstdc++’s new operator allows for overflow to occur. This happens because we are asking for new to allocate ‘safe_count * sizeof(Obj)’. This a known issue, but more on that later.

So now we’ve allocated a smaller number of class instances than ‘safe_count’ specifies. Following the allocation of our class instances a meta data structure is placed on the heap using malloc(), zeroed out and a function pointer is setup. Now we enter a for loop using our attacker controlled safe_count value. This is where our problems begin, our for loop allocates a temporary buffer with each iteration copying a stream from the file into it, a DWORD is read from the stream and the parse_stream() method is called for each class instance we allocated earlier. The parse_stream() method sets the class member variable ‘type’ to the value of the parse_stream() method ‘t’ argument. The location of class member variable ‘type’ should be in the heap because we allocated the class instance using the new operator. Unfortunately this is done for more class instances then we actually allocated earlier. This allows us to overwrite the function pointer in the ‘imd’ structure, by way of the parse_stream() method, and gain code execution. This happens because the parse_stream() method effectively performs a 4-byte overwrite into heap memory that contains the ‘imd’ structure.

The fix here should start at the sanity check of s->num_of_streams. We should declare num_of_streams unsigned. This value should then be validated such that s->num_of_streams is not greater then MAX_STREAMS.

We originally wanted to share a similar real-world code pattern but that bug isn’t officially patched yet and we wanted to get this post out. But thats OK because we can still talk about the core issue.

As our example showed you can easily misuse the new operator. This is a known issue in libstdc and was mentioned as far back as 2002 in this Blackhat presentation. Using new to allocate some storage space may be common but there is another similarly dangerous but slightly less used code pattern here, and thats using new to allocate an array of class instances. Most developers have learned to carefully inspect the size of an allocation before copying user data to it. But allocating too few classes can result in similar memory corruption and have subtle but exploitable consequences when all the right stars are aligned, and thats what this challenge was all about.

In the real world vulnerability we found exploitation was not as straight forward and may not even be possible. But the reason for that is actually an interesting story in itself (and by interesting I mean dry and boring). If the Obj class in our challenge had a constructor things would have been far different. Let’s add a simple constructor to our example:

    Obj(){ length = 0; }

To understand why this changes things you need to dive down and disassemble the code. Long story short, even though we tricked our application into allocating 8 classes, g++ compiles the code in such a way that constructors are called for every class instance we requested, all 357913943 of them! Now if the constructor does something like set some class member variables to NULL then our chances of exploitation are pretty slim. This is because our process will essentially rip through its own heap writing NULL bytes until it hits the end of the heap and crashes. Thats quite a destructive constructor! When a constructor like this isn’t present our fake class instances become an API for writing into arbitrary memory. Thats how you gain code execution in our challenge.

Some of our commentors made the point that this was not C++, but merely C with classes. To those commentors I would like to say ‘welcome to the real world’. Code patterns like this are why security vulnerabilities exist!

This code pattern raises some interesting areas for exploitation since your non-existent class instance(s) are basically an API for writing into arbitrary memory locations in the heap. Bad allocations from the new operator are a known problem, Michael Howard at MSFT has written about this issue before and MSFT did something about it! MSVC produces better code that doesn’t allow the integer overflow in the new operator to occur. Unfortunately g++ users are stuck with this issue for the forseeable future.

Wednesday
14Oct2009

Corporate Pentesters: Fill Out Survey, Get Big Poster

[Update: That was fast! We’re at 97 submissions. 3 more and we go to lottery mode. For the rest of the week, if you fill out the survey, you will almost certainly get a poster. We’d give everyone a poster, but they cost a couple bucks a piece to ship.]

A few weeks ago we ran a survey for firewall operators. The results were a huge win for us. But most of our readers aren’t firewall operators. A lot of you are company pentesters, though. Are you part of a company application security team? Fill out a survey, and we’ll send you a poster. It will look marginally better and be significantly bigger than the picture below.

Poster

Take the survey here. It’ll take you less than 5 minutes.

The first 100 responses will get posters, and we’ll ship 50 more posters by lottery if we go over 100 responses (we’ll let you know in this post if that happens).

Wednesday
14Oct2009

Blog-fix-omatron: DH Parameter Tampering Explained

When we moved the blog off Wordpress, the new host had trouble with our XML export. So we’re missing posts, and I’m gradually adding them back.

Here’s one you may have missed back in ‘07. It explains how to beat bad implementations of Diffie Hellman and SRP:

Adam Bozanich Did Not Uncover An NSA IPSEC Conspiracy (but he found a pretty cool bug).

Friday
09Oct2009

A C++ Challenge

Let’s say you show up at an interview.

The interviewer asks whether your comfortable reviewing C code.

You say “sure!”, confident in your ability to spot a bad call to memcpy() on the spot.

The interviewer asks you if you have any experience auditing not just C, but C++.

Again, you confidently respond “no problem!”.

The interviewer presses further: “What about the intricacies of C++ templates and class instantiation at the assembly level?”.

This time you pause for a moment to ponder the question …

C++ lends itself to much more complex vulnerabilities then plain old C. From templates to string classes, C++ raises the skill level required to play the memory corruption game. And while the quality of C/C++ code we see has increased dramatically over the years, a lot of developers still don’t understand the more obscure C++ bug classes.

I recently found a vulnerable C++ code pattern that I wanted to share with our readers. But instead of just writing some boring technical blog post, Matasano would like to present a C++ audit challenge to our audience. It consists of a contrived vulnerability that follows the same vulnerable code pattern. Our rules are simple:

1. We give you working C++ source code you can compile with g++

2. You audit the source or binary, find the bug and submit your findings via email to: chris _at_ matasano.com All submissions should include a paragraph explaining where the vulnerability is, why its vulnerable, your exploit it and how you would fix it. A working exploit is required to win, but we will also post correct runner-up submissions that don’t include one.

3. Matasano announces the best three correct submissions and sends them Matasano branded magnet and posters (sorry no cash prizes!)

The quicker you submit, the better. Following the contest’s conclusion we will present a follow-up post that goes over the details of our contrived vulnerability and how to exploit it. More importantly, we will also blog about the real world vulnerability we found with a similar code pattern.

The contest vulnerability is confirmed exploitable on Linux and OS X. If you’re an experienced security researcher you can probably spot the bug in just a few minutes. Maybe seconds! We don’t expect to stump the Mark Dowds of the world, but if we can have some fun and educate a few developers in the process then were all for it.

We also ask that you don’t post any answers in the comments, but we can’t stop you and we certainly aren’t in the business of deleting legitimate comments. So without any further delay, you can download our challenge HERE